蓝桥杯嵌入式物联网串口接收中断与空闲中断的最简实用方法
目录
文章目录
前言
一、串口的空闲中断?
二、使用步骤
1.打开串口中断(空闲中断加接受中断)
2.中断服务函数
总结
文章目录
前言
之所以写这篇文章,是因为参加蓝桥杯的时候发现HAL库的串口很难用,以至于串口总是接收都有问题。这篇文章还适合刚刚使用HAL库,回调函数用不习惯的同学,本文章以物联网L071为例来写,嵌入式G431也同样适用。
一、串口的空闲中断?
当使用串口接受数据的时候,你会发现不知道数据什么时间结束,网上也有一种方法教大家接受定长数据的教程,另一种方法就是在数据的后面加上结束位,这样就可以知道数据什么时间结束,从而去处理数据。这两种方法都有局限性,那什么样的方法更好呢?其实STM已经给了我们答案了,就是串口的空闲中断,这里先引用官方对串口空闲中断的解释:
此位由检测到空闲线路时硬件设置。如果出现以下情况,则生成中断
USART_CR1寄存器中的 IDLEIE=1。它由软件清除,将 1 写入 IDLECF
USART_ICR寄存器。
0:未检测到空闲线路
1:检测到空闲线路
注意:在设置 RXNE 位之前,不会再次设置空闲位(即新的空闲行)
发生)。
如果启用了静音模式 (MME=1),则在 USART 未静音 (RWU=0) 时设置空闲,
无论唤醒位选择何种静音模式。如果 RWU=1,则不设置空闲。
以上就是手册中对IDLE的解释,直白点来讲就是,当串口接受到数据后,它会自动检测后面还有没有数据,如果没有数据了,IDLE就置位,表示一帧数据接受完毕,它是在接受中断之后才置位的,如果没有接受的数据,空闲时它是不会置位的。
二、使用步骤
1.打开串口中断(空闲中断加接受中断)
在CubeMX中只需要将串口模式设置为Asynchronous,其他不用动打开串口中断 就可以了。
生成工程后在min()函数中添加打开串口中断的函数
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART2_UART_Init();
/* USER CODE BEGIN 2 */
__HAL_UART_CLEAR_IDLEFLAG(&huart2); //清除串口空闲中断
__HAL_UART_ENABLE_IT(&huart2,UART_IT_IDLE); //打开串口空闲中断
__HAL_UART_ENABLE_IT(&huart2,UART_IT_RXNE); //打开串口接受中断
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
一定要先清除串口空闲中断,然后在打开串口空闲中断,因为串口初始化完成后会自动将IDLE置位,导致还没有接受数据就进入到中断里面去了,所以打开IDLE之前,先把它清楚掉。
2.中断服务函数
在中断服务函数中添加处理空闲中断的函数。
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_IT(&huart2,UART_IT_IDLE) !=RESET) {
ucTemp = 1;
__HAL_UART_CLEAR_IDLEFLAG(&huart2);
}
if(__HAL_UART_GET_IT(&huart2,UART_IT_RXNE) !=RESET) {
ucRxBuff[ucNum++] = USART2->RDR;
}
/* USER CODE END USART2_IRQn 1 */
}
把串口中断函数中自动调用的函数注释掉,因为它每次调用完回调函数后都会关闭中断。(不使用回调函数,可以的提高串口接受的处理速度)当ucTemp置位时,代表数据接受完毕,然后清除空闲中断,下面是接受中断,接受中断不用清除。
在while(1)中处理接收到的数据
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
if(ucTemp == 1) {
ucTemp = 0; //清除标志位
printf("%s\r\n",ucRxBuff); //串口回显
memset(ucRxBuff,0,20); //清除缓冲区
ucNum = 0; //清除缓冲区指针
}
}
/* USER CODE END 3 */
printf的重定向,同时keil中的USE Microlib打上勾
int fputc(int ch,FILE*f)
{
HAL_UART_Transmit(&huart2,(uint8_t *)&ch,1,10);
while(__HAL_UART_GET_FLAG(&huart2,UART_FLAG_TXE) == RESET);
return ch;
}
总结
如果有同学发现这样写还不能用,可以在串口初始化下面加上HAL_DELY(10),延迟一下,能用的同学可以略过,以上串口暂时在使用中没有发现什么问题,如果以上串口大家在使用中有什么问题,欢迎大家在下面留言,我们一起进步。
作者:把握不住