Files
mobile_comm/luatos/air780epm/module/Air780EPM/demo/ws2812/readme.md
T
wangfq 173dd6874f init: Air780EPM 官方 LuatOS 项目代码基线
- 来源: 合宙 LuatOS 官方仓库 air780epm 模块完整代码
- 路径: luatos/air780epm/module/Air780EPM/demo 含官方 demo(含 mqtt/mqtts/socket/uart 等)
- 后续: 基于 demo 开发 UART<->MQTT 数据上报功能
2026-08-31 08:53:49 +08:00

22 KiB
Raw Blame History

WS2812 多模块 Demo

适用产品范围:本 demo 仅适用于合宙 Air1780P / Air1780H / Air1780HV

面向 AI 代理 / 开发者 的自包含文档。本文档描述模块化 WS2812 demo 的目的、结构、API、扩展方式与故障排除。 维护约定:新增/修改 _task.lua 后请同步更新「效果模块」与「项目结构」两节。


目录

  1. 项目概述
  2. 技术栈
  3. 硬件信息
  4. 环境配置
  5. 项目结构解析
  6. 快速开始
  7. 效果模块详解
  8. 公共 APIws2812_config.lua
  9. 字模与文本条带(ws2812_fonts.lua
  10. 运行时配置与串口命令
  11. 编写自定义效果
  12. 故障排除
  13. FAQ
  14. 官方支持渠道

1. 项目概述

在合宙 Air1780P / Air1780H 上驱动一块 22×22 = 484 颗 WS2812 RGB 灯珠的全彩灯板,实现稳定、清晰的滚动文字显示和多种灯光动效。

灯光效果拆成独立、可插拔的任务模块,每个文件对应一个可独立运行的小 demo,main.lua 只负责 require 调度,方便学习、复用与扩展。

1.1 主要功能

  • 六个开箱即用的灯光效果(默认启用色块覆盖/灯珠检测,其他按需取消注释):
模块 效果 速度系数
ws2812_blocks_task 色块覆盖 + 红/绿/蓝/白纯色停留,用于 LED 灯珠坏点检测 1×
ws2812_rainbow_task HSV 彩虹渐变,颜色在矩阵上流动 1×
ws2812_snake_task 带 6 像素拖尾的光点按蛇形走线 S 形跑动 2×
ws2812_sparkle_task 每帧随机 30 颗彩灯,星空闪烁 2×
ws2812_rect_task 从中心向外扩散、再向内收缩的彩色方框 3×
ws2812_scroll_task 横向滚动 "欢迎使用LuatOS"(中文 22×22 + 英文 22×11 半宽) 1×
  • 公共硬件初始化、蛇形坐标映射、HSV 颜色工具集中在 ws2812_config.lua
  • 字模和 UTF-8 条带构建集中在 ws2812_fonts.lua
  • 支持 UART 串口命令实时调节亮度和速度(b=NNN / s=NNN),无需重新烧录;
  • 内置开机复位清零,消除第一颗 LED 上电鬼影。

1.2 与根目录 main.lua 的关系

对比项 根目录 main.lua ws2812/
定位 正式产品程序(色块 2 圈 + 滚动文字) 学习/扩展用的模块化 demo 集合
组织 单文件全部逻辑 多文件按职责拆分
效果 固定两阶段 6 种效果可任意组合(色块覆盖 + 5 种循环效果)
字模 内置 抽到 ws2812_fonts.lua
风格 应用代码 模块化(@module 注释、sys.taskInitrequire 调度)

Luatools 同一时刻只能把一个 main.lua 设为 MAIN。两者不互相依赖,可独立烧录。


2. 技术栈

层级 技术
脚本语言 Lua 5.3(支持位运算符 << >> & |
框架 LuatOSAir1780P / Air1780H
LED 协议 WS2812 单总线归零码(通过 LuatOS ws2812 固件组件驱动)
依赖(固件内置) syslogws2812uartmathstringtableos
外部依赖
编码 UTF-8(中文 3 字节解析)
烧录工具 Luatools 3.x

3. 硬件信息

适用产品范围:本 demo 仅适用于合宙 Air1780P / Air1780H

主控 合宙 Air1780P / Air1780H
灯板 22 × 22 = 484 颗 WS2812
数据引脚 GPIO16PIN97ws2812.GPIO 模式
通信 USB 虚拟串口,命令 115200bps

蛇形走线(本项目物理实际):

y=0  偶行:  LED 0  →  1  → ... → 21   左→右
y=1  奇行:  LED 43 → 42  → ... → 22   右→左
y=2  偶行:  LED 44 → 45  → ... → 65   左→右
...

模块通过 ws2812_xy() 把逻辑坐标 (x,y) 自动映射成正确的物理 LED 编号,业务代码不用关心蛇形。

供电WS2812 单颗全白约 60mA484 颗峰值约 25–30A。彩色实际 2–6A,建议外接 5V/10A 以上 电源并共地;USB 只用于低亮度调试(brightness ≤ 50)。


4. 环境配置

4.1 开发环境

  1. 安装 Luatools 3.x
  2. 下载 Air1780P 对应 LuatOS SoC 固件(.soc必须含 ws2812 组件,固件定义可在 docs.openluat.com 查询);
  3. Type-C USB 连接开发板;
  4. Luatools 新建项目,选择固件。

4.2 生产环境

脚本随固件烧录到片上 Flash(脚本分区约 384KB),上电自动执行 main.lua,独立运行无需额外运行时。灯板需外接 5V 电源并与模组共地(详见第 3 节供电)。

4.3 串口与调试

  • USB 虚拟串口同时承载日志输出与命令输入;
  • 命令通道波特率 115200,在 main.lua 通过 uart.setup(1, 115200) 初始化;
  • 支持 b=NNN(亮度)、s=NNN(速度)等实时命令,详见第 10 节;
  • 若只看日志不发命令,无需额外接线。

4.4 导入 demo 文件

ws2812/ 目录下所有 .lua 文件加入 Luatools 项目的脚本资源:

ws2812/
├── main.lua                 ← 勾选为 MAIN 主脚本
├── ws2812_config.lua
├── ws2812_fonts.lua
├── ws2812_cmd.lua           ← 串口命令模块(随 main.lua 一起加载)
└── ws2812_*_task.lua        ← 全部加入脚本资源(是否启用由 main.lua 控制)

即使 main.lua 里某行 require 被注释,建议也把对应 _task.lua 一起烧录,方便随时取消注释切换效果,只需下次下载脚本即可(1–2 秒)。


5. 项目结构解析

ws2812/
├── main.lua                     # 入口:只负责 require 模块加载调度
├── ws2812_config.lua            # 硬件、坐标、HSV 工具、WS2812 句柄、全局 API
├── ws2812_fonts.lua             # 字模表 + UTF-8 条带构建(滚动任务依赖)
├── ws2812_cmd.lua               # 串口命令接口(b=NNN 亮度 / s=NNN 速度)
├── ws2812_blocks_task.lua       # 色块覆盖 + LED 灯珠检测(红绿蓝白纯色循环)
├── ws2812_rainbow_task.lua      # 彩虹渐变
├── ws2812_snake_task.lua        # 蛇形扫描
├── ws2812_sparkle_task.lua      # 星点闪烁
├── ws2812_rect_task.lua         # 矩形收缩扩散
├── ws2812_scroll_task.lua       # 滚动"欢迎使用LuatOS"
└── readme.md                    # 本文档

加载顺序

main.lua
  ├─ require "ws2812_config"     ① 最先:初始化 WS2812_LEDS、全局工具
  ├─ require "ws2812_*_task"     ② 各效果任务内部再 require config / fonts
  ├─ require "ws2812_cmd"        ③ 串口命令接口(修改 WS2812_CFG 运行时参数)
  └─ sys.run()                   ④ 启动 LuatOS 调度器

每个 _task.lua 自己调用 sys.taskInit(fn)LuatOS 调度器会让它们并发协作(协作式多任务,sys.wait() 让出 CPU)。


6. 快速开始

6.1 启用/切换效果

编辑 main.lua

require "ws2812_config"          -- 必须最先加载

-- 取消/添加注释来启停各效果(默认只启用 LED 检测,避免多个任务抢帧缓冲):
require "ws2812_blocks_task"     -- LED 灯珠检测(逐颗覆盖 + 红绿蓝白纯色循环)
-- require "ws2812_rainbow_task"
-- require "ws2812_snake_task"
-- require "ws2812_sparkle_task"
-- require "ws2812_rect_task"
-- require "ws2812_scroll_task"  -- 若要滚动文字,需把 ws2812_blocks_task.lua 里 DETECT_LOOP 改为 false

require "ws2812_cmd"             -- 串口命令接口(b=NNN/s=NNN,建议保持开启)

保存后在 Luatools 里只下载脚本即可,无需重烧固件。

6.2 多效果并行

多个 require 同时打开时,每个任务都是独立协程,会交替刷新同一帧缓冲。

⚠️ 两个任务同时调用 ws2812_clear() + ws2812.send() 会互相覆盖,出现闪烁。并行仅适合分工明确的任务(如一个负责背景、一个负责前景),或接受快速闪烁的测试场景。日常使用建议一次只启用一个效果

6.3 调亮度/速度

  • 改默认:编辑 ws2812_config.lua
    WS2812_CFG = {
        brightness = 50,
        speed_ms   = 50,
    }
    
  • 运行时:用串口助手发送 b=80 调亮度、s=30 调速度(详见 第 10 节)。

7. 效果模块详解

7.1 ws2812_blocks_task(色块覆盖 / LED 灯珠检测)

用于客户验灯,方便逐色检查每颗 WS2812 是否存在坏点、虚焊、颜色通道缺失。默认 DETECT_LOOP = true 无限循环:

  1. 逐颗覆盖:按物理蛇形顺序从 0 → 483 逐颗点亮随机颜色,验证走线顺序和焊接;
  2. 全红停留 2 秒:检查红色通道;
  3. 全绿停留 2 秒:检查绿色通道;
  4. 全蓝停留 2 秒:检查蓝色通道;
  5. 全白停留 2 秒:检查三通道同时点亮(最耗电,注意供电);
  6. 清屏 1 秒后回到步骤 1。

关键参数(文件顶部可改):

参数 默认 含义
DETECT_LOOP true true=无限循环检测模式;false=开机过渡模式(跑 BLOCK_ROUNDS 圈后退出)
BLOCK_ROUNDS 2 非循环模式下的覆盖圈数
FILL_STEP_MS 10 逐颗覆盖时每颗的间隔 ms
HOLD_MS 300 每轮覆盖填满后的停顿 ms
DETECT_HOLD_MS 2000 红/绿/蓝/白每色停留时间 ms
GAP_MS 1000 一轮结束后清屏停留 ms

坏点判断方法

  • 某位置在所有纯色下都不亮 → LED 损坏或虚焊;
  • 只在某一通道不亮(例如全红时缺一颗)→ 该通道 LED 芯片损坏;
  • 颜色顺序错乱或前一颗影响后一颗 → DIN 走线/焊接问题;
  • 白色时整板变暗/闪烁 → 电源电流不足,降低 brightness

⚠️ 全白模式 484 颗同时点亮电流最大(亮度 255 时约 25–30A),默认亮度 50 时约 5–6A,USB 供电无法支撑,必须外接 5V 电源并共地。

7.2 ws2812_rainbow_task(彩虹渐变)

  • 每帧遍历 22×22 所有 LED
  • 色相 h = ((x+y)*360/44 + frame*3) % 360,斜向彩虹随帧向右上流动;
  • 一轮 120 帧后循环。

关键参数(文件顶部可改):steps = 120、色相步进 frame*3

7.3 ws2812_snake_task(蛇形扫描)

  • 光点按 物理 LED 编号 0→483 跑动,拖尾 6 颗线性渐暗;
  • 因为灯板蛇形走线,视觉上光点"S 形"逐行扫描,是验证蛇形映射最直观的效果;
  • 颜色随位置色相变化。

关键参数trail = 6(拖尾长度),间隔 speed_ms * 2

7.4 ws2812_sparkle_task(星点闪烁)

  • 每帧随机点亮 30 颗 LED,每颗随机色相;
  • 每 100 帧后重新一轮。

关键参数:每轮帧数 100、每帧点数 30

7.5 ws2812_rect_task(矩形收缩扩散)

  • 从中心点 (11,11) 开始画矩形框,半径从 0 扩到 11 再收回;
  • 颜色 h = rr*20 % 360 随半径变化;
  • 越界由 ws2812_set() 自动忽略。

关键参数:间隔 speed_ms * 3(呼吸感更明显)。

7.6 ws2812_scroll_task(滚动文字)

  • 文本默认 "欢迎使用LuatOS"UTF-8);
  • 中文字模 22×22FONT_CN),英文半宽 22×11FONT_EN22),未收录的 ASCII 回退到 5×7 放大字模;
  • 英文字模渲染时 voffset=2 下移 2 排与中文视觉对齐;
  • 文字从右侧(offset = -22)滑入、向左滑出(offset = strip_w-1),无空白帧循环;
  • 每轮通过 6 色区 LCG 选一个新颜色。

改文本:修改 ws2812_scroll_task.lua#L26 local SCROLL_TEXT = "欢迎使用LuatOS"。若含未收录汉字,需要在 ws2812_fonts.luaFONT_CN 里补字模。


8. 公共 APIws2812_config.lua

require "ws2812_config" 后以下符号全局可用。

8.1 常量

名称 类型 说明
LED_W number 22 灯板列数
LED_H number 22 灯板行数
LED_COUNT number 484 LED 总数
LED_GPIO number 16 DIN 引脚编号(PIN97
WS2812_LEDS userdata ws2812 句柄,失败为 nil
WS2812_CFG table {brightness=50, speed_ms=50} 运行时配置,各任务读取

8.2 函数

ws2812_xy(x, y) -> index

蛇形逻辑坐标 → 物理 LED 索引(0-based)。

  • x, y 范围:0..LED_W-10..LED_H-1
  • 奇数行自动水平翻转以匹配蛇形走线。
local idx = ws2812_xy(0, 1)   -- 第 1 行第 0 列 → 物理 43(右→左)

ws2812_hsv2rgb(h, s, v) -> color

HSV 转 24-bit RGB 整数。

  • h 色相 0359s 饱和度 0255v 明度 0255
  • 返回值可直接传给 ws2812.set / ws2812_set
local red = ws2812_hsv2rgb(0, 255, 100)

ws2812_rgb(r, g, b) -> color

RGB 打包为 24-bit 整数。

local white = ws2812_rgb(80, 80, 80)

ws2812_set(x, y, color)

逻辑坐标点亮一颗 LED。越界自动忽略,句柄未就绪时静默丢弃,不会抛错。内部先 ws2812_xy 映射再写缓冲。

ws2812_set(11, 11, ws2812_rgb(255, 0, 0))  -- 中心点红灯
ws2812_send()                                -- 记得发送

ws2812_fill(color)

整片填充同一颜色并立即 send。句柄未就绪时静默返回。

ws2812_fill(ws2812_rgb(0, 0, 60))  -- 整片暗蓝

ws2812_clear()

把整片缓冲写 0。不发送,调用后需要 ws2812_send() 才会真正熄灭。

ws2812_clear()
ws2812_send()

ws2812_send()

发送当前缓冲到灯板。所有 ws2812_set/clear 都是写缓冲,必须调用此函数才会刷新 LED。句柄未就绪(ws2812.create 失败)时静默返回,不会对 nil 调用 C 函数导致崩溃。

ws2812_send()

8.3 底层 ws2812 固件 API

公共函数未覆盖的高级用法可直接调用固件 API:

ws2812.create(ws2812.GPIO, LED_COUNT, LED_GPIO)
ws2812.args(WS2812_LEDS, 20, 30, 35, 20, 0)   -- 本项目实测时序,勿改
ws2812.set(WS2812_LEDS, physical_index, color)
ws2812.send(WS2812_LEDS)

时序 (20,30,35,20,0) 单位 ns,经实测稳定,乱改可能导致颜色错乱或不亮。


9. 字模与文本条带(ws2812_fonts.lua

ws2812_scroll_task 依赖此模块,其他效果不需要。

9.1 字模表

尺寸 内容
FONT_CN 22 行 × 22 列 中文 欢/迎/使/用
FONT_EN22 22 行 × 11 列 英文 L/u/a/t/O/SConsolas 半宽)
FONT5x7 5 字节/字 A–Z 大写、部分小写,作为 ASCII 回退

字符串点阵格式:等长字符串,# = 亮,. = 灭。

9.2 列掩码函数

ws2812_col_mask(glyph, col, voffset) -> integer

把某字形的某一列打包成 22-bit 整数(bit n 表示第 n 行是否亮)。voffset 用于垂直偏移(英文字模传 2 下移 2 排)。

9.3 条带构建

local columns = ws2812_build_strip("欢迎使用LuatOS")
-- columns 是数组,每项是一列的 22-bit 掩码
-- 字间距自动加 2 列空白

内部 UTF-8 解析使用 while 循环(中文 3 字节、ASCII 1 字节)。⚠️ 切勿改成数值 for + 体内 i=i+3,Lua for 循环变量不接受体内修改,会导致中文乱码。

9.4 渲染一帧示例

for y = 0, 21 do
    local bit = 1 << y
    for x = 0, 21 do
        local col_idx = x + offset + 1
        if col_idx >= 1 and col_idx <= #columns then
            if (columns[col_idx] & bit) ~= 0 then
                ws2812_set(x, y, color)
            end
        end
    end
end
ws2812_send()

10. 运行时配置与串口命令

10.1 配置表

所有任务每帧直接读 WS2812_CFG,运行时修改立即生效(下一帧)。

WS2812_CFG.brightness = 80   -- 0255
WS2812_CFG.speed_ms   = 60   -- 202000 ms

10.2 串口命令

ws2812_cmd.lua 在 UART1USB 虚拟串口,115200)注册了命令解析。用任意串口助手(Luatools 自带、SSCOM、minicom 等)发送:

命令 作用 范围
b=80\r\n 设置亮度 0255
s=60\r\n 设置帧间隔 202000 ms(越小越快)
b?\r\n 查询当前亮度
s?\r\n 查询当前速度

交互示例

> b=100
< [cmd] brightness = 100
> s=30
< [cmd] speed_ms = 30
> b?
< [cmd] brightness = 100

实现要点

  • 命令以 \r\n 结尾,支持 \r\n
  • 非法参数自动钳制范围,不会崩溃;
  • 命令在独立 sys.task 里处理,不阻塞 UART 接收回调。

10.3 速度系数

各任务内部对 speed_ms 的使用并不统一,以实现不同的"节奏感":

任务 实际帧间隔
rainbow / scroll speed_ms × 1
snake / sparkle speed_ms × 2
rect speed_ms × 3

11. 编写自定义效果

11.1 模板

ws2812/ 新建 ws2812_myeffect_task.lua

--[[
@module  ws2812_myeffect_task
@summary 我的自定义 WS2812 效果
@version 1.0
@date    2026.08.17
@usage
核心业务逻辑:每帧画一个对角线上移动的红点
]]

require "ws2812_config"

local function myeffect_task()
    if not WS2812_LEDS then return end
    sys.wait(100)  -- 必须等开机复位清零完成

    local pos = 0
    while 1 do
        ws2812_clear()
        local color = ws2812_hsv2rgb(0, 255, WS2812_CFG.brightness)
        ws2812_set(pos, pos, color)         -- 对角线上移动
        ws2812_set(LED_W - 1 - pos, pos, color)
        ws2812_send()
        pos = (pos + 1) % LED_W
        sys.wait(WS2812_CFG.speed_ms)
    end
end

sys.taskInit(myeffect_task)

11.2 在 main.lua 注册

require "ws2812_myeffect_task"

11.3 注意事项

  1. 开头 sys.wait(100):等 config 里 50ms 开机复位发送完成,避免第一帧竞争。
  2. 每帧 clear → set → sendWS2812 没有自动清屏,遗漏 clear() 会让上一帧残留。
  3. 用逻辑坐标 ws2812_set(x,y,color),不要直接 ws2812.set(LEDS, idx, color) 除非你明确要操作物理索引(如 snake 任务)。
  4. 不要在回调里跑循环:长逻辑放在 sys.taskInit 协程里,UART/GPIO 回调只做标志设置。
  5. 避免忙等:用 sys.wait(ms) 让出 CPU,禁止 while true do end 无等待。

12. 故障排除

现象 可能原因 解决
启动报 bad argument #2 to 'timer_start' (number expected, got nil) 任务里 sys.wait(WS2812_CFG.speed_ms) 取到 nil 确认 require "ws2812_config" 在任务文件顶部;config 中字段名是 speed_ms 不是 scroll_ms
开机只有左上角 1 颗微亮 DIN 上电杂波被锁存(鬼影) config 已内置 50ms 复位;若仍存在,检查 330Ω 串联电阻、电源稳定性
完全不亮 GPIO/电源/接线错误 DIN 接 GPIO16/PIN97VCC 5VGND 共地;勿改 ws2812.args
竖线呈之字形 蛇形映射未生效 不要直接使用物理索引;用 ws2812_set(x,y,...);若走线方向相反,在 config 中翻转奇数行判定
颜色错乱、绿色闪烁 电源电流不足 外接 5V/10A+;降低 brightness;检查共地
滚动中文乱码 / 字后多空白列 UTF-8 用了数值 for 必须用 while 循环(见 9.3
串口命令无反应 串口号/波特率/换行 用 Luatools 日志所在虚拟串口;115200;命令末尾回车 \r\n
两个效果同时启用时严重闪烁 两任务都 clear+send 抢缓冲 同一时刻只启用一个效果,或合并到一个任务
日志报 "ws2812.create 失败" 固件不含 ws2812 组件 换用含 ws2812 的 Air1780P LuatOS SoC 固件(固件定义可在 docs.openluat.com 查询)
改了脚本但灯效没变 Luatools MAIN 指向了另一个 main.lua 确认 ws2812/main.lua 被勾选为 MAIN 主脚本

13. FAQ

Q0:本 demo 支持哪些合宙模组? A:本 demo 仅适用于合宙 Air1780P / Air1780H

Q1:为什么拆成这么多文件? A:每个 xxx_task.lua 是一个独立可参考的最小示例,方便学习和复制;公共部分抽到 config/fonts,避免重复。

Q2:能同时跑彩虹 + 滚动文字吗? A:技术上可以(都 require 即可),但两个任务都在 clear + send,会互相覆盖导致闪烁。推荐一次只开一个;如果确实要叠加,需要修改其中一个任务不要 clear、只写自己负责的像素。

Q3:怎么改灯板尺寸(比如改成 16×16)? A:改 ws2812_config.luaLED_W/LED_H/LED_COUNT/LED_GPIO,蛇形映射 ws2812_xy 自动适配;字模是 22 宽,换尺寸后滚动文字可能需要换字模或调整坐标裁剪。

Q4:怎么加新的中文字模? A:在 ws2812_fonts.luaFONT_CN 里加 ["新字"] = { "....", ... },必须 22 行、每行 22 字符。字模可由点阵字模工具生成后解码为 #/.

Q5:亮度设多少合适? A:USB 供电建议 ≤ 50;外接 5V/10A 电源可设 80–150;长时间运行不建议长期 255(电流大、发热明显)。

Q6:为什么英文下移 2 排? A:中文字模基本占满 22 行,英文 22×11 字模上下留白较多,整体视觉偏上;下移 2 排后中英文视觉重心对齐。

Q7speed_ms 设多小会死机? A:代码里串口命令钳制最小 20ms。低于 20ms 可能 WS2812 发送(484 颗约 15ms)占满 CPU 影响系统响应;建议 ≥ 30ms。


14. 官方支持渠道