单片机型号:STM32L051C8T6
开发环境MDK5.12
库版本:STM32L0xx_HAL_Driver V1.1.0
主机环境:Windows XP
承接上文,为采用双缓冲机制,重新开辟一个500字节的空间aTxBuffer来存放所需发送的字节,初始化uart_snd的指针
uart_snd.front = aTxBuffer; uart_snd.rear = aTxBuffer; //两个指针指向相同的地址空间
/********************************************************************** 函数:uart_char() 函数作用:发送一个字节数据 参数: uint8_t *fmt--------------------------------需要发送的数据 返回值: 上一版本:无 当前版本:1.0 作者: 最后修改时间:2015-04-08 说明: **********************************************************************/ void uart_char(uint8_t fmt) { uint8_t ret = HAL_OK; if(uart_snd.rear == uart_snd.front) { //队首指针和队尾指针相等表明当前没有数据需要发送,这里需要手动开启发送请求 *uart_snd.rear = fmt; uart_snd.rear++; if(uart_snd.rear >= aTxBuffer + BUFFSIZE) uart_snd.rear = aTxBuffer; do { ret = HAL_UART_Transmit_IT(&UartHandle,uart_snd.front,1);//请求发送下一个数据 }while(ret != HAL_OK); } else { *uart_snd.rear = fmt; uart_snd.rear++; if(uart_snd.rear >= aTxBuffer + BUFFSIZE) uart_snd.rear = aTxBuffer; } }在此判断是否是重新启动发送请求,如果是重新启动则需要手动发起请求,即调用HAL_UART_Transmit_IT()发送函数,如果已经在发送数据过程中则只需要更新rear指针即可。我们的uart_send函数也需要更改不再调用printf而是调用uart_char函数来实现
int8_t uart_send(uint8_t *fmt, uint8_t len) { while(len) { uart_char(*fmt); fmt++; len--; } return 0; }当数据发送成功后中断函数会调用HAL_UART_TxCpltCallback()回调函数来处理串口发送流程,
/** * @brief Tx Transfer completed callback * @param UartHandle: UART handle. * @note This example shows a simple way to report end of IT Tx transfer, and * you can add your own implementation. * @retval None */ void HAL_UART_TxCpltCallback(UART_HandleTypeDef * huart) { uint8_t ret = HAL_OK; uart_snd.front++; //更新rear指针 if(uart_snd.front >= (aTxBuffer + BUFFSIZE)) uart_snd.front = aTxBuffer; if(uart_snd.front != uart_snd.rear) { //如果队首指针和队尾指针不同表明缓冲区中有数据还未发送 do { ret = HAL_UART_Transmit_IT(&UartHandle,uart_snd.front,1);//请求发送下一个数据 }while(ret != HAL_OK); } }这里我们要判断缓冲区中的数据是否已经发送完毕即判断front指针和rear指针是否相等,如果相等,则表明数据发送完毕,无需再调用HAL_UART_Transmit_IT()发送函数。至此发送流程编辑完毕,在主函数中调用uart_send()函数来进行测试
uint8_t *abc = "abcdefghijklmnopqrstuvwxyz0123456789\r\n"; while(1) { uart_send(abc,strlen(abc)); }经测试发送正常,结果如下
一切显得顺风顺水,就在我以为大功告成之时,从PC机的串口工具向单片机发送了一些数据,结果单片机卡住了,串口输出也没得了。。。跟想象中的结果不一样那,之前采用单缓冲机制是完全没问题的,怎么使用双缓冲就有问题了呢,串口接收流程没有改动,只是改动了串口发送,而且单独测试串口发送也没问题,调试了半天看下是否有串口错误产生呢。因此增加了串口出错回调函数
/** * @brief UART error callbacks * @param UartHandle: UART handle * @note This example shows a simple way to report transfer error, and you can * add your own implementation. * @retval None */ void HAL_UART_ErrorCallback(UART_HandleTypeDef *UartHandle) { printf("error code:%X\r\n",UartHandle->ErrorCode); }在里面输出错误码,测试结果如下
输出的错误码为0x08,查找错误码的定义发现该错误码是ORE错误
/** * @brief HAL UART Error Code structure definition */ typedef enum { HAL_UART_ERROR_NONE = 0x00, /*!< No error */ HAL_UART_ERROR_PE = 0x01, /*!< Parity error */ HAL_UART_ERROR_NE = 0x02, /*!< Noise error */ HAL_UART_ERROR_FE = 0x04, /*!< frame error */ HAL_UART_ERROR_ORE = 0x08, /*!< Overrun error */ HAL_UART_ERROR_DMA = 0x10 /*!< DMA transfer error */ }HAL_UART_ErrorTypeDef;而我们知道ORE错误是只会出现在串口接收中,难道打开串口发送中断会影响到串口接收中断?想想总觉得不可能,这应该是全双工串口的那,两者应该是相互独立的才对那。这让我陷入了深深的纠结中。。。而且在测试结果中一旦出现了ORE错误会一直输出该错误,很明显该错误标志一直没有被清零,同时我也没有往单片机发送串口数据之前使用STM32F030C8T6型号单片机时会有串口乱进的情况是因为打开串口接收中断时会同时开启ORE中断,最后解决办法是在中断处理函数中增加错误标志的检测并清除相应的标志。本以为STM32L0xx_Hal_Driver库封装了这么多,会帮助我们减少这类错误呢,结果还是出现了,查看HAL_UART_Receive_IT()函数代码会发现该函数同样使能了各个错误中断
/* Enable the UART Parity Error Interrupt */ __HAL_UART_ENABLE_IT(huart, UART_IT_PE); /* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */ __HAL_UART_ENABLE_IT(huart, UART_IT_ERR);查看源代码中中断处理函数HAL_UART_IRQHandle()里面也进行了各个错误的判断以及清除标志操作。
该问题还未想到解决办法,等解决之后再更新吧。。。
尝试了两天依然没啥起色,STM32L0xx_Hal_Driver库虽然做了很多封装便于灵活使用3种方式进行通讯,但却使得上层应用无法灵活检测各个中断标志,在使用中断方式发送串口数据时,是开启的TXE中断检测,同时最后还要检测到TC标志置位后才会调用HAL_UART_TxCpltCallback处理函数,因此我们在使用发送缓冲区时每发送一个字节需要进入两次串口中断才得以发送下一个字节数据,这种情况下还是StdPeriph_Lib库灵活,个人觉得要解决这个问题可能需要修改底层库函数,也进行过尝试,没搞定,期待大牛来指导一下吧,持续关注中。。。