【嵌入式系統】STM32串列埠通訊的四種方法(基於RTOS)
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回送資料個數與傳送資料個數相同。
相關文章
- STM32串列埠通訊串列埠
- Arduino下的STM32的串列埠通訊UI串列埠
- 國內的 Stduino IDE 基於 STM32 的串列埠通訊與 LED 燈閃爍UIIDE串列埠
- 基於WebSocket的modbus通訊(三)- websocket和串列埠Web串列埠
- 初步使用Ardunio IDE實現STM32的串列埠通訊IDE串列埠
- 串列埠通訊串列埠
- Android 串列埠通訊Android串列埠
- linux 串列埠通訊Linux串列埠
- 【STM32】串列埠串列埠
- 11. 串列埠通訊串列埠
- (10)uart串列埠通訊串列埠
- 串列埠通訊型別串列埠型別
- 串列埠通訊協議串列埠協議
- 通過串列埠進行通訊 :串列埠
- 串列埠資料抓取及串列埠通訊模擬串列埠
- 串列埠通訊常見的錯誤和故障排除方法串列埠
- 串列埠無法正常通訊串列埠
- C# SerialPort 串列埠通訊C#串列埠
- 一種MODBUS RTU擴充套件串列埠通訊協議套件串列埠協議
- 樹莓派4B基於OpenCV的C++環境的串列埠通訊樹莓派OpenCVC++串列埠
- 嵌入式系統除錯Uboot串列埠互動除錯除錯boot串列埠
- AndroidSerialPort:安卓串列埠通訊庫Android安卓串列埠
- 安卓串列埠通訊疑問安卓串列埠
- ROS環境下串列埠通訊ROS串列埠
- 串列埠通訊gui介面顯示串列埠GUI
- 小型plc串列埠通訊簡介串列埠
- STM32 串列埠列印 NaN的原因分析!!!串列埠NaN
- C# 串列埠通訊利器 SerialPortStream庫C#串列埠
- 基於單連結串列的班級通訊錄
- stm32配合xshell串列埠輸入串列埠
- STM32延時函式的四種方法函式
- STMF4串列埠通訊使用串列埠
- STM32、ESP8266與MQTT連線阿里雲物聯網的串列埠通訊異常解析MQQT阿里串列埠
- 串列埠通訊與其他通訊方式相比有什麼優勢?串列埠
- 【Java專案(2)】基於Javaee的通訊錄系統Java
- STM32串列埠列印的那些知識串列埠
- 美麗新農村:基於PLC和串列埠通訊的農村汙水物聯網解決方案串列埠
- 串列埠通訊利器:SerialPortStream庫詳解,輕鬆實現C#串列埠開發串列埠C#