c - 使用 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 数据表上的这个初始化演练有点困惑:
然后后面的解释进一步解释了如何将其初始化为 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 电源。
解决方案
我想在讨论 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 个步骤
- 传输高 4 位数据。
- 忙碌标志检查 (BF=1)
- 忙碌标志检查 (BF=0)
- 传输低 4 位数据
使用 HD44780U 数据表示例进行测试(数据表P.42)
第6步,需要分2次发送。
它会在 LCD 上显示“H”。