Эх сурвалжийг харах

add:新增780/8000/8101-AirRC522_1000-demo

马亚丹 1 сар өмнө
parent
commit
7500272a54

+ 189 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirRC522_1000/AirRC522_1000.lua

@@ -0,0 +1,189 @@
+--[[
+@module  AirRC522_1000
+@summary AirRC522_1000测试功能模块
+@version 1.0
+@date    2025.07.23
+@author  马亚丹
+@usage
+本demo演示的功能为:使用Air780EHM/EHV/EGH核心板通过SPI实现对RC522的操作,演示读写数据等操作。
+以 Air780EHM/EHV/EGH核心板为例, 接线如下:
+Air780EHM/EHV/EGH         AirRC522_1000
+GND(任意)                     GND
+3V3                           3.3V
+83/SPI0CS                     SDA
+86/SPI0CLK                    SCK
+85/SPI0MOSI                   MOSI
+84/SPI0MISO                   MISO
+19/GPIO22(可选任意空闲IO)      RST
+
+核心逻辑:
+1. 初始化并启用spi,如果初始化失败,退出程序
+2. 初始化RC522模块,如果初始化失败,退出程序
+3. 循环检测卡片。
+4. 向卡片指定块号写入数据,并读取数据验证一致性
+5. 读取卡片所有数据
+]]
+
+
+
+rc522 = require "rc522"
+
+
+-- 硬件配置参数(Air780EHM/EHV/EGH适配)
+local RC522_CONFIG = {
+    spi_id = 0,              -- SPI通道
+    cs_pin = 8,             -- 片选引脚
+    rst_pin = 22,            -- 复位引脚
+    spi_baud = 2 * 1000 * 1000, -- SPI波特率
+}
+
+-- 全局变量(模块内可见)
+local spi_dev = nil -- SPI设备对象
+
+-- 1. 初始化SPI接口
+local function init_spi()
+    log.info("RC522", "初始化SPI接口")
+    -- 配置SPI参数:模式0,8位数据,高位在前
+    spi_dev = spi.setup(
+        RC522_CONFIG.spi_id,
+        RC522_CONFIG.cs_pin,
+        0,       -- 极性0
+        0,       -- 相位0
+        8,       -- 数据位
+        RC522_CONFIG.spi_baud,
+        spi.MSB, -- 高位优先
+        spi.master,--主模式    
+        spi.half   --半双工
+    )
+
+    if not spi_dev then
+        log.error("RC522", "SPI初始化失败")
+        return false
+    end
+    log.info("RC522", "SPI初始化成功")
+    return true
+end
+
+-- 2. 初始化RC522模块
+local function init_rc522()
+    log.info("RC522", "初始化RC522传感器")
+    -- 初始化RC522硬件(SPI ID、CS引脚、RST引脚)
+    local init_ok = rc522.init(
+        RC522_CONFIG.spi_id,
+        RC522_CONFIG.cs_pin,
+        RC522_CONFIG.rst_pin
+    )
+
+    if not init_ok then
+        log.error("RC522", "传感器初始化失败")
+        return false
+    end
+    log.info("RC522", "传感器初始化成功")
+    return true
+end
+
+-- 3. 写数据
+local function write_ic_card(block, data)
+    
+    -- 步骤1:验证数据长度
+    if #data > 16 then
+        log.error("RC522", "数据过长(最大16字节)")
+        return false
+    end
+    
+    -- 步骤2:写入数据块
+    local write_ok = rc522.write_datablock(block, data)
+    if not write_ok then
+        log.error("RC522", "数据写入失败,块号:", block)
+        return false
+    end
+   
+    log.info("RC522", "数据写入成功,块号:", block,"写入数据是:",string.char(table.unpack(data)):toHex())
+    return true
+end
+
+-- 4. 检测并读取IC卡数据
+local function read_ic_card()
+    -- 检测是否有卡片靠近
+    local status, array_id = rc522.request(rc522.reqall)
+    if not status then
+        log.info("RC522", "未检测到卡片")
+        return false
+    end
+    log.info("RC522", "检测到卡片,类型:", array_id:toHex())
+
+    -- 防冲突检测,获取卡片唯一ID
+    local status, card_uid = rc522.anticoll(array_id)
+    if not status then
+        log.error("RC522", "防冲突检测失败")
+        return false
+    end
+    log.info("RC522", "卡片UID:", card_uid:toHex())
+
+    -- -- 选择卡片,激活卡片进行后续操作
+    local select_ok = rc522.select(card_uid)
+    if not select_ok then
+        log.error("RC522", "卡片选择失败")
+        return false
+    end
+
+    -- 写数据测试
+    local TEST_BLOCK=9
+    -- 待写入的数据
+    local TEST_DATA={0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00} 
+    if write_ic_card(TEST_BLOCK, TEST_DATA) then
+        -- 读数据验证
+        local read_ok, read_data = rc522.read_datablock(TEST_BLOCK)
+        
+        if read_ok and read_data == string.char(table.unpack(TEST_DATA))then            
+            log.info("RC522", "写入验证成功,数据是:", read_data:toHex())
+        else            
+            log.warn("RC522", "写入验证失败,实际读取的数据是:", read_data:toHex())
+        end
+    end
+
+    -- 读取卡片数据块(0-63块)
+    log.info("RC522", "开始读取卡片数据...")
+    for block = 0, 63 do
+        local read_ok, data = rc522.read_datablock(block)
+        if read_ok and data then
+            log.info(string.format("块[%d]", block), data:toHex())
+        else
+            log.warn(string.format("块[%d]读取失败", block))
+        end
+        --每读取完一个块等待20ms(时间可按需修改)
+        sys.wait(20)
+    end
+
+    -- 停止卡片操作
+    rc522.halt()
+    return true
+end
+-- 5. 关闭SPI设备,成功返回0
+local function spi_close_func()    
+    log.info("关闭spi", spi.close(RC522_CONFIG.spi_id))
+end
+
+-- 6. 主测试任务
+local function rc522_main_test()
+    -- 初始化硬件
+    if not init_spi() then
+        spi_close_func()
+        return
+    end
+    if not init_rc522() then
+        spi_close_func()
+        return
+    end
+
+    -- 循环检测卡片
+    log.info("RC522", "开始检测卡片")
+    while true do
+        read_ic_card()
+         -- 循环检测间隔:2s
+        sys.wait(2000)
+    end
+end
+
+-- 启动主任务
+sys.taskInit(rc522_main_test)

+ 73 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirRC522_1000/main.lua

@@ -0,0 +1,73 @@
+--[[
+@module  main
+@summary LuatOS用户应用脚本文件入口,总体调度应用逻辑 
+@version 001.000.000
+@date    2025.9.05
+@author  马亚丹
+@usage
+本demo是使用Air780EHM/EHV/EGH核心板挂载合宙AirRC522_1000配件板,来演示rc522的功能使用方法,代码脚本如下:
+1.rc522.lua :rc522扩展库文件,在AirRC522_1000.lua功能模块中require使用  
+2.AirRC522_1000.lua:功能演示核心文件,在main.lua中加载运行,详细逻辑请看AirRC522_1000.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 = "Air780EHM/EHV/EGH_rc522_DEMO"
+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)
+
+-- 加载AirRC522_1000功能模块
+require "AirRC522_1000"
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后不要加任何语句!!!!!因为添加的任何语句都不会被执行

+ 646 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirRC522_1000/rc522.lua

@@ -0,0 +1,646 @@
+--[[
+@module rc522
+@summary rc522 非接触式读写卡驱动
+@version 1.0
+@date    2022.06.14
+@author  Dozingfiretruck
+@usage
+--注意:因使用了sys.wait()所有api需要在task中使用
+-- 用法实例
+local rc522 = require "rc522"
+local function rc522_test()
+    spi_rc522 = spi.setup(0,nil,0,0,8,10*1000*1000,spi.MSB, spi.master,spi.half)
+    rc522.init(0,12,16)
+    wdata = {0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}
+    while 1 do
+        rc522.write_datablock(8,wdata)
+        for i=0,63 do
+            local a,b = rc522.read_datablock(i)
+            if a then
+                log.info("read",i,b:toHex())
+            end
+        end
+        sys.wait(500)
+    end
+end)
+sys.taskInit(rc522_test)
+]]
+
+
+local rc522= {}
+
+local rc522_spi, rc522_rst, rc522_irq, rc522_cs
+
+---器件所用地址
+local rc522_idle             = 0x00 --取消当前命令
+local rc522_authent          = 0x0E --验证密钥
+local rc522_receive          = 0x08 --接收数据
+local rc522_transmit         = 0x04 --发送数据
+local rc522_transceive       = 0x0C --发送并接收数据
+local rc522_resetphase       = 0x0F --复位
+local rc522_calccrc          = 0x03 --CRC计算
+
+--Mifare_One卡片命令字
+rc522.reqidl                 = 0x26 --寻天线区内未进入休眠状态
+rc522.reqall                 = 0x52 --寻天线区内全部卡
+local rc522_anticoll1        = 0x93 --防冲撞
+local rc522_anticoll2        = 0x95 --防冲撞
+local rc522_authent1a        = 0x60 --验证A密钥
+local rc522_authent1b        = 0x61 --验证B密钥
+local rc522_read             = 0x30 --读块
+local rc522_write            = 0xA0 --写块
+local rc522_decrement        = 0xC0 --扣款
+local rc522_increment        = 0xC1 --充值
+local rc522_restore          = 0xC2 --调块数据到缓冲区
+local rc522_transfer         = 0xB0 --保存缓冲区中数据
+local rc522_halt             = 0x50 --休眠
+
+--MF522寄存器定义
+-- PAGE 0
+local rc522_rfu00            = 0x00 --保留为将来之用
+local rc522_com_mand         = 0x01 --启动和停止命令的执行
+local rc522_com_ie           = 0x02 --中断请求传递的使能和禁能控制位
+local rc522_divl_en          = 0x03 --中断请求传递的使能和禁能控制位
+local rc522_com_irq          = 0x04 --包含中断请求标志
+local rc522_div_irq          = 0x05 --包含中断请求标志
+local rc522_error            = 0x06 --错误标志,指示执行的上个命令的错误状态
+local rc522_status1          = 0x07 --包含通信的状态标志
+local rc522_status2          = 0x08 --包含接收器和发送器的状态标志
+local rc522_fifo_data        = 0x09 --64 字节 FIFO 缓冲区的输入和输出
+local rc522_fifo_level       = 0x0A --指示 FIFO 中存储的字节数
+local rc522_water_level      = 0x0B --定义 FIFO 下溢和上溢报警的 FIFO 深度
+local rc522_control          = 0x0C --不同的控制寄存器
+local rc522_bit_framing      = 0x0D --面向位的帧的调节
+local rc522_coll             = 0x0E --RF 接口上检测到的第一个位冲突的位的位置
+local rc522_rfu0f            = 0x0F --保留为将来之用
+-- PAGE 1
+local RFU10                  = 0x10 --保留为将来之用
+local rc522_mode             = 0x11 --定义发送和接收的常用模式
+local rc522_tx_mode          = 0x12 --定义发送过程的数据传输速率
+local rc522_rx_mode          = 0x13 --定义接收过程中的数据传输速率
+local rc522_tx_control       = 0x14 --控制天线驱动器管脚 TX1 和 TX2 的逻辑特性
+local rc522_tx_ayto          = 0x15 --控制天线驱动器的设置
+local rc522_tx_sel           = 0x16 --选择天线驱动器的内部源
+local rc522_rx_sel           = 0x17 --选择内部的接收器设置
+local rc522_rx_threshold     = 0x18 --选择位译码器的阈值
+local rc522_demod            = 0x19 --定义解调器的设置
+local rc522_rfu1a            = 0x1A --保留为将来之用
+local rc522_rfu1b            = 0x1B --保留为将来之用
+local rc522_mifare           = 0x1C --控制 ISO 14443/MIFARE 模式中 106kbit/s 的通信
+local rc522_rfu1d            = 0x1D --保留为将来之用
+local rc522_rfu1e            = 0x1E --保留为将来之用
+local rc522_serial_speed     = 0x1F --选择串行 UART 接口的速率
+-- PAGE 2
+local rc522_rfu20            = 0x20 --保留为将来之用
+local rc522_crcresult_m      = 0x21 --显示 CRC 计算的实际 MSB 值
+local rc522_crcresult_l      = 0x22 --显示 CRC 计算的实际 LSB 值
+local rc522_rfu23            = 0x23 --保留为将来之用
+local rc522_mod_width        = 0x24 --控制 ModWidth 的设置
+local rc522_rfu25            = 0x25 --保留为将来之用
+local rc522_rfcfg            = 0x26 --配置接收器增益
+local rc522_gsn              = 0x27 --选择天线驱动器管脚 TX1 和 TX2 的调制电导
+local rc522_cwgscfg          = 0x28 --选择天线驱动器管脚 TX1 和 TX2 的调制电导
+local rc522_modgscfg         = 0x29 --选择天线驱动器管脚 TX1 和 TX2 的调制电导
+local rc522_tmode            = 0x2A --定义内部定时器的设置
+local rc522_tprescaler       = 0x2B --定义内部定时器的设置
+local rc522_tpreload_h       = 0x2C --描述 16 位长的定时器重装值
+local rc522_tpreload_l       = 0x2D --描述 16 位长的定时器重装值
+local rc522_tcounter_value_h = 0x2E --描述 16 位长的定时器重装值
+local rc522_tcounter_value_l = 0x2F --描述 16 位长的定时器重装值
+-- PAGE 3
+local rc522_rfu30            = 0x30 --保留为将来之用
+local rc522_testsel1         = 0x31 --常用测试信号的配置
+local rc522_testsel2         = 0x32 --常用测试信号的配置和 PRBS 控制
+local rc522_testpin_en       = 0x33 --D1-D7 输出驱动器的使能管脚(注:仅用于串行接口)
+local rc522_testpin_value    = 0x34 --定义 D1-D7 用作 I/O 总线时的值
+local rc522_testbus          = 0x35 --显示内部测试总线的状态
+local rc522_autotest         = 0x36 --控制数字自测试
+local rc522_version          = 0x37 --显示版本
+local rc522_analogtest       = 0x38 --控制管脚 AUX1 和 AUX2
+local rc522_testadc1         = 0x39 --定义 TestDAC1 的测试值
+local rc522_testadc2         = 0x3A --定义 TestDAC2 的测试值
+local rc522_testadc          = 0x3B --显示 ADC I 和 Q 通道的实际值
+local rc522_rfu3c            = 0x3C --保留用于产品测试
+local rc522_rfu3d            = 0x3D --保留用于产品测试
+local rc522_rfu3e            = 0x3E --保留用于产品测试
+local rc522_rfu3f            = 0x3F --保留用于产品测试
+
+local Key_A                  = string.char(0xff, 0xff, 0xff, 0xff, 0xff, 0xff)
+local Key_B                  = string.char(0xff, 0xff, 0xff, 0xff, 0xff, 0xff)
+
+--[[
+写rc522寄存器
+@api rc522.set_bit_mask(address, value)
+@number address 地址
+@number value    值
+@usage
+write_rawrc(rc522_bit_framing,0x07)
+]]
+local function write_rawrc(address, value)
+    rc522_cs(0)
+    local data = string.char((address << 1) & 0x7E) .. string.char(value)
+    spi.send(rc522_spi, data)
+    -- rc522_spi:send(data)
+    rc522_cs(1)
+end
+
+--[[
+读rc522寄存器
+@api rc522.read_rawrc(address)
+@number address 地址
+@return number 寄存器值
+@usage
+local n = read_rawrc(rc522_com_irq)
+]]
+local function read_rawrc(address)
+    rc522_cs(0)
+    local data = string.char(((address << 1) & 0x7E)|0x80)
+    spi.send(rc522_spi, data)
+    local val = spi.recv(rc522_spi, 1)
+    -- rc522_spi:send(data)
+    -- local val = rc522_spi:recv(1)
+    rc522_cs(1)
+    return string.byte(val)
+end
+
+--[[
+对rc522寄存器置位
+@api rc522.set_bit_mask(address, mask)
+@number address 地址
+@number mask    置位值
+@usage
+rc522.set_bit_mask (rc522_fifo_level, 0x80)	
+]]
+function rc522.set_bit_mask(address, mask)
+    local current = read_rawrc(address)
+    write_rawrc(address, bit.bor(current, mask))
+end
+
+--[[
+对rc522寄存器清位
+@api rc522.clear_bit_mask(address, mask)
+@number address 地址
+@number mask    清位值
+@usage
+rc522.clear_bit_mask(rc522_com_irq, 0x80 )
+]]
+function rc522.clear_bit_mask(address, mask)
+    local current = read_rawrc(address)
+    write_rawrc(address, bit.band(current, bit.bnot(mask)))
+end
+
+--[[
+命令通讯
+@api rc522.command(command,data)
+@number command
+@number data
+@return status data len 结果,返回数据,收到的位长度
+@usage
+rc522.version()
+]]
+function rc522.command(command, data)
+    local out_data = {}
+    local len      = 0
+    local status   = false
+    local Irq_en   = 0x00
+    local waitfor  = 0x00
+    local last_bits, n, l
+    if command == rc522_authent then
+        Irq_en  = 0x12
+        waitfor = 0x10
+    elseif command == rc522_transceive then
+        Irq_en  = 0x77
+        waitfor = 0x30
+    end
+    write_rawrc(0x02, bit.bor(Irq_en, 0x80))
+    rc522.clear_bit_mask(rc522_com_irq, 0x80)
+    write_rawrc(rc522_com_mand, rc522_idle)
+    rc522.set_bit_mask(rc522_fifo_level, 0x80)
+    for i = 1, #data do
+        write_rawrc(rc522_fifo_data, data[i])
+    end
+    write_rawrc(rc522_com_mand, command)
+    if (command == rc522_transceive) then
+        rc522.set_bit_mask(rc522_bit_framing, 0x80)
+    end
+    l = 12
+    while true do
+        sys.wait(1)
+        n = read_rawrc(rc522_com_irq)
+        l = l - 1
+        if not ((l ~= 0) and (bit.band(n, 0x01) == 0) and (bit.band(n, waitfor) == 0)) then
+            break
+        end
+    end
+    rc522.clear_bit_mask(rc522_bit_framing, 0x80)
+    if (l ~= 0) then
+        if bit.band(read_rawrc(rc522_error), 0x1B) == 0x00 then
+            status = true
+            if bit.band(n, Irq_en, 0x01) ~= 0 then
+                status = false
+            end
+            if (command == rc522_transceive) then
+                n = read_rawrc(rc522_fifo_level)
+                last_bits = bit.band(read_rawrc(rc522_control), 0x07)
+                if last_bits ~= 0 then
+                    len = (n - 1) * 8 + last_bits
+                else
+                    len = n * 8
+                end
+                if n == 0 then
+                    n = 1
+                end
+                for i = 1, n do
+                    out_data[i] = read_rawrc(rc522_fifo_data)
+                end
+            end
+        end
+    else
+        status = false
+    end
+    rc522.set_bit_mask(rc522_control, 0x80)
+    write_rawrc(rc522_com_mand, rc522_idle)
+    return status, out_data, len
+end
+
+--[[
+防冲撞
+@api rc522.anticoll(id)
+@string id 卡片序列号,4字节
+@return status uid 结果,uid
+@usage
+local status,uid = rc522.anticoll(array_id)
+]]
+function rc522.anticoll(id)
+    local check = 0
+    local uid
+    rc522.clear_bit_mask(rc522_status2, 0x08)
+    write_rawrc(rc522_bit_framing, 0x00)
+    rc522.clear_bit_mask(rc522_coll, 0x80)
+    local status, back_data = rc522.command(rc522_transceive, { 0x93, 0x20 })
+    if back_data and #back_data >= 5 then
+        for i = 1, 4 do
+            check = bit.bxor(check, back_data[i])
+        end
+        if check ~= back_data[5] then
+            status = false
+        end
+        uid = string.char(table.unpack(back_data, 1, 4))
+    end
+    rc522.clear_bit_mask(rc522_coll, 0x80)
+    return status, uid
+end
+
+--[[
+crc计算
+@api calculate_crc(data)
+@table data 数据
+@return table crc值
+@usage
+local crc = calculate_crc(buff)
+]]
+local function calculate_crc(data)
+    local ret_data = {}
+    rc522.clear_bit_mask(rc522_div_irq, 0x04)
+    write_rawrc(rc522_com_mand, rc522_idle)
+    rc522.set_bit_mask(rc522_fifo_level, 0x80)
+    for i = 1, #data do
+        write_rawrc(rc522_fifo_data, data[i])
+    end
+    write_rawrc(rc522_com_mand, rc522_calccrc)
+    local i = 0xFF
+    while true do
+        local n = read_rawrc(0x05)
+        i = i - 1
+        if not ((i ~= 0) and not (n & 0x04)) then
+            break
+        end
+    end
+    ret_data[1] = read_rawrc(0x22)
+    ret_data[2] = read_rawrc(0x21)
+    return ret_data
+end
+
+--[[
+验证卡片密码
+@api authstate(mode, addr,key,uid )
+@number mode 模式
+@number addr 地址
+@string key 密码
+@string uid uid
+@return bool 结果
+@usage
+status = authstate(rc522_authent1b, addr,Key_B,uid )
+]]
+local function authstate(mode, addr, key, uid)
+    local buff = {}
+    buff[1] = mode
+    buff[2] = addr
+    for i = 1, 6 do
+        buff[i + 2] = key:byte(i)
+    end
+    for i = 1, 4 do
+        buff[i + 8] = uid:byte(i)
+    end
+    local status, back_data = rc522.command(rc522_authent, buff)
+    if status == true and (read_rawrc(rc522_status2) & 0x08) ~= 0 then
+        return true
+    end
+    return false
+end
+
+--[[
+写数据到M1卡一块
+@api rc522.write(addr, data)
+@number addr 块地址(0-63)M1卡总共有16个扇区(每个扇区有:3个数据块+1个控制块),共64个块
+@table data 数据
+@return bool 结果
+@usage
+rc522.write(addr, data)
+]]
+function rc522.write(addr, data)
+    local buff = {}
+    buff[1] = rc522_write
+    buff[2] = addr
+    local crc = calculate_crc(buff)
+    buff[3] = crc[1]
+    buff[4] = crc[2]
+    local status, back_data, back_bits = rc522.command(rc522_transceive, buff)
+    if status == true and back_bits == 4 and (back_data[1] & 0x0F) == 0x0A then
+        local buf_w = {}
+        for i = 0, 16 do
+            table.insert(buf_w, data[i])
+        end
+        local crc = calculate_crc(buf_w)
+        table.insert(buf_w, crc[1])
+        table.insert(buf_w, crc[2])
+        status, back_data, back_bits = rc522.command(rc522_transceive, buf_w)
+        if status == true and back_bits == 4 and (back_data[1] & 0x0F) == 0x0A then
+            return status
+        end
+    end
+    return status
+end
+
+--[[
+写数据到M1卡一块
+@api rc522.read(addr)
+@number addr 块地址(0-63)M1卡总共有16个扇区(每个扇区有:3个数据块+1个控制块),共64个块
+@return bool,string 结果,数据
+@usage
+rc522.read(addr, data)
+]]
+local function read(addr)
+    local buff = {}
+    buff[1] = rc522_read
+    buff[2] = addr
+    local crc = calculate_crc(buff)
+    buff[3] = crc[1]
+    buff[4] = crc[2]
+    local status, back_data, back_bits = rc522.command(rc522_transceive, buff)
+    if status == true and back_bits == 0x90 then
+        local data = string.char(table.unpack(back_data, 1, 16))
+        return status, data
+    end
+    return false
+end
+
+--[[
+rc522 硬件版本
+@api rc522.version()
+@return number 硬件版本
+@usage
+rc522.version()
+]]
+function rc522.version()
+    return read_rawrc(rc522_version)
+end
+
+--[[
+rc522 命令卡片进入休眠状态
+@api rc522.halt()
+@return bool 结果
+@usage
+rc522.halt()
+]]
+function rc522.halt()
+    local buff = {}
+    buff[1] = rc522_halt
+    buff[2] = 0
+    local crc = calculate_crc(buff)
+    buff[3] = crc[1]
+    buff[4] = crc[2]
+    local status = rc522.command(rc522_transceive, buff)
+    return status
+end
+
+--[[
+rc522 复位
+@api rc522.reset()
+@usage
+rc522.reset()
+]]
+function rc522.reset()
+    rc522_rst(1)
+    rc522_rst(0)
+    rc522_rst(1)
+    write_rawrc(rc522_com_mand, 0x0f)
+    write_rawrc(rc522_mode, 0x3D)
+    write_rawrc(rc522_tpreload_l, 30)
+    write_rawrc(rc522_tpreload_h, 0)
+    write_rawrc(rc522_tmode, 0x8D)
+    write_rawrc(rc522_tprescaler, 0x3E)
+    write_rawrc(rc522_tx_ayto, 0x40)
+end
+
+--[[
+开启天线
+@api rc522.antenna_on()
+@usage
+rc522.antenna_on()
+]]
+function rc522.antenna_on()
+    local uc = read_rawrc(rc522_tx_control)
+    if ((uc & 0x03) == 0) then
+        rc522.set_bit_mask(rc522_tx_control, 0x03)
+    end
+end
+
+--[[
+关闭天线
+@api rc522.antenna_on()
+@usage
+rc522.antenna_on()
+]]
+function rc522.antenna_off()
+    rc522.clear_bit_mask(rc522_tx_control, 0x03)
+end
+
+--[[
+设置rc522工作方式为ISO14443_A
+@api rc522_config_isotype()
+@usage
+rc522_config_isotype()
+]]
+local function rc522_config_isotype()
+    rc522.clear_bit_mask(rc522_status2, 0x08)
+    write_rawrc(rc522_mode, 0x3D)
+    write_rawrc(rc522_rx_sel, 0x86)
+    write_rawrc(rc522_rfcfg, 0x7F)
+    write_rawrc(rc522_tpreload_l, 30)
+    write_rawrc(rc522_tpreload_h, 0)
+    write_rawrc(rc522_tmode, 0x8D)
+    write_rawrc(rc522_tprescaler, 0x3E)
+    rc522.antenna_on()
+end
+
+--[[
+rc522 寻卡
+@api rc522.request(req_code)
+@number req_code rc522.reqidl 寻天线区内未进入休眠状态 rc522.reqall 寻天线区内全部卡
+@return bool tagtype 结果,卡类型
+@usage
+status,array_id = rc522.request(rc522.reqall)
+]]
+function rc522.request(req_code)
+    rc522.clear_bit_mask(rc522_status2, 0x08)
+    write_rawrc(rc522_bit_framing, 0x07)
+    rc522.set_bit_mask(rc522_tx_control, 0x03)
+    local tagtype
+    local status, back_data, back_bits = rc522.command(rc522_transceive, { req_code })
+    if status == true and back_bits == 0x10 then
+        tagtype = string.char(table.unpack(back_data, 1, 2))
+        return status, tagtype
+    end
+    return false
+end
+
+--[[
+选定卡片
+@api rc522.select(id)
+@number id 卡片序列号,4字节
+@return bool 结果
+@usage
+status = rc522.select(id)
+]]
+function rc522.select(id)
+    if not id then
+        return false
+    end
+    local buff = {}
+    buff[1] = rc522_anticoll1
+    buff[2] = 0x70
+    buff[7] = 0
+    for i = 1, 4 do
+        buff[i + 2] = id:byte(i)
+        buff[7] = bit.bxor(buff[7], id:byte(i))
+    end
+    local crc = calculate_crc(buff)
+    buff[8] = crc[1]
+    buff[9] = crc[2]
+    rc522.clear_bit_mask(rc522_status2, 0x08)
+    local status, back_data, back_bits = rc522.command(rc522_transceive, buff)
+    if status == true and back_bits == 0x18 then
+        return true
+    end
+    return false
+end
+
+--[[
+按照rc522操作流程写入16字节数据到块
+@api rc522.write_datablock(addr,data)
+@number addr 任意块地址.M1卡总共有16个扇区(每个扇区有:3个数据块+1个控制块),共64个块
+@table data 指向要写入的数据,必须为16个字符
+@return bool 结果
+@usage
+rc522.write_datablock(addr,data)
+]]
+function rc522.write_datablock(addr, data)
+    if #data ~= 16 then
+        return false
+    end
+    local status, array_id = rc522.request(rc522.reqall)
+    if status ~= true then
+        status, array_id = rc522.request(rc522.reqall)
+    end
+    if status == true then
+        local status, uid = rc522.anticoll(array_id)
+        if not uid then
+            return false
+        end
+        if status == true and uid then
+            rc522.select(uid)
+            status = authstate(rc522_authent1b, addr, Key_B, uid)
+            if status == true then
+                status = rc522.write(addr, data)
+                rc522.halt()
+                return status
+            end
+        end
+    end
+    return false
+end
+
+--[[
+按照rc522操作流程读取块
+@api rc522.read_datablock(addr)
+@number addr 任意块地址.M1卡总共有16个扇区(每个扇区有:3个数据块+1个控制块),共64个块
+@return bool string 结果 数据
+@usage
+    for i=0,63 do
+        local a,b = rc522.read_datablock(i)
+        if a then
+            print("read",i,b:toHex())
+        end
+    end
+]]
+function rc522.read_datablock(addr)
+    local status, array_id = rc522.request(rc522.reqall)
+    if status ~= true then
+        status, array_id = rc522.request(rc522.reqall)
+    end
+    if status == true then
+        local status, uid = rc522.anticoll(array_id)
+        if status == true and uid then
+            rc522.select(uid)
+            status = authstate(rc522_authent1b, addr, Key_B, uid)
+            if status == true then
+                local status, data = read(addr)
+                if status == true then
+                    return true, data
+                end
+                rc522.halt()
+            end
+        end
+    end
+    return false
+end
+
+--[[
+rc522 初始化
+@api rc522.init(spi_id, cs, rst)
+@number spi_id spi端口号
+@number cs      cs引脚
+@number rst     rst引脚
+@return bool 初始化结果
+@usage
+spi_rc522 = spi.setup(0,nil,0,0,8,10*1000*1000,spi.MSB,1,1)
+rc522.init(0,pin.PB04,pin.PB01)
+]]
+function rc522.init(spi_id, cs, rst)
+    rc522_spi = spi_id
+    rc522_cs = gpio.setup(cs, 0, gpio.PULLUP)
+    rc522_cs(1)
+    rc522_rst = gpio.setup(rst, 0, gpio.PULLUP)
+    rc522_rst(1)
+    rc522.reset()
+    rc522_config_isotype()
+    log.debug("rc522.version", rc522.version())
+    return true
+end
+
+function rc522.test()
+
+end
+
+return rc522

+ 156 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirRC522_1000/readme.md

@@ -0,0 +1,156 @@
+## 功能模块介绍:
+
+1. main.lua:主程序入口
+
+2. rc522.lua :rc522扩展库文件,在AirRC522_1000.lua功能模块中require使用
+
+3. AirRC522_1000.lua:功能演示核心文件,在main.lua中加载运行,详细逻辑请看AirRC522_1000.lua 文件
+
+## 演示功能概述:
+
+核心逻辑:
+
+1. 初始化并启用spi,如果初始化失败,退出程序
+
+2. 初始化RC522模块,如果初始化失败,退出程序
+
+3. 循环检测卡片。
+
+4. 向卡片指定块号写入数据,并读取数据验证一致性
+
+5. 读取卡片所有数据
+
+## 演示硬件环境:
+
+![](https://docs.openluat.com/accessory/AirSPINORFLASH_1000/image/780EHV.jpg)
+
+![](https://docs.openluat.com/accessory/AirSPINORFLASH_1000/image/rc522.jpg)
+
+1. 合宙 Air780EHM/EHV/EGH 核心板一块
+
+2. 合宙 AirRC522_1000 一套
+
+3. TYPE-C USB 数据线一根 ,Air780EHV/EHM/EGH 核心板和数据线的硬件接线方式为:
+* Air780EHV/EHM/EGH核心板通过 TYPE-C USB 口供电;(USB的拨码开关off/on,拨到on)
+
+* TYPE-C USB 数据线直接插到核心板的 TYPE-C USB 座子,另外一端连接电脑 USB 口;
+4. 杜邦线 7根
+   Air780EHV/EHM/EGH 核心板与 AirRC522_1000 按以下方式接线:
+
+| Air780EHV/EHM/EGH核心板                         | AirRC522_1000 |
+| -------------------------------------------- | ------------- |
+| GND(任意)                                      | GND           |
+| 3V3                                          | 3.3V          |
+| 83/SPI0CS                                    | SDA           |
+| 86/SPI0CLK                                   | SCK           |
+| 85/SPI0MOSI                                  | MOSI          |
+| 84/SPI0MISO                                  | MISO          |
+| GPIO22(可选任意空闲IO) | RST           |
+
+## 演示软件环境:
+
+1. Luatools 下载调试工具
+
+2. 固件版本:LuatOS-SoC_V2018_Air780EHM_1,固件地址,如有最新固件请用最新 [https://docs.openluat.com/air780ehm/luatos/firmware/version/](https://docs.openluat.com/air780ehm/luatos/firmware/version/)
+
+3. 固件版本:LuatOS-SoC_V2018_Air780EHV_1,固件地址,如有最新固件请用最新 [https://docs.openluat.com/Air780EHV/luatos/firmware/version/](https://docs.openluat.com/air780ehv/luatos/firmware/version/)
+
+4. 固件版本:LuatOS-SoC_V2018_Air780EGH_1,固件地址,如有最新固件请用最新 [https://docs.openluat.com/air780egh/luatos/firmware/version/](https://docs.openluat.com/air780egh/luatos/firmware/version/)
+
+5. pc 系统 win11(win10 及以上)
+
+## 演示核心步骤:
+
+1. 搭建好硬件环境
+
+2. Luatools 烧录内核固件和demo 脚本代码.
+   AirRC522_1000.lua脚本中,硬件配置参数中rst_pin是使用Air780EHV/EHM/EGH核心板的GPIO22,如果接其他IO,注意修改硬件配置参数中rst_pin对应的管脚号。
+
+3. 烧录成功后,代码会自动运行,查看打印日志,如果正常运行,会打印相关信息,spi 初始化,rc522初始化,写入数据读取数据等。
+
+4. 如下 log 显示:
+
+```
+[2025-12-01 18:17:19.642][000000000.252] I/user.main Air780EHM/EHV/EGH_rc522_DEMO 001.000.000
+[2025-12-01 18:17:19.648][000000000.274] I/user.RC522 初始化SPI接口
+[2025-12-01 18:17:19.654][000000000.274] SPI_HWInit 552:spi0 speed 2000000,1994805,154
+[2025-12-01 18:17:19.661][000000000.275] I/user.RC522 SPI初始化成功
+[2025-12-01 18:17:19.668][000000000.275] I/user.RC522 初始化RC522传感器
+[2025-12-01 18:17:19.681][000000000.277] D/user.rc522.version 178
+[2025-12-01 18:17:19.694][000000000.278] I/user.RC522 传感器初始化成功
+[2025-12-01 18:17:19.705][000000000.278] I/user.RC522 开始检测卡片
+[2025-12-01 18:17:19.715][000000000.283] I/user.RC522 检测到卡片,类型: 0400 4
+[2025-12-01 18:17:19.723][000000000.290] I/user.RC522 卡片UID: E3E2AD14 8
+[2025-12-01 18:17:19.736][000000000.379] I/user.RC522 数据写入成功,块号: 9 写入数据是: 01000000000000000000000000000000 32
+[2025-12-01 18:17:19.744][000000000.417] I/user.RC522 写入验证成功,数据是: 01000000000000000000000000000000 32
+[2025-12-01 18:17:19.754][000000000.418] I/user.RC522 开始读取卡片数据...
+[2025-12-01 18:17:19.764][000000000.473] I/user.块[0] E3E2AD14B80804006263646566676869 32
+[2025-12-01 18:17:19.773][000000000.553] I/user.块[1] 00000000000000000000000000000000 32
+[2025-12-01 18:17:19.864][000000000.642] I/user.块[2] 00000000000000000000000000000000 32
+[2025-12-01 18:17:19.881][000000000.715] I/user.块[3] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 18:17:20.063][000000000.813] I/user.块[4] 00000000000000000000000000000000 32
+[2025-12-01 18:17:20.071][000000000.891] I/user.块[5] 00000000000000000000000000000000 32
+[2025-12-01 18:17:20.083][000000001.010] I/user.块[6] 00000000000000000000000000000000 32
+[2025-12-01 18:17:20.098][000000001.076] I/user.块[7] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 18:17:20.109][000000001.141] I/user.块[8] 00000000000000000000000000000000 32
+[2025-12-01 18:17:20.118][000000001.206] I/user.块[9] 01000000000000000000000000000000 32
+[2025-12-01 18:17:20.181][000000001.273] I/user.块[10] 00000000000000000000000000000000 32
+[2025-12-01 18:17:20.244][000000001.339] I/user.块[11] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 18:17:20.308][000000001.404] I/user.块[12] 00000000000000000000000000000000 32
+[2025-12-01 18:17:20.370][000000001.469] I/user.块[13] 00000000000000000000000000000000 32
+[2025-12-01 18:17:20.448][000000001.534] I/user.块[14] 00000000000000000000000000000000 32
+[2025-12-01 18:17:20.510][000000001.599] I/user.块[15] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 18:17:20.573][000000001.665] I/user.块[16] 00000000000000000000000000000000 32
+[2025-12-01 18:17:20.735][000000001.719] D/mobile cid1, state0
+[2025-12-01 18:17:20.745][000000001.720] D/mobile bearer act 0, result 0
+[2025-12-01 18:17:20.759][000000001.721] D/mobile NETIF_LINK_ON -> IP_READY
+[2025-12-01 18:17:20.773][000000001.753] I/user.块[17] 00000000000000000000000000000000 32
+[2025-12-01 18:17:20.785][000000001.758] D/mobile TIME_SYNC 0
+[2025-12-01 18:17:20.795][000000001.819] I/user.块[18] 00000000000000000000000000000000 32
+[2025-12-01 18:17:20.806][000000001.884] I/user.块[19] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 18:17:20.850][000000001.949] I/user.块[20] 00000000000000000000000000000000 32
+[2025-12-01 18:17:20.928][000000002.014] I/user.块[21] 00000000000000000000000000000000 32
+[2025-12-01 18:17:20.990][000000002.080] I/user.块[22] 00000000000000000000000000000000 32
+[2025-12-01 18:17:21.054][000000002.145] I/user.块[23] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 18:17:21.116][000000002.210] I/user.块[24] 00000000000000000000000000000000 32
+[2025-12-01 18:17:21.180][000000002.276] I/user.块[25] 00000000000000000000000000000000 32
+[2025-12-01 18:17:21.256][000000002.345] I/user.块[26] 00000000000000000000000000000000 32
+[2025-12-01 18:17:21.318][000000002.410] I/user.块[27] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 18:17:21.382][000000002.475] I/user.块[28] 00000000000000000000000000000000 32
+[2025-12-01 18:17:21.443][000000002.540] I/user.块[29] 00000000000000000000000000000000 32
+[2025-12-01 18:17:21.507][000000002.605] I/user.块[30] 00000000000000000000000000000000 32
+[2025-12-01 18:17:21.585][000000002.670] I/user.块[31] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 18:17:21.648][000000002.736] I/user.块[32] 00000000000000000000000000000000 32
+[2025-12-01 18:17:21.711][000000002.801] I/user.块[33] 00000000000000000000000000000000 32
+[2025-12-01 18:17:21.774][000000002.866] I/user.块[34] 00000000000000000000000000000000 32
+[2025-12-01 18:17:21.835][000000002.932] I/user.块[35] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 18:17:21.912][000000002.998] I/user.块[36] 00000000000000000000000000000000 32
+[2025-12-01 18:17:21.976][000000003.064] I/user.块[37] 00000000000000000000000000000000 32
+[2025-12-01 18:17:22.038][000000003.129] I/user.块[38] 00000000000000000000000000000000 32
+[2025-12-01 18:17:22.102][000000003.194] I/user.块[39] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 18:17:22.164][000000003.259] I/user.块[40] 00000000000000000000000000000000 32
+[2025-12-01 18:17:22.240][000000003.324] I/user.块[41] 00000000000000000000000000000000 32
+[2025-12-01 18:17:22.303][000000003.389] I/user.块[42] 00000000000000000000000000000000 32
+[2025-12-01 18:17:22.365][000000003.454] I/user.块[43] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 18:17:22.429][000000003.519] I/user.块[44] 00000000000000000000000000000000 32
+[2025-12-01 18:17:22.491][000000003.584] I/user.块[45] 00000000000000000000000000000000 32
+[2025-12-01 18:17:22.554][000000003.649] I/user.块[46] 00000000000000000000000000000000 32
+[2025-12-01 18:17:22.615][000000003.714] I/user.块[47] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 18:17:22.696][000000003.779] I/user.块[48] 00000000000000000000000000000000 32
+[2025-12-01 18:17:22.757][000000003.844] I/user.块[49] 00000000000000000000000000000000 32
+[2025-12-01 18:17:22.817][000000003.909] I/user.块[50] 00000000000000000000000000000000 32
+[2025-12-01 18:17:22.879][000000003.974] I/user.块[51] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 18:17:22.942][000000004.040] I/user.块[52] 00000000000000000000000000000000 32
+[2025-12-01 18:17:23.018][000000004.105] I/user.块[53] 00000000000000000000000000000000 32
+[2025-12-01 18:17:23.080][000000004.170] I/user.块[54] 00000000000000000000000000000000 32
+[2025-12-01 18:17:23.142][000000004.235] I/user.块[55] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 18:17:23.205][000000004.300] I/user.块[56] 00000000000000000000000000000000 32
+[2025-12-01 18:17:23.267][000000004.366] I/user.块[57] 00000000000000000000000000000000 32
+[2025-12-01 18:17:23.346][000000004.431] I/user.块[58] 00000000000000000000000000000000 32
+[2025-12-01 18:17:23.407][000000004.497] I/user.块[59] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 18:17:23.467][000000004.562] I/user.块[60] 00000000000000000000000000000000 32
+[2025-12-01 18:17:23.530][000000004.627] I/user.块[61] 00000000000000000000000000000000 32
+[2025-12-01 18:17:23.607][000000004.692] I/user.块[62] 00000000000000000000000000000000 32
+[2025-12-01 18:17:23.669][000000004.757] I/user.块[63] 000000000000FF078069FFFFFFFFFFFF 32
+
+```

+ 188 - 0
module/Air780EPM/demo/accessory_board/AirRC522_1000/AirRC522_1000.lua

@@ -0,0 +1,188 @@
+--[[
+@module  AirRC522_1000
+@summary AirRC522_1000测试功能模块
+@version 1.0
+@date    2025.07.23
+@author  马亚丹
+@usage
+本demo演示的功能为:使用Air780EPM核心板通过SPI实现对RC522的操作,演示读写数据等操作。
+以 Air780EPM核心板为例, 接线如下:
+Air780EPM核心板            AirRC522_1000
+GND(任意)                    GND
+3V3                          3.3V
+83/SPI0CS                    SDA
+86/SPI0CLK                   SCK
+85/SPI0MOSI                  MOSI
+84/SPI0MISO                  MISO
+19/GPIO22(可选任意空闲IO)     RST
+
+核心逻辑:
+1. 初始化并启用spi,如果初始化失败,退出程序
+2. 初始化RC522模块,如果初始化失败,退出程序
+3. 循环检测卡片。
+4. 向卡片指定块号写入数据,并读取数据验证一致性
+5. 读取卡片所有数据
+]]
+
+
+
+rc522 = require "rc522"
+
+
+-- 硬件配置参数(Air780EPM适配)
+local RC522_CONFIG = {
+    spi_id = 0,                 -- SPI通道
+    cs_pin = 8,                 -- 片选引脚
+    rst_pin = 22,               -- 复位引脚
+    spi_baud = 2 * 1000 * 1000, -- SPI波特率
+}
+
+-- 全局变量(模块内可见)
+local spi_dev = nil -- SPI设备对象
+
+-- 1. 初始化SPI接口
+local function init_spi()
+    log.info("RC522", "初始化SPI接口")
+    -- 配置SPI参数:模式0,8位数据,高位在前
+    spi_dev = spi.setup(
+        RC522_CONFIG.spi_id,
+        RC522_CONFIG.cs_pin,
+        0,          -- 极性0
+        0,          -- 相位0
+        8,          -- 数据位
+        RC522_CONFIG.spi_baud,
+        spi.MSB,    -- 高位优先
+        spi.master, --主模式
+        spi.half    --半双工
+    )
+
+    if not spi_dev then
+        log.error("RC522", "SPI初始化失败")
+        return false
+    end
+    log.info("RC522", "SPI初始化成功")
+    return true
+end
+
+-- 2. 初始化RC522模块
+local function init_rc522()
+    log.info("RC522", "初始化RC522传感器")
+    -- 初始化RC522硬件(SPI ID、CS引脚、RST引脚)
+    local init_ok = rc522.init(
+        RC522_CONFIG.spi_id,
+        RC522_CONFIG.cs_pin,
+        RC522_CONFIG.rst_pin
+    )
+
+    if not init_ok then
+        log.error("RC522", "传感器初始化失败")
+        return false
+    end
+    log.info("RC522", "传感器初始化成功")
+    return true
+end
+
+-- 3. 写数据
+local function write_ic_card(block, data)
+    -- 步骤1:验证数据长度
+    if #data > 16 then
+        log.error("RC522", "数据过长(最大16字节)")
+        return false
+    end
+
+    -- 步骤2:写入数据块
+    local write_ok = rc522.write_datablock(block, data)
+    if not write_ok then
+        log.error("RC522", "数据写入失败,块号:", block)
+        return false
+    end
+
+    log.info("RC522", "数据写入成功,块号:", block, "写入数据是:", string.char(table.unpack(data)):toHex())
+    return true
+end
+
+-- 4. 检测并读取IC卡数据
+local function read_ic_card()
+    -- 检测是否有卡片靠近
+    local status, array_id = rc522.request(rc522.reqall)
+    if not status then
+        log.info("RC522", "未检测到卡片")
+        return false
+    end
+    log.info("RC522", "检测到卡片,类型:", array_id:toHex())
+
+    -- 防冲突检测,获取卡片唯一ID
+    local status, card_uid = rc522.anticoll(array_id)
+    if not status then
+        log.error("RC522", "防冲突检测失败")
+        return false
+    end
+    log.info("RC522", "卡片UID:", card_uid:toHex())
+
+    -- -- 选择卡片,激活卡片进行后续操作
+    local select_ok = rc522.select(card_uid)
+    if not select_ok then
+        log.error("RC522", "卡片选择失败")
+        return false
+    end
+
+    -- 写数据测试
+    local TEST_BLOCK = 9
+    -- 待写入的数据
+    local TEST_DATA = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
+    if write_ic_card(TEST_BLOCK, TEST_DATA) then
+        -- 读数据验证
+        local read_ok, read_data = rc522.read_datablock(TEST_BLOCK)
+
+        if read_ok and read_data == string.char(table.unpack(TEST_DATA)) then
+            log.info("RC522", "写入验证成功,数据是:", read_data:toHex())
+        else
+            log.warn("RC522", "写入验证失败,实际读取的数据是:", read_data:toHex())
+        end
+    end
+
+    -- 读取卡片数据块(0-63块)
+    log.info("RC522", "开始读取卡片数据...")
+    for block = 0, 63 do
+        local read_ok, data = rc522.read_datablock(block)
+        if read_ok and data then
+            log.info(string.format("块[%d]", block), data:toHex())
+        else
+            log.warn(string.format("块[%d]读取失败", block))
+        end
+        --每读取完一个块等待20ms(时间可按需修改)
+        sys.wait(20)
+    end
+
+    -- 停止卡片操作
+    rc522.halt()
+    return true
+end
+-- 5. 关闭SPI设备,成功返回0
+local function spi_close_func()
+    log.info("关闭spi", spi.close(RC522_CONFIG.spi_id))
+end
+
+-- 6. 主测试任务
+local function rc522_main_test()
+    -- 初始化硬件
+    if not init_spi() then
+        spi_close_func()
+        return
+    end
+    if not init_rc522() then
+        spi_close_func()
+        return
+    end
+
+    -- 循环检测卡片
+    log.info("RC522", "开始检测卡片")
+    while true do
+        read_ic_card()
+        -- 循环检测间隔:2s
+        sys.wait(2000)
+    end
+end
+
+-- 启动主任务
+sys.taskInit(rc522_main_test)

+ 73 - 0
module/Air780EPM/demo/accessory_board/AirRC522_1000/main.lua

@@ -0,0 +1,73 @@
+--[[
+@module  main
+@summary LuatOS用户应用脚本文件入口,总体调度应用逻辑 
+@version 001.000.000
+@date    2025.9.05
+@author  马亚丹
+@usage
+本demo是使用Air780EPM核心板挂载合宙AirRC522_1000配件板,来演示rc522的功能使用方法,代码脚本如下:
+1.rc522.lua :rc522扩展库文件,在AirRC522_1000.lua功能模块中require使用  
+2.AirRC522_1000.lua:功能演示核心文件,在main.lua中加载运行,详细逻辑请看AirRC522_1000.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 = "Air780EPM_rc522_DEMO"
+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)
+
+-- 加载AirRC522_1000功能模块
+require "AirRC522_1000"
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后不要加任何语句!!!!!因为添加的任何语句都不会被执行

+ 646 - 0
module/Air780EPM/demo/accessory_board/AirRC522_1000/rc522.lua

@@ -0,0 +1,646 @@
+--[[
+@module rc522
+@summary rc522 非接触式读写卡驱动
+@version 1.0
+@date    2022.06.14
+@author  Dozingfiretruck
+@usage
+--注意:因使用了sys.wait()所有api需要在task中使用
+-- 用法实例
+local rc522 = require "rc522"
+local function rc522_test()
+    spi_rc522 = spi.setup(0,nil,0,0,8,10*1000*1000,spi.MSB, spi.master,spi.half)
+    rc522.init(0,12,16)
+    wdata = {0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}
+    while 1 do
+        rc522.write_datablock(8,wdata)
+        for i=0,63 do
+            local a,b = rc522.read_datablock(i)
+            if a then
+                log.info("read",i,b:toHex())
+            end
+        end
+        sys.wait(500)
+    end
+end)
+sys.taskInit(rc522_test)
+]]
+
+
+local rc522                  = {}
+
+local rc522_spi, rc522_rst, rc522_irq, rc522_cs
+
+---器件所用地址
+local rc522_idle             = 0x00 --取消当前命令
+local rc522_authent          = 0x0E --验证密钥
+local rc522_receive          = 0x08 --接收数据
+local rc522_transmit         = 0x04 --发送数据
+local rc522_transceive       = 0x0C --发送并接收数据
+local rc522_resetphase       = 0x0F --复位
+local rc522_calccrc          = 0x03 --CRC计算
+
+--Mifare_One卡片命令字
+rc522.reqidl                 = 0x26 --寻天线区内未进入休眠状态
+rc522.reqall                 = 0x52 --寻天线区内全部卡
+local rc522_anticoll1        = 0x93 --防冲撞
+local rc522_anticoll2        = 0x95 --防冲撞
+local rc522_authent1a        = 0x60 --验证A密钥
+local rc522_authent1b        = 0x61 --验证B密钥
+local rc522_read             = 0x30 --读块
+local rc522_write            = 0xA0 --写块
+local rc522_decrement        = 0xC0 --扣款
+local rc522_increment        = 0xC1 --充值
+local rc522_restore          = 0xC2 --调块数据到缓冲区
+local rc522_transfer         = 0xB0 --保存缓冲区中数据
+local rc522_halt             = 0x50 --休眠
+
+--MF522寄存器定义
+-- PAGE 0
+local rc522_rfu00            = 0x00 --保留为将来之用
+local rc522_com_mand         = 0x01 --启动和停止命令的执行
+local rc522_com_ie           = 0x02 --中断请求传递的使能和禁能控制位
+local rc522_divl_en          = 0x03 --中断请求传递的使能和禁能控制位
+local rc522_com_irq          = 0x04 --包含中断请求标志
+local rc522_div_irq          = 0x05 --包含中断请求标志
+local rc522_error            = 0x06 --错误标志,指示执行的上个命令的错误状态
+local rc522_status1          = 0x07 --包含通信的状态标志
+local rc522_status2          = 0x08 --包含接收器和发送器的状态标志
+local rc522_fifo_data        = 0x09 --64 字节 FIFO 缓冲区的输入和输出
+local rc522_fifo_level       = 0x0A --指示 FIFO 中存储的字节数
+local rc522_water_level      = 0x0B --定义 FIFO 下溢和上溢报警的 FIFO 深度
+local rc522_control          = 0x0C --不同的控制寄存器
+local rc522_bit_framing      = 0x0D --面向位的帧的调节
+local rc522_coll             = 0x0E --RF 接口上检测到的第一个位冲突的位的位置
+local rc522_rfu0f            = 0x0F --保留为将来之用
+-- PAGE 1
+local RFU10                  = 0x10 --保留为将来之用
+local rc522_mode             = 0x11 --定义发送和接收的常用模式
+local rc522_tx_mode          = 0x12 --定义发送过程的数据传输速率
+local rc522_rx_mode          = 0x13 --定义接收过程中的数据传输速率
+local rc522_tx_control       = 0x14 --控制天线驱动器管脚 TX1 和 TX2 的逻辑特性
+local rc522_tx_ayto          = 0x15 --控制天线驱动器的设置
+local rc522_tx_sel           = 0x16 --选择天线驱动器的内部源
+local rc522_rx_sel           = 0x17 --选择内部的接收器设置
+local rc522_rx_threshold     = 0x18 --选择位译码器的阈值
+local rc522_demod            = 0x19 --定义解调器的设置
+local rc522_rfu1a            = 0x1A --保留为将来之用
+local rc522_rfu1b            = 0x1B --保留为将来之用
+local rc522_mifare           = 0x1C --控制 ISO 14443/MIFARE 模式中 106kbit/s 的通信
+local rc522_rfu1d            = 0x1D --保留为将来之用
+local rc522_rfu1e            = 0x1E --保留为将来之用
+local rc522_serial_speed     = 0x1F --选择串行 UART 接口的速率
+-- PAGE 2
+local rc522_rfu20            = 0x20 --保留为将来之用
+local rc522_crcresult_m      = 0x21 --显示 CRC 计算的实际 MSB 值
+local rc522_crcresult_l      = 0x22 --显示 CRC 计算的实际 LSB 值
+local rc522_rfu23            = 0x23 --保留为将来之用
+local rc522_mod_width        = 0x24 --控制 ModWidth 的设置
+local rc522_rfu25            = 0x25 --保留为将来之用
+local rc522_rfcfg            = 0x26 --配置接收器增益
+local rc522_gsn              = 0x27 --选择天线驱动器管脚 TX1 和 TX2 的调制电导
+local rc522_cwgscfg          = 0x28 --选择天线驱动器管脚 TX1 和 TX2 的调制电导
+local rc522_modgscfg         = 0x29 --选择天线驱动器管脚 TX1 和 TX2 的调制电导
+local rc522_tmode            = 0x2A --定义内部定时器的设置
+local rc522_tprescaler       = 0x2B --定义内部定时器的设置
+local rc522_tpreload_h       = 0x2C --描述 16 位长的定时器重装值
+local rc522_tpreload_l       = 0x2D --描述 16 位长的定时器重装值
+local rc522_tcounter_value_h = 0x2E --描述 16 位长的定时器重装值
+local rc522_tcounter_value_l = 0x2F --描述 16 位长的定时器重装值
+-- PAGE 3
+local rc522_rfu30            = 0x30 --保留为将来之用
+local rc522_testsel1         = 0x31 --常用测试信号的配置
+local rc522_testsel2         = 0x32 --常用测试信号的配置和 PRBS 控制
+local rc522_testpin_en       = 0x33 --D1-D7 输出驱动器的使能管脚(注:仅用于串行接口)
+local rc522_testpin_value    = 0x34 --定义 D1-D7 用作 I/O 总线时的值
+local rc522_testbus          = 0x35 --显示内部测试总线的状态
+local rc522_autotest         = 0x36 --控制数字自测试
+local rc522_version          = 0x37 --显示版本
+local rc522_analogtest       = 0x38 --控制管脚 AUX1 和 AUX2
+local rc522_testadc1         = 0x39 --定义 TestDAC1 的测试值
+local rc522_testadc2         = 0x3A --定义 TestDAC2 的测试值
+local rc522_testadc          = 0x3B --显示 ADC I 和 Q 通道的实际值
+local rc522_rfu3c            = 0x3C --保留用于产品测试
+local rc522_rfu3d            = 0x3D --保留用于产品测试
+local rc522_rfu3e            = 0x3E --保留用于产品测试
+local rc522_rfu3f            = 0x3F --保留用于产品测试
+
+local Key_A                  = string.char(0xff, 0xff, 0xff, 0xff, 0xff, 0xff)
+local Key_B                  = string.char(0xff, 0xff, 0xff, 0xff, 0xff, 0xff)
+
+--[[
+写rc522寄存器
+@api rc522.set_bit_mask(address, value)
+@number address 地址
+@number value    值
+@usage
+write_rawrc(rc522_bit_framing,0x07)
+]]
+local function write_rawrc(address, value)
+    rc522_cs(0)
+    local data = string.char((address << 1) & 0x7E) .. string.char(value)
+    spi.send(rc522_spi, data)
+    -- rc522_spi:send(data)
+    rc522_cs(1)
+end
+
+--[[
+读rc522寄存器
+@api rc522.read_rawrc(address)
+@number address 地址
+@return number 寄存器值
+@usage
+local n = read_rawrc(rc522_com_irq)
+]]
+local function read_rawrc(address)
+    rc522_cs(0)
+    local data = string.char(((address << 1) & 0x7E)|0x80)
+    spi.send(rc522_spi, data)
+    local val = spi.recv(rc522_spi, 1)
+    -- rc522_spi:send(data)
+    -- local val = rc522_spi:recv(1)
+    rc522_cs(1)
+    return string.byte(val)
+end
+
+--[[
+对rc522寄存器置位
+@api rc522.set_bit_mask(address, mask)
+@number address 地址
+@number mask    置位值
+@usage
+rc522.set_bit_mask (rc522_fifo_level, 0x80)	
+]]
+function rc522.set_bit_mask(address, mask)
+    local current = read_rawrc(address)
+    write_rawrc(address, bit.bor(current, mask))
+end
+
+--[[
+对rc522寄存器清位
+@api rc522.clear_bit_mask(address, mask)
+@number address 地址
+@number mask    清位值
+@usage
+rc522.clear_bit_mask(rc522_com_irq, 0x80 )
+]]
+function rc522.clear_bit_mask(address, mask)
+    local current = read_rawrc(address)
+    write_rawrc(address, bit.band(current, bit.bnot(mask)))
+end
+
+--[[
+命令通讯
+@api rc522.command(command,data)
+@number command
+@number data
+@return status data len 结果,返回数据,收到的位长度
+@usage
+rc522.version()
+]]
+function rc522.command(command, data)
+    local out_data = {}
+    local len      = 0
+    local status   = false
+    local Irq_en   = 0x00
+    local waitfor  = 0x00
+    local last_bits, n, l
+    if command == rc522_authent then
+        Irq_en  = 0x12
+        waitfor = 0x10
+    elseif command == rc522_transceive then
+        Irq_en  = 0x77
+        waitfor = 0x30
+    end
+    write_rawrc(0x02, bit.bor(Irq_en, 0x80))
+    rc522.clear_bit_mask(rc522_com_irq, 0x80)
+    write_rawrc(rc522_com_mand, rc522_idle)
+    rc522.set_bit_mask(rc522_fifo_level, 0x80)
+    for i = 1, #data do
+        write_rawrc(rc522_fifo_data, data[i])
+    end
+    write_rawrc(rc522_com_mand, command)
+    if (command == rc522_transceive) then
+        rc522.set_bit_mask(rc522_bit_framing, 0x80)
+    end
+    l = 12
+    while true do
+        sys.wait(1)
+        n = read_rawrc(rc522_com_irq)
+        l = l - 1
+        if not ((l ~= 0) and (bit.band(n, 0x01) == 0) and (bit.band(n, waitfor) == 0)) then
+            break
+        end
+    end
+    rc522.clear_bit_mask(rc522_bit_framing, 0x80)
+    if (l ~= 0) then
+        if bit.band(read_rawrc(rc522_error), 0x1B) == 0x00 then
+            status = true
+            if bit.band(n, Irq_en, 0x01) ~= 0 then
+                status = false
+            end
+            if (command == rc522_transceive) then
+                n = read_rawrc(rc522_fifo_level)
+                last_bits = bit.band(read_rawrc(rc522_control), 0x07)
+                if last_bits ~= 0 then
+                    len = (n - 1) * 8 + last_bits
+                else
+                    len = n * 8
+                end
+                if n == 0 then
+                    n = 1
+                end
+                for i = 1, n do
+                    out_data[i] = read_rawrc(rc522_fifo_data)
+                end
+            end
+        end
+    else
+        status = false
+    end
+    rc522.set_bit_mask(rc522_control, 0x80)
+    write_rawrc(rc522_com_mand, rc522_idle)
+    return status, out_data, len
+end
+
+--[[
+防冲撞
+@api rc522.anticoll(id)
+@string id 卡片序列号,4字节
+@return status uid 结果,uid
+@usage
+local status,uid = rc522.anticoll(array_id)
+]]
+function rc522.anticoll(id)
+    local check = 0
+    local uid
+    rc522.clear_bit_mask(rc522_status2, 0x08)
+    write_rawrc(rc522_bit_framing, 0x00)
+    rc522.clear_bit_mask(rc522_coll, 0x80)
+    local status, back_data = rc522.command(rc522_transceive, { 0x93, 0x20 })
+    if back_data and #back_data >= 5 then
+        for i = 1, 4 do
+            check = bit.bxor(check, back_data[i])
+        end
+        if check ~= back_data[5] then
+            status = false
+        end
+        uid = string.char(table.unpack(back_data, 1, 4))
+    end
+    rc522.clear_bit_mask(rc522_coll, 0x80)
+    return status, uid
+end
+
+--[[
+crc计算
+@api calculate_crc(data)
+@table data 数据
+@return table crc值
+@usage
+local crc = calculate_crc(buff)
+]]
+local function calculate_crc(data)
+    local ret_data = {}
+    rc522.clear_bit_mask(rc522_div_irq, 0x04)
+    write_rawrc(rc522_com_mand, rc522_idle)
+    rc522.set_bit_mask(rc522_fifo_level, 0x80)
+    for i = 1, #data do
+        write_rawrc(rc522_fifo_data, data[i])
+    end
+    write_rawrc(rc522_com_mand, rc522_calccrc)
+    local i = 0xFF
+    while true do
+        local n = read_rawrc(0x05)
+        i = i - 1
+        if not ((i ~= 0) and not (n & 0x04)) then
+            break
+        end
+    end
+    ret_data[1] = read_rawrc(0x22)
+    ret_data[2] = read_rawrc(0x21)
+    return ret_data
+end
+
+--[[
+验证卡片密码
+@api authstate(mode, addr,key,uid )
+@number mode 模式
+@number addr 地址
+@string key 密码
+@string uid uid
+@return bool 结果
+@usage
+status = authstate(rc522_authent1b, addr,Key_B,uid )
+]]
+local function authstate(mode, addr, key, uid)
+    local buff = {}
+    buff[1] = mode
+    buff[2] = addr
+    for i = 1, 6 do
+        buff[i + 2] = key:byte(i)
+    end
+    for i = 1, 4 do
+        buff[i + 8] = uid:byte(i)
+    end
+    local status, back_data = rc522.command(rc522_authent, buff)
+    if status == true and (read_rawrc(rc522_status2) & 0x08) ~= 0 then
+        return true
+    end
+    return false
+end
+
+--[[
+写数据到M1卡一块
+@api rc522.write(addr, data)
+@number addr 块地址(0-63)M1卡总共有16个扇区(每个扇区有:3个数据块+1个控制块),共64个块
+@table data 数据
+@return bool 结果
+@usage
+rc522.write(addr, data)
+]]
+function rc522.write(addr, data)
+    local buff = {}
+    buff[1] = rc522_write
+    buff[2] = addr
+    local crc = calculate_crc(buff)
+    buff[3] = crc[1]
+    buff[4] = crc[2]
+    local status, back_data, back_bits = rc522.command(rc522_transceive, buff)
+    if status == true and back_bits == 4 and (back_data[1] & 0x0F) == 0x0A then
+        local buf_w = {}
+        for i = 0, 16 do
+            table.insert(buf_w, data[i])
+        end
+        local crc = calculate_crc(buf_w)
+        table.insert(buf_w, crc[1])
+        table.insert(buf_w, crc[2])
+        status, back_data, back_bits = rc522.command(rc522_transceive, buf_w)
+        if status == true and back_bits == 4 and (back_data[1] & 0x0F) == 0x0A then
+            return status
+        end
+    end
+    return status
+end
+
+--[[
+写数据到M1卡一块
+@api rc522.read(addr)
+@number addr 块地址(0-63)M1卡总共有16个扇区(每个扇区有:3个数据块+1个控制块),共64个块
+@return bool,string 结果,数据
+@usage
+rc522.read(addr, data)
+]]
+local function read(addr)
+    local buff = {}
+    buff[1] = rc522_read
+    buff[2] = addr
+    local crc = calculate_crc(buff)
+    buff[3] = crc[1]
+    buff[4] = crc[2]
+    local status, back_data, back_bits = rc522.command(rc522_transceive, buff)
+    if status == true and back_bits == 0x90 then
+        local data = string.char(table.unpack(back_data, 1, 16))
+        return status, data
+    end
+    return false
+end
+
+--[[
+rc522 硬件版本
+@api rc522.version()
+@return number 硬件版本
+@usage
+rc522.version()
+]]
+function rc522.version()
+    return read_rawrc(rc522_version)
+end
+
+--[[
+rc522 命令卡片进入休眠状态
+@api rc522.halt()
+@return bool 结果
+@usage
+rc522.halt()
+]]
+function rc522.halt()
+    local buff = {}
+    buff[1] = rc522_halt
+    buff[2] = 0
+    local crc = calculate_crc(buff)
+    buff[3] = crc[1]
+    buff[4] = crc[2]
+    local status = rc522.command(rc522_transceive, buff)
+    return status
+end
+
+--[[
+rc522 复位
+@api rc522.reset()
+@usage
+rc522.reset()
+]]
+function rc522.reset()
+    rc522_rst(1)
+    rc522_rst(0)
+    rc522_rst(1)
+    write_rawrc(rc522_com_mand, 0x0f)
+    write_rawrc(rc522_mode, 0x3D)
+    write_rawrc(rc522_tpreload_l, 30)
+    write_rawrc(rc522_tpreload_h, 0)
+    write_rawrc(rc522_tmode, 0x8D)
+    write_rawrc(rc522_tprescaler, 0x3E)
+    write_rawrc(rc522_tx_ayto, 0x40)
+end
+
+--[[
+开启天线
+@api rc522.antenna_on()
+@usage
+rc522.antenna_on()
+]]
+function rc522.antenna_on()
+    local uc = read_rawrc(rc522_tx_control)
+    if ((uc & 0x03) == 0) then
+        rc522.set_bit_mask(rc522_tx_control, 0x03)
+    end
+end
+
+--[[
+关闭天线
+@api rc522.antenna_on()
+@usage
+rc522.antenna_on()
+]]
+function rc522.antenna_off()
+    rc522.clear_bit_mask(rc522_tx_control, 0x03)
+end
+
+--[[
+设置rc522工作方式为ISO14443_A
+@api rc522_config_isotype()
+@usage
+rc522_config_isotype()
+]]
+local function rc522_config_isotype()
+    rc522.clear_bit_mask(rc522_status2, 0x08)
+    write_rawrc(rc522_mode, 0x3D)
+    write_rawrc(rc522_rx_sel, 0x86)
+    write_rawrc(rc522_rfcfg, 0x7F)
+    write_rawrc(rc522_tpreload_l, 30)
+    write_rawrc(rc522_tpreload_h, 0)
+    write_rawrc(rc522_tmode, 0x8D)
+    write_rawrc(rc522_tprescaler, 0x3E)
+    rc522.antenna_on()
+end
+
+--[[
+rc522 寻卡
+@api rc522.request(req_code)
+@number req_code rc522.reqidl 寻天线区内未进入休眠状态 rc522.reqall 寻天线区内全部卡
+@return bool tagtype 结果,卡类型
+@usage
+status,array_id = rc522.request(rc522.reqall)
+]]
+function rc522.request(req_code)
+    rc522.clear_bit_mask(rc522_status2, 0x08)
+    write_rawrc(rc522_bit_framing, 0x07)
+    rc522.set_bit_mask(rc522_tx_control, 0x03)
+    local tagtype
+    local status, back_data, back_bits = rc522.command(rc522_transceive, { req_code })
+    if status == true and back_bits == 0x10 then
+        tagtype = string.char(table.unpack(back_data, 1, 2))
+        return status, tagtype
+    end
+    return false
+end
+
+--[[
+选定卡片
+@api rc522.select(id)
+@number id 卡片序列号,4字节
+@return bool 结果
+@usage
+status = rc522.select(id)
+]]
+function rc522.select(id)
+    if not id then
+        return false
+    end
+    local buff = {}
+    buff[1] = rc522_anticoll1
+    buff[2] = 0x70
+    buff[7] = 0
+    for i = 1, 4 do
+        buff[i + 2] = id:byte(i)
+        buff[7] = bit.bxor(buff[7], id:byte(i))
+    end
+    local crc = calculate_crc(buff)
+    buff[8] = crc[1]
+    buff[9] = crc[2]
+    rc522.clear_bit_mask(rc522_status2, 0x08)
+    local status, back_data, back_bits = rc522.command(rc522_transceive, buff)
+    if status == true and back_bits == 0x18 then
+        return true
+    end
+    return false
+end
+
+--[[
+按照rc522操作流程写入16字节数据到块
+@api rc522.write_datablock(addr,data)
+@number addr 任意块地址.M1卡总共有16个扇区(每个扇区有:3个数据块+1个控制块),共64个块
+@table data 指向要写入的数据,必须为16个字符
+@return bool 结果
+@usage
+rc522.write_datablock(addr,data)
+]]
+function rc522.write_datablock(addr, data)
+    if #data ~= 16 then
+        return false
+    end
+    local status, array_id = rc522.request(rc522.reqall)
+    if status ~= true then
+        status, array_id = rc522.request(rc522.reqall)
+    end
+    if status == true then
+        local status, uid = rc522.anticoll(array_id)
+        if not uid then
+            return false
+        end
+        if status == true and uid then
+            rc522.select(uid)
+            status = authstate(rc522_authent1b, addr, Key_B, uid)
+            if status == true then
+                status = rc522.write(addr, data)
+                rc522.halt()
+                return status
+            end
+        end
+    end
+    return false
+end
+
+--[[
+按照rc522操作流程读取块
+@api rc522.read_datablock(addr)
+@number addr 任意块地址.M1卡总共有16个扇区(每个扇区有:3个数据块+1个控制块),共64个块
+@return bool string 结果 数据
+@usage
+    for i=0,63 do
+        local a,b = rc522.read_datablock(i)
+        if a then
+            print("read",i,b:toHex())
+        end
+    end
+]]
+function rc522.read_datablock(addr)
+    local status, array_id = rc522.request(rc522.reqall)
+    if status ~= true then
+        status, array_id = rc522.request(rc522.reqall)
+    end
+    if status == true then
+        local status, uid = rc522.anticoll(array_id)
+        if status == true and uid then
+            rc522.select(uid)
+            status = authstate(rc522_authent1b, addr, Key_B, uid)
+            if status == true then
+                local status, data = read(addr)
+                if status == true then
+                    return true, data
+                end
+                rc522.halt()
+            end
+        end
+    end
+    return false
+end
+
+--[[
+rc522 初始化
+@api rc522.init(spi_id, cs, rst)
+@number spi_id spi端口号
+@number cs      cs引脚
+@number rst     rst引脚
+@return bool 初始化结果
+@usage
+spi_rc522 = spi.setup(0,nil,0,0,8,10*1000*1000,spi.MSB,1,1)
+rc522.init(0,pin.PB04,pin.PB01)
+]]
+function rc522.init(spi_id, cs, rst)
+    rc522_spi = spi_id
+    rc522_cs = gpio.setup(cs, 0, gpio.PULLUP)
+    rc522_cs(1)
+    rc522_rst = gpio.setup(rst, 0, gpio.PULLUP)
+    rc522_rst(1)
+    rc522.reset()
+    rc522_config_isotype()
+    log.debug("rc522.version", rc522.version())
+    return true
+end
+
+function rc522.test()
+
+end
+
+return rc522

+ 152 - 0
module/Air780EPM/demo/accessory_board/AirRC522_1000/readme.md

@@ -0,0 +1,152 @@
+## 功能模块介绍:
+
+1. main.lua:主程序入口
+
+2. rc522.lua :rc522扩展库文件,在AirRC522_1000.lua功能模块中require使用
+
+3. AirRC522_1000.lua:功能演示核心文件,在main.lua中加载运行,详细逻辑请看AirRC522_1000.lua 文件
+
+## 演示功能概述:
+
+核心逻辑:
+
+1. 初始化并启用spi,如果初始化失败,退出程序
+
+2. 初始化RC522模块,如果初始化失败,退出程序
+
+3. 循环检测卡片。
+
+4. 向卡片指定块号写入数据,并读取数据验证一致性
+
+5. 读取卡片所有数据
+
+## 演示硬件环境:
+
+![](https://docs.openluat.com/accessory/AirSPINORFLASH_1000/image/780EPM.jpg)
+
+![](https://docs.openluat.com/accessory/AirSPINORFLASH_1000/image/rc522.jpg)
+
+1. 合宙 Air780EPM 核心板一块
+
+2. 合宙 AirRC522_1000 一套
+
+3. TYPE-C USB 数据线一根 ,Air780EPM 核心板和数据线的硬件接线方式为:
+* Air780EPM 核心板通过 TYPE-C USB 口供电;(USB的拨码开关off/on,拨到on)
+
+* TYPE-C USB 数据线直接插到核心板的 TYPE-C USB 座子,另外一端连接电脑 USB 口;
+4. 杜邦线 7根
+   Air780EPM 核心板与 AirRC522_1000 按以下方式接线:
+
+| Air780EPM核心板                                    | AirRC522_1000 |
+| ----------------------------------------------- | ------------- |
+| GND(任意)                                         | GND           |
+| 3V3                                             | 3.3V          |
+| 83/SPI0CS                                       | SDA           |
+| 86/SPI0CLK                                      | SCK           |
+| 85/SPI0MOSI                                     | MOSI          |
+| 84/SPI0MISO                                     | MISO          |
+| 19/GPIO22(可选任意空闲IO) | RST           |
+
+## 演示软件环境:
+
+1. Luatools 下载调试工具
+
+2. 固件版本:LuatOS-SoC_V2018_Air780EPM_1,固件地址,如有最新固件请用最新[https://docs.openluat.com/air780epm/luatos/firmware/version/](https://docs.openluat.com/air780epm/luatos/firmware/version/)
+
+3. pc 系统 win11(win10 及以上)
+
+## 演示核心步骤:
+
+1. 搭建好硬件环境
+
+2. Luatools 烧录内核固件和demo 脚本代码.
+   AirRC522_1000.lua脚本中,硬件配置参数中rst_pin是使用Air780EPM核心板的GPIO22,如果接其他IO,注意修改硬件配置参数中rst_pin对应的管脚号。
+
+3. 烧录成功后,代码会自动运行,查看打印日志,如果正常运行,会打印相关信息,spi 初始化,rc522初始化,写入数据读取数据等。
+
+4. 如下 log 显示:
+
+```
+[2025-12-01 17:35:36.521][000000000.205] I/user.main Air780EPM_rc522_DEMO 001.000.000
+[2025-12-01 17:35:36.529][000000000.226] I/user.RC522 初始化SPI接口
+[2025-12-01 17:35:36.538][000000000.227] SPI_HWInit 552:spi0 speed 2000000,1994805,154
+[2025-12-01 17:35:36.546][000000000.227] I/user.RC522 SPI初始化成功
+[2025-12-01 17:35:36.553][000000000.227] I/user.RC522 初始化RC522传感器
+[2025-12-01 17:35:36.562][000000000.230] D/user.rc522.version 178
+[2025-12-01 17:35:36.568][000000000.230] I/user.RC522 传感器初始化成功
+[2025-12-01 17:35:36.574][000000000.230] I/user.RC522 开始检测卡片
+[2025-12-01 17:35:36.579][000000000.236] I/user.RC522 检测到卡片,类型: 0400 4
+[2025-12-01 17:35:36.584][000000000.243] I/user.RC522 卡片UID: E3E2AD14 8
+[2025-12-01 17:35:36.589][000000000.335] I/user.RC522 数据写入成功,块号: 9 写入数据是: 01000000000000000000000000000000 32
+[2025-12-01 17:35:36.595][000000000.378] I/user.RC522 写入验证成功,数据是: 01000000000000000000000000000000 32
+[2025-12-01 17:35:36.601][000000000.378] I/user.RC522 开始读取卡片数据...
+[2025-12-01 17:35:36.608][000000000.429] I/user.块[0] E3E2AD14B80804006263646566676869 32
+[2025-12-01 17:35:36.613][000000000.506] I/user.块[1] 00000000000000000000000000000000 32
+[2025-12-01 17:35:36.618][000000000.576] I/user.块[2] 00000000000000000000000000000000 32
+[2025-12-01 17:35:36.623][000000000.648] I/user.块[3] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 17:35:36.630][000000000.720] I/user.块[4] 00000000000000000000000000000000 32
+[2025-12-01 17:35:36.677][000000000.793] I/user.块[5] 00000000000000000000000000000000 32
+[2025-12-01 17:35:36.682][000000000.861] I/user.块[6] 00000000000000000000000000000000 32
+[2025-12-01 17:35:36.690][000000000.927] I/user.块[7] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 17:35:36.698][000000000.992] I/user.块[8] 00000000000000000000000000000000 32
+[2025-12-01 17:35:36.702][000000001.059] I/user.块[9] 01000000000000000000000000000000 32
+[2025-12-01 17:35:36.734][000000001.124] I/user.块[10] 00000000000000000000000000000000 32
+[2025-12-01 17:35:36.796][000000001.191] I/user.块[11] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 17:35:36.874][000000001.256] I/user.块[12] 00000000000000000000000000000000 32
+[2025-12-01 17:35:36.935][000000001.322] I/user.块[13] 00000000000000000000000000000000 32
+[2025-12-01 17:35:37.154][000000001.403] I/user.块[14] 00000000000000000000000000000000 32
+[2025-12-01 17:35:37.174][000000001.473] I/user.块[15] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 17:35:37.192][000000001.585] I/user.块[16] 00000000000000000000000000000000 32
+[2025-12-01 17:35:37.264][000000001.650] I/user.块[17] 00000000000000000000000000000000 32
+[2025-12-01 17:35:37.325][000000001.718] I/user.块[18] 00000000000000000000000000000000 32
+[2025-12-01 17:35:37.387][000000001.784] I/user.块[19] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 17:35:37.463][000000001.850] I/user.块[20] 00000000000000000000000000000000 32
+[2025-12-01 17:35:37.541][000000001.926] I/user.块[21] 00000000000000000000000000000000 32
+[2025-12-01 17:35:37.603][000000001.993] I/user.块[22] 00000000000000000000000000000000 32
+[2025-12-01 17:35:37.668][000000002.060] I/user.块[23] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 17:35:37.786][000000002.138] I/user.块[24] 00000000000000000000000000000000 32
+[2025-12-01 17:35:37.793][000000002.139] D/mobile cid1, state0
+[2025-12-01 17:35:37.800][000000002.139] D/mobile bearer act 0, result 0
+[2025-12-01 17:35:37.811][000000002.140] D/mobile NETIF_LINK_ON -> IP_READY
+[2025-12-01 17:35:37.815][000000002.205] I/user.块[25] 00000000000000000000000000000000 32
+[2025-12-01 17:35:37.826][000000002.211] D/mobile TIME_SYNC 0
+[2025-12-01 17:35:37.884][000000002.276] I/user.块[26] 00000000000000000000000000000000 32
+[2025-12-01 17:35:37.961][000000002.343] I/user.块[27] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 17:35:38.022][000000002.409] I/user.块[28] 00000000000000000000000000000000 32
+[2025-12-01 17:35:38.085][000000002.476] I/user.块[29] 00000000000000000000000000000000 32
+[2025-12-01 17:35:38.146][000000002.542] I/user.块[30] 00000000000000000000000000000000 32
+[2025-12-01 17:35:38.224][000000002.608] I/user.块[31] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 17:35:38.287][000000002.672] I/user.块[32] 00000000000000000000000000000000 32
+[2025-12-01 17:35:38.350][000000002.738] I/user.块[33] 00000000000000000000000000000000 32
+[2025-12-01 17:35:38.413][000000002.804] I/user.块[34] 00000000000000000000000000000000 32
+[2025-12-01 17:35:38.490][000000002.872] I/user.块[35] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 17:35:38.552][000000002.940] I/user.块[36] 00000000000000000000000000000000 32
+[2025-12-01 17:35:38.614][000000003.005] I/user.块[37] 00000000000000000000000000000000 32
+[2025-12-01 17:35:38.676][000000003.070] I/user.块[38] 00000000000000000000000000000000 32
+[2025-12-01 17:35:38.753][000000003.136] I/user.块[39] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 17:35:38.813][000000003.203] I/user.块[40] 00000000000000000000000000000000 32
+[2025-12-01 17:35:38.877][000000003.269] I/user.块[41] 00000000000000000000000000000000 32
+[2025-12-01 17:35:38.938][000000003.334] I/user.块[42] 00000000000000000000000000000000 32
+[2025-12-01 17:35:39.015][000000003.400] I/user.块[43] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 17:35:39.079][000000003.464] I/user.块[44] 00000000000000000000000000000000 32
+[2025-12-01 17:35:39.142][000000003.529] I/user.块[45] 00000000000000000000000000000000 32
+[2025-12-01 17:35:39.204][000000003.596] I/user.块[46] 00000000000000000000000000000000 32
+[2025-12-01 17:35:39.268][000000003.662] I/user.块[47] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 17:35:39.345][000000003.727] I/user.块[48] 00000000000000000000000000000000 32
+[2025-12-01 17:35:39.409][000000003.792] I/user.块[49] 00000000000000000000000000000000 32
+[2025-12-01 17:35:39.473][000000003.857] I/user.块[50] 00000000000000000000000000000000 32
+[2025-12-01 17:35:39.535][000000003.923] I/user.块[51] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 17:35:39.597][000000003.988] I/user.块[52] 00000000000000000000000000000000 32
+[2025-12-01 17:35:39.660][000000004.054] I/user.块[53] 00000000000000000000000000000000 32
+[2025-12-01 17:35:39.723][000000004.118] I/user.块[54] 00000000000000000000000000000000 32
+[2025-12-01 17:35:39.786][000000004.183] I/user.块[55] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 17:35:39.864][000000004.250] I/user.块[56] 00000000000000000000000000000000 32
+[2025-12-01 17:35:39.927][000000004.317] I/user.块[57] 00000000000000000000000000000000 32
+[2025-12-01 17:35:39.988][000000004.382] I/user.块[58] 00000000000000000000000000000000 32
+[2025-12-01 17:35:40.052][000000004.448] I/user.块[59] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 17:35:40.129][000000004.514] I/user.块[60] 00000000000000000000000000000000 32
+[2025-12-01 17:35:40.191][000000004.580] I/user.块[61] 00000000000000000000000000000000 32
+[2025-12-01 17:35:40.254][000000004.646] I/user.块[62] 00000000000000000000000000000000 32
+[2025-12-01 17:35:40.318][000000004.714] I/user.块[63] 000000000000FF078069FFFFFFFFFFFF 32
+
+```

+ 188 - 0
module/Air8000/demo/accessory_board/AirRC522_1000/AirRC522_1000.lua

@@ -0,0 +1,188 @@
+--[[
+@module  AirRC522_1000
+@summary AirRC522_1000测试功能模块
+@version 1.0
+@date    2025.07.23
+@author  马亚丹
+@usage
+本demo演示的功能为:使用Air8000核心板通过SPIl实现对RC522的操作,演示读写数据等操作。
+以 Air8000核心板为例, 接线如下:
+Air8000          AirRC522_1000
+GND(任意)              GND
+VDD_EXT                3.3V
+GPIO12/SPI1_CS         SDA
+SPI1_SCLK              SCK
+SPI1_MOSI              MOSI
+SPI1_MISO              MISO
+GPIO16(任意空闲的io)    RST
+
+核心逻辑:
+1. 初始化并启用spi,如果初始化失败,退出程序
+2. 初始化RC522模块,如果初始化失败,退出程序
+3. 循环检测卡片。
+4. 向卡片指定块号写入数据,并读取数据验证一致性
+5. 读取卡片所有数据
+]]
+
+
+
+rc522 = require "rc522"
+
+
+-- 硬件配置参数(Air8000适配)
+local RC522_CONFIG = {
+    spi_id = 1,                 -- SPI通道
+    cs_pin = 12,                -- 片选引脚
+    rst_pin = 16,               -- 复位引脚
+    spi_baud = 2 * 1000 * 1000, -- SPI波特率
+}
+
+-- 全局变量(模块内可见)
+local spi_dev = nil -- SPI设备对象
+
+-- 1. 初始化SPI接口
+local function init_spi()
+    log.info("RC522", "初始化SPI接口")
+    -- 配置SPI参数:模式0,8位数据,高位在前
+    spi_dev = spi.setup(
+        RC522_CONFIG.spi_id,
+        RC522_CONFIG.cs_pin,
+        0,          -- 极性0
+        0,          -- 相位0
+        8,          -- 数据位
+        RC522_CONFIG.spi_baud,
+        spi.MSB,    -- 高位优先
+        spi.master, --主模式
+        spi.half    --半双工
+    )
+
+    if not spi_dev then
+        log.error("RC522", "SPI初始化失败")
+        return false
+    end
+    log.info("RC522", "SPI初始化成功")
+    return true
+end
+
+-- 2. 初始化RC522模块
+local function init_rc522()
+    log.info("RC522", "初始化RC522传感器")
+    -- 初始化RC522硬件(SPI ID、CS引脚、RST引脚)
+    local init_ok = rc522.init(
+        RC522_CONFIG.spi_id,
+        RC522_CONFIG.cs_pin,
+        RC522_CONFIG.rst_pin
+    )
+
+    if not init_ok then
+        log.error("RC522", "传感器初始化失败")
+        return false
+    end
+    log.info("RC522", "传感器初始化成功")
+    return true
+end
+
+-- 3. 写数据
+local function write_ic_card(block, data)
+    -- 步骤1:验证数据长度
+    if #data > 16 then
+        log.error("RC522", "数据过长(最大16字节)")
+        return false
+    end
+
+    -- 步骤2:写入数据块
+    local write_ok = rc522.write_datablock(block, data)
+    if not write_ok then
+        log.error("RC522", "数据写入失败,块号:", block)
+        return false
+    end
+
+    log.info("RC522", "数据写入成功,块号:", block, "写入数据是:", string.char(table.unpack(data)):toHex())
+    return true
+end
+
+-- 4. 检测并读取IC卡数据
+local function read_ic_card()
+    -- 检测是否有卡片靠近
+    local status, array_id = rc522.request(rc522.reqall)
+    if not status then
+        log.info("RC522", "未检测到卡片")
+        return false
+    end
+    log.info("RC522", "检测到卡片,类型:", array_id:toHex())
+
+    -- 防冲突检测,获取卡片唯一ID
+    local status, card_uid = rc522.anticoll(array_id)
+    if not status then
+        log.error("RC522", "防冲突检测失败")
+        return false
+    end
+    log.info("RC522", "卡片UID:", card_uid:toHex())
+
+    -- -- 选择卡片,激活卡片进行后续操作
+    local select_ok = rc522.select(card_uid)
+    if not select_ok then
+        log.error("RC522", "卡片选择失败")
+        return false
+    end
+
+    -- 写数据测试
+    local TEST_BLOCK = 9
+    -- 待写入的数据
+    local TEST_DATA = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
+    if write_ic_card(TEST_BLOCK, TEST_DATA) then
+        -- 读数据验证
+        local read_ok, read_data = rc522.read_datablock(TEST_BLOCK)
+
+        if read_ok and read_data == string.char(table.unpack(TEST_DATA)) then
+            log.info("RC522", "写入验证成功,数据是:", read_data:toHex())
+        else
+            log.warn("RC522", "写入验证失败,实际读取的数据是:", read_data:toHex())
+        end
+    end
+
+    -- 读取卡片数据块(0-63块)
+    log.info("RC522", "开始读取卡片数据...")
+    for block = 0, 63 do
+        local read_ok, data = rc522.read_datablock(block)
+        if read_ok and data then
+            log.info(string.format("块[%d]", block), data:toHex())
+        else
+            log.warn(string.format("块[%d]读取失败", block))
+        end
+        --每读取完一个块等待20ms(时间可按需修改)
+        sys.wait(20)
+    end
+
+    -- 停止卡片操作
+    rc522.halt()
+    return true
+end
+-- 5. 关闭SPI设备,成功返回0
+local function spi_close_func()
+    log.info("关闭spi", spi.close(RC522_CONFIG.spi_id))
+end
+
+-- 6. 主测试任务
+local function rc522_main_test()
+    -- 初始化硬件
+    if not init_spi() then
+        spi_close_func()
+        return
+    end
+    if not init_rc522() then
+        spi_close_func()
+        return
+    end
+
+    -- 循环检测卡片
+    log.info("RC522", "开始检测卡片")
+    while true do
+        read_ic_card()
+        -- 循环检测间隔:2s
+        sys.wait(2000)
+    end
+end
+
+-- 启动主任务
+sys.taskInit(rc522_main_test)

+ 73 - 0
module/Air8000/demo/accessory_board/AirRC522_1000/main.lua

@@ -0,0 +1,73 @@
+--[[
+@module  main
+@summary LuatOS用户应用脚本文件入口,总体调度应用逻辑 
+@version 001.000.000
+@date    2025.9.05
+@author  马亚丹
+@usage
+本demo是使用Air8000核心板挂载合宙AirRC522_1000配件板,来演示rc522的功能使用方法,代码脚本如下:
+1.rc522.lua :rc522扩展库文件,在AirRC522_1000.lua功能模块中require使用  
+2.AirRC522_1000.lua:功能演示核心文件,在main.lua中加载运行,详细逻辑请看AirRC522_1000.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_rc522_DEMO"
+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)
+
+-- 加载AirRC522_1000功能模块
+require "AirRC522_1000"
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后不要加任何语句!!!!!因为添加的任何语句都不会被执行

+ 646 - 0
module/Air8000/demo/accessory_board/AirRC522_1000/rc522.lua

@@ -0,0 +1,646 @@
+--[[
+@module rc522
+@summary rc522 非接触式读写卡驱动
+@version 1.0
+@date    2022.06.14
+@author  Dozingfiretruck
+@usage
+--注意:因使用了sys.wait()所有api需要在task中使用
+-- 用法实例
+local rc522 = require "rc522"
+local function rc522_test()
+    spi_rc522 = spi.setup(0,nil,0,0,8,10*1000*1000,spi.MSB, spi.master,spi.half)
+    rc522.init(0,12,16)
+    wdata = {0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}
+    while 1 do
+        rc522.write_datablock(8,wdata)
+        for i=0,63 do
+            local a,b = rc522.read_datablock(i)
+            if a then
+                log.info("read",i,b:toHex())
+            end
+        end
+        sys.wait(500)
+    end
+end)
+sys.taskInit(rc522_test)
+]]
+
+
+local rc522 = {}
+
+local rc522_spi, rc522_rst, rc522_irq, rc522_cs
+
+---器件所用地址
+local rc522_idle             = 0x00 --取消当前命令
+local rc522_authent          = 0x0E --验证密钥
+local rc522_receive          = 0x08 --接收数据
+local rc522_transmit         = 0x04 --发送数据
+local rc522_transceive       = 0x0C --发送并接收数据
+local rc522_resetphase       = 0x0F --复位
+local rc522_calccrc          = 0x03 --CRC计算
+
+--Mifare_One卡片命令字
+rc522.reqidl                 = 0x26 --寻天线区内未进入休眠状态
+rc522.reqall                 = 0x52 --寻天线区内全部卡
+local rc522_anticoll1        = 0x93 --防冲撞
+local rc522_anticoll2        = 0x95 --防冲撞
+local rc522_authent1a        = 0x60 --验证A密钥
+local rc522_authent1b        = 0x61 --验证B密钥
+local rc522_read             = 0x30 --读块
+local rc522_write            = 0xA0 --写块
+local rc522_decrement        = 0xC0 --扣款
+local rc522_increment        = 0xC1 --充值
+local rc522_restore          = 0xC2 --调块数据到缓冲区
+local rc522_transfer         = 0xB0 --保存缓冲区中数据
+local rc522_halt             = 0x50 --休眠
+
+--MF522寄存器定义
+-- PAGE 0
+local rc522_rfu00            = 0x00 --保留为将来之用
+local rc522_com_mand         = 0x01 --启动和停止命令的执行
+local rc522_com_ie           = 0x02 --中断请求传递的使能和禁能控制位
+local rc522_divl_en          = 0x03 --中断请求传递的使能和禁能控制位
+local rc522_com_irq          = 0x04 --包含中断请求标志
+local rc522_div_irq          = 0x05 --包含中断请求标志
+local rc522_error            = 0x06 --错误标志,指示执行的上个命令的错误状态
+local rc522_status1          = 0x07 --包含通信的状态标志
+local rc522_status2          = 0x08 --包含接收器和发送器的状态标志
+local rc522_fifo_data        = 0x09 --64 字节 FIFO 缓冲区的输入和输出
+local rc522_fifo_level       = 0x0A --指示 FIFO 中存储的字节数
+local rc522_water_level      = 0x0B --定义 FIFO 下溢和上溢报警的 FIFO 深度
+local rc522_control          = 0x0C --不同的控制寄存器
+local rc522_bit_framing      = 0x0D --面向位的帧的调节
+local rc522_coll             = 0x0E --RF 接口上检测到的第一个位冲突的位的位置
+local rc522_rfu0f            = 0x0F --保留为将来之用
+-- PAGE 1
+local RFU10                  = 0x10 --保留为将来之用
+local rc522_mode             = 0x11 --定义发送和接收的常用模式
+local rc522_tx_mode          = 0x12 --定义发送过程的数据传输速率
+local rc522_rx_mode          = 0x13 --定义接收过程中的数据传输速率
+local rc522_tx_control       = 0x14 --控制天线驱动器管脚 TX1 和 TX2 的逻辑特性
+local rc522_tx_ayto          = 0x15 --控制天线驱动器的设置
+local rc522_tx_sel           = 0x16 --选择天线驱动器的内部源
+local rc522_rx_sel           = 0x17 --选择内部的接收器设置
+local rc522_rx_threshold     = 0x18 --选择位译码器的阈值
+local rc522_demod            = 0x19 --定义解调器的设置
+local rc522_rfu1a            = 0x1A --保留为将来之用
+local rc522_rfu1b            = 0x1B --保留为将来之用
+local rc522_mifare           = 0x1C --控制 ISO 14443/MIFARE 模式中 106kbit/s 的通信
+local rc522_rfu1d            = 0x1D --保留为将来之用
+local rc522_rfu1e            = 0x1E --保留为将来之用
+local rc522_serial_speed     = 0x1F --选择串行 UART 接口的速率
+-- PAGE 2
+local rc522_rfu20            = 0x20 --保留为将来之用
+local rc522_crcresult_m      = 0x21 --显示 CRC 计算的实际 MSB 值
+local rc522_crcresult_l      = 0x22 --显示 CRC 计算的实际 LSB 值
+local rc522_rfu23            = 0x23 --保留为将来之用
+local rc522_mod_width        = 0x24 --控制 ModWidth 的设置
+local rc522_rfu25            = 0x25 --保留为将来之用
+local rc522_rfcfg            = 0x26 --配置接收器增益
+local rc522_gsn              = 0x27 --选择天线驱动器管脚 TX1 和 TX2 的调制电导
+local rc522_cwgscfg          = 0x28 --选择天线驱动器管脚 TX1 和 TX2 的调制电导
+local rc522_modgscfg         = 0x29 --选择天线驱动器管脚 TX1 和 TX2 的调制电导
+local rc522_tmode            = 0x2A --定义内部定时器的设置
+local rc522_tprescaler       = 0x2B --定义内部定时器的设置
+local rc522_tpreload_h       = 0x2C --描述 16 位长的定时器重装值
+local rc522_tpreload_l       = 0x2D --描述 16 位长的定时器重装值
+local rc522_tcounter_value_h = 0x2E --描述 16 位长的定时器重装值
+local rc522_tcounter_value_l = 0x2F --描述 16 位长的定时器重装值
+-- PAGE 3
+local rc522_rfu30            = 0x30 --保留为将来之用
+local rc522_testsel1         = 0x31 --常用测试信号的配置
+local rc522_testsel2         = 0x32 --常用测试信号的配置和 PRBS 控制
+local rc522_testpin_en       = 0x33 --D1-D7 输出驱动器的使能管脚(注:仅用于串行接口)
+local rc522_testpin_value    = 0x34 --定义 D1-D7 用作 I/O 总线时的值
+local rc522_testbus          = 0x35 --显示内部测试总线的状态
+local rc522_autotest         = 0x36 --控制数字自测试
+local rc522_version          = 0x37 --显示版本
+local rc522_analogtest       = 0x38 --控制管脚 AUX1 和 AUX2
+local rc522_testadc1         = 0x39 --定义 TestDAC1 的测试值
+local rc522_testadc2         = 0x3A --定义 TestDAC2 的测试值
+local rc522_testadc          = 0x3B --显示 ADC I 和 Q 通道的实际值
+local rc522_rfu3c            = 0x3C --保留用于产品测试
+local rc522_rfu3d            = 0x3D --保留用于产品测试
+local rc522_rfu3e            = 0x3E --保留用于产品测试
+local rc522_rfu3f            = 0x3F --保留用于产品测试
+
+local Key_A                  = string.char(0xff, 0xff, 0xff, 0xff, 0xff, 0xff)
+local Key_B                  = string.char(0xff, 0xff, 0xff, 0xff, 0xff, 0xff)
+
+--[[
+写rc522寄存器
+@api rc522.set_bit_mask(address, value)
+@number address 地址
+@number value    值
+@usage
+write_rawrc(rc522_bit_framing,0x07)
+]]
+local function write_rawrc(address, value)
+    rc522_cs(0)
+    local data = string.char((address << 1) & 0x7E) .. string.char(value)
+    spi.send(rc522_spi, data)
+    -- rc522_spi:send(data)
+    rc522_cs(1)
+end
+
+--[[
+读rc522寄存器
+@api rc522.read_rawrc(address)
+@number address 地址
+@return number 寄存器值
+@usage
+local n = read_rawrc(rc522_com_irq)
+]]
+local function read_rawrc(address)
+    rc522_cs(0)
+    local data = string.char(((address << 1) & 0x7E)|0x80)
+    spi.send(rc522_spi, data)
+    local val = spi.recv(rc522_spi, 1)
+    -- rc522_spi:send(data)
+    -- local val = rc522_spi:recv(1)
+    rc522_cs(1)
+    return string.byte(val)
+end
+
+--[[
+对rc522寄存器置位
+@api rc522.set_bit_mask(address, mask)
+@number address 地址
+@number mask    置位值
+@usage
+rc522.set_bit_mask (rc522_fifo_level, 0x80)	
+]]
+function rc522.set_bit_mask(address, mask)
+    local current = read_rawrc(address)
+    write_rawrc(address, bit.bor(current, mask))
+end
+
+--[[
+对rc522寄存器清位
+@api rc522.clear_bit_mask(address, mask)
+@number address 地址
+@number mask    清位值
+@usage
+rc522.clear_bit_mask(rc522_com_irq, 0x80 )
+]]
+function rc522.clear_bit_mask(address, mask)
+    local current = read_rawrc(address)
+    write_rawrc(address, bit.band(current, bit.bnot(mask)))
+end
+
+--[[
+命令通讯
+@api rc522.command(command,data)
+@number command
+@number data
+@return status data len 结果,返回数据,收到的位长度
+@usage
+rc522.version()
+]]
+function rc522.command(command, data)
+    local out_data = {}
+    local len      = 0
+    local status   = false
+    local Irq_en   = 0x00
+    local waitfor  = 0x00
+    local last_bits, n, l
+    if command == rc522_authent then
+        Irq_en  = 0x12
+        waitfor = 0x10
+    elseif command == rc522_transceive then
+        Irq_en  = 0x77
+        waitfor = 0x30
+    end
+    write_rawrc(0x02, bit.bor(Irq_en, 0x80))
+    rc522.clear_bit_mask(rc522_com_irq, 0x80)
+    write_rawrc(rc522_com_mand, rc522_idle)
+    rc522.set_bit_mask(rc522_fifo_level, 0x80)
+    for i = 1, #data do
+        write_rawrc(rc522_fifo_data, data[i])
+    end
+    write_rawrc(rc522_com_mand, command)
+    if (command == rc522_transceive) then
+        rc522.set_bit_mask(rc522_bit_framing, 0x80)
+    end
+    l = 12
+    while true do
+        sys.wait(1)
+        n = read_rawrc(rc522_com_irq)
+        l = l - 1
+        if not ((l ~= 0) and (bit.band(n, 0x01) == 0) and (bit.band(n, waitfor) == 0)) then
+            break
+        end
+    end
+    rc522.clear_bit_mask(rc522_bit_framing, 0x80)
+    if (l ~= 0) then
+        if bit.band(read_rawrc(rc522_error), 0x1B) == 0x00 then
+            status = true
+            if bit.band(n, Irq_en, 0x01) ~= 0 then
+                status = false
+            end
+            if (command == rc522_transceive) then
+                n = read_rawrc(rc522_fifo_level)
+                last_bits = bit.band(read_rawrc(rc522_control), 0x07)
+                if last_bits ~= 0 then
+                    len = (n - 1) * 8 + last_bits
+                else
+                    len = n * 8
+                end
+                if n == 0 then
+                    n = 1
+                end
+                for i = 1, n do
+                    out_data[i] = read_rawrc(rc522_fifo_data)
+                end
+            end
+        end
+    else
+        status = false
+    end
+    rc522.set_bit_mask(rc522_control, 0x80)
+    write_rawrc(rc522_com_mand, rc522_idle)
+    return status, out_data, len
+end
+
+--[[
+防冲撞
+@api rc522.anticoll(id)
+@string id 卡片序列号,4字节
+@return status uid 结果,uid
+@usage
+local status,uid = rc522.anticoll(array_id)
+]]
+function rc522.anticoll(id)
+    local check = 0
+    local uid
+    rc522.clear_bit_mask(rc522_status2, 0x08)
+    write_rawrc(rc522_bit_framing, 0x00)
+    rc522.clear_bit_mask(rc522_coll, 0x80)
+    local status, back_data = rc522.command(rc522_transceive, { 0x93, 0x20 })
+    if back_data and #back_data >= 5 then
+        for i = 1, 4 do
+            check = bit.bxor(check, back_data[i])
+        end
+        if check ~= back_data[5] then
+            status = false
+        end
+        uid = string.char(table.unpack(back_data, 1, 4))
+    end
+    rc522.clear_bit_mask(rc522_coll, 0x80)
+    return status, uid
+end
+
+--[[
+crc计算
+@api calculate_crc(data)
+@table data 数据
+@return table crc值
+@usage
+local crc = calculate_crc(buff)
+]]
+local function calculate_crc(data)
+    local ret_data = {}
+    rc522.clear_bit_mask(rc522_div_irq, 0x04)
+    write_rawrc(rc522_com_mand, rc522_idle)
+    rc522.set_bit_mask(rc522_fifo_level, 0x80)
+    for i = 1, #data do
+        write_rawrc(rc522_fifo_data, data[i])
+    end
+    write_rawrc(rc522_com_mand, rc522_calccrc)
+    local i = 0xFF
+    while true do
+        local n = read_rawrc(0x05)
+        i = i - 1
+        if not ((i ~= 0) and not (n & 0x04)) then
+            break
+        end
+    end
+    ret_data[1] = read_rawrc(0x22)
+    ret_data[2] = read_rawrc(0x21)
+    return ret_data
+end
+
+--[[
+验证卡片密码
+@api authstate(mode, addr,key,uid )
+@number mode 模式
+@number addr 地址
+@string key 密码
+@string uid uid
+@return bool 结果
+@usage
+status = authstate(rc522_authent1b, addr,Key_B,uid )
+]]
+local function authstate(mode, addr, key, uid)
+    local buff = {}
+    buff[1] = mode
+    buff[2] = addr
+    for i = 1, 6 do
+        buff[i + 2] = key:byte(i)
+    end
+    for i = 1, 4 do
+        buff[i + 8] = uid:byte(i)
+    end
+    local status, back_data = rc522.command(rc522_authent, buff)
+    if status == true and (read_rawrc(rc522_status2) & 0x08) ~= 0 then
+        return true
+    end
+    return false
+end
+
+--[[
+写数据到M1卡一块
+@api rc522.write(addr, data)
+@number addr 块地址(0-63)M1卡总共有16个扇区(每个扇区有:3个数据块+1个控制块),共64个块
+@table data 数据
+@return bool 结果
+@usage
+rc522.write(addr, data)
+]]
+function rc522.write(addr, data)
+    local buff = {}
+    buff[1] = rc522_write
+    buff[2] = addr
+    local crc = calculate_crc(buff)
+    buff[3] = crc[1]
+    buff[4] = crc[2]
+    local status, back_data, back_bits = rc522.command(rc522_transceive, buff)
+    if status == true and back_bits == 4 and (back_data[1] & 0x0F) == 0x0A then
+        local buf_w = {}
+        for i = 0, 16 do
+            table.insert(buf_w, data[i])
+        end
+        local crc = calculate_crc(buf_w)
+        table.insert(buf_w, crc[1])
+        table.insert(buf_w, crc[2])
+        status, back_data, back_bits = rc522.command(rc522_transceive, buf_w)
+        if status == true and back_bits == 4 and (back_data[1] & 0x0F) == 0x0A then
+            return status
+        end
+    end
+    return status
+end
+
+--[[
+写数据到M1卡一块
+@api rc522.read(addr)
+@number addr 块地址(0-63)M1卡总共有16个扇区(每个扇区有:3个数据块+1个控制块),共64个块
+@return bool,string 结果,数据
+@usage
+rc522.read(addr, data)
+]]
+local function read(addr)
+    local buff = {}
+    buff[1] = rc522_read
+    buff[2] = addr
+    local crc = calculate_crc(buff)
+    buff[3] = crc[1]
+    buff[4] = crc[2]
+    local status, back_data, back_bits = rc522.command(rc522_transceive, buff)
+    if status == true and back_bits == 0x90 then
+        local data = string.char(table.unpack(back_data, 1, 16))
+        return status, data
+    end
+    return false
+end
+
+--[[
+rc522 硬件版本
+@api rc522.version()
+@return number 硬件版本
+@usage
+rc522.version()
+]]
+function rc522.version()
+    return read_rawrc(rc522_version)
+end
+
+--[[
+rc522 命令卡片进入休眠状态
+@api rc522.halt()
+@return bool 结果
+@usage
+rc522.halt()
+]]
+function rc522.halt()
+    local buff = {}
+    buff[1] = rc522_halt
+    buff[2] = 0
+    local crc = calculate_crc(buff)
+    buff[3] = crc[1]
+    buff[4] = crc[2]
+    local status = rc522.command(rc522_transceive, buff)
+    return status
+end
+
+--[[
+rc522 复位
+@api rc522.reset()
+@usage
+rc522.reset()
+]]
+function rc522.reset()
+    rc522_rst(1)
+    rc522_rst(0)
+    rc522_rst(1)
+    write_rawrc(rc522_com_mand, 0x0f)
+    write_rawrc(rc522_mode, 0x3D)
+    write_rawrc(rc522_tpreload_l, 30)
+    write_rawrc(rc522_tpreload_h, 0)
+    write_rawrc(rc522_tmode, 0x8D)
+    write_rawrc(rc522_tprescaler, 0x3E)
+    write_rawrc(rc522_tx_ayto, 0x40)
+end
+
+--[[
+开启天线
+@api rc522.antenna_on()
+@usage
+rc522.antenna_on()
+]]
+function rc522.antenna_on()
+    local uc = read_rawrc(rc522_tx_control)
+    if ((uc & 0x03) == 0) then
+        rc522.set_bit_mask(rc522_tx_control, 0x03)
+    end
+end
+
+--[[
+关闭天线
+@api rc522.antenna_on()
+@usage
+rc522.antenna_on()
+]]
+function rc522.antenna_off()
+    rc522.clear_bit_mask(rc522_tx_control, 0x03)
+end
+
+--[[
+设置rc522工作方式为ISO14443_A
+@api rc522_config_isotype()
+@usage
+rc522_config_isotype()
+]]
+local function rc522_config_isotype()
+    rc522.clear_bit_mask(rc522_status2, 0x08)
+    write_rawrc(rc522_mode, 0x3D)
+    write_rawrc(rc522_rx_sel, 0x86)
+    write_rawrc(rc522_rfcfg, 0x7F)
+    write_rawrc(rc522_tpreload_l, 30)
+    write_rawrc(rc522_tpreload_h, 0)
+    write_rawrc(rc522_tmode, 0x8D)
+    write_rawrc(rc522_tprescaler, 0x3E)
+    rc522.antenna_on()
+end
+
+--[[
+rc522 寻卡
+@api rc522.request(req_code)
+@number req_code rc522.reqidl 寻天线区内未进入休眠状态 rc522.reqall 寻天线区内全部卡
+@return bool tagtype 结果,卡类型
+@usage
+status,array_id = rc522.request(rc522.reqall)
+]]
+function rc522.request(req_code)
+    rc522.clear_bit_mask(rc522_status2, 0x08)
+    write_rawrc(rc522_bit_framing, 0x07)
+    rc522.set_bit_mask(rc522_tx_control, 0x03)
+    local tagtype
+    local status, back_data, back_bits = rc522.command(rc522_transceive, { req_code })
+    if status == true and back_bits == 0x10 then
+        tagtype = string.char(table.unpack(back_data, 1, 2))
+        return status, tagtype
+    end
+    return false
+end
+
+--[[
+选定卡片
+@api rc522.select(id)
+@number id 卡片序列号,4字节
+@return bool 结果
+@usage
+status = rc522.select(id)
+]]
+function rc522.select(id)
+    if not id then
+        return false
+    end
+    local buff = {}
+    buff[1] = rc522_anticoll1
+    buff[2] = 0x70
+    buff[7] = 0
+    for i = 1, 4 do
+        buff[i + 2] = id:byte(i)
+        buff[7] = bit.bxor(buff[7], id:byte(i))
+    end
+    local crc = calculate_crc(buff)
+    buff[8] = crc[1]
+    buff[9] = crc[2]
+    rc522.clear_bit_mask(rc522_status2, 0x08)
+    local status, back_data, back_bits = rc522.command(rc522_transceive, buff)
+    if status == true and back_bits == 0x18 then
+        return true
+    end
+    return false
+end
+
+--[[
+按照rc522操作流程写入16字节数据到块
+@api rc522.write_datablock(addr,data)
+@number addr 任意块地址.M1卡总共有16个扇区(每个扇区有:3个数据块+1个控制块),共64个块
+@table data 指向要写入的数据,必须为16个字符
+@return bool 结果
+@usage
+rc522.write_datablock(addr,data)
+]]
+function rc522.write_datablock(addr, data)
+    if #data ~= 16 then
+        return false
+    end
+    local status, array_id = rc522.request(rc522.reqall)
+    if status ~= true then
+        status, array_id = rc522.request(rc522.reqall)
+    end
+    if status == true then
+        local status, uid = rc522.anticoll(array_id)
+        if not uid then
+            return false
+        end
+        if status == true and uid then
+            rc522.select(uid)
+            status = authstate(rc522_authent1b, addr, Key_B, uid)
+            if status == true then
+                status = rc522.write(addr, data)
+                rc522.halt()
+                return status
+            end
+        end
+    end
+    return false
+end
+
+--[[
+按照rc522操作流程读取块
+@api rc522.read_datablock(addr)
+@number addr 任意块地址.M1卡总共有16个扇区(每个扇区有:3个数据块+1个控制块),共64个块
+@return bool string 结果 数据
+@usage
+    for i=0,63 do
+        local a,b = rc522.read_datablock(i)
+        if a then
+            print("read",i,b:toHex())
+        end
+    end
+]]
+function rc522.read_datablock(addr)
+    local status, array_id = rc522.request(rc522.reqall)
+    if status ~= true then
+        status, array_id = rc522.request(rc522.reqall)
+    end
+    if status == true then
+        local status, uid = rc522.anticoll(array_id)
+        if status == true and uid then
+            rc522.select(uid)
+            status = authstate(rc522_authent1b, addr, Key_B, uid)
+            if status == true then
+                local status, data = read(addr)
+                if status == true then
+                    return true, data
+                end
+                rc522.halt()
+            end
+        end
+    end
+    return false
+end
+
+--[[
+rc522 初始化
+@api rc522.init(spi_id, cs, rst)
+@number spi_id spi端口号
+@number cs      cs引脚
+@number rst     rst引脚
+@return bool 初始化结果
+@usage
+spi_rc522 = spi.setup(0,nil,0,0,8,10*1000*1000,spi.MSB,1,1)
+rc522.init(0,pin.PB04,pin.PB01)
+]]
+function rc522.init(spi_id, cs, rst)
+    rc522_spi = spi_id
+    rc522_cs = gpio.setup(cs, 0, gpio.PULLUP)
+    rc522_cs(1)
+    rc522_rst = gpio.setup(rst, 0, gpio.PULLUP)
+    rc522_rst(1)
+    rc522.reset()
+    rc522_config_isotype()
+    log.debug("rc522.version", rc522.version())
+    return true
+end
+
+function rc522.test()
+
+end
+
+return rc522

+ 153 - 0
module/Air8000/demo/accessory_board/AirRC522_1000/readme.md

@@ -0,0 +1,153 @@
+## 功能模块介绍:
+
+1. main.lua:主程序入口
+
+2. rc522.lua :rc522扩展库文件,在AirRC522_1000.lua功能模块中require使用
+
+3. AirRC522_1000.lua:功能演示核心文件,在main.lua中加载运行,详细逻辑请看AirRC522_1000.lua 文件
+
+## 演示功能概述:
+
+核心逻辑:
+
+1. 初始化并启用spi,如果初始化失败,退出程序
+
+2. 初始化RC522模块,如果初始化失败,退出程序
+
+3. 循环检测卡片。
+
+4. 向卡片指定块号写入数据,并读取数据验证一致性
+
+5. 读取卡片所有数据
+
+## 演示硬件环境:
+
+![](https://docs.openluat.com/accessory/AirSPINORFLASH_1000/image/spi1.jpg)
+
+![](https://docs.openluat.com/accessory/AirSPINORFLASH_1000/image/rc522.jpg)
+
+1. 合宙 Air8000 核心板一块
+
+2. 合宙 AirRC522_1000 一套
+
+3. TYPE-C USB 数据线一根 ,Air8000 核心板和数据线的硬件接线方式为:
+* Air8000核心板通过 TYPE-C USB 口供电(核心板背面的开关拨到 USB ON 一端,正面开关拨到 供电 一端)
+
+* TYPE-C USB 数据线直接插到核心板的 TYPE-C USB 座子,另外一端连接电脑 USB 口;
+4. 杜邦线 7根
+5. 
+   Air8000 核心板与 AirRC522_1000 按以下方式接线:
+
+| Air8000核心板       | AirRC522_1000 |
+| ---------------- | ------------- |
+| GND(任意)          | GND           |
+| VDD_EXT          | 3.3V          |
+| GPIO12/SPI1_CS   | SDA           |
+| SPI1_SCLK        | SCK           |
+| SPI1_MOSI        | MOSI          |
+| SPI1_MISO        | MISO          |
+| GPIO16(可选任意空闲IO) | RST           |
+
+## 演示软件环境:
+
+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 脚本代码。
+   AirRC522_1000.lua脚本中,硬件配置参数中rst_pin是使用Air8000核心板的GPIO16,如果接其他IO,注意修改硬件配置参数中rst_pin对应的管脚号。
+
+3. 烧录成功后,代码会自动运行,查看打印日志,如果正常运行,会打印相关信息,spi 初始化,rc522初始化,写入数据读取数据等。
+
+4. 如下 log 显示:
+
+```
+[2025-12-01 15:03:45.780][000000000.374] I/user.main Air8000_rc522_DEMO 001.000.000
+[2025-12-01 15:03:45.790][000000000.412] I/user.RC522 初始化SPI接口
+[2025-12-01 15:03:45.799][000000000.413] SPI_HWInit 552:spi1 speed 2000000,1994805,154
+[2025-12-01 15:03:45.805][000000000.413] I/user.RC522 SPI初始化成功
+[2025-12-01 15:03:45.815][000000000.413] I/user.RC522 初始化RC522传感器
+[2025-12-01 15:03:45.820][000000000.416] D/user.rc522.version 146
+[2025-12-01 15:03:45.824][000000000.417] I/user.RC522 传感器初始化成功
+[2025-12-01 15:03:45.831][000000000.417] I/user.RC522 开始检测卡片
+[2025-12-01 15:03:45.835][000000000.424] I/user.RC522 检测到卡片,类型: 0400 4
+[2025-12-01 15:03:45.839][000000000.430] I/user.RC522 卡片UID: E3E2AD14 8
+[2025-12-01 15:03:45.845][000000000.526] I/user.RC522 数据写入成功,块号: 9 写入数据是: 01000000000000000000000000000000 32
+[2025-12-01 15:03:45.904][000000000.580] I/user.RC522 写入验证成功,数据是: 01000000000000000000000000000000 32
+[2025-12-01 15:03:45.913][000000000.581] I/user.RC522 开始读取卡片数据...
+[2025-12-01 15:03:45.919][000000000.639] I/user.块[0] E3E2AD14B80804006263646566676869 32
+[2025-12-01 15:03:45.923][000000000.716] I/user.块[1] 00000000000000000000000000000000 32
+[2025-12-01 15:03:46.031][000000000.814] I/user.块[2] 00000000000000000000000000000000 32
+[2025-12-01 15:03:46.040][000000000.942] I/user.块[3] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 15:03:46.045][000000001.011] I/user.块[4] 00000000000000000000000000000000 32
+[2025-12-01 15:03:46.056][000000001.084] I/user.块[5] 00000000000000000000000000000000 32
+[2025-12-01 15:03:46.130][000000001.152] I/user.块[6] 00000000000000000000000000000000 32
+[2025-12-01 15:03:46.195][000000001.219] I/user.块[7] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 15:03:46.273][000000001.289] I/user.块[8] 00000000000000000000000000000000 32
+[2025-12-01 15:03:46.337][000000001.354] I/user.块[9] 01000000000000000000000000000000 32
+[2025-12-01 15:03:46.401][000000001.419] I/user.块[10] 00000000000000000000000000000000 32
+[2025-12-01 15:03:46.462][000000001.485] I/user.块[11] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 15:03:46.524][000000001.550] I/user.块[12] 00000000000000000000000000000000 32
+[2025-12-01 15:03:46.588][000000001.616] I/user.块[13] 00000000000000000000000000000000 32
+[2025-12-01 15:03:46.665][000000001.681] I/user.块[14] 00000000000000000000000000000000 32
+[2025-12-01 15:03:46.728][000000001.747] I/user.块[15] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 15:03:46.790][000000001.812] I/user.块[16] 00000000000000000000000000000000 32
+[2025-12-01 15:03:46.854][000000001.877] I/user.块[17] 00000000000000000000000000000000 32
+[2025-12-01 15:03:46.916][000000001.943] I/user.块[18] 00000000000000000000000000000000 32
+[2025-12-01 15:03:46.994][000000002.009] I/user.块[19] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 15:03:47.056][000000002.074] I/user.块[20] 00000000000000000000000000000000 32
+[2025-12-01 15:03:47.118][000000002.139] I/user.块[21] 00000000000000000000000000000000 32
+[2025-12-01 15:03:47.180][000000002.204] I/user.块[22] 00000000000000000000000000000000 32
+[2025-12-01 15:03:47.241][000000002.269] I/user.块[23] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 15:03:47.319][000000002.335] I/user.块[24] 00000000000000000000000000000000 32
+[2025-12-01 15:03:47.382][000000002.400] I/user.块[25] 00000000000000000000000000000000 32
+[2025-12-01 15:03:47.443][000000002.466] I/user.块[26] 00000000000000000000000000000000 32
+[2025-12-01 15:03:47.505][000000002.532] I/user.块[27] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 15:03:47.607][000000002.589] D/mobile cid1, state0
+[2025-12-01 15:03:47.611][000000002.591] D/mobile bearer act 0, result 0
+[2025-12-01 15:03:47.615][000000002.592] D/mobile NETIF_LINK_ON -> IP_READY
+[2025-12-01 15:03:47.619][000000002.628] I/user.块[28] 00000000000000000000000000000000 32
+[2025-12-01 15:03:47.623][000000002.632] D/mobile TIME_SYNC 0
+[2025-12-01 15:03:47.676][000000002.694] I/user.块[29] 00000000000000000000000000000000 32
+[2025-12-01 15:03:47.738][000000002.759] I/user.块[30] 00000000000000000000000000000000 32
+[2025-12-01 15:03:47.801][000000002.825] I/user.块[31] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 15:03:47.863][000000002.890] I/user.块[32] 00000000000000000000000000000000 32
+[2025-12-01 15:03:47.926][000000002.955] I/user.块[33] 00000000000000000000000000000000 32
+[2025-12-01 15:03:48.004][000000003.021] I/user.块[34] 00000000000000000000000000000000 32
+[2025-12-01 15:03:48.066][000000003.087] I/user.块[35] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 15:03:48.128][000000003.152] I/user.块[36] 00000000000000000000000000000000 32
+[2025-12-01 15:03:48.190][000000003.219] I/user.块[37] 00000000000000000000000000000000 32
+[2025-12-01 15:03:48.270][000000003.288] I/user.块[38] 00000000000000000000000000000000 32
+[2025-12-01 15:03:48.331][000000003.353] I/user.块[39] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 15:03:48.393][000000003.418] I/user.块[40] 00000000000000000000000000000000 32
+[2025-12-01 15:03:48.455][000000003.483] I/user.块[41] 00000000000000000000000000000000 32
+[2025-12-01 15:03:48.532][000000003.548] I/user.块[42] 00000000000000000000000000000000 32
+[2025-12-01 15:03:48.595][000000003.614] I/user.块[43] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 15:03:48.655][000000003.679] I/user.块[44] 00000000000000000000000000000000 32
+[2025-12-01 15:03:48.717][000000003.745] I/user.块[45] 00000000000000000000000000000000 32
+[2025-12-01 15:03:48.794][000000003.810] I/user.块[46] 00000000000000000000000000000000 32
+[2025-12-01 15:03:48.856][000000003.876] I/user.块[47] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 15:03:48.918][000000003.942] I/user.块[48] 00000000000000000000000000000000 32
+[2025-12-01 15:03:48.983][000000004.007] I/user.块[49] 00000000000000000000000000000000 32
+[2025-12-01 15:03:49.044][000000004.073] I/user.块[50] 00000000000000000000000000000000 32
+[2025-12-01 15:03:49.123][000000004.138] I/user.块[51] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 15:03:49.187][000000004.204] I/user.块[52] 00000000000000000000000000000000 32
+[2025-12-01 15:03:49.249][000000004.270] I/user.块[53] 00000000000000000000000000000000 32
+[2025-12-01 15:03:49.311][000000004.335] I/user.块[54] 00000000000000000000000000000000 32
+[2025-12-01 15:03:49.375][000000004.401] I/user.块[55] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 15:03:49.438][000000004.467] I/user.块[56] 00000000000000000000000000000000 32
+[2025-12-01 15:03:49.515][000000004.533] I/user.块[57] 00000000000000000000000000000000 32
+[2025-12-01 15:03:49.577][000000004.600] I/user.块[58] 00000000000000000000000000000000 32
+[2025-12-01 15:03:49.639][000000004.667] I/user.块[59] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-01 15:03:49.716][000000004.732] I/user.块[60] 00000000000000000000000000000000 32
+[2025-12-01 15:03:49.779][000000004.797] I/user.块[61] 00000000000000000000000000000000 32
+[2025-12-01 15:03:49.843][000000004.862] I/user.块[62] 00000000000000000000000000000000 32
+[2025-12-01 15:03:49.905][000000004.927] I/user.块[63] 000000000000FF078069FFFFFFFFFFFF 32
+
+```

+ 188 - 0
module/Air8101/demo/accessory_board/AirRC522_1000/AirRC522_1000.lua

@@ -0,0 +1,188 @@
+--[[
+@module  AirRC522_1000
+@summary AirRC522_1000测试功能模块
+@version 1.0
+@date    2025.07.23
+@author  马亚丹
+@usage
+本demo演示的功能为:使用Air8101核心板通过SPI实现对RC522的操作,演示读写数据等操作。
+以 Air8101核心板为例, 接线如下:
+Air8101核心板                AirRC522_1000
+GND(任意)                         GND
+59/3V3                           3.3V
+54/DISP                           SDA
+28/DCLK                           SCK
+57/DE                             MOSI
+55/HSYN                           MISO
+任意空闲的io(此处使用10/GPIO7)      RST
+
+核心逻辑:
+1. 初始化并启用spi,如果初始化失败,退出程序
+2. 初始化RC522模块,如果初始化失败,退出程序
+3. 循环检测卡片。
+4. 向卡片指定块号写入数据,并读取数据验证一致性
+5. 读取卡片所有数据
+]]
+
+
+
+rc522 = require "rc522"
+gpio.setup(13, 1) --air8101模组,gpio13控制ldo输出3.3v
+
+-- 硬件配置参数(Air8101适配)
+local RC522_CONFIG = {
+    spi_id = 0,                 -- SPI通道
+    cs_pin = 15,                -- 片选引脚
+    rst_pin = 7,                -- 复位引脚
+    spi_baud = 4 * 1000 * 1000, -- SPI波特率
+}
+
+-- 全局变量(模块内可见)
+local spi_dev = nil -- SPI设备对象
+
+-- 1. 初始化SPI接口
+local function init_spi()
+    log.info("RC522", "初始化SPI接口")
+    -- 配置SPI参数:模式0,8位数据,高位在前
+    spi_dev = spi.setup(
+        RC522_CONFIG.spi_id,
+        RC522_CONFIG.cs_pin,
+        0,          -- 极性0
+        0,          -- 相位0
+        8,          -- 数据位
+        RC522_CONFIG.spi_baud,
+        spi.MSB,    -- 高位优先
+        spi.master, --主模式
+        spi.half    --半双工
+    )
+
+    if not spi_dev then
+        log.error("RC522", "SPI初始化失败")
+        return false
+    end
+    log.info("RC522", "SPI初始化成功")
+    return true
+end
+
+-- 2. 初始化RC522模块
+local function init_rc522()
+    log.info("RC522", "初始化RC522传感器")
+    -- 初始化RC522硬件(SPI ID、CS引脚、RST引脚)
+    local init_ok = rc522.init(
+        RC522_CONFIG.spi_id,
+        RC522_CONFIG.cs_pin,
+        RC522_CONFIG.rst_pin
+    )
+
+    if not init_ok then
+        log.error("RC522", "传感器初始化失败")
+        return false
+    end
+    log.info("RC522", "传感器初始化成功")
+    return true
+end
+
+-- 3. 写数据
+local function write_ic_card(block, data)
+    -- 步骤1:验证数据长度
+    if #data > 16 then
+        log.error("RC522", "数据过长(最大16字节)")
+        return false
+    end
+
+    -- 步骤2:写入数据块
+    local write_ok = rc522.write_datablock(block, data)
+    if not write_ok then
+        log.error("RC522", "数据写入失败,块号:", block)
+        return false
+    end
+
+    log.info("RC522", "数据写入成功,块号:", block, "写入数据是:", string.char(table.unpack(data)):toHex())
+    return true
+end
+
+-- 4. 检测并读取IC卡数据
+local function read_ic_card()
+    -- 检测是否有卡片靠近
+    local status, array_id = rc522.request(rc522.reqall)
+    if not status then
+        log.info("RC522", "未检测到卡片")
+        return false
+    end
+    log.info("RC522", "检测到卡片,类型:", array_id:toHex())
+
+    -- 防冲突检测,获取卡片唯一ID
+    local status, card_uid = rc522.anticoll(array_id)
+    if not status then
+        log.error("RC522", "防冲突检测失败")
+        return false
+    end
+    log.info("RC522", "卡片UID:", card_uid:toHex())
+
+    -- -- 选择卡片,激活卡片进行后续操作
+    local select_ok = rc522.select(card_uid)
+    if not select_ok then
+        log.error("RC522", "卡片选择失败")
+        return false
+    end
+
+    -- 写数据测试
+    local TEST_BLOCK = 9
+    -- 待写入的数据
+    local TEST_DATA = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
+    if write_ic_card(TEST_BLOCK, TEST_DATA) then
+        -- 读数据验证
+        local read_ok, read_data = rc522.read_datablock(TEST_BLOCK)
+
+        if read_ok and read_data == string.char(table.unpack(TEST_DATA)) then
+            log.info("RC522", "写入验证成功,数据是:", read_data:toHex())
+        else
+            log.warn("RC522", "写入验证失败,实际读取的数据是:", read_data:toHex())
+        end
+    end
+
+    -- 读取卡片数据块(0-63块)
+    log.info("RC522", "开始读取卡片数据...")
+    for block = 0, 63 do
+        local read_ok, data = rc522.read_datablock(block)
+        if read_ok and data then
+            log.info(string.format("块[%d]", block), data:toHex())
+        else
+            log.warn(string.format("块[%d]读取失败", block))
+        end
+        --每读取完一个块等待20ms(时间可按需修改)
+        sys.wait(20)
+    end
+
+    -- 停止卡片操作
+    rc522.halt()
+    return true
+end
+-- 5. 关闭SPI设备,成功返回0
+local function spi_close_func()
+    log.info("关闭spi", spi.close(RC522_CONFIG.spi_id))
+end
+
+-- 6. 主测试任务
+local function rc522_main_test()
+    -- 初始化硬件
+    if not init_spi() then
+        spi_close_func()
+        return
+    end
+    if not init_rc522() then
+        spi_close_func()
+        return
+    end
+
+    -- 循环检测卡片
+    log.info("RC522", "开始检测卡片")
+    while true do
+        read_ic_card()
+        -- 循环检测间隔:2s
+        sys.wait(2000)
+    end
+end
+
+-- 启动主任务
+sys.taskInit(rc522_main_test)

+ 73 - 0
module/Air8101/demo/accessory_board/AirRC522_1000/main.lua

@@ -0,0 +1,73 @@
+--[[
+@module  main
+@summary LuatOS用户应用脚本文件入口,总体调度应用逻辑 
+@version 001.000.000
+@date    2025.9.05
+@author  马亚丹
+@usage
+本demo是使用Air8101核心板挂载合宙AirRC522_1000配件板,来演示rc522的功能使用方法,代码脚本如下:
+1.rc522.lua :rc522扩展库文件,在AirRC522_1000.lua功能模块中require使用  
+2.AirRC522_1000.lua:功能演示核心文件,在main.lua中加载运行,详细逻辑请看AirRC522_1000.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 = "Air8101_rc522_DEMO"
+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)
+
+-- 加载AirRC522_1000功能模块
+require "AirRC522_1000"
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后不要加任何语句!!!!!因为添加的任何语句都不会被执行

+ 646 - 0
module/Air8101/demo/accessory_board/AirRC522_1000/rc522.lua

@@ -0,0 +1,646 @@
+--[[
+@module rc522
+@summary rc522 非接触式读写卡驱动
+@version 1.0
+@date    2022.06.14
+@author  Dozingfiretruck
+@usage
+--注意:因使用了sys.wait()所有api需要在task中使用
+-- 用法实例
+local rc522 = require "rc522"
+local function rc522_test()
+    spi_rc522 = spi.setup(0,nil,0,0,8,10*1000*1000,spi.MSB, spi.master,spi.half)
+    rc522.init(0,12,16)
+    wdata = {0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}
+    while 1 do
+        rc522.write_datablock(8,wdata)
+        for i=0,63 do
+            local a,b = rc522.read_datablock(i)
+            if a then
+                log.info("read",i,b:toHex())
+            end
+        end
+        sys.wait(500)
+    end
+end)
+sys.taskInit(rc522_test)
+]]
+
+
+local rc522                  = {}
+
+local rc522_spi, rc522_rst, rc522_irq, rc522_cs
+
+---器件所用地址
+local rc522_idle             = 0x00 --取消当前命令
+local rc522_authent          = 0x0E --验证密钥
+local rc522_receive          = 0x08 --接收数据
+local rc522_transmit         = 0x04 --发送数据
+local rc522_transceive       = 0x0C --发送并接收数据
+local rc522_resetphase       = 0x0F --复位
+local rc522_calccrc          = 0x03 --CRC计算
+
+--Mifare_One卡片命令字
+rc522.reqidl                 = 0x26 --寻天线区内未进入休眠状态
+rc522.reqall                 = 0x52 --寻天线区内全部卡
+local rc522_anticoll1        = 0x93 --防冲撞
+local rc522_anticoll2        = 0x95 --防冲撞
+local rc522_authent1a        = 0x60 --验证A密钥
+local rc522_authent1b        = 0x61 --验证B密钥
+local rc522_read             = 0x30 --读块
+local rc522_write            = 0xA0 --写块
+local rc522_decrement        = 0xC0 --扣款
+local rc522_increment        = 0xC1 --充值
+local rc522_restore          = 0xC2 --调块数据到缓冲区
+local rc522_transfer         = 0xB0 --保存缓冲区中数据
+local rc522_halt             = 0x50 --休眠
+
+--MF522寄存器定义
+-- PAGE 0
+local rc522_rfu00            = 0x00 --保留为将来之用
+local rc522_com_mand         = 0x01 --启动和停止命令的执行
+local rc522_com_ie           = 0x02 --中断请求传递的使能和禁能控制位
+local rc522_divl_en          = 0x03 --中断请求传递的使能和禁能控制位
+local rc522_com_irq          = 0x04 --包含中断请求标志
+local rc522_div_irq          = 0x05 --包含中断请求标志
+local rc522_error            = 0x06 --错误标志,指示执行的上个命令的错误状态
+local rc522_status1          = 0x07 --包含通信的状态标志
+local rc522_status2          = 0x08 --包含接收器和发送器的状态标志
+local rc522_fifo_data        = 0x09 --64 字节 FIFO 缓冲区的输入和输出
+local rc522_fifo_level       = 0x0A --指示 FIFO 中存储的字节数
+local rc522_water_level      = 0x0B --定义 FIFO 下溢和上溢报警的 FIFO 深度
+local rc522_control          = 0x0C --不同的控制寄存器
+local rc522_bit_framing      = 0x0D --面向位的帧的调节
+local rc522_coll             = 0x0E --RF 接口上检测到的第一个位冲突的位的位置
+local rc522_rfu0f            = 0x0F --保留为将来之用
+-- PAGE 1
+local RFU10                  = 0x10 --保留为将来之用
+local rc522_mode             = 0x11 --定义发送和接收的常用模式
+local rc522_tx_mode          = 0x12 --定义发送过程的数据传输速率
+local rc522_rx_mode          = 0x13 --定义接收过程中的数据传输速率
+local rc522_tx_control       = 0x14 --控制天线驱动器管脚 TX1 和 TX2 的逻辑特性
+local rc522_tx_ayto          = 0x15 --控制天线驱动器的设置
+local rc522_tx_sel           = 0x16 --选择天线驱动器的内部源
+local rc522_rx_sel           = 0x17 --选择内部的接收器设置
+local rc522_rx_threshold     = 0x18 --选择位译码器的阈值
+local rc522_demod            = 0x19 --定义解调器的设置
+local rc522_rfu1a            = 0x1A --保留为将来之用
+local rc522_rfu1b            = 0x1B --保留为将来之用
+local rc522_mifare           = 0x1C --控制 ISO 14443/MIFARE 模式中 106kbit/s 的通信
+local rc522_rfu1d            = 0x1D --保留为将来之用
+local rc522_rfu1e            = 0x1E --保留为将来之用
+local rc522_serial_speed     = 0x1F --选择串行 UART 接口的速率
+-- PAGE 2
+local rc522_rfu20            = 0x20 --保留为将来之用
+local rc522_crcresult_m      = 0x21 --显示 CRC 计算的实际 MSB 值
+local rc522_crcresult_l      = 0x22 --显示 CRC 计算的实际 LSB 值
+local rc522_rfu23            = 0x23 --保留为将来之用
+local rc522_mod_width        = 0x24 --控制 ModWidth 的设置
+local rc522_rfu25            = 0x25 --保留为将来之用
+local rc522_rfcfg            = 0x26 --配置接收器增益
+local rc522_gsn              = 0x27 --选择天线驱动器管脚 TX1 和 TX2 的调制电导
+local rc522_cwgscfg          = 0x28 --选择天线驱动器管脚 TX1 和 TX2 的调制电导
+local rc522_modgscfg         = 0x29 --选择天线驱动器管脚 TX1 和 TX2 的调制电导
+local rc522_tmode            = 0x2A --定义内部定时器的设置
+local rc522_tprescaler       = 0x2B --定义内部定时器的设置
+local rc522_tpreload_h       = 0x2C --描述 16 位长的定时器重装值
+local rc522_tpreload_l       = 0x2D --描述 16 位长的定时器重装值
+local rc522_tcounter_value_h = 0x2E --描述 16 位长的定时器重装值
+local rc522_tcounter_value_l = 0x2F --描述 16 位长的定时器重装值
+-- PAGE 3
+local rc522_rfu30            = 0x30 --保留为将来之用
+local rc522_testsel1         = 0x31 --常用测试信号的配置
+local rc522_testsel2         = 0x32 --常用测试信号的配置和 PRBS 控制
+local rc522_testpin_en       = 0x33 --D1-D7 输出驱动器的使能管脚(注:仅用于串行接口)
+local rc522_testpin_value    = 0x34 --定义 D1-D7 用作 I/O 总线时的值
+local rc522_testbus          = 0x35 --显示内部测试总线的状态
+local rc522_autotest         = 0x36 --控制数字自测试
+local rc522_version          = 0x37 --显示版本
+local rc522_analogtest       = 0x38 --控制管脚 AUX1 和 AUX2
+local rc522_testadc1         = 0x39 --定义 TestDAC1 的测试值
+local rc522_testadc2         = 0x3A --定义 TestDAC2 的测试值
+local rc522_testadc          = 0x3B --显示 ADC I 和 Q 通道的实际值
+local rc522_rfu3c            = 0x3C --保留用于产品测试
+local rc522_rfu3d            = 0x3D --保留用于产品测试
+local rc522_rfu3e            = 0x3E --保留用于产品测试
+local rc522_rfu3f            = 0x3F --保留用于产品测试
+
+local Key_A                  = string.char(0xff, 0xff, 0xff, 0xff, 0xff, 0xff)
+local Key_B                  = string.char(0xff, 0xff, 0xff, 0xff, 0xff, 0xff)
+
+--[[
+写rc522寄存器
+@api rc522.set_bit_mask(address, value)
+@number address 地址
+@number value    值
+@usage
+write_rawrc(rc522_bit_framing,0x07)
+]]
+local function write_rawrc(address, value)
+    rc522_cs(0)
+    local data = string.char((address << 1) & 0x7E) .. string.char(value)
+    spi.send(rc522_spi, data)
+    -- rc522_spi:send(data)
+    rc522_cs(1)
+end
+
+--[[
+读rc522寄存器
+@api rc522.read_rawrc(address)
+@number address 地址
+@return number 寄存器值
+@usage
+local n = read_rawrc(rc522_com_irq)
+]]
+local function read_rawrc(address)
+    rc522_cs(0)
+    local data = string.char(((address << 1) & 0x7E)|0x80)
+    spi.send(rc522_spi, data)
+    local val = spi.recv(rc522_spi, 1)
+    -- rc522_spi:send(data)
+    -- local val = rc522_spi:recv(1)
+    rc522_cs(1)
+    return string.byte(val)
+end
+
+--[[
+对rc522寄存器置位
+@api rc522.set_bit_mask(address, mask)
+@number address 地址
+@number mask    置位值
+@usage
+rc522.set_bit_mask (rc522_fifo_level, 0x80)	
+]]
+function rc522.set_bit_mask(address, mask)
+    local current = read_rawrc(address)
+    write_rawrc(address, bit.bor(current, mask))
+end
+
+--[[
+对rc522寄存器清位
+@api rc522.clear_bit_mask(address, mask)
+@number address 地址
+@number mask    清位值
+@usage
+rc522.clear_bit_mask(rc522_com_irq, 0x80 )
+]]
+function rc522.clear_bit_mask(address, mask)
+    local current = read_rawrc(address)
+    write_rawrc(address, bit.band(current, bit.bnot(mask)))
+end
+
+--[[
+命令通讯
+@api rc522.command(command,data)
+@number command
+@number data
+@return status data len 结果,返回数据,收到的位长度
+@usage
+rc522.version()
+]]
+function rc522.command(command, data)
+    local out_data = {}
+    local len      = 0
+    local status   = false
+    local Irq_en   = 0x00
+    local waitfor  = 0x00
+    local last_bits, n, l
+    if command == rc522_authent then
+        Irq_en  = 0x12
+        waitfor = 0x10
+    elseif command == rc522_transceive then
+        Irq_en  = 0x77
+        waitfor = 0x30
+    end
+    write_rawrc(0x02, bit.bor(Irq_en, 0x80))
+    rc522.clear_bit_mask(rc522_com_irq, 0x80)
+    write_rawrc(rc522_com_mand, rc522_idle)
+    rc522.set_bit_mask(rc522_fifo_level, 0x80)
+    for i = 1, #data do
+        write_rawrc(rc522_fifo_data, data[i])
+    end
+    write_rawrc(rc522_com_mand, command)
+    if (command == rc522_transceive) then
+        rc522.set_bit_mask(rc522_bit_framing, 0x80)
+    end
+    l = 12
+    while true do
+        sys.wait(1)
+        n = read_rawrc(rc522_com_irq)
+        l = l - 1
+        if not ((l ~= 0) and (bit.band(n, 0x01) == 0) and (bit.band(n, waitfor) == 0)) then
+            break
+        end
+    end
+    rc522.clear_bit_mask(rc522_bit_framing, 0x80)
+    if (l ~= 0) then
+        if bit.band(read_rawrc(rc522_error), 0x1B) == 0x00 then
+            status = true
+            if bit.band(n, Irq_en, 0x01) ~= 0 then
+                status = false
+            end
+            if (command == rc522_transceive) then
+                n = read_rawrc(rc522_fifo_level)
+                last_bits = bit.band(read_rawrc(rc522_control), 0x07)
+                if last_bits ~= 0 then
+                    len = (n - 1) * 8 + last_bits
+                else
+                    len = n * 8
+                end
+                if n == 0 then
+                    n = 1
+                end
+                for i = 1, n do
+                    out_data[i] = read_rawrc(rc522_fifo_data)
+                end
+            end
+        end
+    else
+        status = false
+    end
+    rc522.set_bit_mask(rc522_control, 0x80)
+    write_rawrc(rc522_com_mand, rc522_idle)
+    return status, out_data, len
+end
+
+--[[
+防冲撞
+@api rc522.anticoll(id)
+@string id 卡片序列号,4字节
+@return status uid 结果,uid
+@usage
+local status,uid = rc522.anticoll(array_id)
+]]
+function rc522.anticoll(id)
+    local check = 0
+    local uid
+    rc522.clear_bit_mask(rc522_status2, 0x08)
+    write_rawrc(rc522_bit_framing, 0x00)
+    rc522.clear_bit_mask(rc522_coll, 0x80)
+    local status, back_data = rc522.command(rc522_transceive, { 0x93, 0x20 })
+    if back_data and #back_data >= 5 then
+        for i = 1, 4 do
+            check = bit.bxor(check, back_data[i])
+        end
+        if check ~= back_data[5] then
+            status = false
+        end
+        uid = string.char(table.unpack(back_data, 1, 4))
+    end
+    rc522.clear_bit_mask(rc522_coll, 0x80)
+    return status, uid
+end
+
+--[[
+crc计算
+@api calculate_crc(data)
+@table data 数据
+@return table crc值
+@usage
+local crc = calculate_crc(buff)
+]]
+local function calculate_crc(data)
+    local ret_data = {}
+    rc522.clear_bit_mask(rc522_div_irq, 0x04)
+    write_rawrc(rc522_com_mand, rc522_idle)
+    rc522.set_bit_mask(rc522_fifo_level, 0x80)
+    for i = 1, #data do
+        write_rawrc(rc522_fifo_data, data[i])
+    end
+    write_rawrc(rc522_com_mand, rc522_calccrc)
+    local i = 0xFF
+    while true do
+        local n = read_rawrc(0x05)
+        i = i - 1
+        if not ((i ~= 0) and not (n & 0x04)) then
+            break
+        end
+    end
+    ret_data[1] = read_rawrc(0x22)
+    ret_data[2] = read_rawrc(0x21)
+    return ret_data
+end
+
+--[[
+验证卡片密码
+@api authstate(mode, addr,key,uid )
+@number mode 模式
+@number addr 地址
+@string key 密码
+@string uid uid
+@return bool 结果
+@usage
+status = authstate(rc522_authent1b, addr,Key_B,uid )
+]]
+local function authstate(mode, addr, key, uid)
+    local buff = {}
+    buff[1] = mode
+    buff[2] = addr
+    for i = 1, 6 do
+        buff[i + 2] = key:byte(i)
+    end
+    for i = 1, 4 do
+        buff[i + 8] = uid:byte(i)
+    end
+    local status, back_data = rc522.command(rc522_authent, buff)
+    if status == true and (read_rawrc(rc522_status2) & 0x08) ~= 0 then
+        return true
+    end
+    return false
+end
+
+--[[
+写数据到M1卡一块
+@api rc522.write(addr, data)
+@number addr 块地址(0-63)M1卡总共有16个扇区(每个扇区有:3个数据块+1个控制块),共64个块
+@table data 数据
+@return bool 结果
+@usage
+rc522.write(addr, data)
+]]
+function rc522.write(addr, data)
+    local buff = {}
+    buff[1] = rc522_write
+    buff[2] = addr
+    local crc = calculate_crc(buff)
+    buff[3] = crc[1]
+    buff[4] = crc[2]
+    local status, back_data, back_bits = rc522.command(rc522_transceive, buff)
+    if status == true and back_bits == 4 and (back_data[1] & 0x0F) == 0x0A then
+        local buf_w = {}
+        for i = 0, 16 do
+            table.insert(buf_w, data[i])
+        end
+        local crc = calculate_crc(buf_w)
+        table.insert(buf_w, crc[1])
+        table.insert(buf_w, crc[2])
+        status, back_data, back_bits = rc522.command(rc522_transceive, buf_w)
+        if status == true and back_bits == 4 and (back_data[1] & 0x0F) == 0x0A then
+            return status
+        end
+    end
+    return status
+end
+
+--[[
+写数据到M1卡一块
+@api rc522.read(addr)
+@number addr 块地址(0-63)M1卡总共有16个扇区(每个扇区有:3个数据块+1个控制块),共64个块
+@return bool,string 结果,数据
+@usage
+rc522.read(addr, data)
+]]
+local function read(addr)
+    local buff = {}
+    buff[1] = rc522_read
+    buff[2] = addr
+    local crc = calculate_crc(buff)
+    buff[3] = crc[1]
+    buff[4] = crc[2]
+    local status, back_data, back_bits = rc522.command(rc522_transceive, buff)
+    if status == true and back_bits == 0x90 then
+        local data = string.char(table.unpack(back_data, 1, 16))
+        return status, data
+    end
+    return false
+end
+
+--[[
+rc522 硬件版本
+@api rc522.version()
+@return number 硬件版本
+@usage
+rc522.version()
+]]
+function rc522.version()
+    return read_rawrc(rc522_version)
+end
+
+--[[
+rc522 命令卡片进入休眠状态
+@api rc522.halt()
+@return bool 结果
+@usage
+rc522.halt()
+]]
+function rc522.halt()
+    local buff = {}
+    buff[1] = rc522_halt
+    buff[2] = 0
+    local crc = calculate_crc(buff)
+    buff[3] = crc[1]
+    buff[4] = crc[2]
+    local status = rc522.command(rc522_transceive, buff)
+    return status
+end
+
+--[[
+rc522 复位
+@api rc522.reset()
+@usage
+rc522.reset()
+]]
+function rc522.reset()
+    rc522_rst(1)
+    rc522_rst(0)
+    rc522_rst(1)
+    write_rawrc(rc522_com_mand, 0x0f)
+    write_rawrc(rc522_mode, 0x3D)
+    write_rawrc(rc522_tpreload_l, 30)
+    write_rawrc(rc522_tpreload_h, 0)
+    write_rawrc(rc522_tmode, 0x8D)
+    write_rawrc(rc522_tprescaler, 0x3E)
+    write_rawrc(rc522_tx_ayto, 0x40)
+end
+
+--[[
+开启天线
+@api rc522.antenna_on()
+@usage
+rc522.antenna_on()
+]]
+function rc522.antenna_on()
+    local uc = read_rawrc(rc522_tx_control)
+    if ((uc & 0x03) == 0) then
+        rc522.set_bit_mask(rc522_tx_control, 0x03)
+    end
+end
+
+--[[
+关闭天线
+@api rc522.antenna_on()
+@usage
+rc522.antenna_on()
+]]
+function rc522.antenna_off()
+    rc522.clear_bit_mask(rc522_tx_control, 0x03)
+end
+
+--[[
+设置rc522工作方式为ISO14443_A
+@api rc522_config_isotype()
+@usage
+rc522_config_isotype()
+]]
+local function rc522_config_isotype()
+    rc522.clear_bit_mask(rc522_status2, 0x08)
+    write_rawrc(rc522_mode, 0x3D)
+    write_rawrc(rc522_rx_sel, 0x86)
+    write_rawrc(rc522_rfcfg, 0x7F)
+    write_rawrc(rc522_tpreload_l, 30)
+    write_rawrc(rc522_tpreload_h, 0)
+    write_rawrc(rc522_tmode, 0x8D)
+    write_rawrc(rc522_tprescaler, 0x3E)
+    rc522.antenna_on()
+end
+
+--[[
+rc522 寻卡
+@api rc522.request(req_code)
+@number req_code rc522.reqidl 寻天线区内未进入休眠状态 rc522.reqall 寻天线区内全部卡
+@return bool tagtype 结果,卡类型
+@usage
+status,array_id = rc522.request(rc522.reqall)
+]]
+function rc522.request(req_code)
+    rc522.clear_bit_mask(rc522_status2, 0x08)
+    write_rawrc(rc522_bit_framing, 0x07)
+    rc522.set_bit_mask(rc522_tx_control, 0x03)
+    local tagtype
+    local status, back_data, back_bits = rc522.command(rc522_transceive, { req_code })
+    if status == true and back_bits == 0x10 then
+        tagtype = string.char(table.unpack(back_data, 1, 2))
+        return status, tagtype
+    end
+    return false
+end
+
+--[[
+选定卡片
+@api rc522.select(id)
+@number id 卡片序列号,4字节
+@return bool 结果
+@usage
+status = rc522.select(id)
+]]
+function rc522.select(id)
+    if not id then
+        return false
+    end
+    local buff = {}
+    buff[1] = rc522_anticoll1
+    buff[2] = 0x70
+    buff[7] = 0
+    for i = 1, 4 do
+        buff[i + 2] = id:byte(i)
+        buff[7] = bit.bxor(buff[7], id:byte(i))
+    end
+    local crc = calculate_crc(buff)
+    buff[8] = crc[1]
+    buff[9] = crc[2]
+    rc522.clear_bit_mask(rc522_status2, 0x08)
+    local status, back_data, back_bits = rc522.command(rc522_transceive, buff)
+    if status == true and back_bits == 0x18 then
+        return true
+    end
+    return false
+end
+
+--[[
+按照rc522操作流程写入16字节数据到块
+@api rc522.write_datablock(addr,data)
+@number addr 任意块地址.M1卡总共有16个扇区(每个扇区有:3个数据块+1个控制块),共64个块
+@table data 指向要写入的数据,必须为16个字符
+@return bool 结果
+@usage
+rc522.write_datablock(addr,data)
+]]
+function rc522.write_datablock(addr, data)
+    if #data ~= 16 then
+        return false
+    end
+    local status, array_id = rc522.request(rc522.reqall)
+    if status ~= true then
+        status, array_id = rc522.request(rc522.reqall)
+    end
+    if status == true then
+        local status, uid = rc522.anticoll(array_id)
+        if not uid then
+            return false
+        end
+        if status == true and uid then
+            rc522.select(uid)
+            status = authstate(rc522_authent1b, addr, Key_B, uid)
+            if status == true then
+                status = rc522.write(addr, data)
+                rc522.halt()
+                return status
+            end
+        end
+    end
+    return false
+end
+
+--[[
+按照rc522操作流程读取块
+@api rc522.read_datablock(addr)
+@number addr 任意块地址.M1卡总共有16个扇区(每个扇区有:3个数据块+1个控制块),共64个块
+@return bool string 结果 数据
+@usage
+    for i=0,63 do
+        local a,b = rc522.read_datablock(i)
+        if a then
+            print("read",i,b:toHex())
+        end
+    end
+]]
+function rc522.read_datablock(addr)
+    local status, array_id = rc522.request(rc522.reqall)
+    if status ~= true then
+        status, array_id = rc522.request(rc522.reqall)
+    end
+    if status == true then
+        local status, uid = rc522.anticoll(array_id)
+        if status == true and uid then
+            rc522.select(uid)
+            status = authstate(rc522_authent1b, addr, Key_B, uid)
+            if status == true then
+                local status, data = read(addr)
+                if status == true then
+                    return true, data
+                end
+                rc522.halt()
+            end
+        end
+    end
+    return false
+end
+
+--[[
+rc522 初始化
+@api rc522.init(spi_id, cs, rst)
+@number spi_id spi端口号
+@number cs      cs引脚
+@number rst     rst引脚
+@return bool 初始化结果
+@usage
+spi_rc522 = spi.setup(0,nil,0,0,8,10*1000*1000,spi.MSB,1,1)
+rc522.init(0,pin.PB04,pin.PB01)
+]]
+function rc522.init(spi_id, cs, rst)
+    rc522_spi = spi_id
+    rc522_cs = gpio.setup(cs, 0, gpio.PULLUP)
+    rc522_cs(1)
+    rc522_rst = gpio.setup(rst, 0, gpio.PULLUP)
+    rc522_rst(1)
+    rc522.reset()
+    rc522_config_isotype()
+    log.debug("rc522.version", rc522.version())
+    return true
+end
+
+function rc522.test()
+
+end
+
+return rc522

+ 144 - 0
module/Air8101/demo/accessory_board/AirRC522_1000/readme.md

@@ -0,0 +1,144 @@
+## 功能模块介绍:
+
+1. main.lua:主程序入口
+
+2. rc522.lua :rc522扩展库文件,在AirRC522_1000.lua功能模块中require使用
+
+3. AirRC522_1000.lua:功能演示核心文件,在main.lua中加载运行,详细逻辑请看AirRC522_1000.lua 文件
+
+## 演示功能概述:
+
+核心逻辑:
+
+1. 初始化并启用spi,如果初始化失败,退出程序
+
+2. 初始化RC522模块,如果初始化失败,退出程序
+
+3. 循环检测卡片。
+
+4. 向卡片指定块号写入数据,并读取数据验证一致性
+
+5. 读取卡片所有数据
+
+## 演示硬件环境:
+
+![](https://docs.openluat.com/accessory/AirSPINORFLASH_1000/image/8101.jpg)
+
+![](https://docs.openluat.com/accessory/AirSPINORFLASH_1000/image/rc522.jpg)
+
+1. 合宙 Air8101 核心板一块
+
+2. 合宙 AirRC522_1000一套
+
+3. TYPE-C USB 数据线一根 ,Air8101 核心板和数据线的硬件接线方式为:
+* Air8101核心板通过 TYPE-C USB 口供电;(背面功耗测试开关拨到off)
+
+* TYPE-C USB 数据线直接插到开发板的 TYPE-C USB 座子,另外一端连接电脑 USB 口;
+4. 杜邦线 7 根
+
+   Air8101 核心板与 AirRC522_1000按以下方式接线:
+
+| Air8101 核心板                                    | AirRC522_1000 |
+| ---------------------------------------------- | ------------- |
+| GND(任意)                                        | GND           |
+| 59/3V3                                         | 3.3V          |
+| 54/DISP                                        | SDA           |
+| 28/DCLK                                        | SCK           |
+| 57/DE                                          | MOSI          |
+| 55/HSYN                                        | MISO          |
+| 10/GPIO7(可选任意空闲IO) | RST           |
+
+## 演示软件环境:
+
+1. Luatools 下载调试工具
+
+2. 固件版本:LuatOS-SoC_V1006_Air8101_1.soc,固件地址,如有最新固件请用最新 [https://docs.openluat.com/air8101/luatos/firmware/version/](https://docs.openluat.com/air8101/luatos/firmware/)
+
+3. pc 系统 win11(win10 及以上)
+
+## 演示核心步骤:
+
+1. 搭建好硬件环境
+
+2. Luatools 烧录内核固件和demo 脚本代码.
+   AirRC522_1000.lua脚本中,硬件配置参数中rst_pin是使用Air8101核心板的GPIO7,如果接其他IO,注意修改硬件配置参数中rst_pin对应的管脚号。
+
+3. 烧录成功后,代码会自动运行,查看打印日志,如果正常运行,会打印相关信息,spi 初始化,rc522初始化,写入数据读取数据等。
+
+4. 如下 log 显示:
+
+```
+[2025-12-02 10:56:56.862] luat:U(172):D/user.rc522.version 178
+[2025-12-02 10:56:56.862] luat:U(172):I/user.RC522 传感器初始化成功
+[2025-12-02 10:56:56.862] luat:U(172):I/user.RC522 开始检测卡片
+[2025-12-02 10:56:56.862] luat:U(178):I/user.RC522 检测到卡片,类型: 0400 4
+[2025-12-02 10:56:56.862] luat:U(183):I/user.RC522 卡片UID: E3E2AD14 8
+[2025-12-02 10:56:56.957] luat:U(263):I/user.RC522 数据写入成功,块号: 9 写入数据是: 01000000000000000000000000000000 32
+[2025-12-02 10:56:56.962] luat:U(291):I/user.RC522 写入验证成功,数据是: 01000000000000000000000000000000 32
+[2025-12-02 10:56:56.962] luat:U(291):I/user.RC522 开始读取卡片数据...
+[2025-12-02 10:56:57.033] luat:U(339):I/user.块[0] E3E2AD14B80804006263646566676869 32
+[2025-12-02 10:56:57.096] luat:U(405):I/user.块[1] 00000000000000000000000000000000 32
+[2025-12-02 10:56:57.159] luat:U(473):I/user.块[2] 00000000000000000000000000000000 32
+[2025-12-02 10:56:57.223] luat:U(541):I/user.块[3] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-02 10:56:57.299] luat:U(607):I/user.块[4] 00000000000000000000000000000000 32
+[2025-12-02 10:56:57.360] luat:U(673):I/user.块[5] 00000000000000000000000000000000 32
+[2025-12-02 10:56:57.423] luat:U(737):I/user.块[6] 00000000000000000000000000000000 32
+[2025-12-02 10:56:57.490] luat:U(805):I/user.块[7] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-02 10:56:57.562] luat:U(873):I/user.块[8] 00000000000000000000000000000000 32
+[2025-12-02 10:56:57.625] luat:U(941):I/user.块[9] 01000000000000000000000000000000 32
+[2025-12-02 10:56:57.702] luat:U(1008):I/user.块[10] 00000000000000000000000000000000 32
+[2025-12-02 10:56:57.765] luat:U(1075):I/user.块[11] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-02 10:56:57.828] luat:U(1142):I/user.块[12] 00000000000000000000000000000000 32
+[2025-12-02 10:56:57.890] luat:U(1208):I/user.块[13] 00000000000000000000000000000000 32
+[2025-12-02 10:56:57.968] luat:U(1274):I/user.块[14] 00000000000000000000000000000000 32
+[2025-12-02 10:56:58.031] luat:U(1342):I/user.块[15] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-02 10:56:58.092] luat:U(1410):I/user.块[16] 00000000000000000000000000000000 32
+[2025-12-02 10:56:58.169] luat:U(1478):I/user.块[17] 00000000000000000000000000000000 32
+[2025-12-02 10:56:58.230] luat:U(1544):I/user.块[18] 00000000000000000000000000000000 32
+[2025-12-02 10:56:58.295] luat:U(1612):I/user.块[19] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-02 10:56:58.370] luat:U(1678):I/user.块[20] 00000000000000000000000000000000 32
+[2025-12-02 10:56:58.433] luat:U(1744):I/user.块[21] 00000000000000000000000000000000 32
+[2025-12-02 10:56:58.495] luat:U(1810):I/user.块[22] 00000000000000000000000000000000 32
+[2025-12-02 10:56:58.558] luat:U(1876):I/user.块[23] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-02 10:56:58.634] luat:U(1942):I/user.块[24] 00000000000000000000000000000000 32
+[2025-12-02 10:56:58.696] luat:U(2009):I/user.块[25] 00000000000000000000000000000000 32
+[2025-12-02 10:56:58.758] luat:U(2076):I/user.块[26] 00000000000000000000000000000000 32
+[2025-12-02 10:56:58.834] luat:U(2143):I/user.块[27] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-02 10:56:58.897] luat:U(2209):I/user.块[28] 00000000000000000000000000000000 32
+[2025-12-02 10:56:58.959] luat:U(2275):I/user.块[29] 00000000000000000000000000000000 32
+[2025-12-02 10:56:59.035] luat:U(2341):I/user.块[30] 00000000000000000000000000000000 32
+[2025-12-02 10:56:59.096] luat:U(2407):I/user.块[31] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-02 10:56:59.159] luat:U(2471):I/user.块[32] 00000000000000000000000000000000 32
+[2025-12-02 10:56:59.222] luat:U(2537):I/user.块[33] 00000000000000000000000000000000 32
+[2025-12-02 10:56:59.284] luat:U(2603):I/user.块[34] 00000000000000000000000000000000 32
+[2025-12-02 10:56:59.362] luat:U(2669):I/user.块[35] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-02 10:56:59.424] luat:U(2735):I/user.块[36] 00000000000000000000000000000000 32
+[2025-12-02 10:56:59.486] luat:U(2801):I/user.块[37] 00000000000000000000000000000000 32
+[2025-12-02 10:56:59.549] luat:U(2867):I/user.块[38] 00000000000000000000000000000000 32
+[2025-12-02 10:56:59.626] luat:U(2934):I/user.块[39] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-02 10:56:59.688] luat:U(2999):I/user.块[40] 00000000000000000000000000000000 32
+[2025-12-02 10:56:59.751] luat:U(3066):I/user.块[41] 00000000000000000000000000000000 32
+[2025-12-02 10:56:59.828] luat:U(3134):I/user.块[42] 00000000000000000000000000000000 32
+[2025-12-02 10:56:59.890] luat:U(3200):I/user.块[43] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-02 10:56:59.952] luat:U(3266):I/user.块[44] 00000000000000000000000000000000 32
+[2025-12-02 10:57:00.016] luat:U(3332):I/user.块[45] 00000000000000000000000000000000 32
+[2025-12-02 10:57:00.081] luat:U(3398):I/user.块[46] 00000000000000000000000000000000 32
+[2025-12-02 10:57:00.157] luat:U(3464):I/user.块[47] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-02 10:57:00.219] luat:U(3530):I/user.块[48] 00000000000000000000000000000000 32
+[2025-12-02 10:57:00.282] luat:U(3596):I/user.块[49] 00000000000000000000000000000000 32
+[2025-12-02 10:57:00.345] luat:U(3662):I/user.块[50] 00000000000000000000000000000000 32
+[2025-12-02 10:57:00.409] luat:U(3728):I/user.块[51] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-02 10:57:00.486] luat:U(3794):I/user.块[52] 00000000000000000000000000000000 32
+[2025-12-02 10:57:00.548] luat:U(3860):I/user.块[53] 00000000000000000000000000000000 32
+[2025-12-02 10:57:00.610] luat:U(3927):I/user.块[54] 00000000000000000000000000000000 32
+[2025-12-02 10:57:00.687] luat:U(3993):I/user.块[55] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-02 10:57:00.750] luat:U(4059):I/user.块[56] 00000000000000000000000000000000 32
+[2025-12-02 10:57:00.812] luat:U(4125):I/user.块[57] 00000000000000000000000000000000 32
+[2025-12-02 10:57:00.874] luat:U(4191):I/user.块[58] 00000000000000000000000000000000 32
+[2025-12-02 10:57:00.939] luat:U(4257):I/user.块[59] 000000000000FF078069FFFFFFFFFFFF 32
+[2025-12-02 10:57:01.014] luat:U(4323):I/user.块[60] 00000000000000000000000000000000 32
+[2025-12-02 10:57:01.077] luat:U(4389):I/user.块[61] 00000000000000000000000000000000 32
+[2025-12-02 10:57:01.139] luat:U(4457):I/user.块[62] 00000000000000000000000000000000 32
+[2025-12-02 10:57:01.218] luat:U(4525):I/user.块[63] 000000000000FF078069FFFFFFFFFFFF 32
+
+```