• Home
Name Date Size #Lines LOC

..--

BUILD.gnD12-May-20241.2 KiB3328

README_zh.mdD12-May-202412.6 KiB363324

hdf_spi_test.cD12-May-202412.8 KiB485423

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