在智能硬件项目中,显示屏承担的是"把程序状态变成可读信息"的角色。传感器采集到的数据、设备运行状态、调试提示、错误信息、菜单内容,都需要通过某种方式展示出来。相比只在串口终端打印日志,LCD1602 液晶屏能让开发板脱离电脑后仍然具备基础信息反馈能力,这也是硬件编程从"能运行"走向"能交互"的重要一步。
本实验使用 CanMV K210 开发板通过 I2C 总线驱动 LCD1602 液晶显示模块。程序会先创建软件 I2C 对象,再扫描 I2C 设备地址,找到 LCD1602 的 I2C 地址后完成初始化。运行后,屏幕会先显示固定文本 Hello!!! 和 Makerobo kit,随后进入滚动显示模式,让 makerobo k210 kit! ^_^ 在 LCD 第一行循环移动。整个过程展示了 I2C 通信、LCD 初始化、字符写入、光标定位和滚动文本控制的完整逻辑。
| 学习目标 | 说明 |
|---|---|
| 理解 I2C 显示链路 | 认识 CanMV K210 如何通过 SCL、SDA 与 LCD1602 I2C 模块通信 |
| 掌握 LCD1602 基础显示 | 学会清屏、定位光标、写入字符和写入字符串 |
| 理解 I2C 地址扫描 | 通过 i2c.scan() 自动查找 LCD1602 的实际通信地址 |
| 理解显示驱动封装 | 将 LCD 底层写入封装成 LcdApi 和 I2cLcd 类 |
| 实现滚动文本显示 | 使用字符串切片和循环刷新,让长文本在 16 列 LCD 上滚动显示 |
文章目录
理论基础
LCD1602 是常见的字符型液晶显示模块,通常可以显示 2 行字符,每行 16 个字符。它适合显示短文本、数字、状态提示和调试信息,不适合显示复杂图像。相比 LCD 图形屏,LCD1602 的显示能力更简单,但它占用资源少、结构清晰,非常适合用来讲解硬件显示、总线通信和字符输出。
普通 LCD1602 如果直接使用并行接口,通常需要多根数据线和控制线。为了减少开发板引脚占用,很多 LCD1602 模块会加上 I2C 转接板。这样开发板只需要连接 VCC、GND、SCL、SDA 四根线,就能通过 I2C 总线向 LCD 发送控制命令和字符数据。本实验使用的就是这种 LCD1602 I2C 显示模块。
I2C 通信中,CanMV K210 作为主机,LCD1602 I2C 模块作为从机。主机需要知道从机地址,才能把数据写入正确设备。常见 LCD1602 I2C 地址可能是 0x27 或 0x3F,不同模块可能存在差异。因此本实验没有直接完全依赖固定地址,而是先使用 i2c.scan() 扫描总线设备,再优先使用扫描到的地址初始化 LCD。这样可以减少不同模块地址不同带来的调试问题。
下面的流程图从电路通信和显示控制两个角度展示本实验。程序创建 I2C 对象后,通过 SCL 和 SDA 与 LCD1602 I2C 模块通信,LCD 驱动类把字符串转换成底层命令和数据,最终在屏幕上显示固定文本或滚动文本。
Python 程序
clear / move_to / putstr
LCD 驱动封装
LcdApi / I2cLcd
I2C 写入
writeto 地址与数据
CanMV K210
IO7=SCL / IO6=SDA
LCD1602 I2C 转接模块
地址 0x27 或 0x3F
LCD1602 液晶屏
2 行 × 16 字符
显示效果
固定文本 / 滚动文本
模块供电
VCC
公共地线
GND
I2C 扫描
i2c.scan()
这个流程可以理解成"字符串---驱动封装---I2C 总线---LCD 显示"。上层代码只需要调用 lcd.putstr() 写入字符串,底层驱动负责把字符拆成 LCD 能识别的控制数据,再通过 I2C 写入模块。这样的封装方式让后续扩展传感器数据显示、菜单显示和 AI 识别结果显示时,不需要反复关心 LCD 的底层通信细节。
硬件设施
本实验围绕 LCD1602 I2C 液晶显示展开。代码中没有使用摄像头、按键、蜂鸣器、电机或传感器,因此这些模块不作为本节讲解重点。软件侧主要依赖 machine.I2C 创建 I2C 通信对象,使用 fpioa_manager.fm 作为兼容模式下的引脚功能参数,借助 time 控制显示停留时间和滚动速度。
接线关系可以先通过下面这张图建立整体印象。LCD1602 I2C 模块只需要连接 VCC、GND、SCL、SDA,其中 SCL 接 CanMV IO7,SDA 接 CanMV IO6。程序中的 scl=7 和 sda=6 与这个接线关系对应。

| 硬件 / 软件 | 作用 | 说明 |
|---|---|---|
| CanMV K210 开发板 | 实验运行平台 | 负责执行 MicroPython 程序,并通过 I2C 总线向 LCD1602 发送显示指令 |
| LCD1602 I2C 液晶模块 | 文本显示外设 | 用于显示两行字符内容,每行最多显示 16 个字符 |
| I2C 转接模块 | LCD 与开发板之间的通信桥接 | 将 I2C 数据转换成 LCD1602 可以识别的控制信号 |
| CanMV IO7 | I2C SCL 时钟线 | 根据代码和接线说明,用于 I2C 通信时钟 |
| CanMV IO6 | I2C SDA 数据线 | 根据代码和接线说明,用于 I2C 数据传输 |
machine.I2C |
I2C 通信模块 | 用于创建软件 I2C 对象、扫描设备地址、向 LCD 写入数据 |
fpioa_manager.fm |
引脚功能管理模块 | 在兼容创建 I2C 时作为 gscl、gsda 参数使用 |
time |
延时控制模块 | 用于 LCD 初始化等待、固定显示停留和滚动文本节奏控制 |
实验中用到的核心模块如下。LCD1602 用于显示字符,I2C 转接板用于减少接线数量,CanMV K210 负责运行程序并通过 I2C 总线发送显示内容。实际调试时,需要同时关注接线、供电、I2C 地址和 LCD 对比度。

接线关系在代码注释中已经给出,程序中的 scl=7 和 sda=6 也进一步验证了 LCD1602 I2C 模块与 CanMV 开发板之间的通信连接。LCD1602 I2C 模块一般只需要 4 根线,供电、接地、时钟线和数据线共同完成显示屏工作。
| LCD1602 I2C 引脚 | CanMV 连接位置 | 代码变量 / 参数 | 对应功能 | 说明 |
|---|---|---|---|---|
| VCC | 5V 或 3.3V | 无 | 模块供电 | 根据 LCD1602 I2C 模块实际规格选择供电电压 |
| GND | GND | 无 | 公共地 | 开发板和 LCD 模块必须共地,通信电平才有参考基准 |
| SCL | IO7 | scl=7 |
I2C 时钟线 | 负责同步 I2C 通信节奏 |
| SDA | IO6 | sda=6 |
I2C 数据线 | 负责传输命令和字符数据 |
| I2C 地址 | 自动扫描或默认 0x27 |
lcd_addr |
LCD 设备地址 | 常见地址为 0x27 或 0x3F,程序会优先使用扫描结果 |
物理接线中的 IO7 和 IO6 并不是直接控制 LCD 的每一个数据脚,而是通过 I2C 总线把命令和字符数据发送给 LCD1602 背后的 I2C 转接模块。程序中的 I2C(I2C.I2C_SOFT, freq=100000, scl=7, sda=6) 建立了软件 I2C 通信对象,后续 writeto() 会把字节数据发送到扫描得到的 LCD 地址。LCD 模块收到数据后,再把这些数据转换成清屏、定位、显示字符、背光控制等具体动作。
完成接线后的整体效果如下。运行前重点检查四个位置:VCC 是否连接到模块支持的供电端,GND 是否共地,SCL 是否接到 IO7,SDA 是否接到 IO6。如果背光亮但没有字符,需要检查 I2C 地址、LCD 初始化和模块背面的对比度电位器。

| 实验现象 | 正常表现 | 异常提示 |
|---|---|---|
| 程序启动 | 串口打印 LCD1602 I2C test start |
没有串口信息时,检查程序是否正常运行 |
| I2C 创建 | 串口显示 I2C 创建成功 | 初始化失败时,检查固件和 I2C 创建参数 |
| I2C 扫描 | 串口打印扫描到的设备地址 | 扫描为空时,检查 SCL、SDA、VCC、GND |
| 固定文本显示 | 屏幕显示 Hello!!! 和 Makerobo kit |
有背光无字符时,检查地址和对比度 |
| 滚动文本显示 | 第一行持续滚动显示 makerobo k210 kit! ^_^ |
滚动太快或太慢时,调整延时参数 |
| 背光状态 | 背光打开,字符便于观察 | 背光不亮时,检查供电和背光控制 |
软件代码
本实验代码围绕 LCD1602 I2C 显示控制展开。程序整体可以分成 LCD 抽象接口、I2C 驱动实现、I2C 创建与地址扫描、固定文本显示、滚动文本显示和主流程调度几个部分。LcdApi 类负责定义 LCD 的通用操作,I2cLcd 类负责把这些操作转换成 I2C 字节写入,主程序负责创建通信对象并调用显示函数。
| 软件环境 | 作用 | 检查重点 |
|---|---|---|
| CanMV IDE | 编辑、运行和调试 K210 程序 | 能识别开发板串口,并能正常运行基础 print() 测试 |
| CanMV 固件 | 提供 machine.I2C 和 fpioa_manager 等模块 |
固件环境需要支持当前 I2C 创建方式 |
| USB 串口驱动 | 让电脑识别开发板串口 | 串口工具中能看到对应端口 |
| 串口终端 | 查看 I2C 初始化和扫描信息 | 能看到 I2C 创建、扫描地址和显示步骤 |
| LCD1602 I2C 模块 | 接收 I2C 数据并显示字符 | 地址、供电、对比度和背光状态需要正常 |
python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
CanMV K210 I2C LCD1602 液晶显示实验 - 单文件兼容版
接线说明:
LCD1602 I2C VCC -> 5V 或 3.3V
LCD1602 I2C GND -> GND
LCD1602 I2C SCL -> CanMV IO7
LCD1602 I2C SDA -> CanMV IO6
常见地址:
0x27 = 39
0x3F = 63
"""
import time
from machine import I2C
from fpioa_manager import fm
DEFAULT_I2C_ADDR = 0x27
class LcdApi:
LCD_CLR = 0x01
LCD_HOME = 0x02
LCD_ENTRY_MODE = 0x04
LCD_ENTRY_INC = 0x02
LCD_ON_CTRL = 0x08
LCD_ON_DISPLAY = 0x04
LCD_ON_CURSOR = 0x02
LCD_ON_BLINK = 0x01
LCD_FUNCTION = 0x20
LCD_FUNCTION_2LINES = 0x08
LCD_FUNCTION_RESET = 0x30
LCD_CGRAM = 0x40
LCD_DDRAM = 0x80
def __init__(self, num_lines, num_columns):
self.num_lines = num_lines
self.num_columns = num_columns
self.cursor_x = 0
self.cursor_y = 0
self.backlight = 1
self.display_off()
self.backlight_on()
self.clear()
self.hal_write_command(self.LCD_ENTRY_MODE | self.LCD_ENTRY_INC)
self.hide_cursor()
self.display_on()
def clear(self):
self.hal_write_command(self.LCD_CLR)
self.hal_write_command(self.LCD_HOME)
self.cursor_x = 0
self.cursor_y = 0
def display_on(self):
self.hal_write_command(self.LCD_ON_CTRL | self.LCD_ON_DISPLAY)
def display_off(self):
self.hal_write_command(self.LCD_ON_CTRL)
def hide_cursor(self):
self.hal_write_command(self.LCD_ON_CTRL | self.LCD_ON_DISPLAY)
def show_cursor(self):
self.hal_write_command(
self.LCD_ON_CTRL | self.LCD_ON_DISPLAY | self.LCD_ON_CURSOR
)
def blink_cursor_on(self):
self.hal_write_command(
self.LCD_ON_CTRL
| self.LCD_ON_DISPLAY
| self.LCD_ON_CURSOR
| self.LCD_ON_BLINK
)
def backlight_on(self):
self.backlight = 1
self.hal_backlight_on()
def backlight_off(self):
self.backlight = 0
self.hal_backlight_off()
def move_to(self, cursor_x, cursor_y):
self.cursor_x = cursor_x
self.cursor_y = cursor_y
addr = cursor_x & 0x3F
if cursor_y & 1:
addr += 0x40
if cursor_y & 2:
addr += 0x14
self.hal_write_command(self.LCD_DDRAM | addr)
def putchar(self, char):
if char != "\n":
self.hal_write_data(ord(char))
self.cursor_x += 1
if self.cursor_x >= self.num_columns or char == "\n":
self.cursor_x = 0
self.cursor_y += 1
if self.cursor_y >= self.num_lines:
self.cursor_y = 0
self.move_to(self.cursor_x, self.cursor_y)
def putstr(self, string):
for char in string:
self.putchar(char)
def custom_char(self, location, charmap):
location &= 0x07
self.hal_write_command(self.LCD_CGRAM | (location << 3))
time.sleep_us(40)
for i in range(8):
self.hal_write_data(charmap[i])
time.sleep_us(40)
self.move_to(self.cursor_x, self.cursor_y)
def hal_backlight_on(self):
pass
def hal_backlight_off(self):
pass
def hal_write_command(self, cmd):
raise NotImplementedError
def hal_write_data(self, data):
raise NotImplementedError
MASK_RS = 0x01
MASK_E = 0x04
SHIFT_BACKLIGHT = 3
SHIFT_DATA = 4
class I2cLcd(LcdApi):
def __init__(self, i2c, i2c_addr, num_lines, num_columns):
self.i2c = i2c
self.i2c_addr = i2c_addr
self.write_i2c_byte(0)
time.sleep_ms(20)
self.hal_write_init_nibble(self.LCD_FUNCTION_RESET)
time.sleep_ms(5)
self.hal_write_init_nibble(self.LCD_FUNCTION_RESET)
time.sleep_ms(1)
self.hal_write_init_nibble(self.LCD_FUNCTION_RESET)
time.sleep_ms(1)
self.hal_write_init_nibble(self.LCD_FUNCTION)
time.sleep_ms(1)
LcdApi.__init__(self, num_lines, num_columns)
cmd = self.LCD_FUNCTION
if num_lines > 1:
cmd |= self.LCD_FUNCTION_2LINES
self.hal_write_command(cmd)
def write_i2c_byte(self, value):
self.i2c.writeto(self.i2c_addr, bytes([value & 0xFF]))
def hal_write_init_nibble(self, nibble):
byte = ((nibble >> 4) & 0x0F) << SHIFT_DATA
self.write_i2c_byte(byte | MASK_E)
self.write_i2c_byte(byte)
def hal_backlight_on(self):
self.write_i2c_byte(1 << SHIFT_BACKLIGHT)
def hal_backlight_off(self):
self.write_i2c_byte(0)
def hal_write_command(self, cmd):
byte = (
(self.backlight << SHIFT_BACKLIGHT)
| (((cmd >> 4) & 0x0F) << SHIFT_DATA)
)
self.write_i2c_byte(byte | MASK_E)
self.write_i2c_byte(byte)
byte = (
(self.backlight << SHIFT_BACKLIGHT)
| ((cmd & 0x0F) << SHIFT_DATA)
)
self.write_i2c_byte(byte | MASK_E)
self.write_i2c_byte(byte)
if cmd <= 3:
time.sleep_ms(5)
def hal_write_data(self, data):
byte = (
MASK_RS
| (self.backlight << SHIFT_BACKLIGHT)
| (((data >> 4) & 0x0F) << SHIFT_DATA)
)
self.write_i2c_byte(byte | MASK_E)
self.write_i2c_byte(byte)
byte = (
MASK_RS
| (self.backlight << SHIFT_BACKLIGHT)
| ((data & 0x0F) << SHIFT_DATA)
)
self.write_i2c_byte(byte | MASK_E)
self.write_i2c_byte(byte)
def create_i2c():
print("Step 1: create soft I2C")
try:
i2c = I2C(
I2C.I2C_SOFT,
freq=100000,
scl=7,
sda=6
)
print("I2C create ok: simple mode")
return i2c
except Exception as e:
print("Simple mode failed:", e)
try:
i2c = I2C(
I2C.I2C_SOFT,
freq=100000,
scl=7,
sda=6,
gscl=fm.fpioa.GPIOHS1,
gsda=fm.fpioa.GPIOHS2
)
print("I2C create ok: gscl gsda mode")
return i2c
except Exception as e:
print("I2C create failed:", e)
return None
def get_lcd_addr(i2c):
print("Step 2: scan I2C device")
try:
addr_list = i2c.scan()
print("I2C scan:", addr_list)
if len(addr_list) > 0:
return addr_list[0]
except Exception as e:
print("I2C scan failed:", e)
print("Use default address:", DEFAULT_I2C_ADDR)
return DEFAULT_I2C_ADDR
def lcd_show_fixed(lcd):
lcd.clear()
lcd.move_to(0, 0)
lcd.putstr("Hello!!!")
lcd.move_to(0, 1)
lcd.putstr("Makerobo kit")
time.sleep(2)
def lcd_scroll(lcd):
space = " "
text = "makerobo k210 kit! ^_^"
text = space + text + space
while True:
for i in range(0, len(text) - 15):
lcd.clear()
lcd.move_to(0, 0)
lcd.putstr(text[i:i + 16])
time.sleep_ms(800)
print("LCD1602 I2C test start")
i2c = create_i2c()
if i2c is None:
print("I2C init failed, please check firmware or pins")
else:
lcd_addr = get_lcd_addr(i2c)
print("Step 3: init LCD, address:", lcd_addr)
lcd = I2cLcd(i2c, lcd_addr, 2, 16)
print("Step 4: display text")
lcd_show_fixed(lcd)
print("Step 5: scroll text")
lcd_scroll(lcd)
这段程序可以分成 LCD 通用接口、I2C LCD 实现、I2C 初始化、地址扫描、固定文本显示和滚动文本显示几个部分。LcdApi 定义了清屏、回到起始位置、打开显示、关闭显示、隐藏光标、移动光标、写入字符、写入字符串和自定义字符等通用操作。它不直接关心 I2C 如何发送数据,只负责定义 LCD 应该具备的显示能力。
I2cLcd 继承了 LcdApi,负责把 LCD 的通用操作转换成 I2C 写入。LCD1602 的 I2C 转接模块通常以 4 位方式传输数据,因此一个命令或字符会被拆成高 4 位和低 4 位分别发送。MASK_RS 用来区分命令和数据,MASK_E 用来产生使能脉冲,SHIFT_BACKLIGHT 控制背光位,SHIFT_DATA 控制数据位移动位置。通过这些掩码和位移操作,程序把普通字符转换成 LCD 能识别的控制字节。
create_i2c() 是兼容初始化函数。程序优先使用简单模式创建软件 I2C,指定频率为 100000,时钟线为 IO7,数据线为 IO6。如果简单模式失败,程序会尝试带 gscl 和 gsda 的兼容模式。get_lcd_addr() 用于扫描 I2C 总线上的设备,扫描到地址后直接使用扫描结果;如果扫描失败,则使用默认地址 0x27。
| 函数 / 方法 | 功能 | 对应现象 |
|---|---|---|
clear() |
清空屏幕并让光标回到起点 | LCD 上原有字符消失,显示区域重新变为空白 |
display_on() |
打开 LCD 显示 | 屏幕内容正常可见 |
display_off() |
关闭 LCD 显示 | 屏幕显示被关闭,但程序仍可继续写入数据 |
hide_cursor() |
隐藏光标 | 显示文字时不出现光标标记 |
show_cursor() |
显示光标 | 当前写入位置出现光标提示 |
blink_cursor_on() |
开启闪烁光标 | 当前光标位置出现闪烁效果 |
backlight_on() |
打开背光 | LCD 背光亮起,字符更容易观察 |
backlight_off() |
关闭背光 | LCD 背光熄灭,屏幕变暗 |
move_to(cursor_x, cursor_y) |
移动显示位置 | 后续字符从指定列和指定行开始显示 |
putchar(char) |
写入单个字符 | LCD 上出现一个字符,光标自动后移 |
putstr(string) |
连续写入字符串 | LCD 上显示一段文本 |
custom_char(location, charmap) |
写入自定义字符图案 | 可在 LCD 上显示自定义小图标 |
write_i2c_byte(value) |
向 LCD 地址写入一个字节 | I2C 总线上发送底层控制数据 |
hal_write_command(cmd) |
写入 LCD 控制命令 | 执行清屏、定位、显示开关等操作 |
hal_write_data(data) |
写入 LCD 字符数据 | 屏幕显示对应 ASCII 字符 |
create_i2c() |
创建软件 I2C 对象 | 串口打印 I2C 创建结果,决定后续能否通信 |
get_lcd_addr(i2c) |
扫描 LCD I2C 地址 | 串口打印扫描到的设备地址 |
lcd_show_fixed(lcd) |
显示固定两行文本 | 第一行显示 Hello!!!,第二行显示 Makerobo kit |
lcd_scroll(lcd) |
循环滚动显示文本 | 第一行出现横向移动的滚动字符 |
主流程从打印 LCD1602 I2C test start 开始,便于在串口终端确认程序已经运行。I2C 创建失败时,程序会输出错误提示并停止后续 LCD 初始化,避免空对象继续调用造成异常。I2C 创建成功后,程序扫描 LCD 地址,创建 I2cLcd(i2c, lcd_addr, 2, 16) 对象,其中 2 表示两行,16 表示每行 16 列。固定文本显示结束后,程序进入 lcd_scroll() 的无限循环,持续刷新屏幕第一行内容,形成滚动效果。
扩展应用
LCD1602 I2C 实验常见问题集中在供电、地址、接线、I2C 初始化和显示对比度几个方向。排查时应先确认 I2C 能否扫描到设备,再判断 LCD 初始化和文本写入是否正常。
| 问题现象 | 可能原因 | 处理思路 |
|---|---|---|
| 串口提示 I2C 初始化失败 | 固件不支持当前 I2C 创建方式,或 IO 引脚参数不兼容 | 保留代码中的兼容模式,检查 CanMV 固件版本和 machine.I2C 支持参数 |
| I2C 扫描结果为空 | SCL、SDA 接反,供电异常,模块未共地 | 按 IO7 接 SCL、IO6 接 SDA 核对接线,确认 VCC 和 GND 连接正常 |
扫描地址不是 0x27 |
LCD1602 I2C 模块地址不同 | 程序会优先使用扫描地址,不需要手动改默认地址 |
| 屏幕有背光但无字符 | 对比度不合适,初始化失败,地址不正确 | 调整 LCD 背后的电位器,观察字符阴影是否出现,并确认扫描地址正确 |
| 屏幕显示乱码 | I2C 通信不稳定,供电不足,字符编码不匹配 | 缩短接线长度,确认模块供电稳定,优先显示英文和数字字符 |
| 滚动速度太慢或太快 | time.sleep_ms(800) 参数不适合观察需求 |
调小数值会加快滚动,调大数值会放慢滚动 |
| 只显示第一行滚动 | 代码中的滚动函数只写入第 0 行 | 当前程序设计为第一行滚动,第二行滚动需要在函数中增加 move_to(0, 1) 和对应文本 |
| 背光亮度正常但字符很淡 | LCD 对比度没有调好 | 调整 I2C 背板上的电位器,让字符阴影和显示内容清晰可见 |
LCD1602 实验的价值不只是把几行字符显示出来,而是建立"程序状态可视化"的硬件思维。很多嵌入式设备并不一定配备复杂屏幕,但仍然需要显示运行模式、采集数据、错误状态、通信结果或调试信息。LCD1602 的字符显示能力有限,却足够承担基础状态提示任务。当前代码已经具备 I2C 初始化、地址扫描、光标定位、字符串写入和循环刷新能力,因此可以自然扩展到更多硬件项目中。
| 应用场景 | 实现思路 | 可扩展能力 |
|---|---|---|
| 设备启动提示 | 开机后显示项目名称、版本号和初始化状态 | 可把不同初始化阶段写入 LCD,辅助判断设备是否正常启动 |
| 传感器数据显示 | 将温度、湿度、距离、光照等数值转换成字符串显示 | 可与 I2C、ADC 或 UART 数据读取结合 |
| 调试信息输出 | 把关键变量、运行模式或错误提示显示到屏幕 | 可减少对串口终端的依赖,让设备离线运行时也能观察状态 |
| 菜单与模式显示 | 使用两行文本显示当前模式和参数值 | 后续可配合按键切换菜单或修改参数 |
| 网络状态提示 | 显示连接中、连接成功、上传成功或通信失败 | 可用于物联网设备状态展示 |
| AI 识别结果显示 | 将摄像头识别到的类别、数量或置信度显示到 LCD | 可把 AI 结果转成文本输出 |
| 滚动公告显示 | 使用字符串切片和循环刷新实现长文本滚动 | 可扩展为状态轮播、课程提示、设备编号或任务进度展示 |
从工程角度看,当前代码的扩展基础比较清楚。LCD 底层通信被封装在 I2cLcd 类中,显示业务只需要调用 clear()、move_to() 和 putstr()。后续接入传感器、按键或 AI 识别模块时,不需要反复关心 LCD 的底层 4 位数据写入,只要把需要展示的信息整理成字符串,就可以复用现有显示接口。这样的结构能把硬件通信细节和业务显示逻辑分开,代码维护成本更低。
总结
本实验通过 CanMV K210 开发板完成了 LCD1602 I2C 液晶显示控制,核心能力包括软件 I2C 创建、SCL 与 SDA 接线映射、I2C 地址扫描、LCD 初始化、背光控制、光标定位、字符串写入和滚动文本刷新。程序从 I2C 通信对象创建开始,把 LCD1602 封装成可操作的显示对象,再通过固定文本和滚动文本展示真实硬件反馈过程。
这类显示实验非常适合作为硬件交互课程的基础案例。串口日志主要服务开发调试,而 LCD 显示更接近真实设备的人机交互界面。后续课程可以继续在这套结构上扩展按键菜单、传感器数据显示、蜂鸣器状态提醒、网络数据上传、AI 摄像头识别结果展示等内容。只要掌握了 I2C 总线和字符显示的基本思路,开发板就不再只是执行代码的板子,而是可以通过屏幕主动表达运行状态的智能硬件节点。