前言

本实验在上一节介绍的接收中断法的基础上进行修改,方便去理解空闲中断和接收中断的区别。

空闲中断

串口在空闲时,也就是说串口在一段时间里没有接收到新数据,则会触发空闲中断。细心的同学应该发现了,空闲中断实际上跟上面的超时判断是一样样的,只不过空闲中断是硬件自带,但超时判断需要我们自己实现。

所以,一旦接收到空闲中断,可以认为接收到一帧完整的数据。

注意:空闲中断并不是所有的 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指令)等,点击关注不迷路~

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐