基于HAL库的stm32F7串口通信

串口相关的源码一般在SYSTEM分组之下的usart.c和usart.h中。下面我们讨论如何实现串口通信。

初始化串口相关参数

我们自定义函数uart_init来初始化串口相关参数,如波特率,字长,停止位,奇偶校验位,有无硬件流控,收发模式等,其中也会调用HAL库提供的函数HAL_UART_Init,不需要我们编写,此函数的作用的是初始化串口参数配置,uart_init具体函数代码如下

 void uart_init(u32 bound)
{	
	//UART 初始化设置
	UART1_Handler.Instance=USART1;					    //USART1
	UART1_Handler.Init.BaudRate=bound;				    //波特率
	UART1_Handler.Init.WordLength=UART_WORDLENGTH_8B;   //字长为8位数据格式
	UART1_Handler.Init.StopBits=UART_STOPBITS_1;	    //一个停止位
	UART1_Handler.Init.Parity=UART_PARITY_NONE;		    //无奇偶校验位
	UART1_Handler.Init.HwFlowCtl=UART_HWCONTROL_NONE;   //无硬件流控制
	UART1_Handler.Init.Mode=UART_MODE_TX_RX;		    //收发模式
	HAL_UART_Init(&UART1_Handler);					    //HAL_UART_Init()使能UART1

	HAL_UART_Receive_IT(&UART1_Handler, (u8 *)aRxBuffer, RXBUFFERSIZE);//开启接收中断:标志位UART_IT_RXNE,并且设置接收缓冲以及接受最大的数据量
  
}

我们会看到,在uart_init函数的最后一行调用了 HAL_UART_Receive_IT函数,这个函数的功能是开启接收中断:使得标志位UART_IT_RXNE置1,并且设置接收缓冲以及接受最大的数据量,缓冲区的大小代表一次接收的数据量,我们一般会在usart.h中通过宏定义设置缓冲区大小。如#define RXBUFFERSIZE   1 //缓冲区大小为1,代表一次只能接收一个数据。当然如果我们不需要开启中断,那么就不需要调用HAL_UART_Receive_IT函数。

GPIO初始化

在串口初始化函数HAL_UART_Init内部,会调用串口Msp函数HAL_UART_MspInit(自定义的函数)来设置与MCU相关的配置,来设置GOIO初始化。在开启中断的情况下,此函数中会调用HAL_NVIC_EnableIRQ和HAL_NVIC_SetPriority两个函数,作用分别是 UART中断通道和抢占优先级,子优先级的设置;同理不开中断时并不需要调用,HAL_UART_MspInit函数的具体代码如下

void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{ 
    //GPIO端口设置
	GPIO_InitTypeDef GPIO_Initure;
	
	if(huart->Instance==USART1)//如果是串口1,进行串口1MSP的初始化
	{
		__HAL_RCC_GPIOA_CLK_ENABLE();			//使能GPIOA时钟
		__HAL_RCC_USART1_CLK_ENABLE();			//使能USART1时钟
	
		GPIO_Initure.Pin=GPIO_PIN_9;			//PA9
		GPIO_Initure.Mode=GPIO_MODE_AF_PP;		//复用推挽输出
		GPIO_Initure.Pull=GPIO_PULLUP;			//上拉
		GPIO_Initure.Speed=GPIO_SPEED_FAST;		//高速
		GPIO_Initure.Alternate=GPIO_AF7_USART1;	//复用为USART1
		HAL_GPIO_Init(GPIOA,&GPIO_Initure);	   	//初始化PA9

		GPIO_Initure.Pin=GPIO_PIN_10;			//PA10
		HAL_GPIO_Init(GPIOA,&GPIO_Initure);	   	//初始化PA10
		
#if EN_USART1_RX
		HAL_NVIC_EnableIRQ(USART1_IRQn);				//使能UART1中断通道
		HAL_NVIC_SetPriority(USART1_IRQn,3,3);			//抢占优先级3,子优先级3
#endif	
	}
}

到目前只要在主函数中调用自定义的uart_init函数,便可以执行以上提到的所有代码实现初始化串口相关参数和GPIO的初始化操作。以上操作属于串口通信的初始化部分,需要要在主函数一开始调用的,代码如下;接下来我们将介绍串口收发程序整个执行流程。

int main(void)
{
    Cache_Enable();                 //打开L1-Cache
    HAL_Init();				        //初始化HAL库
    Stm32_Clock_Init(432,25,2,9);   //设置时钟,216Mhz 
    delay_init(216);                //延时初始化
	uart_init(115200);		        //串口初始化
    LED_Init();                     //初始化LED
    while(1)
    {
       
		}
}

 串口收发程序执行流程

我们通过电脑给单片机发送数据,单片机首先需要接收电脑发来的数据,接收完成后再发送给电脑端,这就是串口收发的过程,那么单片机是如何实现这一整个过程的呢?开启中断和不开启中断的执行流程有点区别,下面我们都会讨论,执行流程如下图所示。

  • 开启中断 

电脑发送数据给单片机,会触发单片机启动程序中的中断USART1_IRQHandler,我们会自定义函数USART1_IRQHandler,所以只要单片机接收到数据,便会自动调用我们自定义的函数USART1_IRQHandler,在此函数我们会调用HAL库提供的中断服务函数(不需要我们编写)HAL_UART_IRQHandler(&UART1_Handler),然后关闭中断,至于为什么需要在这个函数中再次开启中断下面会讲解,自定义函数USART1_IRQHandler的代码实现如下

void USART1_IRQHandler(void)                	
{ 
	u32 timeout=0;
    u32 maxDelay=0x1FFFF;
#if SYSTEM_SUPPORT_OS	 	//使用OS
	OSIntEnter();    
#endif
	
	HAL_UART_IRQHandler(&UART1_Handler);	//调用HAL库的中断服务函数
	//以下操作是重新开启中断
	timeout=0;
    while (HAL_UART_GetState(&UART1_Handler)!=HAL_UART_STATE_READY)//等待就绪
	{
        timeout++;超时处理
        if(timeout>maxDelay) break;		
	}
     
	timeout=0;
//一次处理完成后重新开启中断并设置RxXferCountÎ为1
	while(HAL_UART_Receive_IT(&UART1_Handler,(u8 *)aRxBuffer, RXBUFFERSIZE)!=HAL_OK)
	{
        timeout++; //超时处理
        if(timeout>maxDelay) break;	
	}
#if SYSTEM_SUPPORT_OS	 	//使用OS
	OSIntExit();  											 
#endif
} 

HAL_UART_IRQHandler函数的作用是判断此次中断属于哪类中断,然后调用相对应的中断函数,实现相对应的操作。比如这里是接受完成中断,那么便会调用HAL库提供的UART_RECEIVE_IT函数,这个函数会把接受到的数据放到串口句柄成员变量pRxBuffPtr接受缓冲区中(缓冲区大小是自己设置的,在串口初始部分已经讲过);每放入一个数据记录缓冲区大小的变量便会减1,不为0便会一直接收数据,减为0后便会调用接收完成回调函数HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart),同时关闭中断,这就是为什么在自定义函数USART1_IRQHandler中需要重新开启中断。HAL_UART_RxCpltCallback函数需要我们自己编写,此函数会将接收缓冲区中的数据取出,然后通过调用HAL库提供的发送函数HAL_UART_Transmit(&UART1_Handler,&rec,1,1000)将数据发送给电脑端。自定义函数HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)的代码实现如下

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  u8 rec;
	if(huart->Instance==USART1)//Èç¹ûÊÇ´®¿Ú1
	{
		  rec = *((huart->pRxBuffPtr)-1);
          HAL_UART_Transmit(&UART1_Handler,&rec,1,1000);
	}
}
  •  不开启中断

 不开中断时,执行流程比较简单,当电脑发送数据给单片机,会触发单片机启动程序中的USART1_IRQHandler,我们会自定义函数USART1_IRQHandler,所以只要单片机接收到数据,便会自动调用我们自定义的函数USART1_IRQHandler,只是此时的这个函数的编写跟开启中断情况下有点不一样,不开中断时,我们直接把中断控制逻辑写在USART1_IRQHandler中,也就是HAL_UART_RxCpltCallback中的代码会直接写到USART1_IRQHandler中。因为不开中断时,USART1_IRQHandler函数不会调用HAL库提供的中断服务函数HAL_UART_IRQHandler,那么它是如何实现数据的接收操作呢?它是通过在USART1_IRQHandler函数中调用HAL库提供的串口接收函数(不需要我们编写,直接调用即可)HAL_UART_Receive(&UART1_Handler,&Res,1,1000),将数据保存到接收缓冲区中。USART1_IRQHandler函数的代码如下

void USART1_IRQHandler(void)                	
{ 
	u8 Res;
#if SYSTEM_SUPPORT_OS	 	//ʹÓÃOS
	OSIntEnter();    
#endif
	if((__HAL_UART_GET_FLAG(&UART1_Handler,UART_FLAG_RXNE)!=RESET))  //½ÓÊÕÖжÏ(½ÓÊÕµ½µÄÊý¾Ý±ØÐëÊÇ0x0d 0x0a½áβ)
	{
        HAL_UART_Receive(&UART1_Handler,&Res,1,1000); 
		if((USART_RX_STA&0x8000)==0)//½ÓÊÕδÍê³É
		{
			if(USART_RX_STA&0x4000)//½ÓÊÕµ½ÁË0x0d
			{
				if(Res!=0x0a)USART_RX_STA=0;//½ÓÊÕ´íÎó,ÖØпªÊ¼
				else USART_RX_STA|=0x8000;	//½ÓÊÕÍê³ÉÁË 
			}
			else //»¹Ã»ÊÕµ½0X0D
			{	
				if(Res==0x0d)USART_RX_STA|=0x4000;
				else
				{
					USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
					USART_RX_STA++;
					if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//½ÓÊÕÊý¾Ý´íÎó,ÖØпªÊ¼½ÓÊÕ	  
				}		 
			}
		}   		 
	}
	HAL_UART_IRQHandler(&UART1_Handler);	
#if SYSTEM_SUPPORT_OS	 	//ʹÓÃOS
	OSIntExit();  											 
#endif
} 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值