分享到plurk 分享到twitter 分享到facebook

版本 ee9a093b9a4b52537dfcb854882b927f38d68d1f

Pulse-Width Modulation (PWM)

Introduction

  • Pulse-Width Modulation, 又稱pulse-duration modulation(PDM),

.. raw:: html 其 利用在頻率不變的狀態下, 改變工作週期大小 , 使整體平均電壓值上升或下降, 藉此間歇性電壓及功率切換以節省能源及控制等效果.

.. image:: /PWM_intr.PNG - 因PWM需要透過Timer來實現, 後面會稍作簡介

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)

.. code-block:: prettyprint

TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Down; TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_CenterAligned1;

.. image:: /Timer_upcounting.PNG
.. image:: /countdown.png .. image:: /center_aligned.png

- TIM_ClockDivision  - 採樣頻率基準,當連續採樣到N個有效電平時,才當作一次有效電平。
- TIM_Prescaler  -  將TIMxCLK除以(TIM_Prescaler+1)
- TIM_CounterMode  -  選擇計數模式
- TIM_Period  -  TIMx_ARR,counter 週期
  • 輸出脈波週期 = (TIM_Period+1) * (TIM_Prescaler+1) * (TIM_ClockDivision+1) / TIMxCLK
  • PWM configuration
    • 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_OCPolarity - 設置輸出極性,例如:TIM_OCPolarity_High PWM輸出關閉時默認為低電位。

以LED為例:

.. image:: /timer.png

TIM_Prescaler = 499,TIM_ClockDivision=0,TIMxCLK=84MHz

輸出脈波週期 = (TIM_Period+1) * (TIM_Prescaler+1) * (TIM_ClockDivision+1) / TIMxCLK

       = (1999+1) * (499+1) * (0+1)/84000000 ≒ 0.012 s

0.012 s 遠小於人眼視覺暫留的時間(0.1~0.4s),因此上圖的PWM 1 duty cycle(75%)>PWM 2 duty cycle(25%),PWM 1 Mode看起來會比PWM 2 Mode亮。


Code_section

.. code-block:: prettyprint linenums

//====set pin enable=====================================
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD  | RCC_AHB1Periph_GPIOB |
                       RCC_AHB1Periph_GPIOA, ENABLE ); //開啟Pin腳 D B E提供使用
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4 | RCC_APB1Periph_TIM3, ENABLE ); //開啟Timer

GPIO_PinAFConfig(GPIOD, GPIO_PinSource12, GPIO_AF_TIM4);
GPIO_PinAFConfig(GPIOD, GPIO_PinSource13, GPIO_AF_TIM4);
GPIO_PinAFConfig(GPIOD, GPIO_PinSource14, GPIO_AF_TIM4);
GPIO_PinAFConfig(GPIOD, GPIO_PinSource15, GPIO_AF_TIM4);

/* GPIOA Configuration: CH1 (PB4) and CH2 (PB5) */ //設定PB4 5為輸出模式 提供模擬中斷來源
GPIO_InitStructure2.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 ;
GPIO_InitStructure2.GPIO_Mode = GPIO_Mode_OUT;            //輸出信號模式
GPIO_InitStructure2.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure2.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure2.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOB, &GPIO_InitStructure2);

/* GPIOA Configuration: (PA2) and (PA3) */     //設定PA2 3為輸入模式 提供接收中斷來源
GPIO_InitStructure2.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3 ;
GPIO_InitStructure2.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStructure2.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure2.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure2.GPIO_PuPd = GPIO_PuPd_DOWN ;
GPIO_Init(GPIOA, &GPIO_InitStructure2); 

// Setup Blue & Green LED on STM32-Discovery Board to use PWM. / //PD與LED3456相連實作PWM
GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_12 | GPIO_Pin_13| GPIO_Pin_14| GPIO_Pin_15;
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 );

//======================================================

//====Interrupt set section==============================

//==清空中斷標誌==
EXTI_ClearITPendingBit(EXTI_Line2);
EXTI_ClearITPendingBit(EXTI_Line3);

//==選擇中斷PIN腳 A2 A3======
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource2);
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource3);

//==  
EXTI_InitStructure.EXTI_Line = EXTI_Line2  ; //選擇中斷線路2 
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //設置為中斷請求,非事件請求
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling //設置中斷觸發方式為 下降沿觸發
EXTI_InitStructure.EXTI_LineCmd = ENABLE;            //外部中斷enable
EXTI_Init(&EXTI_InitStructure);
EXTI_GenerateSWInterrupt(EXTI_Line2);

EXTI_InitStructure.EXTI_Line = EXTI_Line3 ; //選擇中斷線路3 
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //设設置為中斷請求,非事件請求
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //設置中斷觸發方式為 下降沿觸發
EXTI_InitStructure.EXTI_LineCmd = ENABLE;            //外部中斷enable
EXTI_Init(&EXTI_InitStructure);
EXTI_GenerateSWInterrupt(EXTI_Line3);

//===給予中斷分組與優先權的設定===
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);      //選擇中斷分組2
NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn  ;     //選擇中斷通道2
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //搶占式中斷優先級設置為0
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;        //響應式中斷優先級設置為0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;           //enable中断
NVIC_Init(&NVIC_InitStructure);

//========================================================

//===Timer set section========================================

PrescalerValue = (uint16_t) ((SystemCoreClock /4) / 100000) - 1; //算脈衝周期的時間

//==TIMER 4 設定
TIM_TimeBaseStructInit( &TIM_TimeBaseInitStruct );
TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1; 
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up; //上數模式
TIM_TimeBaseInitStruct.TIM_Period = 2000 - 1;   // 0..2000   //周期
TIM_TimeBaseInitStruct.TIM_Prescaler = PrescalerValue;       
TIM_TimeBaseInit( TIM4, &TIM_TimeBaseInitStruct );

//==TIMER 4 CCRx 暫存器設定
TIM_OCStructInit( &TIM_OCInitStruct );
TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;
// Initial duty cycle equals 0%. Value can range from zero to 1000.
TIM_OCInitStruct.TIM_Pulse = 1000-1; // 0 .. 1000 (0=Always Off, 1000=Always On)

//==TIMER channel init
TIM_OC1Init( TIM4, &TIM_OCInitStruct ); // Channel 1  LED
TIM_OC2Init( TIM4, &TIM_OCInitStruct ); // Channel 2  LED
TIM_OC3Init( TIM4, &TIM_OCInitStruct ); // Channel 3  LED
TIM_OC4Init( TIM4, &TIM_OCInitStruct ); // Channel 4  LED
TIM_Cmd( TIM4, ENABLE );

TIM4->CCR1 = 500-1;
TIM4->CCR2 = 500-1;
TIM4->CCR3 = 500-1;
TIM4->CCR4 = 500-1;

//================================================================

//====中斷subroutine===============================================

void EXTI2_IRQHandler(void) //加長脈波頻寬的中斷subroutine
{
    if(EXTI_GetITStatus(EXTI_Line2) != 0) {
        EXTI_ClearITPendingBit(EXTI_Line2);   

    if (brightness <= 1999 - 100)
        brightness +=100;

    TIM4->CCR1 = brightness;
    TIM4->CCR2 = brightness;
    TIM4->CCR3 = brightness;
    TIM4->CCR4 = brightness;

    EXTI_ClearITPendingBit(EXTI_Line2);

    EXTI_ClearITPendingBit(EXTI_Line3);
    }
}

void EXTI3_IRQHandler(void)  //減少脈波頻寬的中斷subroutine
{
    if(EXTI_GetITStatus(EXTI_Line3) != 0) {
        EXTI_ClearITPendingBit(EXTI_Line3);
        if (brightness >= 199)
            brightness -=100;

        TIM4->CCR1 = brightness;
        TIM4->CCR2 = brightness;
        TIM4->CCR3 = brightness;
        TIM4->CCR4 = brightness;

        EXTI_ClearITPendingBit(EXTI_Line2);
        EXTI_ClearITPendingBit(EXTI_Line3);
    }
}

//============================================================

Demo video

  • http://youtu.be/RHzLguvuOKY
  • http://youtu.be/guG6MdLIwfI (LED 3 4 5 6依序 亮-暗 轉換)
  • 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

  1. PWM先高後低和先低後高 在步進馬達上表現有什麼差別?

    在馬達剛要啟動時,因為先高後低是在duty cycle的前半段先供電,而先低後高則是到duty cycle的後半段才供電,在馬達的表現上 先高後低的啟動速度會比先低後高還要快一點

  2. 改成中央對齊計數方式會怎麼樣?

.. image:: /中央對齊.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 嵌入式微控制器快速上手,中國:電子工業出版社。

    P.155 8.3.3 計數器模式

範例程式

  • 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