因为项目原因需要使用stm32l0xx芯片,原本在f103上使用的是标准库开发,改用后发现stm32l0由于比较新,不支持标准库开发,移植很麻烦,只能重新学Hal库开发。 在开发上Hal库还是比较完善的,基本在STM32CubeMX的帮助下可以生成所有所需要的初始化函数,只是需要尽量提前想好需要哪些配置,否则中间再添加main函数内写的代码会被清除。目前把串口收发做好了,查了很多资料,参考了很多博客,因为比较杂一时想不起哪些,无法放全链接,需要可以联系我补上。
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);//串口中断接收,以中断方式接收指定长度数据。正常情况下开发流程是(一些问题和改进后面会说):
直接调用
HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_USART2_UART_Init();这段代码由MX直接生成,不需要改动什么。 在USART_Init中
if(Hal_UART_Init(&huart2) ==OK)这是使能了USART2以及配置一些硬件设备
在初始化后,进行中断处理函数,在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)作为中断处理函数是每次中断触发都会运行的,所以如果有什么自己写的中断服务函数,可以在这里面添加。
回调函数我是写在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();这样就能打印出接收的数据。
中断使能是在初始化后添加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实现效果如下:
由衷感谢提供了帮助的博主。
在实现上面中断收发后,就想把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