【CanMV K210】显示交互 LCD1602 I2C 通信与滚动文本显示

在智能硬件项目中,显示屏承担的是"把程序状态变成可读信息"的角色。传感器采集到的数据、设备运行状态、调试提示、错误信息、菜单内容,都需要通过某种方式展示出来。相比只在串口终端打印日志,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 底层写入封装成 LcdApiI2cLcd
实现滚动文本显示 使用字符串切片和循环刷新,让长文本在 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 地址可能是 0x270x3F,不同模块可能存在差异。因此本实验没有直接完全依赖固定地址,而是先使用 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=7sda=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 时作为 gsclgsda 参数使用
time 延时控制模块 用于 LCD 初始化等待、固定显示停留和滚动文本节奏控制

实验中用到的核心模块如下。LCD1602 用于显示字符,I2C 转接板用于减少接线数量,CanMV K210 负责运行程序并通过 I2C 总线发送显示内容。实际调试时,需要同时关注接线、供电、I2C 地址和 LCD 对比度。

接线关系在代码注释中已经给出,程序中的 scl=7sda=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 设备地址 常见地址为 0x270x3F,程序会优先使用扫描结果

物理接线中的 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.I2Cfpioa_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。如果简单模式失败,程序会尝试带 gsclgsda 的兼容模式。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 总线和字符显示的基本思路,开发板就不再只是执行代码的板子,而是可以通过屏幕主动表达运行状态的智能硬件节点。

相关推荐
IT_陈寒1 小时前
SpringBoot那个自动配置的坑,害我排查到凌晨三点
前端·人工智能·后端
常威正在打来福1 小时前
不想让你的网页长得像「AI 做的」?试试这个
人工智能·aigc·ai编程
大模型推理1 小时前
《从 0 实现 SGLang》第 1 篇 · LLM 推理引擎到底在做什么
人工智能
PILIPALAPENG1 小时前
Python 语法速成指南:前端开发者视角(JS 类比版)
前端·人工智能·python
Binary_Soul2 小时前
一文读懂:如何让 Claude Code 拥有"过目不忘"的记忆力
人工智能
黎阳之光2 小时前
黎阳之光:以视频孪生重构智慧医院信息化,打造高标项目核心竞争力
大数据·人工智能·物联网·算法·数字孪生
东风破_2 小时前
Claude Code 实战指南:像带实习生一样让 AI 帮你维护项目
人工智能
常威正在打来福2 小时前
frontend-design入门指南:OpenClaw/Claude Code/Codex 三平台安装教程
人工智能·aigc·ai编程
百度智能云技术站2 小时前
百度 Agent 安全中心:构筑企业智能体的安全底座
人工智能·安全·dubbo