• STM32 HAL库函数HAL_SPI_Receive_IT和HAL_SPI_Receive的区别


    背景

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

    项目中的按键板通过SPI总线与主板连接,按键板是Slave设备,因此无法确定什么时候收到主板的读写请求,要么轮询,要么依赖控制器提供的中断机制。
    带IT的receive

    两种Receive流程

    说明一下,SPI的BPW(bits per word)=8,不是16,因此一个word就是一个字节。

    轮询:HAL_SPI_Receive流程

    先看函数签名

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

    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);
    
    • 1
    • 2
    • 3

    参数含义跟HAL_SPI_Receive一样,少了个超时参数,因为中断方式并不关心rx fifo深度

    根据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触发的,知道的帮忙确认下。
  • 相关阅读:
    [go学习笔记.第十四章.协程和管道] 1.协程的引入,调度模型以及运行cpu数目,协程资源竞争问题
    maven仓库与jar查找
    Lazarus网络编程
    PHP XML Expat 解析器
    Flutter框架实现登录注册功能,不连接数据库
    mosquitto使用的基本流程以及一些遇见的问题
    基于粒子群算法优化的lssvm回归预测-附代码
    企业在数字化管理中有哪些难点?如何有效解决?
    [Java]线上监控诊断工具Arathas,入门使用
    Java进阶之路--继承中的方法重写与属性覆盖
  • 原文地址:https://blog.csdn.net/happen23/article/details/134475636