STM32串口接收不定长数据2(HAL库)(空闲中断)
串口在空闲时,也就是说串口在一段时间里没有接收到新数据,则会触发空闲中断。本实验在上一本文介绍了STM32串口接收不定长数据的空闲中断用法,包括初始化配置、中断处理和数据收发。希望能通过这个小实验能区别出接收中断和空闲中断的处理方法,同时稳固串口通信的知识点。节介绍的接收中断法的基础上进行修改,方便去理解空闲中断和接收中断的区别。
前言
本实验在上一节介绍的接收中断法的基础上进行修改,方便去理解空闲中断和接收中断的区别。
空闲中断
串口在空闲时,也就是说串口在一段时间里没有接收到新数据,则会触发空闲中断。细心的同学应该发现了,空闲中断实际上跟上面的超时判断是一样样的,只不过空闲中断是硬件自带,但超时判断需要我们自己实现。
所以,一旦接收到空闲中断,可以认为接收到一帧完整的数据。
注意:空闲中断并不是所有的 MCU 都具备,一般高端一点的 MCU 才有,低端一些的 MCU 并没有空闲中断,我们STM32F103C8T6是有空闲中断的。
实验目的
主要使用空闲中断的处理方法,让串口助手实现串口接收不定长数据。
硬件清单
(1)STM32F103C8T6核心板
(2)ST-Link
(3)USB转TTL(CH340驱动)
具体步骤
我们既然是用到了空闲中断我们就使能空闲中断,然后在串口的服务函数中,去多加判断是否接收到空闲中断,接收到了代表数据接收完整了(空闲中断标志位置1),我们就打印接收数据,然后老样子需要清空接收数据,用一个我们之前写好的清空函数,其中还需要写一个专门清除空闲中断标志位,在STM32F1xxx的产品手册中有具体说明,RXNE不需要手动清除而空闲中断标志位(IDLE)需要手动清除,以便于下次使用。
还有一个需要修改的是我们清除函数中是清空了uart1_rx_len这个变量,所以我们接收数据的时候存储变量的索引值需要由uart1_cnt改变为uart1_rx_len,这样清除函数可以清除对应的索引值。
void USART1_IRQHandler(void)
{
uint8_t receive_data = 0;
if(__HAL_UART_GET_FLAG(&uart1_handle,UART_FLAG_RXNE) != RESET)
{
if(uart1_rx_len >= sizeof(uart1_rx_buf)) //判断接收数据是否超限
uart1_rx_len = 0;
HAL_UART_Receive(&uart1_handle,&receive_data,1,1000);
uart1_rx_buf[uart1_rx_len++] = receive_data; //保存数据
}
if(__HAL_UART_GET_FLAG(&uart1_handle,UART_FLAG_IDLE) != RESET) // 判断是否有空闲中断
{
printf("recv: %s\r\n",uart1_rx_buf); //把缓冲区的数据读出来
uart1_rx_clear(); //清空数据,方便下次接收
__HAL_UART_CLEAR_IDLEFLAG(&uart1_handle); //专门清空中断空闲标志位
}
}
uart1.c
#include "uart1.h"
uint8_t uart1_rx_buf[UART1_RX_SIZE]; //保存接收的数据
uint16_t uart1_rx_len = 0; //数据长度
/*初始化结构体*/
UART_HandleTypeDef uart1_handle = {0};
void uart1_Init(uint32_t baudrate)
{
uart1_handle.Instance = USART1; //选择串口1
uart1_handle.Init.BaudRate = baudrate; //设置波特率,用入口参数设置
uart1_handle.Init.WordLength = UART_WORDLENGTH_8B; //数据长度为8位数据
uart1_handle.Init.StopBits = UART_STOPBITS_1; //1位停止位
uart1_handle.Init.Parity = UART_PARITY_NONE; //不启用奇偶校验位
uart1_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE; //不启用硬件流控制
uart1_handle.Init.Mode = UART_MODE_TX_RX; //同时开启接收和发送
HAL_UART_Init(&uart1_handle);
}
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART1)
{
//打开时钟
__HAL_RCC_USART1_CLK_ENABLE(); // 使能 USART1 时钟
__HAL_RCC_GPIOA_CLK_ENABLE(); // 使能 GPIOA 时钟(具体看USART1 的 TX/RX引脚)
//调用GPIO初始化函数
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.Mode = GPIO_MODE_AF_PP ; // 复用推挽输出(用于发送数据 TX)
GPIO_InitStructure.Pin = GPIO_PIN_9; // PA9(USART1_TX)
GPIO_InitStructure.Pull = GPIO_PULLUP; // 上拉电阻(增强信号稳定性)
GPIO_InitStructure.Speed = GPIO_SPEED_HIGH; // 高速模式
HAL_GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_InitStructure.Mode = GPIO_MODE_AF_INPUT; // 复用输入(用于接收数据 RX)
GPIO_InitStructure.Pin = GPIO_PIN_10; // PA10(USART1_RX)
HAL_GPIO_Init(GPIOA,&GPIO_InitStructure);
HAL_NVIC_EnableIRQ(USART1_IRQn); // 使能 USART1 全局中
HAL_NVIC_SetPriority(USART1_IRQn,2,2); // 设置中断优先级
__HAL_UART_ENABLE_IT(huart,UART_IT_RXNE); // 使能 "接收缓冲区非空" 中断
__HAL_UART_ENABLE_IT(huart,UART_IT_IDLE); // 使能空闲中断标志位
}
}
/*清空接收寄存器函数*/
void uart1_rx_clear(void)
{
memset(uart1_rx_buf,0,sizeof(uart1_rx_buf));
uart1_rx_len = 0;
}
void USART1_IRQHandler(void)
{
uint8_t receive_data = 0;
if(__HAL_UART_GET_FLAG(&uart1_handle,UART_FLAG_RXNE) != RESET)
{
if(uart1_rx_len >= sizeof(uart1_rx_buf)) //判断接收数据是否超限
uart1_rx_len = 0;
HAL_UART_Receive(&uart1_handle,&receive_data,1,1000);
uart1_rx_buf[uart1_rx_len++] = receive_data; //保存数据
}
if(__HAL_UART_GET_FLAG(&uart1_handle,UART_FLAG_IDLE) != RESET) // 判断是否有空闲中断
{
printf("recv: %s\r\n",uart1_rx_buf); //把缓冲区的数据读出来
uart1_rx_clear(); //清空数据,方便下次接收
__HAL_UART_CLEAR_IDLEFLAG(&uart1_handle); //专门清空中断空闲标志位
}
}
/*重定向到串口1中,printf会调用fputc*/
int fputc(int ch, FILE *f)
{
while((USART1->SR & 0X40) == 0);
USART1->DR = (uint8_t)ch;
return ch;
}
uart1.h
#ifndef __USART__H
#define __USART__H
#include "sys.h"
#include "string.h"
#include "stdio.h"
#define UART1_RX_SIZE 128
#define UART1_TX_SIZE 64
#define UART_EOK 0 //接收成功
#define UART_ERROR 1 //接收错误
#define UART_ETIMEOUT 2 //接收超时
#define UART_EINVAL 3 //接收非法
void uart1_Init(uint32_t baudrate);
void uart1_rx_clear(void);
#endif
main.c
#include "sys.h"
#include "delay.h"
#include "uart1.h"
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
uart1_Init(115200);
while(1)
{
}
}
实验现象
同样的我们发送一个经典的Hello,World!试试数据的不定长收发,大家可以试试更有趣的~

总结:本文介绍了STM32串口接收不定长数据的空闲中断用法,包括初始化配置、中断处理和数据收发。希望能通过这个小实验能区别出接收中断和空闲中断的处理方法,同时稳固串口通信的知识点。如果有疑问或发现错误,欢迎在评论区留言讨论!
📜 下一篇预告:
本文将介绍《蓝牙模块基本使用》,可以了解蓝牙的基本介绍、运用(AT指令)等,点击关注不迷路~
更多推荐



所有评论(0)