0% found this document useful (0 votes)
12 views8 pages

PWM Based Output Compare Mode

Uploaded by

odjaaleb
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
12 views8 pages

PWM Based Output Compare Mode

Uploaded by

odjaaleb
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 8

1

STM32F107 Output Compare PWM Mode with applications

Timer Registers:
Before we start coding, I must point out that perhaps the most complex sets of registers in
a STM32 micro belong to timer-counter modules. We don’t need all of them for a specific
application and so to avoid complexity we will only discuss about those registers that we
will be needing for a particular task. A few registers will always be needed but other will
needed for specific tasks like input capture/ output compare. Another important thing to
note on these registers is the fact that though all registers in an ARM micro are 32-bit
wide, for timer modules and most hardware peripherals most of the bits are reserved. This
makes handling them easy. Just for reference check out the register map of any timer
module. I have shown one below.
2

Design Considerations:
 A few things should be remembered before coding and designing hardware. These
are as follows:
 Timer modules to be used should be enabled first by setting
appropriate RCC_APBx_ENR register bits. Individual counters, DMA and interrupts
are enabled after setting up everything else.
3

 Right after power on reset every bit of internal registers of a STM32 micro are in
their default reset values. For timer registers the default is all zeroes, meaning
everything disabled. Thus, the ones we don’t need to use, need not to be forcefully
cleared. Usually this is the case for most internal hardware peripherals. However,
if you need to change stuffs on the fly, you better reset all settings just to avoid
running down to the jaws of a software bug or unprecedented results.
 Timers can’t be used like RTCs for a number of factors and so it is not always wise
to use them as precision time keepers. If accuracy is not very important, timers
can be used for general time-keeping purposes. No doubt timers are accurate than
wasteful delay functions but they are not good as dedicated RTCs.
 When using GPIOs for capture/compare mode, make sure which I/O pins are being
used because sometimes the I/O pins related to these functionalities can be
remapped elsewhere. Use STM32CubeMX to find out which pins can be used with a
given timer.
 AFIO block must be enabled and used for remapping I/O pins since capture/compare
are alternative functions of ordinary GPIOs. AFIO block should be enabled even if
remapping is not used. GPIOs should be configured as AFIOs for compare/output
modes.
 When using input capture mode, be sure of the maximum input voltage level of an
incoming waveform. Not all but most I/O pins are 5V tolerant. I will however
strongly suggest not to cross the VDD limit, typically 3.3V. Similarly, when using
compare mode, be sure that the end device like a LED or an opto-coupler
connected to a compare pin doesn’t draw too much current. Simply do not exceed
the specified GPIO electrical specs. This is very important.
 The internal oscillator of STM32 micros are quite accurate (1% tolerant) but if more
precision is needed, it is better to use well calibrated external clock generators or
precise external crystals.
 Be aware of clock system prescalers, multipliers and clock sources.
 Remember APB2 bus peripherals can run at maximum system clock speed while
APB1 bus peripherals are limited to half of that speed. Thus, APB1 timers may not
run at the same clock speed as the APB2 timers.
 Since internal clock speed of each timer is dependent on its APB bus speed, it is a
must know to which APB bus a given timer belongs. On MikroE’s Timer Calculator
utility software this is actually the MCU clock speed. Use STM32CubeMX and
MikroC compiler’s project editor to determine APB bus speeds. I have shown an
example below. Here APB1 timers are being fed with 36MHz while APB2 timers are
being fed by 72MHz.

Output Compare Mode (PWM) Overview


Sometimes a programmer needs to get out of the luxury of prebuilt compiler libraries
and apply his/her skills. I pointed out some limitations of MikroC compiler’s PWM library
and so we need to go around to avoid these limitations. By this way we will have a good
understanding of the output compare mode (A.K.A PWM mode) and also optimize coding
as per need.

Firstly, check out the code. You’ll notice that GPIO pins are setup as AFIO push-pull output
pins and that’s because PWM mode is the alternative functionality of an I/O pin. Note that
unlike timer 8 in which it is not possible, timer 1 channels can be remapped. However, I
didn’t apply remapping for the sake of simplicity.

In this example, both advance timers of STM32F107VC micro are used. The PWM outputs
have a common frequency and it is 45 kHz. The PWM frequency can be calculated just like
what we saw in time-base generation examples .
4

The maximum duty cycle count will be equal to the value that will be set in the timer auto-
reload register, TIMx_ARR. In my example, I set PSC zero and ARR 1599 and so now you
can see why the PWM frequency is 45 kHz. TIM1_ARR and TIM8_ARR registers are set
1599. Thus 100% duty cycle equals 1599 count, 50% duty cycle equals 800 counts and so
forth. As said before, PWM is achieved by comparing counts in
the TIMx_CCRn and TIMx_CNT registers. All channels of a timer will have the same PWM
frequency but their duty cycles may be different.

For PWM modes interrupts are not necessary as PWM generation is an independent
process. To control PWM duty cycle, we only need to load values to TIMx_CCRn, anything
between 0 to TIMx_ARR value.
The following hardware setup is used:

Introductory example:

In this example, we will adopt the aspect of register-oriented structured


programming in order to make programming modular and comprehensive.
Furthermore, we will use two general Timers such as Timer3 and Timer4 and
we will try to exploit their resources in terms of the channels they have to
see the possibility of controlling several drivers, especially in power
electronics.

void Setup_Config();
void setup_IO();
void setup_TIM3();
void setup_TIM4();
unsigned int Period = 2000;

void main() {

unsigned int duty = 0;


Setup_Config();

while(1) {
while(duty < Period) {
TIM3_CCR1 = duty;
TIM3_CCR2 = duty;
TIM3_CCR3 = duty;
TIM3_CCR4 = duty;
TIM4_CCR1 = duty;
TIM4_CCR2 = duty;
TIM4_CCR3 = duty;
5

TIM4_CCR4 = duty;
duty++;
delay_ms(4);
}
while(duty > 0) {
TIM3_CCR1 = duty;
TIM3_CCR2 = duty;
TIM3_CCR3 = duty;
TIM3_CCR4 = duty;
TIM4_CCR1 = duty;
TIM4_CCR2 = duty;
TIM4_CCR3 = duty;
TIM4_CCR4 = duty;
duty--;
delay_ms(4);
}

};
}

void Setup_Config(){
setup_IO();
setup_TIM3();
setup_TIM4();
}

void setup_IO()
{
RCC_APB2ENRbits.AFIOEN = 0x01; // AFIO_enable;
RCC_APB2ENRbits.IOPCEN = 0x01; // enable_GPIOE(true);
RCC_APB2ENRbits.IOPDEN = 0x01; // enable_GPIOD(true);
AFIO_MAPR |= 0x00;
AFIO_MAPRbits.TIM3_REMAP=0x0; //AFIO_remap(TIM3_not_remapped);
AFIO_MAPRbits.TIM4_REMAP=0x1;
// Alternate Function Output Push-Pull
GPIOC_CRLbits.MODE6=0x3; GPIOC_CRLbits.CNF6=0x2;
GPIOC_CRLbits.MODE7=0x3; GPIOC_CRLbits.CNF7=0x2;
GPIOC_CRHbits.MODE8=0x3; GPIOC_CRHbits.CNF8=0x2;
GPIOC_CRHbits.MODE9=0x3; GPIOC_CRHbits.CNF9=0x2;

GPIOD_CRHbits.MODE12=0x3; GPIOD_CRHbits.CNF12=0x2;
GPIOD_CRHbits.MODE13=0x3; GPIOD_CRHbits.CNF13=0x2;
GPIOD_CRHbits.MODE14=0x3; GPIOD_CRHbits.CNF14=0x2;
GPIOD_CRHbits.MODE15=0x3; GPIOD_CRHbits.CNF15=0x2;
}

void setup_TIM3()
{
RCC_APB1ENRbits.TIM3EN = 0x1; //enable_TIM3 APB1
TIM3_CR1bits.CEN = 0x0;

TIM3_CR1bits.DIR_ = 0x0; //set_TIM3_counting_direction(up_counting);


TIM3_ARR = Period;
6

TIM3_PSC = 18;

TIM3_CCMR1_Output &= (~(0x7 << 4)); TIM3_CCMR1_Output |= (0x6 << 4);


//set_TIM3_OC1_compare_mode(PWM_mode_1);
TIM3_CCMR1_Output &= (~(0x7 << 12)); TIM3_CCMR1_Output |= (0x6 << 12);
//set_TIM3_OC2_compare_mode(PWM_mode_1);
TIM3_CCMR2_Output &= (~(0x7 << 4)); TIM3_CCMR2_Output |= (0x7 << 4);
//set_TIM3_OC3_compare_mode(PWM_mode_2);
TIM3_CCMR2_Output &= (~(0x7 << 12)); TIM3_CCMR2_Output |= (0x7 << 12);
//set_TIM3_OC4_compare_mode(PWM_mode_2);

TIM3_CCERbits.CC1E = 0x1; TIM3_CCERbits.CC1P = 0x0; //set_TIM3_CC1_state_and_polarity(enable, active_high);


TIM3_CCERbits.CC2E = 0x1; TIM3_CCERbits.CC2P =0x0; //set_TIM3_CC2_state_and_polarity(enable, active_high);
TIM3_CCERbits.CC3E = 0x1; TIM3_CCERbits.CC3P = 0x0; //set_TIM3_CC3_state_and_polarity(enable, active_high);
TIM3_CCERbits.CC4E = 0x1; TIM3_CCERbits.CC4P = 0x0; //set_TIM3_CC4_state_and_polarity(enable, active_high);

TIM3_CR1bits.ARPE =0x1; // set_TIM3_auto_reload_preload_mode(true);

TIM3_CR1bits.CEN = 0x1; //enable_TIM3_counter(true);


}

void setup_TIM4()
{
RCC_APB1ENRbits.TIM4EN=0x1; // enable_TIM4(true);
TIM4_CR1bits.CEN = 0x0; // enable_TIM4_counter(false);
TIM4_CR1bits.DIR_ = 0x1; // set_TIM4_counting_direction(down_counting);
TIM4_ARR = Period;
TIM4_PSC = 18;
TIM4_CCMR1_Output &= (~(0x7 << 4)); TIM4_CCMR1_Output |= (0x6 << 4);
//set_TIM4_OC1_compare_mode(PWM_mode_1);
TIM4_CCMR1_Output &= (~(0x7 << 12)); TIM4_CCMR1_Output |= (0x6 << 12);
//set_TIM4_OC2_compare_mode(PWM_mode_1);
TIM4_CCMR2_Output &= (~(0x7 << 4)); TIM4_CCMR2_Output |= (0x7 << 4);
//set_TIM4_OC3_compare_mode(PWM_mode_2);
TIM4_CCMR2_Output &= (~(0x7 << 12)); TIM4_CCMR2_Output |= (0x7 << 12);
//set_TIM4_OC4_compare_mode(PWM_mode_2);
TIM4_CCERbits.CC1E = 0x1; TIM4_CCERbits.CC1P = 0x1; //set_TIM4_CC1_state_and_polarity(enable, active_low);
TIM4_CCERbits.CC2E = 0x1; TIM4_CCERbits.CC2P = 0x1; //set_TIM4_CC2_state_and_polarity(enable, active_low);
TIM4_CCERbits.CC3E = 0x1; TIM4_CCERbits.CC3P = 0x1; //set_TIM4_CC3_state_and_polarity(enable, active_low);
TIM4_CCERbits.CC4E = 0x1; TIM4_CCERbits.CC4P = 0x1; //set_TIM4_CC4_state_and_polarity(enable, active_low);
//TIM4_BDTRbits.MOE = 0x1; // enable_TIM4_main_output(true);
//TIM4_CR2bits.CCPC = 0x1; //set_TIM4_CC_preload_control(true);
TIM4_CR1bits.ARPE = 0x1; //set_TIM4_auto_reload_preload_mode(true);
TIM4_CR1bits.CEN = 0x1; //enable_TIM4_counter(true);
}

Application :

This application is designed to control a servo motor using a suitable


PWM generator, often referred to as Output Compare (OCx), to rotate it as
desired. We will exclusively utilize timer2, which is implicitly linked to pins
PA0..PA3 for the four channels. Therefore, in this application, we will focus
only on the PA0 channel to streamline the implementation process.
7

A) Servo-Motor PWM Controlling Signal

int main(void)
{
RCC_APB2ENRbits.IOPAEN=1;

/*Configure PA0 as Output Alternate Push/Pull */


GPIOA_CRLbits.MODE0=0x1;
GPIOA_CRLbits.CNF0=0x2;

AFIO_MAPRbits.TIM2_REMAP=0x0; /*Don't remap the pin*/


RCC_APB1ENRbits.TIM2EN=1; /*Enable clock access to timer2*/

/*Configure timer2*/
TIM2_PSC=16-1;
TIM2_ARR=10000-1;
TIM2_CNT=0;
TIM2_CCERbits.CC1E=1;
TIM2_CCMR1_Outputbits.OC1M =0x6;
TIM2_CCMR1_Output |= 0x0060;
TIM2_CR1.CEN=1;
8

while(1)
{ volatile int step;
int j;
for (step=250;step<1250;step++)
{
TIM2_CCR1=step;
for ( j=0;j<5000;j++);// soft delay()
}
Delay_ms(150);
for ( step=1250;step>250;step--)
{
TIM2_CCR1=step;
for ( j=0;j<5000;j++); // soft delay()
}
Delay_ms(150);
}
}

You might also like