--- title: Pulse-Width Modulation (PWM) toc: yes ... Introduction ============ - Pulse-Width Modulation, 又稱pulse-duration modulation(PDM),其利用在頻率不變的狀態下, 改變工作週期大小, 使整體平均電壓值上升或下降, 藉此間歇性電壓及功率切換以節省能源及控制等效果. .. image:: /PWM_intr.PNG - 因PWM需要透過Timer來實現, 後面會稍作簡介 Clock control ============== - 在學習Timer前, 先瞭解系統clock是如何產生的, 以及給Timer的clock值. - A part of clock tree (STM32F407xx Reference mamual - p. 576) .. image:: /Timer_clock.png - HSE clock (High speed external clock signal) 我們使用Crystal resonator作為外部震盪, 其頻率為8Hz(由STM32f4 discovery user manual 可知) - 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 = 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 ======= Basic timers 基本定時器(TIM6 和 TIM7) ---------------------------------------- - 具有 16-bit auto-reload upcounter driven by a programmable prescaler - 用途: Synchronization circuit to trigger the DAC(內部連接至DAC) Advanced-control timers 高級控制定時器(TIM1 和 TIM8) ----------------------------------------------------- - 具有 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) ------------------------------------------------------ - 具有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) (STM32F407xx Reference mamual - 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來源 : 內部clock(CK_INT)、外部clock模式1(TIx)、外部clock模式2(ETR)、內部觸發輸入(ITRx)。 - Counter modes (STM32F407xx Reference mamual - p. 579) Counting Mode ============= up counting mode ---------------- .. code-block:: prettyprint linenums TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up; .. image:: /Timer_upcounting.png down counting 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 Timer Mode ========== Input capture mode ------------------ - 目的 : 用來計算某外部信號特定狀態發生的時間點 - 用途 : 脈波寬測量, 頻率量測 - 可以設定外部觸發信號的型式,並使用外部觸發來觸動一個Timer的栓鎖動作,這時候Timer計時值則會存入TIMx_CCRx暫存器。 PWM input mode -------------- - 為Input capture mode的一個特例,此模式能夠量測到TI1上的PWM信號長度(TIMx_CCR1暫存器)和工作週期(TIMx_CCR2暫存器),所以會使用到兩個通道。 Forced output mode ------------------- - 在輸出模式下,輸出比較信號能夠直接由軟體強制設為有效(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 - CCxP = 1 (TIM_OCPolarity = TIM_OCPolarity_High) -> OCx = ocxref Output compare mode ------------------- - 此項功能是用來控制一個輸出波形或是用來指示一段給定的時間已到。 **PWM mode** ------------ - PWM configuration - TIM_ClockDivision - 採樣頻率基準,當連續採樣到N個有效電平時,才當作一次有效電平。 - 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_CNTTIMx_CCRx時,輸出為0,否則輸出為1。 - PWM 2 Mode : 在向上計數,TIMx_CNTTIMx_CCRx時,輸出為1,否則輸出為0。 - TIM_Pulse - TIMx_CCRx,脈衝寬度 - **輸出脈波週期 = (TIM_Period+1) * (TIM_Prescaler+1) * (TIM_ClockDivision+1) / TIMxCLK ** - 以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亮。 Code_section ============= 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 ========== * http://youtu.be/RHzLguvuOKY * http://youtu.be/guG6MdLIwfI (LED 3 4 5 6依序 亮-暗 轉換) * http://youtu.be/ygELIXJFHM8 * http://youtu.be/jJfebbbMDPw (透過外接 BUTTON 調整 LED亮度) * http://youtu.be/bFRWfXI_k5Q (透過PWM使MOTOR達到快慢快慢變化) 補充 ============= 1. PWM較省電? 因為一般類比電壓要降低電壓輸出需靠增加電阻,源頭輸出電壓"持續"都為同一電壓,不過利用電阻改變最後輸出電壓,而PWM他靠的是一段時間內輸出的頻率來模擬類比電壓,"不需要持續的輸出",故不會降電浪費在電阻上,即可達到省電效果。 例如:使用9V電池來給一燈泡供電,連接電池跟燈泡時間為50ms,斷開電池和燈泡時間為50ms。1秒鐘(1000ms)過後,會重複此過程10次,燈泡將會連接到一個4.5V電池(9V電池的50%)上一樣。 2. overflow 和 UEV UIF的關係 UEV occurs everytime an overflow occurs, and UIF is a flag triggered when overflow occurs, if you don't clean the flag, it will remain triggered. 3. 向上計數和向下計數的差別 以四位元向上計數器為例,由四個JK正反器串接而成,輸出DCBA由0000依據二進位的變化至1111。下一個脈波來時,又回到0000。依此類推故此計數器可由0計數到15。而向下計數器與向上計數器不同點是下一級的CLK接於前一級的Q,輸出DCBA由1111向下計數到0000,待下一個時脈輸入時又變為1111,再依序變化。 4. timer 和 RTC的差別 The system timer is what makes everything in the computer run at the same speed. For example, your CPU won't look for a fresh batch of data while the Memory is still trying to feed it the last batch.The Real Time Clock tells the system what day/time it is. 5. TIM_OCMode (timer output compare mode) .. image:: /TIM_OCMode.png 6. PWM先高後低和先低後高 在步進馬達上表現有什麼差別? 在馬達剛要啟動時,因為先高後低是在duty cycle的前半段先供電,而先低後高則是到duty cycle的後半段才供電,在馬達的表現上 先高後低的啟動速度會比先低後高還要快一點 7. 改成中央對齊計數方式會怎麼樣? .. image:: /中央對齊.png 範例程式 ============= - https://github.com/PJayChen/STM32f4_discovery_TIM_PWM_Output 依序點亮LED - https://gitcafe.com/embedded2012/P-coolod/blob/master/Lab-6/discoveryF4/TIM_PWM_Output/main.c 此程式為依序更改 LED 3 4 5 6(利用PD12 13 14 15 作PWM應用)亮度 由暗漸漸轉亮 再由亮漸漸轉暗 - 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 ============= - http://blog.csdn.net/scarlettsp/article/details/6656588 - http://zh.wikipedia.org/zh-tw/%E8%84%88%E8%A1%9D%E5%AF%AC%E5%BA%A6%E8%AA%BF%E8%AE%8A - http://ppt.cc/nIAF - http://www.google.com.tw/url?sa=t&rct=j&q=pwm&source=web&cd=10&cad=rja&ved=0CFMQFjAJ&url=http%3A%2F%2Fwww.vr.ncue.edu.tw%2Fesa%2Fa1001%2FPWM.pdf&ei=S3OoUO6pOafcmAWWiIGQDw&usg=AFQjCNE_7pw6paGQzH7kSwkwD2witqcC-A - http://hi.baidu.com/snic_k/item/0f045e3288e6683d2e20c42b - 陳志旺(2012),STM32 嵌入式微控制器快速上手,中國:電子工業出版社。 - http://blog.sina.com.cn/s/blog_76c545390100ovfj.html P.155 8.3.3 計數器模式