STM32 HAL库多串口任意长度接收的方法(无起始和结束标志,不使用DMA)

STM32 HAL库多串口任意长度接收的方法(无起始和结束标志,不使用DMA)

发现最近随便转载的很多啊,未经授权禁止转载!抄袭!!否则转载者死全家!!另外这是我的笔记,不是教程,难免会有错误,不具有很高的参考性,望周知。

今天在HAL库串口接收任意长度上卡了好久,之前用的都是标准库或者HAL库+DMA的方式来写,今天这个项目用的是F429木有标准库可用,需要和其他模块配合,其他模块发的是不定长的数据且无起始和结束标志,串口又不能用DMA,所以只得使用接收中断和空闲中断的方式来解决。

我也尝试了使用网络上的例程来实现,不知是HAL库的原因还是什么,要么是无法正常接收数据,要么就是无法进入空闲中断,因此自己摸索了好久。

现在的代码思路是:STM32的串口有一个空闲中断的功能,串口在没有数据传输的时候会进入空闲中断,因此利用空闲中断可以接收任意不定长且无起始和结束标志的数据。

首先在STM32 CubeMX中正常配置串口接收,并打开串口中断,我这里勾选了创建默认HAL_UART_IRQHandler函数的选项。

工程创建完毕之后,在main.c文件中,创建两个串口缓冲数组,以及两个unsigned char型变量用来存放接收到的串口数据。在main函数中,需要调用HAL_UART_Receive_IT函数来对串口接收中断进行配置。

还需要在main()函数中添加开启串口空闲中断的代码,有几个串口就开启几个,这里比较坑的是在HAL库中开启空闲中断的方法使用的是宏定义,鼠标在语句上不会有参数提示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
main.c:
unsigned char RxBuffer1[100];
unsigned char RxBuffer2[100];
unsigned char RxByte1;
unsigned char RxByte2;

int main()
{
HAL_UART_Receive_IT(&huart1,&RxByte1,1); /*开启串口接收中断并指定将接收到的数据放进RxByte1变量中,第三个参数为接收n个字符进入接收回调函数,这里设置为1,即一个字符一个字符地接收*/
HAL_UART_Receive_IT(&huart2,&RxByte2,1);

__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);
__HAL_UART_ENABLE_IT(&huart2,UART_IT_IDLE);

while(1)
...;
}

当串口接收到数据以后,会将数据放进RxByte1变量中并调用HAL_UART_RxCpltCallback函数,因此在main.c文件中创建void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
main.c:
unsigned int RxCount1;
unsigned int RxCount2;
unsigned char RxFlag1;
unsigned char RxFlag2;

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART1) // 判断是由哪个串口触发的中断
{
RxBuffer1[RxCount1] = RxByte1; //将接收到的数据放进RxBuffer1缓冲区中
RxCount1++; //RxBuffer1下标累加
HAL_UART_Receive_IT(&huart1,&RxByte1,1); //处理完毕还需要重新打开接收中断,否则不会接收下一个数据
}
if(huart->Instance == USART2)
{
RxBuffer2[RxCount2] = RxByte2;
RxCount2++;
HAL_UART_Receive_IT(&huart2,&RxByte2,1);
}

}

在stm32f4xx_it.c文件中,找到串口中断服务函数,添加以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
stm32f4xx_it.c:
extern unsigned int RxCount1; //声明外部变量,与main.c文件中的变量联系在一起
extern unsigned int RxCount2;
extern unsigned char RxFlag1;
extern unsigned char RxFlag2;

void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */

/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE) != RESET) /*判断是空闲中断标志位,要注意的是这个语句不要写到HAL_UART_RxCpltCallback函数中,因为HAL_UART_RxCpltCallback只是接收回调函数,不是接收的情况下是不会调用的,如果写到HAL_UART_RxCpltCallback中永远也处理不了空闲中断*/
{
__HAL_UART_CLEAR_IDLEFLAG(&huart1); //清除空闲中断标志
RxFlag1 = 1; //接收标志置1,表示收到了一帧数据
}
/* USER CODE END USART1_IRQn 1 */
}

void USART2_IRQHandler(void)
{
/* USER CODE BEGIN USART2_IRQn 0 */

/* USER CODE END USART2_IRQn 0 */
HAL_UART_IRQHandler(&huart2);
/* USER CODE BEGIN USART2_IRQn 1 */
if(__HAL_UART_GET_FLAG(&huart2, UART_FLAG_IDLE) != RESET)
{
__HAL_UART_CLEAR_IDLEFLAG(&huart2);
RxFlag2 = 1;
}
/* USER CODE END USART2_IRQn 1 */
}

接着就可以在mian.c文件中处理接收到的数据了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
main.c:
int main()
{
while (1)
{
/* USER CODE END WHILE */
if(RxFlag1 == 1) //如果串口1收到了一帧数据
{
printf("USART1:%s\r\n", RxBuffer1); //printf内容
memset(RxBuffer1,0,sizeof(RxBuffer1)); //清空缓冲区,一定要清空
RxFlag1 = 0; //接收标志置0
RxCount1 = 0; //清零计数
}
if(RxFlag2 == 1)
{
printf("USART2:%s\r\n", RxBuffer2);
memset(RxBuffer2,0,sizeof(RxBuffer2));
RxFlag2 = 0;
RxCount2 = 0
}
/* USER CODE BEGIN 3 */
}

}

坚持原创技术分享,您的支持是我前进的动力!