树莓派Pico连接LCM1602 I2C适配器LCD无显示问题求助
问题描述
- 尝试将树莓派Pico与带LCM1602 I2C适配器的1602LCD连接,使用树莓派官方C/C++ SDK示例代码,编译加载成功但LCD无任何显示。
- 已通过I2C总线扫描确认适配器I2C地址为0x27,引脚与地址配置无误。
- 无法找到覆盖该适配器所有命令的优质数据手册,同时对代码中
lcd_send_byte(uint8_t val, int mode)函数将1字节拆分为6字节发送的操作存在疑惑(不熟悉串行通信,不确定是否正常)。 - 倾向于使用C语言解决问题,暂未尝试MicroPython。
所用代码
/** * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. * * SPDX-License-Identifier: BSD-3-Clause */ #include <stdio.h> #include <string.h> #include "pico/stdlib.h" #include "hardware/i2c.h" #include "pico/binary_info.h" /* Example code to drive a 16x2 LCD panel via a I2C bridge chip (e.g. PCF8574) NOTE: The panel must be capable of being driven at 3.3v NOT 5v. The Pico GPIO (and therefor I2C) cannot be used at 5v. You will need to use a level shifter on the I2C lines if you want to run the board at 5v. Connections on Raspberry Pi Pico board, other boards may vary. GPIO 4 (pin 6)-> SDA on LCD bridge board GPIO 5 (pin 7)-> SCL on LCD bridge board 3.3v (pin 36) -> VCC on LCD bridge board GND (pin 38) -> GND on LCD bridge board */ // commands const int LCD_CLEARDISPLAY = 0x01; const int LCD_RETURNHOME = 0x02; const int LCD_ENTRYMODESET = 0x04; const int LCD_DISPLAYCONTROL = 0x08; const int LCD_CURSORSHIFT = 0x10; const int LCD_FUNCTIONSET = 0x20; const int LCD_SETCGRAMADDR = 0x40; const int LCD_SETDDRAMADDR = 0x80; // flags for display entry mode const int LCD_ENTRYSHIFTINCREMENT = 0x01; const int LCD_ENTRYLEFT = 0x02; // flags for display and cursor control const int LCD_BLINKON = 0x01; const int LCD_CURSORON = 0x02; const int LCD_DISPLAYON = 0x04; // flags for display and cursor shift const int LCD_MOVERIGHT = 0x04; const int LCD_DISPLAYMOVE = 0x08; // flags for function set const int LCD_5x10DOTS = 0x04; const int LCD_2LINE = 0x08; const int LCD_8BITMODE = 0x10; // flag for backlight control const int LCD_BACKLIGHT = 0x08; const int LCD_ENABLE_BIT = 0x04; // By default these LCD display drivers are on bus address 0x27 static int addr = 0x27; // Modes for lcd_send_byte #define LCD_CHARACTER 1 #define LCD_COMMAND 0 #define MAX_LINES 2 #define MAX_CHARS 16 /* Quick helper function for single byte transfers */ void i2c_write_byte(uint8_t val) { #ifdef i2c_default i2c_write_blocking(i2c_default, addr, &val, 1, false); #endif } void lcd_toggle_enable(uint8_t val) { // Toggle enable pin on LCD display // We cannot do this too quickly or things don't work #define DELAY_US 600 sleep_us(DELAY_US); i2c_write_byte(val | LCD_ENABLE_BIT); sleep_us(DELAY_US); i2c_write_byte(val & ~LCD_ENABLE_BIT); sleep_us(DELAY_US); } // The display is sent a byte as two separate nibble transfers void lcd_send_byte(uint8_t val, int mode) { uint8_t high = mode | (val & 0xF0) | LCD_BACKLIGHT; uint8_t low = mode | ((val << 4) & 0xF0) | LCD_BACKLIGHT; i2c_write_byte(high); lcd_toggle_enable(high); i2c_write_byte(low); lcd_toggle_enable(low); } void lcd_clear(void) { lcd_send_byte(LCD_CLEARDISPLAY, LCD_COMMAND); } // go to location on LCD void lcd_set_cursor(int line, int position) { int val = (line == 0) ? 0x80 + position : 0xC0 + position; lcd_send_byte(val, LCD_COMMAND); } static void inline lcd_char(char val) { lcd_send_byte(val, LCD_CHARACTER); } void lcd_string(const char *s) { while (*s) { lcd_char(*s++); } } void lcd_init() { lcd_send_byte(0x03, LCD_COMMAND); lcd_send_byte(0x03, LCD_COMMAND); lcd_send_byte(0x03, LCD_COMMAND); lcd_send_byte(0x02, LCD_COMMAND); lcd_send_byte(LCD_ENTRYMODESET | LCD_ENTRYLEFT, LCD_COMMAND); lcd_send_byte(LCD_FUNCTIONSET | LCD_2LINE, LCD_COMMAND); lcd_send_byte(LCD_DISPLAYCONTROL | LCD_DISPLAYON, LCD_COMMAND); lcd_clear(); } int main() { #if !defined(i2c_default) || !defined(PICO_DEFAULT_I2C_SDA_PIN) || !defined(PICO_DEFAULT_I2C_SCL_PIN) #warning i2c/lcd_1602_i2c example requires a board with I2C pins #else // This example will use I2C0 on the default SDA and SCL pins (4, 5 on a Pico) i2c_init(i2c_default, 100 * 1000); gpio_set_function(PICO_DEFAULT_I2C_SDA_PIN, GPIO_FUNC_I2C); gpio_set_function(PICO_DEFAULT_I2C_SCL_PIN, GPIO_FUNC_I2C); gpio_pull_up(PICO_DEFAULT_I2C_SDA_PIN); gpio_pull_up(PICO_DEFAULT_I2C_SCL_PIN); // Make the I2C pins available to picotool bi_decl(bi_2pins_with_func(PICO_DEFAULT_I2C_SDA_PIN, PICO_DEFAULT_I2C_SCL_PIN, GPIO_FUNC_I2C)); lcd_init(); static char *message[] = { "RP2040 by", "Raspberry Pi", "A brand new", "microcontroller", "Twin core M0", "Full C SDK", "More power in", "your product", "More beans", "than Heinz!" }; while (1) { for (int m = 0; m < sizeof(message) / sizeof(message[0]); m += MAX_LINES) { for (int line = 0; line < MAX_LINES; line++) { lcd_set_cursor(line, (MAX_CHARS / 2) - strlen(message[m + line]) / 2); lcd_string(message[m + line]); } sleep_ms(2000); lcd_clear(); } } return 0; #endif }
解答
关于lcd_send_byte的字节拆分说明
这种操作是完全正常的,原因如下:
- 你的I2C适配器核心是PCF8574(或PCF8574A)8位IO扩展芯片,它将LCD的并行接口转为I2C。
- 代码中LCD工作在4位模式(HD44780控制器的常见模式),需要将1字节数据拆分为高4位和低4位分别传输。
- 每次传输还需配合
LCD_ENABLE_BIT的脉冲来让LCD锁存数据,再加上背光控制位,最终就会出现1字节拆分为多次I2C字节发送的情况。
排查建议
检查电源与电平兼容性
- 确认LCD适配器的供电电压:如果适配器是5V供电,Pico的3.3V I2C引脚可能无法稳定驱动,必须添加电平转换器(虽然I2C扫描能找到地址,但数据传输可能失败)。
- 检查背光是否点亮:部分适配器的背光需要单独接线,或者尝试将代码中的
LCD_BACKLIGHT值改为0x00(部分适配器的背光控制位是低电平有效)。
调整初始化时序
LCD对初始化时序要求严格,可尝试在lcd_init函数的每个lcd_send_byte调用后添加更长延迟:void lcd_init() { lcd_send_byte(0x03, LCD_COMMAND); sleep_ms(5); lcd_send_byte(0x03, LCD_COMMAND); sleep_ms(5); lcd_send_byte(0x03, LCD_COMMAND); sleep_ms(5); lcd_send_byte(0x02, LCD_COMMAND); sleep_ms(5); lcd_send_byte(LCD_ENTRYMODESET | LCD_ENTRYLEFT, LCD_COMMAND); sleep_ms(1); lcd_send_byte(LCD_FUNCTIONSET | LCD_2LINE, LCD_COMMAND); sleep_ms(1); lcd_send_byte(LCD_DISPLAYCONTROL | LCD_DISPLAYON, LCD_COMMAND); sleep_ms(1); lcd_clear(); sleep_ms(2); }也可尝试增大
lcd_toggle_enable中的DELAY_US值(比如改为1000)。验证I2C写入有效性
修改i2c_write_byte函数,检查写入是否成功:void i2c_write_byte(uint8_t val) { #ifdef i2c_default int ret = i2c_write_blocking(i2c_default, addr, &val, 1, false); if (ret != 1) { // 可通过UART打印错误信息,确认写入失败 printf("I2C write failed: %d\n", ret); } #endif }需提前初始化UART:在
main函数开头添加stdio_init_all();。简化测试代码
跳过复杂的循环显示,直接在main中写入简单测试内容,排除其他代码干扰:int main() { stdio_init_all(); // I2C初始化代码不变... lcd_init(); sleep_ms(100); lcd_set_cursor(0,0); lcd_string("TEST"); while(1); }
参考资料
- LCM1602的核心是HD44780液晶显示控制器,所有命令遵循HD44780的规范。
- I2C适配器的核心是PCF8574 IO扩展芯片,它的引脚映射为:P0=RS, P1=RW, P2=E, P3=背光控制, P4-P7=LCD的D4-D7引脚。
内容的提问来源于stack exchange,提问作者Plertz
相关产品推荐
相关产品推荐

