STM32L053R8 USART Hal库开发小结

 因为项目原因需要使用stm32l0xx芯片,原本在f103上使用的是标准库开发,改用后发现stm32l0由于比较新,不支持标准库开发,移植很麻烦,只能重新学Hal库开发。
 在开发上Hal库还是比较完善的,基本在STM32CubeMX的帮助下可以生成所有所需要的初始化函数,只是需要尽量提前想好需要哪些配置,否则中间再添加main函数内写的代码会被清除。目前把串口收发做好了,查了很多资料,参考了很多博客,因为比较杂一时想不起哪些,无法放全链接,需要可以联系我补上。

(一)USART中断收发(使用的是usart2)

 Hal库里中断收发主要用到的函数是

HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)//串口中断发送,以中断方式发送指定长度的数据。
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);//串口中断接收,以中断方式接收指定长度数据。

正常情况下开发流程是(一些问题和改进后面会说):

(1)初始化USART

直接调用

HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART2_UART_Init();

这段代码由MX直接生成,不需要改动什么。
在USART_Init中

if(Hal_UART_Init(&huart2) ==OK)

这是使能了USART2以及配置一些硬件设备

(2)中断处理函数

在初始化后,进行中断处理函数,在stm32l0xx_it.c中找到

void USART2_IRQHandler(void)
{
  /* USER CODE BEGIN USART2_IRQn 0 */

  /* USER CODE END USART2_IRQn 0 */
  HAL_UART_IRQHandler(&huart2);
  /* USER CODE BEGIN USART2_IRQn 1 */

  /* USER CODE END USART2_IRQn 1 */
}

这写都是生成好的,其中 HAL_UART_IRQHandler(&huart2);是Hal库处理UART中断请求,这个函数是一个总的中断处理函数,也就是很多串口的中断处理最后都汇集到这个地方来处理go to definition,会发现其内是一系列包括清中断、读取寄存器等操作,我们也不需要管,但也不能删掉。
void USART2_IRQHandler(void)作为中断处理函数是每次中断触发都会运行的,所以如果有什么自己写的中断服务函数,可以在这里面添加。

(3)回调函数

回调函数我是写在main.c里的

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{

	if(huart->Instance==USART2)//判断,如果是串口2
    {
 
          //写入自己要实现的代码
    }
	
}

每次中断后会进入这段函数,go to definition一下会发现

__weak void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(huart);

  /* NOTE: This function should not be modified, when the callback is needed,
           the HAL_UART_RxHalfCpltCallback can be implemented in the user file.
   */
}

出现在stn32l0xx_hal_uart.c里,这里__weak表示可以在用户文件中实现,实现后只会调用用户文件里的函数。
我在这个函数里调用了HAL_UART_Transmit();这样就能打印出接收的数据。

(4)使能中断

中断使能是在初始化后添加HAL_UART_Receive_IT();
实际使用

__HAL_UART_ENABLE_IT(&huart2,UART_IT_RXNE);

也可以实现。
在这之后,中断收发基本都都好了,整个过程除了添加收发数组,使能函数和回调函数,基本不用做什么改动。
这里要记得 HAL_UART_IRQHandler(&huart2);后还要再用HAL_UART_Receive_IT();使能中断,不然只能收一次。

最后代码是这样的:

main.c
变量
STM32L053R8 USART Hal库开发小结_第1张图片STM32L053R8 USART Hal库开发小结_第2张图片
stm32l0xx_it.c
定义
STM32L053R8 USART Hal库开发小结_第3张图片
然后编译执行时会发现,发送不满100不打印,发送超过100只打印前100(这里的100是由于我使能的时候填的100)。
STM32L053R8 USART Hal库开发小结_第4张图片
STM32L053R8 USART Hal库开发小结_第5张图片
后面我考虑可能是因为只有到设定的数据量后才触发回调函数,然后我把HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);的Size改为了1
STM32L053R8 USART Hal库开发小结_第6张图片
STM32L053R8 USART Hal库开发小结_第7张图片
然后就是这样子了,对,它坏了。
于是开始翻各种博客,最后找到了通过直接对寄存器操作实现不限字数中断收发,顺便实现了printf重定向。
代码实现如下:
添加接收状态标记:

uint16_t USART2_RX_STA=0;

在usart2初始化函数里添加中断使能

__HAL_UART_ENABLE_IT(&huart2,UART_IT_RXNE);

STM32L053R8 USART Hal库开发小结_第8张图片
在中断处理函数中添加寄存器状态读取部分

void USART2_IRQHandler(void)                	//串口2中断服务程序
{
		
	  
	uint8_t Res;
     
	HAL_UART_IRQHandler(&huart2);
    
	if(__HAL_UART_GET_IT(&huart2, UART_IT_RXNE) != RESET)  //产生中断
    {
          USART2->RQR |= 0x08;
          Res =USART2->RDR;//USARTx_RX_Data(&UartHandle);
        if((USART2_RX_STA&0x8000)==0)//
       {
               if(USART2_RX_STA&0x4000)//
             {
                     if(Res!=0x0a)
											 USART2_RX_STA=0;//
                    else 
											USART2_RX_STA|=0x8000;        // 
             }
           else 
           { 
                 if(Res==0x0d)
									 USART2_RX_STA|=0x4000;
                 else
                {
                       USART2_RX_BUF[USART2_RX_STA&0X3FFF]=Res ;
                      USART2_RX_STA++;
                      if(USART2_RX_STA>(USART_REC_LEN-1))
												USART2_RX_STA=0;        
               }         
          }
       }  
 
  } 
		HAL_NVIC_ClearPendingIRQ(USART2_IRQn);	
}

当有数据时产生中断,标记位为1,数据存入USART2_RX_BUF。
在主函数while(1)里添加

if(USART2_RX_STA&0x8000)//接收到数据
{
	//写入自己要实现的代码
	USART2_RX_STA = 0;
}

发送时需要发送数据长度,可以由下面操作得到

len=USART2_RX_STA&0x3f;

STM32L053R8 USART Hal库开发小结_第9张图片
另附上printf重定向部分

#if 1
#pragma import(__use_no_semihosting)             
//标准库需要的支持函数                 
struct __FILE 
{ 
	int handle; 

}; 

FILE __stdout;       
//定义_sys_exit()以避免使用半主机模式    
void _sys_exit(int x) 
{ 
	x = x; 
} 
 
//重定义fputc函数 
int fputc(int ch, FILE *f)
{      
	while((USART2->ISR&0X40)==0)
	{
	};//循环发送,直到发送完毕   
    USART2->TDR = (uint8_t) ch;      
	return ch;
}
#endif 

实现效果如下:
STM32L053R8 USART Hal库开发小结_第10张图片

由衷感谢提供了帮助的博主。

(二)USART DMA IDLE收发

  在实现上面中断收发后,就想把usart1用DMA来实现收发,与之前一样DMA用到的函数是

HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);//串口DMA发送,以DMA方式发送指定长度的数据。
HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);//串口DMA接收,以DMA方式接收指定长度的数据

与中断一样串口发送很简单,其他的不多说,这里注意一个就是DMA发送需要打开串口的中断并使能,即

1
在这里插入图片描述
否则只能发送一次,不能再发送。
直接使用DMA接收与中断接收基本类似,会有类似的数据长度固定的问题,下面直接给出解决方案:
定义接收标志

volatile uint8_t  RX_flag=0;  //IDLE 接收标志

在初始化函数里使能中断和DMA


	HAL_UART_Receive_DMA(&huart1, usartDMA_rxBuf, RECEIVELEN);  /*使能DMA接收*/
	__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);  /*使能串口空闲中断*/
 

在这里插入图片描述
在中断处理函数添加寄存器读取部分

void USART1_IRQHandler(void)
{
  UsartReceive_IDLE(&huart1);
  HAL_UART_IRQHandler(&huart1);

}
void UsartReceive_IDLE(UART_HandleTypeDef *huart)  
{  
    uint32_t temp;  

    if((__HAL_UART_GET_FLAG(huart,UART_FLAG_IDLE) != RESET))  
    {   
      __HAL_UART_CLEAR_IDLEFLAG(&huart1);  
      HAL_UART_DMAStop(&huart1);  
      temp = huart1.hdmarx->Instance->CNDTR;  
      rx_len =  RECEIVELEN - temp; 
      HAL_UART_Receive_DMA(&huart1,usartDMA_rxBuf,RECEIVELEN); 
	
			RX_flag=1;
			
		}  
} 

在主函数while(1)里添加

if(RX_flag == 1)
{
     //写入自己要实现的代码
     rx_len = 0;
     RX_flag = 0;
}

STM32L053R8 USART Hal库开发小结_第11张图片这里需要注意的就是DMA接收需要配置为循环模式,DMA发送配置为普通模式

STM32L053R8 USART Hal库开发小结_第12张图片
STM32L053R8 USART Hal库开发小结_第13张图片
实现效果如下
STM32L053R8 USART Hal库开发小结_第14张图片
以上为开发过程中自己的理解以及参考别的博客实现的串口功能,存在不足与误导感谢批评指正,有问题也可以留言讨论。
再次感谢提供帮助的博主博客。
附参考了以下链接的部分内容:
http://www.stmcu.org.cn/module/forum/thread-609561-1-1.html
https://blog.csdn.net/youmeichifan/article/details/51750435

你可能感兴趣的:(嵌入式,stm32,中断,stm32l053r8,usart,DMA)