最近完成鸿蒙 LiteOS 环境下的二维码生成实验,基于 STM32L431 主控,移植 libqrencode 编码库,通过 SPI 总线驱动 TFT‑LCD 屏幕,直接在硬件屏幕上生成并打印 QR 二维码。踩过屏幕黑屏、二维码扫码识别失败的坑,把原理、代码逻辑、排坑经验整理分享出来。

项目概述

本实验在华为 LiteOS 实时操作系统上完成,把字符串xxx编码成标准 QR‑Code 二维码,在 SPI 接口的 TFT 液晶屏上渲染输出;串口同步输出原始字符串,手机扫描屏幕二维码可以还原出原始文本,完成 “软件编码‑硬件显示‑手机解码” 完整闭环。

硬件环境

  • 主控芯片:STM32L431VCT6
  • 显示设备:SPI 接口 TFT‑LCD 屏幕
  • 通信外设:SPI1(主机模式),USART1 串口
  • 系统框架:华为 LiteOS RTOS,Keil MDK 开发
  • 编码库:移植 libqrencode 二维码编码库

整体信号链路: 待编码字符串 → LiteOS 显示任务调用编码库生成二维码模块矩阵 → SPI1 总线把像素数据写入 LCD 显存 → 屏幕输出二维码;同时串口打印原始内容;手机摄像头扫码反向解析出原始字符串。

核心原理学习

1.QR 二维码编码原理

QR Code 是矩阵式二维码,依靠黑白方块存储数据,具备大容量、高速识别、容错纠错的特点。 完整编码流程:

  1. 将原始字符串转为二进制位流,包含模式指示符、字符计数、原始数据;
  2. 根据数据长度自动匹配二维码版本,版本 1 对应 21×21 模块矩阵;
  3. 添加终止符、填充码补齐数据长度;
  4. 数据分块,使用Reed‑Solomon(RS)算法生成纠错码字,做数据交织;
  5. 叠加定位角标、校正图形、格式掩码,生成最终模块矩阵; 手机扫码就是以上流程反向执行:定位图案定位二维码位置,读取模块,纠错还原原始信息。

本实验纠错等级为 L 级,输入字符串xxx共 8 个字符,使用版本 1 二维码。

2.SPI 驱动 LCD 屏幕

LCD 与 STM32 使用SPI1同步串行通信,STM32 作为 SPI 主机。 引脚分配:

  • SCK:PE13 串行时钟
  • MOSI:PE15 主机发送数据
  • MISO:PE14 主机接收数据 额外控制线:CS 片选、DC (RS) 命令 / 数据选择脚、RESET 复位脚、背光控制脚。

SPI 配置模式 0:CPOL=0,CPHA=0,8bit 数据,高位先行。 关键逻辑:

  • RS=0:发送寄存器命令
  • RS=1:发送屏幕像素数据 拉低 CS 选中屏幕设备,SPI 逐字节发送,写完拉高 CS 释放总线。LCD_Fill 填充函数就是反复调用写命令、写数据,把二维码黑白方块绘制到屏幕上。

程序架构解读

项目基于 LiteOS 多任务,系统启动后创建两个任务:

  1. view_task(优先级 1)显示任务:二维码编码、屏幕绘制、按键扫描,本实验核心业务任务。
  2. control_task(优先级 0)控制任务:LED 控制、NB‑IoT 模块、物联网协议处理,本实验仅保留框架。

main.c 系统入口

硬件、系统时钟初始化完成,初始化 LCD 屏幕与外设;打印串口提示信息;初始化 LiteOS 内核,创建view_task显示任务,最后启动任务调度器,进入多任务运行。

view_task.c 二维码业务任务

任务循环内定义待编码字符串xxx,调用QR_CODE_Display()函数,完成编码 + 屏幕绘制;执行按键扫描;调用LOS_TaskDelay(10)延时让出 CPU,保证其他任务可以并行执行。

重点:creat_view_task()创建任务的时候,printf 打印待编码字符串,串口输出xxx,用于后期校验。

GUI.c:二维码绘制函数 QR_CODE_Display()

  1. 设置画笔黑白颜色,定义二维码在屏幕上的位置、整体像素大小;
  2. 调用EncodeData(),输入字符串,生成二维码模块矩阵;
  3. 双重循环遍历矩阵,把每一个黑白模块放大为多个像素方块,调用LCD_Fill()填充屏幕显存;
  4. 在屏幕底部打印提示文字 “欢迎来到万物互联的智能世界”。

QR_Encode.c:核心编码函数 EncodeData ()

这是 libqrencode 移植后的核心编码逻辑:

  • 计算输入字符串长度,自动选择二维码版本;
  • 生成数据位流,添加终止符、填充码字;
  • 数据分块,RS 算法生成纠错码字,数据交织;
  • 生成定位图案、掩码,输出模块矩阵,交给 GUI 绘制。

lcd.c SPI 底层驱动

封装LCD_WR_REG()写命令、LCD_WR_DATA()写数据两个底层函数,完成 SPI 时序,上层 GUI 直接调用,不需要关心 SPI 底层时序细节。

完整数据流: 字符串xxx → EncodeData 编码生成模块矩阵 → QR_CODE_Display 遍历矩阵 → SPI 写屏输出图像;同时串口打印原字符串;手机扫码解析得到原字符串。

实验现象

  1. 串口输出(波特率 115200) 串口调试助手依次打印:实验标题、开始实验提示、创建二维码显示任务、待编码内容xxx,串口输出和代码内 printf 语句完全对应,串口链路工作正常。
  2. LCD 屏幕显示效果 屏幕顶部展示实验标题,中间渲染生成完整黑白 QR 二维码,屏幕底部输出欢迎提示语句,二维码图案完整无破损。
  3. 手机扫码验证 手机直接扫描屏幕上的二维码,解码结果输出xxx,和程序内待编码的原始内容完全一致。串口打印、屏幕图像、手机扫码三者结果匹配,实验验证闭环完成。

踩坑记录与问题排查

  1. 烧录程序后 LCD 屏幕无任何显示 一开始怀疑是 SPI 配置代码出错,核对 SPI 模式、引脚复用配置没有问题。回头检查 LCD 初始化复位时序,RESET 引脚需要拉低再拉高维持规定时间。最后定位为屏幕排线松动,重新压紧排线、复位设备之后屏幕正常点亮。

小经验:显示类实验,优先排查硬件接线、复位时序,再去怀疑软件配置。

  1. 二维码图案存在,但是手机扫码识别失败 屏幕可以正常画出二维码,但是手机很难扫出来。观察发现二维码模块尺寸偏小,加上屏幕反光干扰识别。修改绘制函数,增大二维码模块放大倍数,保证黑白色块对比度,重新编译下载后扫码识别稳定。

QR 码识别对图案尺寸、完整度、对比度都有要求。

学习收获

通过本次实验,弄懂 QR 二维码整套编码流程,理解 RS 纠错算法在二维码里面的作用;熟悉 STM32 SPI 主机通信的配置与时序,明白 RS 引脚区分命令和数据的设计意义。 同时加深对 LiteOS 实时操作系统任务创建、任务优先级、延时调度的理解;学会串口 + 屏幕 + 外部设备扫码,多维度交叉校验程序输出结果,能够快速缩小 bug 排查范围。从原始字符串,到编码矩阵,再经过 SPI 外设输出到屏幕,完整走完软件算法到硬件输出的开发链路。

拓展思考:项目可以如何升级

  1. 动态刷新二维码:借助 RTC 实时时钟或者按键触发,定时更新二维码存储内容,可以用作动态验证码、一次性票据场景,提升安全性。
  2. 扫码事件上云:利用板载 BC35G NB‑IoT 模块,把扫码时间、识别到的内容上传物联网云平台,可以实现巡检打卡、简单考勤、物品溯源 demo。
  3. 优化编码能力:切换更高纠错等级 M/Q/H,提升二维码抗污损能力;拓展支持中文 UTF‑8 字符,承载更多业务信息。
  4. UI 美化改造:实现彩色二维码,中间嵌入简易 logo;
  5. 低功耗改造:平时 LCD 休眠,按键唤醒屏幕展示二维码,一段时间之后自动熄屏,适配电池供电的便携设备。
Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐