busy check with hd44780 via 12c - Page 1
Forum section: electronics / general

author: Dan Moos
author_rank: Frequent Contributor
timestamp: October 12, 2016, 03:38:52 am
content: 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 TWDRRoughly, my routine is:strobe the En or'd with the Rwsend a startsend address+rread high nibblestrobe the En or'd with the Rwread low nibblesend a stopone 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 functionvoid 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 functionvoid LCD_flashenable(uint8_t data){Write_TWI(data | En);_delay_ms(1);Write_TWI(data & ~En);}//write to current slavevoid Write_TWI(uint8_t data){TWI_Start(_writeAddress);TWDR = data; //load byte to be sent into data registerTWCR = (1<<TWINT) | (1<<TWEN); //clear interrupt flagWaitforTWINT();TWI_Stop();}//read current slaveuint8_t Read_TWI(void){uint8_t data;TWI_Start(_readAddress);data = TWDR;TWCR = (1<<TWINT) | (1<<TWEN); //clear interrupt flagWaitforTWINT();TWI_Stop();return data;}//Send startvoid 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 TWIWaitforTWINT();TWDR = address; //send slave addressTWCR = (1<< TWINT) | (1<< TWEN); //clear interrupt flag.WaitforTWINT();}//Send stopvoid 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 HIGHvoid WaitforTWINT(){while(!(TWCR & (1<<TWINT))){continue;}}

author: newbrain
author_rank: Super Contributor
timestamp: October 12, 2016, 07:39:47 am
content: Quote from: Dan Moos on October 12, 2016, 03:38:52 amIt's of course feasible...but pretty much pointless: at 100kHz each bit will take 10µs and you need at least 10+9x2 bits = 280µs to write two consecutive nibbles (even more, considering the need to pulse E!).TheHD44780, at the usual ~300kHz clock, will complete a read or write in 37µS (+ 4µs to update the ADDR register).By the time you do all the bit-flipping dance needed to read the status flag (see below), it will most certainly be 0.Thereareoperations that take longer: clearing the display and homing the cursor, for example. A simple delay will work there.About your code:Please use the "code quote" when posting (# icon), it will make it much more readable.Remember that thePCF8574hasn't got a separate real input mode: in order to read something, you first have to write 1 to the relevant bits (see chapter 8.1 "Quasi-bidirectional I/Os" in the datasheet), your code does not do that.In the read routine, if I understand correctly chapter 22.7.2 of theATmega DS,, you should clear the TWINT flag and wait for it again (signalling that a data byte has been received), before reading TWDR. You read it immediately after sending the address, with no time for the slave to answer.Hope this helps.

author: Dan Moos
author_rank: Frequent Contributor
timestamp: October 13, 2016, 04:07:12 am
content: Ok, first thanks for the heads up on posting code. Knew there was a way, just didn't know what it was.Next, isn't the clearing of TWINT and waiting for it to be cleared routine at the end of my TWI start sufficient before trying to read?Finally, I read the portion of the pcf8574 datasheet, and it indeed seems to be the main problem. I'll let you know how it goes after attempting the proper change.

author: Dan Moos
author_rank: Frequent Contributor
timestamp: October 13, 2016, 04:42:32 am
content: Ok, I'm not sure how to go about setting the port registers to HIGH per the pcf8574 datasheet.I tried doing a write with all 1's (start->address+w->0xff->stop) just before my read routine, but didn't work. It seems I'm misunderstanding.Thoughts?

author: Ian.M
author_rank: Super Contributor
timestamp: October 13, 2016, 05:53:28 am
content: You'd only want to set the data pins and RW high, and you need to do so before asserting the E strobe, so that's something like:Code:<START><Addr+Write><Data:0xF+RW><Data:0xF+RW+E><RESTART><Addr+Read><?nibble1><NAK><RESTART><Addr+Write><Data:0xF+RW><Data:0xF+RW+E><RESTART><Addr+Read><?nibble2><NAK><RESTART><Addr+Write><Data:0xF><STOP>for a full 4 bit mode read of both nibbles leaving the E strobe inactive and the interface ready to write to.

author: newbrain
author_rank: Super Contributor
timestamp: October 13, 2016, 07:23:25 am
content: What Ian said!Saw your posts, but he beat me on my way to a real keyboardBut I shall reiterate that this, though being a good exercise in bit twiddling and I2C handling, will not be of much use, especially at 100kHz.Most transaction will be completed even before you start reading back the status, and the very few ones that requires more time can be handled by a tailored delay.Quote from: Dan Moos on October 13, 2016, 04:07:12 amInTWI_Start()you correctly wait for the address+R/W to be transmitted.Now, the system is ready to receive a byte but has not received it yet, if the the TWDR is read it will return the address that's just been written.To wait for the reception to take place, one needs to start the interface and wait again on TWINT.When the wait is over, TWDR will contain the received data (warning: untested code):Code://read current slaveuint8_t Read_TWI(void){uint8_t data;TWI_Start(_readAddress);//clear interrupt flag, start interfaceTWCR = (1<<TWINT) | (1<<TWEN);// Wait for data to be receivedWaitforTWINT();// Now we can read it!data = TWDR;TWI_Stop();return data;}Another couple of remarks:The_delay_ms(1);inLCD_flashenable()is redundant, as the 44780 has an minimum enable cycle time tcycE= 1µs, with a minimum pulse width of PWEH= 450ns: just writing 1 then 0 with no wait in between is enough to satisfy these constraints.A slight optimization might be writing consecutive bytes (e.g. in the case above) without starting a new transaction every time, as also shown in the sequences provided by Ian

author: dannyf
author_rank: Super Contributor
timestamp: October 13, 2016, 10:03:32 am
content: Quoteyou will need to read back the pins' status.

author: Dan Moos
author_rank: Frequent Contributor
timestamp: October 13, 2016, 01:49:35 pm
content: Ha!! Dannyf could write documentation for Rigol! I do know I need to read the pin's status. It's a couple of details specific to doing it through this particular I2C expander that are giving me trouble.I have to go to work, but tonight I'll experiment with the stuff Ian and Newbrain pointed out.Thanks for all the help!

author: dannyf
author_rank: Super Contributor
timestamp: October 13, 2016, 02:39:20 pm
content: I think your difficulties originate from trying to do multiple things at the same time.For example, the LCD data sheet has a very simple requirement to read the flag: just send rs 0, rw 1 and read the db7 pin.So if I were to do it, I would do this.Uint8 lcdisbusy(void)Lcdrwset() //set rwLcdsendcmd(0x00) //tell LCD to set flagReturn lcdread()?true:falseThis is a generic routine that would work with any configuration. If you link in the 8bit mode, it will work. If you like in a 4bit mode, it will work too.Say that you are in four bit mode. You can use a global variable (to the LCD module) to communicate the rs, rw and en plus data bits.So your lcdsendcmd would look like this.Lcdsendcmd(DAT)Tmp = lcden | (DAT &0x0f)Pcf8754write(tmp). Send the lower 4bits with lcden set.Tmp = DAT >> 4.Pcdiutrwrite(tmp). Send the higher 4bits with lcden cleared.With this approach, you have isolated the logic part of the code from the implementation part of the code.Obviously, you will need to make sure that the i2c part of the code works.

author: Dan Moos
author_rank: Frequent Contributor
timestamp: October 14, 2016, 12:15:12 am
content: I'm more or less doing that. The actual functions that will be used are fairly generic, with the important stuff isolated in focused functions. The point is, it's specifically the I2C stuff that is giving me trouble.Also, since Newbrain confirmed my suspicion that a busy check in my case is superfluous, this is basically just and educational exercise at this point.