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