主控用stm32最小板,lcd1602 i2c模块网上买的现成的。
我是个新手,代码让ai写的。最先显示的是一排方块,修改之后显示两排方块,但还是不显示字符,调过电位器,i2c电压也是5v
画一下接线图,以及把代码放上来,你这信息量这么少是要让别人猜吗?
优秀提问案例:https://bbs.eeclub.top/t/topic/289
提问的智慧:https://bbs.eeclub.top/t/topic/109
兄弟别急,这个LCD1602+I2C的组合绝对是单片机新手的“必经之坑”,大家刚开始玩的时候基本都在这上面卡过。
结合你的描述和这三张图,给你吃个定心丸:你的硬件大概率没坏,单纯是通信没成功。
屏幕能亮,且显示两排方块,这说明供电正常,且你的电位器对比度调得刚刚好。1602屏幕显示满屏方块的含义是:屏幕通电了,但没有收到单片机发来的初始化指令。 既然是让AI写的代码,那问题99%出在代码和通信配置上。你可以按下面这几步排查:
1. 最常见的坑:I2C器件地址填错了
看你背面那张图,黑色转接板用的是PCF8574T芯片。市面上这种模块默认的I2C地址通常是 0x27 或者 0x3F。
但是注意! 如果你的AI是用STM32的HAL库写的代码,HAL库的I2C发送函数要求把7位地址左移1位。
0x27,代码里可能要填 0x4E (0x27 << 1)。0x3F,代码里可能要填 0x7E (0x3F << 1)。AI经常在这点上犯迷糊,直接把0x27丢进HAL库函数里,导致根本找不到设备。2. 线接反了(新手常犯)
检查一下STM32端的引脚。比如F103C8T6默认的硬件I2C1是 PB6 (SCL) 和 PB7 (SDA)。确认一下转接板上的SDA有没有准确接到PB7,SCL有没有接到PB6。
3. AI写的底层驱动时序/引脚映射不对
这种I2C转接板实际上是把I2C信号转成8个并行IO口(P0-P7)去控制1602的 RS, RW, EN 和数据引脚。不同的转接板,具体哪个P口接哪个管脚可能稍微有区别。AI直接从头“手搓”的底层代码往往会搞错这个映射关系。
建议: 别让AI从头写底层驱动了。直接去B站或者CSDN搜“STM32 HAL库 LCD1602 I2C”,找别人验证过的 lcd1602.c 和 lcd1602.h 文件直接拉进工程里调用,这样最稳妥。
4. 硬件逻辑电平(小概率)
你提到供电是5V,这很好,1602必须5V供电。STM32的引脚虽然是3.3V逻辑电平,但大部分I2C引脚(如PB6, PB7)是“5V容忍(FT)”的,直接连通常没问题。只要确保STM32和LCD模块共地(GND连在一起)就行。
破局建议(下一步怎么做):
先别急着让它显示字符。你先让AI给你写一个 “STM32 I2C Scanner (I2C地址扫描)” 的代码。
把程序烧进去,打开串口助手,看看STM32能不能在总线上扫描到这个模块。
祝你早日看到 “Hello World”!卡住了再来问。
关于硬件方面的一个快速提醒:LCD 和 I2C 背包需要 5V 供电,但你的 STM32 工作在 3.3V。虽然大多数 STM32 的 I2C 引脚是 5V 容忍的(FT),但仍建议查阅数据手册,确认你所使用的具体引脚是否确实支持 5V。通常情况下,模块上的上拉电阻已足够,但如果逻辑电平不匹配,通信仍可能失败。
20p排母上是stm32最小系统板,4p排母上是lcd1602_i2c模块
以下是main.c
#include “main.h”
#include “i2c.h”
#include “gpio.h”
#include “lcd1602_i2c.h”
void SystemClock_Config(void);
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_I2C1_Init();
lcd_init(); // 初始化 LCD1602
lcd_clear(); // 清屏
lcd_set_cursor(0, 0); // 设置光标到第一行第一列
lcd_send_string(“Hello STM32!”); //
while (1)
{
}
}
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
{
Error_Handler();
}
}
void Error_Handler(void)
{
\__disable_irq();
while (1)
{
}
}
#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t \*file, uint32_t line)
{
}
#endif /\* USE_FULL_ASSERT \*/
以下是lcd1602_i2c.h
#ifndef \__LCD1602_I2C_H
#define \__LCD1602_I2C_H
#include “main.h” // 引入 HAL 库和引脚定义
#define LCD_I2C_ADDRESS 0x4E
void lcd_init(void);
void lcd_send_cmd(char cmd);
void lcd_send_data(char data);
void lcd_send_string(char \*str);
void lcd_set_cursor(int row, int col);
void lcd_clear(void);
#endif
以下是lcd1602_i2c.c
#include “lcd1602_i2c.h”
// 声明外部的 I2C 句柄,CubeMX 生成在 main.c 中
extern I2C_HandleTypeDef hi2c1;
// 内部函数:发送数据到 I2C
void lcd_send_to_i2c(char data, int rs)
{
uint8_t data_t\[4\];
uint8_t upper_nibble, lower_nibble;
upper_nibble = data & 0xF0;
lower_nibble = (data << 4) & 0xF0;
// 控制字:Bit 3 是背光 (1=亮),Bit 2 是 EN,Bit 1 是 RW (0=写),Bit 0 是 RS
uint8_t backlight = 0x08;
data_t[0] = upper_nibble | backlight | 0x04 | rs; // EN = 1
data_t[1] = upper_nibble | backlight | 0x00 | rs; // EN = 0
data_t[2] = lower_nibble | backlight | 0x04 | rs; // EN = 1
data_t[3] = lower_nibble | backlight | 0x00 | rs; // EN = 0
// 通过 I2C1 发送 4 个字节
HAL_I2C_Master_Transmit(&hi2c1, LCD_I2C_ADDRESS, data_t, 4, 100);
}
// 发送命令
void lcd_send_cmd(char cmd)
{
lcd_send_to_i2c(cmd, 0); // RS = 0 表示发送命令
}
// 发送数据 (字符)
void lcd_send_data(char data)
{
lcd_send_to_i2c(data, 1); // RS = 1 表示发送数据
}
// 清屏
void lcd_clear(void)
{
lcd_send_cmd(0x01);
HAL_Delay(2); // 清屏命令需要较长执行时间
}
// 设置光标位置 (row: 0-1, col: 0-15)
void lcd_set_cursor(int row, int col)
{
uint8_t address;
switch (row)
{
case 0:
address = 0x00;
break;
case 1:
address = 0x40;
break;
default:
address = 0x00;
}
address += col;
lcd_send_cmd(0x80 | address); // 设置 DDRAM 地址
}
// 发送字符串
void lcd_send_string(char \*str)
{
while (\*str)
{
lcd_send_data(\*str++);
}
}
// 初始化 LCD1602
void lcd_init(void)
{
// 4 bit 模式的标准初始化流程
HAL_Delay(50);
lcd_send_cmd(0x30);
HAL_Delay(5);
lcd_send_cmd(0x30);
HAL_Delay(1);
lcd_send_cmd(0x30);
HAL_Delay(10);
lcd_send_cmd(0x20);
HAL_Delay(10);
// 显示设置
lcd_send_cmd(0x28); // 4线,2行,5x8 字体
HAL_Delay(1);
lcd_send_cmd(0x08); // 关闭显示
HAL_Delay(1);
lcd_send_cmd(0x01); // 清屏
HAL_Delay(2);
lcd_send_cmd(0x06); // 光标右移
HAL_Delay(1);
lcd_send_cmd(0x0C); // 开启显示,关闭光标
}
我把简化接线图和代码写在回复区了,谢谢帮我看看
好的,谢谢你,我去试试你的建议
我编译下载之后用的是电源模块供电,此时i2c的vcc脚和gnd脚电压是5v。
太棒了,提供原理图和源码能让排查效率提升百倍!
先给你吃个定心丸:你的硬件连接完全正确。原理图上 PB6 接 SCL、PB7 接 SDA 是 STM32F103 的标准 I2C1 引脚。而且你在头文件里写了 #define LCD_I2C_ADDRESS 0x4E,说明你(或者 AI)已经知道 HAL 库需要把 0x27 左移一位,成功避开了新手最容易踩的两个大坑。
屏幕只显示方块的真正原因,在于 AI 写的初始化函数(lcd_init)在“4位模式”的握手时序上翻车了。
你用的 I2C 转接板(PCF8574)是通过 4 根数据线(D4-D7)来控制 1602 的。
当 1602 刚通电时,它默认自己是 8位模式。为了让它切换到 4位模式,必须按照数据手册的要求,严格发送几次单半字节(只发高 4 位,发完就结束)。
看看你的 lcd_send_cmd() 函数,它调用了 lcd_send_to_i2c()。这个底层函数非常“勤奋”,无论你传什么指令,它都会自动把指令拆成两半(高 4 位发一次,低 4 位发一次),并触发两次 EN 脉冲。
当 AI 在初始化时写下 lcd_send_cmd(0x30) 时:
0x3 给屏幕。lcd_send_to_i2c 发送了高四位 0x3(触发一次 EN),紧接着又发送了低四位 0x0(又触发一次 EN)。0x0 脉冲,时序瞬间彻底错乱,导致屏幕拒绝初始化,死机停留在“满屏方块”的通电初始状态。我们需要在 lcd1602_i2c.c 中增加一个 “只发送半个字节” 的专用函数,专门用于唤醒屏幕,并重写 lcd_init()。
请把你的 lcd1602_i2c.c 修改为以下内容(直接覆盖原来的 lcd_init 及之前的部分,保留其他的):
#include "lcd1602_i2c.h"
// 声明外部的 I2C 句柄
extern I2C_HandleTypeDef hi2c1;
// ====== 新增:专用于初始化的单半字节发送函数 ======
void lcd_send_cmd_4bit(uint8_t nibble)
{
uint8_t data_t[2];
uint8_t backlight = 0x08; // 保持背光常亮
// 注意:这里传入的 nibble 必须已经是高 4 位对齐的数据 (例如 0x30)
data_t[0] = (nibble & 0xF0) | backlight | 0x04 | 0; // EN = 1, RS = 0
data_t[1] = (nibble & 0xF0) | backlight | 0x00 | 0; // EN = 0, RS = 0
HAL_I2C_Master_Transmit(&hi2c1, LCD_I2C_ADDRESS, data_t, 2, 100);
}
// 内部函数:发送完整字节到 I2C (分为高四位和低四位)
void lcd_send_to_i2c(char data, int rs)
{
uint8_t data_t[4];
uint8_t upper_nibble, lower_nibble;
upper_nibble = data & 0xF0;
lower_nibble = (data << 4) & 0xF0;
uint8_t backlight = 0x08;
data_t[0] = upper_nibble | backlight | 0x04 | rs; // EN = 1
data_t[1] = upper_nibble | backlight | 0x00 | rs; // EN = 0
data_t[2] = lower_nibble | backlight | 0x04 | rs; // EN = 1
data_t[3] = lower_nibble | backlight | 0x00 | rs; // EN = 0
HAL_I2C_Master_Transmit(&hi2c1, LCD_I2C_ADDRESS, data_t, 4, 100);
}
// ====== 重写:正确的 4-bit 初始化流程 ======
void lcd_init(void)
{
// 1. 强制初始化阶段(必须严格只发送单次 4-bit 脉冲)
HAL_Delay(50);
lcd_send_cmd_4bit(0x30); // 唤醒1
HAL_Delay(5);
lcd_send_cmd_4bit(0x30); // 唤醒2
HAL_Delay(1);
lcd_send_cmd_4bit(0x30); // 唤醒3
HAL_Delay(10);
lcd_send_cmd_4bit(0x20); // 告诉屏幕:“进入 4位 模式!”
HAL_Delay(10);
// 2. 正常设置阶段(此时屏幕已在 4位 模式,可以使用正常的双脉冲发送函数了)
lcd_send_cmd(0x28); // 功能设置:4线数据,2行显示,5x8 字体
HAL_Delay(1);
lcd_send_cmd(0x08); // 关显示
HAL_Delay(1);
lcd_send_cmd(0x01); // 清屏
HAL_Delay(2); // 清屏指令耗时较长,至少延时 2ms
lcd_send_cmd(0x06); // 进入模式设置:光标右移
HAL_Delay(1);
lcd_send_cmd(0x0C); // 开显示,关光标
}
// ... 你的 lcd_send_cmd, lcd_send_data 等其余函数保持不变 ...
如果你修改代码并重新烧录后,屏幕仍然显示方块:
那唯一的可能就是买到的模块默认 I2C 地址不是 0x27,而是 0x3F。你只需要去 lcd1602_i2c.h 里,把 #define LCD_I2C_ADDRESS 0x4E 改成 0x7E (0x3F 左移一位) 再试一次即可。
你先修改这部分初始化代码测试一下,需要我教你怎么写一个简易的 “I2C 地址扫描器” 来100%确认模块的通讯地址吗?
可以用逻辑分析仪检查一下I2C波形有没有出来,时序对不对,如果I2C没问题那再用逻辑分析仪检查一下I2C转并口模块的输出的并行信号有没有,时序对不对
STM32CubeMX你怎么配置的
你的LCD1602背光正常亮但无字符显示,90%的概率是I2C通信异常(地址错误/引脚配置错/无应答),其次是初始化时序/延时异常、硬件供电/上拉缺失,下面按优先级给你完整的排查和修复方案。
你的原理图定义:
LCD1602+I2C转接板是5V器件,3.3V供电会出现「背光亮、但I2C无法通信」的情况,必须接STM32的5V引脚,不能接3.3V。
I2C是开漏总线,必须加上拉:
你的代码里写的#define LCD_I2C_ADDRESS 0x4E,这个地址不一定匹配你的模块:
HAL_I2C_Master_Transmit需要传入8位写地址(7位地址左移1位):
0x4E(你当前的地址)0x7E(最常见的另一个地址)在MX_I2C1_Init()之后,加入I2C地址扫描代码,看哪个地址有应答:
// 放在MX_I2C1_Init()之后,lcd_init()之前
uint8_t i2c_addr;
for(i2c_addr=0; i2c_addr<128; i2c_addr++)
{
if(HAL_I2C_IsDeviceReady(&hi2c1, i2c_addr<<1, 1, 100) == HAL_OK)
{
// 这里加个断点/点灯,看能应答的地址,把(i2c_addr<<1)填到LCD_I2C_ADDRESS里
break;
}
}
如果扫描不到任何地址,说明硬件接线/I2C初始化有问题,先解决硬件。
你的LCD初始化极度依赖延时,如果HAL_Delay失效,初始化时序完全错误,必然无法显示。
while (1)
{
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); // 对应你的板载LED引脚
HAL_Delay(500);
}
如果LED不是500ms翻转,说明系统时钟/SysTick配置错误,必须先修复时钟配置。
你的代码没有判断I2C是否发送成功,无法定位通信问题,修改lcd_send_to_i2c函数,增加返回值和错误处理:
// 内部函数:发送数据到 I2C,返回HAL状态
HAL_StatusTypeDef lcd_send_to_i2c(char data, int rs)
{
uint8_t data_t[4];
uint8_t upper_nibble, lower_nibble;
upper_nibble = data & 0xF0;
lower_nibble = (data << 4) & 0xF0;
uint8_t backlight = 0x08;
data_t[0] = upper_nibble | backlight | 0x04 | rs; // EN = 1
data_t[1] = upper_nibble | backlight | 0x00 | rs; // EN = 0
data_t[2] = lower_nibble | backlight | 0x04 | rs; // EN = 1
data_t[3] = lower_nibble | backlight | 0x00 | rs; // EN = 0
// 增加超时时间,返回发送状态
return HAL_I2C_Master_Transmit(&hi2c1, LCD_I2C_ADDRESS, data_t, 4, 200);
}
// 发送命令
void lcd_send_cmd(char cmd)
{
lcd_send_to_i2c(cmd, 0); // RS = 0 表示发送命令
HAL_Delay(1); // 增加命令执行延时,避免时序过快
}
// 发送数据 (字符)
void lcd_send_data(char data)
{
lcd_send_to_i2c(data, 1); // RS = 1 表示发送数据
HAL_Delay(1);
}
你的初始化流程基本正确,但可以增加更稳妥的延时,避免时序问题:
// 初始化 LCD1602
void lcd_init(void)
{
// 4 bit 模式的标准初始化流程,增加延时余量
HAL_Delay(100); // 上电等待更长时间,确保LCD稳定
lcd_send_cmd(0x30);
HAL_Delay(10);
lcd_send_cmd(0x30);
HAL_Delay(5);
lcd_send_cmd(0x30);
HAL_Delay(10);
lcd_send_cmd(0x20); // 切换到4位模式
HAL_Delay(10);
// 显示设置,增加每个命令的延时
lcd_send_cmd(0x28); // 4线,2行,5x8 字体
HAL_Delay(2);
lcd_send_cmd(0x08); // 关闭显示
HAL_Delay(2);
lcd_send_cmd(0x01); // 清屏
HAL_Delay(5);
lcd_send_cmd(0x06); // 光标右移,地址自增
HAL_Delay(2);
lcd_send_cmd(0x0C); // 开启显示,关闭光标,无闪烁
HAL_Delay(2);
}
LCD_I2C_ADDRESS。Hello STM32!。如果以上步骤都做完还是不显示:
谢谢你的解答,我已经尝试用你修改过的代码进行编译下载,结果还是不显示字符。且我发现,#define LCD_I2C_ADDRESS 0x4E中地址是0x4E时显示两排方块,其他地址都是一排方块
把I2C速率调低试试,比如10k,速率高了线太长时可能有问题,I2C的那两个引脚有没有设置开漏模式上拉
这是一个绝佳的好消息!你观察到的这个现象(0x4E显示两排方块,其他地址显示一排)不仅不是坏事,反而证明了你的代码和通信现在已经完全跑通了!
让我给你揭秘一下1602屏幕的“内心活动”,你就知道为什么了:
0x4E 这个地址找到了模块,并且成功执行了我们修改后的初始化代码!屏幕收到了 0x28 这个指令(设置为2行显示),乖乖地把第二行也激活了。也就是说,你的单片机、连线、I2C通信、甚至初始化代码全都是对的!字其实已经写上去了!
只有一个原因:对比度(V0)调得实在太高了。
字其实已经打印在了屏幕上,但是因为对比度拉满了,导致“没显示字”的背景像素点也变成了纯黑色,把原本的字给掩盖了。
放下代码,拿起螺丝刀,按这几步做:
"Hello STM32!" 会清晰地浮现出来!
补充一个防背锅的小补丁(可选):
虽然现在大概率是硬件电位器的问题,但为了让你的底层代码“无懈可击”,建议在发送指令后稍微加一点延时,防止STM32发得太快,屏幕脑力跟不上。
你可以把 lcd1602_i2c.c 里的这两个小函数稍微加一句 HAL_Delay(1);:
// 发送命令
void lcd_send_cmd(char cmd)
{
lcd_send_to_i2c(cmd, 0);
HAL_Delay(1); // <- 加上这句,给屏幕一点处理时间
}
// 发送数据 (字符)
void lcd_send_data(char data)
{
lcd_send_to_i2c(data, 1);
HAL_Delay(1); // <- 加上这句
}
并且把清屏函数的延时稍微加大一点:
// 清屏
void lcd_clear(void)
{
lcd_send_cmd(0x01);
HAL_Delay(5); // <- 从2改成5,清屏是最耗时的操作
}
快去转动那个蓝色电位器吧,成功点亮了记得来报个喜!
我将尝试你的建议,谢谢你的解答
谢谢你的解答,我将速率拉低还是一样的,而且在 CubeMX 的 GPIO 配置界面,PB6/PB7 的Pull选项可能不可见,这是 CubeMX 对 F1 系列的界面限制
谢谢你的解答,我真的在转动电位器,但是只能看到两排方块从亮变淡,没有显示文字,而且增加延时后,我能看到上电瞬间从一排方块变成两排方块,证明确实收到了0x28 这个指令
你能精准捕捉到“上电瞬间从一排方块变成两排方块”这个细节,说明你具备非常敏锐的硬件排错直觉!这个现象非常关键,它不仅100%证实了你的 I2C 地址(0x4E)是对的,还证明了单片机和屏幕之间的通讯线路已经打通了。
既然 0x28(开启两行显示)这个指令成功执行了,那为什么后面的字符显示不出来,且依然满屏方块呢?这里面其实有两个典型的“坑”,特别是对于 STM32F1 系列:
你提到的 CubeMX 中 F1 系列 PB6/PB7 看不到 Pull(上拉/下拉)选项,这是因为当配置为 I2C 时,引脚会被强制接管为开漏复用输出(Alternate Function Open Drain),这是正常的界面限制。
不用担心上拉的问题:你可以仔细看一眼 LCD 转接模块背面(蓝色电位器旁边),上面焊着几颗黑色的贴片电阻(通常印着 103 或 472),那些就是模块自带的硬件上拉电阻。所以即便 STM32 内部不提供上拉,硬件电路上也是完全满足 I2C 通信要求的。
STM32F1 系列的硬件 I2C 是出了名的“娇气”(存在硅缺陷/Errata),如果在发送数据时连续不断地狂塞字节,很容易触发 I2C 的 Busy 标志位死锁。
此外,LCD1602 内部的 HD44780 是一颗极其古老的低速芯片。我们原来的代码在一个 I2C 传输包(4个字节)里瞬间完成了高四位和低四位的连续 EN 脉冲跳变。对于某些体质较弱的 LCD 来说,这个速度太快了,导致 0x28 之后的指令(比如清屏 0x01 和写字符)全被它忽略了,或者让 STM32 的 I2C 直接罢工了。
我们需要把原本“一次性发送4个字节”的底层函数,暴力拆分成两次独立的发送,并在中间强制插入延时。这既能重置 STM32 的 I2C 状态机防止死锁,也能给 LCD 留出充足的响应时间。
请用下面这段代码彻底替换你 lcd1602_i2c.c 里的 lcd_send_to_i2c 函数:
// 内部函数:发送完整字节到 I2C (分为高四位和低四位,强力稳定版)
void lcd_send_to_i2c(char data, int rs)
{
uint8_t data_high[2];
uint8_t data_low[2];
uint8_t upper_nibble = data & 0xF0;
uint8_t lower_nibble = (data << 4) & 0xF0;
uint8_t backlight = 0x08; // 背光常亮
// ==========================================
// 第一步:只发送高四位,并制造一个完整的 EN 脉冲
// ==========================================
data_high[0] = upper_nibble | backlight | 0x04 | rs; // EN = 1
data_high[1] = upper_nibble | backlight | 0x00 | rs; // EN = 0
// 发送高四位的 2 个字节
HAL_I2C_Master_Transmit(&hi2c1, LCD_I2C_ADDRESS, data_high, 2, 100);
// 关键保命延时:给 LCD 控制器时间去锁存高四位,并防止 I2C 总线堵死
HAL_Delay(2);
// ==========================================
// 第二步:只发送低四位,并制造一个完整的 EN 脉冲
// ==========================================
data_low[0] = lower_nibble | backlight | 0x04 | rs; // EN = 1
data_low[1] = lower_nibble | backlight | 0x00 | rs; // EN = 0
// 发送低四位的 2 个字节
HAL_I2C_Master_Transmit(&hi2c1, LCD_I2C_ADDRESS, data_low, 2, 100);
// 发送完一个完整字节后,再等一等
HAL_Delay(2);
}
同时,保险起见,把 main.c 里的这段代码稍微调整一下顺序:
把 lcd_clear(); 删掉,因为我们在 lcd_init() 的最后一步已经包含过清屏指令了,连续清屏可能会导致指令堆叠失败。你的 main 函数里这部分这样写就好:
/* USER CODE BEGIN 2 */
lcd_init(); // 初始化 LCD1602
// lcd_clear(); // 注释掉或者删掉这句
lcd_set_cursor(0, 0); // 设置光标到第一行第一列
lcd_send_string("Hello STM32!");
/* USER CODE END 2 */
编译烧录后,如果屏幕是一片空白(方块消失了),再轻轻转动那个蓝色电位器,字符一定会出来的。
如果这次顺利点亮了,需要我进一步教你如何将 I2C 改为基于 DMA 或中断的非阻塞模式,以避免 HAL_Delay 占用你主程序的宝贵运行时间吗?