test_spi.c 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. /*
  2. * Copyright (c) 2022-2024, Xiaohua Semiconductor Co., Ltd.
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. *
  6. * Change Logs:
  7. * Date Author Notes
  8. * 2024-12-30 CDT first version
  9. */
  10. /*
  11. * 程序清单:这是一个 SPI 设备使用例程
  12. * 例程导出了 spi_w25q_sample 命令到控制终端
  13. * 命令调用格式:spi_w25q_sample spix0
  14. * 命令解释:命令第二个参数是要使用的SPI设备名称,为空则使用默认的SPI设备
  15. * 程序功能:通过SPI设备读取 w25q 的 ID 数据
  16. */
  17. #include <rtthread.h>
  18. #include <rtdevice.h>
  19. #include "board_config.h"
  20. #include <string.h>
  21. #if defined(BSP_USING_SPI)
  22. #include "drv_spi.h"
  23. #define W25Q_FLAG_BUSY (0x01)
  24. #define W25Q_WR_ENABLE (0x06)
  25. #define W25Q_SECTOR_ERASE (0x20)
  26. #define W25Q_RD_STATUS_REG1 (0x05)
  27. #define W25Q_PAGE_PROGRAM (0x02)
  28. #define W25Q_STD_RD (0x03)
  29. #define W25Q_PAGE_SIZE (256UL)
  30. #define W25Q_SECTOR_SIZE (1024UL * 4UL)
  31. #define W25Q_PAGE_PER_SECTOR (W25Q_SECTOR_SIZE / W25Q_PAGE_SIZE)
  32. #define W25Q_MAX_ADDR (0x800000UL)
  33. #define W25Q_SPI_WR_RD_ADDR 0x4000
  34. #define W25Q_SPI_DATA_BUF_LEN 0x2000
  35. #if defined(HC32F4A0) || defined(HC32F448) || defined(HC32F4A8)
  36. #define SPI_CS_PORT SPI1_CS_PORT
  37. #define SPI_CS_PIN SPI1_CS_PIN
  38. #define SPI_CS_PORT_PIN GET_PIN(C, 7)
  39. #define W25Q_SPI_BUS_NAME "spi1"
  40. #define W25Q_SPI_DEVICE_NAME "spi10"
  41. #elif defined(HC32F472)
  42. #define SPI_CS_PORT SPI1_CS_PORT
  43. #define SPI_CS_PIN SPI1_CS_PIN
  44. #define SPI_CS_PORT_PIN GET_PIN(B, 12)
  45. #define W25Q_SPI_BUS_NAME "spi1"
  46. #define W25Q_SPI_DEVICE_NAME "spi10"
  47. #elif defined(HC32F460)
  48. #define SPI_CS_PORT SPI3_CS_PORT
  49. #define SPI_CS_PIN SPI3_CS_PIN
  50. #define SPI_CS_PORT_PIN GET_PIN(C, 7)
  51. #define W25Q_SPI_BUS_NAME "spi3"
  52. #define W25Q_SPI_DEVICE_NAME "spi30"
  53. #endif
  54. struct rt_spi_device *spi_dev_w25q; /* SPI 设备句柄 */
  55. static uint8_t u8WrBuf[W25Q_SPI_DATA_BUF_LEN];
  56. static uint8_t u8RdBuf[W25Q_SPI_DATA_BUF_LEN];
  57. static int rt_hw_spi_flash_init(void)
  58. {
  59. if (RT_EOK != rt_hw_spi_device_attach(W25Q_SPI_BUS_NAME, W25Q_SPI_DEVICE_NAME, SPI_CS_PORT_PIN))
  60. {
  61. rt_kprintf("Failed to attach the spi device.");
  62. return -RT_ERROR;
  63. }
  64. return RT_EOK;
  65. }
  66. /* 导出到自动初始化 */
  67. INIT_COMPONENT_EXPORT(rt_hw_spi_flash_init);
  68. void w25q_read_uid(struct rt_spi_device *device)
  69. {
  70. rt_uint8_t w25x_read_uid = 0x4B; /* 命令 */
  71. rt_uint8_t u8UID[8] = {0};
  72. rt_uint8_t txBuf[5] = {0};
  73. memset(txBuf, 0xFF, 5);
  74. txBuf[0] = w25x_read_uid;
  75. /* 方式1:使用 rt_spi_send_then_recv()发送命令读取ID */
  76. if (RT_EOK != rt_spi_send_then_recv(device, txBuf, 5, u8UID, 8))
  77. {
  78. rt_kprintf("spi get uid failed!\n");
  79. }
  80. else
  81. {
  82. rt_kprintf("w25q UID is: %02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x\r\n",
  83. u8UID[0], u8UID[1], u8UID[2], u8UID[3], u8UID[4], u8UID[5], u8UID[6], u8UID[7]);
  84. }
  85. /* 方式2:使用 rt_spi_transfer_message()发送命令读取ID */
  86. struct rt_spi_message msg1, msg2;
  87. msg1.send_buf = txBuf;
  88. msg1.recv_buf = RT_NULL;
  89. msg1.length = 5;
  90. msg1.cs_take = 1;
  91. msg1.cs_release = 0;
  92. msg1.next = &msg2;
  93. msg2.send_buf = RT_NULL;
  94. msg2.recv_buf = u8UID;
  95. msg2.length = 8;
  96. msg2.cs_take = 0;
  97. msg2.cs_release = 1;
  98. msg2.next = RT_NULL;
  99. rt_spi_transfer_message(device, &msg1);
  100. rt_kprintf("use rt_spi_transfer_message() read w25q ID is: UID is: %02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x\r\n",
  101. u8UID[0], u8UID[1], u8UID[2], u8UID[3], u8UID[4], u8UID[5], u8UID[6], u8UID[7]);
  102. }
  103. int32_t w25q_check_process_done(struct rt_spi_device *device, uint32_t u32Timeout)
  104. {
  105. __IO uint32_t u32Count = 0U;
  106. int32_t i32Ret = LL_ERR_TIMEOUT;
  107. rt_uint8_t rxBuf[5] = {0};
  108. rt_uint8_t txBuf[5] = {0};
  109. txBuf[0] = W25Q_RD_STATUS_REG1;
  110. while (u32Count < u32Timeout)
  111. {
  112. if (RT_EOK != rt_spi_send_then_recv(device, txBuf, 1, rxBuf, 1))
  113. {
  114. rt_kprintf("spi get SR failed!\n");
  115. }
  116. else
  117. {
  118. if (W25Q_FLAG_BUSY != (rxBuf[0] & W25Q_FLAG_BUSY))
  119. {
  120. i32Ret = LL_OK;
  121. break;
  122. }
  123. }
  124. rt_thread_mdelay(1);
  125. u32Count++;
  126. }
  127. return i32Ret;
  128. }
  129. int32_t w25q_read_data(struct rt_spi_device *device, uint32_t u32Addr, uint8_t *pu8ReadBuf, uint32_t u32Size)
  130. {
  131. int32_t i32Ret = LL_OK;
  132. rt_uint8_t txBuf[5] = {0};
  133. txBuf[0] = W25Q_STD_RD;
  134. txBuf[1] = (u32Addr >> 16) & 0xFFU;
  135. txBuf[2] = (u32Addr >> 8) & 0xFFU;
  136. txBuf[3] = u32Addr & 0xFFU;
  137. if (RT_EOK != rt_spi_send_then_recv(device, txBuf, 4, pu8ReadBuf, u32Size))
  138. {
  139. i32Ret = LL_ERR;
  140. }
  141. return i32Ret;
  142. }
  143. int32_t w25q_write_data(struct rt_spi_device *device, uint32_t u32Addr, uint8_t *pu8WriteBuf, uint32_t u32Size)
  144. {
  145. int32_t i32Ret = LL_OK;
  146. uint32_t u32TempSize, u32AddrOffset = 0U;
  147. uint8_t w25q_txBuf[W25Q_PAGE_SIZE + 10];
  148. if ((u32Addr % W25Q_PAGE_SIZE) != 0U)
  149. {
  150. return LL_ERR_INVD_PARAM;
  151. }
  152. while (u32Size != 0UL)
  153. {
  154. if (u32Size >= W25Q_PAGE_SIZE)
  155. {
  156. u32TempSize = W25Q_PAGE_SIZE;
  157. }
  158. else
  159. {
  160. u32TempSize = u32Size;
  161. }
  162. w25q_txBuf[0] = W25Q_WR_ENABLE;
  163. if (1 != rt_spi_send(device, w25q_txBuf, 1))
  164. {
  165. rt_kprintf("spi send cmd failed!\n");
  166. }
  167. w25q_txBuf[0] = W25Q_PAGE_PROGRAM;
  168. w25q_txBuf[1] = (u32Addr >> 16) & 0xFFU;
  169. w25q_txBuf[2] = (u32Addr >> 8) & 0xFFU;
  170. w25q_txBuf[3] = u32Addr & 0xFFU;
  171. memcpy(&w25q_txBuf[4], &pu8WriteBuf[u32AddrOffset], u32TempSize);
  172. if ((u32TempSize + 4) != rt_spi_send(device, w25q_txBuf, u32TempSize + 4))
  173. {
  174. rt_kprintf("spi send addr failed!\n");
  175. }
  176. i32Ret = w25q_check_process_done(device, 500U);
  177. if (i32Ret != LL_OK)
  178. {
  179. break;
  180. }
  181. u32Addr += u32TempSize;
  182. u32AddrOffset += u32TempSize;
  183. u32Size -= u32TempSize;
  184. }
  185. return i32Ret;
  186. }
  187. int32_t w25q_erase_sector(struct rt_spi_device *device, uint32_t u32Addr, uint32_t u32Size)
  188. {
  189. uint8_t txBuf[10];
  190. uint32_t u32SectorNum, u32Cnt;
  191. int32_t i32Ret = LL_OK;
  192. if ((u32Addr % W25Q_SECTOR_SIZE) != 0U)
  193. {
  194. return LL_ERR_INVD_PARAM;
  195. }
  196. u32SectorNum = u32Size / W25Q_SECTOR_SIZE;
  197. if ((u32Size % W25Q_SECTOR_SIZE) != 0U)
  198. {
  199. u32SectorNum += 1;
  200. }
  201. for (u32Cnt = 0; u32Cnt < u32SectorNum; u32Cnt++)
  202. {
  203. txBuf[0] = W25Q_WR_ENABLE;
  204. if (1 != rt_spi_send(device, txBuf, 1))
  205. {
  206. rt_kprintf("spi send cmd failed!\n");
  207. }
  208. txBuf[0] = W25Q_SECTOR_ERASE;
  209. txBuf[1] = (u32Addr >> 16) & 0xFFU;
  210. txBuf[2] = (u32Addr >> 8) & 0xFFU;
  211. txBuf[3] = u32Addr & 0xFFU;
  212. if (4 != rt_spi_send(device, txBuf, 4))
  213. {
  214. rt_kprintf("spi send addr failed!\n");
  215. }
  216. if (LL_OK != w25q_check_process_done(device, 500U))
  217. {
  218. i32Ret = LL_ERR;
  219. break;
  220. }
  221. u32Addr += W25Q_SECTOR_SIZE;
  222. }
  223. return i32Ret;
  224. }
  225. void w25q_write_read_data(struct rt_spi_device *device, uint32_t u32Addr)
  226. {
  227. uint32_t u32Cnt;
  228. for (u32Cnt = 0; u32Cnt < W25Q_SPI_DATA_BUF_LEN; u32Cnt++)
  229. {
  230. u8WrBuf[u32Cnt] = u32Cnt & 0xFFUL;
  231. u8RdBuf[u32Cnt] = 0U;
  232. }
  233. if (LL_OK != w25q_erase_sector(device, u32Addr, W25Q_SPI_DATA_BUF_LEN))
  234. {
  235. rt_kprintf("spi erase sector failed!\n");
  236. }
  237. if (LL_OK != w25q_write_data(device, u32Addr, u8WrBuf, W25Q_SPI_DATA_BUF_LEN))
  238. {
  239. rt_kprintf("spi write data failed!\n");
  240. }
  241. if (LL_OK != w25q_read_data(device, u32Addr, u8RdBuf, W25Q_SPI_DATA_BUF_LEN))
  242. {
  243. rt_kprintf("spi read data failed!\n");
  244. }
  245. if (memcmp(u8WrBuf, u8RdBuf, W25Q_SPI_DATA_BUF_LEN) == 0)
  246. {
  247. rt_kprintf("spi write and read test ok! addr=%06x\n", u32Addr);
  248. }
  249. else
  250. {
  251. rt_kprintf("spi write and read failed!\n");
  252. }
  253. }
  254. static void spi_thread_entry(void *parameter)
  255. {
  256. uint32_t u32Addr = W25Q_SPI_WR_RD_ADDR;
  257. struct rt_spi_configuration cfg;
  258. cfg.data_width = 8;
  259. cfg.mode = RT_SPI_MASTER | RT_SPI_MODE_0 | RT_SPI_MSB;
  260. cfg.max_hz = 80 * 1000 * 1000; /* 80M */
  261. rt_spi_configure(spi_dev_w25q, &cfg);
  262. /* 读取UID */
  263. w25q_read_uid(spi_dev_w25q);
  264. while (1)
  265. {
  266. /* 读写对比数据 */
  267. w25q_write_read_data(spi_dev_w25q, u32Addr);
  268. u32Addr += W25Q_SPI_DATA_BUF_LEN;
  269. if (u32Addr >= (W25Q_MAX_ADDR - W25Q_SPI_DATA_BUF_LEN))
  270. {
  271. u32Addr = W25Q_SPI_WR_RD_ADDR;
  272. }
  273. rt_thread_mdelay(500);
  274. }
  275. }
  276. static void spi_w25q_sample(int argc, char *argv[])
  277. {
  278. char name[RT_NAME_MAX];
  279. if (argc == 2)
  280. {
  281. rt_strncpy(name, argv[1], RT_NAME_MAX);
  282. }
  283. else
  284. {
  285. rt_strncpy(name, W25Q_SPI_DEVICE_NAME, RT_NAME_MAX);
  286. }
  287. /* 查找 spi 设备获取设备句柄 */
  288. spi_dev_w25q = (struct rt_spi_device *)rt_device_find(name);
  289. if (!spi_dev_w25q)
  290. {
  291. rt_kprintf("spi sample run failed! can't find %s device!\n", name);
  292. }
  293. else
  294. {
  295. /* 创建 线程 */
  296. rt_thread_t thread = rt_thread_create("spi", spi_thread_entry, RT_NULL, 2048, 15, 10);
  297. /* 创建成功则启动线程 */
  298. if (thread != RT_NULL)
  299. {
  300. rt_thread_startup(thread);
  301. }
  302. }
  303. }
  304. /* 导出到 msh 命令列表中 */
  305. MSH_CMD_EXPORT(spi_w25q_sample, spi w25q sample);
  306. #endif