串口通信-中斷方式
1 中斷方式的串口通信
串口中斷方式的特點:
- 發送數據時,將一字節數據放入數據寄存器DR;接收數據時,將DR的內容存放到用戶存儲區;
- 中斷方式不必等待數據的傳輸過程,只需要在每字節數據收發完成后,由中斷標志位觸發中斷,在中斷服務程序中放入新的一字節數據或者讀取接收到的一字節數據;
- 在傳輸數據量較大,且通信波特率較高(大于38400)時,如果采用中斷方式,每收發一個字節的數據,CPU都會被打斷,造成CPU無法處理其他事務。因此在批量數據傳輸,通信波特率較高時,建議采用DMA方式。
串口中斷方式發送函數:
HAL_UART_Transmit_IT
函數原型 HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_Handle TypeDef *huart, uint 8_t *pData, uint 16_t Size)
功能描述 在中斷方式下發送一定數量的數據 入口參數1 huart:串口句柄的地址 入口參數 pData:待發送數據的首地址 入口參數3 Size:待發送數據的個數 入口參數4 Timeout:超時等待時間, 以ms為單位, HAL MAX DELAY表示無限等待 返回值 HAL狀態值:HAL_OK表示發送成功;HAL_ERROR表示參數錯誤;HAL_BUSY表示串口被占用; 注意事項 1. 函數將使能串口發送中斷2. 函數將置位TXEIE和TCIE,使能發送數據寄存器空中斷和發送完成中斷。完成指定數量的數據發送后,將會關閉發送中斷,即清零TXEIE和TCIE。因此用戶采用中斷方式連續發送數據時,需要重復調用該函數,以便重新開啟發送中斷3. 當指定數量的數據發送完成后,將調用發送中斷回調函數HAL_UART_TxCpltCallback進行后續處理4. 該函數由用戶調用 串口中斷方式接收函數:
HAL_UART_Receive_IT
函數原型 HAL_StatusTypeDef HAL_UART_Receive_IT(UART_Handle TypeDef *huart, uint 8_t *pData, uint 16_t Size, uint 32_t Timeout)
功能描述 在中斷方式下接收一定數量的數據 入口參數1 huart:串口句柄的地址 入口參數2 pData:存放數據的首地址 入口參數3 Size:待接收數據的個數 入口參數4 Timeout:超時等待時間, 以ms為單位, HAL MAX DELAY表示無限等待 返回值 HAL狀態值:HAL_OK表示發送成功;HAL_ERROR表示參數錯誤;HAL_BUSY表示串口被占用; 注意事項 1. 函數將使能串口接收中斷2. 函數將置位RXNEIE,使能接收數據寄存器非空中斷RXNE。完成指定數量的數據接收后,將會關閉接收中斷,即清零RXNEIE。因此用戶采用中斷方式連續接收數據時,要重復調用該函數,以重新開啟接收中斷3. 當指定數量的數據接收完成后,將調用接收中斷回調函數HAL_UART_RxCpltCallback進行后續處理4. 該函數由用戶調用 串口中斷通用處理函數:
HAL_UART_IRQHandler
函數原型 void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)
功能描述 作為所有串口中斷發生后的通用處理函數 入口參數 htim:定時器句柄的地址 返回值 無 注意事項 1. 函數內部先判斷中斷類型,并清除對應的中斷標志,最后調用回調函數完成對應的中斷處理2. 該函數由CubeMX自動生成 串口發送中斷回調函數:
HAL_UART_TxCpltCallback
函數原型 void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
功能描述 回調函數,用于處理所有串口的發送中斷,用戶在該函數內編寫實際的任務處理程序 入口參數 htim:定時器句柄的地址 返回值 無 注意事項 1. 函數由串口中斷通用處理函數HAL_UART_IRQHandler調用,完成所有注意事項2.串口的發送中斷任務處理函數內部需要根據串口句柄的實例來判斷是哪一個串口產生的發送中斷3. 函數由用戶根據具體的處理任務編寫 串口接收中斷回調函數:
HAL_UART_RxCpltCallback
函數原型 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
功能描述 回調函數,用于處理所有串口的接收中斷,用戶在該函數內編寫實際的任務處理程序 入口參數 htim:定時器句柄的地址 返回值 無 注意事項 1. 函數由串口中斷通用處理函數HAL_UART_IRQHandler調用,完成所有注意事項2.串口的發送中斷任務處理函數內部需要根據串口句柄的實例來判斷是哪一個串口產生的接收中斷3. 函數由用戶根據具體的處理任務編寫 串口中斷使能函數:
__HAL_UART_ENABLE_IT
函數原型 __HAL_UART_ENABLE_IT(__HANDLE__, __INTERRUPT__)
功能描述 使能對應的串口中斷類型 入口參數 __INTERRUPT __ :串口中斷類型,該參數幾個常用的取值如下UART_IT_TXE :發送數據寄存器空中斷UART_IT_TC :發送完成中斷UART_IT_RXNE:接收數據寄存器非空中斷UART_IT_IDLE :線路空閑中斷 返回值 無 注意事項 1. 該函數是宏函數,進行宏替換,不發生函數調用2. 函數需要由用戶調用,用于使能對應的串口中斷類型 串口中斷標志查詢函數:
__HAL_UART_GET_FLAG
函數原型 __HAL_UART_GET_FLAG (__HANDLE__, __INTERRUPT__)
功能描述 查詢對應的串口中斷類型 入口參數 __INTERRUPT __ :串口中斷類型,該參數幾個常用的取值如下UART_IT_TXE :發送數據寄存器空中斷UART_IT_TC :發送完成中斷UART_IT_RXNE:接收數據寄存器非空中斷UART_IT_IDLE :線路空閑中斷 返回值 中斷標志的狀態值:SET表示中斷標志置位;RESET表示中斷標志沒有置位 注意事項 1. 該函數是宏函數,進行宏替換,不發生函數調用2. 函數需要由用戶調用,用于查詢對應的串口中斷類型 空閑中斷標志清除函數:
__HAL_UART_CLEAR_IDLEFLAG
函數原型 __HAL_UART_CLEAR_IDLEFLAG
功能描述 清除串口的空閑中斷標志 入口參數 HANDLE :串口句柄的地址 返回值 無 注意事項 1. 該函數是宏函數,進行宏替換,不發生函數調用2. 函數需要由用戶調用,用于清除對應的串口空閑中斷標志
2 HAL庫串口中斷處理過程:
HAL_UART_Receive_IT
:開啟中斷,在中斷方式下接收一定數量的數據。USART2_IRQHandler
:串口2的中斷服務程序,調用串口中斷通用處理函數HAL_UART_IRQHandler
。HAL_UART_IRQHandler
:在函數HAL_UART_IRQHandler
內部通過判斷中斷類型是否為接收完成中斷,確定是否調用UART_Receive_IT
。
函數
UART_Receive_IT
的作用是把每次中斷接收到的字符保存在串口句柄的緩存指針pRxBuffPtr
中,同時每次接收一個字符,其計數器RxXferCount
減 1,直到接收完成RxXferSize
個字符之后RxXferCount
設置為0,同時調用接收完成回調函數HAL_UART_RxCpltCallback
進行處理。
HAL_UART_RxCpltCallback
:函數由串口中斷通用處理函數UART_Receive_IT
調用,完成所有串口的接收中斷任務處理,函數內部需要根據串口句柄的實例來判斷是哪一個串口產生的接收中斷,函數由用戶根據具體的處理任務編寫。
3 任務實踐2
利用串口調試助手,從PC上發送10個字符到開發板,開發板收到后原樣發回到PC。
前后臺編程模式: 前臺程序為中斷服務程序,一旦數據接收完成,則設置一個標志位為1;后臺程序為while(1)的死循環,在循環中不斷檢測標志位是否為1。如果為1,表明數據接收完成,并存放在接收緩沖區中。然后進行后續處理:先清除標志位,再把接收的數據原樣發回。
串口外設配置
- 異步模式,無硬件流控
- 設置通信參數:波特率115200,8位數據位,無奇偶校驗,1位停止位,使能接收和發送,16倍過采樣(CubeMX默認配置)
- 使能串口中斷
編寫代碼
printf和scanf重定向:略// -----------------------------------------------------------------------// /* USER CODE BEGIN PD */ #define LENGTH 10 // 接收數據緩沖區大小 /* USER CODE END PD */ // -----------------------------------------------------------------------// /* USER CODE BEGIN PV */ uint8_t RxBuffer[LENGTH]; // 接收緩沖區 uint8_t RxFlag = 0; // 接收完成標志,0未完成,1完成 /* USER CODE END PV */ // -----------------------------------------------------------------------// /* USER CODE BEGIN 2 */ // 打印提示信息 printf("UART Communication Using ITn"); printf("Please enter 10 characters:n"); HAL_UART_Receive_IT(&huart1, (uint8_t *)RxBuffer, LENGTH); // 使能接收中斷 /* USER CODE END 2 */ // -----------------------------------------------------------------------// while (1) { /* USER CODE BEGIN 3 */ if (RxFlag == 1) // 判斷接收是否完成 { RxFlag = 0; // 接收完成,清除標志位 printf("Receive Successfully!n"); // 打印提示信息 HAL_UART_Transmit_IT(&huart1, (uint8_t*)RxBuffer, LENGTH); // 將接收的字符原樣發回 } } /* USER CODE END 3 */ // -----------------------------------------------------------------------// /* USER CODE BEGIN 4 */ int fputc(int ch, FILE *f) { HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, HAL_MAX_DELAY); return ch; } int fgetc(FILE *f) { uint8_t ch = 0; HAL_UART_Receive(&huart1, (uint8_t *)&ch, 1, HAL_MAX_DELAY); return ch; } void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if (huart- >Instance == USART1) // 判斷發生接收中斷的串口 { RxFlag = 1; // 置位接收完成b標志 HAL_UART_Receive_IT(&huart1, (uint8_t*)RxBuffer, LENGTH); // 使能接收中斷 } } /* USER CODE END 4 */ // -----------------------------------------------------------------------//
實驗現象
4 任務實踐3
實現簡單的幀格式通信:PC按照自定義的幀格式發送指令開啟或關閉開發板上的LED1。
幀格式的概念:
- 幀(Frame)是數據傳輸的一種單位。一幀數據由多個字符組合而成,不同字段的字符代表不同的含義,執行不同的功能;
- 在實際的工程應用中,數據的傳輸常常以幀為單位來進行,如工控領域中最常用的Modbus通信協議中的消息幀;
- 發送方按照規定的幀格式發送一幀數據,接收方接收下這一幀數據后,再按照幀格式進行解析,最后完成后續的處理。
Modbus消息幀格式:
起始符 | 設備地址 | 功能代碼 | 數據 | 校驗 | 結束符 |
---|---|---|---|---|---|
1個字符 | 2個字符 | 1個字符 | n個字符 | 2個字符 | 1個字符 |
- 起始符:表示一幀數據的開始
- 設備地址:用于指定需要進行信息傳遞的設備
- 功能代碼:用于指定需要完成的操作
- 數據:表示需要傳輸的數據
- 校驗:用于通信中的錯誤校驗
- 結束符:表示一幀數據的結束
自定義的幀格式設定:
幀頭 | 設備碼 | 功能碼 | 幀尾 |
---|---|---|---|
0xaa | 1個字符(8bit) | 1個字符(8bit) | 0x55 |
- 幀頭 :0xaa表示一幀數據的開始
- 設備碼:0x01表示指示燈
- 功能碼:0x00表示關閉指示燈,0x01表示開啟指示燈
- 幀尾 :0x55表示一幀數據的結束
串口配置同任務實踐1
配置PA1為GPIO_Output模式
編寫代碼
// -----------------------------------------------------------------------// /* USER CODE BEGIN PV */ uint8_t RxBuffer[4]; // 接收緩沖區 uint8_t RxFlag = 0; // 接收完成標志,0位完成,1完成 uint8_t ErrFlag = 0; // 指令錯誤標志,0正確,1錯誤 /* USER CODE END PV */ // -----------------------------------------------------------------------// /* USER CODE BEGIN 2 */ // 打印提示信息 printf("***** Communication Frame *****n"); printf("Please enter instruction:n"); printf("Head- >0xaa, Device- >0x01, Operation- >0x00/0x10, Tail- >0x55n"); HAL_UART_Receive_IT(&huart1, (uint8_t*)RxBuffer, 4); // 使能接收中斷 /* USER CODE END 2 */ // -----------------------------------------------------------------------// while(1) { /* USER CODE BEGIN 3 */ // Determine if reception is complete if (RxFlag == 1) // 判斷接收是否完成 { RxFlag = 0; // 完成,清除標志位 // 幀格式解析 printf("head = RxBuffer[0] = %xn", RxBuffer[0]); printf("tail = RxBuffer[3] = %xn", RxBuffer[3]); printf("device = RxBuffer[1] = %xn", RxBuffer[1]); printf("function = RxBuffer[2] = %xn", RxBuffer[2]); if (RxBuffer[0] == 0xaa && RxBuffer[3] == 0x55) // 判斷幀頭幀尾 { if (RxBuffer[1] == 0x01) // 判斷設備碼 { if (RxBuffer[2] == 0x00) // 判斷功能碼 { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET); printf("LED1 is close!n"); } else if (RxBuffer[2] == 0x01) // 判斷功能碼 { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET); printf("LED1 is open!n"); } else // 功能碼錯誤 { ErrFlag = 3; } } else // 設備碼錯誤 { ErrFlag = 2; } } else // 幀頭幀尾錯誤 { ErrFlag = 1; } // 發送錯誤提示信息 switch (ErrFlag) { case 1: printf("Head and tail error! Please send again!n"); break; case 2: printf("Device code error! Please send again!n"); break; case 3: printf("Function code error! Please send again!n"); break; default: break; } // 清除接收緩沖區和錯誤標志,準備下一次接收 ErrFlag = 0; RxBuffer[0] = 0; RxBuffer[1] = 0; RxBuffer[2] = 0; RxBuffer[3] = 0; } } /* USER CODE END 3 */ // -----------------------------------------------------------------------// /* USER CODE BEGIN 4 */ int fputc (int ch, FILE *f) { HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, HAL_MAX_DELAY); return ch; } int fgetc(FILE *f) { uint8_t ch = 0; HAL_UART_Receive(&huart1, (uint8_t *)&ch, 1, HAL_MAX_DELAY); return ch; } void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if (huart- >Instance == USART1) // 判斷發生接收中斷的串口 { RxFlag = 1; // 置位接收完成標志 HAL_UART_Receive_IT(&huart1 , (uint8_t*)RxBuffer, 4); // 使能串口中斷 } } /* USER CODE END 4 */
實驗現象
-
寄存器
+關注
關注
31文章
5363瀏覽量
120933 -
cpu
+關注
關注
68文章
10901瀏覽量
212664 -
STM32
+關注
關注
2270文章
10923瀏覽量
357076 -
中斷
+關注
關注
5文章
900瀏覽量
41648 -
串口通信
+關注
關注
34文章
1627瀏覽量
55656
發布評論請先 登錄
相關推薦
評論