📝前言

基于NBV2.1 物联网主板 + 鸿蒙 LiteOS完成二维码实验。 一开始我以为这套代码可以直接识别纸质二维码,实际跑通之后才发现:原有工程只实现了字符串编码,LCD 屏幕绘制输出二维码,摄像头解码识别外部二维码只是预留框架,并没有实现。

本文完整记录整个实验:整体架构、核心代码、现象结果、实际踩坑问题、问题排查思路与创新拓展,适合刚接触 LiteOS 多任务嵌入式开发的同学参考。

⚠️开发环境: 硬件:NBV2.1 鸿蒙物联网开发板 OS:HarmonyOS‑LiteOS‑M 屏幕:SPI‑TFT 240*320 外设:DVP 摄像头接口、4 路按键、LED、BC35G NB‑IoT 模组


🏗整体系统架构

本实验采用 LiteOS 多任务设计,两个业务任务分工明确:

表格

任务优先级职责
control_task0(高)硬件主控、LED 状态指示、串口、NB‑IoT 模组业务,预留摄像头解码业务
view_task1(低)LCD 屏幕绘图、按键扫描、二维码编码绘制输出

💡重要概念区分(很多同学这里搞混)

  1. 编码:QR_CODE_Display():输入字符串 → LCD 屏幕画出二维码,供手机扫码读取(本实验已实现)
  2. 解码:摄像头采集图像 → 算法解析纸质二维码得到文本(本工程只有框架,需要自己新增代码)

main函数只做硬件初始化、内核初始化、创建任务;LOS_Start()启动调度之后 main 死循环,所有业务逻辑全部交给任务执行,和裸机开发思维有很大区别。

🧩硬件简要原理

  1. 主控 STM32 运行 LiteOS 操作系统,负责任务调度、图像处理;
  2. SPI‑TFT 屏幕通过 PE9~PE14 引脚与 MCU 相连,完成二维码图形渲染;
  3. 4 路 GPIO 按键,上拉输入模式,软件消抖;
  4. LED1 作为系统运行指示灯,1s 闪烁一次判断任务是否正常调度;
  5. DVP 摄像头接口预留,用于后续拓展二维码解码识别;
  6. BC35G NB‑IoT 模组,可拓展将扫码数据上传云端。

💻核心代码片段

main.c 入口函数

int main(void)
{
    UINT32 uwRet = LOS_OK;
    HAL_Init();                    //硬件底层初始化
    sysclkinit();                  //系统时钟配置
    task_view_init();              //显示模块初始化
    task_control_init();           //控制模块初始化

    printf("\r\n        二维码识别实验        \r\n");
    printf("----------------------------------\r\n");
    printf("开始本实验:\r\n\r\n");

    uwRet = LOS_KernelInit();      //LiteOS内核初始化
    if (uwRet != LOS_OK)
    {
        return LOS_NOK;
    }
    uwRet = creat_view_task();     //创建显示任务
    uwRet = creat_control_task(); //创建控制任务

    LOS_Start();	//开启操作系统任务调度
    while (1)
    {
    }
}

main 函数不执行业务,只做初始化与任务创建。

view_task.c 核心‑二维码绘制函数

void QR_CODE_Display(uint8_t  *qrcode_data)
{
	uint8_t  i,j;
	uint16_t  x,y,p;
	uint16_t  old_point_color = POINT_COLOR;
	uint16_t  old_back_color = BACK_COLOR;
	BACK_COLOR = WHITE;
	POINT_COLOR = BLACK;
    #define   QR_CODE_SIZE     120
    #define   QR_CODE_X        45
    #define   QR_CODE_Y        65

	EncodeData((char *)qrcode_data); //字符串编码生成二维码矩阵
	if(m_nSymbleSize*2 > QR_CODE_SIZE)
	{
		return;
	}
	for(i=0;i<10;i++)
	{
		if((m_nSymbleSize*i*2) > QR_CODE_SIZE)	break;
	}
	p=(i-1)*3;
	x=QR_CODE_X;
	y=QR_CODE_Y;
	for(i=0;i<m_nSymbleSize;i++)
	{
		for(j=0;j<m_nSymbleSize;j++)
		{
			if(m_byModuleData[i][j]==0)
				LCD_Fill(x+p*i,y+p*j,x+p*(i+1)-1,y+p*(j+1)-1,WHITE);
			else
				LCD_Fill(x+p*i,y+p*j,x+p*(i+1)-1,y+p*(j+1)-1,BLACK);
		}
	}
	Gui_StrCenter(0, 240, BLACK, WHITE, "欢迎来到万物互联的智能世界", 16, 0);
	BACK_COLOR=old_back_color;
	POINT_COLOR=old_point_color;
}

逻辑:调用EncodeData()把字符串转为二维码矩阵,双重循环填充黑白方块,在屏幕指定位置渲染出二维码图案。

control_task.c 控制任务主循环

VOID control_task(VOID)
{
    UINT32 uwRet = LOS_OK;
    static uint32_t sys_time = 0;
    printf("** LED灯初始化\r\n");
    while(1)
    {
        ptl_Handle();
        if(HAL_GetTick() - sys_time > 1000)
        {
            sys_time = HAL_GetTick();
            led_toggle(LED1_PORT); //LED1每秒闪烁,判断任务存活
        }
        Get_Bc35g();
        ptl_Heartbeat_Handle();
        BC35G_Handle();
        uwRet = LOS_TaskDelay(10);
        if(uwRet != LOS_OK)
        {
            return;
        }
    }
}

这里目前只有 LED、NB‑IoT 业务,摄像头解码逻辑需要自行添加到此循环内。

📊实验现象 & 预期对比

✅预期目标:

  1. 操作系统、两个任务正常启动,串口打印日志;
  2. LCD 显示标题、二维码,手机扫码读取预置字符串;
  3. LED1 一秒闪烁一次;
  4. 识别外部纸质二维码。

📌实际现象:

  1. 串口完整输出启动日志,两个任务创建成功;
  2. LCD 屏幕显示标题,生成二维码,手机扫码可以读到Hello world!陈菲菲;
  3. LED1 周期闪烁;
  4. ❗无法识别纸质二维码,只有编码输出功能;
  5. 屏幕偶发局部乱码,二维码持续重绘存在轻微闪烁。

🔍偏差原因:

  1. 缺少摄像头采集与解码业务代码,只实现编码;
  2. view_task 每 10ms 无条件重绘二维码,没有做字符串变更判断,无效绘图多;
  3. 多任务共享全局缓冲区,没有互斥锁保护,读写竞争造成随机乱码。

⚡实验踩坑记录(真实遇到的问题)

坑 1:串口打印正常,LCD 黑屏无显示

现象:LED 闪烁、串口日志全部正常,屏幕完全黑屏。 排查:排除主控问题;查看屏幕初始化函数,背光引脚没有使能。 解决:初始化函数增加背光 IO 开启。

坑 2:任务创建失败,上电直接死机

现象:串口打印到 “开始本实验” 就卡死。 原因:修改代码时把任务堆栈0x1000误写成0x0100,堆栈空间不足,LiteOS 任务直接创建失败。

✨心得体会:RTOS 开发,堆栈过小死机经常没有报错打印,是高频踩坑点。

坑 3:二维码随机局部乱码,复现概率低

现象:大部分时间显示正常,偶尔方块错乱。 原因:多任务共用字符串缓冲区,没有互斥锁,一边写一边读,拿到残缺数据绘图。 解决:后续正式项目必须加上 LiteOS 互斥锁保护共享资源。

坑 4:混淆编码和解码,以为代码可以识别纸质二维码

很多初学者会把QR_CODE_Display()解码,实际它是编码输出函数,只是把文字画成二维码。识别外界二维码需要摄像头 + 解码算法,需要自己拓展开发。

坑 5:Key4 按键一直提示等待 NB 联网

现象:按键检测正常,但不会发送 AT 指令。 原因:实验环境没有接 NB 天线,模组没有联网,属于硬件环境限制,不是程序 bug。

💡创新拓展思路

基于现有硬件可以继续做功能升级:

  1. 扫码控制外设:识别特定内容二维码,控制 LED、蜂鸣器,实现无按键控制开发板;
  2. 光敏 + 自动补光:暗光自动打开补光 LED,提升复杂环境二维码识别成功率;
  3. Flash 本地存储扫码记录,按键翻看历史扫码日志;
  4. NB‑IoT 上云:识别结果上传华为 IoT 平台,实现端云联动;
  5. 低功耗改造:LiteOS 休眠唤醒,按键触发扫码,模拟电池供电便携式扫码终端。

📚学习总结

  1. LiteOS 中 main 函数和裸机 main 不一样,main 只管初始化,业务交给任务,LOS_Start()之后 main 不再干活;
  2. 任务优先级、任务堆栈是 RTOS 两大易错点,堆栈太小直接死机,高优先级任务不延时会抢占全部 CPU;
  3. 多任务共享数据一定要考虑资源竞争,偶发乱码 bug 调试难度远大于必现 bug,前期就要做好锁保护;
  4. 做实验一定要分清 “哪些功能已经实现,哪些只是预留框架”,不要被函数名字误导;
  5. 嵌入式调试优先看串口打印,通过日志定位初始化故障,不要只盯着屏幕现象。

本实验完整报告(实验目的、原理图、代码、结果、总结、创新拓展)可以直接用于课程作业。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐