Quellcode durchsuchen

fix:修改air8000、air780epm、air780ehm等的fft的demo

mw vor 4 Monaten
Ursprung
Commit
a20e3922c5

+ 77 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/fft/main.lua

@@ -0,0 +1,77 @@
+--[[
+@module  main
+@summary LuatOS用户应用脚本文件入口,总体调度应用逻辑
+@version 1.0
+@date    2025.10.30
+@author  孟伟
+@usage
+本demo是FFT(快速傅里叶变换)测试,支持Q15定点和F32浮点两种实现方式。
+主要功能:
+   - 生成200Hz正弦波测试信号
+   - 使用Q15定点FFT算法处理数据
+   - 使用F32浮点FFT算法处理相同数据
+   - 计算并输出两种实现方式的性能对比(执行时间)
+   - 分析频谱结果,定位并显示主峰频率
+
+更多说明参考本目录下的readme.md文件
+]]
+
+--[[
+必须定义PROJECT和VERSION变量,Luatools工具会用到这两个变量,远程升级功能也会用到这两个变量
+PROJECT:项目名,ascii string类型
+        可以随便定义,只要不使用,就行
+VERSION:项目版本号,ascii string类型
+        如果使用合宙iot.openluat.com进行远程升级,必须按照"XXX.YYY.ZZZ"三段格式定义:
+            X、Y、Z各表示1位数字,三个X表示的数字可以相同,也可以不同,同理三个Y和三个Z表示的数字也是可以相同,可以不同
+            因为历史原因,YYY这三位数字必须存在,但是没有任何用处,可以一直写为000
+        如果不使用合宙iot.openluat.com进行远程升级,根据自己项目的需求,自定义格式即可
+]]
+PROJECT = "fft_test"
+VERSION = "001.000.000"
+
+
+-- 在日志中打印项目名和项目版本号
+log.info("main", PROJECT, VERSION)
+
+
+-- 如果内核固件支持wdt看门狗功能,此处对看门狗进行初始化和定时喂狗处理
+-- 如果脚本程序死循环卡死,就会无法及时喂狗,最终会自动重启
+if wdt then
+    --配置喂狗超时时间为9秒钟
+    wdt.init(9000)
+    --启动一个循环定时器,每隔3秒钟喂一次狗
+    sys.timerLoopStart(wdt.feed, 3000)
+end
+
+
+-- 如果内核固件支持errDump功能,此处进行配置,【强烈建议打开此处的注释】
+-- 因为此功能模块可以记录并且上传脚本在运行过程中出现的语法错误或者其他自定义的错误信息,可以初步分析一些设备运行异常的问题
+-- 以下代码是最基本的用法,更复杂的用法可以详细阅读API说明文档
+-- 启动errDump日志存储并且上传功能,600秒上传一次
+-- if errDump then
+--     errDump.config(true, 600)
+-- end
+
+
+-- 使用LuatOS开发的任何一个项目,都强烈建议使用远程升级FOTA功能
+-- 可以使用合宙的iot.openluat.com平台进行远程升级
+-- 也可以使用客户自己搭建的平台进行远程升级
+-- 远程升级的详细用法,可以参考fota的demo进行使用
+
+
+-- 启动一个循环定时器
+-- 每隔3秒钟打印一次总内存,实时的已使用内存,历史最高的已使用内存情况
+-- 方便分析内存使用是否有异常
+-- sys.timerLoopStart(function()
+--     log.info("mem.lua", rtos.meminfo())
+--     log.info("mem.sys", rtos.meminfo("sys"))
+-- end, 3000)
+
+
+-- 加载FFT测试模块
+require("test_fft")
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 68 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/fft/readme.md

@@ -0,0 +1,68 @@
+## 功能模块介绍
+
+1、main.lua:主程序入口;
+
+2、test_fft.lua:FFT测试模块,包含Q15定点和F32浮点两种实现方式的FFT计算和频谱分析;
+
+## 演示功能概述
+
+本demo是FFT(快速傅里叶变换)测试,支持Q15定点和F32浮点两种实现方式。
+
+主要功能:
+
+   - 生成200Hz正弦波测试信号
+   - 使用Q15定点FFT算法处理数据
+   - 使用F32浮点FFT算法处理相同数据
+   - 计算并输出两种实现方式的性能对比(执行时间)
+   - 分析频谱结果,定位并显示主峰频率
+
+
+## 演示硬件环境
+
+![](https://docs.openluat.com/air780epm/luatos/app/driver/eth/image/RFSvb75NRoEWqYxfCRVcVrOKnsf.jpg)
+
+1、Air780EHM 核心板一块
+
+2、TYPE-C USB数据线一根 + USB转串口数据线一根,Air780EHM 核心板和数据线的硬件接线方式为:
+
+- Air780EHM 核心板通过TYPE-C USB口供电;(外部供电/USB供电 拨动开关 拨到 USB供电一端)
+
+- TYPE-C USB数据线直接插到核心板的TYPE-C USB座子,另外一端连接电脑USB口;
+
+## 演示软件环境
+
+1、Luatools下载调试工具
+
+2、[Air780EHM V2016版本固件](https://docs.openluat.com/air780epm/luatos/firmware/version/)
+
+## 演示操作步骤
+
+1、搭建好演示硬件环境
+
+2、Luatools烧录内核固件和demo脚本代码
+
+3、烧录成功后,自动开机运行
+
+4、luatools可以看到如下日志:
+
+```lua
+[2025-10-31 13:21:49.565][000000000.075] I/pm poweron: Power/Reset
+[2025-10-31 13:21:49.568][000000000.195] self_info 127:model Air780EPM_A11 imei 862419074066563
+[2025-10-31 13:21:49.571][000000000.195] self_info 129:firmware[104] DATA2
+[2025-10-31 13:21:49.575][000000000.195] self_info 131:zone(kbytes) fs 168 script 368
+[2025-10-31 13:21:49.578][000000000.195] I/main LuatOS@Air780EPM base 25.03 bsp V2016 64bit
+[2025-10-31 13:21:49.581][000000000.196] I/main ROM Build: Oct  9 2025 21:38:13
+[2025-10-31 13:21:49.588][000000000.198] W/pins /luadb/pins_AIR780EPM.json not exist!!
+[2025-10-31 13:21:49.590][000000000.200] D/main loadlibs luavm 1048568 17352 17352
+[2025-10-31 13:21:49.592][000000000.200] D/main loadlibs sys   2393280 52996 58740
+[2025-10-31 13:21:49.595][000000000.200] D/main loadlibs psram 2393280 53080 58740
+[2025-10-31 13:21:49.597][000000000.227] I/user.fft q15 测试开始 N=2048 fs=2000 freq=200
+[2025-10-31 13:21:49.599][000000000.848] I/user.fft q15 FFT 完成 耗时:10ms
+[2025-10-31 13:21:49.658][000000001.039] I/user.fft 主峰(Hz/bin) 200.20 205
+[2025-10-31 13:21:49.691][000000001.580] I/user.fft f32 FFT 完成 耗时:24ms
+[2025-10-31 13:21:49.692][000000001.580] I/user.fft 对比(q15 vs f32, ms) 10 / 24
+```
+
+
+
+

+ 185 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/fft/test_fft.lua

@@ -0,0 +1,185 @@
+--[[
+@module  test_fft
+@summary FFT测试功能模块
+@version 1.0
+@date    2025.10.30
+@author  孟伟
+@usage
+本demo是FFT(快速傅里叶变换)测试,支持Q15定点和F32浮点两种实现方式。
+主要功能:
+   - 生成200Hz正弦波测试信号
+   - 使用Q15定点FFT算法处理数据
+   - 使用F32浮点FFT算法处理相同数据
+   - 计算并输出两种实现方式的性能对比(执行时间)
+   - 分析频谱结果,定位并显示主峰频率
+
+本文件没有对外接口,直接在main.lua中require "test_fft"就可以加载运行;
+]]
+
+
+function test_fft_fun()
+    if not fft then
+        -- 如果不支持FFT库,进入死循环并提示错误
+        while 1 do
+            sys.wait(1000) -- 等待1秒
+            log.info("bsp", "此BSP不支持fft库,请检查") -- 输出错误信息
+        end
+    end
+
+    -- FFT 参数配置段
+
+    -- 设置FFT基本参数
+    local N = 2048 -- FFT 点数:决定频率分辨率和计算复杂度,必须是2的幂次方
+    local fs = 2000 -- 采样频率 (Hz):根据奈奎斯特定理,可分析的最高频率为fs/2=1000Hz
+    local freq = 200 -- 测试信号频率 (Hz):生成一个200Hz的正弦波作为测试信号
+
+    -- 输出测试开始信息和参数
+    log.info("fft", "q15 测试开始", "N=" .. N, "fs=" .. fs, "freq=" .. freq)
+
+
+    -- 内存分配段:为FFT计算准备缓冲区
+    -- 分配 zbuff 缓冲区:
+    -- 使用16位整数缓冲区来存储Q15格式的数据
+    -- 每个复数点需要2个16位整数(实部和虚部),所以总大小为 N * 2 * 2 字节
+    local real_i16 = zbuff.create(N * 2) -- 实部缓冲区:存储时域信号的实部
+    local imag_i16 = zbuff.create(N * 2) -- 虚部缓冲区:存储时域信号的虚部(初始为0)
+
+
+    -- 测试信号生成段:生成一个200Hz的正弦波测试信号
+    -- 生成 U12 整数正弦波并写入缓冲区(避免浮点预处理干扰)
+    -- U12格式:12位无符号整数,范围0-4095,2048对应0电平
+    for i = 0, N - 1 do
+        -- 计算时间点:i/fs 表示第i个样本的时间(秒)
+        local t = i / fs
+
+        -- 生成200Hz正弦波:sin(2π * 频率 * 时间)
+        local x = math.sin(2 * math.pi * freq * t)
+
+        -- 将浮点数转换为U12格式:
+        -- 2048为直流偏置(中间值),2047为幅度范围
+        -- +0.5是为了四舍五入到最接近的整数
+        local val = math.floor(2048 + 2047 * x + 0.5)
+
+        -- 数值范围限制:确保在U12的有效范围内(0-4095)
+        if val < 0 then val = 0 end
+        if val > 4095 then val = 4095 end
+
+        -- 将数据写入缓冲区:
+        -- seek定位到正确的位置,每个样本占2字节
+        -- writeU16写入16位无符号整数(低12位有效)
+        real_i16:seek(i * 2, zbuff.SEEK_SET); real_i16:writeU16(val) -- 实部写入正弦波数据
+        imag_i16:seek(i * 2, zbuff.SEEK_SET); imag_i16:writeU16(0) -- 虚部写入0(实数信号)
+    end
+
+
+    -- 旋转因子生成段:为Q15 FFT计算准备旋转因子
+    -- 生成 Q15 旋转因子到 zbuff(避免任何浮点旋转因子)
+    -- 旋转因子用于FFT计算中的复数乘法,长度是FFT点数的一半
+    local Wc_q15 = zbuff.create((N // 2) * 2) -- 余弦旋转因子缓冲区
+    local Ws_q15 = zbuff.create((N // 2) * 2) -- 正弦旋转因子缓冲区(实际存储-sin值)
+
+    -- 生成Q15格式的旋转因子表
+    fft.generate_twiddles_q15_to_zbuff(N, Wc_q15, Ws_q15)
+
+
+    -- 使用定点Q15内核执行FFT
+    -- 执行 Q15 FFT(输入为 U12),显式传入 Q15 旋转因子
+    local t0 = mcu.ticks() -- 记录开始时间
+
+    -- 执行FFT计算:
+    -- real_i16, imag_i16: 输入输出缓冲区(原地计算)
+    -- N: FFT点数
+    -- Wc_q15, Ws_q15: Q15格式旋转因子
+    -- {core = "q15", input_format = "u12"}: 使用Q15定点内核,输入格式为U12
+    fft.run(real_i16, imag_i16, N, Wc_q15, Ws_q15, { core = "q15", input_format = "u12" })
+
+    local t1 = mcu.ticks() -- 记录结束时间
+    -- 输出Q15 FFT计算耗时
+    log.info("fft", "q15 FFT 完成", "耗时:" .. (t1 - t0) .. "ms")
+
+
+    -- 分析FFT结果,找到主要频率成分
+    -- 扫描前半部分频谱(0 ~ fs/2),寻找主峰
+    -- 由于频谱的对称性,只需要分析前N/2个点
+    local peak_k, peak_pow = 1, -1 -- peak_k: 峰值位置, peak_pow: 峰值功率
+
+    -- 遍历所有频率bin(跳过直流分量k=0)
+    for k = 1, (N // 2) - 1 do
+        -- 定位到第k个频率点的实部和虚部
+        real_i16:seek(k * 2, zbuff.SEEK_SET)
+        imag_i16:seek(k * 2, zbuff.SEEK_SET)
+
+        -- 读取实部和虚部值(Q15格式)
+        local rr = real_i16:readI16() -- 实部
+        local ii = imag_i16:readI16() -- 虚部
+
+        -- 计算功率谱:|X[k]|² = real² + imag²
+        -- 功率谱表示该频率成分的能量大小
+        local p = rr * rr + ii * ii
+
+        -- 更新峰值信息
+        if p > peak_pow then
+            peak_pow = p -- 更新最大功率值
+            peak_k = k -- 更新峰值位置
+        end
+    end
+
+    -- 计算峰值对应的实际频率:频率 = bin索引 * 频率分辨率
+    -- 频率分辨率 = 采样频率 / FFT点数
+    local peak_freq = (peak_k) * fs / N
+
+    -- 输出主峰频率信息
+    -- peak_k: 峰值所在的bin索引
+    -- peak_freq: 计算出的实际频率(应该接近200Hz)
+    log.info("fft", "主峰(Hz/bin)", string.format("%.2f", peak_freq), peak_k)
+
+
+    -- 使用浮点F32内核进行相同计算,对比性能
+    -- 比较:使用 f32 内核处理相同输入(验证 q15 与 f32 结果一致性)
+    -- 复制相同的 U12 输入到新的 zbuff(浮点格式)
+
+    -- 为浮点FFT分配缓冲区:每个浮点数占4字节
+    local real_f32 = zbuff.create(N * 4) -- 实部缓冲区(浮点)
+    local imag_f32 = zbuff.create(N * 4) -- 虚部缓冲区(浮点)
+
+    -- 将Q15数据复制到浮点缓冲区
+    for i = 0, N - 1 do
+        -- 从Q15缓冲区读取U12数据
+        real_i16:seek(i * 2, zbuff.SEEK_SET)
+        local val = real_i16:readU16()
+
+        -- 写入浮点缓冲区(直接写入U12原始值,不进行格式转换)
+        real_f32:seek(i * 4, zbuff.SEEK_SET)
+        imag_f32:seek(i * 4, zbuff.SEEK_SET)
+        real_f32:writeF32(val) -- 实部写入原始U12值
+        imag_f32:writeF32(0.0) -- 虚部写入0.0
+    end
+
+    -- 生成 f32 旋转因子(浮点格式)
+    -- 返回Lua table格式的旋转因子,而不是zbuff
+    local Wc, Ws = fft.generate_twiddles(N)
+
+    -- 执行 f32 FFT(输入为 U12)
+    local t2 = mcu.ticks() -- 记录开始时间
+
+    -- 执行浮点FFT计算:
+    -- 使用默认的f32内核,输入格式为u12
+    fft.run(real_f32, imag_f32, N, Wc, Ws, { input_format = "u12" })
+
+    local t3 = mcu.ticks() -- 记录结束时间
+    local dt_f32 = (t3 - t2) -- 计算浮点FFT耗时
+
+    -- 输出浮点FFT计算耗时
+    log.info("fft", "f32 FFT 完成", "耗时:" .. dt_f32 .. "ms")
+
+
+    -- 结果总结段:对比两种实现的性能
+
+
+    -- 总结耗时对比:Q15定点 vs F32浮点
+    -- 在没有硬件浮点加速的嵌入式设备上,Q15会比F32快很多,
+    -- 而780和8000系列都没有硬件浮点加速,建议使用q15计算提高速度,但如果追求计算精度,仍然可以用浮点计算
+    log.info("fft", "对比(q15 vs f32, ms)", string.format("%d / %d", (t1 - t0), dt_f32))
+end
+
+sys.taskInit(test_fft_fun)

+ 77 - 0
module/Air780EPM/demo/fft/main.lua

@@ -0,0 +1,77 @@
+--[[
+@module  main
+@summary LuatOS用户应用脚本文件入口,总体调度应用逻辑
+@version 1.0
+@date    2025.10.30
+@author  孟伟
+@usage
+本demo是FFT(快速傅里叶变换)测试,支持Q15定点和F32浮点两种实现方式。
+主要功能:
+   - 生成200Hz正弦波测试信号
+   - 使用Q15定点FFT算法处理数据
+   - 使用F32浮点FFT算法处理相同数据
+   - 计算并输出两种实现方式的性能对比(执行时间)
+   - 分析频谱结果,定位并显示主峰频率
+
+更多说明参考本目录下的readme.md文件
+]]
+
+--[[
+必须定义PROJECT和VERSION变量,Luatools工具会用到这两个变量,远程升级功能也会用到这两个变量
+PROJECT:项目名,ascii string类型
+        可以随便定义,只要不使用,就行
+VERSION:项目版本号,ascii string类型
+        如果使用合宙iot.openluat.com进行远程升级,必须按照"XXX.YYY.ZZZ"三段格式定义:
+            X、Y、Z各表示1位数字,三个X表示的数字可以相同,也可以不同,同理三个Y和三个Z表示的数字也是可以相同,可以不同
+            因为历史原因,YYY这三位数字必须存在,但是没有任何用处,可以一直写为000
+        如果不使用合宙iot.openluat.com进行远程升级,根据自己项目的需求,自定义格式即可
+]]
+PROJECT = "fft_test"
+VERSION = "001.000.000"
+
+
+-- 在日志中打印项目名和项目版本号
+log.info("main", PROJECT, VERSION)
+
+
+-- 如果内核固件支持wdt看门狗功能,此处对看门狗进行初始化和定时喂狗处理
+-- 如果脚本程序死循环卡死,就会无法及时喂狗,最终会自动重启
+if wdt then
+    --配置喂狗超时时间为9秒钟
+    wdt.init(9000)
+    --启动一个循环定时器,每隔3秒钟喂一次狗
+    sys.timerLoopStart(wdt.feed, 3000)
+end
+
+
+-- 如果内核固件支持errDump功能,此处进行配置,【强烈建议打开此处的注释】
+-- 因为此功能模块可以记录并且上传脚本在运行过程中出现的语法错误或者其他自定义的错误信息,可以初步分析一些设备运行异常的问题
+-- 以下代码是最基本的用法,更复杂的用法可以详细阅读API说明文档
+-- 启动errDump日志存储并且上传功能,600秒上传一次
+-- if errDump then
+--     errDump.config(true, 600)
+-- end
+
+
+-- 使用LuatOS开发的任何一个项目,都强烈建议使用远程升级FOTA功能
+-- 可以使用合宙的iot.openluat.com平台进行远程升级
+-- 也可以使用客户自己搭建的平台进行远程升级
+-- 远程升级的详细用法,可以参考fota的demo进行使用
+
+
+-- 启动一个循环定时器
+-- 每隔3秒钟打印一次总内存,实时的已使用内存,历史最高的已使用内存情况
+-- 方便分析内存使用是否有异常
+-- sys.timerLoopStart(function()
+--     log.info("mem.lua", rtos.meminfo())
+--     log.info("mem.sys", rtos.meminfo("sys"))
+-- end, 3000)
+
+
+-- 加载FFT测试模块
+require("test_fft")
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 68 - 0
module/Air780EPM/demo/fft/readme.md

@@ -0,0 +1,68 @@
+## 功能模块介绍
+
+1、main.lua:主程序入口;
+
+2、test_fft.lua:FFT测试模块,包含Q15定点和F32浮点两种实现方式的FFT计算和频谱分析;
+
+## 演示功能概述
+
+本demo是FFT(快速傅里叶变换)测试,支持Q15定点和F32浮点两种实现方式。
+
+主要功能:
+
+   - 生成200Hz正弦波测试信号
+   - 使用Q15定点FFT算法处理数据
+   - 使用F32浮点FFT算法处理相同数据
+   - 计算并输出两种实现方式的性能对比(执行时间)
+   - 分析频谱结果,定位并显示主峰频率
+
+
+## 演示硬件环境
+
+![](https://docs.openluat.com/air780epm/luatos/app/driver/eth/image/RFSvb75NRoEWqYxfCRVcVrOKnsf.jpg)
+
+1、Air780EPM V1.3版本开发板一块
+
+2、TYPE-C USB数据线一根 + USB转串口数据线一根,Air780EPM V1.3版本开发板和数据线的硬件接线方式为:
+
+- Air780EPM V1.3版本开发板通过TYPE-C USB口供电;(外部供电/USB供电 拨动开关 拨到 USB供电一端)
+
+- TYPE-C USB数据线直接插到核心板的TYPE-C USB座子,另外一端连接电脑USB口;
+
+## 演示软件环境
+
+1、Luatools下载调试工具
+
+2、[Air780EPM V2016版本固件](https://docs.openluat.com/air780epm/luatos/firmware/version/)
+
+## 演示操作步骤
+
+1、搭建好演示硬件环境
+
+2、Luatools烧录内核固件和demo脚本代码
+
+3、烧录成功后,自动开机运行
+
+4、luatools可以看到如下日志:
+
+```lua
+[2025-10-31 13:21:49.565][000000000.075] I/pm poweron: Power/Reset
+[2025-10-31 13:21:49.568][000000000.195] self_info 127:model Air780EPM_A11 imei 862419074066563
+[2025-10-31 13:21:49.571][000000000.195] self_info 129:firmware[104] DATA2
+[2025-10-31 13:21:49.575][000000000.195] self_info 131:zone(kbytes) fs 168 script 368
+[2025-10-31 13:21:49.578][000000000.195] I/main LuatOS@Air780EPM base 25.03 bsp V2016 64bit
+[2025-10-31 13:21:49.581][000000000.196] I/main ROM Build: Oct  9 2025 21:38:13
+[2025-10-31 13:21:49.588][000000000.198] W/pins /luadb/pins_AIR780EPM.json not exist!!
+[2025-10-31 13:21:49.590][000000000.200] D/main loadlibs luavm 1048568 17352 17352
+[2025-10-31 13:21:49.592][000000000.200] D/main loadlibs sys   2393280 52996 58740
+[2025-10-31 13:21:49.595][000000000.200] D/main loadlibs psram 2393280 53080 58740
+[2025-10-31 13:21:49.597][000000000.227] I/user.fft q15 测试开始 N=2048 fs=2000 freq=200
+[2025-10-31 13:21:49.599][000000000.848] I/user.fft q15 FFT 完成 耗时:10ms
+[2025-10-31 13:21:49.658][000000001.039] I/user.fft 主峰(Hz/bin) 200.20 205
+[2025-10-31 13:21:49.691][000000001.580] I/user.fft f32 FFT 完成 耗时:24ms
+[2025-10-31 13:21:49.692][000000001.580] I/user.fft 对比(q15 vs f32, ms) 10 / 24
+```
+
+
+
+

+ 185 - 0
module/Air780EPM/demo/fft/test_fft.lua

@@ -0,0 +1,185 @@
+--[[
+@module  test_fft
+@summary FFT测试功能模块
+@version 1.0
+@date    2025.10.30
+@author  孟伟
+@usage
+本demo是FFT(快速傅里叶变换)测试,支持Q15定点和F32浮点两种实现方式。
+主要功能:
+   - 生成200Hz正弦波测试信号
+   - 使用Q15定点FFT算法处理数据
+   - 使用F32浮点FFT算法处理相同数据
+   - 计算并输出两种实现方式的性能对比(执行时间)
+   - 分析频谱结果,定位并显示主峰频率
+
+本文件没有对外接口,直接在main.lua中require "test_fft"就可以加载运行;
+]]
+
+
+function test_fft_fun()
+    if not fft then
+        -- 如果不支持FFT库,进入死循环并提示错误
+        while 1 do
+            sys.wait(1000) -- 等待1秒
+            log.info("bsp", "此BSP不支持fft库,请检查") -- 输出错误信息
+        end
+    end
+
+    -- FFT 参数配置段
+
+    -- 设置FFT基本参数
+    local N = 2048 -- FFT 点数:决定频率分辨率和计算复杂度,必须是2的幂次方
+    local fs = 2000 -- 采样频率 (Hz):根据奈奎斯特定理,可分析的最高频率为fs/2=1000Hz
+    local freq = 200 -- 测试信号频率 (Hz):生成一个200Hz的正弦波作为测试信号
+
+    -- 输出测试开始信息和参数
+    log.info("fft", "q15 测试开始", "N=" .. N, "fs=" .. fs, "freq=" .. freq)
+
+
+    -- 内存分配段:为FFT计算准备缓冲区
+    -- 分配 zbuff 缓冲区:
+    -- 使用16位整数缓冲区来存储Q15格式的数据
+    -- 每个复数点需要2个16位整数(实部和虚部),所以总大小为 N * 2 * 2 字节
+    local real_i16 = zbuff.create(N * 2) -- 实部缓冲区:存储时域信号的实部
+    local imag_i16 = zbuff.create(N * 2) -- 虚部缓冲区:存储时域信号的虚部(初始为0)
+
+
+    -- 测试信号生成段:生成一个200Hz的正弦波测试信号
+    -- 生成 U12 整数正弦波并写入缓冲区(避免浮点预处理干扰)
+    -- U12格式:12位无符号整数,范围0-4095,2048对应0电平
+    for i = 0, N - 1 do
+        -- 计算时间点:i/fs 表示第i个样本的时间(秒)
+        local t = i / fs
+
+        -- 生成200Hz正弦波:sin(2π * 频率 * 时间)
+        local x = math.sin(2 * math.pi * freq * t)
+
+        -- 将浮点数转换为U12格式:
+        -- 2048为直流偏置(中间值),2047为幅度范围
+        -- +0.5是为了四舍五入到最接近的整数
+        local val = math.floor(2048 + 2047 * x + 0.5)
+
+        -- 数值范围限制:确保在U12的有效范围内(0-4095)
+        if val < 0 then val = 0 end
+        if val > 4095 then val = 4095 end
+
+        -- 将数据写入缓冲区:
+        -- seek定位到正确的位置,每个样本占2字节
+        -- writeU16写入16位无符号整数(低12位有效)
+        real_i16:seek(i * 2, zbuff.SEEK_SET); real_i16:writeU16(val) -- 实部写入正弦波数据
+        imag_i16:seek(i * 2, zbuff.SEEK_SET); imag_i16:writeU16(0) -- 虚部写入0(实数信号)
+    end
+
+
+    -- 旋转因子生成段:为Q15 FFT计算准备旋转因子
+    -- 生成 Q15 旋转因子到 zbuff(避免任何浮点旋转因子)
+    -- 旋转因子用于FFT计算中的复数乘法,长度是FFT点数的一半
+    local Wc_q15 = zbuff.create((N // 2) * 2) -- 余弦旋转因子缓冲区
+    local Ws_q15 = zbuff.create((N // 2) * 2) -- 正弦旋转因子缓冲区(实际存储-sin值)
+
+    -- 生成Q15格式的旋转因子表
+    fft.generate_twiddles_q15_to_zbuff(N, Wc_q15, Ws_q15)
+
+
+    -- 使用定点Q15内核执行FFT
+    -- 执行 Q15 FFT(输入为 U12),显式传入 Q15 旋转因子
+    local t0 = mcu.ticks() -- 记录开始时间
+
+    -- 执行FFT计算:
+    -- real_i16, imag_i16: 输入输出缓冲区(原地计算)
+    -- N: FFT点数
+    -- Wc_q15, Ws_q15: Q15格式旋转因子
+    -- {core = "q15", input_format = "u12"}: 使用Q15定点内核,输入格式为U12
+    fft.run(real_i16, imag_i16, N, Wc_q15, Ws_q15, { core = "q15", input_format = "u12" })
+
+    local t1 = mcu.ticks() -- 记录结束时间
+    -- 输出Q15 FFT计算耗时
+    log.info("fft", "q15 FFT 完成", "耗时:" .. (t1 - t0) .. "ms")
+
+
+    -- 分析FFT结果,找到主要频率成分
+    -- 扫描前半部分频谱(0 ~ fs/2),寻找主峰
+    -- 由于频谱的对称性,只需要分析前N/2个点
+    local peak_k, peak_pow = 1, -1 -- peak_k: 峰值位置, peak_pow: 峰值功率
+
+    -- 遍历所有频率bin(跳过直流分量k=0)
+    for k = 1, (N // 2) - 1 do
+        -- 定位到第k个频率点的实部和虚部
+        real_i16:seek(k * 2, zbuff.SEEK_SET)
+        imag_i16:seek(k * 2, zbuff.SEEK_SET)
+
+        -- 读取实部和虚部值(Q15格式)
+        local rr = real_i16:readI16() -- 实部
+        local ii = imag_i16:readI16() -- 虚部
+
+        -- 计算功率谱:|X[k]|² = real² + imag²
+        -- 功率谱表示该频率成分的能量大小
+        local p = rr * rr + ii * ii
+
+        -- 更新峰值信息
+        if p > peak_pow then
+            peak_pow = p -- 更新最大功率值
+            peak_k = k -- 更新峰值位置
+        end
+    end
+
+    -- 计算峰值对应的实际频率:频率 = bin索引 * 频率分辨率
+    -- 频率分辨率 = 采样频率 / FFT点数
+    local peak_freq = (peak_k) * fs / N
+
+    -- 输出主峰频率信息
+    -- peak_k: 峰值所在的bin索引
+    -- peak_freq: 计算出的实际频率(应该接近200Hz)
+    log.info("fft", "主峰(Hz/bin)", string.format("%.2f", peak_freq), peak_k)
+
+
+    -- 使用浮点F32内核进行相同计算,对比性能
+    -- 比较:使用 f32 内核处理相同输入(验证 q15 与 f32 结果一致性)
+    -- 复制相同的 U12 输入到新的 zbuff(浮点格式)
+
+    -- 为浮点FFT分配缓冲区:每个浮点数占4字节
+    local real_f32 = zbuff.create(N * 4) -- 实部缓冲区(浮点)
+    local imag_f32 = zbuff.create(N * 4) -- 虚部缓冲区(浮点)
+
+    -- 将Q15数据复制到浮点缓冲区
+    for i = 0, N - 1 do
+        -- 从Q15缓冲区读取U12数据
+        real_i16:seek(i * 2, zbuff.SEEK_SET)
+        local val = real_i16:readU16()
+
+        -- 写入浮点缓冲区(直接写入U12原始值,不进行格式转换)
+        real_f32:seek(i * 4, zbuff.SEEK_SET)
+        imag_f32:seek(i * 4, zbuff.SEEK_SET)
+        real_f32:writeF32(val) -- 实部写入原始U12值
+        imag_f32:writeF32(0.0) -- 虚部写入0.0
+    end
+
+    -- 生成 f32 旋转因子(浮点格式)
+    -- 返回Lua table格式的旋转因子,而不是zbuff
+    local Wc, Ws = fft.generate_twiddles(N)
+
+    -- 执行 f32 FFT(输入为 U12)
+    local t2 = mcu.ticks() -- 记录开始时间
+
+    -- 执行浮点FFT计算:
+    -- 使用默认的f32内核,输入格式为u12
+    fft.run(real_f32, imag_f32, N, Wc, Ws, { input_format = "u12" })
+
+    local t3 = mcu.ticks() -- 记录结束时间
+    local dt_f32 = (t3 - t2) -- 计算浮点FFT耗时
+
+    -- 输出浮点FFT计算耗时
+    log.info("fft", "f32 FFT 完成", "耗时:" .. dt_f32 .. "ms")
+
+
+    -- 结果总结段:对比两种实现的性能
+
+
+    -- 总结耗时对比:Q15定点 vs F32浮点
+    -- 在没有硬件浮点加速的嵌入式设备上,Q15会比F32快很多,
+    -- 而780和8000系列都没有硬件浮点加速,建议使用q15计算提高速度,但如果追求计算精度,仍然可以用浮点计算
+    log.info("fft", "对比(q15 vs f32, ms)", string.format("%d / %d", (t1 - t0), dt_f32))
+end
+
+sys.taskInit(test_fft_fun)

+ 77 - 0
module/Air8000/demo/fft/main.lua

@@ -0,0 +1,77 @@
+--[[
+@module  main
+@summary LuatOS用户应用脚本文件入口,总体调度应用逻辑
+@version 1.0
+@date    2025.10.30
+@author  孟伟
+@usage
+本demo是FFT(快速傅里叶变换)测试,支持Q15定点和F32浮点两种实现方式。
+主要功能:
+   - 生成200Hz正弦波测试信号
+   - 使用Q15定点FFT算法处理数据
+   - 使用F32浮点FFT算法处理相同数据
+   - 计算并输出两种实现方式的性能对比(执行时间)
+   - 分析频谱结果,定位并显示主峰频率
+
+更多说明参考本目录下的readme.md文件
+]]
+
+--[[
+必须定义PROJECT和VERSION变量,Luatools工具会用到这两个变量,远程升级功能也会用到这两个变量
+PROJECT:项目名,ascii string类型
+        可以随便定义,只要不使用,就行
+VERSION:项目版本号,ascii string类型
+        如果使用合宙iot.openluat.com进行远程升级,必须按照"XXX.YYY.ZZZ"三段格式定义:
+            X、Y、Z各表示1位数字,三个X表示的数字可以相同,也可以不同,同理三个Y和三个Z表示的数字也是可以相同,可以不同
+            因为历史原因,YYY这三位数字必须存在,但是没有任何用处,可以一直写为000
+        如果不使用合宙iot.openluat.com进行远程升级,根据自己项目的需求,自定义格式即可
+]]
+PROJECT = "fft_test"
+VERSION = "001.000.000"
+
+
+-- 在日志中打印项目名和项目版本号
+log.info("main", PROJECT, VERSION)
+
+
+-- 如果内核固件支持wdt看门狗功能,此处对看门狗进行初始化和定时喂狗处理
+-- 如果脚本程序死循环卡死,就会无法及时喂狗,最终会自动重启
+if wdt then
+    --配置喂狗超时时间为9秒钟
+    wdt.init(9000)
+    --启动一个循环定时器,每隔3秒钟喂一次狗
+    sys.timerLoopStart(wdt.feed, 3000)
+end
+
+
+-- 如果内核固件支持errDump功能,此处进行配置,【强烈建议打开此处的注释】
+-- 因为此功能模块可以记录并且上传脚本在运行过程中出现的语法错误或者其他自定义的错误信息,可以初步分析一些设备运行异常的问题
+-- 以下代码是最基本的用法,更复杂的用法可以详细阅读API说明文档
+-- 启动errDump日志存储并且上传功能,600秒上传一次
+-- if errDump then
+--     errDump.config(true, 600)
+-- end
+
+
+-- 使用LuatOS开发的任何一个项目,都强烈建议使用远程升级FOTA功能
+-- 可以使用合宙的iot.openluat.com平台进行远程升级
+-- 也可以使用客户自己搭建的平台进行远程升级
+-- 远程升级的详细用法,可以参考fota的demo进行使用
+
+
+-- 启动一个循环定时器
+-- 每隔3秒钟打印一次总内存,实时的已使用内存,历史最高的已使用内存情况
+-- 方便分析内存使用是否有异常
+-- sys.timerLoopStart(function()
+--     log.info("mem.lua", rtos.meminfo())
+--     log.info("mem.sys", rtos.meminfo("sys"))
+-- end, 3000)
+
+
+-- 加载FFT测试模块
+require("test_fft")
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 68 - 0
module/Air8000/demo/fft/readme.md

@@ -0,0 +1,68 @@
+## 功能模块介绍
+
+1、main.lua:主程序入口;
+
+2、test_fft.lua:FFT测试模块,包含Q15定点和F32浮点两种实现方式的FFT计算和频谱分析;
+
+## 演示功能概述
+
+本demo是FFT(快速傅里叶变换)测试,支持Q15定点和F32浮点两种实现方式。
+
+主要功能:
+
+   - 生成200Hz正弦波测试信号
+   - 使用Q15定点FFT算法处理数据
+   - 使用F32浮点FFT算法处理相同数据
+   - 计算并输出两种实现方式的性能对比(执行时间)
+   - 分析频谱结果,定位并显示主峰频率
+
+
+## 演示硬件环境
+
+![](https://docs.openluat.com/air8000/luatos/common/hwenv/image/Air8000_core_board1.jpg)
+
+1、Air8000 核心板一块
+
+2、TYPE-C USB数据线一根 + USB转串口数据线一根,Air8000 核心板和数据线的硬件接线方式为:
+
+- Air8000 核心板通过TYPE-C USB口供电;(外部供电/USB供电 拨动开关 拨到 USB供电一端)
+
+- TYPE-C USB数据线直接插到核心板的TYPE-C USB座子,另外一端连接电脑USB口;
+
+## 演示软件环境
+
+1、Luatools下载调试工具
+
+2、[Air8000 V2016版本固件](https://docs.openluat.com/air8000/luatos/firmware/)
+
+## 演示操作步骤
+
+1、搭建好演示硬件环境
+
+2、Luatools烧录内核固件和demo脚本代码
+
+3、烧录成功后,自动开机运行
+
+4、luatools可以看到如下日志:
+
+```lua
+[2025-10-31 13:21:49.565][000000000.075] I/pm poweron: Power/Reset
+[2025-10-31 13:21:49.568][000000000.195] self_info 127:model Air780EPM_A11 imei 862419074066563
+[2025-10-31 13:21:49.571][000000000.195] self_info 129:firmware[104] DATA2
+[2025-10-31 13:21:49.575][000000000.195] self_info 131:zone(kbytes) fs 168 script 368
+[2025-10-31 13:21:49.578][000000000.195] I/main LuatOS@Air780EPM base 25.03 bsp V2016 64bit
+[2025-10-31 13:21:49.581][000000000.196] I/main ROM Build: Oct  9 2025 21:38:13
+[2025-10-31 13:21:49.588][000000000.198] W/pins /luadb/pins_AIR780EPM.json not exist!!
+[2025-10-31 13:21:49.590][000000000.200] D/main loadlibs luavm 1048568 17352 17352
+[2025-10-31 13:21:49.592][000000000.200] D/main loadlibs sys   2393280 52996 58740
+[2025-10-31 13:21:49.595][000000000.200] D/main loadlibs psram 2393280 53080 58740
+[2025-10-31 13:21:49.597][000000000.227] I/user.fft q15 测试开始 N=2048 fs=2000 freq=200
+[2025-10-31 13:21:49.599][000000000.848] I/user.fft q15 FFT 完成 耗时:10ms
+[2025-10-31 13:21:49.658][000000001.039] I/user.fft 主峰(Hz/bin) 200.20 205
+[2025-10-31 13:21:49.691][000000001.580] I/user.fft f32 FFT 完成 耗时:24ms
+[2025-10-31 13:21:49.692][000000001.580] I/user.fft 对比(q15 vs f32, ms) 10 / 24
+```
+
+
+
+

+ 185 - 0
module/Air8000/demo/fft/test_fft.lua

@@ -0,0 +1,185 @@
+--[[
+@module  test_fft
+@summary FFT测试功能模块
+@version 1.0
+@date    2025.10.30
+@author  孟伟
+@usage
+本demo是FFT(快速傅里叶变换)测试,支持Q15定点和F32浮点两种实现方式。
+主要功能:
+   - 生成200Hz正弦波测试信号
+   - 使用Q15定点FFT算法处理数据
+   - 使用F32浮点FFT算法处理相同数据
+   - 计算并输出两种实现方式的性能对比(执行时间)
+   - 分析频谱结果,定位并显示主峰频率
+
+本文件没有对外接口,直接在main.lua中require "test_fft"就可以加载运行;
+]]
+
+
+function test_fft_fun()
+    if not fft then
+        -- 如果不支持FFT库,进入死循环并提示错误
+        while 1 do
+            sys.wait(1000) -- 等待1秒
+            log.info("bsp", "此BSP不支持fft库,请检查") -- 输出错误信息
+        end
+    end
+
+    -- FFT 参数配置段
+
+    -- 设置FFT基本参数
+    local N = 2048 -- FFT 点数:决定频率分辨率和计算复杂度,必须是2的幂次方
+    local fs = 2000 -- 采样频率 (Hz):根据奈奎斯特定理,可分析的最高频率为fs/2=1000Hz
+    local freq = 200 -- 测试信号频率 (Hz):生成一个200Hz的正弦波作为测试信号
+
+    -- 输出测试开始信息和参数
+    log.info("fft", "q15 测试开始", "N=" .. N, "fs=" .. fs, "freq=" .. freq)
+
+
+    -- 内存分配段:为FFT计算准备缓冲区
+    -- 分配 zbuff 缓冲区:
+    -- 使用16位整数缓冲区来存储Q15格式的数据
+    -- 每个复数点需要2个16位整数(实部和虚部),所以总大小为 N * 2 * 2 字节
+    local real_i16 = zbuff.create(N * 2) -- 实部缓冲区:存储时域信号的实部
+    local imag_i16 = zbuff.create(N * 2) -- 虚部缓冲区:存储时域信号的虚部(初始为0)
+
+
+    -- 测试信号生成段:生成一个200Hz的正弦波测试信号
+    -- 生成 U12 整数正弦波并写入缓冲区(避免浮点预处理干扰)
+    -- U12格式:12位无符号整数,范围0-4095,2048对应0电平
+    for i = 0, N - 1 do
+        -- 计算时间点:i/fs 表示第i个样本的时间(秒)
+        local t = i / fs
+
+        -- 生成200Hz正弦波:sin(2π * 频率 * 时间)
+        local x = math.sin(2 * math.pi * freq * t)
+
+        -- 将浮点数转换为U12格式:
+        -- 2048为直流偏置(中间值),2047为幅度范围
+        -- +0.5是为了四舍五入到最接近的整数
+        local val = math.floor(2048 + 2047 * x + 0.5)
+
+        -- 数值范围限制:确保在U12的有效范围内(0-4095)
+        if val < 0 then val = 0 end
+        if val > 4095 then val = 4095 end
+
+        -- 将数据写入缓冲区:
+        -- seek定位到正确的位置,每个样本占2字节
+        -- writeU16写入16位无符号整数(低12位有效)
+        real_i16:seek(i * 2, zbuff.SEEK_SET); real_i16:writeU16(val) -- 实部写入正弦波数据
+        imag_i16:seek(i * 2, zbuff.SEEK_SET); imag_i16:writeU16(0) -- 虚部写入0(实数信号)
+    end
+
+
+    -- 旋转因子生成段:为Q15 FFT计算准备旋转因子
+    -- 生成 Q15 旋转因子到 zbuff(避免任何浮点旋转因子)
+    -- 旋转因子用于FFT计算中的复数乘法,长度是FFT点数的一半
+    local Wc_q15 = zbuff.create((N // 2) * 2) -- 余弦旋转因子缓冲区
+    local Ws_q15 = zbuff.create((N // 2) * 2) -- 正弦旋转因子缓冲区(实际存储-sin值)
+
+    -- 生成Q15格式的旋转因子表
+    fft.generate_twiddles_q15_to_zbuff(N, Wc_q15, Ws_q15)
+
+
+    -- 使用定点Q15内核执行FFT
+    -- 执行 Q15 FFT(输入为 U12),显式传入 Q15 旋转因子
+    local t0 = mcu.ticks() -- 记录开始时间
+
+    -- 执行FFT计算:
+    -- real_i16, imag_i16: 输入输出缓冲区(原地计算)
+    -- N: FFT点数
+    -- Wc_q15, Ws_q15: Q15格式旋转因子
+    -- {core = "q15", input_format = "u12"}: 使用Q15定点内核,输入格式为U12
+    fft.run(real_i16, imag_i16, N, Wc_q15, Ws_q15, { core = "q15", input_format = "u12" })
+
+    local t1 = mcu.ticks() -- 记录结束时间
+    -- 输出Q15 FFT计算耗时
+    log.info("fft", "q15 FFT 完成", "耗时:" .. (t1 - t0) .. "ms")
+
+
+    -- 分析FFT结果,找到主要频率成分
+    -- 扫描前半部分频谱(0 ~ fs/2),寻找主峰
+    -- 由于频谱的对称性,只需要分析前N/2个点
+    local peak_k, peak_pow = 1, -1 -- peak_k: 峰值位置, peak_pow: 峰值功率
+
+    -- 遍历所有频率bin(跳过直流分量k=0)
+    for k = 1, (N // 2) - 1 do
+        -- 定位到第k个频率点的实部和虚部
+        real_i16:seek(k * 2, zbuff.SEEK_SET)
+        imag_i16:seek(k * 2, zbuff.SEEK_SET)
+
+        -- 读取实部和虚部值(Q15格式)
+        local rr = real_i16:readI16() -- 实部
+        local ii = imag_i16:readI16() -- 虚部
+
+        -- 计算功率谱:|X[k]|² = real² + imag²
+        -- 功率谱表示该频率成分的能量大小
+        local p = rr * rr + ii * ii
+
+        -- 更新峰值信息
+        if p > peak_pow then
+            peak_pow = p -- 更新最大功率值
+            peak_k = k -- 更新峰值位置
+        end
+    end
+
+    -- 计算峰值对应的实际频率:频率 = bin索引 * 频率分辨率
+    -- 频率分辨率 = 采样频率 / FFT点数
+    local peak_freq = (peak_k) * fs / N
+
+    -- 输出主峰频率信息
+    -- peak_k: 峰值所在的bin索引
+    -- peak_freq: 计算出的实际频率(应该接近200Hz)
+    log.info("fft", "主峰(Hz/bin)", string.format("%.2f", peak_freq), peak_k)
+
+
+    -- 使用浮点F32内核进行相同计算,对比性能
+    -- 比较:使用 f32 内核处理相同输入(验证 q15 与 f32 结果一致性)
+    -- 复制相同的 U12 输入到新的 zbuff(浮点格式)
+
+    -- 为浮点FFT分配缓冲区:每个浮点数占4字节
+    local real_f32 = zbuff.create(N * 4) -- 实部缓冲区(浮点)
+    local imag_f32 = zbuff.create(N * 4) -- 虚部缓冲区(浮点)
+
+    -- 将Q15数据复制到浮点缓冲区
+    for i = 0, N - 1 do
+        -- 从Q15缓冲区读取U12数据
+        real_i16:seek(i * 2, zbuff.SEEK_SET)
+        local val = real_i16:readU16()
+
+        -- 写入浮点缓冲区(直接写入U12原始值,不进行格式转换)
+        real_f32:seek(i * 4, zbuff.SEEK_SET)
+        imag_f32:seek(i * 4, zbuff.SEEK_SET)
+        real_f32:writeF32(val) -- 实部写入原始U12值
+        imag_f32:writeF32(0.0) -- 虚部写入0.0
+    end
+
+    -- 生成 f32 旋转因子(浮点格式)
+    -- 返回Lua table格式的旋转因子,而不是zbuff
+    local Wc, Ws = fft.generate_twiddles(N)
+
+    -- 执行 f32 FFT(输入为 U12)
+    local t2 = mcu.ticks() -- 记录开始时间
+
+    -- 执行浮点FFT计算:
+    -- 使用默认的f32内核,输入格式为u12
+    fft.run(real_f32, imag_f32, N, Wc, Ws, { input_format = "u12" })
+
+    local t3 = mcu.ticks() -- 记录结束时间
+    local dt_f32 = (t3 - t2) -- 计算浮点FFT耗时
+
+    -- 输出浮点FFT计算耗时
+    log.info("fft", "f32 FFT 完成", "耗时:" .. dt_f32 .. "ms")
+
+
+    -- 结果总结段:对比两种实现的性能
+
+
+    -- 总结耗时对比:Q15定点 vs F32浮点
+    -- 在没有硬件浮点加速的嵌入式设备上,Q15会比F32快很多,
+    -- 而780和8000系列都没有硬件浮点加速,建议使用q15计算提高速度,但如果追求计算精度,仍然可以用浮点计算
+    log.info("fft", "对比(q15 vs f32, ms)", string.format("%d / %d", (t1 - t0), dt_f32))
+end
+
+sys.taskInit(test_fft_fun)

+ 221 - 0
module/Air8101/demo/iconv/iconv_test.lua

@@ -0,0 +1,221 @@
+--[[
+@module  iconv_test
+@summary 字符编码转换测试模块
+@version 1.0
+@date    2025.10.27
+@author  孟伟
+@usage
+本模块提供多种字符编码之间的相互转换功能,支持以下编码转换:
+1. Unicode小端(ucs2)与GB2312编码互转
+2. Unicode大端(ucs2be)与GB2312编码互转
+3. Unicode小端(ucs2)与UTF8编码互转
+4. Unicode大端(ucs2be)与UTF8编码互转
+5. GB2312 编码与 UTF-8 编码之间的转换。
+
+
+
+本文件没有对外接口,直接在main.lua中require "iconv_test"就可以加载运行;
+]]
+
+--- unicode小端编码 转化为 gb2312编码
+-- @string ucs2s unicode小端编码数据
+-- @return string data,gb2312编码数据
+-- @usage local data = common.ucs2ToGb2312(ucs2s)
+function ucs2ToGb2312(ucs2s)
+    local cd = iconv.open("gb2312", "ucs2")
+    return cd:iconv(ucs2s)
+end
+
+--- gb2312编码 转化为 unicode小端编码
+-- @string gb2312s gb2312编码数据
+-- @return string data,unicode小端编码数据
+-- @usage local data = common.gb2312ToUcs2(gb2312s)
+function gb2312ToUcs2(gb2312s)
+    local cd = iconv.open("ucs2", "gb2312")
+    return cd:iconv(gb2312s)
+end
+
+--- unicode大端编码 转化为 gb2312编码
+-- @string ucs2s unicode大端编码数据
+-- @return string data,gb2312编码数据
+-- @usage data = common.ucs2beToGb2312(ucs2s)
+function ucs2beToGb2312(ucs2s)
+    local cd = iconv.open("gb2312", "ucs2be")
+    return cd:iconv(ucs2s)
+end
+
+--- gb2312编码 转化为 unicode大端编码
+-- @string gb2312s gb2312编码数据
+-- @return string data,unicode大端编码数据
+-- @usage local data = common.gb2312ToUcs2be(gb2312s)
+function gb2312ToUcs2be(gb2312s)
+    local cd = iconv.open("ucs2be", "gb2312")
+    return cd:iconv(gb2312s)
+end
+
+--- unicode小端编码 转化为 utf8编码
+-- @string ucs2s unicode小端编码数据
+-- @return string data,utf8编码数据
+-- @usage data = common.ucs2ToUtf8(ucs2s)
+function ucs2ToUtf8(ucs2s)
+    local cd = iconv.open("utf8", "ucs2")
+    return cd:iconv(ucs2s)
+end
+
+--- utf8编码 转化为 unicode小端编码
+-- @string utf8s utf8编码数据
+-- @return string data,unicode小端编码数据
+-- @usage local data = common.utf8ToUcs2(utf8s)
+function utf8ToUcs2(utf8s)
+    local cd = iconv.open("ucs2", "utf8")
+    return cd:iconv(utf8s)
+end
+
+--- unicode大端编码 转化为 utf8编码
+-- @string ucs2s unicode大端编码数据
+-- @return string data,utf8编码数据
+-- @usage data = common.ucs2beToUtf8(ucs2s)
+function ucs2beToUtf8(ucs2s)
+    local cd = iconv.open("utf8", "ucs2be")
+    return cd:iconv(ucs2s)
+end
+
+--- utf8编码 转化为 unicode大端编码
+-- @string utf8s utf8编码数据
+-- @return string data,unicode大端编码数据
+-- @usage local data = common.utf8ToUcs2be(utf8s)
+function utf8ToUcs2be(utf8s)
+    local cd = iconv.open("ucs2be", "utf8")
+    return cd:iconv(utf8s)
+end
+
+--- utf8编码 转化为 gb2312编码
+-- @string utf8s utf8编码数据
+-- @return string data,gb2312编码数据
+-- @usage local data = common.utf8ToGb2312(utf8s)
+function utf8ToGb2312(utf8s)
+    local cd = iconv.open("ucs2", "utf8")
+    local ucs2s = cd:iconv(utf8s)
+    cd = iconv.open("gb2312", "ucs2")
+    return cd:iconv(ucs2s)
+end
+
+--- gb2312编码 转化为 utf8编码
+-- @string gb2312s gb2312编码数据
+-- @return string data,utf8编码数据
+-- @usage local data = common.gb2312ToUtf8(gb2312s)
+function gb2312ToUtf8(gb2312s)
+    local cd = iconv.open("ucs2", "gb2312")
+    local ucs2s = cd:iconv(gb2312s)
+    cd = iconv.open("utf8", "ucs2")
+    return cd:iconv(ucs2s)
+end
+
+--------------------------------------------------------------------------------------------------------
+--[[
+函数名:ucs2ToGb2312
+功能  :unicode小端编码 转化为 gb2312编码,并打印出gb2312编码数据
+参数  :
+        ucs2s:unicode小端编码数据,注意输入参数的字节数
+返回值:
+]]
+local function testucs2ToGb2312(ucs2s)
+    print("ucs2ToGb2312")
+    local gb2312num = ucs2ToGb2312(ucs2s) --调用的是common.ucs2ToGb2312,返回的是编码所对应的字符串
+    --print("gb2312  code:",gb2312num)
+    print("gb2312  code:", string.toHex(gb2312num))
+end
+
+--[[
+函数名:gb2312ToUcs2
+功能  :gb2312编码 转化为 unicode十六进制小端编码数据并打印
+参数  :
+        gb2312s:gb2312编码数据,注意输入参数的字节数
+返回值:
+]]
+local function testgb2312ToUcs2(gb2312s)
+    print("gb2312ToUcs2")
+    local ucs2num = gb2312ToUcs2(gb2312s)
+    print("unicode little-endian code:" .. string.toHex(ucs2num)) -- 要将二进制转换为十六进制,否则无法输出
+end
+
+--[[
+函数名:ucs2beToGb2312
+功能  :unicode大端编码 转化为 gb2312编码,并打印出gb2312编码数据,
+大端编码数据是与小端编码数据位置调换
+参数  :
+        ucs2s:unicode大端编码数据,注意输入参数的字节数
+返回值:
+]]
+local function testucs2beToGb2312(ucs2s)
+    print("ucs2beToGb2312")
+    local gb2312num = ucs2beToGb2312(ucs2s) -- 转化后的数据直接变成字符可以直接输出
+    print("gb2312 code :" .. string.toHex(gb2312num))
+end
+
+--[[
+函数名:gb2312ToUcs2be
+功能  :gb2312编码 转化为 unicode大端编码,并打印出unicode大端编码
+参数  :
+        gb2312s:gb2312编码数据,注意输入参数的字节数
+返回值:unicode大端编码数据
+]]
+function testgb2312ToUcs2be(gb2312s)
+    print("gb2312ToUcs2be")
+    local ucs2benum = gb2312ToUcs2be(gb2312s)
+    print("unicode big-endian code :" .. string.toHex(ucs2benum))
+end
+
+--[[
+函数名:ucs2ToUtf8
+功能  :unicode小端编码 转化为 utf8编码,并打印出utf8十六进制编码数据
+参数  :
+        ucs2s:unicode小端编码数据,注意输入参数的字节数
+返回值:
+]]
+local function testucs2ToUtf8(ucs2s)
+    print("ucs2ToUtf8")
+    local utf8num = ucs2ToUtf8(ucs2s)
+    print("utf8  code:" .. string.toHex(utf8num))
+end
+
+--[[
+函数名:utf8ToGb2312
+功能  :utf8编码 转化为 gb2312编码,并打印出gb2312编码数据
+参数  :
+        utf8s:utf8编码数据,注意输入参数的字节数
+返回值:
+]]
+local function testutf8ToGb2312(utf8s)
+    print("utf8ToGb2312")
+    local gb2312num = utf8ToGb2312(utf8s)
+    print("gb2312 code:" .. string.toHex(gb2312num))
+end
+
+--[[
+函数名:gb2312ToUtf8
+功能  :gb2312编码 转化为 utf8编码,并打印出utf8编码数据
+参数  :
+        gb2312s:gb2312s编码数据,注意输入参数的字节数
+返回值:
+]]
+local function testgb2312ToUtf8(gb2312s)
+    print("gb2312ToUtf8")
+    local utf8s = gb2312ToUtf8(gb2312s)
+    print("utf8s code:" .. utf8s)
+end
+
+function iconv_test_fun()
+    while 1 do
+        sys.wait(10000)
+        testucs2ToGb2312(string.fromHex("1162"))   -- "1162"是"我"字的ucs2编码,这里调用了string.fromHex将参数转化为二进制,也就是两个字节。
+        testgb2312ToUcs2(string.fromHex("CED2"))   -- "CED2"是"我"字的gb2312编码
+        testucs2beToGb2312(string.fromHex("6211")) -- "6211"是"我"字的ucs2be编码
+        testgb2312ToUcs2be(string.fromHex("CED2"))
+        testucs2ToUtf8(string.fromHex("1162"))
+        testutf8ToGb2312(string.fromHex("E68891")) -- "E68891"是"我"字的utf8编码
+        testgb2312ToUtf8(string.fromHex("CED2"))
+    end
+end
+
+sys.taskInit(iconv_test_fun)

+ 69 - 0
module/Air8101/demo/iconv/readme.md

@@ -0,0 +1,69 @@
+## 功能模块介绍
+
+1、main.lua:主程序入口;
+
+2、iconv.lua:字符编码转换模块,提供多种字符编码之间的相互转换功能;
+
+
+## 演示功能概述
+
+本demo演示的功能为:
+提供多种字符编码之间的相互转换功能,支持以下编码转换:
+1. Unicode小端(ucs2)与GB2312编码互转
+2. Unicode大端(ucs2be)与GB2312编码互转
+3. Unicode小端(ucs2)与UTF8编码互转
+4. Unicode大端(ucs2be)与UTF8编码互转
+5. GB2312 编码与 UTF-8 编码之间的转换。
+
+
+
+## 演示硬件环境
+
+![](https://docs.openluat.com/air8101/luatos/app/image/netdrv_multi.jpg)
+
+1、Air8101核心板一块
+
+2、TYPE-C USB数据线一根
+
+3、Air8101核心板和数据线的硬件接线方式为
+
+- Air8101核心板通过TYPE-C USB口供电;(核心板背面的功耗测试开关拨到OFF一端)
+
+- 如果测试发现软件频繁重启,重启原因值为:poweron reason 0,可能是供电不足,此时再通过直流稳压电源对核心板的vbat管脚进行4V供电,或者VIN管脚进行5V供电;
+
+- TYPE-C USB数据线直接插到核心板的TYPE-C USB座子,另外一端连接电脑USB口;
+
+- USB转串口数据线,一般来说,白线连接核心板的12/U1TX,绿线连接核心板的11/U1RX,黑线连接核心板的gnd,另外一端连接电脑USB口;
+
+
+## 演示软件环境
+
+1、Luatools下载调试工具
+
+2、[Air8101 V1006版本固件](https://docs.openluat.com/air8101/luatos/firmware/)
+
+## 演示操作步骤
+
+1、搭建好硬件环境
+
+2、将demo烧录到模组中
+
+3、可以看到如下输出:
+```lua
+[2025-10-27 18:28:17.553][000000005.822] D/mobile NETIF_LINK_ON -> IP_READY
+[2025-10-27 18:28:17.614][000000005.932] D/mobile TIME_SYNC 0
+[2025-10-27 18:28:21.797][000000010.217] ucs2ToGb2312
+[2025-10-27 18:28:21.850][000000010.218] gb2312  code: CED2 4
+[2025-10-27 18:28:21.927][000000010.218] gb2312ToUcs2
+[2025-10-27 18:28:21.990][000000010.218] unicode little-endian code:1162
+[2025-10-27 18:28:22.050][000000010.219] ucs2beToGb2312
+[2025-10-27 18:28:22.109][000000010.219] gb2312 code :CED2
+[2025-10-27 18:28:22.169][000000010.219] gb2312ToUcs2be
+[2025-10-27 18:28:22.227][000000010.220] unicode big-endian code :6211
+[2025-10-27 18:28:22.294][000000010.220] ucs2ToUtf8
+[2025-10-27 18:28:22.349][000000010.220] utf8  code:E68891
+[2025-10-27 18:28:22.400][000000010.220] utf8ToGb2312
+[2025-10-27 18:28:22.452][000000010.221] gb2312 code:CED2
+[2025-10-27 18:28:22.510][000000010.221] gb2312ToUtf8
+
+```