Again it would help to have additional information here. you say it is an Hitachi LCD, which probably just the command set it uses. You are also saying that you are using PortC and you talk about 4 bit code. So again I am assuming a parallell LCD.
So assuming your LCD-Controller is similar you have have DB4-DB7 (data lines) connected to some pins on Port C (maybe C4-C7?). You also have the Enable(E) line connected to another I/O port pin and Likewise for the Register Select (RS) pin connected to another I/O output pin. When you output a normal character character on this display the RS is high and when you output a command the RS is low. For each nibble you output you need to set the appropriate 4 data bits and toggle the E bit high and then low to toggle it out. There is also initialization code that you need to set this controller into 4 bit mode instead of 8 bit mode. I have include some excerpts of the SRS workshop C++ class below that shows some of this.
void LCD::Init() const
{
m_fInit = true;
/* make sure LCD has a chance to finish initializing after power-up */
m_ptimer->WaitMs(20);
/* send forced reset */
WriteNibble(0, 0x03); // (RS = 0, code = 0011)
m_ptimer->WaitMs(5);
WriteNibble(0, 0x03);
m_ptimer->WaitMs(1);
WriteNibble(0, 0x03);
m_ptimer->WaitMs(1);
/* default is 8-bit operation; we send the top half, it changes,
then we need to send the request again to set the rest of the
options for that command (e.g. 2 lines) (from that point on,
all instructions are sent in 2 parts, high and low) */
WriteNibble(0, 0x02); // set 4-bit operation (RS = 0, code = 0010 [4-bit])
m_ptimer->WaitMs(1);
WriteCommand(0, 0x28); // 4-bit operation, 2 lines (5x8)
/* other initialization */
WriteCommand(0, 0x06); // increment, no shift
ClearAndReturn(); // clear screen and return cursor home
HideCursor(); // (this also turns on display)
}
/* prints given string on the specified line (1 or 2);
remainder of (visible) line will be padded with spaces by default */
void LCD::Print(const char *psz, char line, bool fPad) const
{
const char *pch, *pchLim;
SetMemAddr(line == 1 ? 0x80 : 0xC0);
pch = psz;
while (*pch != 0)
WriteCh(*pch++);
if (fPad)
{
/* blank rest of line */
pchLim = psz + cchScreen;
while (pch++ < pchLim)
WriteCh(' ');
}
}
/* writes a character at the current location, updates
current location to next space (to right) */
void LCD::WriteCh(char ch) const
{
WriteCommand(1, ch);
}
/* clears the display and resets the current location to the upper left */
void LCD::ClearAndReturn() const
{
WriteCommand(0, 0x01); // clear display
m_ptimer->WaitMs(5); // a guess...
WriteCommand(0, 0x02); // return home (can take 1.52ms)
m_ptimer->WaitMs(2);
}
/* send an instruction to the LCD; we need to send a byte, which
is done in two nibbles, high, then low */
void LCD::WriteCommand(bool fRS, unsigned char ch) const
{
/* send high 4 bits, then low 4 bits */
WriteNibble(fRS, ch >> 4);
WriteNibble(fRS, ch & 0x0f);
}
/* set outputs for LCD control, then toggle Enable */
void LCD::WriteNibble(bool fRS, unsigned char ch) const
{
int ipout;
if (fRS)
m_poutRS->High();
else
m_poutRS->Low();
/* the nibble to send is in ch: xxxxDCBA
D goes to DB7, C to DB6, B to DB5, A to DB4;
the code sets outputs from low (A/DB4) to high (D/DB7) */
for (ipout = 0; ipout < ipoutMax; ++ipout)
{
/* see if low bit is set */
if (ch & 0x01)
m_apout[ipout]->High();
else
m_apout[ipout]->Low();
/* shift ch right 1 bit to get next bit in low spot */
ch >>= 1;
}
/* all data is now waiting for LCD; needs to be stable
for >=40ns before E high */
asm("nop");
m_poutE->High();
/* need to wait 230ns before E low
(nop = 1 cycle = 125ns at 8MHz; do enough for 16MHz)*/
asm("nop");
asm("nop");
asm("nop");
asm("nop");
m_poutE->Low();
/* we need to make sure LCD inputs don't change for 10ns after
E low, which would be taken care of by other instructions;
but, processing of request can take up to 37+4us, so
just to be safe, we'll delay for another ms here */
m_ptimer->WaitMs(1);
}
The rest of the code as well as documention is up on the web site I mentioned previously.
Note: this code uses some other classes, such as an IN and OUT which properly initialize the atmel registers to set the IO pins for either Input or Output. In this case they are all output pins.
I tried your link but it pointed to somewhere strange…
Good Luck