在 LCD 上直接画一个二维码:STM32L4 + QR_Encode 实现“扫码登录“
在 LCD 上直接画一个二维码:STM32L4 + QR_Encode 实现"扫码登录"(鸿蒙智联设备接入)
关键词:STM32L4、二维码、QR_Encode、LCD、SPI、扫码登录、鸿蒙智联、LiteOS
一、需求与方案
物联网设备没有键盘输入,怎么把设备接入信息告诉用户?在屏幕上直接显示一个二维码,用户拿手机一扫就能绑定/登录,是目前最常见的方案。
本文实现的是:STM32L4 主控把"平台地址 + 设备 nodeId"拼成字符串 → 用 QR_Encode 库编码成二维码点阵 → 通过 SPI 驱动的 TFT-LCD 把二维码逐点画出来 → 屏幕下方显示"扫码登录"。
整体流程:
平台地址前缀 + 设备ID
│
▼
QR_Encode 编码(EncodeData)
│
▼
二维码点阵(m_byModuleData[][], m_nSymbleSize)
│
▼
按放大倍数逐点绘制到 LCD(LCD_Fill 填色块)
│
▼
屏幕显示二维码 + "扫码登录"提示
二、硬件准备
| 器件 | 说明 |
|---|---|
| STM32L4 核心板 | 主控 |
| TFT-LCD 屏 | SPI 接口,用于显示二维码 |
| ESP8266 等联网模块 | 获取设备 nodeId,接入 IoT 平台 |
| 手机 | 扫码登录验证 |
LCD 挂在 SPI1 上,引脚分配:
| 引脚 | 功能 |
|---|---|
| PE13 | SPI1_SCK |
| PE14 | SPI1_MISO |
| PE15 | SPI1_MOSI |
三、LCD 初始化(SPI + 寄存器时序)
LCD 驱动初始化分两步:先开 SPI,再按 ILI9341 等常见驱动 IC 的初始化序列写寄存器。
spi_open(spi1,SPI_BAUDRATEPRESCALER_2,Rising)
spi_open 内部会调用 MX_SPI1_Init() 并使能 SPI1,对应引脚配置:
/**SPI1 GPIO Configuration
PE13 ------> SPI1_SCK
PE14 ------> SPI1_MISO
PE15 ------> SPI1_MOSI
*/
GPIO_InitStruct.Pin = GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;
HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
然后 LCD_Init() 里用 LCD_WR_REG() / LCD_WR_DATA() 一条条写初始化寄存器(下面是开头片段,完整序列按屏厂 datasheet 来):
void LCD_Init(void)
{
SPI_GPIO_Init();
LCD_GPIO_init();
LCD_RESET();
LCD_WR_REG(0xCF);
LCD_WR_DATA(0x00);
LCD_WR_DATA(0xC1);
LCD_WR_DATA(0X30);
LCD_WR_REG(0xED);
LCD_WR_DATA(0x64);
LCD_WR_DATA(0x03);
LCD_WR_DATA(0X12);
LCD_WR_DATA(0X81);
LCD_WR_REG(0xE8);
LCD_WR_DATA(0x85);
LCD_WR_DATA(0x10);
LCD_WR_DATA(0x7A);
LCD_WR_REG(0xCB);
LCD_WR_DATA(0x39);
LCD_WR_DATA(0x2C);
LCD_WR_DATA(0x00);
LCD_WR_DATA(0x34);
LCD_WR_DATA(0x02);
LCD_WR_REG(0xF7);
LCD_WR_DATA(0x20);
/* …… 后续寄存器配置省略 …… */
}
四、二维码内容是怎么拼出来的
二维码里存的内容不是随便定的,前缀是平台固定的接入地址,后面跟设备唯一标识 nodeId:
#define QR_STR_HEAD "https://iot.zj-huawei.com/wx/#/device?nodeId=" //二维码生成数据
拼字符串的代码在视图任务里:
static void QRCodeview(void)
{
if(qrcodeflag == 1)
{
return;
}
char strbuffer[sizeof(QR_STR_HEAD) + 20] = {0};
qrcodeflag = 1;
strcat(strbuffer, QR_STR_HEAD);
strcat(strbuffer, (char *)Get_Net_Module_ID());
QR_CODE_Display((uint8_t *)strbuffer);
}
要点:
qrcodeflag是绘制标志,画过一次就置 1,避免每帧重复刷新二维码Get_Net_Module_ID()从联网模块(ESP8266)读取设备 nodeId- 最终二维码内容形如:
https://iot.zj-huawei.com/wx/#/device?nodeId=xxxxx,手机扫码后即可跳转到设备管理页面完成绑定/登录
五、核心:QR_CODE_Display 逐点绘制
这是本次实验的灵魂函数。EncodeData() 把字符串编码成二维码点阵,存到全局数组 m_byModuleData[m_nSymbleSize][m_nSymbleSize];接下来就是把这个点阵放大后画到屏幕上。
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 85 //二维码图片大小
#define QR_CODE_X 150 //设置x轴二维码显示位置
#define QR_CODE_Y 55 //设置Y轴二维码显示位置
EncodeData((char *)qrcode_data);
if(m_nSymbleSize * 2 > QR_CODE_SIZE)
{
// Show_Str(0,0,BLUE,YELLOW,"The QR Code is too large!",12,1);
return;
}
for(i = 0; i < 10; i++)
{
if((m_nSymbleSize * i * 2) > QR_CODE_SIZE)
{
break;
}
}
p = (i - 1) * 3; //点大小
x = QR_CODE_X; //设置x轴二维码显示位置
y = QR_CODE_Y; //设置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);
}
}
}
Show_Str((QR_CODE_SIZE - 64) / 2 + QR_CODE_X, QR_CODE_Y + QR_CODE_SIZE+5, BLACK, YELLOW, "扫码登录",
16, 1); //修改此处的前两个参数调节字体显示位置
BACK_COLOR = old_back_color;
POINT_COLOR = old_point_color;
}
这段代码的 3 个关键设计
1. 自动计算放大倍数
二维码原始点阵通常只有几十像素,直接画太小扫不出来,要放大。算法:
for(i = 0; i < 10; i++)
{
if((m_nSymbleSize * i * 2) > QR_CODE_SIZE) // 放大后尺寸超过 QR_CODE_SIZE(85)
{
break;
}
}
p = (i - 1) * 3; //点大小
先用循环试探放大多少倍不超过 85 像素,再乘 3 作为最终点大小 p。p 越大,每个码点占的屏幕像素越多,扫码越容易识别。
2. 白底黑点
- 码点数据
m_byModuleData[i][j] == 0→ 填白色(背景) - 否则 → 填黑色(码点)
二维码识别需要高对比度,白底黑点是标准做法。
3. 底部提示文字
Show_Str((QR_CODE_SIZE - 64) / 2 + QR_CODE_X, QR_CODE_Y + QR_CODE_SIZE+5, BLACK, YELLOW, "扫码登录", 16, 1);
"扫码登录"四个字水平居中((QR_CODE_SIZE-64)/2 就是为了居中),放在二维码正下方。
六、显示效果与验证
- 上电 → 联网模块获取 nodeId → 屏幕自动显示二维码 + “扫码登录”
- 手机扫码 → 跳转
https://iot.zj-huawei.com/wx/#/device?nodeId=...→ 完成设备绑定/登录 - 串口可打印二维码内容,方便核对
这里插入实际显示效果图(LCD 上的二维码 + 手机扫码结果)
七、踩坑与经验
- 放大倍数要够:二维码过小(比如
p=3以下)手机经常识别失败;本实验点大小按(i-1)*3计算,实测识别稳定。 - 别在循环里反复整屏刷新:
LCD_Fill是按区域填色,画二维码只改码点所在区域;加上qrcodeflag标志保证只画一次,避免闪烁和卡顿。 - 内容长度:QR_STR_HEAD + nodeId 总长度不要超过二维码容量,内容越长码越密,越难扫;buffer 用
sizeof(QR_STR_HEAD) + 20预留空间。 - 对比度:一定要白底黑点,别用反色,很多识别失败都是对比度问题。
八、扩展思路
- 二维码不止能放 URL:把设备 IP、配网信息(SSID+密码)、产品序列号编码进去,就是"一码配网"
- 复用
QR_CODE_Display的思路,把点阵换成图片数组,就能在 LCD 上显示任意黑白图片 - 道闸场景里车牌号也能编成二维码(
QR_CODE_Display_Carid),扫码核验后开闸,后面可以单独写一篇
更多推荐



所有评论(0)