介绍
在使用单片机的串口通信功能时,常用的接收数据方法是通过固定的字节数来判断一帧数是否发送完成,或者是通过固定的结束标志位来表示一帧数据发送完成。但是有时候会遇到发送的数据长度不固定,也没有固定的结束标志,对于这样的数据通常的做法是每隔一段时间查看一下接收数据的长度是否发生了变化,如果指定的一段时间内接收数据长度没有发生变化,就认为是一帧数据发送完成。在STM32单片机中串口提供了一个更好用的功能,就是空闲中断功能。也就是说当一帧数据发送结束后,就会产生一个空闲中断。这样就可以利用这个空闲中断来判断一帧数据接收是否完成。
关于串口空闲检测可以在STM32参考手册上找到相关介绍
通过这个图可以看出来,当第一组数据Data1、Data2、Data3、Data4发送结束后,总线就会处于空闲状态,这时就会产生一个空闲中断。
当Data1、Data2、Data3、Data4每一个数据到来时串口产生的中断为 RXNE:读数据寄存器非空中断。具体的相关介绍可以在状态寄存器(USART_SR)中查看。
也就是每接收一个字节,串口会产生一个RXNE中断,当一帧数据发送完成就产生一个IDLE中断。
这样就可以在串口每个RXNE中断来临后将数据先存储起来,然后在IDLE中断到来后说明数据接收结束。这时候就可以去处理接收到的数据了。
下面就通过代码来说明一下如何使用串口空闲中断来接收不定长数据
首先初始化串口
UART_HandleTypeDef huart3;
_USART3_Typedef USART3_Typedef = {"\0"};
/* USART3 init function */
void MX_USART3_UART_Init(void)
{
huart3.Instance = USART3;
huart3.Init.BaudRate = 115200;
huart3.Init.WordLength = UART_WORDLENGTH_8B;
huart3.Init.StopBits = UART_STOPBITS_1;
huart3.Init.Parity = UART_PARITY_NONE;
huart3.Init.Mode = UART_MODE_TX_RX;
huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart3.Init.OverSampling = UART_OVERSAMPLING_16;
huart3.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart3.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_UART_Init(&huart3) != HAL_OK)
{
Error_Handler();
}
}
void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
if(uartHandle->Instance==USART3)
{
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART3;
PeriphClkInit.Usart3ClockSelection = RCC_USART3CLKSOURCE_PCLK1;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}
/* USART3 clock enable */
__HAL_RCC_USART3_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/**USART3 GPIO Configuration
PB10 ------> USART3_TX
PB11 ------> USART3_RX
*/
GPIO_InitStruct.Pin = GPIO_PIN_10|GPIO_PIN_11;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF7_USART3;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
// 使能串口接收中断
__HAL_UART_ENABLE_IT(&huart3, UART_IT_RXNE); //使能RXNE中断
__HAL_UART_ENABLE_IT(&huart3, UART_IT_IDLE); //使能IDLE中断
// HAL_UART_Receive_IT(&huart3, &rxData, 1);
HAL_NVIC_SetPriority(USART3_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(USART3_IRQn);
/* USER CODE BEGIN USART3_MspInit 1 */
/* USER CODE END USART3_MspInit 1 */
}
}
与常规的串口初始化唯一不同的就是多了一行空闲中断初始化代码
USART_ITConfig( USART2, USART_IT_IDLE, ENABLE ); //使能串口空闲中断
下来在中断中根据中断类型来处理数据
#define SCREEN_RXBuf_SIZE 128
typedef struct
{
uint8_t RxBuf[SCREEN_RXBuf_SIZE]; //串口数据接收缓冲区
uint8_t Rx_over; //接收完成标志 0-没有接收完成 1-接收完成
uint32_t Rx_count; //接收数据的个数
}_USART3_Typedef;
extern _USART3_Typedef USART3_Typedef;
extern UART_HandleTypeDef huart3;
// 中断服务函数
void USART3_IRQHandler(void)
{
if(__HAL_UART_GET_FLAG(&huart3,UART_FLAG_RXNE) == SET)
{
__HAL_UART_CLEAR_FLAG(&huart3,UART_FLAG_RXNE);//清除标志位
// if(USART3_Typedef.Rx_over == 0)
{
USART3_Typedef.RxBuf[USART3_Typedef.Rx_count++] = USART3->RDR;
}
}
if(__HAL_UART_GET_FLAG(&huart3,UART_FLAG_IDLE) == SET)
{
__HAL_UART_CLEAR_FLAG(&huart3,UART_FLAG_IDLE);//清除标志位
USART3_Typedef.Rx_over = 1; //接收完成
}
// HAL_UART_IRQHandler(&huart3);
}
重定义putc函数,这样可以使用printf函数从串口1打印输出
/*
*********************************************************************************************************
* 函 数 名:
* 功能说明: 支持printf函数,而不需要选择use MicroLIB
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
#pragma import(__use_no_semihosting)
//标准库需要的支持函数
struct __FILE
{
int handle;
};
FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
void _sys_exit(int x)
{
x = x;
}
/*
*********************************************************************************************************
* 函 数 名: fputc
* 功能说明: 重定义putc函数,这样可以使用printf函数从串口1打印输出
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
int fputc(int ch, FILE *f)
{
while((USART3->ISR & 0X40)==0 || (USART3->ISR & 0X80)==0);//循环发送,直到发送完毕
USART3->TDR = (uint16_t) ch;
return ch;
}