RIOT 中驱动 HD44780 I2C 液晶屏:基于 PCF857x 端口扩展器的配置与测试指南
2026/9/20 7:10:36 网站建设 项目流程

RIOT 中驱动 HD44780 I2C 液晶屏:基于 PCF857x 端口扩展器的配置与测试指南

【免费下载链接】RIOTRIOT - The friendly OS for IoT项目地址: https://gitcode.com/GitHub_Trending/riot/RIOT

导读

HD44780 是大量 Arduino 入门套件中 16×2 字符液晶屏(常被称为 LCM1602C)的内置控制器。在 RIOT 操作系统中,除了直接使用 GPIO 的并行模式外,还可以通过 PCF8574(A)/PCF8575 I2C 端口扩展器以极少的引脚驱动该屏。本文以 RIOT 仓库中的 hd44780_i2c 测试应用 为主线,完整讲解 I2C 模式下芯片选型、地址配置、编译烧录与运行验证的全流程,并结合驱动源码剖析其底层实现原理,帮助你在一小时内让 LCM1602C 在 RIOT 上跑起来。

HD44780 的两种驱动模式与 I2C 扩展方案

并行接口与 I2C 转接板

HD44780 原生提供的是 8 位(或 4 位)并行数据总线,加上 RS、RW、E 控制线,直接接线需要占用大量 GPIO。常见的做法是购买带 I2C 接口的转接板:转接板上有一颗 I2C 端口扩展芯片(PCF8574 / PCF8574A / PCF8575),它把 I2C 总线上的数据展开为 8 路并行 GPIO,再接在液晶屏的并行接口上。RIOT 的 hd44780 驱动同时支持两种模式:

  • 并行模式:直接操作 MCU 的 GPIO,参考 tests/drivers/hd44780;
  • I2C 模式:挂载pcf857x模块,将 LCD 引脚映射到扩展芯片的位,即本文讨论的对象。

从 drivers/include/hd44780.h 的模块注释可以看到,驱动文档明确说明:“当前驱动同时支持直接寻址,以及在使用 PCF857X 模块时的 I2C 方式。”

端口扩展芯片与可选的 I2C 地址

不同转接板上使用的芯片不同,其硬件地址跳线决定了 I2C 地址范围:

芯片基地址可配置地址范围说明
PCF85740x200x20 ~ 0x27最常见,A0~A2 三根地址线
PCF8574A0x380x38 ~ 0x3F与 8574 地址段不同的变体
PCF85750x200x20 ~ 0x2716 位 I/O 扩展,同样常用

这三个基地址宏定义在 drivers/include/pcf857x.h 中:PCF8575_BASE_ADDRPCF8574_BASE_ADDR均为 0x20,PCF8574A_BASE_ADDR为 0x38。驱动在初始化时会把“相对地址”加上基地址得到完整从机地址,见 drivers/pcf857x/pcf857x.c。

编译前必须做的事:选芯片、定地址

通过 make 变量指定芯片与地址

测试应用的 Makefile 是整套配置的核心。它定义了两个可覆盖变量,并允许在命令行直接传入:

DRIVER ?= pcf8574 ADDRESS ?= 0x20

因此编译时只需一条命令即可完成选型与定址:

DRIVER=pcf8575 ADDRESS=0x27 BOARD=... make ...

例如在某个板上选用 PCF8575、地址 0x27,则执行:

DRIVER=pcf8575 ADDRESS=0x27 BOARD=nucleo-f411re make flash

若不指定任何参数,构建系统默认采用PCF8574 + 地址 0x20

地址偏移的计算机制

Makefile 并不会把完整地址直接传给驱动,而是换算成“相对基地址的偏移”,再以-D编译宏注入:

ifeq (pcf8574,$(DRIVER)) CFLAGS += -D"PCF857X_PARAM_ADDR=($(ADDRESS)-PCF8574_BASE_ADDR)" else ifeq (pcf8574a,$(DRIVER)) CFLAGS += -D"PCF857X_PARAM_ADDR=($(ADDRESS)-PCF8574A_BASE_ADDR)" else ifeq (pcf8575,$(DRIVER)) CFLAGS += -D"PCF857X_PARAM_ADDR=($(ADDRESS)-PCF8575_BASE_ADDR)" else $(error Invalid I2C port expander selected: $(DRIVER)) endif

即当DRIVER=pcf8575 ADDRESS=0x27时,编译期计算0x27 - 0x20 = 0x07存入PCF857X_PARAM_ADDR,运行时再由pcf857x.c加回基地址。若传入的DRIVER不是上述三者之一,构建会直接报错Invalid I2C port expander selected,避免误配置。选中芯片后,Makefile 通过USEMODULE += $(DRIVER)自动引入对应的pcf8574pcf8574apcf8575模块。

不清楚转接板地址时:用 I2C 总线扫描

转接板上的地址跳线(A0~A2 焊盘)位置不同,实际地址往往需要确认。RIOT 提供了现成的扫描工具 tests/periph/i2c:编译运行该测试程序,它会枚举 I2C 总线上的所有设备并打印各自的从机地址,据此即可确定本板转接芯片的地址,再回填到ADDRESS变量中重新编译。

驱动参数与引脚映射的默认约定

默认面板尺寸

drivers/hd44780/include/hd44780_params.h 定义默认屏参为 16 列、2 行:

#ifndef HD44780_PARAM_COLS #define HD44780_PARAM_COLS (16U) #endif #ifndef HD44780_PARAM_ROWS #define HD44780_PARAM_ROWS (2U) #endif

驱动在初始化时会校验尺寸合法性:列数不超过HD44780_MAX_COLS(40),行数不超过HD44780_MAX_ROWS(4),且行列乘积不超过 80 个字符(对应 HD44780 的 80 字节 DDRAM 上限),否则返回 -1 并打印hd44780_init: invalid LCD size!,见 drivers/hd44780/hd44780.c。

PCF857X 模式下的引脚位映射

当启用了MODULE_PCF857X时,驱动把 LCD 的控制与数据引脚映射到扩展芯片的各位(hd44780_params.h):

LCD 引脚扩展芯片位说明
RSBit 0寄存器选择
RWBit 1读写选择(测试中保持低电平)
EBit 2使能脉冲
D4 ~ D7Bit 4 ~ Bit 74 位数据总线
D0 ~ D3GPIO_UNDEF未使用

注意:数据引脚数组长度为 8(HD44780_MAX_PINS),其中第 4 位起是GPIO_UNDEF。驱动正是通过检测data[4]是否有效来自动判定工作模式——gpio_is_valid(data[4])为真则走 8 位模式,否则走 4 位模式(hd44780.c)。由于 I2C 转接方案只接了 D4~D7,驱动会自动落入4 位模式,这也与绝大多数 1602 转接板一致。

驱动底层原理:初始化序列与数据发送

4 位模式的握手初始化

hd44780_init严格遵循 HD44780 数据手册(第 45/46 页)规定的初始化时序。在 4 位模式下,驱动通过_write_bits依次发送三次0x03(间隔HD44780_INIT_WAIT_LONG/HD44780_INIT_WAIT_SHORT),再发送0x02进入 4 位模式,随后设置FUNCTIONSET、打开显示、清屏、设置字符输入方向,见 hd44780.c。

数据如何经 PCF857x 落到 LCD 上

发送任意字节(命令或字符)时,_send先根据state置位/清零 RS,若 RW 引脚有效则拉低,然后按 4 位模式先发高 4 位再发低 4 位(hd44780.c)。_write_bits逐位写入数据引脚,最后在 E 引脚上产生一个“先低、再高、再低”的使能脉冲(_pulse),让 LCD 锁存数据(hd44780.c)。

关键在于:当编译进MODULE_PCF857X后,驱动内部所有的_gpio_set/_gpio_clear/_gpio_init都被重定向为pcf857x_gpio_set/pcf857x_gpio_clear/pcf857x_gpio_init(hd44780.c),即每次操作 LCD 引脚,驱动都会通过 I2C 写一次扩展芯片的寄存器。同时hd44780_init在配置引脚前会先调用pcf857x_init完成扩展芯片本身的 I2C 初始化(hd44780.c)。源码注释也提示了一个已知约束:目前该实现下系统中仅能存在一个 PCF857x 实例。

行地址偏移

对于 16×2 屏,驱动预设了行偏移:第 0 行0x00、第 1 行0x40、第 2 行0x00 + cols、第 3 行0x40 + cols(hd44780.c)。hd44780_set_cursor会把“列 + 行偏移”拼进SETDDRAMADDR命令,因此第二行首个字符对应 DDRAM 地址 0x40。若传入的row超出实际行数,驱动会自动收敛到最后一行(hd44780.c)。

测试程序:从初始化到输出验证

主程序流程

main.c 演示了驱动 API 的典型用法:

  1. 调用hd44780_init(&dev, &hd44780_params[0])完成初始化,失败则打印[FAILED]并退出;
  2. hd44780_clear+hd44780_home清屏并复位光标;
  3. 第一行打印Hello World ...ztimer_sleep延时 1 秒;
  4. hd44780_set_cursor(&dev, 0, 1)将光标移到第二行,打印RIOT is here!,延时 3 秒;
  5. 再次清屏,依次打印The friendly IoTOperating System
  6. 全部完成后输出[SUCCESS]

程序依赖ztimerztimer_sec模块完成延时(Makefile 中USEMODULE += ztimer ztimer_sec)。这段代码同时展示了hd44780_inithd44780_clearhd44780_homehd44780_printhd44780_set_cursor五个核心 API 的调用方式。

自动化验证与 CI

仓库配套了 01-run.py 测试脚本,通过 RIOT 的 testrunner 框架自动校验串口输出是否依次出现[START][SUCCESS]

def testfunc(child): child.expect_exact("[START]") child.expect_exact("[SUCCESS]")

这意味着make test可以无人工干预地判断液晶初始化与写入是否成功。另外 Makefile.ci 声明了atmega8nucleo-l011k4两个内存不足(BOARD_INSUFFICIENT_MEMORY)的板卡,即该测试在这些极小内存平台上无法通过 CI,其余主流板卡均可直接运行。

完整运行步骤

按以下步骤即可在目标板上复现本测试:

# 1. 确认转接板芯片与地址(未知时先用 tests/periph/i2c 扫描) # 2. 进入测试目录并编译烧录(以 PCF8574、地址 0x27 为例) cd tests/drivers/hd44780_i2c DRIVER=pcf8574 ADDRESS=0x27 BOARD=<你的板卡> make flash # 3. 观察串口输出,应依次出现 # [START] # [SUCCESS] # 4. 自动化回归测试(需要串口终端环境) DRIVER=pcf8574 ADDRESS=0x27 BOARD=<你的板卡> make test

常见问题排查顺序:

  • 屏幕无显示:先用 tests/periph/i2c 扫描确认芯片地址,核对ADDRESS与实际跳线是否一致;
  • 选错芯片DRIVER只能是pcf8574pcf8574apcf8575三者之一,否则编译直接报错;
  • 对比度问题:多数 1602 转接板背面有可调电位器,需旋到能清晰看到字符的位置(属于硬件层面调校);
  • 换用并行模式:如需绕过 I2C 转接板,请参考 tests/drivers/hd44780 测试应用。

小结

RIOT 的 hd44780_i2c 测试应用完整覆盖了“芯片选型 → 地址换算 → 编译烧录 → 串口验证”的全链路:Makefile 中DRIVER/ADDRESS变量负责把用户配置翻译成驱动所需的地址偏移,hd44780_params.h提供 PCF857X 模式的引脚位映射,而hd44780.c内部的 GPIO 重定向机制让同一套驱动代码无缝工作在并行与 I2C 两种模式下。理解这三个文件的协作关系后,你不仅可以跑通现成测试,也能轻松把该方案移植到自己的 RIOT 应用中。

【免费下载链接】RIOTRIOT - The friendly OS for IoT项目地址: https://gitcode.com/GitHub_Trending/riot/RIOT

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询