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

版本 b6db1c6d251907ae90f7a0e0daae4df57b82d936

embedded/PWM

Changes from b6db1c6d251907ae90f7a0e0daae4df57b82d936 to 8dce9a5d899517b235438793d4faeba2d6d67add

---
title: Pulse-width modulation(PWM)
...

Introduction
============


又稱pulse-duration modulation(PDM),是將類比信號轉為脈波的一種技術。

為何需要PWM?雖然類比電壓可直接用來控制,但類比電路控制信號容易隨時間漂移,功耗大。

.. image:: /pwm.png

如圖所示,PWM 電路主要功能是將輸入電壓的振幅轉換成脈衝寬度。一般轉換後脈波的週期固定,脈波的占空比(duty cycle)會依類比信號的大小而改變,可用來控制燈泡亮度、馬達轉速等等,脈波寬度越大燈泡亮度越亮、馬達轉速越快。


Implementation
==============
PWM需要透過Timer來實現,STM32的Timer可分為以下幾種

- 基本定時器(TIM6 和 TIM7)
- 高級控制定時器(TIM1 和 TIM8)
- 通用定時器(TIMx):具有測量輸入信號的脈衝長度、產生輸出波形和PWM的功能。
  - clock來源 : 內部clock(CK_INT)、外部clock模式1(TIx)、外部clock模式2(ETR)、內部觸發輸入(ITRx)。
  - CK_INT -> AHB Prescaler(/1、2、...、512) -> APB1 Prescaler(/1、2、4、8、16) or APB2 Prescaler(/1、2、4、8、16)。
  - Time Base Configuration :

  .. image:: /up mode.bmp

-----------

  .. image:: /down mode.bmp

-----------

  .. image:: /center aligned.jpg


    - 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
=============

//====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
=============
http://youtu.be/RHzLguvuOKY

http://youtu.be/guG6MdLIwfI

http://youtu.be/jJfebbbMDPw

補充
=============
1. PWM較省電?

   因為一般類比電壓要降低電壓輸出需靠增加電阻,源頭輸出電壓"持續"都為同一電壓,不過利用電阻改變最後輸出電壓,而PWM他靠的是一段時間內輸出的頻率來模擬類比電壓,"不需要持續的輸出",故不會降電浪費在電阻上,即可達到省電效果。

2. overflow 和 UEV UIF的關係

<<<<<<< edited
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. 
=======
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. 
Reference
>>>>>>> 39ea17c62218ce018f2a8279e125b9314a21b9f0
    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. 向上計數和向下計數的差別

4. timer 和 RTC的差別

The system timer is what makes everything in the computer run at the same speed. 
    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先高後低和先低後高 在步進馬達上表現有什麼差別?
7. 改成中央對齊計數方式會怎麼樣?
8. Nyquist-Shannon Sampling Theorem
如果信號是帶限(某個頻率以上及以下~0)的,並且採樣頻率高於信號帶寬的一倍,那麼,原來的連續信號可以從採樣樣本中完全重建出來。


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