STM32中斷控制


一、什么是中斷

1.1 基本概念

中斷,在單片機中占有非常重要的地位,幾乎任何一款單片機都會有中斷。。代碼默認地從上向下執行,遇到條件或者其他語句,會按照指定的地方跳轉。而在單片機執行代碼的過程中,難免會有一些突發的情況需要處理,這樣就會打斷當前的代碼,待處理完突發情況之后,程序會回到被打斷的地方繼續執行。

1.2 關於STM32的中斷

STM32具有十分強大的中斷系統,將中斷分為了兩個類型:內核異常和外部中斷。並將所有中斷通過一個表編排起來,下面是stm32中斷向量表的部分內容:

 

 

 

 

 

 上圖-3到6這個區域被標黑了,這個區域就是內核異常。內核異常不能夠被打斷,不能被設置優先級(也就是說優先級是凌駕於外部中斷之上的)。常見的內核異常有以下幾種:復位(reset),不可屏蔽中斷(NMI),硬錯誤(Hardfault),其他的也可以在表上找到。

從第7個開始,后面所有的中斷都是外部中斷。外部中斷是我們必須學習掌握的知識,包含線中斷,定時器中斷,IIC,SPI等所有的外設中斷,可配置優先級。外部中斷的優先級分為兩種:搶占優先級和響應優先級。

什么是搶占優先級?

搶占優先級比較霸道,一言不和就插隊。搶占優先級高的,能夠打斷優先級低的任務,等優先級較高的任務執行完畢后,再回來繼續執行之前的任務。所以當存在多個搶占優先級不同的任務時,很有可能會產生任務的嵌套。

什么是響應優先級?

響應優先級則稍微謙遜些,比較有禮貌。響應優先級又被稱為次優先級,若兩個任務的搶占式優先級一樣,那么響應優先級較高的任務則先執行,且在執行的同時不能被下一個響應優先級更高的任務打斷,所以我說它比較有有禮貌

1.3中斷發生的過程

 

 

 1.4中斷的作用

  • 速度匹配:可以解決快速的CPU與慢速的外部設備之間傳送數據的矛盾。
  • 分時操作:CPU可以分時為多個外部設備服務,提高計算機的利用率。
  • 實時響應:CPU能夠及時處理應用系統的隨機事件,增強系統的實時性。
  • 可靠性高:CPU可以處理設備故障及掉電等突發事件,提高系統可靠性。

2 HAL庫

2.1HAL的中斷

例如51單片機中的中斷函數,之前使用標准庫編寫程序時,中斷程序(函數)由我們自己實現。

而STM32的HAL庫的中斷處理函數是按照HAL處理機制來實現,如USART1,統一由HAL_UART_IRQHandler來進行處理,如下圖:

 

 

 

其它大部分外設(TIM、SPI、CAN...)中斷都類似,HAL進行統一處理。

也就是說,HAL已經幫我們把中斷處理函數寫好了,我們只需要調用相應函數來編寫應用程序就行了。

HAL_xxx_IRQHandler里面做了哪些處理? 我們以STM32F1的HAL_UART_IRQHandler為例:

void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)
{
   uint32_t isrflags   = READ_REG(huart->Instance->SR);
   uint32_t cr1its     = READ_REG(huart->Instance->CR1);
   uint32_t cr3its     = READ_REG(huart->Instance->CR3);
   uint32_t errorflags = 0x00U;
   uint32_t dmarequest = 0x00U;
 
 
  /* If no error occurs */
  errorflags = (isrflags & (uint32_t)(USART_SR_PE | USART_SR_FE | USART_SR_ORE | USART_SR_NE));
  if(errorflags == RESET)
  {
    /* UART in mode Receiver -------------------------------------------------*/
    if(((isrflags & USART_SR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET))
    {
      UART_Receive_IT(huart);
      return;
    }
  }
 
 
  /* If some errors occur */
  if((errorflags != RESET) && (((cr3its & USART_CR3_EIE) != RESET) || ((cr1its & (USART_CR1_RXNEIE | USART_CR1_PEIE)) != RESET)))
  {
  /*
  ·
  ·刪減了部分代碼
  ·
  */
  } /* End if some error occurs */
 
 
  /* UART in mode Transmitter ------------------------------------------------*/
  if(((isrflags & USART_SR_TXE) != RESET) && ((cr1its & USART_CR1_TXEIE) != RESET))
  {
    UART_Transmit_IT(huart);
    return;
  }
  /* UART in mode Transmitter end --------------------------------------------*/
  if(((isrflags & USART_SR_TC) != RESET) && ((cr1its & USART_CR1_TCIE) != RESET))
  {
    UART_EndTransmit_IT(huart);
    return;
  }
}

這些和我們之前編寫的中斷處理函數是不是有類似之處?

這是無非就是接收中斷、發送中斷、錯誤中斷等一系列處理。只是這里又進行了再次封裝,比如接收中斷UART_Receive_IT。

當然,這個UART_Receive_IT接收中斷實現方式又可能存在不同。像F0、F1...就是直接調用這個接收中斷函數來進一步處理。

像L0、G0...是通過執行指針函數RxISR來進一步處理。G0的接收中斷處理為:huart->RxISR(huart);

void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)
{
  //刪除了前面代碼
  /* If no error occurs */
  errorflags = (isrflags & (uint32_t)(USART_ISR_PE | USART_ISR_FE | USART_ISR_ORE | USART_ISR_NE));
  if (errorflags == 0U)
  {
    /* UART in mode Receiver ---------------------------------------------------*/
    if (((isrflags & USART_ISR_RXNE_RXFNE) != 0U)
        && (((cr1its & USART_CR1_RXNEIE_RXFNEIE) != 0U)
            || ((cr3its & USART_CR3_RXFTIE) != 0U)))
    {
      if (huart->RxISR != NULL)
      {
        huart->RxISR(huart);
      }
      return;
    }
  }
  //刪除了后面代碼
}

2.2回調函數

在HAL庫中存在大量類似HAL_XXX_XXXCallback這樣的函數,這些都是回調函數。

回調函數就是一個通過函數指針調用的函數。如果你把函數的指針(地址)作為參數傳遞給另一個函數,當這個指針被用來調用其所指向的函數時,我們就說這是回調函數。

回調函數不是由該函數的實現方直接調用,而是在特定的事件或條件發生時由另外的一方調用的,用於對該事件或條件進行響應。

三、進行一些嘗試(控制LED燈)

在STMCUBEX,新建一個項目,例如圖中用的單片機是STM32103C8

點擊sys,將debug選項改為Serial Wire

 

 然后在Rcc里的High Speed Clock 中選擇Crystal/Ceramic Resonator

 

 

 將PB0選為外部中斷觸發器(GPIO_EXTI0),PA1是控制led燈的,和將它選擇為GPIO_output

 

選擇PLLCLK,然后將后面的晶振頻率最大值改為72M赫茲

 

 

 project里把toolchain那里改為MDK-ARM,版本選擇最新

 

 選擇生成初始化文件,然后選擇生成代碼

 

 創建過程

 

創建成功

 

 打開項目,來到keil5,進入到mian.c里,接下來就是剛剛提到的在回調函數里寫代碼了,將回調函數重寫一遍就行了,代碼如下:

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin){
    GPIO_PinState b0_pin = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0);  // 讀取b0的狀態
    b0_pin=1-b0_pin;
    switch (GPIO_Pin){//判斷引腳
        case GPIO_PIN_0:
            HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1,1-b0_pin);  // 將a1寫入與b0相同的電位
            break;
    }
    
}

結果展示:

 

四、中斷實現串口通信

設置大多數和上述相同 下面只展示不同的部分

 

 

project manager里的步驟也和上一個是一樣的,接下來就是在keil5里打開項目,進入main.c文件,在里面定義如下數據,注意不是在main函數里,而是在頭文件后

uint8_t aRxBuffer;//接收緩沖中斷
uint8_t Uart1_RxBuff[256];//接收緩沖
uint8_t Uart1_Rx_Cnt=0;//接收緩沖計數
uint8_t cAlmStr[]="DataOverflow(>256)";

在定義了

 

 這個數據類型后插入代碼

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  if(Uart1_Rx_Cnt >= 255)  //溢出判斷
    {
        Uart1_Rx_Cnt = 0;
        memset(Uart1_RxBuff,0x00,sizeof(Uart1_RxBuff));
        HAL_UART_Transmit(&huart1, (uint8_t *)&cAlmStr, sizeof(cAlmStr),0xFFFF);    
    }
    else
    {
        Uart1_RxBuff[Uart1_Rx_Cnt++] = aRxBuffer;   //接收數據轉存
    
        if((Uart1_RxBuff[Uart1_Rx_Cnt-1] == 0x0A)||(Uart1_RxBuff[Uart1_Rx_Cnt-2] == 0x0D)) //判斷結束位
        {
            HAL_UART_Transmit(&huart1, (uint8_t *)&Uart1_RxBuff, Uart1_Rx_Cnt,0xFFFF); //將收到的信息發送出去
            Uart1_Rx_Cnt = 0;
            memset(Uart1_RxBuff,0x00,sizeof(Uart1_RxBuff)); //清空數組
        }
    }
    
    HAL_UART_Receive_IT(&huart1, (uint8_t *)&aRxBuffer, 1);   //再開啟接收中斷

}

然后在主函數寫一個接收中斷函數

int main(void)
{
 //初始化
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_USART1_UART_Init();
 
    
    
    //接收中斷函數
    HAL_UART_Receive_IT(&huart1,(uint8_t*)&aRxBuffer,1);
    
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

結果如下:

 

 我們發送什么 就會接受到什么。

五、總結

中斷函數的學習過程是一次很大的自我挑戰,但是中斷函數在單片機中的運用可以說是無比的重要,他極大程度的節約了單片機的使用資源,學會了使用中斷單片機可以不同再像之前一樣,傻傻的等待我們的指令再去進行操作,這次的學習過程讓我受益匪淺。

六、參考鏈接

https://blog.csdn.net/m0_58414679/article/details/121060073?spm=1001.2014.3001.5501

https://blog.csdn.net/DP29syM41zyGndVF/article/details/113804917

https://blog.csdn.net/as480133937/article/details/104827639

https://www.cnblogs.com/breezy-ye/articles/12157442


免責聲明!

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



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