raw_spi.lua 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. --[[
  2. @module raw_spi
  3. @summary raw_spi测试功能模块
  4. @version 1.0
  5. @date 2025.9.05
  6. @author 马亚丹
  7. @usage
  8. 本demo演示的功能为:使用Air8000核心板通过SPI核心库实现对Flash的操作,演示读数据写数据、删除数据等操作。
  9. 以 Air8000核心板为例, 接线如下:
  10. Air8000 AirSPINORFLASH_1000
  11. GND(任意) GND
  12. VDD_EXT VCC
  13. GPIO12/SPI1_CS CS,片选
  14. SPI1_SLK CLK,时钟
  15. SPI1_MOSI DI,主机输出,从机输入
  16. SPI1_MISO DO,主机输入,从机输出
  17. --使用SPI1,硬件SPI CS接在gpio12上
  18. 核心逻辑:
  19. 1.初始化并启用spi,如果初始化失败,退出程序
  20. 2.spi启用后读取并验证flash芯片ID,如果验证失败,退出程序
  21. 3.验证flash芯片后读取寄存器状态,确认芯片就绪
  22. 4.擦除扇区,为写入数据做准备
  23. 5.擦除扇区后,写数据到扇区,并读取扇区数据与写入数据进行验证
  24. 6.关闭写使能并关闭SPI。
  25. ]]
  26. -- SPI配置参数
  27. local SPI_ID = 1 -- SPI总线ID,根据实际情况修改
  28. local CS_PIN = 12 -- CS引脚,根据实际情况修改
  29. local CPHA = 0 -- 时钟相位
  30. local CPOL = 0 -- 时钟极性
  31. local data_Width = 8 -- 数据宽度(位)
  32. local bandrate = 2000000 -- 波特率(Hz),初始化为2MHz
  33. local timeout = 1000 -- 操作超时时间(ms)
  34. local cspin = gpio.setup(CS_PIN, 1) --CS脚置于高电平
  35. -- 1. 设置并启用 SPI
  36. local function spiDev_init_func()
  37. log.info("raw_spi", "SPI_ID", SPI_ID, "CS_PIN", CS_PIN)
  38. local spiDevice = spi.setup(SPI_ID,nil, CPHA,CPOL,data_Width,bandrate,
  39. spi.MSB, --高低位顺序 可选,默认高位在前
  40. spi.master,--主模式 可选,默认主
  41. spi.half --半双工 spi flash只支持半双工
  42. )
  43. log.info("硬件spi", "初始化,波特率:", spiDevice, bandrate)
  44. return true
  45. end
  46. -- 2. 定义功能函数:发送和接收数据
  47. local function spi_transfer_func(sendData, recvLen)
  48. -- 选中设备
  49. cspin(0)
  50. if sendData then
  51. -- 发送数据
  52. spi.send(SPI_ID, sendData)
  53. end
  54. local recvData = ""
  55. if recvLen and recvLen > 0 then
  56. -- 接收数据
  57. recvData = spi.recv(SPI_ID, recvLen)
  58. end
  59. -- 取消选中
  60. cspin(1)
  61. return recvData
  62. end
  63. -- 3. 定义功能函数:读取并验证芯片ID
  64. local function spi_readChipId_func()
  65. --0x9F指令读取JEDEC ID
  66. local id = spi_transfer_func(string.char(0x9F), 3)
  67. --读取成功会返回 3 字节(制造商 + 设备 ID)
  68. if #id == 3 then
  69. local b1, b2, b3 = id:byte(1, 3)
  70. log.info("spi", "芯片ID: 0x%02X 0x%02X 0x%02X", b1, b2, b3)
  71. -- 验证是否为W25Q系列:
  72. --制造商 ID(第 1 字节): 0xEF(代表 Winbond)
  73. -- 设备类型(第 2 字节): 0x40(表示 W25Q 系列 NOR Flash)
  74. -- 容量代码(第 3 字节): 0x18(对应 128Mbit = 16MB 容量)
  75. if b1 == 0xEF and (b2 == 0x40 or b2 == 0x18) then
  76. return true
  77. end
  78. end
  79. log.error("spi", "读取芯片ID失败")
  80. return false
  81. end
  82. -- 4. 定义功能函数:写数据使能
  83. local function spi_writeEnable_func()
  84. --0x06指令设置Write Enable
  85. spi_transfer_func(string.char(0x06))
  86. return 0
  87. end
  88. -- 5. 定义功能函数:写数据禁用
  89. local function spi_writeDisable_func()
  90. --0x04指令设置Write Disable
  91. spi_transfer_func(string.char(0x04))
  92. end
  93. -- 6. 定义功能函数:读取状态寄存器
  94. local function spi_readStatus_func()
  95. --0x05指令读取寄存器状态
  96. local status = spi_transfer_func(string.char(0x05), 1)
  97. if #status == 1 then
  98. --返回0 表示WIP=0芯片就绪且未使能写操作
  99. return status:byte(1)
  100. end
  101. end
  102. --7. 等待写入完成
  103. local function spi_waitForWriteComplete_func()
  104. while timeout > 0 do
  105. local status = spi_readStatus_func()
  106. -- WIP位为0表示写入完成
  107. if bit.band(status, 0x01) == 0 then
  108. return true
  109. end
  110. sys.wait(10)
  111. timeout = timeout - 10
  112. end
  113. log.error("spi", "等待写入超时")
  114. return false
  115. end
  116. --8. 扇区擦除,根据需要修改
  117. -- 0x20:扇区擦除(4KB)
  118. -- 0xD8:块擦除(64KB)
  119. -- 0xC7:整片擦除
  120. --address是要擦除的扇区的起始地址,本demo演示扇区擦除,块擦除和整片擦除可自行研究
  121. local function spi_erase_sector(address)
  122. -- 使能写操作
  123. if not spi_writeEnable_func() then
  124. log.error("SPI", "写使能失败")
  125. return false
  126. end
  127. -- 发送扇区擦除指令
  128. local result = spi_transfer_func(string.char(0x20) ..
  129. string.char(bit.rshift(address, 16) & 0xFF) ..
  130. string.char(bit.rshift(address, 8) & 0xFF) ..
  131. string.char(address & 0xFF))
  132. if not result then
  133. log.error("SPI", "发送扇区擦除指令失败")
  134. return false
  135. end
  136. -- 等待写入完成
  137. return spi_waitForWriteComplete_func()
  138. end
  139. --9. 页编程(写入数据到指定地址)
  140. local function spi_pageProgram_func(address, data)
  141. -- 检查数据长度(不能超过256字节)
  142. local len = #data
  143. if len == 0 or len > 256 then
  144. log.error("spi", "数据长度无效:", len)
  145. return false
  146. end
  147. -- 准备写入命令和地址
  148. local cmd = string.char(0x02) ..
  149. string.char(bit.rshift(address, 16) & 0xFF) ..
  150. string.char(bit.rshift(address, 8) & 0xFF) ..
  151. string.char(address & 0xFF)
  152. -- 写数据使能
  153. spi_writeEnable_func()
  154. -- 发送写命令和数据
  155. spi_transfer_func(cmd .. data)
  156. -- 等待写入完成
  157. return spi_waitForWriteComplete_func()
  158. end
  159. -- 10. 读取数据
  160. local function spi_readData_func(address, length)
  161. if length <= 0 then
  162. return ""
  163. end
  164. -- 准备读取命令和地址
  165. local cmd = string.char(0x03) ..
  166. string.char(bit.rshift(address, 16) & 0xFF) ..
  167. string.char(bit.rshift(address, 8) & 0xFF) ..
  168. string.char(address & 0xFF)
  169. -- 发送读取命令并接收数据
  170. return spi_transfer_func(cmd, length)
  171. end
  172. -- 11. 关闭SPI设备,成功返回0
  173. local function spi_close_func()
  174. log.info("关闭spi", spi.close(SPI_ID))
  175. end
  176. --12. 功能演示核心函数
  177. local function spi_test_func()
  178. --1.判断SPI初始化
  179. if not spiDev_init_func() then
  180. return
  181. end
  182. --2.判断flash芯片ID
  183. if not spi_readChipId_func() then
  184. spi_close_func()
  185. return
  186. end
  187. -- 3.读取寄存器状态
  188. local status = spi_readStatus_func()
  189. --返回状态0表示芯片就绪且未使能写操作
  190. log.info("spi", "寄存器状态为: 0x%02X", status)
  191. -- 4.擦除扇区(4KB)
  192. --0x000000是要擦除的扇区的起始地址,即第一个存储单元的位置,
  193. --擦除从地址0x000000开始的整个 4KB 扇区,该扇区包含地址0x000000到0x000FFF的所有存储单元,可按需修改
  194. log.info("spi", "擦除扇区 0x000000...")
  195. if not spi_erase_sector(0x000000) then
  196. log.error("spi", "擦除失败")
  197. spi_close_func() --
  198. return
  199. end
  200. -- 5.读取擦除后的数据(应为0xFF)
  201. local erasedData = spi_readData_func(0x000000, 16)
  202. log.info("spi", "擦除后数据:", string.toHex(erasedData))
  203. -- 测试数据
  204. local testData = "Hello, SPI Flash! "
  205. -- 6.写入数据到地址0x000000
  206. log.info("spi", "写入数据:", testData)
  207. if spi_pageProgram_func(0x000000, testData) then
  208. -- 读取数据
  209. log.info("spi", "正在验证数据...")
  210. local readData = spi_readData_func(0x000000, #testData)
  211. -- 验证数据
  212. if readData == testData then
  213. log.info("spi", "数据验证成功!,读取到数据为:" .. readData)
  214. else
  215. log.error("spi", "数据验证失败!")
  216. log.info("spi", "预期读到的数据是:", testData)
  217. log.info("spi", "实际读取的数据是:", string.toHex(readData))
  218. end
  219. else
  220. log.error("spi", "写入操作失败")
  221. end
  222. -- 7.禁用写操作
  223. spi_writeDisable_func()
  224. -- 8.关闭SPI设备
  225. spi_close_func()
  226. end
  227. sys.taskInit(spi_test_func)