Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
SPI
SPI设备驱动初始化的基础步骤
发布于 2025-02-11 11:27:12 浏览:25
订阅该版
[tocm] # 一、SPI设备驱动初始化的基础步骤 (关于RT_Thread_Studio里SPI的配置就不讲了,其实就是在组件里使能SPI组件,然后按照board.h里SPI的使用步骤定义相关宏就好了) 注意:本文是基于RT_Thread提供的drv_spi.c(stm32的)来写的,当然也可以自己写驱动。 初始化步骤一共两步:注册SPI总线;挂载SPI设备。 以下是具体实现分析: 首先要找到核心的spi驱动结构体:stm32_spi,这里面的成员可以被分成两部分:1、SPI和DMA的设置参数;2、spi总线,即struct rt_spi_bus spi_bus; ```c /* stm32 spi dirver class */ struct stm32_spi { SPI_HandleTypeDef handle; struct stm32_spi_config *config; struct rt_spi_configuration *cfg; struct { DMA_HandleTypeDef handle_rx; DMA_HandleTypeDef handle_tx; } dma; rt_uint8_t spi_dma_flag; struct rt_spi_bus spi_bus; }; ``` 1. 注册SPI总线 SPI总线的注册drv_spi.h里面已经写好了,通过INIT_BOARD_EXPORT直接调用了,代码如下: ```c int rt_hw_spi_init(void) { stm32_get_dma_info(); return rt_hw_spi_bus_init(); //SPI总线初始化 } INIT_BOARD_EXPORT(rt_hw_spi_init); //已经自动初始化 ``` rt_hw_spi_bus_init()的主要作用如下: a. 设置总线名,如spi1,spi2 b. 设置SPIx外设、DMA外设需要的初始化参数(请注意这里只是设置好参数,并没有真正初始化) c. 配置总线的mode,ops(总线配置函数spi_configure()和总线数据传输函数spixfer()),并将总线设备注册到设备框架里。 ```c struct rt_spi_bus { struct rt_device parent; rt_uint8_t mode; const struct rt_spi_ops *ops; struct rt_mutex lock; `struct rt_spi_device *owner;` }; ``` 到这里注册SPI总线已经完成。 2. 挂载SPI设备 ```c /** * Attach the spi device to SPI bus, this function must be used after initialization. */ rt_err_t rt_hw_spi_device_attach(const char *bus_name, const char *device_name, GPIO_TypeDef *cs_gpiox, uint16_t cs_gpio_pin) { RT_ASSERT(bus_name != RT_NULL); RT_ASSERT(device_name != RT_NULL); rt_err_t result; struct rt_spi_device *spi_device; struct stm32_hw_spi_cs *cs_pin; /* initialize the cs pin && select the slave*/ GPIO_InitTypeDef GPIO_Initure; GPIO_Initure.Pin = cs_gpio_pin; GPIO_Initure.Mode = GPIO_MODE_OUTPUT_PP; GPIO_Initure.Pull = GPIO_PULLUP; GPIO_Initure.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(cs_gpiox, &GPIO_Initure); HAL_GPIO_WritePin(cs_gpiox, cs_gpio_pin, GPIO_PIN_SET); /* attach the device to spi bus*/ spi_device = (struct rt_spi_device *)rt_malloc(sizeof(struct rt_spi_device)); RT_ASSERT(spi_device != RT_NULL); cs_pin = (struct stm32_hw_spi_cs *)rt_malloc(sizeof(struct stm32_hw_spi_cs)); RT_ASSERT(cs_pin != RT_NULL); cs_pin->GPIOx = cs_gpiox; cs_pin->GPIO_Pin = cs_gpio_pin; result = rt_spi_bus_attach_device(spi_device, device_name, bus_name, (void *)cs_pin); if (result != RT_EOK) { LOG_E("%s attach to %s faild, %d\n", device_name, bus_name, result); } RT_ASSERT(result == RT_EOK); LOG_D("%s attach to %s done", device_name, bus_name); return result; } ``` 挂载SPI设备的函数是rt_hw_spi_device_attach(),这个函数作用是实例化SPI设备(有几个需要通信的从设备就调用几次),输入参数是:SPI设备名,SPI设备的片选引脚,和你希望把它挂载在的那条总线的名字。 可以多次使用rt_hw_spi_device_attach()来在一条总线上挂载多个设备(在我的理解里,因为通常我们是主机,所以这个“设备”指的是各个从设备) 挂载之后我们需要通过rt_spi_configure()对每个spi设备配置其通信参数(这时候并没有对SPI外设进行初始化)。 ```c rt_err_t rt_spi_configure(struct rt_spi_device *device, struct rt_spi_configuration *cfg) { rt_err_t result; RT_ASSERT(device != RT_NULL); /* set configuration */ device->config.data_width = cfg->data_width; device->config.mode = cfg->mode & RT_SPI_MODE_MASK ; device->config.max_hz = cfg->max_hz ; if (device->bus != RT_NULL) { result = rt_mutex_take(&(device->bus->lock), RT_WAITING_FOREVER); if (result == RT_EOK) { if (device->bus->owner == device) { device->bus->ops->configure(device, &device->config); } /* release lock */ rt_mutex_release(&(device->bus->lock)); } } return RT_EOK; } ``` 挂载完SPI设备并配置spi设备之后,就可以调用下面这些接口选择一个SPI设备进行通信了,使用时须输入SPI设备名。 ```c rt_err_t rt_spi_send_then_send(struct rt_spi_device *device, const void *send_buf1, rt_size_t send_length1, const void *send_buf2, rt_size_t send_length2); rt_err_t rt_spi_send_then_recv(struct rt_spi_device *device, const void *send_buf, rt_size_t send_length, void *recv_buf, rt_size_t recv_length); rt_size_t rt_spi_transfer(struct rt_spi_device *device, const void *send_buf, void *recv_buf, rt_size_t length); struct rt_spi_message *rt_spi_transfer_message(struct rt_spi_device *device, struct rt_spi_message *message); ``` 接口内会自动检测,输入设备是否是当前正在通信的设备,如果不是,则会对SPI、DMA外设进行初始化。我们可以看到在struct rt_spi_bus,总线结构体中存在成员owner,这个指针会指向当前我们正在使用或者说正在占有总线的SPI设备,初始时owner指向RT_NULL。 ```c struct rt_spi_device *owner; ``` 除了上面这些通信传输接口外还有如下接口: ```c rt_err_t rt_spi_take(struct rt_spi_device *device); rt_err_t rt_spi_release(struct rt_spi_device *device); ``` 这两个接口rt_spi_take()用于片选SPI设备,作为强制选中,rt_spi_release()用于释放SPI设备,(通常在调用传输接口时,其内部会在传输开始时片选选中从设备,传输结束后释放从设备) ```c rt_err_t rt_spi_take_bus(struct rt_spi_device *device); rt_err_t rt_spi_release_bus(struct rt_spi_device *device); ``` 这两个接口比较特殊,rt_spi_take_bus()这个接口会使对应设备强制占用总线,在rt_spi_release_bus()前,不允许其他设备获取总线。 当然,由于在物理上各个从设备实际上挂载在一条总线上,只要通过rt_spi_take()片选选中其他从设备,那么其他从设备可能收到消息,但这样是不合适的,因为不同SPI设备的配置可能不同。 除非你需要广播给配置相同的SPI设备,否则不要这样做。
0
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
Heven
这家伙很懒,什么也没写!
文章
1
回答
0
被采纳
0
关注TA
发私信
相关文章
1
BBB的SPI驱动
2
求个SPI上挂两个或多个设备的使用例子
3
SPI设备有个bug
4
spi flash 的fatfs使用一段时间后读写文件出现故障
5
SPI驱动
6
请教rt_spi_configure函数理解
7
SPI FLASH挂载的问题
8
SPI-FLASH 文件系统 SPIFFS
9
求助一个完整的 spi flash 驱动
10
关于同时使用文件系统与SPI FLASH的问题
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
DMA
USB
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
I2C_IIC
ESP8266
UART
WIZnet_W5500
ota在线升级
cubemx
PWM
flash
freemodbus
BSP
packages_软件包
潘多拉开发板_Pandora
定时器
ADC
flashDB
GD32
socket
编译报错
中断
Debug
rt_mq_消息队列_msg_queue
keil_MDK
SFUD
msh
ulog
C++_cpp
MicroPython
本月问答贡献
三世执戟
5
个答案
1
次被采纳
KunYi
4
个答案
1
次被采纳
RTT_逍遥
3
个答案
1
次被采纳
xiaorui
1
个答案
1
次被采纳
JonasWen
1
个答案
1
次被采纳
本月文章贡献
出出啊
1
篇文章
3
次点赞
小小李sunny
1
篇文章
1
次点赞
张世争
1
篇文章
3
次点赞
crystal266
2
篇文章
2
次点赞
whj467467222
2
篇文章
2
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部