介绍
获取见文末
这是个基于odrive0.5.5源码spi驱动的改进型,首先感谢odrive的开源,于是在观看源码的我有感而据自己的理解的改进并开源,改进有:
- 在任务创建时比较是否要进行spi的配置,而不再中断里
- 采用头尾指针记录代替递归查询(顺序查询)进一步加快中断响应时间
- 针对使用os和非os提供不同的添加了可更改的宏选项
总而言之就是加了多三个变量来提高驱动在恶劣条件下的执行性能
基于HAL库使用
初始化SPI外设
本驱动程序不进行外设的初始化(最开始),故需用户自行初始化,使用cubeMX初始化spi外设及相关GPIO(包括CS GPIO)以最快使用本库
注意:本库在传输位数变化变动时会调动HAL_SPI_Msp_Init
要求,需打开中断和DMA,并正确配置,且使用软件cs控制,除非该spi直挂载了一个设备且你认为有必要
配置本库
#define USE_OS 1
//在阻塞(高实时)模式下的等待函数
#define SPI_IDLE_NONE ((void)0U)
#if USE_OS
//使用os时的非cpu阻塞(低实时)的等待函数
#define SPI_IDLE() osDelay(1)
#else
//非使用os时的低实时时的等待函数
#define SPI_IDLE() ((void)0U)
#endif
本库并无太多可配置项,仅使用一个USE_OS
区别,同时配置SPI_IDLE_NONE
和SPI_IDLE
USE_OS
在该宏为1时表示使用rtos或具有cpu调度功能的模块,会多一个配置项SPI_IDLE_NONE
,此项在传输等待时不进行线程挂起的cpu阻塞函数每调用一次 timeout会加一,达到限定值后会退出
SPI_IDLE
这个宏有两个选项,一个在os下生效(通常使用会引起系统调度的delay函数,因为不调度有另一个函数SPI_IDLE_NONE
),一个在非os下生效(通常是自己的delay函数,也可以不更改,让cpu就干等,这样实时性也是最好的)
初始化本库
SPI_Bus spi1(&hspi1);
如果使用cubemx和hal库仅仅需要这样一句创建一个对象即可,hspi1
为你想配置的spi的句柄
配置中断处理函数
将on_complete
放到hal库对应的spi cplt callback 里面,因为它会告诉本库这个任务已经完成可以开始下一个任务了(如果有)
使用它
我提供两个或者说三个函数transfer
和transfer_async
以及它需要的SpiTask
的构造函数
transfer_async
bool transfer_async(SpiTask* task);
他只有一个参数task
所以你要创建一个,所以调用它的构造函数创建一个
SpiTask(SPI_InitTypeDef* spi_cfg,
void (*_spi_cs)(bool is_high),
const uint8_t* tx_buf,
uint8_t* _rx_buf,
size_t _length,
void (*_on_complete)(void*, bool),
void* _on_complete_ctx) :
SPI_Config(spi_cfg), spi_cs(_spi_cs), tx_buf(_tx_buf), rx_buf(_rx_buf), length(_length),
on_complete(_on_complete), on_complete_ctx(_on_complete_ctx) {}
从构造函数来看大致就可以知道要哪些参数,这里只说明几个特别的
- 当你不要tx或rx时直接让它 = nullptr即可
- _spi_cs是cs引脚的控制函数,有自己编写
- spi_cfg 就是HAL库的spi配置
- _on_complete这是用户自定义的完成回调函数
- _on_complete_ctx这是用户自定义的完成回调函数输入的参数
- 回调函数在中断里完成,所以不要在里面写复杂的逻辑
- 开放它的目的是:一般一个线程(任务)只会使用一个spi及其配置项,所以,只要将task初始化为全局变量,就可以无限次使用,每次最少只需要改变
length
即可 。同时完成回调有参数也可使用户像现代代码框架一样完成更为模块安全的扩展 - 该函数是及时退出的,不会阻塞,退出不表示发送完成
transfer
这是个可阻塞的函数(当timeout为0时马上退出,但你超时后你将无法得知任务什么时候完成,所以不应该让任务超时(因为这一般是错误的),超时仅应该用于debug阶段排除故障),且它可选是等待时否线程切换
== 这个函数通常适用于仅临时调用spi发送时使用,或阻塞spi发送时使用==
原理分析
本质是创建一个个任务链接成一个单向链表,同时,每个spi外设有两个指针,一个指向链表头一个指向链表尾部(这样防止每次都从头查找,用空间换时间,保障了函数指行时间的固定)。然后传输时调用HAL库的dma传输函数,在完成中断里初始化下一个任务的spi配置,并再次调用dma传输
移植
如果你不在HAL库下使用它,那么你可能就需要进行移植,而它也十分方便
- 自己实现__SPI_CFG,它是用于两个spi任务之间spi配置不同时的配置函数
- 自己实现__SPI_TRANSMIT,他是用于spi的发送接受的,==他应该是非阻塞的
- 更改SPI_HandleTypeDef* hspix;,每一个SPI_Bus 都用一个绑定硬件的句柄或者编号,它与__SPI_TRANSMIT和__SPI_CFG都有关,也需要自己实现
- SpiTask的SPI_InitTypeDef和equals函数,它们一个是保存该任务的spi配置另一个是与上一个任务比较看要不要进行配置更改即是否会调用__SPI_CFG函数
获取它
github地址
如果你感兴趣并且想要使用它的话,请点个免费的star吧