stm32 HAL庫筆記(一)——串口的操作


  昨天分析了普通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中,就行了,然后繼續接收使能。

  概過程就是這樣,這里面並沒有發送中斷,目前我還沒用,只接受就行了,抓緊調四軸。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM