STM32 HAL库:利用空闲中断实现不定长数据接收详解
一、什么是空闲中断
IDLE中断产生条件:串口在接收完一个字符串,进入空闲状态时(IDLE置1)便会激发一个空闲中断。
RXNE中断(串口接收中断):当串口接收到一个bit的数据时,(读取到一个停止位) 便会触发 RXNE接收数据中断。
区别:比如上位机一次性给单片机发送了114个字节,会产生114次RXNE中断,1次串口空闲中断。
注意:RXNE中断类似于HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
回调函数是一种特殊的函数,它在特定事件发生时由系统或库调用,而不是由程序显式调用。当使用 HAL_UART_Receive_IT 函数启动UART接收中断后,每当接收到一个字符时,该回调函数就会被调用一次。
我们在接收中断中保存各个字节进缓冲数组,在传输结束触发空闲中断时将缓冲数组内容取出,即可实现不定长数据接收。
二、如何实现不定长数据收发
第一步:在初始化时,添加需要的缓冲数组与标志位。开启串口空闲中断与接收中断。(这一步操作在main.c中)
注意:由于初始化完成后,IDLE位默认为一,这里先清除,再开启串口空闲中断。
第二步: 修改串口中断服务函数(在stm32f1xx_it.c中)
第三步:在后台中添加将串口收到的内容发出的程序,验证是否达到目的。(while(1)中添加)
三、现象验证
蓝色内容是上位机发送,绿色内容是上位机接收。串口助手使用的是vofa+。
四、补充一:硬件连线以及CUBEMX配置(使用最小系统板+STLINKV2下载器)
下载使用SWD接口。使用串口需要使用USB转TTL模块。这是下载器接线。
这是USB转TTL接线。用于调串口。
CUBEMX配置方面,选择外部高速时钟源,debug选择Serial Wire,打开串口一中断即可。
五、补充二:部分硬件底层逻辑
1.关于串口初始化结构体:
在usart.c中:自动生成了这个结构体变量。
进去看一眼:
关注这个USART TypeDef* Instance,进去看一眼:
是寄存器级别的操作。
而在初始化中,CUBEMX生成的函数中帮我们调用了这个函数:
也就是说,我们操作USART1就可以操作底层的寄存器了。
2.关于上面我们用到的DR寄存器:
首先,TDR和RDR都是USART_DR寄存器的缓冲区,指的是USART_DR的0到8位,TDR和RDR共用一片物理空间,即DR寄存器。
接收数据过程: 数据通过串口线一位一位传过来,先传到移位寄存器,当移位寄存器识别了这个数据帧之后,通过“并行通信”的方式“一次性”传递给RDR寄存器,当移位寄存器中的数据位传到RDR中时,RXNE会硬件置为1。我们在串口接收中断中去读取RDR缓冲区里面的数据内容,而一旦读取了RDR中的内容,RXNE标志位会硬件置0。下次还有数据传进来的时候,RXNE又会置1。如此循环往复。
六、抄代码在这:
main.c:
/* USER CODE BEGIN 0 */
int flag = 0;
uint8_t RxBuff[256] = {0};
uint16_t Num = 0;
/* USER CODE END 0 */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
if(flag == 1)
{
flag = 0;
HAL_UART_Transmit(&huart1,RxBuff,sizeof(RxBuff),1000);
memset(RxBuff,0,sizeof(RxBuff));
}
}
/* USER CODE END 3 */
it.c:
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE)) //接收完,空闲中断置位
{
flag = 1;
__HAL_UART_CLEAR_IDLEFLAG(&huart1);
}
if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE))
{
RxBuff[Num++] = USART1 -> DR;
}
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
/* USER CODE END USART1_IRQn 1 */
}
作者:Stwie Su