Author Topic: busy check with hd44780 via 12c  (Read 5972 times)

0 Members and 2 Guests are viewing this topic.

Offline Dan MoosTopic starter

  • Frequent Contributor
  • **
  • Posts: 359
  • Country: us
busy check with hd44780 via 12c
« on: October 12, 2016, 03:38:52 am »
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;
   }
}
















« Last Edit: October 12, 2016, 03:44:54 am by Dan Moos »
 

Offline newbrain

  • Super Contributor
  • ***
  • Posts: 1915
  • Country: se
Re: busy check with hd44780 via 12c
« Reply #1 on: October 12, 2016, 07:39:47 am »
Using an atmega328p, and I2c expander (pcf8574)
[...8<...]
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.
[...>8...]
It'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!).

The HD44780, 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.

There are operations 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 the PCF8574 hasn'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 the ATmega 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.
« Last Edit: October 12, 2016, 07:41:34 am by newbrain »
Nandemo wa shiranai wa yo, shitteru koto dake.
 

Offline Dan MoosTopic starter

  • Frequent Contributor
  • **
  • Posts: 359
  • Country: us
Re: busy check with hd44780 via 12c
« Reply #2 on: October 13, 2016, 04:07:12 am »
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.
 

Offline Dan MoosTopic starter

  • Frequent Contributor
  • **
  • Posts: 359
  • Country: us
Re: busy check with hd44780 via 12c
« Reply #3 on: October 13, 2016, 04:42:32 am »
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?
 

Online Ian.M

  • Super Contributor
  • ***
  • Posts: 14132
Re: busy check with hd44780 via 12c
« Reply #4 on: October 13, 2016, 05:53:28 am »
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: [Select]
<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.
« Last Edit: October 13, 2016, 10:17:38 am by Ian.M »
 

Offline newbrain

  • Super Contributor
  • ***
  • Posts: 1915
  • Country: se
Re: busy check with hd44780 via 12c
« Reply #5 on: October 13, 2016, 07:23:25 am »
What Ian said!
Saw your posts, but he beat me on my way to a real keyboard  ;D

But 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.

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?
In TWI_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: [Select]
//read current slave
uint8_t Read_TWI(void)
{
   uint8_t data;
   TWI_Start(_readAddress);

   //clear interrupt flag, start interface
   TWCR = (1<<TWINT) | (1<<TWEN);

   // Wait for data to be received
   WaitforTWINT();
   
   // Now we can read it!
   data = TWDR;
   
   TWI_Stop();
   return data;
}

Another couple of remarks:
  • The _delay_ms(1); in LCD_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
Nandemo wa shiranai wa yo, shitteru koto dake.
 

Offline dannyf

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: busy check with hd44780 via 12c
« Reply #6 on: October 13, 2016, 10:03:32 am »
Quote
how can I/should I do the busy check on the Hd44780 LCD controller?

you will need to read back the pins' status.
================================
https://dannyelectronics.wordpress.com/
 

Offline Dan MoosTopic starter

  • Frequent Contributor
  • **
  • Posts: 359
  • Country: us
Re: busy check with hd44780 via 12c
« Reply #7 on: October 13, 2016, 01:49:35 pm »
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!
 

Offline dannyf

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: busy check with hd44780 via 12c
« Reply #8 on: October 13, 2016, 02:39:20 pm »
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 rw
  Lcdsendcmd(0x00) //tell LCD to set flag
  Return lcdread()?true:false


This 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.
================================
https://dannyelectronics.wordpress.com/
 

Offline Dan MoosTopic starter

  • Frequent Contributor
  • **
  • Posts: 359
  • Country: us
Re: busy check with hd44780 via 12c
« Reply #9 on: October 14, 2016, 12:15:12 am »

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.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf

 

-->