版本 4a671bff5821f1959efb67028edcc3d737b8948d
Changes from 4a671bff5821f1959efb67028edcc3d737b8948d to b32897dee4705c7307ad725ebe2f38382b5302e5
---
title: Pulse-Width Modulation (PWM)
toc: yes
...
Introduction
============
- Pulse-Width Modulation, 又稱pulse-duration modulation(PDM),其利用在頻率不變的狀態下, 改變工作週期大小, 使整體平均電壓值上升或下降, 藉此間歇性電壓及功率切換以節省能源及控制等效果.
.. image:: /PWM_intr.PNG
- PWM會較省電的原因
因為一般類比電壓要降低電壓輸出需靠增加電阻,源頭輸出電壓"持續"都為同一電壓,不過利用電阻改變最後輸出電壓,而PWM他靠的是一段時間內輸出的頻率來模擬類比電壓,"不需要持續的輸出",故不會將電浪費在電阻上,即可達到省電效果。
- STM32內部要產生PWM訊號時,需要透過Timer來實現。
Clock control
==============
- 在學習Timer前, 先瞭解系統clock是如何產生的, 以及給Timer的clock值.
- A part of clock tree
[#]_
.. [#] ` Clock tree<http://www.st.com/st-web-ui/static/active/en/resource/technical/document/reference_manual/DM00031020.pdf>`_ p. 212
.. image:: /Timer_clock.png
- 我們使用Crystal resonator作為外部震盪, 其頻率為8Hz(由STM32f4 discovery user manual 可知)
[#]_
.. [#] ` system_stm32f4xx.c<https://github.com/PJayChen/STM32f4_discovery_TIM_PWM_Output/blob/master/TIM_PWM_Output/system_stm32f4xx.c>`_
- APBx timer clocks 計算
- 參閱system_stm32f4xx.c
- line 341 RCC->CR |= ((uint32_t)RCC_CR_HSEON);
- enable HSE(High Speed External clock)
- line 374 ~ 379 配置與啟動PLL
- line 146 ~ 151, 可知
- PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N
- SYSCLK = PLL_VCO / PLL_P
- 並定義 PLL_M = 8, PLL_N = 336, PLL_P = 2 而外部晶體頻率為8MHz
- -> PLL_VCO = (8MHz / 8) * 336 = 336MHz
- -> SYSCLK = 336MHz / 2 = 168MHz
- line 365 RCC->CFGR |= RCC_CFGR_HPRE_DIV1;
- 此行設定HCLK(High speed external clock signal) = SYSCLK / 1 = 168MHz
- line 372 RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;
- 此行設定APB1 peripheral clocks為 HCLK / 4 = 42MHz
- 而由上圖可知
- APB1 timer clocks = APB1 peripheral clocks * 2 = **84MHz**
- 該clock亦為後面Timer章節提及的 **CK_INT** 之值
Timers
=======
- Timer 和 RTC的差別
- Timer可被用於多種用途, 其中包含量測輸入訊號之pulse寬度, 或產生輸出波形。
- Real-Time Clock(RTC)是負責記錄時間的專用積體電路,出現在需要長期使用時鐘的電子設備中。[#]_
.. [#] `RTC introducion<http://wiki.csie.ncku.edu.tw/embedded/RTC#introduction>`_
Basic timers 基本定時器(TIM6 和 TIM7)
----------------------------------------
[#]_
.. [#] ` TIM6 和 TIM7<http://www.st.com/st-web-ui/static/active/en/resource/technical/document/reference_manual/DM00031020.pdf>`_ p. 678
- 具有 16-bit auto-reload upcounter driven by a programmable prescaler
- 用途: Synchronization circuit to trigger the DAC(內部連接至DAC)
Advanced-control timers 高級控制定時器(TIM1 和 TIM8)
-----------------------------------------------------
[#]_
.. [#] ` TIM1 和 TIM8<http://www.st.com/st-web-ui/static/active/en/resource/technical/document/reference_manual/DM00031020.pdf>`_ p. 507
- 具有 16-bit up, down, up/down auto-reload counter driven by a programmable prescaler which allowing dividing the counter clock frequency either by any factor between 1 and 65536(2^16).
- 用途: 最多有**四個**獨立通道可用於input capture, output compare, PWM generation(Edge and Center-aligned Mode) and one-pulse mode output.
- 與TIM2&TIM5之差異: 可以 Break input to put the timer's output signals in reset state or in a known state
General-purpose timers 通用定時器(TIM9 to TIM14)
------------------------------------------------------
[#]_
.. [#] ` TIM9 to TIM14<http://www.st.com/st-web-ui/static/active/en/resource/technical/document/reference_manual/DM00031020.pdf>`_ p. 637
- 具有16-bit auto-reload up counter.
- 以及16-bit programmable prescaler used to divide the counter clock frequency by any factor between 1 and 65536.
- 用途: 最多有**兩個**獨立通道可用於 input capture, output compare, PWM generation(Edge and Center-aligned Mode) and one-pulse mode output.
General-purpose timers 通用定時器(TIM2 to TIM5)
--------------------------------------------------------------------------------------
[#]_
.. [#] ` TIM2 to TIM5<http://www.st.com/st-web-ui/static/active/en/resource/technical/document/reference_manual/DM00031020.pdf>`_ p. 576
- 具有16-bit (TIM3 & TIM4) or 32-bit (TIM2 & TIM5) up, down, up/dowm auto-reload counter.
- 以及16-bit programmable prescaler used to divide the counter clock frequency by any factor between 1 and 65536.
- 用途: 最多有**四個**獨立通道可用於 input capture, output compare, PWM generation(Edge and Center-aligned Mode) and one-pulse mode output.
- Block diagram
.. image:: /BD of GP timer.png
- clock來源 :
- Internal clock(CK_INT)
- External clock mode1: external input pin(TIx)
- External clock mode2: external trigger input(ETR) available on TIM2, TIM3 and TIM4 only
- Internal trigger inputs(ITRx): 使用另一個timer作為prescaler來產生除頻後之clock
Counter Modes
=============
[#]_
.. [#] ` Counter Modes<http://www.st.com/st-web-ui/static/active/en/resource/technical/document/reference_manual/DM00031020.pdf>`_ p. 579
Registers
---------
- TIMx_CNT: Counter Register, 計數器目前數道的值
- TIMx_PSC: Prescaler Register (software R/W), 除頻數
- TIMx_ARR: Auto-Reload Register (software R/W), 存放計數起始(下數)或最大(上數)值
- UEV: 當計數器overflow or underflow, 將會產生update event(UEV), 此時會把 Auto-Reload preload Register (software write)內容放入 Auto-Reload shadow register (hardware write)
- auto-reload preload enable bit(ARPE) in the TIMx_CR1 register之值設定是否直接放入,
- ARPE = 0 直接把值轉入shadow register,
- ARPE = 1 UEV產生時才將值轉入shadow register
Prescaler Description
-----------------------
.. code-block:: prettyprint linenums
TIM_TimeBaseInitStruct.TIM_Prescaler = PRES_VALUE - 1;
- 可除頻範圍1 ~ 65536(Set it in TIMx_PSC register)
- example: prescaler control register設為1,但須等 update event產生才會載入至prescaler buffer, 因此下一個計數週期才會依據該值來除頻.
.. image:: /Timer_prescaler_description.PNG
Upcounting mode
----------------
.. code-block:: prettyprint linenums
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
- 由0數至TIMx_ARR register之值, 然後再回到0並產生overflow event(update event), 此時auto-reload shadow register將會更新為TIMx_ARR之值.
- 可藉由設定UDIS bit = 1來關閉UEV, 可用於避免正在更改preload register內容時, 剛好發生UEV而將舊的值轉移至shadow register中
- Example: TIMx_ARR = 0x36, prescaler buffer = 1
.. image:: /Timer_upcounting.png
- Example: TIMx_ARR = 0x36, ARPE = 0
.. image:: /Timer_upcount_ARPE0.PNG
- Example: TIMx_ARR = 0x36, ARPE = 1
.. image:: /Timer_upcount_ARPE1.PNG
UEV(clear by hardware) occurs every time an overflow occurs, and UIF is a flag triggered when overflow occurs, if you don't clean the flag, it will remain triggered(clear by software).
Downcounting mode
------------------
.. code-block:: prettyprint linenums
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Down;
.. image:: /countdown.png
Center-aligned mode
-------------------
.. code-block:: prettyprint linenums
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_CenterAligned1;
.. image:: /center_aligned.png
Functional Modes
=================
Input capture mode
------------------
[#]_
.. [#] `Input capture mode<http://www.st.com/st-web-ui/static/active/en/resource/technical/document/reference_manual/DM00031020.pdf>`_ p. 592
- 目的 : 用來計算某外部信號特定狀態發生的時間點
- 用途 : 脈波寬測量, 頻率量測
- 可以設定外部觸發信號的型式,並使用外部觸發來觸動一個Timer的栓鎖動作,這時候Timer計時值則會存入TIMx_CCRx暫存器。
PWM input mode
--------------
[#]_
.. [#] `PWM input mode<http://www.st.com/st-web-ui/static/active/en/resource/technical/document/reference_manual/DM00031020.pdf>`_ p. 593
- 為Input capture mode的一個特例,此模式能夠量測到TI1上的PWM信號長度(TIMx_CCR1暫存器)和工作週期(TIMx_CCR2暫存器),所以會使用到兩個通道。
Forced output mode
-------------------
[#]_
.. [#] `Forced output mode<http://www.st.com/st-web-ui/static/active/en/resource/technical/document/reference_manual/DM00031020.pdf>`_ p. 594
- 在輸出模式下,輸出比較信號能夠直接由軟體強制設為有效(active)或是無效(inactive)的狀態,而無視輸出比較暫存器和計數器之間的比較結果。
.. image:: /timer output stage.PNG
- TIMx_CCMRx = 100 -> force **inactive** level -> ocxref force **low**
- TIMx_CCMRx = 101 -> force **active** level -> ocxref force **high**
- 而OCx又會受到CCxP bit (TIM_OCPolarity)影響
- CCxP = 0 (TIM_OCPolarity = TIM_OCPolarity_Low) -> OCx = ocxref (default)
- CCxP = 1 (TIM_OCPolarity = TIM_OCPolarity_High) -> OCx = !ocxref
- 對應
.. code-block:: prettyprint linenums
TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_Low;
TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;
Output compare mode
-------------------
[#]_
.. [#] `PWM input mode<http://www.st.com/st-web-ui/static/active/en/resource/technical/document/reference_manual/DM00031020.pdf>`_ p. 594
- 此項功能是用來控制一個輸出波形或是用來指示一段給定的時間已到。
Detail of OC1M[2:0] in the TIMx_CCMR1 Register
------------------------------------------------
[#]_
.. [#] `TIMx_CCMR1<http://www.st.com/st-web-ui/static/active/en/resource/technical/document/reference_manual/DM00031020.pdf>`_ p. 621
.. image:: /OC1M(output compare mode).png
**PWM mode**
------------
[#]_
.. [#] `PWM mode<http://www.st.com/st-web-ui/static/active/en/resource/technical/document/reference_manual/DM00031020.pdf>`_ p. 595
- 目的 : 產生一個由TIMx_ARR暫存器決定頻率、由TIMx_CCRx暫存器決定工作週期的PWM信號。
- PWM configuration
- TIM_Prescaler : 將TIMxCLK除以(TIM_Prescaler + 1)
- TIM_CounterMode : 選擇計數模式
- TIM_Period : TIMx_ARR,counter 週期
- TIM_OCPolarity: 設置輸出極性,例如:TIM_OCPolarity_High PWM輸出關閉時默認為低電位。
- TIM_OCMode: PWM模式
- PWM 1 Mode :
- 在向**上**計數,**TIMx_CNT<TIMx_CCRx**時,輸出為1,否則輸出為0;
- 在向**下**計數,**TIMx_CNT>TIMx_CCRx**時,輸出為0,否則輸出為1。
- PWM 2 Mode :
- 在向**上**計數,**TIMx_CNT<TIMx_CCRx**時,輸出為0,否則輸出為1;
- 在向**下**計數,**TIMx_CNT>TIMx_CCRx**時,輸出為1,否則輸出為0。
- TIM_Pulse: 即TIMx_CCRx暫存器,設定脈衝寬度
- **輸出脈波週期 = (TIM_Period+1) * (TIM_Prescaler+1) * (TIM_ClockDivision+1) / TIMxCLK **
- **輸出脈波週期 = (TIM_Period + 1) * (TIM_Prescaler + 1) / TIMxCLK **
- Example: PWM mode 1, 向上計數並以LED為例:
.. image:: /timer.png
- TIM_Prescaler = 500-1, TIM_Period = 1680-1 , TIMxCLK = 84MHz
- 輸出脈波週期 = (TIM_Period+1) * (TIM_Prescaler+1) / TIMxCLK
- = (1680-1+1) * (500-1+1) /84000000 = 0.01 s(100Hz)
- 0.01 s 遠小於人眼視覺暫留的時間(0.1~0.4s),因此上圖的PWM 1 duty cycle(75%)>PWM 2 duty cycle(25%),PWM 1 Mode看起來會比PWM 2 Mode亮。
- Example: PWM mode 1, 中央對齊計數方式:
.. image:: /中央對齊.png
- PWM先高後低和先低後高 在步進馬達上表現有什麼差別?
在馬達剛要啟動時,因為先高後低是在duty cycle的前半段先供電,而先低後高則是到duty cycle的後半段才供電,在馬達的表現上 先高後低的啟動速度會比先低後高還要快一點
Code_section
=============
[#]_
.. [#] `Demo Codes main.c<https://github.com/PJayChen/STM32f4_discovery_TIM_PWM_Output/blob/master/TIM_PWM_Output/main.c>`_
RCC_Configuration
--------------------
.. code-block:: prettyprint linenums
RCC_AHB1PeriphClockCmd( RCC_AHB1Periph_GPIOD , ENABLE );//Enalbe AHB for GPIOD
RCC_APB1PeriphClockCmd( RCC_APB1Periph_TIM4, ENABLE );//Enable APB for TIM4
GPIO_Configuration
--------------------
.. code-block:: prettyprint linenums
GPIO_InitTypeDef GPIO_InitStructure;//Create GPIO_InitStructure
GPIO_StructInit(&GPIO_InitStructure); // Reset GPIO_structure
GPIO_PinAFConfig(GPIOD, GPIO_PinSource12, GPIO_AF_TIM4); // set GPIOD_Pin12 to AF_TIM4
GPIO_PinAFConfig(GPIOD, GPIO_PinSource13, GPIO_AF_TIM4); // set GPIOD_Pin13 to AF_TIM4
GPIO_PinAFConfig(GPIOD, GPIO_PinSource14, GPIO_AF_TIM4); // set GPIOD_Pin14 to AF_TIM4
GPIO_PinAFConfig(GPIOD, GPIO_PinSource15, GPIO_AF_TIM4); // set GPIOD_Pin15 to AF_TIM4
// Setup Blue & Green LED on STM32-Discovery Board to use PWM.
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13| GPIO_Pin_14| GPIO_Pin_15; //PD12->LED3 PD13->LED4 PD14->LED5 PD15->LED6
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; // Alt Function - Push Pull
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init( GPIOD, &GPIO_InitStructure );
TIM_Configuration
--------------------
.. code-block:: prettyprint linenums
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;//Create TIM Time Base Init structure
TIM_OCInitTypeDef TIM_OCInitStruct;//Create TIM Output Compare Init structure
// Let PWM frequency equal 100Hz. ( 84MHz / 1680 /500 = 100Hz )
// Let period equal 1600. Therefore, timer runs from zero to 1600.
TIM_TimeBaseStructInit( &TIM_TimeBaseInitStruct );//reset TIM_TimeBaseStructInit
TIM_TimeBaseInitStruct.TIM_Period = 1680 - 1;
TIM_TimeBaseInitStruct.TIM_Prescaler = 500 - 1;
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit( TIM4, &TIM_TimeBaseInitStruct );
TIM_OCStructInit( &TIM_OCInitStruct );reset TIM_OCStructInit
TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;
// TIM4_CCR register (16 bits). Value can range from zero to 65535.
// TIM_Pulse = Compare Capture register(CCR) = 1680 ( duty_cycle = 100%)
TIM_OCInitStruct.TIM_Pulse = 1680; //(0=Always Off, >1680 =Always On)
TIM_OC1Init( TIM4, &TIM_OCInitStruct ); // set TIM_OCInitStruce to TIM4_channel1
TIM_OC2Init( TIM4, &TIM_OCInitStruct ); // set TIM_OCInitStruce to TIM4_channel2
TIM_OC3Init( TIM4, &TIM_OCInitStruct ); // set TIM_OCInitStruce to TIM4_channel3
TIM_OC4Init( TIM4, &TIM_OCInitStruct ); // set TIM_OCInitStruce to TIM4_channel4
TIM_Cmd( TIM4, ENABLE ); //Enables TIM4 peripheral
-----------------------------------------------------------
PWM control
--------------------
.. code-block:: prettyprint linenums
while(1) // Do not exit
{
if(brightness + n <= 0)
who_run = (who_run + 1) % 4;
if (((brightness + n) >= 3000) || ((brightness + n) <= 0))
n = -n; // if brightness maximum/maximum change direction
brightness += n;
//Light LEDs in turn
switch(who_run){
case 0:
TIM4->CCR1 = brightness - 1; // set brightness
break;
case 1:
TIM4->CCR2 = brightness - 1; // set brightness
break;
case 2:
TIM4->CCR3 = brightness - 1; // set brightness
break;
case 3:
TIM4->CCR4 = brightness - 1; // set brightness
break;
}
for(i=0;i<10000;i++); // delay
}
return(0); // System will implode
}
-----------------------------------------------------------
Demo video
==========
1. `輪流點亮LED <http://youtu.be/ygELIXJFHM8>`_
2. `透過PWM使MOTOR達到快慢快慢變化 <http://youtu.be/bFRWfXI_k5Q>`_
補充
=============
範例程式
=============
1. `輪流點亮LED <https://github.com/PJayChen/STM32f4_discovery_TIM_PWM_Output>`_
2. `藉由button改變duty cycle <https://gitcafe.com/embedded2012/P-coolod/blob/master/Lab-6/discoveryF4/GPIO_test/main.c>`_
此程式碼利用PB4 5作Output pin持續輸出高態電壓,PA2 3作Input Pin接收來在output pin的電壓輸入,並設定負緣觸發中斷,中斷觸發以改變duty cycle佔空比,電壓高低態變化則利用外接button,當按下button則電壓 高->低 ,PA2 3接收到變化便會觸發中斷,透過改變Timer來做duty cycle佔空比的改變,以達到LED亮暗改變.(以下圖說明)
.. image:: /embedded/PWM-GPIO_test_Explain_figure.png
Reference
=============
Website
--------
- `Shadow register <http://blog.csdn.net/scarlettsp/article/details/6656588>`_
- `Wikipedia-PWM<http://en.wikipedia.org/wiki/Pulse-width_modulation>`_
- `TIM_ClockDivision功用說明<http://blog.sina.com.cn/s/blog_76c545390100ovfj.html>`_
- `PWM實現ADC與DAC<http://www.xjtudll.cn/Exp/143/>`_
Books
------
- Keil MDK ARM 原理與實作,MDK研究團隊編著,台科大圖書。
- 陳志旺(2012),STM32 嵌入式微控制器快速上手,中國:電子工業出版社。 P.155 8.3.3 計數器模式