前言
项目上需要5个串口,于是选型了STM32F103RCTx,编写代码的时候发现UART5没有DMA,于是自己写了一个驱动,来模拟DMA串口不定长空闲中断接收(关于DMA空闲中断另外一篇博文有所描述:https://blog.csdn.net/tiantangmoke/article/details/94438536)
CUBEMX配置
配置基本串口参数
打开中断
修改工程代码实现单字节接收中断
1、定义数据缓冲区
uint8_t RxUart5[1]; //单字节接收缓冲区
volatile uint16_t DMA_Usart5_RxSize=0; //虚拟DMA缓冲区接收数据的大小
volatile uint8_t recv_end_flag=0; //接收完成标志位
uint8_t RxDMABuf_5[RXBUF_5_SIZE]; //虚拟DMA缓冲区
volatile uint8_t RxBuf_5_LOCK = 0; //处理数据时锁定缓冲区标志位
uint8_t RxBuf_5[RXBUF_5_SIZE]; //待处理接收数据缓冲区
volatile uint16_t RxBufSize_5 = 0; //待处理接收数据缓冲区内接收数据的大小
volatile uint16_t Uart5_Idle_Cnt = 0; //空闲计时计数
2、修改MX_UART5_Init
后面加上:
HAL_UART_Receive_IT(&huart5, (uint8_t*)RxUart5, 1);
__HAL_UART_ENABLE_IT(&huart5, UART_IT_RXNE);
RxUart5 是单字节接收的缓冲区
uint8_t RxUart5[1];
/* UART5 init function */
void MX_UART5_Init(void)
{
huart5.Instance = UART5;
huart5.Init.BaudRate = 115200;
huart5.Init.WordLength = UART_WORDLENGTH_8B;
huart5.Init.StopBits = UART_STOPBITS_1;
huart5.Init.Parity = UART_PARITY_NONE;
huart5.Init.Mode = UART_MODE_TX_RX;
huart5.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart5.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart5) != HAL_OK)
{
Error_Handler();
}
HAL_UART_Receive_IT(&huart5, (uint8_t*)RxUart5, 1);
__HAL_UART_ENABLE_IT(&huart5, UART_IT_RXNE);
}
3、修改HAL_UART_RxCpltCallback
HAL_UART_RxCpltCallback是一个虚函数,我们这里重定义,当接收到一个字节的时候,中断回调函数会调用这个虚函数
RxDMABuf_5这个是我们模拟DMA的缓冲区
每次接收到一个字节,就重置Uart5_Idle_Cnt,这个是空闲中断的计时,当为0则认为一帧接收完成,进而处理数据
//重定义虚函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef * huart)
{
if(huart->Instance == UART5 && (RxBuf_5_LOCK == 0))
{
RxDMABuf_5[DMA_Usart5_RxSize] = RxUart5[0];
DMA_Usart5_RxSize ++;
Uart5_Idle_Cnt = 20; //20毫秒没有接收到数据认为接收完成
HAL_UART_Receive_IT(&huart5, (uint8_t*)RxUart5, 1);
}
}
4、处理空闲计数
这个函数处理2个东西,
一个是串口死机时复位,HAL的串口有时会出错,然后一直无反应,这样可以增加可靠性。
一个是空闲中断的虚拟判断。通过Uart5_Idle_Cnt的倒计时实现,Uart5_Idle_Cnt在每次接收一个字节的数据都会置位。长时间每接收到字节数据,将会触发处理数据。
static void Uart5_Reset()
{
memset(&huart5,0,sizeof(UART_HandleTypeDef) );
HAL_UART_MspDeInit(&huart5);
MX_UART5_Init();
}
void Uart_Reset_Idle_Task_1ms()
{
if(huart5.ErrorCode != 0)
{
UartErrCnt ++;
Uart5_Reset();
}
if(Uart5_Idle_Cnt > 0)
{
Uart5_Idle_Cnt --;
if(Uart5_Idle_Cnt == 0)
{
RxBuf_5_LOCK = 1; //防止这边正在传输数据,中断改变了数据
RxBufSize_5 = DMA_Usart5_RxSize;
memcpy(RxBuf_5,RxDMABuf_5,DMA_Usart5_RxSize);
DMA_Usart5_RxSize = 0;
RxBuf_5_LOCK = 0;
}
}
}
5、编写对外接口函数
所有数据都在后台处理好了,我们的应用软件只需简单调用这两个函数即可实现非阻塞式的收发数据。
uint16_t Uart_GetRxSize(UART_HandleTypeDef *huart,uint8_t *buf , uint16_t DstBufSize)
uint8_t Uart_SendData(UART_HandleTypeDef *huart,uint8_t *buf,uint16_t Size)
uint16_t Uart_GetRxSize(UART_HandleTypeDef *huart,uint8_t *buf , uint16_t DstBufSize)
{
uint16_t Size = 0;
if(huart->Instance == UART5)
{
if(RxBufSize_5 > 0)
{
Size = RxBufSize_5;
if(DstBufSize < RxBufSize_5)
RxBufSize_5 = DstBufSize;
memcpy(buf,RxBuf_5,RxBufSize_5);
RxBufSize_5 = 0;
}
}
return Size;
}
uint8_t Uart_SendData(UART_HandleTypeDef *huart,uint8_t *buf,uint16_t Size)
{
if(Size == 0 )
return 0;
if(huart->Instance == UART5 && Size <TXBUF_5_SIZE )
{
HAL_UART_Transmit_IT(&huart5,buf,Size);
return 1;
}
return 0;
}
收发数据示例
void UART5_TEST_10ms()
{
uint8_t rxBuf[100] = {0};
uint8_t rxSize = 0;
rxSize = Uart_GetRxSize(DEBUG_UART,rxBuf,100);
if(rxSize == 0)
return;
Uart_SendData(DEBUG_UART,rxBuf,(uint8_t)rxSize );
}