目錄
- 1、串行通信的基本參數
- 2、輪詢方式代碼效果
- 3、中斷方式代碼效果
- 4、中斷加上時間戳方式代碼及效果
- 5、DMA空閑中斷方式接收數據
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回送數據個數與發送數據個數相同。