一. 串口通信简介

串口通信的原理网上教程一大堆了,入坑单片机的必会串口,这里就不多说了。唯一值得一提的就是STM32是有USART和UART之分的。UART就是通用异步收发传输器(Universal Asynchronous Receiver/Transmitter),是一种异步收发传输器。USART是通用同步/异步串行接收/发送器(Universal Synchronous/Asynchronous Receiver/Transmitter)。USART是一个全双工通用同步/异步串行收发模块,该接口是一个高度灵活的串行通信设备。从名字上可以看出,USART在UART基础上增加了同步功能,即USART是UART的增强型。

当然,一般应用时我们都是使用的异步串行通信,此时两者是一样的用法。具体在写代码时就注意不要将UART写成了USART,比如STM32F1系列有五个串口,分别是USART1,USART2,USART3,UART4,UART5。在使用4和5号串口的时候不要写错了。

本教程在STM32F103系列上实现五个串口的驱动,全部使用中断接收方式。最后的功能是上位机通过串口调试助手向任何一个串口发送数据(以回车键结束),单片机都即时通过串口1返回接收的数据。使用原子哥的精英版子测试通过,当然任何一个最小系统板或其他板子都可以测试。

本例程上传到了CSDN,可以作为串口通信的驱动模板下载使用。

CSDN下载地址:https://download.csdn.net/download/qq_30267617/20244343

百度网盘下载地址:https://pan.baidu.com/s/1Olju-OqyXFi06wOkpaKklg

提取码:5232

二. STM32CubeMx实现STM32F103系列串口驱动

串口的配置还是相对简单的。使用STM32CubeMx很容易实现串口的驱动,这里我们在CubeMx中新建一个工程,实现五个串口和两个LED灯的驱动。

  1. 使能时钟RCC,使用高速外部时钟(HSE)
    在这里插入图片描述

  2. 配置时钟,外部时钟源8MHz,配置系统时钟为72MHz。
    在这里插入图片描述

  3. 配置GPIO,实现两个LED灯。
    在这里插入图片描述

  4. 配置串口1,2,3

    以串口1为例,设置为异步通信方式,使能全局中断,其他使用默认。
    在这里插入图片描述

  5. 配置串口4和串口5

    以串口4为例,设置为异步通信方式,使能全局中断,其他使用默认。
    在这里插入图片描述6. 设置调试模式为ST-debug
    在这里插入图片描述

    然后就可以Generate Code了。

    但是我一般都是使用CubeMx生成驱动代码,然后摘到自己的工程里面,方便管理。 所以后面讲的就不是CubeMx的代码框架了。

三. 五个串口发送与中断接收例程

  1. 串口驱动代码

    新建uart.c和uart.h文件。uart.c中存放驱动代码与中断处理代码。

void uart1_init(u32 bound)
{	
	//UART 初始化设置
	huart1.Instance = USART1;
	huart1.Init.BaudRate = bound;
	huart1.Init.WordLength = UART_WORDLENGTH_8B;
	huart1.Init.StopBits = UART_STOPBITS_1;
	huart1.Init.Parity = UART_PARITY_NONE;
	huart1.Init.Mode = UART_MODE_TX_RX;
	huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
	huart1.Init.OverSampling = UART_OVERSAMPLING_16;
	if (HAL_UART_Init(&huart1) != HAL_OK)
	{
	}
	HAL_UART_Receive_IT(&huart1, (u8 *)aRxBuffer1, RXBUFFERSIZE);//该函数会开启接收中断:标志位UART_IT_RXNE,并且设置接收缓冲以及接收缓冲接收最大数据量
}

void uart2_init(u32 bound)
{	
	//UART 初始化设置
	huart2.Instance = USART2;
	huart2.Init.BaudRate = bound;
	huart2.Init.WordLength = UART_WORDLENGTH_8B;
	huart2.Init.StopBits = UART_STOPBITS_1;
	huart2.Init.Parity = UART_PARITY_NONE;
	huart2.Init.Mode = UART_MODE_TX_RX;
	huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
	huart2.Init.OverSampling = UART_OVERSAMPLING_16;
	if (HAL_UART_Init(&huart2) != HAL_OK)
	{
	}
	HAL_UART_Receive_IT(&huart2, (u8 *)aRxBuffer2, RXBUFFERSIZE);//该函数会开启接收中断:标志位UART_IT_RXNE,并且设置接收缓冲以及接收缓冲接收最大数据量
}

void uart3_init(u32 bound)
{	
	huart3.Instance = USART3;
	huart3.Init.BaudRate = bound;
	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;
	if (HAL_UART_Init(&huart3) != HAL_OK)
	{
	}
	HAL_UART_Receive_IT(&huart3, (u8 *)aRxBuffer3, RXBUFFERSIZE);//该函数会开启接收中断:标志位UART_IT_RXNE,并且设置接收缓冲以及接收缓冲接收最大数据量
}

void uart4_init(u32 bound)
{
  huart4.Instance = UART4;
  huart4.Init.BaudRate = bound;
  huart4.Init.WordLength = UART_WORDLENGTH_8B;
  huart4.Init.StopBits = UART_STOPBITS_1;
  huart4.Init.Parity = UART_PARITY_NONE;
  huart4.Init.Mode = UART_MODE_TX_RX;
  huart4.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart4.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart4) != HAL_OK)
  {
  }
  HAL_UART_Receive_IT(&huart4, (u8 *)aRxBuffer4, RXBUFFERSIZE);//该函数会开启接收中断:标志位UART_IT_RXNE,并且设置接收缓冲以及接收缓冲接收最大数据量
}

void uart5_init(u32 bound)
{
  huart5.Instance = UART5;
  huart5.Init.BaudRate = bound;
  huart5.Init.WordLength = UART_WORDLENGTH_8B;
  huart5.Init.StopBits = UART_STOPBITS_1;
  huart5.Init.Parity = UART_PARITY_NONE;
  huart5.Init.Mode = UART_MODE_TX_RX;
  huart5.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart5.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart5) != HAL_OK)
  {  
  }
  HAL_UART_Receive_IT(&huart5, (u8 *)aRxBuffer5, RXBUFFERSIZE);//该函数会开启接收中断:标志位UART_IT_RXNE,并且设置接收缓冲以及接收缓冲接收最大数据量
}

//UART底层初始化,时钟使能,引脚配置,中断配置
//此函数会被HAL_UART_Init()调用
//huart:串口句柄
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
	//GPIO端口设置
	GPIO_InitTypeDef GPIO_InitStruct;

	if (huart->Instance == USART1)
	{
		__HAL_RCC_USART1_CLK_ENABLE();
		__HAL_RCC_GPIOA_CLK_ENABLE();
		/**USART1 GPIO Configuration
	    PA9     ------> USART1_TX
	    PA10     ------> USART1_RX
	    */
		GPIO_InitStruct.Pin = GPIO_PIN_9;
		GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
		GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
		HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

		GPIO_InitStruct.Pin = GPIO_PIN_10;
		GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
		GPIO_InitStruct.Pull = GPIO_NOPULL;
		HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

		/* USART1 interrupt Init */
		HAL_NVIC_SetPriority(USART1_IRQn, 3, 3);
		HAL_NVIC_EnableIRQ(USART1_IRQn);
		
	}
	else if (huart->Instance == USART2)
	{
		__HAL_RCC_USART2_CLK_ENABLE();
		__HAL_RCC_GPIOA_CLK_ENABLE();
		/**USART2 GPIO Configuration
	    PA2     ------> USART2_TX
	    PA3     ------> USART2_RX
	    */
		GPIO_InitStruct.Pin = GPIO_PIN_2;
		GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
		GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
		HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

		GPIO_InitStruct.Pin = GPIO_PIN_3;
		GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
		GPIO_InitStruct.Pull = GPIO_NOPULL;
		HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

		/* USART2 interrupt Init */
		HAL_NVIC_SetPriority(USART2_IRQn, 2, 1);
		HAL_NVIC_EnableIRQ(USART2_IRQn);
	}
	else if (huart->Instance == USART3)
	{
		__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_InitStruct.Mode = GPIO_MODE_AF_PP;
		GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
		HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

		GPIO_InitStruct.Pin = GPIO_PIN_11;
		GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
		GPIO_InitStruct.Pull = GPIO_NOPULL;
		HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

		/* USART3 interrupt Init */
		HAL_NVIC_SetPriority(USART3_IRQn, 2, 2);
		HAL_NVIC_EnableIRQ(USART3_IRQn);
	}

	if (huart->Instance == UART4)
	{
		__HAL_RCC_UART4_CLK_ENABLE();
		__HAL_RCC_GPIOC_CLK_ENABLE();
		/**UART4 GPIO Configuration
	    PC10     ------> UART4_TX
	    PC11     ------> UART4_RX
	    */
		GPIO_InitStruct.Pin = GPIO_PIN_10;
		GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
		GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
		HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

		GPIO_InitStruct.Pin = GPIO_PIN_11;
		GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
		GPIO_InitStruct.Pull = GPIO_NOPULL;
		HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

		/* UART4 interrupt Init */
		HAL_NVIC_SetPriority(UART4_IRQn, 3, 1);
		HAL_NVIC_EnableIRQ(UART4_IRQn);
	}
	if (huart->Instance == UART5)
	{
		__HAL_RCC_UART5_CLK_ENABLE();

		__HAL_RCC_GPIOC_CLK_ENABLE();
		__HAL_RCC_GPIOD_CLK_ENABLE();
		/**UART5 GPIO Configuration
	    PC12     ------> UART5_TX
	    PD2     ------> UART5_RX
	    */
		GPIO_InitStruct.Pin = GPIO_PIN_12;
		GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
		GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
		HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

		GPIO_InitStruct.Pin = GPIO_PIN_2;
		GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
		GPIO_InitStruct.Pull = GPIO_NOPULL;
		HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);

		/* UART5 interrupt Init */
		HAL_NVIC_SetPriority(UART5_IRQn, 3, 2);
		HAL_NVIC_EnableIRQ(UART5_IRQn);
	}
}

  1. 中断处理函数

    中断函数里面处理接收数据,每一帧数据以回车符结束。每接收完一帧数据就把接收到的数据拷贝到预定义的数据中,等待主函数处理,同时标志位置1,标志着有新一帧数据,在主函数里面需要将标志位清零。

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	if (huart->Instance == USART1) //如果是串口1
	{
		if ((USART1_RX_STA & 0x8000) == 0) //接收未完成
		{
			if (USART1_RX_STA & 0x4000) //接收到了0x0d
			{
				if (aRxBuffer1[0] != 0x0a)
					USART1_RX_STA = 0; //接收错误,重新开始
				else
					USART1_RX_STA |= 0x8000; //接收完成了
			}
			else //还没收到0X0D
			{
				if (aRxBuffer1[0] == 0x0d)
					USART1_RX_STA |= 0x4000;
				else
				{
					USART1_RX_BUF[USART1_RX_STA & 0X3FFF] = aRxBuffer1[0];
					USART1_RX_STA++;
					if (USART1_RX_STA > (USART_REC_LEN - 1))
						USART1_RX_STA = 0; //接收数据错误,重新开始接收
				}
			}
		}
		if (USART1_RX_STA & 0x8000)
		{
			flag = 1;
			memcpy(data, USART1_RX_BUF, USART1_RX_STA & 0x3fff);
			USART1_RX_STA = 0;
		}
	}

	if (huart->Instance == USART2) //如果是串口1
	{
		if ((USART2_RX_STA & 0x8000) == 0) //接收未完成
		{
			if (USART2_RX_STA & 0x4000) //接收到了0x0d
			{
				if (aRxBuffer2[0] != 0x0a)
					USART2_RX_STA = 0; //接收错误,重新开始
				else
					USART2_RX_STA |= 0x8000; //接收完成了
			}
			else //还没收到0X0D
			{
				if (aRxBuffer2[0] == 0x0d)
					USART2_RX_STA |= 0x4000;
				else
				{
					USART2_RX_BUF[USART2_RX_STA & 0X3FFF] = aRxBuffer2[0];
					USART2_RX_STA++;
					if (USART2_RX_STA > (USART_REC_LEN - 1))
						USART2_RX_STA = 0; //接收数据错误,重新开始接收
				}
			}
		}

		if (USART2_RX_STA & 0x8000)
		{
			flag = 1;
			memcpy(data, USART2_RX_BUF, USART2_RX_STA & 0x3fff);
			USART2_RX_STA = 0;
		}
	}

	if (huart->Instance == USART3) //如果是串口1
	{
		if ((USART3_RX_STA & 0x8000) == 0) //接收未完成
		{
			if (USART3_RX_STA & 0x4000) //接收到了0x0d
			{
				if (aRxBuffer3[0] != 0x0a)
					USART3_RX_STA = 0; //接收错误,重新开始
				else
					USART3_RX_STA |= 0x8000; //接收完成了
			}
			else //还没收到0X0D
			{
				if (aRxBuffer3[0] == 0x0d)
					USART3_RX_STA |= 0x4000;
				else
				{
					USART3_RX_BUF[USART3_RX_STA & 0X3FFF] = aRxBuffer3[0];
					USART3_RX_STA++;
					if (USART3_RX_STA > (USART_REC_LEN - 1))
						USART3_RX_STA = 0; //接收数据错误,重新开始接收
				}
			}
		}
		if (USART3_RX_STA & 0x8000)
		{
			flag = 1;
			memcpy(data, USART3_RX_BUF, USART3_RX_STA & 0x3fff);
			USART3_RX_STA = 0;
		}
	}

	if (huart->Instance == UART4) //如果是串口1
	{
		if ((USART4_RX_STA & 0x8000) == 0) //接收未完成
		{
			if (USART4_RX_STA & 0x4000) //接收到了0x0d
			{
				if (aRxBuffer4[0] != 0x0a)
					USART4_RX_STA = 0; //接收错误,重新开始
				else
					USART4_RX_STA |= 0x8000; //接收完成了
			}
			else //还没收到0X0D
			{
				if (aRxBuffer4[0] == 0x0d)
					USART4_RX_STA |= 0x4000;
				else
				{
					USART4_RX_BUF[USART4_RX_STA & 0X3FFF] = aRxBuffer4[0];
					USART4_RX_STA++;
					if (USART4_RX_STA > (USART_REC_LEN - 1))
						USART4_RX_STA = 0; //接收数据错误,重新开始接收
				}
			}
		}

		if (USART4_RX_STA & 0x8000)
		{
			flag = 1;
			memcpy(data, USART4_RX_BUF, USART4_RX_STA & 0x3fff);
			USART4_RX_STA = 0;
		}
	}

	if (huart->Instance == UART5) //如果是串口1
	{
		if ((USART5_RX_STA & 0x8000) == 0) //接收未完成
		{
			if (USART5_RX_STA & 0x4000) //接收到了0x0d
			{
				if (aRxBuffer5[0] != 0x0a)
					USART5_RX_STA = 0; //接收错误,重新开始
				else
					USART5_RX_STA |= 0x8000; //接收完成了
			}
			else //还没收到0X0D
			{
				if (aRxBuffer5[0] == 0x0d)
					USART5_RX_STA |= 0x4000;
				else
				{
					USART5_RX_BUF[USART5_RX_STA & 0X3FFF] = aRxBuffer5[0];
					USART5_RX_STA++;
					if (USART5_RX_STA > (USART_REC_LEN - 1))
						USART5_RX_STA = 0; //接收数据错误,重新开始接收
				}
			}
		}

		if (USART5_RX_STA & 0x8000)
		{
			flag = 1;
			memcpy(data, USART5_RX_BUF, USART5_RX_STA & 0x3fff);
			USART5_RX_STA = 0;
		}
	}
}

//串口1中断服务程序
void USART1_IRQHandler(void)
{
	u32 timeout = 0;
#if SYSTEM_SUPPORT_OS //使用OS
	OSIntEnter();
#endif

	HAL_UART_IRQHandler(&huart1); //调用HAL库中断处理公用函数

	timeout = 0;
	while (HAL_UART_GetState(&huart1) != HAL_UART_STATE_READY) //等待就绪
	{
		timeout++; ////超时处理
		if (timeout > HAL_MAX_DELAY)
			break;
	}

	timeout = 0;
	while (HAL_UART_Receive_IT(&huart1, (u8 *)aRxBuffer1, RXBUFFERSIZE) != HAL_OK) //一次处理完成之后,重新开启中断并设置RxXferCount为1
	{
		timeout++; //超时处理
		if (timeout > HAL_MAX_DELAY)
			break;
	}
#if SYSTEM_SUPPORT_OS //使用OS
	OSIntExit();
#endif
}

//串口2中断服务程序
void USART2_IRQHandler(void)
{
	u32 timeout = 0;

	HAL_UART_IRQHandler(&huart2); //调用HAL库中断处理公用函数

	timeout = 0;
	while (HAL_UART_GetState(&huart2) != HAL_UART_STATE_READY) //等待就绪
	{
		timeout++; ////超时处理
		if (timeout > HAL_MAX_DELAY)
			break;
	}

	timeout = 0;
	while (HAL_UART_Receive_IT(&huart2, (u8 *)aRxBuffer2, RXBUFFERSIZE) != HAL_OK) //一次处理完成之后,重新开启中断并设置RxXferCount为1
	{
		timeout++; //超时处理
		if (timeout > HAL_MAX_DELAY)
			break;
	}
}

//串口2中断服务程序
void USART3_IRQHandler(void)
{
	u32 timeout = 0;

	HAL_UART_IRQHandler(&huart3); //调用HAL库中断处理公用函数

	timeout = 0;
	while (HAL_UART_GetState(&huart3) != HAL_UART_STATE_READY) //等待就绪
	{
		timeout++; ////超时处理
		if (timeout > HAL_MAX_DELAY)
			break;
	}

	timeout = 0;
	while (HAL_UART_Receive_IT(&huart3, (u8 *)aRxBuffer3, RXBUFFERSIZE) != HAL_OK) //一次处理完成之后,重新开启中断并设置RxXferCount为1
	{
		timeout++; //超时处理
		if (timeout > HAL_MAX_DELAY)
			break;
	}
}

//串口4中断服务程序
void UART4_IRQHandler(void)
{
	u32 timeout = 0;

	HAL_UART_IRQHandler(&huart4); //调用HAL库中断处理公用函数

	timeout = 0;
	while (HAL_UART_GetState(&huart4) != HAL_UART_STATE_READY) //等待就绪
	{
		timeout++; ////超时处理
		if (timeout > HAL_MAX_DELAY)
			break;
	}

	timeout = 0;
	while (HAL_UART_Receive_IT(&huart4, (u8 *)aRxBuffer4, RXBUFFERSIZE) != HAL_OK) //一次处理完成之后,重新开启中断并设置RxXferCount为1
	{
		timeout++; //超时处理
		if (timeout > HAL_MAX_DELAY)
			break;
	}
}

//串口5中断服务程序
void UART5_IRQHandler(void)
{
	u32 timeout = 0;

	HAL_UART_IRQHandler(&huart5); //调用HAL库中断处理公用函数

	timeout = 0;
	while (HAL_UART_GetState(&huart5) != HAL_UART_STATE_READY) //等待就绪
	{
		timeout++; ////超时处理
		if (timeout > HAL_MAX_DELAY)
			break;
	}

	timeout = 0;
	while (HAL_UART_Receive_IT(&huart5, (u8 *)aRxBuffer5, RXBUFFERSIZE) != HAL_OK) //一次处理完成之后,重新开启中断并设置RxXferCount为1
	{
		timeout++; //超时处理
		if (timeout > HAL_MAX_DELAY)
			break;
	}
}
  1. 主函数处理接收数据

    main函数内容如下。这样处理之后就实现无论哪个串口接收到了数据,都会在中断函数中将接收到的数据拷贝到data数组中,置位标志位flag,然后主函数中将data数组通过串口1输出,清空data数组,清零标志位,等待下一次接收。

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "key.h"


int main(void)
{
	u8 len;	
	u16 times=0;
	
    HAL_Init();                    	 	//初始化HAL库    
    Stm32_Clock_Init(RCC_PLL_MUL9);   	//设置时钟,72M
	delay_init(72);               		//初始化延时函数
	uart1_init(115200);					//初始化串口
	uart2_init(115200);					//初始化串口
	uart3_init(115200);					//初始化串口
	uart4_init(115200);					//初始化串口
	uart5_init(115200);					//初始化串口
	LED_Init();							//初始化LED	
	KEY_Init();							//初始化按键
	
    while(1)
    {
        if (flag)
		{
			HAL_UART_Transmit(&huart1,(uint8_t*)data,sizeof(data),1000);	//发送接收到的数据
			printf("\r\n"); 
			memset(data, 0, sizeof(data)); 
			flag = 0;
		}
		else
		{
			times++;
			if(times%200==0)printf("请输入数据,以回车键结束\r\n");  
			if(times%30==0)LED0=!LED0;//闪烁LED,提示系统正在运行.
			delay_ms(10);   
		} 
    }
}

工程文件上传到了百度网盘,提供免费下载。

下载地址:https://pan.baidu.com/s/1Olju-OqyXFi06wOkpaKklg
提取码:5232

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐