Quellcode durchsuchen

add:添加Air8000 AirGPIO_1000配件板demo

shenyuanyuan vor 5 Monaten
Ursprung
Commit
6589a90caa

+ 459 - 0
module/Air8000/demo/accessory_board/AirGPIO_1000/AirGPIO_1000.lua

@@ -0,0 +1,459 @@
+--本文件中的主机是指I2C主机,具体指Air8000
+--本文件中的从机是指I2C从机,具体指AirGPIO_1000配件板上的IO扩展芯片
+
+local AirGPIO_1000 = 
+{
+    -- i2c_id:主机的i2c id;
+    -- gpio_int_id:主机的GPIO中断引脚id;
+    -- slave_address:从机地址;
+    -- ints =    --从机各个扩展IO配置为中断时的处理函数以及上一次的输入电平
+    -- {
+    --     [0x00] = {cb_func=, old_level=},
+    --     [0x01] = {cb_func=, old_level=},
+    --     [0x02] = {cb_func=, old_level=},
+    --     [0x03] = {cb_func=, old_level=},
+    --     [0x04] = {cb_func=, old_level=},
+    --     [0x05] = {cb_func=, old_level=},
+    --     [0x06] = {cb_func=, old_level=},
+    --     [0x07] = {cb_func=, old_level=},
+
+    --     [0x10] = {cb_func=, old_level=},
+    --     [0x11] = {cb_func=, old_level=},
+    --     [0x12] = {cb_func=, old_level=},
+    --     [0x13] = {cb_func=, old_level=},
+    --     [0x14] = {cb_func=, old_level=},
+    --     [0x15] = {cb_func=, old_level=},
+    --     [0x16] = {cb_func=, old_level=},
+    --     [0x17] = {cb_func=, old_level=},
+    -- }
+}
+
+-- 从机硬件上有三个引脚A0 A1 A2可以配置I2C从设备的地址
+-- 当A0 A1 A2都接地时的从设备基地址为(0x40 >> 1)
+-- A0 A1 A2有0到7一共八种排序组合,基地址+0/1/2/3/4/5/6/7即为八种从设备的地址
+-- AirGPIO_1000默认A0 A1 A2都接地,所以AirGPIO_1000的从设备地址默认也为(0x40 >> 1)
+local SALVE_ADDRESS_HIGH_4BIT = (0x40 >> 1)
+
+-- 寄存器地址
+local REG_INPUT_PORT_0 = 0x00    -- 输入端口0
+local REG_INPUT_PORT_1 = 0x01    -- 输入端口1
+local REG_OUTPUT_PORT_0 = 0x02   -- 输出端口0
+local REG_OUTPUT_PORT_1 = 0x03   -- 输出端口1
+local REG_POL_INV_0 = 0x04       -- 极性反转端口0
+local REG_POL_INV_1 = 0x05       -- 极性反转端口1
+local REG_CONFIG_0 = 0x06        -- 配置端口0
+local REG_CONFIG_1 = 0x07        -- 配置端口1
+
+
+-- 写入AirGPIO_1000的寄存器
+
+--reg:number类型;
+--         表示AirGPIO_1000上的寄存器地址;
+--         取值范围:0x00到0x07,参考本文件上方的寄存器地址列表;
+--         必须传入,不允许为空;
+
+--value:number类型;
+--         表示要写入到AirGPIO_1000寄存器中的数据;
+--         取值范围:0x00到0xFF,1个字节的长度;
+--         必须传入,不允许为空;
+
+--返回值:成功返回true,失败返回false
+local function write_register(reg, value)
+    local data = {reg, value}
+    local result = i2c.send(AirGPIO_1000.i2c_id, AirGPIO_1000.slave_address, data)
+    return result
+end
+
+-- 读取AirGPIO_1000的寄存器
+
+--reg:number类型;
+--         表示AirGPIO_1000上的寄存器地址;
+--         取值范围:0x00到0x07,参考本文件上方的寄存器地址列表;
+--         必须传入,不允许为空;
+
+--返回值:成功返回1个字节的number类型,失败返回nil
+local function read_register(reg)
+    i2c.send(AirGPIO_1000.i2c_id, AirGPIO_1000.slave_address, reg)
+    local data = i2c.recv(AirGPIO_1000.i2c_id, AirGPIO_1000.slave_address, 1)
+    if data and #data == 1 then
+        return string.byte(data, 1)
+    end
+    return nil
+end
+
+
+--主机上的中断引脚处理函数
+local function gpio_int_callback()
+    log.info("gpio_int_callback")
+    --在中断处理函数中不能直接执行耗时较长的动作
+    --所以在此处publish一个"AirGPIO_1000_INT"消息
+    --在其他位置订阅这个消息,进行异步处理
+    --异步处理这个消息的函数可以直接执行耗时较长的动作
+    sys.publish("AirGPIO_1000_INT")
+end
+
+--遍历用户扩展GPIO中断函数表,进行处理
+local function user_gpio_int_callback()
+    if AirGPIO_1000.ints then
+        --遍历用户扩展GPIO中断函数表
+        for k,v in pairs(AirGPIO_1000.ints) do
+            if v then
+                --读取扩展GPIO的输入电平
+                local cur_level = AirGPIO_1000.get(k)
+                --如果输入电平和上一次输入电平不一致
+                --则执行用户扩展GPIO中断函数
+                if v.old_level~=cur_level then
+                    v.old_level = cur_level
+                    if v.cb_func then v.cb_func(k, cur_level) end
+                end
+            end
+        end
+    end
+end
+
+--订阅"AirGPIO_1000_INT"消息的处理函数user_gpio_int_callback
+--当其他位置publish "AirGPIO_1000_INT"消息时,会执行user_gpio_int_callback
+sys.subscribe("AirGPIO_1000_INT", user_gpio_int_callback)
+
+--检查AirGPIO_1000上的扩展GPIO ID是否有效
+
+--gpio_id:number类型;
+--         表示AirGPIO_1000上的扩展GPIO ID;
+--         取值范围:0x00到0x07,0x10到0x17,一共16种,分别对应16个扩展GPIO引脚;
+--         必须传入,不允许为空;
+
+--返回值:有效返回true,无效返回false
+local function check_gpio_id_valid(gpio_id)
+    return (gpio_id>=0x00 and gpio_id<=0x07 or gpio_id>=0x10 and gpio_id<=0x17)
+end
+
+--配置主机和AirGPIO_1000之间的通信参数;
+
+--i2c_id:number类型;
+--        主机使用的I2C ID,用来控制AirGPIO_1000;
+--        取值范围:仅支持0和1;
+--        如果没有传入此参数,则默认为0;
+--int_id:number类型;
+--        主机使用的中断引脚GPIO ID,和AirGPIO_1000上的INT引脚相连;
+--        AirGPIO_1000可以扩展出来16个GPIO,这些GPIO支持配置为输入;
+--        AirGPIO_1000上的任意一个输入GPIO的状态发生上升沿或者下降沿变化时,会通过INT引脚通知到主机的int_id中断引脚;
+--        此时主机可以通过I2C接口立即读取AirGPIO_1000上配置为输入模式的扩展GPIO的电平状态,从而判断是哪些扩展GPIO的输入电平发生了变化;
+--        取值范围:nil或者空,或者0到9,或者12到55,注意不要使用已经复用为其他功能的引脚;nil或者空时,表示不使用中断通知功能;
+--        如果没有传入此参数,则默认为空,表示不使用中断通知功能;
+
+--返回值:成功返回true,失败返回false
+function AirGPIO_1000.init(i2c_id, gpio_int_id)
+    --检查参数的合法性
+    if not (i2c_id == 0 or i2c_id == 1) then
+        log.error("AirGPIO_1000.init", "invalid i2c_id", i2c_id)
+        return false
+    end
+
+    if not (gpio_int_id==nil or gpio_int_id>=0 and gpio_int_id<=9 or gpio_int_id>=12 and gpio_int_id<=55) then
+        log.error("AirGPIO_1000.init", "invalid gpio_int_id", gpio_int_id)
+        return false
+    end
+
+    AirGPIO_1000.i2c_id = i2c_id
+    AirGPIO_1000.gpio_int_id = gpio_int_id
+
+    --初始化I2C
+    if i2c.setup(i2c_id, i2c.FAST) ~= 1 then
+        log.error("AirGPIO_1000.init", "i2c.setup error", i2c_id)
+        return false
+    end
+
+    --自动识别从设备地址
+    --AirGPIO_1000上使用的TCA9555芯片有三个引脚,A2 A1 A0,可以配置三个bit的I2C从设备地址
+    --从 0 0 0 到 1 1 1,也就是十进制的0到7,一共可以配置8种;
+    --依次读取这8个从设备地址上的一个寄存器地址数据
+    --如果返回应答数据,则从设备地址自动识别成功
+    for i=0,7 do
+        i2c.send(i2c_id, SALVE_ADDRESS_HIGH_4BIT+i, REG_INPUT_PORT_0)
+        local data = i2c.recv(i2c_id, SALVE_ADDRESS_HIGH_4BIT+i, 1)
+        if data~=nil then
+            AirGPIO_1000.slave_address = SALVE_ADDRESS_HIGH_4BIT+i
+            log.error("AirGPIO_1000.init", "slave_address", SALVE_ADDRESS_HIGH_4BIT+i, data:byte())
+            break
+        end
+    end
+
+    --自动识别从设备地址失败
+    if not AirGPIO_1000.slave_address then
+        log.error("AirGPIO_1000.init", "slave_address unknown")
+        i2c.close(i2c_id)
+        return false
+    end
+
+
+    --配置主机上的中断GPIO,用来实时检测从机上扩展GPIO的输入电平变化
+    if gpio_int_id then
+        gpio.setup(gpio_int_id, gpio_int_callback, gpio.PULLUP, gpio.FALLING)
+    end
+
+    return true
+end
+
+--关闭主机和AirGPIO_1000之间的通信;
+
+--返回值:成功返回true,失败返回false
+function AirGPIO_1000.deinit()
+    --关闭主机I2C
+    if AirGPIO_1000.i2c_id then
+        i2c.close(AirGPIO_1000.i2c_id)
+        AirGPIO_1000.i2c_id = nil
+        AirGPIO_1000.slave_address = nil
+    end
+
+    --关闭主机中断GPIO
+    if AirGPIO_1000.gpio_int_id then
+        gpio.close(AirGPIO_1000.gpio_int_id)
+        AirGPIO_1000.gpio_int_id = nil
+    end
+
+    --清空用户注册的扩展GPIO中断处理表
+    if type(AirGPIO_1000.ints)=="table" then
+        for k,v in pairs(AirGPIO_1000.ints) do
+            AirGPIO_1000.ints[k] = nil
+        end        
+        AirGPIO_1000.ints = nil
+    end
+end
+
+--[[
+配置AirGPIO_1000上的扩展GPIO管脚功能;
+支持配置为输出,输入和中断三种模式;
+
+@api AirGPIO_1000.setup(gpio_id, gpio_mode)
+
+@number
+gpio_id
+表示AirGPIO_1000上的扩展GPIO ID;
+取值范围:0x00到0x07,0x10到0x17,一共16种,分别对应16个扩展GPIO引脚;
+必须传入,不允许为空或者nil;
+
+@number or function or nil or 空
+gpio_mode
+number类型时,表示输出模式,取值范围为0和1,0表示默认输出低电平,1表示默认输出高电平;
+nil或者空类型时,表示输入模式;
+function类型时,表示中断模式,此function为中断回调函数,函数的定义格式如下:
+function cb_func(id, level)
+    --id:表示触发中断的AirGPIO_1000上的扩展GPIO ID,取值范围为0x00到0x07,0x10到0x17,一共16种,分别对应16个扩展GPIO引脚;
+    --level:触发中断后,某一时刻,扩展GPIO输入的电平状态,高电平为1, 低电平为0;并不是指触发中断的电平状态;
+end
+
+@return bool
+成功返回true,失败返回false
+
+@usage
+-- GPIO ID 0x00配置为输出模式,默认输出低电平
+AirGPIO_1000.setup(0x00, 0)
+
+-- GPIO ID 0x11配置为输入模式
+AirGPIO_1000.setup(0x11)
+
+
+--P04引脚中断处理函数
+--id:0x04
+--level:触发中断后,某一时刻,扩展GPIO输入的电平状态,高电平为1, 低电平为0
+local function P04_int_cbfunc(id, level)
+    log.info("P04_int_cbfunc", id, level)
+end
+
+-- GPIO ID 0x04配置为中断模式,中断处理函数为P04_int_cbfunc
+AirGPIO_1000.setup(0x04, P04_int_cbfunc)
+]]
+function AirGPIO_1000.setup(gpio_id, gpio_mode)
+    --检查参数的合法性
+    if not check_gpio_id_valid(gpio_id) then
+        log.error("AirGPIO_1000.setup", "invalid gpio_id", gpio_id)
+        return false
+    end
+
+    if not (gpio_mode==0 or gpio_mode==1 or gpio_mode==nil or type(gpio_mode)=="function") then
+        log.error("AirGPIO_1000.setup", "invalid gpio_mode", type(gpio_mode), gpio_mode)
+        return false
+    end    
+
+    log.info("AirGPIO_1000.setup", "enter", gpio_id, type(gpio_mode), gpio_mode)
+
+
+    --根据扩展GPIO ID识别当前扩展GPIO使用的配置寄存器地址
+    --0x0x开头的ID为REG_CONFIG_0,0x01开头的ID为REG_CONFIG_1
+    local reg_addr = ((gpio_id>>4) == 0) and REG_CONFIG_0 or REG_CONFIG_1
+    --读取从机中输出寄存器当前的值
+    local reg_data = read_register(reg_addr)
+
+    if reg_data==nil then
+        log.error("AirGPIO_1000.setup", "read config register error", reg_addr)
+        return false
+    end    
+
+    local mask = 1<<(gpio_id&0x0F)
+    local value
+    --GPIO配置为输出模式
+    if gpio_mode==0 or gpio_mode==1 then
+        value = reg_data & (~mask)
+    --GPIO配置为输入模式
+    elseif gpio_mode==nil or type(gpio_mode)=="function" then
+        value = reg_data | mask
+    end
+
+    --如果寄存器新值和旧值相比,发生变化
+    --写新值到从机的配置寄存器中
+    if reg_data~=value then
+        if not write_register(reg_addr, value) then
+            log.error("AirGPIO_1000.setup", "config write error", reg_addr, value)
+            return false
+        end
+    end
+
+    log.info("AirGPIO_1000.setup", "config", reg_addr, reg_data, value)
+
+    --如果是中断模式,并且用户注册了中断处理函数
+    if type(gpio_mode)=="function" then
+        if AirGPIO_1000.ints==nil then
+            AirGPIO_1000.ints = {}
+        end
+        if AirGPIO_1000.ints[gpio_id]==nil then
+            AirGPIO_1000.ints[gpio_id] = {}
+        end
+        --存储中断处理函数
+        AirGPIO_1000.ints[gpio_id].cb_func = gpio_mode
+        --读取当前时刻GPIO的输入电平状态
+        AirGPIO_1000.ints[gpio_id].old_level = AirGPIO_1000.get(gpio_id)
+    end
+
+    --如果配置的是输入模式或者中断模式,可以直接返回了
+    if gpio_mode~=0 and gpio_mode~=1 then return true end
+
+
+    --如果配置的输出模式,初始化输出的电平为gpio_mode
+    if not AirGPIO_1000.set(gpio_id, gpio_mode) then
+        log.error("AirGPIO_1000.setup", "output set error")
+        return false
+    end
+
+    log.info("AirGPIO_1000.setup", "output", reg_addr, reg_data, value)
+
+    return true    
+end
+
+
+
+--设置AirGPIO_1000上配置为输出模式的扩展GPIO的输出电平
+
+--gpio_id:number类型;
+--         表示AirGPIO_1000上的扩展GPIO ID;
+--         取值范围:0x00到0x07,0x10到0x17,一共16种,分别对应16个扩展GPIO引脚;
+--         必须传入,不允许为空;
+--output_level:number类型;
+--              表示配置为输出模式的扩展GPIO对外输出的电平;
+--              取值范围:0和1,0表示输出低电平,1表示输出高电平;
+--              必须传入,不允许为空;
+
+--返回值:成功返回true,失败返回false
+function AirGPIO_1000.set(gpio_id, output_level)
+    --检查参数的合法性
+    if not check_gpio_id_valid(gpio_id) then
+        log.error("AirGPIO_1000.set", "invalid gpio_id", gpio_id)
+        return false
+    end
+
+    if not (output_level==0 or output_level==1) then
+        log.error("AirGPIO_1000.set", "invalid output_level", type(output_level), output_level)
+        return false
+    end    
+
+    log.info("AirGPIO_1000.set", "enter", gpio_id, output_level)
+
+    --根据扩展GPIO ID识别当前扩展GPIO使用的输出寄存器地址
+    --0x0x开头的ID为REG_OUTPUT_PORT_0,0x01开头的ID为REG_OUTPUT_PORT_1
+    local reg_addr = ((gpio_id>>4) == 0) and REG_OUTPUT_PORT_0 or REG_OUTPUT_PORT_1
+    --读取从机中输出寄存器当前的值
+    local reg_data = read_register(reg_addr)
+
+    if reg_data==nil then
+        log.error("AirGPIO_1000.set", "read output register error", reg_addr)
+        return false
+    end    
+
+    local mask = 1<<(gpio_id&0x0F)
+    local value
+
+    --输出低电平
+    if output_level==0 then
+        value = reg_data & (~mask)
+    --输出高电平
+    elseif output_level==1 then
+        value = reg_data | mask
+    end
+
+    --如果寄存器新值和旧值相比,发生变化
+    --写新值到从机的输出寄存器中
+    if reg_data~=value then
+        if not write_register(reg_addr, value) then
+            log.error("AirGPIO_1000.set", "output write error", reg_addr, value)
+            return false
+        end
+    end
+
+    log.info("AirGPIO_1000.set", "output", reg_addr, reg_data, value)
+
+    return true
+end
+
+
+--读取AirGPIO_1000上配置为输入或者中断模式的扩展GPIO的输入电平
+
+--gpio_id:number类型;
+--         表示AirGPIO_1000上的扩展GPIO ID;
+--         取值范围:0x00到0x07,0x10到0x17,一共16种,分别对应16个扩展GPIO引脚;
+--         必须传入,不允许为空;
+
+--返回值:number类型,表示输入的电平,0表示低电平,1表示高电平;如果读取失败,返回false
+function AirGPIO_1000.get(gpio_id)
+    --检查参数的合法性
+    if not check_gpio_id_valid(gpio_id) then
+        log.error("AirGPIO_1000.get", "invalid gpio_id", gpio_id)
+        return false
+    end
+
+    --根据扩展GPIO ID识别当前扩展GPIO使用的输入寄存器地址
+    --0x0x开头的ID为REG_INPUT_PORT_0,0x01开头的ID为REG_INPUT_PORT_1
+    local reg_addr = ((gpio_id>>4) == 0) and REG_INPUT_PORT_0 or REG_INPUT_PORT_1
+    --读取从机中输入寄存器当前的值
+    local value = read_register(reg_addr)
+
+    if not value then
+        log.error("AirGPIO_1000.get", "read_register error", reg_addr)
+        return false
+    end
+
+    --返回输入寄存器的值和GPIO对应的bit位的值
+    return ((value>>(gpio_id&0x0F)) & 0x01)
+end
+
+
+
+--关闭AirGPIO_1000上的扩展GPIO功能
+--实际上是恢复为默认状态(配置为输入)
+
+--gpio_id:number类型;
+--         表示AirGPIO_1000上的扩展GPIO ID;
+--         取值范围:0x00到0x07,0x10到0x17,一共16种,分别对应16个扩展GPIO引脚;
+--         必须传入,不允许为空;
+
+--返回值:成功返回true,失败返回false
+function AirGPIO_1000.close(gpio_id)
+    local result = AirGPIO_1000.setup(gpio_id)
+
+    if not result then
+        log.error("AirGPIO_1000.close", "error", gpio_id)
+    end
+
+    return result
+end
+
+
+return AirGPIO_1000

+ 96 - 0
module/Air8000/demo/accessory_board/AirGPIO_1000/gpio_app.lua

@@ -0,0 +1,96 @@
+--加载AirGPIO_1000驱动文件
+local air_gpio = require "AirGPIO_1000"
+
+
+--AirGPIO_1000扩展GPIO输出测试
+--P00每隔一秒切换输出一次高低电平,可以通过示波器或者万用表测量AirGPIO_1000上P00引脚电平
+local function gpio_output_task_func()
+    air_gpio.setup(0x00, 0)
+
+    while true do
+        air_gpio.set(0x00, 0)
+        sys.wait(1000)
+        air_gpio.set(0x00, 1)
+        sys.wait(1000)
+    end
+end
+
+
+--AirGPIO_1000扩展GPIO输入测试
+--P10配置为输出模式,每隔一秒切换输出一次高低电平
+--P11配置为输入模式,每隔一秒调用get接口读取一次输入的电平
+--将P10和P11两个引脚短接
+local function gpio_input_task_func()
+    air_gpio.setup(0x10, 0)
+    air_gpio.setup(0x11)
+
+    while true do
+        air_gpio.set(0x10, 0)
+        sys.wait(1000)
+        log.info("air_gpio.get(0x11)", air_gpio.get(0x11))
+        air_gpio.set(0x10, 1)
+        sys.wait(1000)
+        log.info("air_gpio.get(0x11)", air_gpio.get(0x11))
+    end
+end
+
+--P04引脚中断处理函数
+--id:0x04
+--level:触发中断后,某一时刻,扩展GPIO输入的电平状态,高电平为1, 低电平为0
+local function P04_int_cbfunc(id, level)
+    log.info("P04_int_cbfunc", id, level)
+end
+
+--P14引脚中断处理函数
+--id:0x14
+--level:触发中断后,某一时刻,扩展GPIO输入的电平状态,高电平为1, 低电平为0
+local function P14_int_cbfunc(id, level)
+    log.info("P14_int_cbfunc", id, level)
+end
+
+--AirGPIO_1000扩展GPIO中断测试
+--P03配置为输出模式,每隔一秒切换输出一次高低电平
+--P04配置为中断模式,并且配置中断处理函数P04_int_cbfunc
+--将P03和P04两个引脚短接
+--P13配置为输出模式,每隔一秒切换输出一次高低电平
+--P14配置为中断模式,并且配置中断处理函数P14_int_cbfunc
+--将P13和P14两个引脚短接
+local function gpio_int_task_func()
+    air_gpio.setup(0x03, 0)
+    air_gpio.setup(0x04, P04_int_cbfunc)
+
+    air_gpio.setup(0x13, 0)
+    air_gpio.setup(0x14, P14_int_cbfunc)
+
+    while true do
+        air_gpio.set(0x03, 0)
+        air_gpio.set(0x13, 0)
+        sys.wait(1000)
+        air_gpio.set(0x03, 1)
+        air_gpio.set(0x13, 1)
+        sys.wait(1000)
+    end
+end
+
+
+--初始化Air8000和AirGPIO_1000之间的通信参数
+--使用Air8101的I2C0
+--使用Air8101的GPIO2做为中断引脚
+--Air8101核心板和AirGPIO_1000配件板的接线方式如下
+--Air8101核心板             AirGPIO_1000配件板
+--VDD_EXT(3.3V)-----------------3V3
+--       GND-----------------GND
+--     I2C1_SDA-----------------SDA
+--     I2C1_SCL-----------------SCL
+--     GPIO2-----------------INT
+air_gpio.init(1, 2)
+
+--AirGPIO_1000的GPIO输出测试
+sys.taskInit(gpio_output_task_func)
+
+--AirGPIO_1000的GPIO输入测试
+sys.taskInit(gpio_input_task_func)
+
+--AirGPIO_1000的GPIO中断测试
+sys.taskInit(gpio_int_task_func)
+

+ 64 - 0
module/Air8000/demo/accessory_board/AirGPIO_1000/main.lua

@@ -0,0 +1,64 @@
+--[[
+必须定义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进行远程升级,根据自己项目的需求,自定义格式即可
+
+AirGPIO_1000是合宙设计生产的一款I2C转16路扩展GPIO的配件板;
+本demo演示的核心功能为:
+Air8000核心板+AirGPIO_1000配件板,演示I2C扩展16路GPIO功能;
+分输出、输入和中断三种应用场景来演示;
+更多说明参考本目录下的readme.md文件
+]]
+PROJECT = "AirGPIO_1000"
+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)
+
+ -- 加载gpio应用模块
+ require "gpio_app"
+
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后不要加任何语句!!!!!因为添加的任何语句都不会被执行

+ 95 - 0
module/Air8000/demo/accessory_board/AirGPIO_1000/readme.md

@@ -0,0 +1,95 @@
+
+## 演示功能概述
+
+AirGPIO_1000是合宙设计生产的一款I2C转16路扩展GPIO的配件板;
+
+本demo演示的核心功能为:
+
+Air8000核心板+AirGPIO_1000配件板,演示I2C扩展16路GPIO功能;
+
+分输出、输入和中断三种应用场景来演示;
+
+
+## 核心板+配件板资料
+
+[Air8000核心板+配件板相关资料](https://docs.openluat.com/air8000/product/shouce/)
+
+
+## 演示硬件环境
+
+![](https://docs.openluat.com/accessory/AirGPIO_1000/image/connect_Air8000.jpg)
+
+![](https://docs.openluat.com/accessory/AirSHT30_1000/image/8000.png)
+
+1、Air8000核心板
+
+2、AirGPIO_1000配件板
+
+3、母对母的杜邦线8根
+
+4、Air8000核心板和AirGPIO_1000配件板的硬件接线方式为
+
+| Air8000核心板 | AirGPIO_1000配件板 |
+| ------------ | ------------------ |
+|     VDD_EXT     |         3V3        |
+|     GND     |         GND        |
+|  I2C1_SDA  |         SDA        |
+| I2C1_SCL |         SCL        |
+|   GPIO2   |         INT        |
+
+- 扩展GPIO输出演示时,无需接线;通过万用表或者示波器检测AirGPIO_1000配件板上的P00电平即可
+
+- 扩展GPIO输入演示时,将AirGPIO_1000配件板上的P10和P11两个引脚通过杜邦线短接;软件上会将P10配置为输出(第一秒输出低电平,第二秒输出高电平,如此循环输出),将P11配置为输入,通过检测P11引脚输入电平的状态来演示
+
+- 扩展GPIO中断演示时,将AirGPIO_1000配件板上的P03和P04两个引脚通过杜邦线短接,将AirGPIO_1000配件板上的P13和P14两个引脚通过杜邦线短接;软件上会将P03和P13配置为输出(第一秒输出低电平,第二秒输出高电平,如此循环输出),将P04和P14配置为中断,通过检测中断函数的触发状态来演示
+
+
+## 演示软件环境
+
+1、Luatools下载调试工具
+
+2、[Air8000最新版本的内核固件](https://docs.openluat.com/air8101/luatos/firmware/)
+
+
+## 演示操作步骤
+
+1、搭建好演示硬件环境
+
+2、不需要修改demo脚本代码
+
+3、Luatools烧录内核固件和demo脚本代码
+
+4、烧录成功后,自动开机运行
+
+   (1) 通过万用表或者示波器检测AirGPIO_1000配件板上的P00电平,持续1秒输出0V的低电平,持续1秒输出3.3V的高电平,循环输出,表示GPIO输出测试正常;
+
+   (2) 通过观察Luatools的运行日志,首先打印 air_gpio.get(0x11) 0, 再隔一秒打印 air_gpio.get(0x11) 1,再隔一秒打印 air_gpio.get(0x11) 0,如此循环输出,表示GPIO输入测试正常;
+
+   (3) 通过观察Luatools的运行日志,首先打印 P04_int_cbfunc 4 0      P14_int_cbfunc 20 0, 再隔一秒打印  P04_int_cbfunc 4 1      P14_int_cbfunc 20 1,再隔一秒打印 P04_int_cbfunc 4 0      P14_int_cbfunc 20 0,如此循环输出,表示GPIO中断测试正常;
+
+[2025-09-24 16:15:09.221][000000054.571] I/user.air_gpio.get(0x11) 1
+[2025-09-24 16:15:09.223][000000054.572] I/user.AirGPIO_1000.set enter 16 0
+[2025-09-24 16:15:09.223][000000054.573] I/user.AirGPIO_1000.set output 3 255 254
+[2025-09-24 16:15:09.228][000000054.573] I/user.gpio_int_callback
+[2025-09-24 16:15:09.290][000000054.635] I/user.AirGPIO_1000.set enter 3 0
+[2025-09-24 16:15:09.290][000000054.636] I/user.AirGPIO_1000.set output 2 254 246
+[2025-09-24 16:15:09.295][000000054.636] I/user.AirGPIO_1000.set enter 19 0
+[2025-09-24 16:15:09.300][000000054.637] I/user.AirGPIO_1000.set output 3 254 246
+[2025-09-24 16:15:09.300][000000054.638] I/user.gpio_int_callback
+[2025-09-24 16:15:09.305][000000054.639] I/user.P04_int_cbfunc 4 0
+[2025-09-24 16:15:09.310][000000054.640] I/user.P14_int_cbfunc 20 0
+[2025-09-24 16:15:10.184][000000055.532] I/user.AirGPIO_1000.set enter 0 1
+[2025-09-24 16:15:10.187][000000055.533] I/user.AirGPIO_1000.set output 2 246 247
+[2025-09-24 16:15:10.228][000000055.573] I/user.air_gpio.get(0x11) 0
+[2025-09-24 16:15:10.228][000000055.574] I/user.AirGPIO_1000.set enter 16 1
+[2025-09-24 16:15:10.233][000000055.575] I/user.AirGPIO_1000.set output 3 246 247
+[2025-09-24 16:15:10.238][000000055.575] I/user.gpio_int_callback
+[2025-09-24 16:15:10.288][000000055.638] I/user.AirGPIO_1000.set enter 3 1
+[2025-09-24 16:15:10.288][000000055.639] I/user.AirGPIO_1000.set output 2 247 255
+[2025-09-24 16:15:10.293][000000055.639] I/user.AirGPIO_1000.set enter 19 1
+[2025-09-24 16:15:10.298][000000055.640] I/user.AirGPIO_1000.set output 3 247 255
+[2025-09-24 16:15:10.298][000000055.641] I/user.gpio_int_callback
+[2025-09-24 16:15:10.305][000000055.642] I/user.P04_int_cbfunc 4 1
+[2025-09-24 16:15:10.308][000000055.643] I/user.P14_int_cbfunc 20 1
+[2025-09-24 16:15:11.190][000000056.534] I/user.AirGPIO_1000.set enter 0 0
+[2025-09-24 16:15:11.200][000000056.535] I/user.AirGPIO_1000.set output 2 255 254