昨天分析了普通io口的使用,和初始化代碼流程,回顧一下,首先定義一個配置io口功能的結構體,然后開啟時鍾,再去配置這個結構體里面的各個成員變量,每個成員變量都有很多種選擇,可以看各個成員變量 后面的注釋,找到可選的配置即可,把這個結構體配置完了之后,把它扔到hal庫提供的io口初始化函數中,另一個參數是ABC。。。中的一個,指定端口。
同理,操作串口的流程大致和上面敘述的過程差不多,但是串口多了收發數據,中斷等等功能。在Cubemx里面配置串口,然后打開工程,跟蹤代碼,了解詳細的過程。首先,串口的初始化代碼部分:
UART_HandleTypeDef UART1_Handler; //UART句柄 UART1_Handler.Instance=USART1; // 指定串口 UART1_Handler.Init.BaudRate=bound; // 波特率 UART1_Handler.Init.WordLength=UART_WORDLENGTH_8B; // 字長為8位數據格式 UART1_Handler.Init.StopBits=UART_STOPBITS_1; // 一個停止位 UART1_Handler.Init.Parity=UART_PARITY_NONE; // 無奇偶校驗位 UART1_Handler.Init.HwFlowCtl=UART_HWCONTROL_NONE; // 無硬件流控 UART1_Handler.Init.Mode=UART_MODE_TX_RX; // 收發模式 HAL_UART_Init(&UART1_Handler); // 初始化這個串口
大概過程和配置io口類似,不再細說。但是要進到串口初始化函數中去看一下,發現會調用這個函數,但是這個函數的weak虛函數,虛函數的概念百度一下即可,大概就是用戶需要自己重新寫他的功能:
HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart) { ... HAL_UART_MspInit(huart); ... } ... __weak void HAL_UART_MspInit(UART_HandleTypeDef *huart) { /* Prevent unused argument(s) compilation warning */ UNUSED(huart); /* NOTE: This function Should not be modified, when the callback is needed, the HAL_UART_MspInit could be implemented in the user file */ }
看一下注釋,大意就是與回調有關系,cubemx給做了,看一下:
void HAL_UART_MspInit(UART_HandleTypeDef* huart) { GPIO_InitTypeDef GPIO_InitStruct; /**USART1 GPIO Configuration PA9 ------> USART1_TX PA10 ------> USART1_RX */ GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FAST; GPIO_InitStruct.Alternate = GPIO_AF7_USART1; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); HAL_NVIC_SetPriority(USART1_IRQn, 0, 1); HAL_NVIC_EnableIRQ(USART1_IRQn); }
從代碼可以功能:配置io口,剛才的初始化串口函數,只是配置了串口的波特率、數據位等等,但是還沒有配置串口所在的引腳,在這里配置,注釋很清楚,A9 A10是這個川口的T和R,然后配置速度、復用模式,復用為什么,這個復用說一下。舉個例子,如果有的用戶想用很多普通io口,不用串口啊、SPI接口啊。有的呢,就用串口和spi接口,那廠家要是想滿足這些用戶,就要給片上,多加io口,加串口,等等其他的引腳,那么就可能會使腳很多,芯片大或者制造成本高,於是廠家就用100根引腳(407某型號)然后讓一些腳有多個功能,既可以干這個也可以做那個,至於到底做什么,用戶決定,這樣就是腳會少,同時滿足大多數用戶的需求。片上除了電源等口,剩下的,基本都可以當作普通的io口用,同時也可以做串口、SPI等等功能,這叫復用。剛才說到,我們只配置了串口功能,還沒配置這個串口所在的io口為復用,復用為串口。在這個回調初始化中定義了。同時,也配置了,中斷優先號,中斷使能。
那么中斷怎么用呢?這里我直接參考了正點原子的教程:
// 串口號 接受緩沖數組 接收量:自己宏定義或者直接寫數就行
HAL_UART_Receive_IT(&huart1, (u8 *)aRxBuffer1, RXBUFFERSIZE1); //該函數會開啟接收中斷:標志位UART_IT_RXNE,並且設置接收緩沖以及接收緩沖接收最大數據量
這個函數放在串口初始化中,就使能了串口接收中斷,串口接收到數據,這個函數就把數據放在我們定義的接受緩沖數組中,可能一次過來很多,但是我們只接收RXBUFFERSIZ1個,這個還要配合回中斷調用函數,在利用JY901模塊收再說,下面再說。如果想深入了解的話,還得進入這個函數去看看,發現他會調用函數:__HAL_UART_ENABLE_IT(huart, UART_IT_RXNE);,其實這是一個宏定義,如下:
#define UART_IT_MASK ((uint32_t)0x0000FFFFU) #define __HAL_UART_ENABLE_IT(__HANDLE__, __INTERRUPT__) ((((__INTERRUPT__) >> 28U) == 1U)? ((__HANDLE__)->Instance->CR1 |= ((__INTERRUPT__) & UART_IT_MASK)): \ (((__INTERRUPT__) >> 28U) == 2U)? ((__HANDLE__)->Instance->CR2 |= ((__INTERRUPT__) & UART_IT_MASK)): \ ((__HANDLE__)->Instance->CR3 |= ((__INTERRUPT__) & UART_IT_MASK)))
很復雜,如果帶參數宏定義,把參數帶入進來,然后對比寄存器,配置中斷的,這個就不詳細寫了,其實如果不了解寄存器的話也可用起來的。(剛才分析了一下,感覺這塊還是要一步一步的寫出來,以后自己看還是很省事的,但是時間緊迫,以后在回過頭來補充)。
那如果發生了中斷呢,剛才配置的是USART1_IRQn,我們去查一下他,發現:
DCD USART1_IRQHandler ; USART1
void USART1_IRQHandler(void) { HAL_UART_IRQHandler(&huart1); }
串口1的中斷處理函數是USART1_IRQHandler,他又調用了HAL_UART_IRQHandler(&huart1):
void HAL_UART_IRQHandler(UART_HandleTypeDef *huart) { ... UART_Receive_IT(huart); //讀數據寄存器,並且調用回調函數 .... } static HAL_StatusTypeDef UART_Receive_IT(UART_HandleTypeDef *huart) { HAL_UART_RxCpltCallback(huart); // 回調函數,虛函數自己定義 } __weak void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { /* Prevent unused argument(s) compilation warning */ UNUSED(huart); /* NOTE: This function Should not be modified, when the callback is needed, the HAL_UART_TxCpltCallback could be implemented in the user file */ }
那么我們接收到了傳到串口的數據后我們利用hal庫函數也得到了,然后我們想利用一下這些數據做點什么怎么辦呢?這個接收中斷回調函數就來了,接收到數據后,執行中斷函數,中斷函數的功能是讀取數據寄存器,把數據放在我們指定的數組中,然后調用這個回調函數,我們在這各回調函數中就可以操作這些數據了(讀取到了),(但是根據正點院子教程所說,這么做效率不高,入門的話,就這么做就行,畢竟官方的)。
if(huart->Instance == USART1)//如果是串口1 { a = aRxBuffer1[0]; HAL_UART_Receive_IT(&huart1, (u8 *)aRxBuffer1, RXBUFFERSIZE1); }
這里要仔細說一下,我這么寫是來一個中斷,讀取一個字節(之前宏的),但是我用的JY901模塊是一次發送11個數據,每條11個字節,我就要中斷11次,想來效率不高,該進:把RXBUFFERSIZE1定義為11,一條指令包過來11個字節全部存儲在aRxBuffer1中,就行了,然后繼續接收使能。
概過程就是這樣,這里面並沒有發送中斷,目前我還沒用,只接受就行了,抓緊調四軸。
