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