Switching outputs to inputs, LCD signals, & Pullups

Go To Last Post
8 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

My understanding from looking at the datasheets is that the contents of the port register controls the pullup resistors whenever a pin is set to be an input.

If a bit was set to be an output and set to 1 when the corresponding bit in the DDR is set to 0, the pullup resistor would be enabled until the corresponding bit in the PORT register is cleared.

Likewise if a bit was set to be an output and set to 0 when the corresponding bit in the DDR is set to 0, the pullup resistor would be disabled until the corresponding bit in the PORT register is set.

In the case of a GLCD, 8 data bits are sometimes output and sometimes inputs.

The main question is what should be done with the internal pullups on input?
I would assume they should be disabled, but would it matter if they are enabled?

The reason I ask, is I have looked around at many LCD & GLCD examples, and most of them don't deal with this issue. In fact, my code isn't dealing with this either.

So what happens is that when the data bits are turned around from output to input, the bits that had 1s on them during the last write will have their pullups enabled and the bits that last had a 0 would not have the pullups.

To ensure the pullups are set consistently requires writing to the port bits prior to reading the pins.

This takes a little extra time, especially in environments where the 8 data bits/pins are not all on the same port so it would be nice if you can get away with using the pullups either way.

The reason I ask is I've seen some odd behavior on one particular ks0108 based GLCD and I'm wondering if there is a some kind of weird slew rate issue or open collector drive issue.

Here is the link to the odd behaving GLCD:
http://www.topwaydisplay.com/Pub/Manual/LM19264ACW-Manual-Rev0.1.pdf

All timing uses minimum nanosecond delays specified from each datasheet. The topway display misbehaves until you increase its twh on data writes to 3500ns vs the specified 700ns.

This doesn't seem to make sense to me.

No - I haven't yet hooked up a scope yet to really see the signals yet as I'm still trying to debug things and figure out what is happening on this one particular GLCD. Several other GLCDs work fine with their minimum timing.

(And yes I'm not using for delays because of its incorrect delay calculations)

But in general what are peoples thoughts with respect to using/not using the internal pullups on the LCD data lines vs "randomly" using them. i.e. letting them be enabled/disabled by the contents of the last data written?

--- bill

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It probably makes little difference except in marginal cases. Personally, I'd turn on the pullups when reading.

As for weird behaviour - make sure you obey the setup and hold times religiously.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

But in general what are peoples thoughts with respect to using/not using the internal pullups on the LCD data lines vs "randomly" using them. i.e. letting them be enabled/disabled by the contents of the last data written?

You are right that existing libs generally don't bother. AVR-LibC's code has:

/*
 * Read one nibble from the LCD controller.
 */
static uint8_t
hd44780_innibble(uint8_t rs)
{
  uint8_t x;

  HD44780_PORTOUT |= _BV(HD44780_RW);
  HD44780_DDR &= ~HD44780_DATABITS;
  if (rs)
    HD44780_PORTOUT |= _BV(HD44780_RS);
  else
    HD44780_PORTOUT &= ~_BV(HD44780_RS);
  x = hd44780_pulse_e(true);
  HD44780_DDR |= HD44780_DATABITS;
  HD44780_PORTOUT &= ~_BV(HD44780_RW);

  return (x & HD44780_DATABITS) >> HD44780_D4;
}

Fleury's code has:

/*************************************************************************
Low-level function to read byte from LCD controller
Input:    rs     1: read data    
                 0: read busy flag / address counter
Returns:  byte read from LCD controller
*************************************************************************/
#if LCD_IO_MODE
static uint8_t lcd_read(uint8_t rs) 
{
    uint8_t data;
    
    
    if (rs)
        lcd_rs_high();                       /* RS=1: read data      */
    else
        lcd_rs_low();                        /* RS=0: read busy flag */
    lcd_rw_high();                           /* RW=1  read mode      */
    
    if ( ( &LCD_DATA0_PORT == &LCD_DATA1_PORT) && ( &LCD_DATA1_PORT == &LCD_DATA2_PORT ) && ( &LCD_DATA2_PORT == &LCD_DATA3_PORT )
      && ( LCD_DATA0_PIN == 0 )&& (LCD_DATA1_PIN == 1) && (LCD_DATA2_PIN == 2) && (LCD_DATA3_PIN == 3) )
    {
        DDR(LCD_DATA0_PORT) &= 0xF0;         /* configure data pins as input */
        
        lcd_e_high();
        lcd_e_delay();        
        data = PIN(LCD_DATA0_PORT) << 4;     /* read high nibble first */
        lcd_e_low();
        
        lcd_e_delay();                       /* Enable 500ns low       */
        
        lcd_e_high();
        lcd_e_delay();
        data |= PIN(LCD_DATA0_PORT)&0x0F;    /* read low nibble        */
        lcd_e_low();
    }
    else
    {
        /* configure data pins as input */
        DDR(LCD_DATA0_PORT) &= ~_BV(LCD_DATA0_PIN);
        DDR(LCD_DATA1_PORT) &= ~_BV(LCD_DATA1_PIN);
        DDR(LCD_DATA2_PORT) &= ~_BV(LCD_DATA2_PIN);
        DDR(LCD_DATA3_PORT) &= ~_BV(LCD_DATA3_PIN);
                
        /* read high nibble first */
        lcd_e_high();
        lcd_e_delay();        
        data = 0;
        if ( PIN(LCD_DATA0_PORT) & _BV(LCD_DATA0_PIN) ) data |= 0x10;
        if ( PIN(LCD_DATA1_PORT) & _BV(LCD_DATA1_PIN) ) data |= 0x20;
        if ( PIN(LCD_DATA2_PORT) & _BV(LCD_DATA2_PIN) ) data |= 0x40;
        if ( PIN(LCD_DATA3_PORT) & _BV(LCD_DATA3_PIN) ) data |= 0x80;
        lcd_e_low();

        lcd_e_delay();                       /* Enable 500ns low       */
    
        /* read low nibble */    
        lcd_e_high();
        lcd_e_delay();
        if ( PIN(LCD_DATA0_PORT) & _BV(LCD_DATA0_PIN) ) data |= 0x01;
        if ( PIN(LCD_DATA1_PORT) & _BV(LCD_DATA1_PIN) ) data |= 0x02;
        if ( PIN(LCD_DATA2_PORT) & _BV(LCD_DATA2_PIN) ) data |= 0x04;
        if ( PIN(LCD_DATA3_PORT) & _BV(LCD_DATA3_PIN) ) data |= 0x08;        
        lcd_e_low();
    }
    return data;
}

Yet both are WIDELY used and I've never heard of anyone reporting it as an issue that some of the pull-ups might be enabled each time. I suppose that if the module had a very weak drive low that it might battle the pull-ups but I doubt this is ever really the case.

Cliff

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok, so a bit of a followup.
The use or non-use of the pullups is fine. Either way will work. So to save cycles I'm not going to mess with setting them up when switching pins to input mode and so sometimes they will be enabled and sometimes not.

As far as my ks0108 "odd behavior" goes, it was not a timing issue. It was a logic error in the code. Under a certain set of conditions the code was not waiting for busy to go away after sending a SET_PAGE command.

All is good now.

--- bill

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So a bit more followup.
There was actually another even more subtle issue.

The AVR uses a synchronizer circuit on the PIN register.
This delays seeing an input signal value for up to 1.5 clocks.
This means that when delaying for a specific interval and then reading a PIN register, the signal level may be read incorrectly as the AVR may need 1 more internal clock to propagate the PIN value through the synchronizer.

--- bill

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Bill:
Let's go back to the first paragraph of your original post

Quote:
My understanding from looking at the datasheets is that the contents of the port register controls the pullup resistors whenever a pin is set to be an input.
and look at a different interpretation of the datasheet.

Here's what's in the datasheet for the ATmega328P, I'm sure the others are similar:

Quote:
If PORTxn is written logic one when the pin is configured as an input pin, the pull-up resistor is activated.

If the 'when' is changed to 'while' (perhaps by a different translator) you come up with a whole different interpretation. It means that what is written to the port while it is configured as an output will have no effect on the pull-ups when the port is subsequently reconfigured as an input. The pull-up status will be affected only when data is written to the port while it is configured as an input.

Don

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
The pull-up status will be affected only when data is written to the port while it is configured as an input.

But that is a wholly incorrect interpretation. It is unimportant whether the PORTx is written before or after the DDRx is set to input. This is very clear if the whole section on the port registers is read.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As far as I know, the LCD driver chips have active outputs. So if you are reading the LCD memory or checking the BUSY flag, there is no need to activate any internal pull-ups.

Regarding port reading with or without pull-ups. Surely if you change the DDR bit and follow with an immediate read, you have to allow time for any external or internal pull-up to charge any wiring capacitance.

A 10k pull-up with 20pF has a time constant of 200ns. An internal pull-up of say 100k could give you a 2us risetime.

So this could be an issue with some devices. But as far as I know the HD44780 is specified for 500ns and presumably its output drivers are active and will cope.

Even so. Bill is clearly worried about nanosecond timing. I can only guess that he wins all those fairground games that involve quick reactions.

David.