Search code examples
cavri2catmegalcd

AVR Atmega168 I2C LCD does not want to initialize


I'm using I2C converter to send data to my lcd. The converter is based on PCF85741, and the lcd is Hitachi hd44780.

Port mapping between PCF85741 and lcd is as follows:

P0 -> RS

P1 -> RW

P2 -> E

P3 -> ?

P4 -> D4

P5 -> D5

P6 -> D6

P7 -> D7

The documentation says that the default address of my slave is 0x20, but with RW bit I need to send 0x40.

Here is my code:

void twi_start()
{
    TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTA);

    while (!(TWCR & (1 << TWINT)));
}

void twi_stop()
{
    TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO);

    while (!(TWCR & (1 << TWSTO)));
}

void twi_write(uint8_t byte)
{
    TWDR = byte;

    TWCR = (1 << TWINT) | (1 << TWEN);

    while (!(TWCR & (1 << TWINT)));
}

void twi_write_byte(uint8_t byte)
{
    uint8_t SLAVE_ADDRESS = 0x40;


    twi_start();
    twi_write(SLAVE_ADDRESS);
    twi_write(byte);

    twi_stop();
}

Lcd init

void lcd_init2()
{
    for (int i = 0; i < 3; i++) {
        twi_write_byte(0x03);

        _delay_ms(20);
    }

    twi_write_byte(0x02);
    _delay_ms(20);

    //4 bit mode
    twi_write_byte(0x24); // D5 -> 1, E -> 1 
    _delay_ms(10);
    twi_write_byte(0x20); // D5 -> 1, E -> 0
    _delay_ms(10);

    //2 lines
    twi_write_byte(0x24); // D5 -> 1, E -> 1
    _delay_ms(10);
    twi_write_byte(0x20); // D5 -> 1, E -> 0 first nibble
    _delay_ms(10);

    twi_write_byte(0x84); // D7 -> 1, E -> 1
    _delay_ms(10);
    twi_write_byte(0x80); // D7 -> 1, E -> 0 second nibble 
    _delay_ms(10);
}

After this code, the lcd should be in 4bit mode, with 2 lines, but it isn't Nothing changes on lcd.


Solution

  • 1) Please clarify what the I2C-to-parallel IC you have? I cannot find what is PCF85741, I see datasheets only for PCF8574 and PCF8574A.

    In the first case, the slave address will be (including r/w bit) 0x40...0x4F, in the second - 0x70...0x7F.

    Speaking from my experience the usual I2C circuit which comes with those displays has PCF8574A on it (i.e. address is 0x7*). And pin 3 there, by the way, is used to control the backlight.

    2) Make sure what lower bits of the address you have? Are inputs A0 A1 A2 pulled up or tied to ground?

    Again, from my experience, those boards usually have pull-ups to +5, and solderable jumpers on the board, to short those to ground. By default, the jumpers are not soldered, therefore A0 A1 A2 has a high logical level, thus the I2C address of the device is 0x7E (writing)/0x7F (reading). If you have PCF8574T on board, then the address will be 0x4E/0x4F

    You can easily detect has the IC answered or not, by checking TWS bits in TWSR register (TWSR & TW_STATUS_MASK) should be equal to TW_MT_SLA_ACK (0x18) after the address is transmitted, or TW_MT_DATA_ACK (0x28) after the data byte is transmitted. (See the datasheet section 19.8.1 Master Transmitter Mode pages 186-188)

    Or, more simply, if you have P3 of the PCF8574 connected to the backlight, you can try output 0x08 / 0x00 to see if the backlight is turning on and off.

    3) For the initialization sequence look at Figure 24 4-Bit Interface on page 46 of HD44780 datasheet Note bits DB5 and DB4 are high. Also, be careful, since you're only writing to the display controller, the bit R/W (i.e. the bit 1 of the output) should always be zero (note you're sending wi_write_byte(0x03); and then twi_write_byte(0x02); with the bit 1 is set high).

    The example may be as follows:

    void send4bits(uint8_t fourbits, bool is_cmd) {
      uint8_t d = (fourbits << 4) | 0b1000;
      if (!is_cmd) d |= 1;
      twi_write_byte(d | 0b100); // E high
      twi_write_byte(d);         // E low
    }
    
    void sendcmd(uint8_t cmd) {
      send4bits(cmd >> 4, true);
      send4bits(cmd & 0xF, true);
    }
    
    void senddata(uint8_t cmd) {
      send4bits(cmd >> 4, false);
      send4bits(cmd & 0xF, false);
    }
    
    
    // initialization sequence
    
    send4bits(0b0011, true);
    _delay_ms(5);
    send4bits(0b0011, true);
    _delay_ms(1);
    send4bits(0b0011, true);
    // since I2C is slow enough the required 100us pause already happened here
    send4bits(0b0010, true);
    
    sendcmd(0b00101000);
    sendcmd(0b00001000);
    sendcmd(0b00000001);
    delay_ms(2);
    sendcmd(0b00000110);
    
    sendcmd(0b00001110);
    
    // Initialization is done
    
    sendcmd(0x80); // Set cursor at the beginning
    for (uint8_t i = 'A' ; i <= 'Z' ; i++) {
      senddata(i); // Send some random data
    }