哈Ha!在本文中,我想谈谈我使用I2C总线上的HAL库将LCD显示器连接到STM32微控制器的经验。
我将连接显示器1602和2004。它们都具有基于PCF8574T芯片的焊接I2C适配器。调试板为Nucleo767ZI,开发环境为STM32CubeIDE 1.3.0。我不会详细讨论I2C总线的工作原理,建议您在这里和这里查看。我们创建项目,选择调试板:我们
指示我们将使用I2C1。我还将连接UART5以与开发板通信,这对于从开发板接收有关显示地址的信息是必需的。
在同一窗口中,您可以看到与显示器连接的支脚的编号,在我的情况下,结果是这样的:
要启动,仅连接一个显示器,我将从1602开始。此外,我将连接经验丰富的arduin驱动程序已知的USB-UART CH340适配器,以从板上接收数据。
请注意,适配器将RX连接到TX,将TX连接到RX,适配器上的跳线为3.3V
让我们仔细研究一下PCF8574T芯片和显示器。下面是带有显示屏的模块的示意图:
PCF8574T芯片的功能类似于移位寄存器74hc595-它通过I2C接口接收字节并将相应位的值分配给其输出(P0-P7)。考虑一下微电路的哪些引脚连接到显示器以及它们负责什么:- 微电路的端子P0连接到显示器的RS端子,该RS端子负责接收显示数据(1)或显示操作指令(0);
- 如果1-将数据写入显示器,0-读取,则引脚P1连接到R \ W。
- 端子P2连接到CS-端子,正在读取状态变化;
- 结论P3-背光控制;
- 结论P4-P7用于将数据传输到显示器。
多个设备可以同时连接到一条I2C总线。为了能够访问特定的设备,每个设备都有其自己的地址,首先我们将发现它们。如果转接板上的触点A1,A2和A3没有密封,则地址很可能为0x27,但最好检查一下。为此,我们将编写一个小的函数,该函数将显示连接到I2C总线的所有设备的地址:void I2C_Scan ()
{
HAL_StatusTypeDef res;
char info[] = "Scanning I2C bus...\r\n";
HAL_UART_Transmit(&huart5, (uint8_t*)info, strlen(info), HAL_MAX_DELAY);
for(uint16_t i = 0; i < 128; i++)
{
res = HAL_I2C_IsDeviceReady(&hi2c1, i << 1, 1, HAL_MAX_DELAY);
if(res == HAL_OK)
{
char msg[64];
snprintf(msg, sizeof(msg), "0x%02X", i);
HAL_UART_Transmit(&huart5, (uint8_t*)msg, strlen(msg), HAL_MAX_DELAY);
HAL_UART_Transmit(&huart5, (uint8_t*)"\r\n", 2, HAL_MAX_DELAY);
}
else HAL_UART_Transmit(&huart5, (uint8_t*)".", 1, HAL_MAX_DELAY);
}
HAL_UART_Transmit(&huart5, (uint8_t*)"\r\n", 2, HAL_MAX_DELAY);
}
该函数轮询从0到127的所有地址,如果从该地址接收到响应,它将以十六进制形式将地址号发送到UART。为了与董事会沟通,我使用了白蚁程序。默认情况下,微控制器的UART速度设置为115200,必须在白蚁中设置相同的速度。我们在程序的主体中调用该函数,使电路板闪烁,并以白蚁方式连接到我们的微控制器:
点显示所有未收到答案的地址。由于我焊接了跳线A0,因此显示器上的地址为0x26。现在,将第二个显示器与第一个显示器并行连接,并查看程序将提供什么:
我们有两个地址:0x26(显示1602)和0x27(显示2004)。现在介绍如何使用显示器。微控制器发送一个地址字节,连接到总线的所有设备都用自己的地址字节进行检查。如果匹配,则模块开始与微控制器通信。首先,您需要配置显示:字符计数的来源和方向,光标的行为等。之后,已经可以将用于显示的信息传输到显示器。唯一的特点是我们只能使用4位来传输信息,即数据必须分为两部分。数据存储在高位(4-7)中,低位用于指示背光是否将打开(3位),数据是要输出还是显示设置(RS输出,0位)和2位,通过阅读发生的变化,即e要发送1个字节的数据,您需要发送4个字节-第一个字节将包含4位信息,第二个字节为状态1,第二个字节是第1个字节的重复,仅第二个字节为状态0。第3个和第4个字节相似,仅包含数据的后半部分。这听起来有点不可理解,我将向您展示一个示例:void I2C_send(uint8_t data, uint8_t flags)
{
HAL_StatusTypeDef res;
for(;;) {
res = HAL_I2C_IsDeviceReady(&hi2c1, LCD_ADDR, 1, HAL_MAX_DELAY);
if(res == HAL_OK) break;
}
uint8_t up = data & 0xF0;
uint8_t lo = (data << 4) & 0xF0;
uint8_t data_arr[4];
data_arr[0] = up|flags|BACKLIGHT|PIN_EN;
data_arr[1] = up|flags|BACKLIGHT;
data_arr[2] = lo|flags|BACKLIGHT|PIN_EN;
data_arr[3] = lo|flags|BACKLIGHT;
HAL_I2C_Master_Transmit(&hi2c1, LCD_ADDR, data_arr, sizeof(data_arr), HAL_MAX_DELAY);
HAL_Delay(LCD_DELAY_MS);
}
让我们按顺序进行。开头有存储显示地址的变量,以及每次必须与数据一起发送的设置位。在发送功能中,我们首先检查记录的地址是否有模块。在接收到HAL_OK消息的情况下,我们开始形成用于发送的字节。在我们将要发送的字节的开头,有必要将其分为两部分,并将它们都写入高位。假设我们要让显示器显示字符“ s”,在二进制系统中为1110011(计算器)使用逻辑运算&我们将= 01110000写入变量,即只写最高有效位。低位开始时向左移动4个字符,然后写入变量lo =00110000。接下来,我们形成一个4字节的数组,其中包含有关要显示的字符的信息。现在,我们将配置位(0-3位)分配给现有字节。之后,我们使用HAL_I2C_Master_Transmit()函数将地址字节和4个字节的信息发送到显示器;但不要急于下载程序,因为一开始您需要设置显示设置。该站点具有出色的翻译表,其中包含用于自定义显示的命令。与文档一起检查后,我得出了最适合自己的以下设置: I2C_send(0b00110000,0);
I2C_send(0b00000010,0);
I2C_send(0b00001100,0);
I2C_send(0b00000001,0);
我们将这些命令放在无限循环的开始之前,以便在开始工作之前将设置发送一次(如arduinki中的void设置)。除字节外,I2C_send函数还要求您指定是否发送显示设置或数据。如果函数的第二个参数为0,则为设置,如果为1,则为数据。最后一点-您需要一个函数,该函数将按字符发送截止日期。这里的一切都很简单:void LCD_SendString(char *str)
{
while(*str)
{
I2C_send((uint8_t)(*str), 1);
str++;
}
}
收集了所有这些功能后,您可以编写: LCD_SendString(" Hello");
I2C_send(0b11000000,0);
LCD_SendString(" Habr");
好了,我们找到了现在是2004年的1602显示器。它们之间的差异很小,即使该代码也能正常工作。所有的不同归结于组织显示器上单元格的地址。在两个显示屏中,内存均包含80个单元,在1602显示屏中,前16个单元负责第一行,而40至56个单元负责第二行,其余的存储单元均不显示,因此,如果您向显示屏发送17个字符,则最后一个将不被传输在第二行上,并将被记录在没有输出到显示器的存储单元中。更清楚一点,存储器的组织方式如下:
要使用换行,我使用了I2C_send命令(0b11000000,0);,它只剩下40个单元格。 2004年的展览更加有趣。第一行是单元格1到20。第二行是单元格40到60第三行-从21到40的单元格第四行-从60到80的单元格,即 如果您发送命令LCD_SendString("___________________1___________________2___________________3___________________4");
我们得到以下信息:
要组织各行之间的过渡,必须将光标手动移至所需的存储单元,或者可以通过编程方式补充该功能。到目前为止,我已经决定使用手动版本: I2C_send(0b10000000,0);
LCD_SendString(" Hello Habr");
I2C_send(0b11000000,0);
LCD_SendString(" STM32 + LCD 1602");
I2C_send(0b10010100,0);
LCD_SendString(" +LCD 2004A");
I2C_send(0b11010100,0);
LCD_SendString(" library HAL");
结果:
这些显示可能就是所有这些,有用的链接使我得以弄清一切:- 代码在这里看起来很多
- 显示配置表在这里
- 程序在这里看了
程序和数据表PS:不要忘了预先调整显示屏的亮度。