大家好、我想向具有 I2C 背包的 HD4478 LCD 屏幕发送一些字符。 LCD 本身连接到 Arduino 提供的5V 电源。 我正在尝试使用内部复位电路在 LCD 上执行初始化、因此我可以将其所有操作设置为4bt 模式、因为 I2C 扩展器会将可以立即发送的数据位数从8缩减为4。 以下是 LCD 数据表中描述该过程的图像:

此外、我正在尝试按照使用内部复位电路的说明将模式设置为4位模式、其中包含2行用于字符的空格、以及5 x 10字体、其中闪烁光标从屏幕左侧进入。 以下是描述该过程的数据表页面:

问题当前是、当我尝试按照所述的顺序执行这些指令时、我在 LCD 上看不到任何内容、只有它的背光处于打开状态。
我遇到的第一个奇怪的问题是、初始化相应的 GPIO_Porte 引脚(4/5)以用作 I2C2模块的 I2C 引脚。 我必须手动将 HWREG 宏与 GPIO_PUR 寄存器一起使用、以初始化两个引脚的标准弱上拉电阻。 我尝试将 GPIOPadConfigSet()与 GPIO_PIN_TYPE_STD_WPU 一起使用,我认为这完全一样,但我发现了不同的行为。 调用该函 数不会在我开始传输之前将我的时钟和数据线设置为高电平、也不会发送任何内容(我通过发送0x00来测试此情况、该0x00会关闭背光)。 将 HWREG 与 GPIO_O_PUR 一起使用实际上会在传输前将数据和时钟线路设置为高电平、通过这种方式发送0x00实际上会关闭背光:
void GPIOE_enable()
{
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);
GPIOPinConfigure(GPIO_PE4_I2C2SCL);
GPIOPinConfigure(GPIO_PE5_I2C2SDA);
GPIOPinTypeI2C(GPIO_PORTE_BASE,GPIO_PIN_5);
GPIOPinTypeI2CSCL(GPIO_PORTE_BASE,GPIO_PIN_4);
HWREG(GPIO_PORTE_BASE + GPIO_O_PUR) = (HWREG(GPIO_PORTE_BASE + GPIO_O_PUR) | GPIO_PIN_4 | GPIO_PIN_5);
}
我在这里初始化了 I2C2模块:
void I2C_enable()
{
SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C2);
SysCtlClockSet(SYSCTL_SYSDIV_5|SYSCTL_USE_PLL|SYSCTL_XTAL_16MHZ|SYSCTL_OSC_MAIN);
I2CMasterEnable(I2C2_BASE);
I2CMasterInitExpClk(I2C2_BASE,SysCtlClockGet(),false);
I2CMasterSlaveAddrSet(I2C2_BASE,0x27,false);
}
我感到困惑的最大部分、以及我认为可能出错的地方、是如何格式化我发送到 LCD 的数据。 不幸的是,我没有示波器或逻辑分析仪,但我知道,基于我可以传输0x00的事实,它将关闭背光*某些东西*必须能够越过线路。 由于我使用的是 I2C I/O 扩展器、因此我严格要求一次仅发送4位。 对于 HD4478 LCD、有一个 EN (使能)引脚。 脉冲此操作(发送 EN 为高电平的数据/命令字节、然后再次发送 EN 为低电平)将导致 HD4478 "锁定"此数据/命令字节并实际执行它。 但是、现在我将一次发送每个字节的高半字节和低半字节。 这使我感到困惑-如果我发送一个上半字节和下半字节、我何时启用脉冲? 目前、我已对其进行配置、使我对高半字节的 EN 进行脉冲、然后对我发送的每个字节的低半字节的 EN 进行脉冲。 这意味着现在、我的代码中的每个字节都需要4个传输-上部半字节 E 为高电平、上部半字节 E 为低电平、下部半字节 E 为高电平、而下部半字节 E 为低电平。 这似乎是不正确的、但我不确定正确的方法。 我不确定的另一部分是 burst_send_start/cont/stop 的用法。 每次我发送代表单个字节的4个传输时、E 高电平的上半字节(第一次传输)将 I2CMasterControl 的参数2设置为 I2C2_MASTER_CMD_BURST_SEND_START、最后一次传输将低电平的下半字节设置为 I2C_MASTER_CMD_BURST_SEND_STOP
以下是描述此传输方法的方法:
//send upper 4 bits of command with E set to High, and with backlight on
//Since this is the beginning of the new command, parameter 2 of I2CMasterControl is set to "BURST_SEND_START"
void expander_write_upper_nibble(uint8_t data)
{
I2CMasterDataPut(I2C2_BASE,PULSE_HIGH);
I2CMasterControl(I2C2_BASE,I2C_MASTER_CMD_BURST_SEND_START);
while (I2CMasterBusy(I2C2_BASE) & I2CMasterBusBusy(I2C2_BASE));
}
//send lower 4 bits of command with E set to High, and with backlight on
//Since this is the beginning of the new command, parameter 2 of I2CMasterControl is set to "BURST_SEND_CONT"
void expander_write_lower_nibble(uint8_t data)
{
I2CMasterDataPut(I2C2_BASE,PULSE_HIGH);
I2CMasterControl(I2C2_BASE,I2C_MASTER_CMD_BURST_SEND_CONT);
while (I2CMasterBusy(I2C2_BASE) & I2CMasterBusBusy(I2C2_BASE));
}
//send command with E set to low and with back light on
void pulse_enable_upper_nibble(uint8_t data)
{
I2CMasterDataPut(I2C2_BASE,PULSE_LOW);
I2CMasterControl(I2C2_BASE,I2C_MASTER_CMD_BURST_SEND_CONT);
}
//after sending the lower nibble with the EN high, send again with EN low (pulsing enable)
//this is the final data we should be sending to the shift register. We can pass the send state as
// I2C2_MASTER_CMD_BURST_SEND_STOP
void pulse_enable_lower_nibble(uint8_t data)
{
I2CMasterDataPut(I2C2_BASE,PULSE_LOW);
I2CMasterControl(I2C2_BASE,I2C_MASTER_CMD_BURST_SEND_STOP);
}
//send upper 4 bits w/E high and then send w/ E low
void I2C_write_upper_nibble(uint8_t byte)
{
expander_write_upper_nibble(byte);
pulse_enable_upper_nibble(byte);
}
//write lower nibble pulse enable
void I2C_write_lower_nibble(uint8_t byte)
{
expander_write_lower_nibble(byte);
pulse_enable_lower_nibble(byte);
}
//write upper 4 bits + pulse, write lower 4 bits + pulse E
void send(uint8_t data, int mode)
{
uint8_t byte_shifted;
uint8_t byte_upper_nibble;
uint8_t byte_lower_nibble;
byte_shifted = data << 4;
byte_upper_nibble = data & 0xF0;
byte_lower_nibble = byte_shifted & 0xF0;
I2C_write_upper_nibble(byte_upper_nibble | mode);
I2C_write_lower_nibble(byte_lower_nibble | mode);
}
//RS set 0
void command(uint8_t data)
{
send(data,0);
}
//RS set to 1
void data(uint8_t data)
{
send(data,1);
}
最后、这是我的 LCD_init 方法、尝试按照内部复位电路的指令进行操作
/*initializationCommands[0] = 4 bit mode / 2 Lines / 5 x 10 dot display
[1] = cursor on / display on / blink on (0x08 + 0x04 + 0x02 + 0x01)
[2] = Enter cursor left
*/
void I2C_LCD_init()
{ uint8_t i;
uint8_t initializationCommands[3] = {0x30,0xE,0x06};
//12.5 ms delay
delay();
I2CMasterDataPut(I2C2_BASE,0x20 | E | LCD_BACKLIGHT);
while (I2CMasterBusy(I2C2_BASE) & I2CMasterBusBusy(I2C2_BASE));
I2CMasterDataPut(I2C2_BASE,(0x20 & ~ E) | LCD_BACKLIGHT);
I2CMasterControl(I2C2_BASE,I2C_MASTER_CMD_BURST_SEND_STOP);
while (I2CMasterBusy(I2C2_BASE) & I2CMasterBusBusy(I2C2_BASE));
for(i = 0; i<3; i++) {
send(initializationCommands[i],0);
}
}
最后、为了更清楚地说明、可以发送到 HD4478 LCD 的命令的定义:
#define LCD_CLEARDISPLAY 0x01 #define LCD_RETURNHOME 0x02 #define LCD_ENTRYMODESET 0x04 #define LCD_DISPLAYCONTROL 0x08 #define LCD_CURSORSHIFT 0x10 #define LCD_FUNCTIONSET 0x20 #define LCD_SETCGRAMADDR 0x40 #define LCD_SETDDRAMADDR 0x80 // flags for display entry mode #define LCD_ENTRYRIGHT 0x00 #define LCD_ENTRYLEFT 0x02 #define LCD_ENTRYSHIFTINCREMENT 0x01 #define LCD_ENTRYSHIFTDECREMENT 0x00 // flags for display on/off control #define LCD_DISPLAYON 0x04 #define LCD_DISPLAYOFF 0x00 #define LCD_CURSORON 0x02 #define LCD_CURSOROFF 0x00 #define LCD_BLINKON 0x01 #define LCD_BLINKOFF 0x00 // flags for display/cursor shift #define LCD_DISPLAYMOVE 0x08 #define LCD_CURSORMOVE 0x00 #define LCD_MOVERIGHT 0x04 #define LCD_MOVELEFT 0x00 // flags for function set #define LCD_8BITMODE 0x10 #define LCD_4BITMODE 0x00 #define LCD_2LINE 0x08 #define LCD_1LINE 0x00 #define LCD_5x10DOTS 0x04 #define LCD_5x8DOTS 0x00 // flags for backlight control #define LCD_BACKLIGHT 0x08 #define LCD_NOBACKLIGHT 0x00