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