AVR Atmega168 I2C LCD 不想初始化

Posted

技术标签:

【中文标题】AVR Atmega168 I2C LCD 不想初始化【英文标题】:AVR Atmega168 I2C LCD does not want to initialize 【发布时间】:2019-11-25 20:58:53 【问题描述】:

我正在使用 I2C 转换器将数据发送到我的液晶显示器。 转换器基于PCF85741,lcd为日立hd44780。

PCF85741与lcd的端口映射如下:

P0->RS

P1 -> RW

P2 -> E

P3 -> ?

P4 -> D4

P5 -> D5

P6 -> D6

P7 -> D7

文档说我的从属设备的默认地址是 0x20,但是使用 RW 位我需要发送 0x40。

这是我的代码:

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();

液晶初始化

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);

在这段代码之后,lcd 应该是 4bit 模式,有 2 行,但不是 液晶显示器没有任何变化。

【问题讨论】:

【参考方案1】:

1) 请说明您拥有的 I2C 转并行 IC 是什么?我找不到 PCF85741 是什么,我只看到 PCF8574 and PCF8574A 的数据表。

在第一种情况下,从机地址将是(包括 r/w 位)0x40...0x4F,在第二种情况下 - 0x70...0x7F。

根据我的经验,这些显示器随附的常用 I2C 电路上有 PCF8574A(即地址为 0x7*)。顺便说一下,那里的针脚 3 是用来控制背光的。

2) 确定地址的低位是多少?输入 A0 A1 A2 是上拉还是接地?

同样,根据我的经验,这些电路板通常具有 +5 的上拉电阻,电路板上有可焊接的跳线,可以将它们短接到地。默认情况下,跳线没有焊接,因此A0 A1 A2为高逻辑电平,因此设备的I2C地址为0x7E(写)/0x7F(读)。如果板上有 PCF8574T,那么地址将为 0x4E/0x4F

您可以通过检查 TWSR 寄存器中的 TWS 位 (TWSR &amp; TW_STATUS_MASK) 在发送地址后是否等于 TW_MT_SLA_ACK (0x18) 或在数据后检查 TW_MT_DATA_ACK (0x28) 来轻松检测 IC 是否应答字节被传输。 (请参阅the datasheet 部分 19.8.1 主发射器模式 第 186-188 页)

或者,更简单的说,如果你有PCF8574的P3连接到背光,你可以尝试输出0x08/0x00,看看背光是否开启和关闭。

3)有关初始化序列,请参见HD44780 datasheet 第 46 页上的 图 24 4 位接口 注意位 DB5 和 DB4 为高。另外,请注意,由于您只是在写入显示控制器,因此位 R/W(即输出的位 1)应始终为零(注意您正在发送 wi_write_byte(0x03);然后将位 1 的twi_write_byte(0x02); 设置为高)。

例子可能如下:

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

【讨论】:

谢谢AterLux,您对地址的建议是正确的。如果我将 0x4E 作为 SLA+W 发送,则 TWSR 中的状态为 0x18,这意味着 SLA+W 已发送,并且 ACK 已收到。发送任何其他 SLA+W 字节在 TWSR 中返回 0x20 状态,因此地址 0x4E 是正确的。但是当我发送下一个字节(0x08 应该设置背光)时,写入功能卡住了。请看一下这段代码。 pastebin.com/rrahhtec 你可以看到 led_on() 函数永远不会被调用,因为写循环永远不会停止。 @bielu000 我在您的代码中没有看到 TWBR 初始化。默认情况下,它的值为零,在 16MHz CPU 上给出 1MHz I2C 频率(请参阅数据表的 22.5.2 比特率发生器单元部分)。您必须将 I2C 速度限制在最大 100kHz。因此,对于 16MHz CPU,将 TWBR 设置为 72,对于 8MHz CPU,设置为 32。 你完全正确。我完全忘记了比特率发生器。就我而言,我将 TWBR 设置为 52,因为我的 CPU 速度是 12 Mhz。谢谢。

以上是关于AVR Atmega168 I2C LCD 不想初始化的主要内容,如果未能解决你的问题,请参考以下文章

使用 AVR TWI 接口的问题

Linux / Windows Arduino IDE 上的 avr-gcc Atmel AVR 微控制器

USART 传输 - AVR (atmega169p) 接收 0 或 null 而不是 char 'a'

一个软硬件开源的低功耗LCD时钟

avr-gcc atmega164pa 错误端口未声明

AVR ATmega在主循环之前使用printf时保持重置