f3discovery:尝试将液晶屏 1602 与 I2C 模块一起使用
f3discovery: trying to use an lcd screen 1602 with an I2C module
我正在尝试在我的 stm32F3discovery 上使用 lcd 屏幕。
屏幕由2行16字液晶和I2C模块组成。
这是产品的link:
https://www.aliexpress.com/item/32763867041.html?spm=a2g0s.9042311.0.0.27424c4dsV7dLS
在屏幕背面我可以看到写着:QAPASS 1602A
在I2C模块的芯片上我可以看到写着:PCF8574T
芯片数据表如下:
https://www.nxp.com/docs/en/data-sheet/PCF8574_PCF8574A.pdf
我试着按照这个教程(最接近我正在尝试做的):
https://www.youtube.com/watch?v=1COFk1M2tak
我用的是HAL库,发送数据的主要功能是"HAL_I2C_Master_Transmit"。
这里是"HAL_I2C_Master_Transmit"中函数的描述:
- @brief 在主模式下以阻塞模式传输一定量的数据。
- @param hi2c 指向包含指定 I2C 配置信息的 I2C_HandleTypeDef 结构的指针。
- @param DevAddress 目标设备地址:datasheet中的设备7位地址值在调用接口前必须左移
- @param pData 指向数据缓冲区的指针
- @param Size 要发送的数据量
- @param Timeout 超时时间
- @retval HAL 状态
HAL_StatusTypeDef HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t 大小, uint32_t超时)
//I initialise a buffer
//I use a for loop to find the address of my slave: 0x3D (even tho on the //datasheet it's 0x3F, looks like A1 is bridged :O )
//I use the HAL_I2C_Master_Transmit function
//I move the address one bit to the left
//I reuse the HAL_I2C_Master_Transmit
//Nothing happens on the screen
//Here is my code (I tried to remove the useless comments):
#include "main.h"
I2C_HandleTypeDef hi2c1; // Init generated bu CubeMX
SPI_HandleTypeDef hspi1; // Init generated bu CubeMX
PCD_HandleTypeDef hpcd_USB_FS; // Init generated bu CubeMX
uint16_t adresseLCD; // the variable I put the slave address on
uint8_t buffer[]="123"; // The buffer I wanna see on the screen
void SystemClock_Config(void); // Generated by CubeMX
static void MX_GPIO_Init(void); // Generated by CubeMX
static void MX_I2C1_Init(void); // Generated by CubeMX
static void MX_SPI1_Init(void); // Generated by CubeMX
static void MX_USB_PCD_Init(void); // Generated by CubeMX
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_I2C1_Init();
MX_SPI1_Init();
MX_USB_PCD_Init();
adresseLCD=0x3D;
HAL_I2C_Master_Transmit(&hi2c1, adresseLCD, buffer, 1, 1000);
adresseLCD=adresseLCD*2; // Yes I could have used "adresseLCD<<1" but I
//am not used to that
HAL_I2C_Master_Transmit(&hi2c1, adresseLCD, buffer, 1, 1000);
while(1)
{
}
}
我希望屏幕上显示一些内容(甚至是随机值),但什么也没有出现(尽管它亮了)。
我没有收到任何错误(只有警告,因为我在 WritePIn 时使用“1”而不是 "Pin_ON")
你想错了。
首先是焊在背面的I2C模块,它只是简单的serial-to并行转换器。当您在 I2C 总线上写入数据时,它会根据数据字节中接收到的 8 位设置其 8 个输出。当您读取数据字节时,它会切换到输入模式并读取 8 个引脚上的逻辑电平并通过串行线传输。您可以在您提供的 datasheet on PCF8574 中阅读更详细的描述。
即这部分不执行任何 "magic" ,它将在输入时获取字符并在显示时输出它们。也就是说输出随机数据是徒劳的
你还需要知道两件事:
- 此模块如何连接到显示驱动程序
- 使用什么样的显示驱动程序以及如何使用它。
第一个问题的答案可以在网上找到:
(taken from here)
您可以看到显示器以 4 位模式连接,输出 P4-P7(即您在数据字节的四个最高有效位中传输的内容)连接到数据显示线,而输出 P0-P2 连接到控制线 RS,R/W、EH和P3用于控制背光。
知道了这一点我们就来到了第二个问题。我只能建议,但更有可能你的显示模块上有 Hitachi HD44780 IC。在 datasheet 中,您可以找到控制线上应输出哪些数据的信息。
您可以在第 8 页找到引脚说明:
- bit RS 选择命令 (0) 或数据 (1)
- bit R/W 选择写入操作 (0) 或读取 (1)
- 和位E实际上是一个频闪。在下降沿,即从 1 变为 0 时,显示驱动器从数据线上读取数据。这意味着要传递 4 位数据,您应该执行 2 次写入操作:第一个位 2 设置为高,第二个所有其他位都相同,但位 2 为零。
现在您可以阅读数据表第 25 页的指令列表。以及第 46 页的 4 位模式的初始化序列(图 24)。请注意,对于每一行位,您实际上发送了 2 个数据字节:第 2 位高,然后第 2 位低。
注意,在 4 位模式下,所有命令和数据都包含两个写入阶段:第一个字节的上半部分,然后是字节的下半部分。每个阶段是2个数据写入I2C模块,E位高低,即需要发送4个字节才能输出1个字节的数据。
所以基本上,为了将数据从 STM32 传输到 LCD 显示驱动器 (HD44780),您需要通过 I2C 接口芯片 (PCF8574) 模拟后者的接口信号。
换句话说,MCU 将发送 I2C 命令来切换 I2C "backpack" 芯片,使其模拟 LCD 驱动器的 right 信号。
当您使用 HAL_I2C_Master_*() 方法时,这在某种程度上很容易发生。在缓冲区数组中,您可以根据需要指定 LCD 上引脚的状态,并按 [0]、[1]、[2]... 等顺序指定。例如,假设我们有 DB[7: 4] 连接到 PCF I2C 扩展器的高 4 位。我们可以设置以下内容:
buffer[0] = 0xD0 ; // 0b11010000;
buffer[1] = 0xA0 ; // 0b10100000;
buffer[2] = 0xF0 ; // 0b11110000;
HAL_I2C_Master_Transmit(&hi2c1, adresseLCD, buffer, 3, 10); // Note 3 bytes are sent
准备好缓冲区后,HAL_I2C_Master_Transmit() 调用将连续发送树字节,从而如您所述切换 DB 引脚:
DB7: 1 -> 1 -> 1
DB6: 1 -> 0 -> 1
DB5: 0 -> 1 -> 1
DB4: 1 -> 0 -> 1
同样适用于PCF芯片上的所有8个引脚。还值得注意的是,这种顺序 IO 更新还减少了一点 I2C 通信开销,因为它只对 I2C 从属芯片寻址一次:)。
顺便说一下,我目前正在为 STM32F1xx 开发基于 I2C 库的 LCD(可能它适用于其他 STM32F 系列)。您可以在 github 上查看(仍在开发中):
https://github.com/meteowrite/stm32_i2cLcd
干杯
我正在尝试在我的 stm32F3discovery 上使用 lcd 屏幕。 屏幕由2行16字液晶和I2C模块组成。
这是产品的link: https://www.aliexpress.com/item/32763867041.html?spm=a2g0s.9042311.0.0.27424c4dsV7dLS
在屏幕背面我可以看到写着:QAPASS 1602A 在I2C模块的芯片上我可以看到写着:PCF8574T
芯片数据表如下: https://www.nxp.com/docs/en/data-sheet/PCF8574_PCF8574A.pdf
我试着按照这个教程(最接近我正在尝试做的): https://www.youtube.com/watch?v=1COFk1M2tak
我用的是HAL库,发送数据的主要功能是"HAL_I2C_Master_Transmit"。
这里是"HAL_I2C_Master_Transmit"中函数的描述:
- @brief 在主模式下以阻塞模式传输一定量的数据。
- @param hi2c 指向包含指定 I2C 配置信息的 I2C_HandleTypeDef 结构的指针。
- @param DevAddress 目标设备地址:datasheet中的设备7位地址值在调用接口前必须左移
- @param pData 指向数据缓冲区的指针
- @param Size 要发送的数据量
- @param Timeout 超时时间
- @retval HAL 状态
HAL_StatusTypeDef HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t 大小, uint32_t超时)
//I initialise a buffer
//I use a for loop to find the address of my slave: 0x3D (even tho on the //datasheet it's 0x3F, looks like A1 is bridged :O )
//I use the HAL_I2C_Master_Transmit function
//I move the address one bit to the left
//I reuse the HAL_I2C_Master_Transmit
//Nothing happens on the screen
//Here is my code (I tried to remove the useless comments):
#include "main.h"
I2C_HandleTypeDef hi2c1; // Init generated bu CubeMX
SPI_HandleTypeDef hspi1; // Init generated bu CubeMX
PCD_HandleTypeDef hpcd_USB_FS; // Init generated bu CubeMX
uint16_t adresseLCD; // the variable I put the slave address on
uint8_t buffer[]="123"; // The buffer I wanna see on the screen
void SystemClock_Config(void); // Generated by CubeMX
static void MX_GPIO_Init(void); // Generated by CubeMX
static void MX_I2C1_Init(void); // Generated by CubeMX
static void MX_SPI1_Init(void); // Generated by CubeMX
static void MX_USB_PCD_Init(void); // Generated by CubeMX
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_I2C1_Init();
MX_SPI1_Init();
MX_USB_PCD_Init();
adresseLCD=0x3D;
HAL_I2C_Master_Transmit(&hi2c1, adresseLCD, buffer, 1, 1000);
adresseLCD=adresseLCD*2; // Yes I could have used "adresseLCD<<1" but I
//am not used to that
HAL_I2C_Master_Transmit(&hi2c1, adresseLCD, buffer, 1, 1000);
while(1)
{
}
}
我希望屏幕上显示一些内容(甚至是随机值),但什么也没有出现(尽管它亮了)。 我没有收到任何错误(只有警告,因为我在 WritePIn 时使用“1”而不是 "Pin_ON")
你想错了。
首先是焊在背面的I2C模块,它只是简单的serial-to并行转换器。当您在 I2C 总线上写入数据时,它会根据数据字节中接收到的 8 位设置其 8 个输出。当您读取数据字节时,它会切换到输入模式并读取 8 个引脚上的逻辑电平并通过串行线传输。您可以在您提供的 datasheet on PCF8574 中阅读更详细的描述。
即这部分不执行任何 "magic" ,它将在输入时获取字符并在显示时输出它们。也就是说输出随机数据是徒劳的
你还需要知道两件事:
- 此模块如何连接到显示驱动程序
- 使用什么样的显示驱动程序以及如何使用它。
第一个问题的答案可以在网上找到:
您可以看到显示器以 4 位模式连接,输出 P4-P7(即您在数据字节的四个最高有效位中传输的内容)连接到数据显示线,而输出 P0-P2 连接到控制线 RS,R/W、EH和P3用于控制背光。
知道了这一点我们就来到了第二个问题。我只能建议,但更有可能你的显示模块上有 Hitachi HD44780 IC。在 datasheet 中,您可以找到控制线上应输出哪些数据的信息。
您可以在第 8 页找到引脚说明:
- bit RS 选择命令 (0) 或数据 (1)
- bit R/W 选择写入操作 (0) 或读取 (1)
- 和位E实际上是一个频闪。在下降沿,即从 1 变为 0 时,显示驱动器从数据线上读取数据。这意味着要传递 4 位数据,您应该执行 2 次写入操作:第一个位 2 设置为高,第二个所有其他位都相同,但位 2 为零。
现在您可以阅读数据表第 25 页的指令列表。以及第 46 页的 4 位模式的初始化序列(图 24)。请注意,对于每一行位,您实际上发送了 2 个数据字节:第 2 位高,然后第 2 位低。
注意,在 4 位模式下,所有命令和数据都包含两个写入阶段:第一个字节的上半部分,然后是字节的下半部分。每个阶段是2个数据写入I2C模块,E位高低,即需要发送4个字节才能输出1个字节的数据。
所以基本上,为了将数据从 STM32 传输到 LCD 显示驱动器 (HD44780),您需要通过 I2C 接口芯片 (PCF8574) 模拟后者的接口信号。
换句话说,MCU 将发送 I2C 命令来切换 I2C "backpack" 芯片,使其模拟 LCD 驱动器的 right 信号。
当您使用 HAL_I2C_Master_*() 方法时,这在某种程度上很容易发生。在缓冲区数组中,您可以根据需要指定 LCD 上引脚的状态,并按 [0]、[1]、[2]... 等顺序指定。例如,假设我们有 DB[7: 4] 连接到 PCF I2C 扩展器的高 4 位。我们可以设置以下内容:
buffer[0] = 0xD0 ; // 0b11010000;
buffer[1] = 0xA0 ; // 0b10100000;
buffer[2] = 0xF0 ; // 0b11110000;
HAL_I2C_Master_Transmit(&hi2c1, adresseLCD, buffer, 3, 10); // Note 3 bytes are sent
准备好缓冲区后,HAL_I2C_Master_Transmit() 调用将连续发送树字节,从而如您所述切换 DB 引脚:
DB7: 1 -> 1 -> 1
DB6: 1 -> 0 -> 1
DB5: 0 -> 1 -> 1
DB4: 1 -> 0 -> 1
同样适用于PCF芯片上的所有8个引脚。还值得注意的是,这种顺序 IO 更新还减少了一点 I2C 通信开销,因为它只对 I2C 从属芯片寻址一次:)。
顺便说一下,我目前正在为 STM32F1xx 开发基于 I2C 库的 LCD(可能它适用于其他 STM32F 系列)。您可以在 github 上查看(仍在开发中):
https://github.com/meteowrite/stm32_i2cLcd
干杯