STM32L053R8 USART Hal库开发小结

it2022-05-05  110

 因为项目原因需要使用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 stm32l0xx_it.c 然后编译执行时会发现,发送不满100不打印,发送超过100只打印前100(这里的100是由于我使能的时候填的100)。 后面我考虑可能是因为只有到设定的数据量后才触发回调函数,然后我把HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);的Size改为了1 然后就是这样子了,对,它坏了。 于是开始翻各种博客,最后找到了通过直接对寄存器操作实现不限字数中断收发,顺便实现了printf重定向。 代码实现如下: 添加接收状态标记:

uint16_t USART2_RX_STA=0;

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

__HAL_UART_ENABLE_IT(&huart2,UART_IT_RXNE);

在中断处理函数中添加寄存器状态读取部分

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;

另附上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

实现效果如下:

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

(二)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发送需要打开串口的中断并使能,即

否则只能发送一次,不能再发送。 直接使用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; }

这里需要注意的就是DMA接收需要配置为循环模式,DMA发送配置为普通模式

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


最新回复(0)