前言

項目上需要5個串口,於是選型了STM32F103RCTx,編寫代碼的時候發現UART5沒有DMA,於是自己寫了一個驅動,來模擬DMA串口不定長空閒中斷接收

CUBEMX配置

配置基本串口參數

stm32f103RB6 cubemx tim2 trigger source 沒有disable 選擇_#UART


打開中斷

stm32f103RB6 cubemx tim2 trigger source 沒有disable 選擇_#空閒中斷_02

修改工程代碼實現單字節接收中斷

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 );
}