STM32 HAL库函数HAL_SPI_Receive_IT和HAL_SPI_Receive的区别

背景

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

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

两种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触发的,知道的帮忙确认下。
  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SystemClock_Config是一个函数,用于配置STM32微控制器的系统时钟。它是在STM32 HAL库中提供的,用于设置时钟源、时钟分频和时钟倍频等参数。 该函数的具体实现会根据不同的STM32系列微控制器有所区别,但一般情况下,它会通过修改相关寄存器的值来配置系统时钟。 你可以使用该函数来设置系统时钟以满足你的应用需求,例如调整主频、选择外部晶振或内部时钟源等。在使用该函数之前,你需要先初始化相关的时钟源和时钟分频器。 以下是一个示例代码片段,展示了如何使用SystemClock_Config函数来配置系统时钟: ``` /* Includes */ #include "stm32xxxx.h" #include "stm32xxxx_hal_rcc.h" /* Function prototypes */ void SystemClock_Config(void); /* Main function */ int main(void) { /* Configure the system clock */ SystemClock_Config(); /* Rest of your code */ while (1) { /* Your code here */ } } /* System Clock Configuration */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct; RCC_ClkInitTypeDef RCC_ClkInitStruct; /* Configure the main PLL clock source, multiplication factor, and division factors */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI; RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL6; RCC_OscInitStruct.PLL.PLLDIV = RCC_PLL_DIV3; HAL_RCC_OscConfig(&RCC_OscInitStruct); /* Configure the clock prescalers, clock source, and clock division factors for each clock */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1); } ``` 请注意,上述示例代码中的"stm32xxxx"和"xxxx"应该替换为你使用的具体STM32系列型号。在实际使用中,你需要根据你的需求和具体的硬件配置来调整SystemClock_Config函数中的参数。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值