Using an atmega328p, and I2c expander (pcf8574)
how can I/should I do the busy check on the Hd44780 LCD controller?
To add complexity, do to the limited I/0 on the expander, I'm forced to use 4 bit mode with the LCD. Probably obvious to most here, as this expander seems to pretty common in this application.
Anyway, I'm creating a library for this setup for my own enjoyment. I can get everything working if I use delays instead of checking the busy flag. If I use my busycheck() routine, it fails.
First, with such a setup, is a busy check even feasible? In other words, does all the effort to read the busy flag in 4 bit mode through I2C take more processor time than it takes for the flag to clear? Running I2C at 100 kHz BTW.
Even if that's the case I'm still stumped by why my check doesn't work.
Debug shows that once I put the address+r into TWDR to setup the read of the LCD busy flag, that works, but when I actually do the read, the address+r is still in TWDR.
Logic analyzer reading the HD448780 pins says the busy check happens, and returns 'not busy'. That would make sense, because if the address+r is whats being read, it would be interpreted as 'not busy'
logic analyzer reading I2C bus says read happens, and that it reads all zeroes on the LCD pins except Rw. In other words, my busy check command. Strange to me that the I2C bus shows a different read than the debugger says I get. Its like I save the contents of TWDR before its filled correctly.
I do set TWINT after sending address+r, and I do wait for it to be set before trying to actually read the data on TWDR
Roughly, my routine is:
strobe the En or'd with the Rw
send a start
send address+r
read high nibble
strobe the En or'd with the Rw
read low nibble
send a stop
one question comes to mind. Do I need a stop between reads since my strobe routine uses writes? In other words, can I switch from writes to reads on the same address without a stop in between?
my gut says the problem is something to do with the way I strobe the En to send the busy check command. Like my understanding of how to make that part work in 4 bit mode is not right.
Here's the code if you want a look: The variable Rw is an unint8_t that is all 0's execept for the where the Rw pin is.
//busycheck function
void LCD_busyCheck()
{
uint8_t busyFlag;
uint8_t nibble;
do{
LCD_flashenable(Rw); //flash enable while setting read mode;
nibble = Read_TWI();
busyFlag = (nibble & 0xf0);
LCD_flashenable(Rw); //flash enable while setting read mode;
nibble = Read_TWI();
busyFlag |= ((nibble >> 4) & 0x0f);
}
while (busyFlag >= 0x80);
}
//Strobe En function
void LCD_flashenable(uint8_t data)
{
Write_TWI(data | En);
_delay_ms(1);
Write_TWI(data & ~En);
}
//write to current slave
void Write_TWI(uint8_t data)
{
TWI_Start(_writeAddress);
TWDR = data; //load byte to be sent into data register
TWCR = (1<<TWINT) | (1<<TWEN); //clear interrupt flag
WaitforTWINT();
TWI_Stop();
}
//read current slave
uint8_t Read_TWI(void)
{
uint8_t data;
TWI_Start(_readAddress);
data = TWDR;
TWCR = (1<<TWINT) | (1<<TWEN); //clear interrupt flag
WaitforTWINT();
TWI_Stop();
return data;
}
//Send start
void TWI_Start(uint8_t address)
{
TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN); //TWINT clears interrupt flag (sets it to 1); TWSTA sends start bit; TWEN enables TWI
WaitforTWINT();
TWDR = address; //send slave address
TWCR = (1<< TWINT) | (1<< TWEN); //clear interrupt flag.
WaitforTWINT();
}
//Send stop
void TWI_Stop()
{
TWCR = (1<<TWINT) | (1<<TWSTO) | (1<<TWEN); //TWINT clears interrupt flag (sets it to 1); TWSTO sends stop bit; TWEN enables
}
//Loop until TWI interrupt flag is HIGH
void WaitforTWINT()
{
while(!(TWCR & (1<<TWINT)))
{
continue;
}
}