【嵌入式系統】STM32串列埠通訊的四種方法(基於RTOS)

拾牙慧者發表於2020-11-13

1、序列通訊的基本引數

串列埠的通訊方式是將位元組拆分成一個接一個的位再傳輸出去,接收方再將此一個一個的位組合成原來的字元,如此形成一個位元組的完整傳輸,在資料傳輸時,應在通訊埠的初始化時設定幾個通訊引數。

1)波特率,即傳送資料的速度。波特率的意思就是在一秒中可以傳輸的資料位數,單位是bps。如果採用波特率4800bps進行傳輸,那麼每秒可以傳輸600個byte。
2)資料位,當接收裝置收到起始位後,緊接著就會收到資料位,資料位的個數可以是5、6、7或者8位。在字元資料傳輸的過程中,資料位從最低有效位開始傳輸。
3)起始位,在串列埠線上,沒有資料傳輸時處於邏輯"1"狀態,當傳送裝置要傳送一個字元資料時,首先發出一個邏輯“0"訊號,這個邏輯低電平就是起始位。
4)停止位,是一個字元資料的結束標誌,它可以是1位、1.5位或者2位。
5)奇偶校驗位,資料位傳送完之後,就可以傳送奇偶校驗位。奇偶校驗用於有限差錯校驗,通訊雙方在通訊時約定一致的奇偶校驗方式。

在這裡插入圖片描述

2、輪詢方式程式碼效果

在這裡插入圖片描述

char ch;
/* Infinite loop */
for(;;)
{
if(HAL_UART_Receive(&huart1,(uint8_t*)&ch,1,100) == HAL_OK)
	printf("%c",ch);
  //osDelay(1);
}

在這裡插入圖片描述
輪詢方式不適合接收一大段的資料,否則會卡死,如上圖。

3、中斷方式程式碼效果

在這裡插入圖片描述

#define MAX_RECV_LEN 128							//定義的一次最多接收位元組的位數
uint8_t rx1_buff[MAX_RECV_LEN] = {0};	//串列埠接收資料緩衝
uint8_t *pBuf;												//當前接收位元組存放的位置指標
uint8_t line_flag = 0;								//一行資料接收結束標誌

在這裡插入圖片描述

void StartTaskUsart(void *argument)
{
  /* USER CODE BEGIN StartTaskUsart */
	printf("HELLO WORLD\n");
	pBuf = rx1_buff;
	HAL_UART_Receive_IT(&huart1,pBuf, 1); 
  /* Infinite loop */
  for(;;)
  {
		if(line_flag)
		{
			printf("%s",rx1_buff);
			line_flag=0;
		}
    osDelay(1);
  }
  /* USER CODE END StartTaskUsart */
}

在這裡插入圖片描述

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle) 
{ 
	if (UartHandle->Instance == USART1) 
	{ 
		++ pBuf; // 已接收一個位元組資料,當前儲存位置指標後移 
		if(pBuf == rx1_buff + MAX_RECV_LEN) // 如果指標已移出陣列邊界 
			pBuf = rx1_buff; 									// 重新指向陣列開頭 
		else if(*(pBuf - 1) == '\n') // 如果之前接收到‘\n’換行符,則表示接收完成 
		{
			line_flag = 1;		// 行接收標誌置1
			*pBuf = '\0';			// 新增字串末尾結束符
			pBuf = rx1_buff;	// 重新指向陣列開頭
		}
		
		__HAL_UNLOCK(UartHandle); 								// 解鎖串列埠
		HAL_UART_Receive_IT(UartHandle, pBuf, 1); // 重新開啟接收中斷
	} 
}

缺點:由於中斷接收是以換行符為標準的,這裡我們按下兩次傳送鍵才將4個數字全部傳送。
在這裡插入圖片描述

4、中斷加上時間戳方式程式碼及效果

利用時間戳來輔助判斷接收空閒,實現了分段傳送。

 for(;;)
  {
		if(recv_tick>0 && (osKernelGetTickCount()-recv_tick)>=20)
		{
			*pBuf = '\0';	//新增字串末尾結束符
			printf("%s",rx1_buff);
			recv_tick=0;
			pBuf = rx1_buff;//重新指向陣列開頭
		}
  }
//加了時間戳的完善程式碼
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle) 
{ 
	if (UartHandle->Instance == USART1) 
	{ 
		++ pBuf; // 已接收一個位元組資料,當前儲存位置指標後移 
		if(pBuf == rx1_buff + MAX_RECV_LEN) // 如果指標已移出陣列邊界 
			pBuf = rx1_buff; 									// 重新指向陣列開頭 
		recv_tick = osKernelGetTickCount();
		
		__HAL_UNLOCK(UartHandle); 								// 解鎖串列埠
		HAL_UART_Receive_IT(UartHandle, pBuf, 1); // 重新開啟接收中斷
	} 
}

在這裡插入圖片描述

5、DMA空閒中斷方式接收資料

步驟總結:

1、開啟串列埠DMA接收
2、串列埠收到資料,DMA不斷傳輸資料到接收緩衝
3、一幀資料傳送完畢,串列埠暫時空閒,觸發串列埠空閒中斷
4、在串列埠中斷函式中,計算剛才收到的資料長度
5、複製接收緩衝資料,清除標誌位,開始下一幀接收

在這裡插入圖片描述
在這裡插入圖片描述
在這裡插入圖片描述

//新增串列埠資料緩衝、DMA資料緩衝、接收標誌
uint8_t data_buff[MAX_BUFF_LEN] = {0};	//串列埠接收資料緩衝
uint8_t dma_buff[MAX_BUFF_LEN] = {0};		//DMA資料緩衝
uint32_t recv_len = 0;			//接收資料長度

在這裡插入圖片描述

#define MAX_BUFF_LEN 512
extern uint8_t data_buff[MAX_BUFF_LEN];
extern uint8_t dma_buff[MAX_BUFF_LEN];
extern uint32_t recv_len;
extern void UART_IDLE_Callback(UART_HandleTypeDef *huart);

在這裡插入圖片描述

void UART_IDLE_Callback(UART_HandleTypeDef *huart)
{
		if (__HAL_UART_GET_FLAG(huart,UART_FLAG_IDLE) != RESET &&
			huart->Instance == USART1)
		{
				__HAL_UART_CLEAR_IDLEFLAG(huart);
				HAL_UART_DMAStop(huart);
				recv_len = MAX_BUFF_LEN - __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);
				if (recv_len < MAX_BUFF_LEN - 1)
				{
						memcpy(data_buff, dma_buff, recv_len);
						data_buff[recv_len] = '\0';
				}
				else
				{
						recv_len = 0;
				}
				__HAL_UNLOCK(huart);
				HAL_UART_Receive_DMA(huart, dma_buff, MAX_BUFF_LEN);
		}
}

在這裡插入圖片描述

void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */
	
  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */
	UART_IDLE_Callback(&huart1);
  /* USER CODE END USART1_IRQn 1 */
}

在這裡插入圖片描述

void StartTaskUsart(void *argument)
{
  /* USER CODE BEGIN StartTaskUsart */
	__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);		//使能idle中斷
  HAL_UART_Receive_DMA(&huart1,dma_buff,MAX_BUFF_LEN);//開啟DMA接收,資料存入dma_buff陣列中
  /* Infinite loop */
  for(;;)
  {
		if(recv_len>0)
		{
			printf("%s",(char *)data_buff);
			recv_len=0;
		}
	}
	osDelay(1);
  /* USER CODE END StartTaskUsart */
}

這種方法效果極其有效:
使用串列埠除錯助手軟體,定時每隔20ms傳送總共100個位元組的多行字串對STM32串列埠接收功能進行測試。
如圖所示,傳送資料個數超過50000位元組後停止傳送,接收到的STM32回送資料個數與傳送資料個數相同。
在這裡插入圖片描述

相關文章