stm32基于HAL库的串口UART中断接收不定长数据代码实现

总体分析:代码使用的串口USART1,GPIO的复用引脚分别是:PA9复用为RX引脚;PA10复用为TX引脚。数据接收标志符为"\r\n"即回车按键按下,当接收到\r\n时接收停止。接收数据缓冲区只能容纳一个字节数据,设置的接收一个字节数据产生一次接收中断,在中断回调函数中进行串口接收协议的编写。

代码现象:将接受的数据重新发送至串口进行回显。

代码实现

uart.h

#ifndef __USART_H
#define __USART_H

#include "stdio.h"
#include "./SYSTEM/sys/sys.h"

#define USART_X						USART1
#define DR_DATA						USART_X->DR
#define USART_GPIO					GPIOA
#define USART_RX					GPIO_PIN_10
#define USART_TX					GPIO_PIN_9
#define USART_CLK_ENABLE()			do{__HAL_RCC_USART1_CLK_ENABLE();}while(0)
#define USART_RX_CLK_ENABLE()		do{__HAL_RCC_GPIOA_CLK_ENABLE();}while(0)
#define USART_TX_CLK_ENABLE()		do{__HAL_RCC_GPIOA_CLK_ENABLE();}while(0)
#define RX_IT_EN					1
#define RX_BUFFER_SIZE				1
#define SX_BUFFER_SIZE				10
#define USART_IRQHandler			USART1_IRQHandler
#define USART_IRQn					USART1_IRQn
extern UART_HandleTypeDef UART_InitStructure;
extern uint8_t UART_RX_Buffer[];
extern uint8_t UART_SX_BUFFER[];
extern uint8_t Index;
extern uint8_t	RX_Flag;
extern uint8_t End_Flag;
extern uint8_t EnterLine_Flag;	
void Clear_Buffer(void);
void UART_Config(uint32_t baudrate);
void Show_Back(void);




#endif

uart.c

#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"

UART_HandleTypeDef UART_InitStructure;
uint8_t Index = 0;
uint8_t UART_RX_Buffer[RX_BUFFER_SIZE];
uint8_t UART_SX_BUFFER[SX_BUFFER_SIZE];
uint8_t	RX_Flag = 0;
uint8_t EnterLine_Flag = 0;
uint8_t End_Flag = 0;
//缓冲区元素清0
void Clear_Buffer(void)
{
	uint8_t i;
	UART_RX_Buffer[0]=0;
	for(i =0;i<SX_BUFFER_SIZE;i++)
	{
		UART_SX_BUFFER[i] = 0;
	}

}

void UART_Config(uint32_t baudrate)
{
	
	UART_InitStructure.Instance=USART1;
	
	UART_InitStructure.Init.BaudRate=baudrate;
	UART_InitStructure.Init.HwFlowCtl=UART_HWCONTROL_NONE;
	UART_InitStructure.Init.Mode=UART_MODE_TX_RX;
	UART_InitStructure.Init.Parity=UART_PARITY_NONE;
	UART_InitStructure.Init.StopBits=UART_STOPBITS_1;
	UART_InitStructure.Init.WordLength=UART_WORDLENGTH_8B;
	
	HAL_UART_Init(&UART_InitStructure);
	//开启串口异步接收中断,设置缓冲区大小。
	HAL_UART_Receive_IT(&UART_InitStructure,(uint8_t *)UART_RX_Buffer,RX_BUFFER_SIZE);
	Clear_Buffer();
}

//这个函数才是真正初始化串口的一些接口的函数,这个函数在HAL_UART_Init中进行调用
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
	if(huart->Instance == USART1)
	{	
		//开启USART1,RX,TX时钟
		USART_CLK_ENABLE();
		USART_RX_CLK_ENABLE();
		USART_TX_CLK_ENABLE();
		
		GPIO_InitTypeDef GPIO_InitStructure;
		//接收RX,复用成串口输入,看103产品手册;
		GPIO_InitStructure.Mode=GPIO_MODE_AF_INPUT;
		GPIO_InitStructure.Pin=USART_RX;
		//串口发送数据的时序图中空闲时刻输入线是高电平,所以初始化为上拉
		GPIO_InitStructure.Pull=GPIO_PULLUP;
		GPIO_InitStructure.Speed=GPIO_SPEED_FREQ_HIGH;
		HAL_GPIO_Init(USART_GPIO,&GPIO_InitStructure);
		
		//发送TX,复用为串口输出
		GPIO_InitStructure.Mode=GPIO_MODE_AF_PP;
		GPIO_InitStructure.Pin=USART_TX;
		HAL_GPIO_Init(USART_GPIO,&GPIO_InitStructure);

//配置接收中断,串口中断要先设置优先级和使能中断
#if RX_IT_EN
		HAL_NVIC_SetPriority(USART1_IRQn,3,0);
		HAL_NVIC_EnableIRQ(USART1_IRQn);
#endif
	}
}

//在硬件中断入口函数调用hal公共中断函数
void USART_IRQHandler(void)
{
	//在这个中断函数中会失能串口数据接收中断,所以如果希望在中断发生后继续产生中断,需要在调用该函数之后再次使能接收中断。
	HAL_UART_IRQHandler(&UART_InitStructure);
	//再次开启串口接收中断
	HAL_UART_Receive_IT(&UART_InitStructure,(uint8_t *)UART_RX_Buffer,RX_BUFFER_SIZE);
}

//接收中断回调处理函数
//输入不定长的数据,设置结束标志位是"\r\n",\r对应0x0D;\n对应0x0A
//实际上这个回调函数只会在接收一个字节时调用一次
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	//判断是不是当前选中的串口,进行数据协议编写
	if(huart->Instance == USART1)
	{
		//进入串口中断将接收标志置1
		RX_Flag=1;
		//判断当前接收缓冲区中的字节是不是结束标志符"\r"
		if((UART_RX_Buffer[0] == 0x0D))
		{
			//如果是\r就将回车标志位置1,等待下一个字符被接收
			EnterLine_Flag = 1;	
		}
		//"\r"之后的第一个字符传入进行判断
		else if(EnterLine_Flag == 1)
		{
			//如果是\n,说明数据接收的完成,置标志位End_Flag为1
			if(UART_RX_Buffer[0] == 0x0A)
			{
				End_Flag = 1;
			}	
			//如果不是\n 说明数据接收并未完成,将该数据放入回显缓冲区内
			else
			{
				UART_SX_BUFFER[Index] = UART_RX_Buffer[0];
				Index++;
			}
				
		}
		//接收数据未收到\r情况下进入这个条件判断语句中
		else if(EnterLine_Flag != 1)
		{
			UART_SX_BUFFER[Index] = UART_RX_Buffer[0];
			Index++;
		}

	}

	/*使用位来标志*/
	
}

//接收回显数据函数
void Show_Back(void)
{
	//通过标志位判断是否接收到数据并且接收数据操作已经完成
	if((RX_Flag == 1) && (End_Flag == 1))
	{
		//通过hal库函数将数据发送至串口
		HAL_UART_Transmit(&UART_InitStructure,(uint8_t *)UART_SX_BUFFER,Index,1000);
		//等待串口发送完毕
		while(__HAL_UART_GET_FLAG(&UART_InitStructure,UART_FLAG_TC) != 1);
		//换行
		printf("\r\n");

	}
	//索引、标志位清零
	Index = 0;
	EnterLine_Flag = 0;
	End_Flag = 0;
	RX_Flag =0;
}

注意如果要将数据打印到串口,还需要将printf函数进行重定向。下面的代码是死的,只需要复制到工程内就可以实现数据打印到串口。

///* 加入以下代码, 支持printf函数, 而不需要选择use MicroLIB */

#if 1

#if (__ARMCC_VERSION >= 6010050)            /* 使用AC6编译器时 */
__asm(".global __use_no_semihosting\n\t");  /* 声明不使用半主机模式 */
__asm(".global __ARM_use_no_argv \n\t");    /* AC6下需要声明main函数为无参数格式,否则部分例程可能出现半主机模式 */

#else
/* 使用AC5编译器时, 要在这里定义__FILE 和 不使用半主机模式 */
#pragma import(__use_no_semihosting)

struct __FILE
{
    int handle;
    /* Whatever you require here. If the only file you are using is */
    /* standard output using printf() for debugging, no file handling */
    /* is required. */
};

#endif

/* 不使用半主机模式,至少需要重定义_ttywrch\_sys_exit\_sys_command_string函数,以同时兼容AC6和AC5模式 */
int _ttywrch(int ch)
{
    ch = ch;
    return ch;
}

/* 定义_sys_exit()以避免使用半主机模式 */
void _sys_exit(int x)
{
    x = x;
}

char *_sys_command_string(char *cmd, int len)
{
    return NULL;
}


/* FILE 在 stdio.h里面定义. */
FILE __stdout;

/* MDK下需要重定义fputc函数, printf函数最终会通过调用fputc输出字符串到串口 */
int fputc(int ch, FILE *f)
{
    while ((USART_X->SR & 0X40) == 0);     /* 等待上一个字符发送完成 */

    USART_X->DR = (uint8_t)ch;             /* 将要发送的字符 ch 写入到DR寄存器 */
    return ch;
}
#endif

main.c

在轮训过程中不建议一直读取串口接收的数据,建议在没有数据被接受时执行其他的代码

#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "beep.h"
#include "key.h"
#include "led.h"
#include "exti.h"

int main(void)
{
    HAL_Init();                              /* 初始化HAL库 */
    sys_stm32_clock_init(RCC_PLL_MUL9);      /* 设置时钟, 72Mhz */
    delay_init(72);                          /* 延时初始化 */
    //led_init();                              /* LED初始化 */
	LED_Init();
	UART_Config(115200);
	printf("11111111\r\n");
    while(1)
    { 
		if((RX_Flag == 1) && (End_Flag == 1))
		{
			Show_Back();
		}
		else
		{
			LED0_TOGGLE;										
			LED1_TOGGLE;
			HAL_Delay(100);
			LED0_TOGGLE;										
			LED1_TOGGLE;
			HAL_Delay(100);
		}
			

	}
}

代码现象

注意箭头位置输入了回车按键

e3abbc75b4104d63a61a03b8fa64e841.png

 

 

作者:ls20010901

物联沃分享整理
物联沃-IOTWORD物联网 » stm32基于HAL库的串口UART中断接收不定长数据代码实现

发表回复