- 来源: 合宙 LuatOS 官方仓库 air780epm 模块完整代码 - 路径: luatos/air780epm/module/Air780EPM/demo 含官方 demo(含 mqtt/mqtts/socket/uart 等) - 后续: 基于 demo 开发 UART<->MQTT 数据上报功能
163 lines
6.7 KiB
Lua
163 lines
6.7 KiB
Lua
--[[
|
||
@module ws2812_config
|
||
@summary WS2812 22×22 点阵公共配置(硬件参数、坐标映射、颜色工具、句柄初始化)
|
||
@version 1.1
|
||
@date 2026.08.17
|
||
@usage
|
||
适用产品:合宙 Air1780P / Air1780H / Air1780HV。
|
||
灯板尺寸不是 22×22 时,请同步修改 LED_W / LED_H / LED_COUNT。
|
||
|
||
本文件由 main.lua 通过 require 自动加载,为各效果模块提供全局 WS2812 句柄与工具函数:
|
||
- LED_W / LED_H / LED_COUNT / LED_GPIO 硬件参数
|
||
- WS2812_CFG 运行时配置(brightness / speed_ms)
|
||
- WS2812_LEDS ws2812 句柄(创建失败为 nil)
|
||
- ws2812_xy(x, y) 蛇形逻辑坐标 → 物理 LED 编号
|
||
- ws2812_hsv2rgb(h, s, v) HSV → RGB888 整数
|
||
- ws2812_rgb(r, g, b) RGB → 24bit 整数
|
||
- ws2812_set(x, y, color) 按逻辑坐标点亮(写缓冲,不发送)
|
||
- ws2812_fill(color) 整片填充并立即发送
|
||
- ws2812_clear() 整片清屏(写缓冲,不发送)
|
||
- ws2812_send() 发送当前缓冲到灯板
|
||
]]
|
||
|
||
-- ==================== 硬件参数 ====================
|
||
-- 根据实际灯板修改这几个值即可适配其他尺寸
|
||
LED_W = 22 -- 灯板列数(水平方向 LED 数量)
|
||
LED_H = 22 -- 灯板行数(垂直方向 LED 数量)
|
||
LED_COUNT = LED_W * LED_H -- LED 总数 = 484
|
||
LED_GPIO = 16 -- DIN 数据引脚接 Air1780P/H/HV 的 GPIO16(PIN97)
|
||
|
||
-- ==================== 运行时可调参数 ====================
|
||
-- 可通过串口命令 b=NNN / s=NNN 在运行时修改,无需重新烧录
|
||
WS2812_CFG = {
|
||
brightness = 50, -- 亮度 0~255。全白 255 时整机峰值电流约 25-30A,注意电源
|
||
speed_ms = 50, -- 默认帧间隔 ms,越小动画越快。各任务会再乘自己的速度系数
|
||
}
|
||
|
||
-- ==================== 蛇形走线映射 ====================
|
||
-- 本灯板物理走线为蛇形:偶数行从左到右,奇数行从右到左
|
||
-- 视觉上的逻辑坐标 (x=列, y=行) 需要通过此函数转换成 ws2812 串中的物理编号
|
||
local SERPENTINE = true
|
||
|
||
--- 逻辑坐标 → 物理 LED 编号
|
||
-- @param x 列 0..LED_W-1(从左到右)
|
||
-- @param y 行 0..LED_H-1(从上到下)
|
||
-- @return 物理 LED 编号 0..LED_COUNT-1
|
||
function ws2812_xy(x, y)
|
||
if SERPENTINE and (y % 2 == 1) then
|
||
-- 奇数行走线方向相反,水平翻转
|
||
x = LED_W - 1 - x
|
||
end
|
||
return y * LED_W + x
|
||
end
|
||
|
||
-- ==================== 颜色工具 ====================
|
||
|
||
--- HSV 颜色空间转 24-bit RGB 整数
|
||
-- @param h 色相 0~359(0=红, 120=绿, 240=蓝)
|
||
-- @param s 饱和度 0~255(0=灰白, 255=纯色)
|
||
-- @param v 明度 0~255(0=黑, 255=最亮)
|
||
-- @return 24-bit RGB 整数,可直接传给 ws2812_set / ws2812.fill
|
||
function ws2812_hsv2rgb(h, s, v)
|
||
-- 标准 HSV→RGB 算法,参考 https://en.wikipedia.org/wiki/HSL_and_HSV
|
||
local c = v * s / 255 / 255 -- 色度(chroma)
|
||
local x = c * (1 - math.abs((h / 60) % 2 - 1)) -- 中间色分量
|
||
local m = v / 255 - c -- 明度匹配增量
|
||
local r, g, b
|
||
-- 根据色相所在的 60° 扇区决定 RGB 排列
|
||
if h < 60 then r, g, b = c, x, 0
|
||
elseif h < 120 then r, g, b = x, c, 0
|
||
elseif h < 180 then r, g, b = 0, c, x
|
||
elseif h < 240 then r, g, b = 0, x, c
|
||
elseif h < 300 then r, g, b = x, 0, c
|
||
else r, g, b = c, 0, x
|
||
end
|
||
-- 加上明度增量并打包成 0xRRGGBB
|
||
return math.floor((r + m) * 255 + 0.5) * 65536
|
||
+ math.floor((g + m) * 255 + 0.5) * 256
|
||
+ math.floor((b + m) * 255 + 0.5)
|
||
end
|
||
|
||
--- 直接用 RGB 三原色打包 24-bit 整数
|
||
-- @param r 红 0~255
|
||
-- @param g 绿 0~255
|
||
-- @param b 蓝 0~255
|
||
-- @return 24-bit RGB 整数
|
||
function ws2812_rgb(r, g, b)
|
||
return r * 65536 + g * 256 + b
|
||
end
|
||
|
||
-- ==================== WS2812 句柄初始化 ====================
|
||
-- ws2812.create(mode, count, pin): 创建 ws2812 句柄
|
||
-- mode = ws2812.GPIO 表示使用普通 GPIO 外设驱动
|
||
-- count = LED_COUNT 灯珠总数
|
||
-- pin = LED_GPIO 数据引脚
|
||
WS2812_LEDS = ws2812.create(ws2812.GPIO, LED_COUNT, LED_GPIO)
|
||
if not WS2812_LEDS then
|
||
log.error("ws2812_config", "ws2812.create 失败,请确认固件含 ws2812 组件")
|
||
else
|
||
-- 设置 WS2812 时序(单位 ns),这是本灯板实测稳定的参数:
|
||
-- T0H=20 T0L=30 T1H=35 T1L=20 T_RESET=0 us
|
||
-- 乱改可能导致颜色错乱或整板不亮
|
||
ws2812.args(WS2812_LEDS, 20, 30, 35, 20, 0)
|
||
|
||
-- 第一次清屏:创建句柄后立即把缓冲区置 0
|
||
-- 避免上电瞬间 DIN 引脚电气杂波被第一颗 LED 误识别为数据位并锁存(开机鬼影)
|
||
for i = 0, LED_COUNT - 1 do
|
||
ws2812.set(WS2812_LEDS, i, 0)
|
||
end
|
||
|
||
-- 后台任务:50ms 后再发送一次真正的复位信号
|
||
-- 此时电源和 DIN 已稳定,能可靠清除所有 LED 的随机锁存
|
||
sys.taskInit(function()
|
||
sys.wait(50)
|
||
for i = 0, LED_COUNT - 1 do
|
||
ws2812.set(WS2812_LEDS, i, 0)
|
||
end
|
||
ws2812.send(WS2812_LEDS)
|
||
log.info("ws2812_config", "LED 已复位清零,共", LED_COUNT, "颗")
|
||
end)
|
||
end
|
||
|
||
-- ==================== 帧缓冲操作函数 ====================
|
||
-- ws2812 的工作方式:先把每颗 LED 的颜色写入内部缓冲,再调用 send() 一次性刷新到灯板
|
||
-- 下面这组函数都做了 nil 句柄保护,即使 ws2812.create 失败也不会崩溃
|
||
|
||
--- 按逻辑坐标点亮一颗 LED(写缓冲,不发送)
|
||
-- @param x 列 0..LED_W-1
|
||
-- @param y 行 0..LED_H-1
|
||
-- @param color 24-bit RGB 整数(ws2812_hsv2rgb / ws2812_rgb 的返回值)
|
||
function ws2812_set(x, y, color)
|
||
if not WS2812_LEDS then return end
|
||
-- 越界保护:不在灯板范围内的坐标静默忽略
|
||
if x >= 0 and x < LED_W and y >= 0 and y < LED_H then
|
||
ws2812.set(WS2812_LEDS, ws2812_xy(x, y), color)
|
||
end
|
||
end
|
||
|
||
--- 整片填充同一颜色并立即发送
|
||
-- @param color 24-bit RGB 整数
|
||
function ws2812_fill(color)
|
||
if not WS2812_LEDS then return end
|
||
for i = 0, LED_COUNT - 1 do
|
||
ws2812.set(WS2812_LEDS, i, color)
|
||
end
|
||
ws2812.send(WS2812_LEDS)
|
||
end
|
||
|
||
--- 整片清屏(写缓冲,不发送)
|
||
-- 调用后必须再调用 ws2812_send() 才会真正熄灭 LED
|
||
function ws2812_clear()
|
||
if not WS2812_LEDS then return end
|
||
for i = 0, LED_COUNT - 1 do
|
||
ws2812.set(WS2812_LEDS, i, 0)
|
||
end
|
||
end
|
||
|
||
--- 发送当前缓冲区到灯板(刷新一帧)
|
||
-- 所有 ws2812_set / ws2812_clear 都是写缓冲,必须调用此函数才会生效
|
||
function ws2812_send()
|
||
if not WS2812_LEDS then return end
|
||
ws2812.send(WS2812_LEDS)
|
||
end
|