串口通信-中斷方式
1 中斷方式的串口通信
串口中斷方式的特點(diǎn):
- 發(fā)送數(shù)據(jù)時(shí),將一字節(jié)數(shù)據(jù)放入數(shù)據(jù)寄存器DR;接收數(shù)據(jù)時(shí),將DR的內(nèi)容存放到用戶存儲(chǔ)區(qū);
- 中斷方式不必等待數(shù)據(jù)的傳輸過(guò)程,只需要在每字節(jié)數(shù)據(jù)收發(fā)完成后,由中斷標(biāo)志位觸發(fā)中斷,在中斷服務(wù)程序中放入新的一字節(jié)數(shù)據(jù)或者讀取接收到的一字節(jié)數(shù)據(jù);
- 在傳輸數(shù)據(jù)量較大,且通信波特率較高(大于38400)時(shí),如果采用中斷方式,每收發(fā)一個(gè)字節(jié)的數(shù)據(jù),CPU都會(huì)被打斷,造成CPU無(wú)法處理其他事務(wù)。因此在批量數(shù)據(jù)傳輸,通信波特率較高時(shí),建議采用DMA方式。
串口中斷方式發(fā)送函數(shù):
HAL_UART_Transmit_IT
函數(shù)原型 HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_Handle TypeDef *huart, uint 8_t *pData, uint 16_t Size)
功能描述 在中斷方式下發(fā)送一定數(shù)量的數(shù)據(jù) 入口參數(shù)1 huart:串口句柄的地址 入口參數(shù) pData:待發(fā)送數(shù)據(jù)的首地址 入口參數(shù)3 Size:待發(fā)送數(shù)據(jù)的個(gè)數(shù) 入口參數(shù)4 Timeout:超時(shí)等待時(shí)間, 以ms為單位, HAL MAX DELAY表示無(wú)限等待 返回值 HAL狀態(tài)值:HAL_OK表示發(fā)送成功;HAL_ERROR表示參數(shù)錯(cuò)誤;HAL_BUSY表示串口被占用; 注意事項(xiàng) 1. 函數(shù)將使能串口發(fā)送中斷2. 函數(shù)將置位TXEIE和TCIE,使能發(fā)送數(shù)據(jù)寄存器空中斷和發(fā)送完成中斷。完成指定數(shù)量的數(shù)據(jù)發(fā)送后,將會(huì)關(guān)閉發(fā)送中斷,即清零TXEIE和TCIE。因此用戶采用中斷方式連續(xù)發(fā)送數(shù)據(jù)時(shí),需要重復(fù)調(diào)用該函數(shù),以便重新開(kāi)啟發(fā)送中斷3. 當(dāng)指定數(shù)量的數(shù)據(jù)發(fā)送完成后,將調(diào)用發(fā)送中斷回調(diào)函數(shù)HAL_UART_TxCpltCallback進(jìn)行后續(xù)處理4. 該函數(shù)由用戶調(diào)用 串口中斷方式接收函數(shù):
HAL_UART_Receive_IT
函數(shù)原型 HAL_StatusTypeDef HAL_UART_Receive_IT(UART_Handle TypeDef *huart, uint 8_t *pData, uint 16_t Size, uint 32_t Timeout)
功能描述 在中斷方式下接收一定數(shù)量的數(shù)據(jù) 入口參數(shù)1 huart:串口句柄的地址 入口參數(shù)2 pData:存放數(shù)據(jù)的首地址 入口參數(shù)3 Size:待接收數(shù)據(jù)的個(gè)數(shù) 入口參數(shù)4 Timeout:超時(shí)等待時(shí)間, 以ms為單位, HAL MAX DELAY表示無(wú)限等待 返回值 HAL狀態(tài)值:HAL_OK表示發(fā)送成功;HAL_ERROR表示參數(shù)錯(cuò)誤;HAL_BUSY表示串口被占用; 注意事項(xiàng) 1. 函數(shù)將使能串口接收中斷2. 函數(shù)將置位RXNEIE,使能接收數(shù)據(jù)寄存器非空中斷RXNE。完成指定數(shù)量的數(shù)據(jù)接收后,將會(huì)關(guān)閉接收中斷,即清零RXNEIE。因此用戶采用中斷方式連續(xù)接收數(shù)據(jù)時(shí),要重復(fù)調(diào)用該函數(shù),以重新開(kāi)啟接收中斷3. 當(dāng)指定數(shù)量的數(shù)據(jù)接收完成后,將調(diào)用接收中斷回調(diào)函數(shù)HAL_UART_RxCpltCallback進(jìn)行后續(xù)處理4. 該函數(shù)由用戶調(diào)用 串口中斷通用處理函數(shù):
HAL_UART_IRQHandler
函數(shù)原型 void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)
功能描述 作為所有串口中斷發(fā)生后的通用處理函數(shù) 入口參數(shù) htim:定時(shí)器句柄的地址 返回值 無(wú) 注意事項(xiàng) 1. 函數(shù)內(nèi)部先判斷中斷類型,并清除對(duì)應(yīng)的中斷標(biāo)志,最后調(diào)用回調(diào)函數(shù)完成對(duì)應(yīng)的中斷處理2. 該函數(shù)由CubeMX自動(dòng)生成 串口發(fā)送中斷回調(diào)函數(shù):
HAL_UART_TxCpltCallback
函數(shù)原型 void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
功能描述 回調(diào)函數(shù),用于處理所有串口的發(fā)送中斷,用戶在該函數(shù)內(nèi)編寫(xiě)實(shí)際的任務(wù)處理程序 入口參數(shù) htim:定時(shí)器句柄的地址 返回值 無(wú) 注意事項(xiàng) 1. 函數(shù)由串口中斷通用處理函數(shù)HAL_UART_IRQHandler調(diào)用,完成所有注意事項(xiàng)2.串口的發(fā)送中斷任務(wù)處理函數(shù)內(nèi)部需要根據(jù)串口句柄的實(shí)例來(lái)判斷是哪一個(gè)串口產(chǎn)生的發(fā)送中斷3. 函數(shù)由用戶根據(jù)具體的處理任務(wù)編寫(xiě) 串口接收中斷回調(diào)函數(shù):
HAL_UART_RxCpltCallback
函數(shù)原型 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
功能描述 回調(diào)函數(shù),用于處理所有串口的接收中斷,用戶在該函數(shù)內(nèi)編寫(xiě)實(shí)際的任務(wù)處理程序 入口參數(shù) htim:定時(shí)器句柄的地址 返回值 無(wú) 注意事項(xiàng) 1. 函數(shù)由串口中斷通用處理函數(shù)HAL_UART_IRQHandler調(diào)用,完成所有注意事項(xiàng)2.串口的發(fā)送中斷任務(wù)處理函數(shù)內(nèi)部需要根據(jù)串口句柄的實(shí)例來(lái)判斷是哪一個(gè)串口產(chǎn)生的接收中斷3. 函數(shù)由用戶根據(jù)具體的處理任務(wù)編寫(xiě) 串口中斷使能函數(shù):
__HAL_UART_ENABLE_IT
函數(shù)原型 __HAL_UART_ENABLE_IT(__HANDLE__, __INTERRUPT__)
功能描述 使能對(duì)應(yīng)的串口中斷類型 入口參數(shù) __INTERRUPT __ :串口中斷類型,該參數(shù)幾個(gè)常用的取值如下UART_IT_TXE :發(fā)送數(shù)據(jù)寄存器空中斷UART_IT_TC :發(fā)送完成中斷UART_IT_RXNE:接收數(shù)據(jù)寄存器非空中斷UART_IT_IDLE :線路空閑中斷 返回值 無(wú) 注意事項(xiàng) 1. 該函數(shù)是宏函數(shù),進(jìn)行宏替換,不發(fā)生函數(shù)調(diào)用2. 函數(shù)需要由用戶調(diào)用,用于使能對(duì)應(yīng)的串口中斷類型 串口中斷標(biāo)志查詢函數(shù):
__HAL_UART_GET_FLAG
函數(shù)原型 __HAL_UART_GET_FLAG (__HANDLE__, __INTERRUPT__)
功能描述 查詢對(duì)應(yīng)的串口中斷類型 入口參數(shù) __INTERRUPT __ :串口中斷類型,該參數(shù)幾個(gè)常用的取值如下UART_IT_TXE :發(fā)送數(shù)據(jù)寄存器空中斷UART_IT_TC :發(fā)送完成中斷UART_IT_RXNE:接收數(shù)據(jù)寄存器非空中斷UART_IT_IDLE :線路空閑中斷 返回值 中斷標(biāo)志的狀態(tài)值:SET表示中斷標(biāo)志置位;RESET表示中斷標(biāo)志沒(méi)有置位 注意事項(xiàng) 1. 該函數(shù)是宏函數(shù),進(jìn)行宏替換,不發(fā)生函數(shù)調(diào)用2. 函數(shù)需要由用戶調(diào)用,用于查詢對(duì)應(yīng)的串口中斷類型 空閑中斷標(biāo)志清除函數(shù):
__HAL_UART_CLEAR_IDLEFLAG
函數(shù)原型 __HAL_UART_CLEAR_IDLEFLAG
功能描述 清除串口的空閑中斷標(biāo)志 入口參數(shù) HANDLE :串口句柄的地址 返回值 無(wú) 注意事項(xiàng) 1. 該函數(shù)是宏函數(shù),進(jìn)行宏替換,不發(fā)生函數(shù)調(diào)用2. 函數(shù)需要由用戶調(diào)用,用于清除對(duì)應(yīng)的串口空閑中斷標(biāo)志
2 HAL庫(kù)串口中斷處理過(guò)程:
HAL_UART_Receive_IT
:開(kāi)啟中斷,在中斷方式下接收一定數(shù)量的數(shù)據(jù)。USART2_IRQHandler
:串口2的中斷服務(wù)程序,調(diào)用串口中斷通用處理函數(shù)HAL_UART_IRQHandler
。HAL_UART_IRQHandler
:在函數(shù)HAL_UART_IRQHandler
內(nèi)部通過(guò)判斷中斷類型是否為接收完成中斷,確定是否調(diào)用UART_Receive_IT
。
函數(shù)
UART_Receive_IT
的作用是把每次中斷接收到的字符保存在串口句柄的緩存指針pRxBuffPtr
中,同時(shí)每次接收一個(gè)字符,其計(jì)數(shù)器RxXferCount
減 1,直到接收完成RxXferSize
個(gè)字符之后RxXferCount
設(shè)置為0,同時(shí)調(diào)用接收完成回調(diào)函數(shù)HAL_UART_RxCpltCallback
進(jìn)行處理。
HAL_UART_RxCpltCallback
:函數(shù)由串口中斷通用處理函數(shù)UART_Receive_IT
調(diào)用,完成所有串口的接收中斷任務(wù)處理,函數(shù)內(nèi)部需要根據(jù)串口句柄的實(shí)例來(lái)判斷是哪一個(gè)串口產(chǎn)生的接收中斷,函數(shù)由用戶根據(jù)具體的處理任務(wù)編寫(xiě)。
3 任務(wù)實(shí)踐2
利用串口調(diào)試助手,從PC上發(fā)送10個(gè)字符到開(kāi)發(fā)板,開(kāi)發(fā)板收到后原樣發(fā)回到PC。
前后臺(tái)編程模式: 前臺(tái)程序?yàn)橹袛喾?wù)程序,一旦數(shù)據(jù)接收完成,則設(shè)置一個(gè)標(biāo)志位為1;后臺(tái)程序?yàn)閣hile(1)的死循環(huán),在循環(huán)中不斷檢測(cè)標(biāo)志位是否為1。如果為1,表明數(shù)據(jù)接收完成,并存放在接收緩沖區(qū)中。然后進(jìn)行后續(xù)處理:先清除標(biāo)志位,再把接收的數(shù)據(jù)原樣發(fā)回。
串口外設(shè)配置
- 異步模式,無(wú)硬件流控
- 設(shè)置通信參數(shù):波特率115200,8位數(shù)據(jù)位,無(wú)奇偶校驗(yàn),1位停止位,使能接收和發(fā)送,16倍過(guò)采樣(CubeMX默認(rèn)配置)
- 使能串口中斷
編寫(xiě)代碼
printf和scanf重定向:略// -----------------------------------------------------------------------// /* USER CODE BEGIN PD */ #define LENGTH 10 // 接收數(shù)據(jù)緩沖區(qū)大小 /* USER CODE END PD */ // -----------------------------------------------------------------------// /* USER CODE BEGIN PV */ uint8_t RxBuffer[LENGTH]; // 接收緩沖區(qū) uint8_t RxFlag = 0; // 接收完成標(biāo)志,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; // 接收完成,清除標(biāo)志位 printf("Receive Successfully!n"); // 打印提示信息 HAL_UART_Transmit_IT(&huart1, (uint8_t*)RxBuffer, LENGTH); // 將接收的字符原樣發(fā)回 } } /* 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) // 判斷發(fā)生接收中斷的串口 { RxFlag = 1; // 置位接收完成b標(biāo)志 HAL_UART_Receive_IT(&huart1, (uint8_t*)RxBuffer, LENGTH); // 使能接收中斷 } } /* USER CODE END 4 */ // -----------------------------------------------------------------------//
實(shí)驗(yàn)現(xiàn)象
4 任務(wù)實(shí)踐3
實(shí)現(xiàn)簡(jiǎn)單的幀格式通信:PC按照自定義的幀格式發(fā)送指令開(kāi)啟或關(guān)閉開(kāi)發(fā)板上的LED1。
幀格式的概念:
- 幀(Frame)是數(shù)據(jù)傳輸?shù)囊环N單位。一幀數(shù)據(jù)由多個(gè)字符組合而成,不同字段的字符代表不同的含義,執(zhí)行不同的功能;
- 在實(shí)際的工程應(yīng)用中,數(shù)據(jù)的傳輸常常以幀為單位來(lái)進(jìn)行,如工控領(lǐng)域中最常用的Modbus通信協(xié)議中的消息幀;
- 發(fā)送方按照規(guī)定的幀格式發(fā)送一幀數(shù)據(jù),接收方接收下這一幀數(shù)據(jù)后,再按照幀格式進(jìn)行解析,最后完成后續(xù)的處理。
Modbus消息幀格式:
起始符 | 設(shè)備地址 | 功能代碼 | 數(shù)據(jù) | 校驗(yàn) | 結(jié)束符 |
---|---|---|---|---|---|
1個(gè)字符 | 2個(gè)字符 | 1個(gè)字符 | n個(gè)字符 | 2個(gè)字符 | 1個(gè)字符 |
- 起始符:表示一幀數(shù)據(jù)的開(kāi)始
- 設(shè)備地址:用于指定需要進(jìn)行信息傳遞的設(shè)備
- 功能代碼:用于指定需要完成的操作
- 數(shù)據(jù):表示需要傳輸?shù)臄?shù)據(jù)
- 校驗(yàn):用于通信中的錯(cuò)誤校驗(yàn)
- 結(jié)束符:表示一幀數(shù)據(jù)的結(jié)束
自定義的幀格式設(shè)定:
幀頭 | 設(shè)備碼 | 功能碼 | 幀尾 |
---|---|---|---|
0xaa | 1個(gè)字符(8bit) | 1個(gè)字符(8bit) | 0x55 |
- 幀頭 :0xaa表示一幀數(shù)據(jù)的開(kāi)始
- 設(shè)備碼:0x01表示指示燈
- 功能碼:0x00表示關(guān)閉指示燈,0x01表示開(kāi)啟指示燈
- 幀尾 :0x55表示一幀數(shù)據(jù)的結(jié)束
串口配置同任務(wù)實(shí)踐1
配置PA1為GPIO_Output模式
編寫(xiě)代碼
// -----------------------------------------------------------------------// /* USER CODE BEGIN PV */ uint8_t RxBuffer[4]; // 接收緩沖區(qū) uint8_t RxFlag = 0; // 接收完成標(biāo)志,0位完成,1完成 uint8_t ErrFlag = 0; // 指令錯(cuò)誤標(biāo)志,0正確,1錯(cuò)誤 /* 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; // 完成,清除標(biāo)志位 // 幀格式解析 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) // 判斷設(shè)備碼 { 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 // 功能碼錯(cuò)誤 { ErrFlag = 3; } } else // 設(shè)備碼錯(cuò)誤 { ErrFlag = 2; } } else // 幀頭幀尾錯(cuò)誤 { ErrFlag = 1; } // 發(fā)送錯(cuò)誤提示信息 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; } // 清除接收緩沖區(qū)和錯(cuò)誤標(biāo)志,準(zhǔn)備下一次接收 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) // 判斷發(fā)生接收中斷的串口 { RxFlag = 1; // 置位接收完成標(biāo)志 HAL_UART_Receive_IT(&huart1 , (uint8_t*)RxBuffer, 4); // 使能串口中斷 } } /* USER CODE END 4 */
實(shí)驗(yàn)現(xiàn)象
-
寄存器
+關(guān)注
關(guān)注
31文章
5294瀏覽量
119816 -
cpu
+關(guān)注
關(guān)注
68文章
10804瀏覽量
210829 -
STM32
+關(guān)注
關(guān)注
2264文章
10854瀏覽量
354296 -
中斷
+關(guān)注
關(guān)注
5文章
895瀏覽量
41349 -
串口通信
+關(guān)注
關(guān)注
34文章
1607瀏覽量
55381
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論