© Warren Gay 2018

Warren Gay, Beginning STM32, https://doi.org/10.1007/978-1-4842-3624-6_16

16. PWM with Timer 2

Warren Gay

(1)St. Catharines, Ontario, Canada

The STM32 family has a complex array of options available for operating timers. The STM32F103 has general-purpose timers TIM2, 3, 4, and 5 and then advanced timers TIM1 and TIM8. The general-purpose timers have plenty of features so you won’t need to reach for the advanced ones.

This chapter will demonstrate one of the frequently sought-after applications of a timerthat of driving a PWM (Pulse Width Modulated) servo motor. Figure 16-1 illustrates one example of a typical servo motor, which was pulled out of service.

A465982_1_En_16_Fig1_HTML.jpg
Figure 16-1 A typical RC (Radio Controlled) servo motor (PKZ1081 SV80)

PWM Signals

What does a PWM signal look like? Figure 16-2 illustrates one. The sample shown has a high pulse that is 2.6 ms long (note the cursors and the BX-AX value shown). The measured frequency was about 147 Hz. This means the entire period of the signal is about 6.8 ms.

Most RC controls are based upon the length of time that the signal is high rather than the duty cycle, but control systems do vary.

A465982_1_En_16_Fig2_HTML.jpg
Figure 16-2 A PWM signal with a period of 147 Hz and pulse width of 2.6 ms

Timer 2

Timers 2 to 5 are general-purpose timers in the STM32F103C8T6. Despite being general purpose, they are quite flexible. Their overall features include the following:

  • 16-bit up, down, up/down auto-reload counter

  • 16-bit prescaler to divide the counter-clock frequency by 1 to 65536

  • Prescaler can be changed “on the fly.”

  • Up to four independent channels for:

    • input capture

    • output capture

    • PWM generation (edge- and center-aligned modes)

    • One-pulse mode output

  • Synchronization circuit controlling timer with external signals and for interconnection with other timers

  • Interrupt/DMA generation:

    • Update counter overflow/underflow, counter initialization by software or trigger

    • Trigger event (counter start, stop, initialization, or count by internal/external trigger)

    • Input capture

    • Output capture

  • Trigger input

Section 15 of the RM0008 1 reference manual discusses all of this, but in this chapter we’ll focus on the generation of a PWM signal. The software for this chapter’s demonstration is found in the following directory:

$ cd ~/stm32f103c8t6/rtos/tim2_pwm

The source code is entirely in the file main.c. The portions of task1() that apply to the timer will be listed in small sections to ease the discussion. Some initial configuration is shown in Listing 16-1.

Listing 16-1 Configuration of PA1 for Timer 2 Output
0029:   rcc_periph_clock_enable(RCC_TIM2);
0030:   rcc_periph_clock_enable(RCC_AFIO);
0031:
0032:   // PA1 == TIM2.CH2  
0033:   rcc_periph_clock_enable(RCC_GPIOA);
0034:   gpio_primary_remap(
0035:       AFIO_MAPR_SWJ_CFG_JTAG_OFF_SW_OFF,  // Optional
0036:       AFIO_MAPR_TIM2_REMAP_NO_REMAP);     // default: TIM2.CH2=GPIOA1
0037:   gpio_set_mode(GPIOA,GPIO_MODE_OUTPUT_50_MHZ,   // High speed
0038:       GPIO_CNF_OUTPUT_ALTFN_PUSHPULL,GPIO1); // GPIOA1=TIM2.CH2

Line 29 enables the clock for Timer 2, while AFIO’s clock is enabled in line 30. This needs to be done before line 35.

Line 33 enables the clock for GPIOA for PA1’s use. Lines 34 to 36 are an AFIO call that says use the default mapping where channel 2 of Timer 2 comes out on PA1 (this can be omitted in the default case). Change this call if you need it to come out to PB3 instead. Finally, lines 37 and 38 use the ALTFN macro to connect the GPIO pin to the timer for PA1. This is critical.

Listing 16-2 illustrates code that initializes the timer and establishes its operating mode.

Listing 16-2 Initialize Timer 2 and Set Its Mode
0042:   // TIM2:
0043:   timer_disable_counter(TIM2);
0044:   timer_reset(TIM2);
0045:
0046:   timer_set_mode(TIM2,
0047:       TIM_CR1_CKD_CK_INT,
0048:       TIM_CR1_CMS_EDGE,
0049:       TIM_CR1_DIR_UP);

Lines 43 and 44 disable the counter and reset Timer 2. Line 46 then establishes the operating mode for the timer as follows:

  • TIM_CR1_CKD_CK_INT configures the division ration between the timer clock (CLK_INT) frequency and the sampling clock used by the digital filters. Here, we don’t use the digital filters, so this macro sets the digital filter frequency equal to the clock frequency (see datasheet TIMx_CR1.CKD for Timer 2 for more).

  • TIM_CR1_CMS_EDGE specifies that the edge-aligned mode is to be used (versus center-aligned).

  • TIM_CR1_DIR_UP specifies that the counter will count up.

Listing 16-3 continues with the Timer 2 configuration and then launches into the demonstration.

Listing 16-3 Remainder of Configuration and Timer Launch
0026:   static const int ms[4] = { 500, 1200, 2500, 1200 };
0027:   int msx = 0;
...
0050:   timer_set_prescaler(TIM2,72);
0051:   // Only needed for advanced timers:
0052:   // timer_set_repetition_counter(TIM2,0);
0053:   timer_enable_preload(TIM2);
0054:   timer_continuous_mode(TIM2);
0055:   timer_set_period(TIM2,33333);
0056:
0057:   timer_disable_oc_output(TIM2,TIM_OC2);
0058:   timer_set_oc_mode(TIM2,TIM_OC2,TIM_OCM_PWM1);
0059:   timer_enable_oc_output(TIM2,TIM_OC2);
0060:
0061:   timer_set_oc_value(TIM2,TIM_OC2,ms[msx=0]);
0062:   timer_enable_counter(TIM2);

Line 50 establishes the timer frequency by setting the prescaler for it. This will be described fully later. Line 52 is needed for the advanced timers only (Timers 1 and 8) and is thus commented out. This call is ignored by libopencm3 for Timers 2 through 5.

Lines 53 and 54 configure two more options for the timer. The timer_enable_preload() call indicates that the TIM2_ARR register is buffered (for reloading). The function timer_continuous_mode() configures the timer to keep running rather than to stop after one pulse. Line 55 sets the maximum timer count to establish its period. More will be said about this later.

Lines 57 to 59 configure Timer 2’s channel OC2 (output-compare channel 2) to operate in PWM1 mode. The configuration to PWM1 mode occurs in line 58. TIM_OCM_PWM1 specifies the following:

When counting up, the output channel is active (high) when the timer’s count is less than the timer capture/compare register, or else the channel goes low.

Line 61 sets the output-compare register to the value found in the ms[] array. This establishes a starting pulse width (in microseconds). The timer is finally started in line 62.

Line 26 declares an array ms[4] containing four values. These are pulse widths in microseconds, with 1200 (1.2 ms) as the center position. With the mode established in line 58, the following will happen:

  • Counter values 0 through ms[msx]-1 will cause GPIO PA1 to go high (while it is considered active).

  • Once the counter climbs above that value, the PA1 level goes low.

With this configuration, the PA1 output will initially go high for 500 μsec (0.5 ms) for a total of 33,333 counts (the period configured in line 55).

PWM Loop

The demonstration program performs the loop illustrated in Listing 16-4.

Listing 16-4 Demonstration PWM Loop
0064:   for (;;) {
0065:       vTaskDelay(1000);
0066:       gpio_toggle(GPIOC,GPIO13);
0067:       msx = (msx+1) % 4;
0068:       timer_set_oc_value(TIM2,TIM_OC2,ms[msx]);
0069:   }

At the top of the loop, line 65 delays execution for about one second (1000 ms), after which the PC13 LED GPIO is toggled (line 66). Line 67 updates array index variable msx so that it counts up, but starts over at zero if it goes past three. Using the index variable, the next position value is used to change the output-compare register in line 68. Once the servo sees this pulse, its position will change (or you can view the pulse-width change on the scope).

Calculating Timer Prescale

In Listing 16-3, line 50 was a function call that established the prescaler count. Let’s break that calculation down. For your convenience, the prescaler setting was this:

timer_set_prescaler(TIM2,72)

The input to the counter is 72 MHz because when the Blue Pill is configured the APB1 prescaler is normally set to 2, and thus the bus frequency is divided down to 36 MHz (its maximum). What does that note about the TIM2, 3, 4, and 5 prescaler say?

When APB1 prescaler = 1, then is times 1, else it is times 2.

You could be excused if you got confused by all of this. After all, we have the 72 MHz SYSCLCK frequency divided down by 2 to meet the 36 MHz maximum frequency for the APB1 bus. After that, there is another prescaler that applies for timers 2 through 5. I’ll refer to this as the global prescaler since it applies to all timers (2 through 5). The output of that prescaler feeds into the timers’ own private prescalers. With all these prescalers, it’s no wonder there is confusion!

The quote about when prescaler = 1 reminds us of the fact that what comes out of the global timer prescaler is actually the APB1 bus frequency times two! Therefore, what goes into Timer 2’s own private prescaler is 72 MHz, not 36. So now we can explain the top part of the formula:

$$ frac{72000000}{72}=1000000 $$

The numerator represents the 72 MHz entering Timer 2’s private prescaler. Supplying a private timer prescale value of 72 causes the timer to be updated at 1 MHz (line 50 of Listing 16-3).

We didn’t have to use this ratio, but it proves to be convenient. Each count occurs in 1 μsec, allowing us to specify the pulse width in microseconds.

30 Hz Cycle

I have assumed that your RC servo needs a cycle rate of 30 Hz. This is defined by the configuration performed in Listing 16-3 line 55:

$$ frac{f_{APB1}	imes 2}{frac{prescaler}{f_{period}}}=frac{36000000	imes 2}{frac{72}{30}}=33333.3 $$

To program it, we could code:

    timer_set_prescaler(TIM2,36000000*2/72/30);

or simply code:

    timer_set_prescaler(TIM2,33333);

To reduce the period (increase the frequency) to 50 Hz, simply replace 30 with 50 in the calculation.

Tip

Remember that the frequency entering the timer’s private prescaler is doubled if the APBx prescaler is 1.

Servo Hookup

Unfortunately, servos generally operate at around 6 volts. For the STM32, this requires a small driver circuit to bridge the gap.

The good news is that the interface is rather simple. You can use a CD4050BE CMOS IC to accept a 3.3-volt signal on its input and produce a nearly 6-volt level on its output (Figure 16-3). Notice that pin 1 of IC1A is connected to the servo motor’s supply. The design of the CD4050 is such that the input (IC1A pin 3) can be safely connected to the STM32.

A465982_1_En_16_Fig3_HTML.jpg
Figure 16-3 The 6-volt interface circuit between STM32 MCU and servo motor

Other replacements for the CD4050 would be the 74HCT244 or 74HCT245 (with different pinouts). It is critical that these special-talent chips are used to bridge the voltage gap. For more about this, see the book Custom Raspberry Pi Interfaces, Chapter 2, “3V/5V Signal Interfacing.” While other CMOS chips can operate at 6 volts, they may not see the STM32 input signal as a high (this depends upon the V IH threshold value).

When hooking up your circuit, make certain that you connect the 6-volt system ground to the STM32 ground. This provides a common voltage reference point.

Running the Demo

After building and flashing the software as follows:

$ make clobber
$ make
$ make flash

all you have to do is make sure your connections are correct and plug in the power (or power the STM32 from USB). No USB or UART communication is used by this demo.

With the servo connected, it should twitch every second to one extreme, middle position, other extreme, middle again, and back to the first extreme. Servos vary in their PWM requirements, so you may need to change the following:

  • The pulse-width table in Listing 16-3, line 26 (these are in microseconds)

  • The period in Listing 16-3, line 55

For amusement, attach a cat’s laser pointer to the servo arm.

PWM on PB3

Timer 2 output-compare 2 can be redirected to PB3. This can be exploited if you require a 5-volt PWM signal. PB3 is a 5-volt-tolerant GPIO, though it can’t produce a 5-volt high signal directly. When driven as an open-drain GPIO, however, a pull-up resistor can make the signal rise to 5 volts.

The source code for this version of the project is located here:

$ cd stm32f103c8t6/rtos/tim2_pwm_pb3

The main.c module is nearly identical except for the differences shown here:

$ diff -c ../tim2_pwm/main.c main.c
...
!     // PA1 == TIM2.CH2
!     rcc_periph_clock_enable(RCC_GPIOA);        // Need GPIOA clock
      gpio_primary_remap(
          AFIO_MAPR_SWJ_CFG_JTAG_OFF_SW_OFF, // Optional
!         AFIO_MAPR_TIM2_REMAP_NO_REMAP);    // default: TIM2.CH2=GPIOA1
!     gpio_set_mode(GPIOA,GPIO_MODE_OUTPUT_50_MHZ, // High speed
!         GPIO_CNF_OUTPUT_ALTFN_PUSHPULL,GPIO1);   // GPIOA1=TIM2.CH2
--- 29,41 ----
!     // PB3 == TIM2.CH2
!     rcc_periph_clock_enable(RCC_GPIOB);          // Need GPIOB clock
      gpio_primary_remap(
          AFIO_MAPR_SWJ_CFG_JTAG_OFF_SW_OFF,       // Optional
!         AFIO_MAPR_TIM2_REMAP_PARTIAL_REMAP1);    // TIM2.CH2=PB3
!     gpio_set_mode(GPIOB,GPIO_MODE_OUTPUT_50_MHZ, // High speed
!         GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN,GPIO3);  // PB3=TIM2.CH2

The first change is to activate GPIOB instead of GPIOA. Following that, the gpio_primary_remap() call uses argument AFIO_MAPR_TIM2_REMAP_PARTIAL_REMAP1 to direct Timer 2’s output-compare 2 to PB3.

The last change in gpio_set_mode() configures PB3 to use open-drain output. This is necessary because PB3 cannot produce a 5-volt signal directly (it can only pull it up to +3.3 volts). PB3 can, however, allow it to be pulled up to +5 volts by a resistor when it is operating as open drain. This change and the addition of a pullup resistor in the range of 2K to 10K ohms will permit a 5-volt output signal to be generated.

Other Timers

When it comes to servo PWM signals, people often want to know how many PWM channels can be made available. Table 16-1 summarizes the timers available on the STM32F103C8T6 and the default GPIO assignments. Some timers also have alternate assignments available.

Table 16-1 Timers Available to the STM32F103C8T6

Timer

Type

Channel 1

Channel 2

Channel 3

Channel 4

TIM1

Advanced

PA12

PA8

PA9

PA10

PB13=CH1N

PB14=CH2N

PB15=CH3N

PB12=BKIN

TIM2

General Purpose

PA0

PA1

PA2

PA3

TIM3

General Purpose

PA6

PA7

PB0

PB1

TIM4

General Purpose

PB6

PB7

PB8

PB9

TIM5

General Purpose

None

None

None

PA3

TIM8

Advanced

None

None

None

None

Timers have four channels, which are configurable as GPIO inputs or outputs. Timer TIM8 has no GPIO connections at all but can link internally to other timers. TIM5 only links its channel 4 to PA3. The remaining general-purpose timers TIM2 through TIM4 have a full complement of GPIOs.

Advanced timer TIM1 has the most comprehensive set of I/Os, with up to eight assignments. Entries marked with CHxN are GPIOs that use the opposite polarity. Finally, the signal BKIN serves as a special “break” input.

The answer to the question “How many PWM timers?” is five. To use all five, you have to accept GPIO PA3 for TIM5. TIM1 through TIM4 can produce PWM output signals on four GPIO-connected channels. Altogether, these five timers provide a possible total of twenty-one output channels.

More PWM Channels

Getting more PWM output channels requires a little organization and software. Each timer has four channels, so TIM8, for example, could be used to generate up to four different interrupts based upon each channel’s output-compare register. Even though none of Timer 8’s channels are connected to GPIOs, the interrupt routine itself can drive GPIO outputs with software with no loss in precision.

Summary

This chapter applied hardware timers to the task of generating PWM signal outputs suitable for driving RC servo motors. Of course, PWM is not restricted to servos alone. PWM signals may be applied in other ways to take advantage of duty-cycle changes.

The beauty of using hardware timers is that it requires little or no software support once it is configured to run. To change a pulse width or duty cycle requires one small update to the timer, and then the timer goes on its merry way. Hardware timers also offer greater precision since they are not subject to software delays.

Bibliography

  1. STMicroelectronics. Accessed January 12, 2018. http://www.st.com/resource/en/reference_manual/cd00171190.pdf

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.221.163.13