【STM32H7教程】第33章 STM32H7的定時器應用之TIM1-TIM17的中斷實現

  • 2019 年 11 月 14 日
  • 筆記

完整教程下載地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980

第33章       STM32H7的定時器應用之TIM1-TIM17的中斷實現

本章教程為大家講解定時器應用之TIM1 – TIM17所有定時器的周期性中斷實現。實際項目中用到的地方較多,特別是周期性的事件查詢。

33.1 初學者重要提示

33.2 定時器中斷的驅動設計

33.3 定時器板級支援包(bsp_tim_pwm.c)

33.4 定時器驅動移植和使用

33.5 實驗常式設計框架

33.6 實驗常式說明(MDK)

33.7 實驗常式說明(IAR)

33.8 總結

33.1 初學者重要提示

  1.   學習本章節前,務必優先學習第32章,HAL庫的幾個常用API均作了講解和舉例。
  2.   STM32H7支援TIM1-TIM8,TIM12-TIM17共14個定時器,而中間的TIM9,TIM10,TIM11是不存在的,這點要注意。
  3.   STM32H7的進出中斷的速度能跑到12.5MHz,所有程式在TCM和Flash運行沒差別,詳情可看本章2.3小節。
  4.   實際應用中,中斷入口函數名稱不要寫錯,有些中斷的入口函數名稱比較特殊,詳情可看本章的2.2小節。

33.2 定時器中斷的驅動設計

定時器中斷的實現相對比較簡單,僅需一個函數即可實現TIM1-TIM17定時器的中斷更新配置。

33.2.1 定時器中斷初始化

實現程式碼如下:

1.    /*  2.    ******************************************************************************************************  3.    *    函 數 名: bsp_RCC_TIM_Enable  4.    *    功能說明: 使能TIM RCC 時鐘  5.    *    形    參: 無  6.    *    返 回 值: 無  7.    ******************************************************************************************************  8.    */  9.    void bsp_RCC_TIM_Enable(TIM_TypeDef* TIMx)  10.    {  11.        if (TIMx == TIM1) __HAL_RCC_TIM1_CLK_ENABLE();  12.        else if (TIMx == TIM2) __HAL_RCC_TIM2_CLK_ENABLE();  13.        else if (TIMx == TIM3) __HAL_RCC_TIM3_CLK_ENABLE();  14.        else if (TIMx == TIM4) __HAL_RCC_TIM4_CLK_ENABLE();  15.        else if (TIMx == TIM5) __HAL_RCC_TIM5_CLK_ENABLE();  16.        else if (TIMx == TIM6) __HAL_RCC_TIM6_CLK_ENABLE();  17.        else if (TIMx == TIM7) __HAL_RCC_TIM7_CLK_ENABLE();  18.        else if (TIMx == TIM8) __HAL_RCC_TIM8_CLK_ENABLE();  19.    //    else if (TIMx == TIM9) __HAL_RCC_TIM9_CLK_ENABLE();  20.    //    else if (TIMx == TIM10) __HAL_RCC_TIM10_CLK_ENABLE();  21.    //    else if (TIMx == TIM11) __HAL_RCC_TIM11_CLK_ENABLE();  22.        else if (TIMx == TIM12) __HAL_RCC_TIM12_CLK_ENABLE();  23.        else if (TIMx == TIM13) __HAL_RCC_TIM13_CLK_ENABLE();  24.        else if (TIMx == TIM14) __HAL_RCC_TIM14_CLK_ENABLE();  25.        else if (TIMx == TIM15) __HAL_RCC_TIM15_CLK_ENABLE();  26.        else if (TIMx == TIM16) __HAL_RCC_TIM16_CLK_ENABLE();  27.        else if (TIMx == TIM17) __HAL_RCC_TIM17_CLK_ENABLE();  28.        else  29.        {  30.            Error_Handler(__FILE__, __LINE__);  31.        }  32.    }  33.  34.    /*  35.    ******************************************************************************************************  36.    *    函 數 名: bsp_SetTIMforInt  37.    *    功能說明: 配置TIM和NVIC,用於簡單的定時中斷,開啟定時中斷。另外注意中斷服務程式需要由用戶應  38.    *              用程式實現。  39.    *    形    參: TIMx : 定時器  40.    *             _ulFreq : 定時頻率 (Hz)。 0 表示關閉。  41.    *             _PreemptionPriority : 搶佔優先順序  42.    *             _SubPriority : 子優先順序  43.    *    返 回 值: 無  44.    ******************************************************************************************************  45.    */  46.    void bsp_SetTIMforInt(TIM_TypeDef* TIMx, uint32_t _ulFreq, uint8_t _PreemptionPriority,  47.                          uint8_t _SubPriority)  48.    {  49.        TIM_HandleTypeDef   TimHandle = {0};  50.        uint16_t usPeriod;  51.        uint16_t usPrescaler;  52.        uint32_t uiTIMxCLK;  53.  54.        /* 使能TIM時鐘 */  55.        bsp_RCC_TIM_Enable(TIMx);  56.  57.        /*-----------------------------------------------------------------------  58.            bsp.c 文件中 void SystemClock_Config(void) 函數對時鐘的配置如下:  59.  60.            System Clock source       = PLL (HSE)  61.            SYSCLK(Hz)                = 400000000 (CPU Clock)  62.            HCLK(Hz)                  = 200000000 (AXI and AHBs Clock)  63.            AHB Prescaler             = 2  64.            D1 APB3 Prescaler         = 2 (APB3 Clock  100MHz)  65.            D2 APB1 Prescaler         = 2 (APB1 Clock  100MHz)  66.            D2 APB2 Prescaler         = 2 (APB2 Clock  100MHz)  67.            D3 APB4 Prescaler         = 2 (APB4 Clock  100MHz)  68.  69.            因為APB1 prescaler != 1, 所以 APB1上的TIMxCLK = APB1 x 2 = 200MHz;  70.            因為APB2 prescaler != 1, 所以 APB2上的TIMxCLK = APB2 x 2 = 200MHz;  71.            APB4上面的TIMxCLK沒有分頻,所以就是100MHz;  72.  73.            APB1 定時器有 TIM2, TIM3 ,TIM4, TIM5, TIM6, TIM7, TIM12, TIM13, TIM14,LPTIM1  74.            APB2 定時器有 TIM1, TIM8 , TIM15, TIM16,TIM17  75.  76.            APB4 定時器有 LPTIM2,LPTIM3,LPTIM4,LPTIM5  77.        ----------------------------------------------------------------------- */  78.        if ((TIMx == TIM1) || (TIMx == TIM8) || (TIMx == TIM15) || (TIMx == TIM16) || (TIMx == TIM17))  79.        {  80.            /* APB2 定時器時鐘 = 200M */  81.            uiTIMxCLK = SystemCoreClock / 2;  82.        }  83.        else  84.        {  85.            /* APB1 定時器 = 200M */  86.            uiTIMxCLK = SystemCoreClock / 2;  87.        }  88.  89.        if (_ulFreq < 100)  90.        {  91.            usPrescaler = 10000 - 1;                    /* 分頻比 = 10000 */  92.            usPeriod =  (uiTIMxCLK / 10000) / _ulFreq  - 1;        /* 自動重裝的值 */  93.        }  94.        else if (_ulFreq < 3000)  95.        {  96.            usPrescaler = 100 - 1;                    /* 分頻比 = 100 */  97.            usPeriod =  (uiTIMxCLK / 100) / _ulFreq  - 1;        /* 自動重裝的值 */  98.        }  99.        else    /* 大於4K的頻率,無需分頻 */  100.        {  101.            usPrescaler = 0;                    /* 分頻比 = 1 */  102.            usPeriod = uiTIMxCLK / _ulFreq - 1;    /* 自動重裝的值 */  103.        }  104.  105.        /*  106.           定時器中斷更新周期 = TIMxCLK / usPrescaler + 1)/usPeriod + 1)  107.        */  108.        TimHandle.Instance = TIMx;  109.        TimHandle.Init.Prescaler         = usPrescaler;  110.        TimHandle.Init.Period            = usPeriod;  111.        TimHandle.Init.ClockDivision     = 0;  112.        TimHandle.Init.CounterMode       = TIM_COUNTERMODE_UP;  113.        TimHandle.Init.RepetitionCounter = 0;  114.        TimHandle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;  115.        if (HAL_TIM_Base_Init(&TimHandle) != HAL_OK)  116.        {  117.            Error_Handler(__FILE__, __LINE__);  118.        }  119.  120.        /* 使能定時器中斷  */  121.        __HAL_TIM_ENABLE_IT(&TimHandle, TIM_IT_UPDATE);  122.  123.  124.        /* 配置TIM定時更新中斷 (Update) */  125.        {  126.            uint8_t irq = 0;    /* 中斷號, 定義在 stm32h7xx.h */  127.  128.            if (TIMx == TIM1) irq = TIM1_UP_IRQn;  129.            else if (TIMx == TIM2) irq = TIM2_IRQn;  130.            else if (TIMx == TIM3) irq = TIM3_IRQn;  131.            else if (TIMx == TIM4) irq = TIM4_IRQn;  132.            else if (TIMx == TIM5) irq = TIM5_IRQn;  133.            else if (TIMx == TIM6) irq = TIM6_DAC_IRQn;  134.            else if (TIMx == TIM7) irq = TIM7_IRQn;  135.            else if (TIMx == TIM8) irq = TIM8_UP_TIM13_IRQn;  136.            else if (TIMx == TIM12) irq = TIM8_BRK_TIM12_IRQn;  137.            else if (TIMx == TIM13) irq = TIM8_UP_TIM13_IRQn;  138.            else if (TIMx == TIM14) irq = TIM8_TRG_COM_TIM14_IRQn;  139.            else if (TIMx == TIM15) irq = TIM15_IRQn;  140.            else if (TIMx == TIM16) irq = TIM16_IRQn;  141.            else if (TIMx == TIM16) irq = TIM17_IRQn;  142.            else  143.            {  144.                Error_Handler(__FILE__, __LINE__);  145.            }  146.            HAL_NVIC_SetPriority((IRQn_Type)irq, _PreemptionPriority, _SubPriority);  147.            HAL_NVIC_EnableIRQ((IRQn_Type)irq);  148.        }  149.  150.        HAL_TIM_Base_Start(&TimHandle);  151.    }

程式中的注釋已經比較詳細,這裡把幾個關鍵的地方再闡釋下:

  •   第9- 32行,函數bsp_RCC_TIM_Enable用於獲取要使能的定時器時鐘。
  •   第49行,HAL庫的定時器句柄變數要初始化為0,這個問題在教程上一章的4.1小節有專門說明。
  •   第78 – 103行,計算出要配置的分頻和周期。這裡要注意一點,因為除了TIM2和TIM5,其它定時器都是16位的,相關暫存器大部分也都是16位的,配置的時候不可以超出0 -65535。這裡分頻變數usPrescaler和周期變數usPeriod統一按照16位計算,所以有了這幾行程式碼做頻率區分,防止超出範圍。
  •   第108 – 118行,通過函數HAL_TIM_Base_Init初始化定時器的基本功能。
  •   第125 – 148行,配置定時器中斷的優先順序,並使能中斷。

33.2.2 定時器中斷服務程式

定時器初始化完畢了,定時器中斷服務程式不要忘了寫。比如使用定時器6的中斷。

/*  *********************************************************************************************************  *    函 數 名: TIM6_DAC_IRQHandler  *    功能說明: TIM6定時中斷服務程式  *    返 回 值: 無  *********************************************************************************************************  */  void TIM6_DAC_IRQHandler(void)  {      if((TIM6->SR & TIM_FLAG_UPDATE) != RESET)      {          /* 清除標誌 */          TIM6->SR = ~ TIM_FLAG_UPDATE;          /* 添加用戶程式 */      }  }

使用定時器中斷不要把中斷入口函數的名字寫錯了,比如這個定時器6,很容易錯搞成TIM6__IRQHandler。

TIM1 – TIM17中斷入口名如下(在startup_stm32h743xx.s文件裡面有弱定義):

TIM1_BRK_IRQHandler  TIM1_UP_IRQHandler  TIM1_TRG_COM_IRQHandler  TIM1_CC_IRQHandler  TIM2_IRQHandler  TIM3_IRQHandler  TIM4_IRQHandler  TIM5_IRQHandler  TIM6_DAC_IRQHandler  TIM7_IRQHandler  TIM8_BRK_TIM12_IRQHandler         /* 注意這裡是TIM8 BRK和TIM12公用 */  TIM8_UP_TIM13_IRQHandler          /* 注意這裡是TIM8 UP和TIM13公用 */  TIM8_TRG_COM_TIM14_IRQHandler     /* 注意這裡是TIM8 TRG COM和TIM14公用 */  TIM8_CC_IRQHandler  TIM15_IRQHandler  TIM16_IRQHandler  TIM17_IRQHandler

33.2.3 定時器中斷的最高頻率

有時候我們希望定時器的中斷服務程式執行頻率越快越好,這樣可以方便的在中斷裡面執行一些特定功能,比如控制引腳輸出指定個數的IO脈衝,使用定時器中斷就可以方便的實現。

測試時開啟MDK的最高等級優化和時間優化。

測試下面情況下,性能沒差別:

  •   程式在Flash運行,變數在DTCM,開啟Cache。
  •   程式和變數都在DTCM運行。

中斷部分的測試程式:

/*  *********************************************************************************************************  *        函 數 名: TIM6_DAC_IRQHandler  *        功能說明: TIM6定時中斷服務程式  *        返 回 值: 無  *********************************************************************************************************  */  void TIM6_DAC_IRQHandler(void)  {       TIM6->SR = ~TIM_FLAG_UPDATE;       //GPIOB->ODR ^= GPIO_PIN_1;   /* 使用通用GPIO */       HC574_TogglePin(GPIO_PIN_23); /* 使用的FMC擴展IO */  }

測試結果是STM32H7的進出中斷的速度能跑到12.5MHz,所有程式在TCM和Flash運行沒差別。

IO翻轉10MHz,方波頻率5MHz:

IO翻轉12.5MHz,方波頻率6.25MHz:

12.5Hz是最高頻率,實際應用中別跑這麼高,因為這個頻率下,程式基本一直在執行中斷服務程式。實際應用做個1MHz及其以下還是沒問題的。

33.3 定時器板級支援包(bsp_tim_pwm.c)

定時器驅動文件bsp_tim_pwm.c主要實現了如下兩個API供用戶調用:

  •   bsp_SetTIMOutPWM
  •   bsp_SetTIMforInt

這兩個函數都是TIM1-TIM17所有定時器都支援,函數bsp_SetTIMOutPWM用於PWM,下個章節為大家講解,本小節主要把函數bsp_SetTIMforInt做個說明。

33.3.1 函數bsp_SetTIMforInt

函數原型:

void bsp_SetTIMforInt(TIM_TypeDef* TIMx, uint32_t _ulFreq, uint8_t _PreemptionPriority, uint8_t _SubPriority)

函數描述:

此函數主要用配置定時器周期性中斷。

函數參數:

  •   第1個參數用於指定使用那個定時器,參數可以是TIM1 – TIM17所有定時器(不含TIM9,TIM10和TIM11,因為STM32H7不支援這三個定時器)。
  •   第2個參數是要實現的定時器中斷頻率,單位Hz,如果填0的話,表示關閉。
  •   第3個參數是定時器搶佔式優先順序,範圍0 – 15。
  •   第4個參數是定時器子優先順序,範圍0 – 15。

注意事項:

  1. 定時器中斷頻率最好別超過10MHz,本章2.3小節有說明。
  2. 初始化後,別忘了寫對應的中斷服務程式。

使用舉例:

比如使用定時器6設置為20Hz頻率, 周期0.05秒定時中斷:

bsp_SetTIMforInt(TIM6, 20, 2, 0); 

33.4 定時器驅動移植和使用

定時器的移植比較簡單:

  •   第1步:複製bsp_tim_pwm.c和bsp_tim_pwm.h到自己的工程目錄,並添加到工程裡面。
  •   第2步:這幾個驅動文件主要用到HAL庫的GPIO和TIM驅動文件,簡單省事些可以添加所有HAL庫.C源文件進來。
  •   第3步:應用方法看本章節配套例子即可。

33.5 實驗常式設計框架

通過程式設計框架,讓大家先對配套常式有一個全面的認識,然後再理解細節,本次實驗常式的設計框架如下:

第1階段,上電啟動階段:

  • 這部分在第14章進行了詳細說明。

  第2階段,進入main函數:

  •  第1步,硬體初始化,主要是MPU,Cache,HAL庫,系統時鐘,滴答定時器,LED和串口。
  •  第2步,按鍵應用程式設計部分。
  •  定時器中斷服務程式裡面實現翻轉LED4和FMC擴展引腳23。

33.6 實驗常式說明(MDK)

配套例子:

V7-018_定時器周期性中斷(驅動支援TIM1-TIM17)

實驗目的:

  1. 學習定時器周期性中斷實現,支援TIM1-TIM17所有定時器。

實驗內容:

  1. 系統上電後驅動了1個軟體定時器,每100ms翻轉一次LED2,同時啟動1個TIM6周期性中斷,每50ms執行一次,在中斷服務程式裡面翻轉LED4和FMC擴展引腳23。
  2. STM32H7支援TIM1-TIM8,TIM12-TIM17共14個定時器,而中間的TIM9,TIM10,TIM11是不存在的。
  3. STM32H7的進出中斷的速度能跑到12.5MHz,所有程式在TCM和Flash運行沒差別,實際應用中最好別超過1MHz。
  4. 中斷入口函數名稱不要寫錯,有些中斷的入口函數名稱比較特殊,詳情可看V7開發板用戶手冊。

實驗操作:

  1. K1按鍵按下,開啟TIM6的周期性中斷。
  2. K2按鍵按下,關閉TIM6的周期性中斷。

FMC擴展引腳23的位置:

上電後串口列印的資訊:

波特率 115200,數據位 8,奇偶校驗位無,停止位 1

程式設計:

系統棧大小分配:

RAM空間用的DTCM:

硬體外設初始化

硬體外設的初始化是在 bsp.c 文件實現:

/*  *********************************************************************************************************  *    函 數 名: bsp_Init  *    功能說明: 初始化所有的硬體設備。該函數配置CPU暫存器和外設的暫存器並初始化一些全局變數。只需要調用一次  *    形    參:無  *    返 回 值: 無  *********************************************************************************************************  */  void bsp_Init(void)  {      /* 配置MPU */      MPU_Config();        /* 使能L1 Cache */      CPU_CACHE_Enable();        /*         STM32H7xx HAL 庫初始化,此時系統用的還是H7自帶的64MHz,HSI時鐘:         - 調用函數HAL_InitTick,初始化滴答時鐘中斷1ms。         - 設置NVIV優先順序分組為4。       */      HAL_Init();        /*         配置系統時鐘到400MHz         - 切換使用HSE。         - 此函數會更新全局變數SystemCoreClock,並重新配置HAL_InitTick。      */      SystemClock_Config();        /*         Event Recorder:         - 可用於程式碼執行時間測量,MDK5.25及其以上版本才支援,IAR不支援。         - 默認不開啟,如果要使能此選項,務必看V7開發板用戶手冊第xx章      */  #if Enable_EventRecorder == 1      /* 初始化EventRecorder並開啟 */      EventRecorderInitialize(EventRecordAll, 1U);      EventRecorderStart();  #endif        bsp_InitKey();        /* 按鍵初始化,要放在滴答定時器之前,因為按鈕檢測是通過滴答定時器掃描 */      bsp_InitTimer();      /* 初始化滴答定時器 */      bsp_InitUart();    /* 初始化串口 */      bsp_InitExtIO();    /* 初始化FMC匯流排74HC574擴展IO. 必須在 bsp_InitLed()前執行 */      bsp_InitLed();        /* 初始化LED */  }

MPU配置和Cache配置:

數據Cache和指令Cache都開啟。配置了AXI SRAM區(本例子未用到AXI SRAM)和FMC的擴展IO區。

/*  *********************************************************************************************************  *    函 數 名: MPU_Config  *    功能說明: 配置MPU  *    形    參: 無  *    返 回 值: 無  *********************************************************************************************************  */  static void MPU_Config( void )  {      MPU_Region_InitTypeDef MPU_InitStruct;        /* 禁止 MPU */      HAL_MPU_Disable();        /* 配置AXI SRAM的MPU屬性為Write back, Read allocate,Write allocate */      MPU_InitStruct.Enable           = MPU_REGION_ENABLE;      MPU_InitStruct.BaseAddress      = 0x24000000;      MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;      MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;      MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;      MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;      MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;      MPU_InitStruct.Number           = MPU_REGION_NUMBER0;      MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;      MPU_InitStruct.SubRegionDisable = 0x00;      MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;        HAL_MPU_ConfigRegion(&MPU_InitStruct);          /* 配置FMC擴展IO的MPU屬性為Device或者Strongly Ordered */      MPU_InitStruct.Enable           = MPU_REGION_ENABLE;      MPU_InitStruct.BaseAddress      = 0x60000000;      MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;      MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;      MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;      MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;      MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;      MPU_InitStruct.Number           = MPU_REGION_NUMBER1;      MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;      MPU_InitStruct.SubRegionDisable = 0x00;      MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;        HAL_MPU_ConfigRegion(&MPU_InitStruct);        /*使能 MPU */      HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);  }    /*  *********************************************************************************************************  *    函 數 名: CPU_CACHE_Enable  *    功能說明: 使能L1 Cache  *    形    參: 無  *    返 回 值: 無  *********************************************************************************************************  */  static void CPU_CACHE_Enable(void)  {      /* 使能 I-Cache */      SCB_EnableICache();        /* 使能 D-Cache */      SCB_EnableDCache();  }

定時器中斷服務程式:

定時器6的中斷服務程式如下,主要實現了LED4翻轉和FMC擴展引腳23的翻轉:

/*  *********************************************************************************************************  *    函 數 名: TIM6_DAC_IRQHandler  *    功能說明: TIM6定時中斷服務程式  *    返 回 值: 無  *********************************************************************************************************  */  void TIM6_DAC_IRQHandler(void)  {      if((TIM6->SR & TIM_FLAG_UPDATE) != RESET)      {          /* 清除更新標誌 */          TIM6->SR = ~ TIM_FLAG_UPDATE;            /* 翻轉LED4和FMC擴展引腳23 */          bsp_LedToggle(4);          HC574_TogglePin(GPIO_PIN_23);      }  }

主功能:

主程式實現如下操作:

  •   K1按鍵按下,開啟TIM6的周期性中斷。
  •   K2按鍵按下,關閉TIM6的周期性中斷。
/*  *********************************************************************************************************  *    函 數 名: main  *    功能說明: c程式入口  *    形    參: 無  *    返 回 值: 錯誤程式碼(無需處理)  *********************************************************************************************************  */  int main(void)  {      uint8_t ucKeyCode;    /* 按鍵程式碼 */          bsp_Init();        /* 硬體初始化 */        PrintfLogo();    /* 列印常式名稱和版本等資訊 */      PrintfHelp();    /* 列印操作提示 */        bsp_StartAutoTimer(0, 100);    /* 啟動1個100ms的自動重裝的定時器 */        bsp_SetTIMforInt(TIM6, 20, 2, 0);    /* 設置為20Hz頻率, 周期0.05秒定時中斷*/        /* 進入主程式循環體 */      while (1)      {          bsp_Idle();        /* 這個函數在bsp.c文件。用戶可以修改這個函數實現CPU休眠和喂狗 */            /* 判斷定時器超時時間 */          if (bsp_CheckTimer(0))          {              /* 每隔50ms 進來一次 */              bsp_LedToggle(2);          }            /* 按鍵濾波和檢測由後台systick中斷服務程式實現,我們只需要調用bsp_GetKey讀取鍵值即可。 */          ucKeyCode = bsp_GetKey();    /* 讀取鍵值, 無鍵按下時返回 KEY_NONE = 0 */          if (ucKeyCode != KEY_NONE)          {              switch (ucKeyCode)              {                  case KEY_DOWN_K1:            /* K1鍵按下 */                      TIM6->DIER |= TIM_IT_UPDATE;                      break;                    case KEY_DOWN_K2:            /* K2鍵按下 */                      TIM6->DIER &= ~TIM_IT_UPDATE;                      break;                    default:                      /* 其它的鍵值不處理 */                      break;              }          }      }  }

33.7 實驗常式說明(IAR)

配套例子:

V7-018_定時器周期性中斷(驅動支援TIM1-TIM17)

實驗目的:

  1. 學習定時器周期性中斷實現,支援TIM1-TIM17所有定時器。

實驗內容:

  1. 系統上電後驅動了1個軟體定時器,每100ms翻轉一次LED2,同時啟動1個TIM6周期性中斷,每50ms執行一次,在中斷服務程式裡面翻轉LED4和FMC擴展引腳23。
  2. STM32H7支援TIM1-TIM8,TIM12-TIM17共14個定時器,而中間的TIM9,TIM10,TIM11是不存在的。
  3. STM32H7的進出中斷的速度能跑到12.5MHz,所有程式在TCM和Flash運行沒差別,實際應用中最好別超過1MHz。
  4. 中斷入口函數名稱不要寫錯,有些中斷的入口函數名稱比較特殊,詳情可看V7開發板用戶手冊。

實驗操作:

  1. K1按鍵按下,開啟TIM6的周期性中斷。
  2. K2按鍵按下,關閉TIM6的周期性中斷。

FMC擴展引腳23的位置:

上電後串口列印的資訊:

波特率 115200,數據位 8,奇偶校驗位無,停止位 1

程式設計:

系統棧大小分配:

RAM空間用的DTCM:

硬體外設初始化

硬體外設的初始化是在 bsp.c 文件實現:

/*  *********************************************************************************************************  *    函 數 名: bsp_Init  *    功能說明: 初始化所有的硬體設備。該函數配置CPU暫存器和外設的暫存器並初始化一些全局變數。只需要調用一次  *    形    參:無  *    返 回 值: 無  *********************************************************************************************************  */  void bsp_Init(void)  {      /* 配置MPU */      MPU_Config();        /* 使能L1 Cache */      CPU_CACHE_Enable();        /*         STM32H7xx HAL 庫初始化,此時系統用的還是H7自帶的64MHz,HSI時鐘:         - 調用函數HAL_InitTick,初始化滴答時鐘中斷1ms。         - 設置NVIV優先順序分組為4。       */      HAL_Init();        /*         配置系統時鐘到400MHz         - 切換使用HSE。         - 此函數會更新全局變數SystemCoreClock,並重新配置HAL_InitTick。      */      SystemClock_Config();        /*         Event Recorder:         - 可用於程式碼執行時間測量,MDK5.25及其以上版本才支援,IAR不支援。         - 默認不開啟,如果要使能此選項,務必看V7開發板用戶手冊第xx章      */  #if Enable_EventRecorder == 1      /* 初始化EventRecorder並開啟 */      EventRecorderInitialize(EventRecordAll, 1U);      EventRecorderStart();  #endif        bsp_InitKey();        /* 按鍵初始化,要放在滴答定時器之前,因為按鈕檢測是通過滴答定時器掃描 */      bsp_InitTimer();      /* 初始化滴答定時器 */      bsp_InitUart();    /* 初始化串口 */      bsp_InitExtIO();    /* 初始化FMC匯流排74HC574擴展IO. 必須在 bsp_InitLed()前執行 */      bsp_InitLed();        /* 初始化LED */  }

MPU配置和Cache配置:

數據Cache和指令Cache都開啟。配置了AXI SRAM區(本例子未用到AXI SRAM)和FMC的擴展IO區。

/*  *********************************************************************************************************  *    函 數 名: MPU_Config  *    功能說明: 配置MPU  *    形    參: 無  *    返 回 值: 無  *********************************************************************************************************  */  static void MPU_Config( void )  {      MPU_Region_InitTypeDef MPU_InitStruct;        /* 禁止 MPU */      HAL_MPU_Disable();        /* 配置AXI SRAM的MPU屬性為Write back, Read allocate,Write allocate */      MPU_InitStruct.Enable           = MPU_REGION_ENABLE;      MPU_InitStruct.BaseAddress      = 0x24000000;      MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;      MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;      MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;      MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;      MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;      MPU_InitStruct.Number           = MPU_REGION_NUMBER0;      MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;      MPU_InitStruct.SubRegionDisable = 0x00;      MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;        HAL_MPU_ConfigRegion(&MPU_InitStruct);          /* 配置FMC擴展IO的MPU屬性為Device或者Strongly Ordered */      MPU_InitStruct.Enable           = MPU_REGION_ENABLE;      MPU_InitStruct.BaseAddress      = 0x60000000;      MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;      MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;      MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;      MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;      MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;      MPU_InitStruct.Number           = MPU_REGION_NUMBER1;      MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;      MPU_InitStruct.SubRegionDisable = 0x00;      MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;        HAL_MPU_ConfigRegion(&MPU_InitStruct);        /*使能 MPU */      HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);  }    /*  *********************************************************************************************************  *    函 數 名: CPU_CACHE_Enable  *    功能說明: 使能L1 Cache  *    形    參: 無  *    返 回 值: 無  *********************************************************************************************************  */  static void CPU_CACHE_Enable(void)  {      /* 使能 I-Cache */      SCB_EnableICache();        /* 使能 D-Cache */      SCB_EnableDCache();  }

定時器中斷服務程式:

定時器6的中斷服務程式如下,主要實現了LED4翻轉和FMC擴展引腳23的翻轉:

/*  *********************************************************************************************************  *    函 數 名: TIM6_DAC_IRQHandler  *    功能說明: TIM6定時中斷服務程式  *    返 回 值: 無  *********************************************************************************************************  */  void TIM6_DAC_IRQHandler(void)  {      if((TIM6->SR & TIM_FLAG_UPDATE) != RESET)      {          /* 清除更新標誌 */          TIM6->SR = ~ TIM_FLAG_UPDATE;            /* 翻轉LED4和FMC擴展引腳23 */          bsp_LedToggle(4);          HC574_TogglePin(GPIO_PIN_23);      }  }

主功能:

主程式實現如下操作:

  •   K1按鍵按下,開啟TIM6的周期性中斷。
  •   K2按鍵按下,關閉TIM6的周期性中斷。
/*  *********************************************************************************************************  *    函 數 名: main  *    功能說明: c程式入口  *    形    參: 無  *    返 回 值: 錯誤程式碼(無需處理)  *********************************************************************************************************  */  int main(void)  {      uint8_t ucKeyCode;    /* 按鍵程式碼 */          bsp_Init();        /* 硬體初始化 */        PrintfLogo();    /* 列印常式名稱和版本等資訊 */      PrintfHelp();    /* 列印操作提示 */        bsp_StartAutoTimer(0, 100);    /* 啟動1個100ms的自動重裝的定時器 */        bsp_SetTIMforInt(TIM6, 20, 2, 0);    /* 設置為20Hz頻率, 周期0.05秒定時中斷*/        /* 進入主程式循環體 */      while (1)      {          bsp_Idle();        /* 這個函數在bsp.c文件。用戶可以修改這個函數實現CPU休眠和喂狗 */            /* 判斷定時器超時時間 */          if (bsp_CheckTimer(0))          {              /* 每隔50ms 進來一次 */              bsp_LedToggle(2);          }            /* 按鍵濾波和檢測由後台systick中斷服務程式實現,我們只需要調用bsp_GetKey讀取鍵值即可。 */          ucKeyCode = bsp_GetKey();    /* 讀取鍵值, 無鍵按下時返回 KEY_NONE = 0 */          if (ucKeyCode != KEY_NONE)          {              switch (ucKeyCode)              {                  case KEY_DOWN_K1:            /* K1鍵按下 */                      TIM6->DIER |= TIM_IT_UPDATE;                      break;                    case KEY_DOWN_K2:            /* K2鍵按下 */                      TIM6->DIER &= ~TIM_IT_UPDATE;                      break;                    default:                      /* 其它的鍵值不處理 */                      break;              }          }      }  }

33.8 總結

本章節就為大家講解這麼多,相對比較容易掌握,望初學者熟練運用。