首页 > 解决方案 > 使用 PCF8574T I2C 背包将数据从 TI TM4C123GH6PM 发送到 HD4478 LCD

问题描述

我正在尝试编写一个程序,使我能够将数据从我的 TI 微控制器发送到非常常见的 HD4478 LCD。与其使用并行引脚设置,我认为尝试使用串行设置是个好主意,因此 LCD 有一个 PCF8574T I2C I/O 扩展器背包。我对编写嵌入式程序相当陌生,这是我第一次使用任何真正的串行布线通信协议(I2C/SPI),所以我正在努力让它工作。在我解释我的困惑之前,这里是 3 个各自组件的数据表:

微控制器数据表:https ://www.ti.com/lit/ds/symlink/tm4c123gh6pm.pdf

LCD 数据表:https ://circuitdigest.com/sites/default/files/HD44 ..。

I/O 扩展器数据表:https ://www.nxp.com/docs/en/data-sheet/PCF8574_PCF ...

我的主要问题是,我真的不知道我做错了什么,但我假设它与我将 LCD 初始化为 4 位模式的方式有关。我对 LCD 数据表上的这个初始化演练有点困惑:

HD4478 初始化

然后后面的解释进一步解释了如何将其初始化为 4 位模式,这就是我将与我的 I/O 扩展器一起使用的:

在此处输入图像描述

我想我不太清楚是在 8 位模式还是 4 位模式下将初始函数 set Command 发送到 4 位模式,以及在初始化过程中何时准确切换到 4 位模式。

我通过数据线传输数据的方式是 4 位模式,所以我发送高半字节。然后降低半字节。这适用于数据字节和命令字节。下面是我的代码。目前闪烁并运行对LCD没有影响。我知道我在正确的从地址上运行,因为在初始从地址传输之后我没有得到任何指示任何错误的标志。但是,我在 I2C_LCD_Enable 中的所有命令和 main() 中试图向 LCD 发送单个字符的后一个命令也无效。

#include "TM4C123.h"   // Device header
#include "RTE_Components.h"             // Component selection

//data pin should be open drain in i2c modules


#define DATA 1

#define COMMAND 0

//I can think of each Pin connected to command/control as a singular bit


void GPIOE_enable(void);
void I2C_enable(void);

void I2C_LCD_enable(void);
void I2C_transfer_byte(char byte,int mode);

void delay_50_ms(void);

uint32_t address_transfer_value;
uint32_t data_value;

#define E 0x04 //bit 2

int main(){
    GPIOE_enable();
    I2C_enable();
    I2C_LCD_enable();
    I2C_transfer_byte(0x01,COMMAND);
    I2C_transfer_byte(0x80,COMMAND); //set cursor to first row
    delay_50_ms();
    I2C_transfer_byte('a',DATA);
}

//port E, pins pe4 and pe5, have the alternative function as acting the clock/data lines for I2C module 2
void GPIOE_enable(void){
    SYSCTL->RCGCGPIO |= 0x10; //enable port E
    GPIOE->DIR |= 0x10 | 0x20;
    GPIOE->DEN |= 0x10 | 0x20; //enable pins pe4 and pe5
    GPIOE->AFSEL = 0x10 | 0x20; //enable pins Pe4 and Pe5 for their alternate function
    GPIOE->ODR |= 0x20; //pe5 is data pin, must be set to open drain
    GPIOE->PCTL |= (3 << 16) | (3 << 20);
}

void I2C_enable(void){
    SYSCTL->RCGCI2C |= 0x04;
    I2C2->MCR |= 0x10; //initialize I2C master
    GPIOE->PUR |= 0x10 | 0x20; //I pulled up the Clock and Data Lines because they were low: Not pulling them up won't allow them to transfer from their high to low states when transmission begins and the I2C->MCS & 0x01 condition hangs forever
    I2C2->MTPR = 0x09; //see data sheet: This initializes SCL speed to 100k kbps
    I2C2->MSA = (0x27 << 1); //see data sheet: This sets slave address and sets mode to TRANSMIT
    I2C2->MCS |= 0x07;
    while (I2C2->MCS & 0x01);
}

//HD775 data sheet explains the initialization process on pages 24 and 42
void I2C_LCD_enable(void){

    //not sure how to initialize quite yet...
    /*I2C2->MDR = 0x28; //this initializes 4-bit mode. This command, AND THIS COMMAND ONLY, takes only one write since it's still in 8-bit mode
    I2C2->MCS |= 0x07;
    while (I2C2->MCS & 0x01);
    delay_50_ms();
    I2C2->MDR = (1 << E); //set ENABLE to high
    I2C2->MCS |= 0x07;
    while (I2C2->MCS & 0x01);
    delay_50_ms();
    I2C2->MDR = ~(1<<E); //set ENABLE to low
    I2C2->MCS |= 0x07;
    while (I2C2->MCS & 0x01);
    delay_50_ms();*/
    I2C_transfer_byte(0x28,COMMAND);
    I2C_transfer_byte(0x06,COMMAND); //Move cursor right
    I2C_transfer_byte(0x01,COMMAND); //clear screen
    I2C_transfer_byte(0x0F,COMMAND); //turn display on
}
    
    
    

//the upper 4 bits are the data pins : D4, D5, D6, D7 (bits 0-3)
//the lower 3 bits are: RS, RW, E
//to send a command, we should set RS to 0 to select "Command Mode" on LCD
//if mode is 0, or COMMAND, do a logical OR with 0, which will set RS, bit 0, to 0
//if mode is 1, or DATA, do a logical or with 1, so RS, bit 0, is set to 1
//we also need to pulse E, or enable to make sure any of these data/commands actually are executed
//The E pin corresponds to bit 2, so I'll send each data byte with first E enabled, and then E set to to low, to pulse Enable
//send upper nibble, pulse enable, send lower nibble, pulse enable
void I2C_transfer_byte(char byte,int mode){
            
    char byte_shifted;
    char byte_upper_nibble;
    char byte_lower_nibble;
    byte_shifted = byte << 4;
    byte_upper_nibble = byte & 0xF0;        
    I2C2->MDR = (mode | (I2C2->MDR & 0xF0)| byte_upper_nibble) | E; //set command to be most significant bit, and enable E
    I2C2->MCS |= 0x07;
    while (I2C2->MCS & 0x01){
        data_value = I2C2->MBMON;
    }
    delay_50_ms();
    I2C2->MDR = (mode | (I2C2->MDR & 0xF0)| byte_upper_nibble) & ~E; //set command to be most significant bit, and disable E (pulsing E enables the command/data)
    I2C2->MCS |= 0x07;
    while (I2C2->MCS & 0x01);
    delay_50_ms();
    byte_lower_nibble = byte_shifted & 0xF0;
    I2C2->MDR = (mode | (I2C2->MDR & 0x0F) | byte_lower_nibble) | E;
    I2C2->MCS |= 0x07;
    while (I2C2->MCS & 0x01);
    delay_50_ms();
    I2C2->MDR = (mode | (I2C2->MDR & 0x0F) | byte_lower_nibble) & ~E;
    I2C2->MCS |= 0x07;
    while (I2C2->MCS & 0x01);
    delay_50_ms();
}



//clock frequency is 1,000,000 cycles/second
void delay_50_ms(void){
    SYSCTL->RCGCTIMER |= 0x01;
    TIMER0->CTL = 0;
    TIMER0->CFG |= 0x04;
    TIMER0->TAMR |= 0x01 | 0x10; //single shot mode, enable interrupt
    TIMER0->TAILR = 20000; //1,000,000 / 20,0000 = 50
    TIMER0->CTL = 0x01;
    while ((TIMER0->RIS & 0x01) == 0);
}
    

我正在使用 TI 板上的板载 3.3V 电源作为我的 VCC 电源。

标签: cembeddedmicrocontrolleri2clcd

解决方案


我想在讨论 LCD 无法正常工作之前,我想知道 I2C 是否正常工作以及地址 PIN 是否定义。

I/O Expander IC 的地址管脚都连接到HIGH,所以你的 Slave 地址是0x27

(我只是想确定这部分是否可以,因为如果出现问题,后续测试也会出现问题)

在此处输入图像描述

如果 I2C 通讯工作正常,则 I/O Expander IC 控制应该是正确的。

(发送一些命令,看看IC是否按照你的命令输出)

如果 I/O 扩展 IC 工作异常,可以通过逻辑分析仪或示波器检查 I2C 信号,检查 MCU 的 I2C 信号是否正确。(从机地址,ACK ...等)

如果以上都正确,我们就可以开始检查液晶部分了!

提醒:您的 LCD 和 I/O 扩展器 IC 数据表的超链接失败。

内部复位电路初始化部分

表示当您给 LCD 供电时,它会执行以下 1~4 的动作。

在此处输入图像描述

在此期间,您可以使用示波器或逻辑分析仪测量 BF 引脚。它应该是高的,总是当你的 VCC 上升到 4.5V 时,BF 会在拉低之前再持续 10ms。

但是如果VCC没有升到4.5V,就必须通过MPU进行初始化。

我在文章最后看到了

我正在使用 TI 板上的板载 3.3V 电源作为我的 VCC 电源。

您的液晶显示器是否使用 3.3V 作为电源?如果是,则由 MPU 初始化 LCD。(但我建议使用 5V 供电,可以减少调试时间。)

如果使用内部初始化功能,LCD 设置如下:

  • 8位接口
  • 1行显示
  • 5x8 点字符字体

所以如果要切换到4-bit模式,需要使用Function set指令重新设置。(数据表P.28)

信号传输部分必须按照时序图进行测试

发送命令需要 4 个步骤

  1. 传输高 4 位数据。
  2. 忙碌标志检查 (BF=1)
  3. 忙碌标志检查 (BF=0)
  4. 传输低 4 位数据

在此处输入图像描述

使用 HD44780U 数据表示例进行测试(数据表P.42)

第6步,需要分2次发送。

在此处输入图像描述

它会在 LCD 上显示“H”。

在此处输入图像描述


推荐阅读