README_zh.md
1# Niobe407开发板OpenHarmony基于HDF驱动框架编程开发——SPI
2本示例将演示如何在Niobe407开发板上通过HDF驱动框架,使用SPI接口读写外部SPI-FLASH。
3
4
5## 编译调试
6- 进入//kernel/liteos_m目录, 在menuconfig配置中进入如下选项:
7
8 `(Top) → Platform → Board Selection → select board niobe407 → use talkweb niobe407 application → niobe407 application choose`
9
10- 选择 `202_hdf_spi_flash`
11
12- 在menuconfig的`(Top) → Driver`选项中使能如下配置:
13
14```
15 [*] Enable Driver
16 [*] HDF driver framework support
17 [*] Enable HDF platform driver
18 [*] Enable HDF platform spi driver
19```
20- 回到sdk根目录,执行`hb build -f`脚本进行编译。
21
22### 运行结果
23
24示例代码编译烧录代码后,按下开发板的RESET按键,通过串口助手查看日志
25```
26[HDF:I/HDF_LOG_TAG]spi 0 open success 0x40003800
27[HDF:I/HDF_LOG_TAG]read device id is 0x17
28[HDF:I/HDF_LOG_TAG]read flash id is 0x4018
29[HDF:I/HDF_LOG_TAG]send buffer is welcome to OpenHarmony
30[HDF:I/HDF_LOG_TAG]recv send buffer is welcome to OpenHarmony
31[HDF:I/HDF_LOG_TAG]hdf spi write read flash success
32[HDF:I/HDF_LOG_TAG]spi 0 close success 0x40003800
33```
34
35## SPI HDF HCS配置文件解析
36
37- device_spi_info.hcs 在/device/board/talkweb/niobe407/sdk/hdf_config/device_spi_info.hcs
38```
39root {
40 module = "talkweb,stm32f407";
41 device_info {
42 match_attr = "hdf_manager";
43 template host {
44 hostName = "";
45 priority = 100;
46 template device {
47 template deviceNode {
48 policy = 0;
49 priority = 100;
50 preload = 0;
51 permission = 0664;
52 moduleName = "";
53 serviceName = "";
54 deviceMatchAttr = "";
55 }
56 }
57 }
58 platform :: host {
59 hostName = "platform_host";
60 device_spi :: device {
61 spi0 :: deviceNode {
62 policy = 2;
63 priority = 100; // 因为spi的gpio引脚要在spi初始化之前初始化,所以优先级比gpio的hdf优先级低
64 moduleName = "STM_TW_SPI_MODULE_HDF"; //驱动名称,该字段的值必须和驱动入口结构的moduleName值一致
65 serviceName = "HDF_PLATFORM_SPI_0";
66 deviceMatchAttr = "spi_w25q_config"; // 对应hdf_spi.hcs中的config
67 }
68 }
69 }
70 }
71}
72```
73- hdf_spi.hcs在在/device/board/talkweb/niobe407/sdk/hdf_config/hdf_spi.hcs,主要包括spi设置的信息的配置
74
75```
76#include "device_spi_info.hcs"
77root {
78 platform {
79 spi1_config {
80 spi1_gpio {
81 // 要配置的引脚个数,接下来的引脚名必须定义成gpio_num_1, gpio_num_2, gpio_num_3...
82 gpio_num_max = 4;
83 // port, pin, mode, speed, outputType, pull, alternate
84 gpio_num_1 = [0, 5, 2, 3, 0, 0, 5]; // clk pa5
85 gpio_num_2 = [1, 5, 2, 3, 0, 0, 5]; // mosi pb5
86 gpio_num_3 = [1, 4, 2, 3, 0, 0, 5]; // miso pb4
87 gpio_num_4 = [0, 15, 1, 3, 0, 0 ,0]; // cs pa15
88 }
89 spi_config : spi1_gpio {
90 match_attr = "spi_w25q_config";
91 busNum = 0; // 注册的总线号
92 csNum = 0; // 片选号
93 transDir = 0; // 0: TW_HAL_SPI_FULL_DUPLEX 1: TW_HAL_SPI_SIMPLEX_RX 2: TW_HAL_SPI_HALF_DUPLEX_RX 3: TW_HAL_SPI_HALF_DUPLEX_TX
94 transMode = 1; // 1: normal 0:dma
95 smMode = 1; // 0: slave 1: master
96 dataWidth = 0; // 0:8bit 1:16bit
97 clkMode = 0; // 0: cpol 0 cpha 0 1:CPOL = 1; CPHA = 0 2:CPOL = 0; CPHA = 1 3:CPOL = 1; CPHA = 1
98 nss = 0; // 0:NSS SOFT 1: NSS HARDWARE INPUT 2: NSS HARDWARE OUTPUT
99 baudRate = 1; // 0:div2 1:div4 2:div8 3:div16 4:div32 5:div64 6:div128 6:div256
100 bitOrder = 0; // 0: MSB first 1: LSB first
101 crcEnable = 0; // 0: crc disable 1: crc enable
102 crcPoly = 10; // Min_Data = 0x00 and Max_Data = 0xFFFF
103 spix = 0; // 0: spi1 1: spi2 2:spi3 本例程使用SPI1作为示例
104 csPin = 15; // cs pin
105 csGpiox = 0; // cs pin group
106 standard = 0; // 0:motorola 1: ti
107 dummyByte = 255;
108 }
109 }
110 }
111}
112```
113
114## 接口解析
115```
1161.在使用SPI进行通信时,首先要调用SpiOpen获取SPI设备句柄,该函数会返回指定总线号和片选号的SPI设备句柄。
117DevHandle SpiOpen(const struct SpiDevInfo *info);
118参数说明:
119 info: SPI设备描述符, 设置了总线号和片选号
120返回值:
121 NULL: 打开失败
122 设备句柄:打开成功
123
1242.在获取到SPI设备句柄之后,需要配置SPI设备属性。配置SPI设备属性之前,可以先获取SPI设备属性,获取SPI设备属性的函数如下所示:
125int32_t SpiGetCfg(DevHandle handle, struct SpiCfg *cfg);
126参数说明:
127 handle: 设备句柄
128 cfg: 设置的spi的设置
129返回值:
130 0: 设置成功
131 负数:设置失败
1323.如果需要发起一次自定义传输,则可以通过以下函数完成:
133int32_t SpiTransfer(DevHandle handle, struct SpiMsg *msgs, uint32_t count);
134参数说明:
135 handle: 设备句柄
136 msgs: 待传输数据的数组
137 count: 待传输的数组的长度
138返回值:
139 0: 执行成功
140 负数:执行失败
1414.SPI通信完成之后,需要销毁SPI设备句柄,销毁SPI设备句柄的函数如下所示:
142void SpiClose(DevHandle handle);
143参数说明:
144 handle: 设备句柄
145```
146
147### 示例代码解析
148- 开启spi,参数获取和设置示例
149``` c
150static void* HdfSpiTestEntry(void* arg)
151{
152 int32_t ret;
153 uint16_t flashId = 0;
154 uint16_t deviceId = 0;
155 struct SpiCfg cfg; /* SPI配置信息 */
156 struct SpiDevInfo spiDevinfo; /* SPI设备描述符 */
157 DevHandle spiHandle = NULL; /* SPI设备句柄 */
158 spiDevinfo.busNum = 0; /* SPI设备总线号 */
159 spiDevinfo.csNum = 0; /* SPI设备片选号 */
160 spiHandle = SpiOpen(&spiDevinfo); /* 根据spiDevinfo获取SPI设备句柄 */
161 if (spiHandle == NULL) {
162 HDF_LOGE("SpiOpen: failed\n");
163 return;
164 }
165 /* 获取SPI设备属性 */
166 ret = SpiGetCfg(spiHandle, &cfg);
167 if (ret != 0) {
168 HDF_LOGE("SpiGetCfg: failed, ret %d\n", ret);
169 goto err;
170 }
171 HDF_LOGI("speed:%d, bitper:%d, mode:%d, transMode:%d\n", cfg.maxSpeedHz, cfg.bitsPerWord, cfg.mode, cfg.transferMode);
172 cfg.maxSpeedHz = 1; /* spi2,spi3 最大频率为42M, spi1 频率为84M, 此处的值为分频系数,0:1/2 1:1/4, 2:1/8 . */
173 /* 3: 1/16, 4: 1/32 5:1/64, 6:1/128 7:1/256 */
174 cfg.bitsPerWord = 8; /* 传输位宽改为8比特 */
175 cfg.mode = 0;
176 cfg.transferMode = 1; /* 0:dma 1:normal */
177 /* 配置SPI设备属性 */
178 ret = SpiSetCfg(spiHandle, &cfg);
179 if (ret != 0) {
180 HDF_LOGE("SpiSetCfg: failed, ret %d\n", ret);
181 goto err;
182 }
183 SpiClose(spiHandle);
184
185```
186- spi消息传输示例使用SpiTransfer
187``` c
188static uint16_t ReadDeviceId(DevHandle spiHandle)
189{
190 struct SpiMsg msg; /* 自定义传输的消息 */
191 uint16_t deviceId = 0;
192 uint8_t rbuff[5] = { 0 };
193 uint8_t wbuff[5] = { 0xAB, 0xFF, 0xFF, 0xFF, 0xFF }; // oxab为读DeviceId 指令,其他为dummyData
194 int32_t ret = 0;
195 msg.wbuf = wbuff; /* 写入的数据 */
196 msg.rbuf = rbuff; /* 读取的数据 */
197 msg.len = 5; /* 读取写入数据的长度为4 */
198 msg.keepCs = 0; /* 进行下一次传输前关闭片选 */
199 msg.delayUs = 0; /* 进行下一次传输前不进行延时 */
200 //msg.speed = 115200; /* 本次传输的速度 */
201 /* 进行一次自定义传输,传输的msg个数为1 */
202 ret = SpiTransfer(spiHandle, &msg, 1);
203 if (ret != 0) {
204 HDF_LOGE("SpiTransfer: failed, ret %d\n", ret);
205 } else {
206 deviceId = rbuff[4]; // 最后一次发送oxff返回deviceid
207 }
208
209 return deviceId;
210}
211
212static void BufferWrite(DevHandle spiHandle, const uint8_t* buf, uint32_t size)
213{
214 WriteEnable(spiHandle); //使能spi写
215 uint8_t wbuf[4] = {0x02, 0x00, 0x00, 0x00}; // 0x02 写指令write buf头,0x00, 0x00, 0x00 三个为6位写的地址
216 uint8_t rbuf[4] = {0};
217 uint8_t *rbuf1 = NULL;
218 int32_t ret = 0;
219
220 struct SpiMsg msg = {0};
221 msg.wbuf = wbuf;
222 msg.rbuf = rbuf;
223 msg.len = 4;
224 msg.keepCs = 1; // 写指令发送后不关闭片选
225 msg.delayUs = 0;
226 ret = SpiTransfer(spiHandle, &msg, 1);
227 if (ret != 0) {
228 HDF_LOGE("SpiTransfer: failed, ret %d\n", ret);
229 }
230
231 rbuf1 = (uint8_t*)OsalMemAlloc(size);
232 memset_s(rbuf1, size, 0, size);
233 msg.wbuf = buf;
234 msg.rbuf = rbuf1;
235 msg.len = size;
236 msg.keepCs = 0; // 写完内容后关闭片选
237 msg.delayUs = 0;
238 ret = SpiTransfer(spiHandle, &msg, 1); //传输真正书写的内容
239 if (ret != 0) {
240 HDF_LOGE("SpiTransfer: failed, ret %d\n", ret);
241 }
242
243 WaitForWriteEnd(spiHandle); // 等待书写完成
244 if (rbuf1!= NULL) {
245 OsalMemFree(rbuf1);
246 }
247
248}
249
250static void BufferRead(DevHandle spiHandle, uint8_t* buf, uint32_t size)
251{
252 int32_t ret = 0;
253
254 uint8_t wbuf[4] = {0x03, 0x00, 0x00, 0x00}; // 0x03 读指令, 后三个0x00 为地址
255 uint8_t rbuf[4] = {0};
256 struct SpiMsg msg = {0};
257 msg.wbuf = wbuf;
258 msg.rbuf = rbuf;
259 msg.len = 4;
260 msg.keepCs = 1; // 写使能不关闭片选
261 msg.delayUs = 0;
262 ret = SpiTransfer(spiHandle, &msg, 1);
263 if (ret != 0) {
264 HDF_LOGE("SpiTransfer: failed, ret %d\n", ret);
265 return;
266 }
267 uint8_t *wbuf1 = NULL;
268 wbuf1 = (uint8_t*)OsalMemAlloc(size);
269 memset_s(wbuf1, size, 0xff, size);
270 msg.wbuf = wbuf1;
271 msg.rbuf = buf;
272 msg.len = size;
273 msg.keepCs = 0;
274 msg.delayUs = 0;
275 ret = SpiTransfer(spiHandle, &msg, 1); // 读取spi flash中存储的值
276 if (ret != 0) {
277 HDF_LOGE("SpiTransfer: failed, ret %d\n", ret);
278 return;
279 }
280
281 if (wbuf1!= NULL) {
282 OsalMemFree(wbuf1);
283 }
284
285}
286```
287
288- 使用SpiWrite, SpiRead通信,由于使用这两个接口无法制定是否关闭片选,因此在写或者读得buf第一个字节为自定义关闭片选信号,0不关闭片选, 1关闭片选,读取内容时要注意去掉第一个字节
289``` c
290static uint16_t ReadDeviceId(DevHandle spiHandle)
291{
292 struct SpiMsg msg; /* 自定义传输的消息 */
293 uint16_t deviceId = 0;
294 uint8_t rbuff1[2] = { 0 };
295 uint8_t wbuff1[5] = {0x00, 0xAB, 0xff,0xff, 0xff}; //0x00 不关闭片选,0xab读取设备指令,0xff为dummydata
296 int32_t ret = 0;
297
298 /* 发送命令:读取芯片型号ID */
299 ret =SpiWrite(spiHandle, wbuff1, 5);
300 if (ret != 0) {
301 HDF_LOGE("SpiWrite: failed, ret %d\n", ret);
302 }
303 rbuff1[0] = 0x01; //关闭片选
304 ret = SpiRead(spiHandle, rbuff1, 2);
305 if (ret != 0) {
306 HDF_LOGE("SpiWrite: failed, ret %d\n", ret);
307 }
308
309 deviceId = rbuff1[1];
310
311 return deviceId;
312}
313
314static void BufferWrite(DevHandle spiHandle, const uint8_t* buf, uint32_t size)
315{
316 WriteEnable(spiHandle);
317 uint8_t wbuf[5] = {0x01, 0x02, 0x00, 0x00, 0x00};// 0x01 关闭片选, 0x02写指令,后面3个0x00为地址
318 uint8_t rbuf[4] = {0};
319 uint8_t *wbuf1 = NULL;
320 int32_t ret = 0;
321 wbuf1 = (uint8_t*)OsalMemAlloc(size + sizeof(wbuf));
322
323 strncpy_s(wbuf1, size + sizeof(wbuf), wbuf, sizeof(wbuf));
324 strncpy_s(wbuf1 + sizeof(wbuf), size, buf, size);
325 ret = SpiWrite(spiHandle, wbuf1, size + sizeof(wbuf)); //将写写指令和内容一起写入spi flash
326 if (ret != 0) {
327 HDF_LOGE("SpiWrite: failed, ret %d\n", ret);
328 }
329
330 WaitForWriteEnd(spiHandle);
331 if (wbuf1!= NULL) {
332 OsalMemFree(wbuf1);
333 }
334
335}
336
337static void BufferRead(DevHandle spiHandle, uint8_t* buf, uint32_t size)
338{
339 WriteEnable(spiHandle);
340 uint8_t wbuf[5] = {0x00, 0x03, 0x00, 0x00, 0x00};// 0x00 不关闭片选, 0x03读指令,后面3个0x00为地址
341 uint8_t *rbuf = NULL;
342 int32_t ret = 0;
343 rbuf = (uint8_t*)OsalMemAlloc(size + 1);
344
345 ret = SpiWrite(spiHandle, wbuf, 5);// 先写读指令和地址
346 if (ret != 0) {
347 HDF_LOGE("SpiWrite: failed, ret %d\n", ret);
348 }
349 rbuf[0] = 0x01; // 读完关闭片选
350 ret = SpiRead(spiHandle, rbuf, size + 1); // 读取内容
351 if (ret != 0) {
352 HDF_LOGE("SpiRead: failed, ret %d\n", ret);
353 }
354
355 strncpy_s(buf, size, rbuf + 1, size);
356
357 if (rbuf!= NULL) {
358 OsalMemFree(rbuf);
359 }
360
361 return;
362```
363