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