STM32定時器應用——PWM


STM32的定時器有三種,高級定時器(TIM1和TIM8),通用定時器(TIM2、TIM3、TIM4、TIM5)和基本定時器(TIM6和TIM7)。

這三者的區別是:

  • 基本定時器:基本定時器功能比較簡單,主要是計時,也可以為DAC提供時鍾,直接觸發驅動DAC

     

     

  • 通用定時器:通用定時器除了基本的定時功能外,還可以測量輸入信號的脈沖長度,也就是輸入捕獲功能,也可以產生輸出波形,即輸出比較和PWM。                                                

     

     

  • 高級定時器:通用定時器有的功能,高級定時器也有,而且高級定時器還可以輸出嵌入死區的互補PWM。 

 

這幾天放假,在家搜到一個NUCLEO-L010RB的開發板,並且在stm社區下載了相關的幾份資料,就搗鼓了一下。

 

 

這個板子用到的MCU是STM32L010RBT6,這個板子剛開始我還不知道怎么用,不知道燒錄程序時是否需要外接ST-Link,最后搗鼓着搗鼓着發現它本身自帶ST-Link的功能,USB端口不僅可以給板子供電,還可以用於燒錄程序。

第一個程序當然是點亮一個LED燈啦,查閱相關文檔發現這個板子的PA5引腳上接了LED2,而且該引腳也是TIM2的CH1通道,那么就可以通過TIM2的CH1輸出PWM波控制LED的亮度,實現呼吸燈效果。

 

一、首先用CubeMX工具生成代碼框架:

 

 

 這個MCU跟STM32的其他系列有很多的不同,比如定時器方面,這個單片機只有TIM2,TIM21和TIM22。這個程序中用到了TIM2和TIM21這兩個定時器,TIM2用於生成PWM調節LED的亮度,TIM21用於定時修改PWM的占空比。

①設置時鍾源,原理圖上HSE上接的是8MHz的晶振,但是實物上實際並沒有外接這個晶振,所以HSE沒有配置。LSE上接了32.768KHz的晶振,所以LSE上就選擇了Crystal/Ceramic Resonator。如圖:

 

 

 

 

 

 

 ②定時器設置,如圖:

 

 

 

 

 

  • 定時器的時鍾源一般都選擇內部時鍾源。
  • 這次我們用到TIM2的CH1輸出PWM波,所以Channel1應該選擇PWM Generation CH1。
  • TIM2掛載在APB1上,而APB1的時鍾頻率為32MHz,所以TIM2的預分頻器設置為32000,分頻后得到1KHz,也就是1ms計數一次,計數模式選擇向上,ARR設置為20。
  • PWM的模式設置為 PWM mode 1,Pulse設置為0。PWM的模式有兩種,模式1:向上計數時,CNT<CCRx時輸出有效電平,CNT>=CCRx時輸出無效電平。向下計數時,CNT>CCRx時輸出無效電平,CNT<=CCRx輸出有效電平。模式2:向上計數時,CNT<CCRx時輸出無效電平,CNT>=CCRx時輸出有效電平。向下計數時,CNT>CCRx時輸出有效電平,CNT<=CCRx輸出無效電平。二者剛好是相反的。(Pulse就是CCRx的值,這里初始化為0,當然也可以設置為小於ARR大於0的其他數值)
  • CH Polarity選擇為High,也就是設置有效電平為高電平。

  TIM21的作用只是定時而已,所以設置比較簡單,同樣時鍾源選擇內部時鍾,預分頻32000,向上計數,ARR為100,因為TIM21掛載在APB2上也是32MHz,所以最終的結果是100ms產生一個中斷(TIM21的全局中斷需要打開)

 

 

 打開TIM21的全局中斷:

 

 

 ③時鍾配置:由圖可知,外部高速時鍾(HSE)的外部晶振和外部低速時鍾(LSE)跟整個時鍾樹的其他部分是斷開的,所以我選擇了內部高速時鍾(HSI)作為系統時鍾源,不過精度肯定沒有外部晶振准確,因為HSI采用的是RC振盪器,容易受到溫度的影響。

 

 

 

二、代碼修改

①首先需要在main()函數里面調用兩個函數:

HAL_TIM_Base_Start_IT(&htim21);//開啟TIM21的中斷
HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_1);//開啟PWM的輸出

②在TIM21中斷的回調函數里增加功能代碼:

1 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
 2 {
 3     static uint8_t DutyCycle = 5;
 4     static uint8_t ToggleFlag = 0;
 5     if(htim->Instance == TIM21)
 6     {
 7         if(ToggleFlag == 0)
 8         {
 9             DutyCycle += 1;
10             if(DutyCycle > 19)
11             {
12                 DutyCycle = 19;
13                 ToggleFlag = 1;
14             }
15         }
16         else
17         {
18             DutyCycle -= 1;
19             if(DutyCycle <= 1)
20             {
21                 ToggleFlag = 0;
22             }
23         }
24         
25         TIM2->CCR1 = DutyCycle;
26     }
27 }

 

TIM21每100ms進入一次中斷,就會調用這個回調函數,這個函數里實現的功能就是修改TIM2的CCR1的值,也就是修改PWM的占空比,CCR1每次比上一次的數值增加1(直到19(ARR的值),也就是占空比為1),當CCR1的值達到了19,又開始遞減。所以LED的效果就是從暗逐漸變量又從亮逐漸變暗,就好像呼吸一樣。關系到呼吸燈的效果有幾個參數:

  ①htim2.Init.Prescaler(TIM2的預分頻系數),htim2.Init.Period(TIM2 的計數周期,也就是ARR的值),預分頻系數越大,得到的頻率越小,CNT增加得越慢,可能造成的效果就是看到燈在閃,因為我們看到燈的亮度變化並不是真的可以從本質上改變燈的亮度,而是一個周期內燈亮的時間占了多大的比例,由於一個周期時間很短,短到人眼分辨不出亮滅的變化,就會覺得燈的亮度變化了,所以周期也不應該太長,太長的話,人眼就可以分辨出亮滅變化,就會覺得燈在閃。

  ②CCR1每次的增量太大的話,呼吸燈就沒有一個漸變的過程,顯得不自然。

  ③htim21.Init.Prescaler 和 htim21.Init.Period這兩個參數決定多久更新一次CCR1的數值,也就是決定每一個亮度可以保持多久的時間。

PS:下面是TIM2和TIM21的初始化代碼:

 1 void MX_TIM2_Init(void)
 2 {
 3   TIM_ClockConfigTypeDef sClockSourceConfig = {0};
 4   TIM_MasterConfigTypeDef sMasterConfig = {0};
 5   TIM_OC_InitTypeDef sConfigOC = {0};
 6 
 7   htim2.Instance = TIM2;
 8   htim2.Init.Prescaler = 8000-1;
 9   htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
10   htim2.Init.Period = 50-1;
11   htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
12   htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
13   if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
14   {
15     Error_Handler();
16   }
17   sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
18   if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
19   {
20     Error_Handler();
21   }
22   if (HAL_TIM_PWM_Init(&htim2) != HAL_OK)
23   {
24     Error_Handler();
25   }
26   sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
27   sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
28   if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
29   {
30     Error_Handler();
31   }
32   sConfigOC.OCMode = TIM_OCMODE_PWM1;
33   sConfigOC.Pulse = 0;
34   sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
35   sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
36   if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
37   {
38     Error_Handler();
39   }
40   HAL_TIM_MspPostInit(&htim2);
41 
42 }

 

 1 void MX_TIM21_Init(void)
 2 {
 3   TIM_ClockConfigTypeDef sClockSourceConfig = {0};
 4   TIM_MasterConfigTypeDef sMasterConfig = {0};
 5 
 6   htim21.Instance = TIM21;
 7   htim21.Init.Prescaler = 32000-1;
 8   htim21.Init.CounterMode = TIM_COUNTERMODE_UP;
 9   htim21.Init.Period = 250-1;
10   htim21.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
11   htim21.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
12   if (HAL_TIM_Base_Init(&htim21) != HAL_OK)
13   {
14     Error_Handler();
15   }
16   sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
17   if (HAL_TIM_ConfigClockSource(&htim21, &sClockSourceConfig) != HAL_OK)
18   {
19     Error_Handler();
20   }
21   sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
22   sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
23   if (HAL_TIMEx_MasterConfigSynchronization(&htim21, &sMasterConfig) != HAL_OK)
24   {
25     Error_Handler();
26   }
27 
28 }

 


免責聲明!

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



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