Przeglądaj źródła

add:新增8000-fskv/otp -demo

马亚丹 3 miesięcy temu
rodzic
commit
e943a2251a

+ 149 - 0
module/Air8000/demo/fskv/fskv_test.lua

@@ -0,0 +1,149 @@
+--[[
+@module  fskv_test
+@summary fskv_test测试功能模块
+@version 1.0
+@date    2025.11.14
+@author  马亚丹
+@usage
+本demo演示的功能为:使用Air8000核心板演示fskv核心库API的用法,演示设置kv数据、设置 table 内的键值对数据,用kv迭代器遍历kv数据,删除kv数据等操作。
+
+运行核心逻辑:
+1.初始化fskv
+2.获取 kv 数据库状态
+3.设置不同类型的kv数据
+4.设置 table 内的键值对数据
+5.根据 key 获取对应的数据
+6.使用kv迭代器遍历kv数据
+7.删除kv数据,清空KV数据
+
+]]
+--1.定义功能函数:置kv 数据
+local function setKV()
+    --如下所示设置用户数据是字符串
+    local r1 = fskv.set("my_str", "goodgoodstudy")
+    log.info("fskv设置用户数据是字符串", r1)
+    --如下所示设置用户数据是布尔值
+    local r2 = fskv.set("my_bool", true)
+    log.info("fskv设置用户数据是布尔值", r2)
+    --如下所示设置用户数据是数值
+    local r3 = fskv.set("my_number", 1.23)
+    log.info("fskv设置用户数据是数值", r3)
+    --如下所示设置用户数据是整数
+    local r4 = fskv.set("my_int", 5)
+    log.info("fskv设置用户数据是整数", r4)
+    --如下所示设置用户数据是table
+    local r5 = fskv.set("my_table", { name = "wendal", age = 18 })
+    log.info("fskv用户数据是table类型", r5)
+
+   return true
+end
+
+
+--2.定义功能函数:设置 table 内的键值对数据
+local function setttable()
+    --如下所示设置用户数据是字符串
+    local r1=fskv.sett("mytable", "wendal", "goodgoodstudy")
+    log.info("mytable设置用户数据是字符串", r1)
+    --如下所示设置用户数据是布尔值
+    local r2=fskv.sett("mytable", "upgrade", true)
+    log.info("mytable设置用户数据是布尔值", r2)
+    --如下所示设置用户数据是数值
+    local r3=fskv.sett("mytable", "timer", 1)
+    log.info("mytable设置用户数据是数值", r3)
+    --如下所示设置用户数据是table
+    local r4=fskv.sett("mytable", "bigd", { name = "wendal", age = 123 })
+    log.info("mytable设置用户数据是table", r4)
+    return true
+end
+
+--3.定义功能函数:获取kv数据
+local function getKV()
+    local my_str = fskv.get("my_str")
+    log.info("获取my_str的类型和值", type(my_str), my_str)
+    local my_bool = fskv.get("my_bool")
+    log.info("获取upgrade的类型和值", type(my_bool), my_bool)
+    local my_number = fskv.get("my_number")
+    log.info("获取my_number的类型和值", type(my_number), my_number)
+    local my_int = fskv.get("my_int")
+    log.info("获取my_int的类型和值", type(my_int), my_int)
+    local my_table = fskv.get("my_table")
+    log.info("获取my_table的类型和值", my_table,json.encode(my_table))
+
+    local mytable = fskv.get("mytable")
+    log.info("获取mytable的类型和值", mytable, json.encode(mytable))
+    return true
+end
+
+--4.定义功能函数:kv数据库迭代器遍历KV数据
+local function iterKV()
+    local iter = fskv.iter()
+    log.info("kv数据库迭代器", iter)
+    if iter then
+        while 1 do
+            local k = fskv.next(iter)
+            log.info("kv迭代器获取下一个key", k)
+            if not k then
+                log.info("kv数据库遍历完成")
+                break
+            end
+            log.info("fskv", k, "value", fskv.get(k))
+        end
+    end
+    return true
+end
+--====功能演示主函数====
+local function fskv_test()
+    -- 初始化kv数据库
+    local r = fskv.init()
+    log.info("fskv", "init complete", r)
+
+    --获取 kv 数据库状态
+    local used, total, kv_count = fskv.status()
+    log.info("获取kv数据库状态", "fskv", "kv", used, total, kv_count)
+
+
+    --=======设置kv 数据=======
+    if not setKV() then
+        log.info("设置kv数据失败")
+    end
+
+    --=======设置 table 内的键值对数据=======
+    if not setttable() then
+        log.info("设置 table 内的键值对数据失败")
+    end
+
+    --======根据 key 获取对应的数据====
+    if not getKV() then
+        log.info("获取kv数据失败")
+    end
+    --======使用kv数据库迭代器遍历KV数据====
+    if not iterKV() then
+        log.info("遍历kv数据失败")
+    end
+
+    
+
+    -- 删除测试
+    local d = fskv.del("my_bool")
+    log.info("fskv", "my_bool删除结果", d)
+    local t = fskv.get("my_bool")
+    log.info("fskv", "删除后查询my_bool", type(t), t)
+
+
+    -- 如果设置table的value为nil, 代表删除对应的skey
+    -- 如下写法是删除name
+    log.info("设置新的table,key是mytable2", fskv.set("mytable2", { age = 18, name = "wendal" }))
+    log.info("mytable2的值", fskv.get("mytable2"),json.encode(fskv.get("mytable2")))
+    log.info("mytable2删除name测试", fskv.sett("mytable2", "name", nil))
+    log.info("mytable2删除结果", fskv.get("mytable2"), json.encode(fskv.get("mytable2")))
+
+
+    --清空整个kv数据库
+    log.info("清空整个kv数据库", fskv.clear())
+
+
+    local used, total, kv_count = fskv.status()
+    log.info("获取kv数据库状态", "fskv", "kv", used, total, kv_count)
+end
+
+sys.taskInit(fskv_test)

+ 71 - 100
module/Air8000/demo/fskv/main.lua

@@ -1,105 +1,76 @@
+--[[
+@module  main
+@summary LuatOS用户应用脚本文件入口,总体调度应用逻辑 
+@version 001.000.000
+@date    2025.9.05
+@author  马亚丹
+@usage
+本demo演示的功能为:使用Air8000核心板演示fskv核心库API 的用法,
+详细逻辑请看fskv_test.lua文件
+
+
+]]
+
+--[[
+必须定义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 = "Air8000_fskv"
+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)
+
+
+
+
+-- 加载fskv_test功能模块
+require "fskv_test"
+
 
--- LuaTools需要PROJECT和VERSION这两个信息
-PROJECT = "fskvdemo"
-VERSION = "1.0.0"
-
--- sys库是标配
-_G.sys = require("sys")
-
-sys.taskInit(function()
-    sys.wait(1000) -- 免得日志刷没了, 生产环境不需要
-
-    -- 检查一下当前固件是否支持fskv
-    if not fskv then
-        while true do
-            log.info("fskv", "this demo need fskv")
-            sys.wait(1000)
-        end
-    end
-
-    -- 初始化kv数据库
-    fskv.init()
-    log.info("fskv", "init complete")
-    -- 先放入一堆值
-    local bootime = fskv.get("boottime")
-    if bootime == nil or type(bootime) ~= "number" then
-        bootime = 0
-    else
-        bootime = bootime + 1
-    end
-    fskv.set("boottime", bootime)
-
-    fskv.set("my_bool", true)
-    fskv.set("my_int", 123)
-    fskv.set("my_number", 1.23)
-    fskv.set("my_str", "luatos")
-    fskv.set("my_table", {name="wendal",age=18})
-    
-    fskv.set("my_str_int", "123")
-    fskv.set("1", "123") -- 单字节key
-    --fskv.set("my_nil", nil) -- 会提示失败,不支持空值
-
-
-    log.info("fskv", "boottime",      type(fskv.get("boottime")),    fskv.get("boottime"))
-    log.info("fskv", "my_bool",      type(fskv.get("my_bool")),    fskv.get("my_bool"))
-    log.info("fskv", "my_int",       type(fskv.get("my_int")),     fskv.get("my_int"))
-    log.info("fskv", "my_number",    type(fskv.get("my_number")),  fskv.get("my_number"))
-    log.info("fskv", "my_str",       type(fskv.get("my_str")),     fskv.get("my_str"))
-    log.info("fskv", "my_table",     type(fskv.get("my_table")),   json.encode(fskv.get("my_table")))
-    log.info("fskv", "my_str_int",     type(fskv.get("my_str_int")),   fskv.get("my_str_int"))
-    log.info("fskv", "1 byte key",     type(fskv.get("1")),   json.encode(fskv.get("1")))
-
-    -- 删除测试
-    fskv.del("my_bool")
-    local t = fskv.get("my_bool")
-    log.info("fskv", "my_bool",      type(t),    t)
-
-    -- 查询kv数据库状态
-    -- local used, total,kv_count = fskv.stat()
-    -- log.info("fdb", "kv", used,total,kv_count)
-
-    -- fskv.clr()
-    -- local used, total,kv_count = fskv.stat()
-    -- log.info("fdb", "kv", used,total,kv_count)
-    
-
-    -- 压力测试
-    -- local start = mcu.ticks()
-    -- local count = 1000
-    -- for i=1,count do
-    --     -- sys.wait(10)
-    --     -- count = count - 1
-    --     -- fskv.set("BENT1", "--" .. os.date() .. "--")
-    --     -- fskv.set("BENT2", "--" .. os.date() .. "--")
-    --     -- fskv.set("BENT3", "--" .. os.date() .. "--")
-    --     -- fskv.set("BENT4", "--" .. os.date() .. "--")
-    --     fskv.get("my_bool")
-    -- end
-    -- log.info("fskv", mcu.ticks() - start)
-
-    if fskv.sett then
-        -- 设置数据, 字符串,数值,table,布尔值,均可
-        -- 但不可以是nil, function, userdata, task
-        log.info("fdb", fskv.sett("mytable", "wendal", "goodgoodstudy"))
-        log.info("fdb", fskv.sett("mytable", "upgrade", true))
-        log.info("fdb", fskv.sett("mytable", "timer", 1))
-        log.info("fdb", fskv.sett("mytable", "bigd", {name="wendal",age=123}))
-        
-        -- 下列语句将打印出4个元素的table
-        log.info("fdb", fskv.get("mytable"), json.encode(fskv.get("mytable")))
-        -- 注意: 如果key不存在, 或者原本的值不是table类型,将会完全覆盖
-        -- 例如下列写法,最终获取到的是table,而非第一行的字符串
-        log.info("fdb", fskv.set("mykv", "123"))
-        log.info("fdb", fskv.sett("mykv", "age", "123")) -- 保存的将是 {age:"123"}
-
-        -- 删除测试
-        log.info("fdb", fskv.set("mytable", {age=18, name="wendal"}))
-        log.info("fdb", fskv.sett("mytable", "name", nil))
-        log.info("fdb", fskv.get("mytable"), json.encode(fskv.get("mytable")))
-    end
-end)
 
 -- 用户代码已结束---------------------------------------------
 -- 结尾总是这一句
 sys.run()
--- sys.run()之后后面不要加任何语句!!!!!
+-- sys.run()之后不要加任何语句!!!!!因为添加的任何语句都不会被执行

+ 106 - 0
module/Air8000/demo/fskv/readme.md

@@ -0,0 +1,106 @@
+## 功能模块介绍:
+
+1. main.lua:主程序入口
+
+2. fskv_test.lua:演示fskv核心库API的用法,详细逻辑请看fskv_test.lua 文件
+
+## 演示功能概述:
+
+### fskv_test.lua:
+
+1.初始化fskv
+
+2.获取 kv 数据库状态
+
+3.设置不同类型的kv数据
+
+4.设置 table 内的键值对数据
+
+5.根据 key 获取对应的数据
+
+6.使用kv迭代器遍历kv数据
+
+7.删除kv数据,清空KV数据
+
+## 演示硬件环境:
+
+![](https://docs.openluat.com/accessory/AirSPINORFLASH_1000/image/80001.jpg)
+
+![](https://docs.openluat.com/accessory/AirSPINORFLASH_1000/image/80002.jpg)
+
+1. 合宙 Air8000 核心板一块
+
+2. TYPE-C USB 数据线一根 ,Air8000 核心板和数据线的硬件接线方式为:
+- Air8000 核心板通过 TYPE-C USB 口供电;(外部供电/USB 供电 拨动开关 拨到 USB 供电一端)
+
+- TYPE-C USB 数据线直接插到开发板的 TYPE-C USB 座子,另外一端连接电脑 USB 口;
+
+## 演示软件环境:
+
+1. Luatools 下载调试工具
+
+2. 固件版本:LuatOS-SoC_V2018_Air8000_1,固件地址,如有最新固件请用最新 [https://docs.openluat.com/air8000/luatos/firmware/](https://docs.openluat.com/air8000/luatos/firmware/)
+
+3. pc 系统 win11(win10 及以上)
+
+## 演示核心步骤:
+
+1. 搭建好硬件环境
+
+2. Luatools 烧录内核固件和 脚本文件
+
+3. 烧录成功后,代码会自动运行,查看打印日志,如果正常运行,会打印相关信息,设置kv数据、设置 table 内的键值对数据,用kv迭代器遍历kv数据,删除kv数据等。
+
+4. 如下 log 显示:
+
+```bash
+[2025-11-14 16:37:13.043][000000000.379] I/user.main Air8000_fskv 001.000.000
+[2025-11-14 16:37:13.058][000000000.396] D/lfs init ok
+[2025-11-14 16:37:13.069][000000000.396] I/user.fskv init complete true
+[2025-11-14 16:37:13.083][000000000.397] I/user.获取kv数据库状态 fskv kv 8192 65536 0
+[2025-11-14 16:37:13.097][000000000.431] I/user.fskv设置用户数据是字符串 true
+[2025-11-14 16:37:13.108][000000000.432] I/user.fskv设置用户数据是布尔值 true
+[2025-11-14 16:37:13.119][000000000.434] I/user.fskv设置用户数据是数值 true
+[2025-11-14 16:37:13.141][000000000.435] I/user.fskv设置用户数据是整数 true
+[2025-11-14 16:37:13.158][000000000.437] I/user.fskv用户数据是table类型 true
+[2025-11-14 16:37:13.182][000000000.440] I/user.mytable设置用户数据是字符串 true
+[2025-11-14 16:37:13.238][000000000.443] I/user.mytable设置用户数据是布尔值 true
+[2025-11-14 16:37:13.258][000000000.446] I/user.mytable设置用户数据是数值 true
+[2025-11-14 16:37:13.283][000000000.450] I/user.mytable设置用户数据是table true
+[2025-11-14 16:37:13.306][000000000.452] I/user.获取my_str的类型和值 string goodgoodstudy
+[2025-11-14 16:37:13.324][000000000.454] I/user.获取upgrade的类型和值 boolean true
+[2025-11-14 16:37:13.342][000000000.456] I/user.获取my_number的类型和值 number 1.230000
+[2025-11-14 16:37:13.361][000000000.458] I/user.获取my_int的类型和值 number 5
+[2025-11-14 16:37:13.382][000000000.460] I/user.获取my_table的类型和值 table: 0C7F5460 {"name":"wendal","age":18}
+[2025-11-14 16:37:13.406][000000000.462] I/user.获取mytable的类型和值 table: 0C7F52E8 {"bigd":{"name":"wendal","age":123},"wendal":"goodgoodstudy","timer":1,"upgrade":true}
+[2025-11-14 16:37:13.426][000000000.462] I/user.kv数据库迭代器 userdata: 0C7F5100
+[2025-11-14 16:37:13.448][000000000.466] I/user.kv迭代器获取下一个key my_bool
+[2025-11-14 16:37:13.468][000000000.468] I/user.fskv my_bool value true
+[2025-11-14 16:37:13.489][000000000.474] I/user.kv迭代器获取下一个key my_int
+[2025-11-14 16:37:13.506][000000000.476] I/user.fskv my_int value 5
+[2025-11-14 16:37:13.523][000000000.479] I/user.kv迭代器获取下一个key my_number
+[2025-11-14 16:37:13.540][000000000.481] I/user.fskv my_number value 1.230000
+[2025-11-14 16:37:13.560][000000000.484] I/user.kv迭代器获取下一个key my_str
+[2025-11-14 16:37:13.578][000000000.486] I/user.fskv my_str value goodgoodstudy
+[2025-11-14 16:37:13.604][000000000.488] I/user.kv迭代器获取下一个key my_table
+[2025-11-14 16:37:13.622][000000000.491] I/user.fskv my_table value table: 0C7F4FC8
+[2025-11-14 16:37:13.637][000000000.493] I/user.kv迭代器获取下一个key mytable
+[2025-11-14 16:37:13.655][000000000.494] I/user.fskv mytable value table: 0C7F4EB0
+[2025-11-14 16:37:13.669][000000000.497] I/user.kv迭代器获取下一个key nil
+[2025-11-14 16:37:13.681][000000000.497] I/user.kv数据库遍历完成
+[2025-11-14 16:37:13.691][000000000.539] I/user.fskv my_bool删除结果 true
+[2025-11-14 16:37:13.706][000000000.540] I/user.fskv 删除后查询my_bool nil nil
+[2025-11-14 16:37:13.721][000000000.551] I/user.设置新的table,key是mytable2 true
+[2025-11-14 16:37:13.740][000000000.553] I/user.mytable2的值 table: 0C7F4C70 {"age":18,"name":"wendal"}
+[2025-11-14 16:37:13.766][000000000.555] I/user.mytable2删除name测试 true
+[2025-11-14 16:37:13.781][000000000.558] I/user.mytable2删除结果 table: 0C7F4A88 {"age":18}
+[2025-11-14 16:37:13.798][000000000.632] I/user.清空整个kv数据库 true
+[2025-11-14 16:37:13.814][000000000.633] I/user.获取kv数据库状态 fskv kv 8192 65536 0
+[2025-11-14 16:37:14.123][000000002.324] D/mobile cid1, state0
+[2025-11-14 16:37:14.140][000000002.325] D/mobile bearer act 0, result 0
+[2025-11-14 16:37:14.160][000000002.326] D/mobile NETIF_LINK_ON -> IP_READY
+[2025-11-14 16:37:14.173][000000002.342] D/mobile TIME_SYNC 0
+
+
+
+```

+ 76 - 0
module/Air8000/demo/otp/main.lua

@@ -0,0 +1,76 @@
+--[[
+@module  main
+@summary LuatOS用户应用脚本文件入口,总体调度应用逻辑 
+@version 001.000.000
+@date    2025.9.05
+@author  马亚丹
+@usage
+本demo演示的功能为:使用Air8000核心板演示otp核心库API 的用法,
+详细逻辑请看otp_test.lua文件
+
+
+]]
+
+--[[
+必须定义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 = "Air8000_otp"
+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)
+
+
+
+
+-- 加载otp_test功能模块
+require "otp_test"
+
+
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后不要加任何语句!!!!!因为添加的任何语句都不会被执行

+ 83 - 0
module/Air8000/demo/otp/otp_test.lua

@@ -0,0 +1,83 @@
+--[[
+@module  otp_test
+@summary otp_test测试功能模块
+@version 1.0
+@date    2025.11.21
+@author  马亚丹
+@usage
+本demo演示的功能为:使用Air8000核心板演示otp核心库API的用法,演示写入,读取,擦除otp数据等操作。
+
+运行核心逻辑:
+1.读取指定 OTP 区域的数据
+2.进入飞行模式,擦除指定的 OTP 区域的数据
+3.擦除完成后向该区域写入数据
+4.谨慎操作区域加锁(区域加锁后会永久变成只读无法写入)
+5.退出飞行模式
+
+Air8000系列otp区域取值是1/2/3;
+写入 / 读取的长度需与 OTP 块大小对齐,按 4 字节对齐。
+]]
+
+local function otp_test()
+    log.info("========otp read start=========")
+    local otpdata = otp.read(1, 0, 64)
+    if otpdata then
+        log.info("otp 读取结果", otpdata, type(otpdata))
+    else
+        log.info("otp 读取失败")
+    end
+    
+    log.info("写数据前先进入飞行模式")
+    local result = mobile.flymode(0, true)
+    if result then
+        log.info("现在是飞行模式",result)
+
+        log.info("========otp erase start=========")
+        local erase_ok = otp.erase(1)
+        if erase_ok then
+            log.info("OTP 擦除成功")
+        else
+            log.info("OTP 擦除失败")
+        end
+        local write_data = "1234"
+        log.info("=========向otp区域1写入数据==========")
+        local write_ok = otp.write(1, write_data, 0)
+        if write_ok then
+            log.info("OTP 写入成功", write_data)
+        else
+            log.info("OTP 写入失败")
+        end
+    end
+
+
+    log.info("=========读取otp区域1数据==========")
+
+    local otpdata_1 = otp.read(1, 0, 4)
+    local otpdata_2 = otp.read(1, 0, 8)
+    if otpdata_1 then
+        log.info("读取4字节数据", otpdata_1, type(otpdata_1))
+    else
+        log.info("===========otp区域1读取失败=========")
+    end
+    if otpdata_2 then
+        log.info("读取8字节数据", otpdata_2, type(otpdata_2))
+    else
+        log.info("===========otp区域1读取失败=========")
+    end
+
+    --=====锁定 OTP 区域,特别注意!!! 一定要在飞行模式下操作锁定!!!=============
+    --=====OTP一旦加锁即无法解锁,OTP 会变成只读!!!
+    -- local lock_ok = otp.lock(1)
+    -- if lock_ok then
+    --     log.info("OTP 锁定成功")
+    -- end
+    --===========================================================================
+
+    --退出飞行模式   
+    local result2 = mobile.flymode(0, false)
+    if not result2 then
+        log.info("退出飞行模式", result2)
+    end
+end
+
+sys.taskInit(otp_test)

+ 74 - 0
module/Air8000/demo/otp/readme.md

@@ -0,0 +1,74 @@
+## 功能模块介绍:
+
+1. main.lua:主程序入口
+
+2. otp_test.lua:演示otp核心库API的用法,详细逻辑请看otp_test.lua 文件
+
+## 演示功能概述:
+
+### otp_test.lua:
+
+1.读取指定 OTP 区域的数据
+
+2.进入飞行模式,擦除指定的 OTP 区域的数据
+
+3.擦除完成后向该区域写入数据
+
+4.谨慎操作区域加锁(区域加锁后会永久变成只读无法写入)
+
+5.退出飞行模式
+
+## 演示硬件环境:
+
+![](https://docs.openluat.com/accessory/AirSPINORFLASH_1000/image/80001.jpg)
+
+![](https://docs.openluat.com/accessory/AirSPINORFLASH_1000/image/80002.jpg)
+
+
+
+1. 合宙 Air8000 核心板一块
+
+2. TYPE-C USB 数据线一根 ,Air8000 核心板和数据线的硬件接线方式为:
+- Air8000 核心板通过 TYPE-C USB 口供电;(外部供电/USB 供电 拨动开关 拨到 USB 供电一端)
+
+- TYPE-C USB 数据线直接插到开发板的 TYPE-C USB 座子,另外一端连接电脑 USB 口;
+
+## 演示软件环境:
+
+1. Luatools 下载调试工具
+
+2. 固件版本:LuatOS-SoC_V2018_Air8000_1,固件地址,如有最新固件请用最新 [https://docs.openluat.com/air8000/luatos/firmware/](https://docs.openluat.com/air8000/luatos/firmware/)
+
+3. pc 系统 win11(win10 及以上)
+
+## 演示核心步骤:
+
+1. 搭建好硬件环境
+
+2. Luatools 烧录内核固件和  demo 脚本
+
+3. 烧录成功后,代码会自动运行,查看打印日志,如果正常运行,会打印相关信息,otp 读取结果、进入飞行模式、otp区域擦除、写入/读取数据、退出飞行模式
+
+4. 如下 log 显示:
+
+```bash
+[2025-11-21 11:27:03.245][000000000.754] I/user.main Air8000_otp 001.000.000
+[2025-11-21 11:27:03.251][000000000.763] I/user.========otp read start=========
+[2025-11-21 11:27:03.255][000000000.763] I/user.otp 读取结果 1234 string
+[2025-11-21 11:27:03.263][000000000.763] I/user.写数据前先进入飞行模式
+[2025-11-21 11:27:03.531][000000001.182] I/user.现在是飞行模式 true
+[2025-11-21 11:27:03.535][000000001.182] I/user.========otp erase start=========
+[2025-11-21 11:27:03.540][000000001.182] I/otp otp erase zone 1 00001000
+[2025-11-21 11:27:03.550][000000001.191] I/user.OTP 擦除成功
+[2025-11-21 11:27:03.555][000000001.191] I/user.=========向otp区域1写入数据==========
+[2025-11-21 11:27:03.564][000000001.191] I/user.OTP 写入成功 1234
+[2025-11-21 11:27:03.570][000000001.191] I/user.=========读取otp区域1数据==========
+[2025-11-21 11:27:03.582][000000001.192] I/user.读取4字节数据 1234 string
+[2025-11-21 11:27:03.593][000000001.192] I/user.读取8字节数据 1234 string
+[2025-11-21 11:27:03.716][000000001.366] I/user.退出飞行模式 false
+[2025-11-21 11:27:09.319][000000006.936] D/mobile cid1, state0
+[2025-11-21 11:27:09.326][000000006.936] D/mobile bearer act 0, result 0
+[2025-11-21 11:27:09.334][000000006.937] D/mobile NETIF_LINK_ON -> IP_READY
+[2025-11-21 11:27:09.342][000000006.956] D/mobile TIME_SYNC 0
+
+```