STM32 HAL库函数HAL_SPI_Receive_IT和HAL_SPI_Receive的区别

背景

前段时间开发一个按键板驱动,该板用的STM32F103系列单片机,前任工程师用STM32CubeMX生成的工程,里面全是HAL库调用,我接手后,学习了下HAL库的用法,踩坑不少,特别是带IT后缀的函数,初学者对其的理解很容易出错,特此记录一下。
STM32 HAL库函数HAL_SPI_Receive_IT和HAL_SPI_Receive的区别_第1张图片

项目中的按键板通过SPI总线与主板连接,按键板是Slave设备,因此无法确定什么时候收到主板的读写请求,要么轮询SPI控制器的rx fifo是否非空,要么依赖SPI控制器提供的中断机制,在中断里将rx fifo内容读出来。
STM32 HAL库函数HAL_SPI_Receive_IT和HAL_SPI_Receive的区别_第2张图片

两种Receive流程

说明一下,SPI的BPW(bits per word)设置为8,因此文中一个word的size就是一个字节。

轮询:HAL_SPI_Receive流程

先看函数签名

HAL_StatusTypeDef HAL_SPI_Receive(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout);

hspi是SPI控制器句柄,pData是接收buf地址,Size是接收buf长度,Timeout是接收超时时间,如果期间一直没收到数据,则返回。

根据HAL源码,梳理流程概要:

获取控制器的锁
记录起始时间
填充控制器的各个字段:
控制器状态=BUSY_RX
接收buf地址=pData
接收buf长度=Size
RxISR = NULL
待接收字节数> 0?
rx fifo为空?
超时?
读取一个字节
待接收字节数-1
释放控制器的锁
返回错误码

注意,RxISR表示接收中断的回调函数,因为我们是轮询模式,所以该字段填0。

中断:HAL_SPI_Receive_IT流程

先看函数签名

HAL_StatusTypeDef HAL_SPI_Receive_IT(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size);
void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi);
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi);

参数含义跟HAL_SPI_Receive一样,少了个超时参数,因为中断方式并不轮询rx fifo是否非空,而是收到rx fifo非空中断后才去读取,不会让CPU无谓等待。

根据HAL源码,梳理流程概要:

HAL_SPI_Receive_IT
填充控制器的各个字段:
控制器状态=BUSY_RX
接收buf地址=pData
接收buf长度=Size
RxISR = SPI_RxISR_8BIT
获取控制器的锁
开启rx_fifo非空和rx_error中断
释放控制器的锁
HAL_SPI_IRQHandler
溢出中断置位?
获取中断使能和中断状态
禁用所有中断
HAL_SPI_ErrorCallback
rx fifo非空中断置位?
SPI_RxISR_8BIT
退出中断
SPI_RxISR_8BIT
待接收字节数-1
将FIFO的第一个字节拷贝到pData
待接收字节数== 0?
SPI_CloseRx_ISR
退出函数
SPI_CloseRx_ISR
控制器的错误码为NONE?
禁用rx_fifo非空和rx_error中断
用户定义的HAL_SPI_RxCpltCallback
用户定义的HAL_SPI_ErrorCallback
退出函数

注意:

  1. 每个字节的接收都会触发一次中断,因为所谓的rx fifo并不存在,其实就是直接读取控制器的DR寄存器(暂存当前收到的word),如果想提高效率,可以使用DMA版本。
  2. HAL_SPI_Receive_IT运行在后台(主循环),HAL_SPI_IRQHandler以及它调用的其他函数都运行在前台(中断),因此后者代码里一定不能有printf之类的打印语句,否则会影响SPI接收时序!

对比

可以看出,带IT后缀的receive函数,只填充控制器的上下文结构体并开启中断,剩下的都交给中断回调。这种策略将接收分成前后台两部分,后台开启中断,前台响应中断并读取数据,检测数据收够了就关闭中断,因此带IT后缀并不是传言的只能在中断态下运行。

后记

  1. HAL库其他总线,像UART、I2C等,它们的带IT后缀的receive函数,应该也是这种设计模式,大家可以验证一下。
  2. rx_fifo非空中断到底是片选信号触发的,还是SCK信号触发的,不太确定,看过芯片TRM手册,好像是SCK触发的,知道的帮忙确认下。

你可能感兴趣的:(驱动开发,stm32,嵌入式硬件,单片机,HAL)