Kindle Fire Coupon Kindle Fire Coupon 2012 Kindle DX Coupon 2012 Kindle Fire 2 Coupon Amazon Coupon Codes 2012 Kindle DX Coupon PlayStation Vita Coupon kindle touch coupon amazon coupon code kindle touch discount coupon kindle touch coupon 2012 logitech g27 coupon 2012 amazon discount codes
Facebook RSS Reset

Hướng dẫn lập trình PWM với STM32

Chú ý : Nội dung bài viết là những những gì mình hiểu và cảm thấy dễ nhớ nhất, đương nhiên sẽ không tránh khỏi sai sót, nhầm lẫn. Do đó mong mọi người góp ý thêm. Xin Cảm ơn !  (Like or share on facebook if you feel usefull 🙂 )

I. Giới thiệu cơ bản về PWM 

1. Khái nim : B điu chế xung, hay còn gi là bbăm xung” là b x lý và điu khin to ra dng xung vuông chu k thay đi theo cu hình. Đây là phương pháp điu chnh đin áp ra ti da vào trung bình tín hiu điu chế. Khi đ rng xung tăng, trung bình đin áp ra tăng. Các module PWM thưng s dng tn s điu chế không đi, và điu chnh da trên s thay đi ca chu k nhim v (duty cycle).

pwm

 

   Như trên hình đã rất rõ : Duty cycle = pulse width x 100/period

– Duty cycle là tỷ lệ phần trăm mức cao.

– Period là chu kỳ xung.

– Pulse width là giá trị mức cao so với period.

       Dựa trên nguyên lý bên trên mà trong STM32 hay các loại vi điều khiển khác điều hỗ trợ bộ tạo độ rộng xung tự động mà bạn không phải tốn thời gian bật tắt chân IO, có thể thiết lập chu kỳ, tần số, độ rộng xung và một số chức năng đặc biệt. PWM thuộc khối timer

2. ng dng : PWM đưc ng dng nhiu trong điu khin như điu khin đng cơ và các b xung áp, điu áp… S dng PWM điu khin đ nhanh chm ca đng cơ và sn đnh tc đ đng cơ. Ngoài lĩnh vc điu khin hay n đnh ti thì PWM còn tham gia và điu chế các mch ngun như : boot, buck, nghch lưu 1 pha và 3 pha… PWM chuyên dùng đ điu khin các phn t đin t công sut có đưng đc tính là tuyến tính khi có sn 1 ngun 1 chiu c đnh. 

II. Phương pháp lập trình PWM trên STM32 :

       Như hình vẽ bên trên và theo công thức Duty cycle = pulse width x 100/period. Ta thấy pwm có 2 thành phần chính để tạo ra duty cycle của pwm là : độ rộng của 1 chu kỳ xung (period) và giá trị của xung cao là bao nhiêu so với period (pulse width). Do đó để cấu hình sử dụng pwm ta có thể chia làm 2 phần như sau :

  • Cấu hình timer cho pwm: sẽ quyết định độ rộng của 1 chu kỳ xung pwm là bao nhiêu (period)
  • Cấu hình pwm : sẽ quyết định phần trăm của xung mức cao là bao nhiêu phần trăm (pulse width)

Ta sẽ bắt đầu tìm hiểu phần cấu hình timer để sử dụng pwm.

1. Cấu hình Timer :

1.1. Một số công thức cần nhớ :

Hai công thức từ phần hướng dẫn timer :

  •  Timer_tick_frequency = Timer_default_frequency / (prescaller_set + 1) 
  • TIM_Prescaler = (Timer_default_frequency/Timer_tick_frequency)-1

Bổ sung thêm công thức cho phần PWM :

  • PWM_frequency = timer_tick_frequency / (TIM_Period + 1) = (Timer_default_freq)/(Prescaler+1)/(TIM_Period+1)
  • TIM_Period = (timer_tick_frequency / PWM_frequency) – 1

* Chú ý :  Khi set Timer Period bạn phải biết giá trị lớn nhất có thể của nó là bao nhiêu, ví dụ trong trường hợp timer 16 bit thì giá trị max là 65535 . Do đó nếu giá trị period lớn hơn giá trị max thì bạn có thể tăng Prescaler để giảm timer tick frequency khi đó Timer period sẽ giảm xuống.

1.2. Ví dụ cụ thể : 

* Yêu cầu : Tạo ra pwm có độ rộng 1 chu kỳ xung là 100us như hình vẽ , sử dụng timer 4 của stm32f407.

pwm2

 

* Phân tích :  3 bước cơ bản

– Tìm pwm_frequence : Độ rộng 1 chu kỳ pwm là 100uS  nghĩa là 1 chu kỳ xung sẽ mất 100uS hay tần số của pwm là : 10kHz ( 10000xung/1giây)

– Tìm TIM_Prescaler : Như vậy tiếp theo ta cần tìm thông số Prescaler và TIM_Period để cấu hình cho timer, dựa vào công thức :

         Timer_tick_frequence = Timer_default_frequence/(Prescaler+1)

Giả sử ta chọn Prescaler = 0 để cho tần số cho timer là cao nhất Timer_tick_frequence = 84000000/(0+1) = 84Mhz

– Tìm TIM_Period : Với pwm_frequence = 10kHz và Timer_tick_freq = 84Mhz ta tìm tiếp TIM_Period dựa vào công thức :

TIM_Period = (Timer_tick_frequence/PWM_frequence )-1 = (84000000/10000)-1 = 8399

* Timer_default_frequence của timer 4 đạt được tối đa là 84Mhz, xem bảng sau :

pwm3

* Code Init Timer 4 :

void TM_TIMER_Init(void) {
    TIM_TimeBaseInitTypeDef TIM_BaseStruct;
    
    /* Enable clock cho TIM4 */
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
/*     
   TIM4 is connected to APB1 bus, which has on F407 device 42MHz clock                 
    But, timer has internal PLL, which double this frequency for timer, up to 84MHz     
    Remember: Not each timer is connected to APB1, there are also timers connected     
    on APB2, which works at 84MHz by default, and internal PLL increase                 
    this to up to 168MHz                                                             
    
    Set timer prescaller 
    Timer count frequency is set with 
    
    timer_tick_frequency = Timer_default_frequency / (prescaller_set + 1)        
    
    In our case, we want a max frequency for timer, so we set prescaller to 0         
    And our timer will have tick frequency        
    
    timer_tick_frequency = 84000000 / (0 + 1) = 84000000 
*/    
    TIM_BaseStruct.TIM_Prescaler = 0;
    /* Count up */
    TIM_BaseStruct.TIM_CounterMode = TIM_CounterMode_Up;
/*
    Set timer period when it have reset
    First you have to know max value for timer
    In our case it is 16bit = 65535
    To get your frequency for PWM, equation is simple
    
    PWM_frequency = timer_tick_frequency / (TIM_Period + 1)
    
    If you know your PWM frequency you want to have timer period set correct
    
    TIM_Period = timer_tick_frequency / PWM_frequency - 1
    
    In our case, for 10Khz PWM_frequency, set Period to
    
    TIM_Period = 84000000 / 10000 - 1 = 8399
    
    If you get TIM_Period larger than max timer value (in our case 65535),
    you have to choose larger prescaler and slow down timer tick frequency
*/
    TIM_BaseStruct.TIM_Period = 8399; /* 10kHz PWM */
    TIM_BaseStruct.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_BaseStruct.TIM_RepetitionCounter = 0;
    /* Initialize TIM4 */
    TIM_TimeBaseInit(TIM4, &TIM_BaseStruct);
    /* Start count on TIM4 */
    TIM_Cmd(TIM4, ENABLE);
}

2. Cấu hình PWM 

2.1. Định nghĩa struct cấu hình cho pwm :

Tương tự mỗi khi qua phần mới ta sẽ tìm hiểu cấu trúc định nghĩa của pwm gồm những thành phần gì? Bắt đầu tìm hiểu cấu trúc định nghĩa Timer output compare (OC) :

pwm4

– TIM_OCMode : Trong timer có nhiều mode bạn cần chọn mode cho phù hợp. Xem thêm mục 2.5 tài liệu application notes về timer. Với pwm mode thì ta có 2 mode phổ biến để lựa chọn là : PWM mode 1 (clear on compare match) và PWM mode 2 (set on compare match).

– TIM_OutputState : Cho phép hoặc không cho phép chân pwm hoạt động.

– TIM_Pulse :    Đây chính là thông số quyết định duty cycle (tỉ lệ phần trăm xung mức cao so với period), được xác định thông qua công thức dưới đây :

  •  pulse_length = ((TIM_Period + 1) * DutyCycle) / 100 – 1

Ví dụ : Tiếp tục với ví dụ phần cấu hình timer bên trên (TIM_Period = 8399), ta tiếp tục xác định phần trăm duty cycle theo các mức như sau :

 + 25% duty cycle:     pulse_length = ((8399 + 1) * 25) / 100 – 1 = 2099

 + 50% duty cycle:     pulse_length = ((8399 + 1) * 50) / 100 – 1 = 4199

 + 75% duty cycle:     pulse_length = ((8399 + 1) * 75) / 100 – 1 = 6299

 + 100% duty cycle:    pulse_length = ((8399 + 1) * 100) / 100 – 1 = 8399

– TIM_OCPolarity :   Nếu polarity bit là 1 thì output sẽ ra mức cao tại lúc bắt đầu chu kỳ và sau đó xuống mức thấp tại thời điểm kết thúc chu kỳ. Minh họa như hình bên dưới với polarity là low :

pwm5

 

Đó là các thông số chính trong struct mà bạn cần quan tâm, các thông số còn lại có thể tự tham khảo thêm.

2.2. Ví dụ cụ thể cho pwm :

Đây là code phần ví dụ cho phần cấu hình pwm cũng như timer và toàn bộ nội dung bài viết :

#include "defines.h"
#include "stm32f4xx.h"
#include "stm32f4xx_rcc.h"
#include "stm32f4xx_gpio.h"
#include "stm32f4xx_tim.h"
 
void TM_LEDS_Init(void) {
    GPIO_InitTypeDef GPIO_InitStruct;
    
    /* Clock for GPIOD */
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
 
    /* Alternating functions for pins */
    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);
    
    /* Set pins */
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
    GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_Init(GPIOD, &GPIO_InitStruct);
}
 
void TM_TIMER_Init(void) {
    TIM_TimeBaseInitTypeDef TIM_BaseStruct;
    
    /* Enable clock for TIM4 */
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
/*    
    TIM4 is connected to APB1 bus, which has on F407 device 42MHz clock                 
    But, timer has internal PLL, which double this frequency for timer, up to 84MHz     
    Remember: Not each timer is connected to APB1, there are also timers connected     
    on APB2, which works at 84MHz by default, and internal PLL increase                 
    this to up to 168MHz                                                             
    
    Set timer prescaller 
    Timer count frequency is set with 
    
    timer_tick_frequency = Timer_default_frequency / (prescaller_set + 1)        
    
    In our case, we want a max frequency for timer, so we set prescaller to 0         
    And our timer will have tick frequency        
    
    timer_tick_frequency = 84000000 / (0 + 1) = 84000000 
*/    
    TIM_BaseStruct.TIM_Prescaler = 0;
    /* Count up */
    TIM_BaseStruct.TIM_CounterMode = TIM_CounterMode_Up;
/*
    Set timer period when it have reset
    First you have to know max value for timer
    In our case it is 16bit = 65535
    To get your frequency for PWM, equation is simple
    
    PWM_frequency = timer_tick_frequency / (TIM_Period + 1)
    
    If you know your PWM frequency you want to have timer period set correct
    
    TIM_Period = timer_tick_frequency / PWM_frequency - 1
    
    In our case, for 10Khz PWM_frequency, set Period to
    
    TIM_Period = 84000000 / 10000 - 1 = 8399
    
    If you get TIM_Period larger than max timer value (in our case 65535),
    you have to choose larger prescaler and slow down timer tick frequency
*/
    TIM_BaseStruct.TIM_Period = 8399; /* 10kHz PWM */
    TIM_BaseStruct.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_BaseStruct.TIM_RepetitionCounter = 0;
    /* Initialize TIM4 */
    TIM_TimeBaseInit(TIM4, &TIM_BaseStruct);
    /* Start count on TIM4 */
    TIM_Cmd(TIM4, ENABLE);
}
 
void TM_PWM_Init(void) {
    TIM_OCInitTypeDef TIM_OCStruct;
    
    /* Common settings */
    
    /* PWM mode 2 = Clear on compare match */
    /* PWM mode 1 = Set on compare match */
    TIM_OCStruct.TIM_OCMode = TIM_OCMode_PWM2;
    TIM_OCStruct.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCStruct.TIM_OCPolarity = TIM_OCPolarity_Low;
    
/*
    To get proper duty cycle, you have simple equation
    
    pulse_length = ((TIM_Period + 1) * DutyCycle) / 100 - 1
    
    where DutyCycle is in percent, between 0 and 100%
    
    25% duty cycle:     pulse_length = ((8399 + 1) * 25) / 100 - 1 = 2099
    50% duty cycle:     pulse_length = ((8399 + 1) * 50) / 100 - 1 = 4199
    75% duty cycle:     pulse_length = ((8399 + 1) * 75) / 100 - 1 = 6299
    100% duty cycle:    pulse_length = ((8399 + 1) * 100) / 100 - 1 = 8399
    
    Remember: if pulse_length is larger than TIM_Period, you will have output HIGH all the time
*/
    TIM_OCStruct.TIM_Pulse = 2099; /* 25% duty cycle */
    TIM_OC1Init(TIM4, &TIM_OCStruct);
    TIM_OC1PreloadConfig(TIM4, TIM_OCPreload_Enable);
    
    TIM_OCStruct.TIM_Pulse = 4199; /* 50% duty cycle */
    TIM_OC2Init(TIM4, &TIM_OCStruct);
    TIM_OC2PreloadConfig(TIM4, TIM_OCPreload_Enable);
    
    TIM_OCStruct.TIM_Pulse = 6299; /* 75% duty cycle */
    TIM_OC3Init(TIM4, &TIM_OCStruct);
    TIM_OC3PreloadConfig(TIM4, TIM_OCPreload_Enable);
    
    TIM_OCStruct.TIM_Pulse = 8399; /* 100% duty cycle */
    TIM_OC4Init(TIM4, &TIM_OCStruct);
    TIM_OC4PreloadConfig(TIM4, TIM_OCPreload_Enable);
}
 
int main(void) {
    /* Initialize system */
    SystemInit();
    /* Init leds */
    TM_LEDS_Init();
    /* Init timer */
    TM_TIMER_Init();
    /* Init PWM */
    TM_PWM_Init();
    
    while (1) {}
 
    }

Leave a comment

No comments yet.

Leave a Comment