This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

[参考译文] TM4C1230H6PM:使用 PCF8574T I2C I/O 扩展器将数据发送到 HD4478 LCD

Guru**** 2527710 points


请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

https://e2e.ti.com/support/microcontrollers/arm-based-microcontrollers-group/arm-based-microcontrollers/f/arm-based-microcontrollers-forum/1061384/tm4c1230h6pm-sending-data-using-a-pcf8574t-i2c-i-o-expander-to-an-hd4478-lcd

器件型号:TM4C1230H6PM

大家好、我想向具有 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

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    Elliott、您好!

    [引用 userid="504098" URL"~/support/microcontrollers/arm-based-microcontrollers-group/arm-based-microcontrollers/f/arm-based-microcontrollers-forum/1061384/tm4c1230h6pm-sending-data-using-a-pcf8574t-i2c-i-o-expander-to-an-hd4478-lcd ]I 遇到的第一个奇怪问题是初始化相应的 GPIO_Porte 引脚(4/5)作为 I2C2模块的 I2C 引脚。 我必须手动将 HWREG 宏与 GPIO_PUR 寄存器一起使用、以初始化两个引脚的标准弱上拉电阻。 我尝试将 GPIOPadConfigSet()与 GPIO_PIN_TYPE_STD_WPU 一起使用,我认为这完全一样,但我发现了不同的行为。 调用该函 数不会在我开始传输之前将我的时钟和数据线设置为高电平、也不会发送任何内容(我通过发送0x00来测试此情况、该0x00会关闭背光)。 将 HWREG 与 GPIO_O_PUR 一起使用实际上会在传输前将数据和时钟线路设置为高电平、而通过这种方式发送0x00实际上会关闭背光:

    这里可能发生的情况是 I2C GPIO 配置更改了您的设置。  

    为什么要像这样设置引脚? 它应该只是开漏、因为这就是 GPIO I2C Config 函数正确配置 I/O 的方式:

    GPIOPadConfigSet(ui32Port, ui8Pins, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_OD);

    您是否尝试使用 I2C 总线的内部弱上拉电阻器替代板载上拉电阻器? 如果这样做不起作用、则需要使用外部上拉电阻器的开漏模式。

    [引用 userid="504098" URL"~/support/microcontrollers/arm-based-microcontrollers-group/arm-based-microcontrollers/f/arm-based-microcontrollers-forum/1061384/tm4c1230h6pm-sending-data-using-a-pcf8574t-i2c-i-o-expander-to-an-hd4478-lcd ]我最困惑的地方是如何设置发送到 LCD 的数据的格式、我认为可能出错。 很遗憾,我没有示波器或逻辑分析仪,但我知道,基于我可以传输0x00的事实,它将关闭背光。*某些东西*必须能够越过线路。

    整个细分市场并不特定于我们的 TM4C 微控制器、它超出了我们提供的支持范围。 我对 I2C 扩展器或 LCD 屏幕一无所知。 我们无法帮助解决特定于应用的问题、例如如何根据外部器件的协议正确控制这些器件。 不过、社区成员可能会有所帮助。

    我 强烈建议您获得一个示波器或逻辑分析仪、这实际上是有效解决此问题的唯一方法、否则您将盲目猜测、并希望您偶然发现正确的过程。 如果您确实对如何使 I2C 总线以您所需的方式运行有疑问(即、您可以告诉我您需要将什么视为输出) 我仍然需要示波器/逻辑分析仪快照来查看输出的内容、而不是您所需的结果、以便能够以任何方式提供帮助。

    此致、

    Ralph Jacobi

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好、Ralph、感谢您的回答。 我希望在下周左右进入逻辑分析仪、这将使调试这一过程变得无比轻松。

    为什么不能使用开漏配置的内部上拉电阻器?

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好、Elliott、

    在几乎所有情况下、它们都不足以驱动 I2C 总线。 因此命名为"弱上拉"。 通常、您需要小于10kOhm 的上拉电阻器和典型值。 内部上拉电阻为20kOhm -除非总线电容极低(这种情况非常罕见)、否则 I2C 总线的上拉电阻太弱。

    下面是一个包含一些详细信息的应用手册: https://www.ti.com/lit/slva689

    通常、我会看到使用的上拉电阻介于1.8k Ω 至4.7k Ω 之间。

    此致、

    Ralph Jacobi

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    你好,Ralph,很抱歉我的回复很晚了。 但是、我对线路上拉电阻器的应用没有经验。 这是否可以通过将 I2C SCL/SDA 线路从主器件连接到试验电路板、然后使用一个电阻器将线路从主器件连接到进入 LCD 的 SDA/SCL 线路来实现?

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    Elliot、您好!

    您可以将电阻器的一端连接到 I2C 信号节点、将另一端连接到3.3V。

    本应用手册应详细说明需要它的原因: https://www.ti.com/lit/an/slva704/slva704.pdf

    此致、

    Ralph Jacobi