Something about PWM:
We all know that microcontrollers, well processors do everything with ones and zeros. Which means that when we would like to make simple sine wave we couldn’t. Because if microcontroller works with 3.3V and 0V as logical 1 and 0, it can not produce for example 1V or 2.2V or any other value different than 0V and 3.3V. But if it’s possible to generate square wave signal than it’s possible to generate any value between minimum and maximum value that that microcontroller produce. Now I will explain how’s that possible… How does PWM works:
Just for better explanation let’s solve simple task:
Using PWM wih reference voltages of (min) 0V and (max) 3.3V generate 2V voltage.
1. If we have reference voltages of 0V and 3.3V then we have to calculate coefficient that somehow connects values 0, 3.3 and 2. Since the biggest value our chip can give is 3.3V you can assume that signal period is consisted from bigger part of ‘high’ output and smaller part of ‘low’ output.
3.3V -> 2V
3.3V * x = 2V
x = 0.6061
Practically, this means that if period is 1ms then 60.61% (named as duty cycle) of that time signal should be driven ‘high’, and rest ‘low’.
3. Now programming preamble…
I decided to use PWM3 output signal. That microcontroller pin is multiplexed with GPIO PB1 output. From picture is seen that PWM3 is generated by PWM Genrator 1 (just like PWM2 output signal).
Most important part of the code:
/* Enables PWM and GPIO peripheral. * By default PWM isn't enabled. * It's because that PWM block uses clock for its work, * since one of our biggest tasks is to make low power * devices, all unnecessary blocks (that are using * clock) are being disabled by default. */ SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM); SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB); /* Pin PB1 is also PWM3. We're using this pin to turn on * LED with different brightness. It is necessary to * define that pin as GPIO with PWM output. (This is why * is written before * "SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);" * even though it's used PWM output.) */ GPIOPinTypePWM(GPIO_PORTB_BASE, GPIO_PIN_1); /* Remember that block diagram from above? Well, it's * important to notice that generator 1 (not 0 or 2) * generates raw PWM signal. */ PWMGenConfigure(PWM_BASE, PWM_GEN_1, PWM_GEN_MODE_DOWN); /* In here you made most basic part of PWM (the one I * explained before with calculation). First of all, it's * important to set period, and later on duty cycle. With * this parameters, below, Period is 1000, and duty cycle * is 20%. */ PWMGenPeriodSet(PWM_BASE, PWM_GEN_1, 1000); PWMPulseWidthSet(PWM_BASE, PWM_OUT_3, 200); /* When all is set enable generator. If you use 2 * generators (2nd and 1st) and you want to turn * them on in synchronously then this function should * look like: * PWMGenEnable(PWM_BASE, PWM_GEN_2 | PWM_GEN_1);" */ PWMGenEnable(PWM_BASE, PWM_GEN_1); /* When generator is enabled, the output isn't! */ PWMOutputState(PWM_BASE, PWM_OUT_3_BIT, 1);
How does this module actually work?
For start, it uses clock that is connected to the PWM generators (0, 1, 2). Every generator gives 2 raw PWM output signals. Usable (the that is on output pin of microcontroller) signals are those that are shown in Figure 17.-1., named as: PWM 1, PWM 2, …, PWM 5. I’m saying usable because that ones you’re using when you connect LEDs on your ports.
Generators generate wanted signal. Inside generator, comparators (A and B) compares written values (in registers PWMnCMPA and PWMnCMPB) with current timer value. Written values are the ones that causes high pulse (only 1 PWM clock tick) on comparators outputs. Those outputs are triggers for Signal generator that uses these pulses and direction signal for generating raw PWM signal. Since I haven’t explained direction signal probably the best is to take a look at this picture:
The triangle lines are representation of PWM counter. There are 2 types of counters; up-down mode and down mode. In this picture it’s used up-down mode. CompA and CompB dashed lines represent triggers for PWM comparators, Load represents value at which counters start counting in different direction (for this mode). If it’s used down counter mode then load value would be the one which resets counter back to zero (the counting direction doesn’t changes). Zero is just trigger for counter that it should start new cycle (in this case change direction of counting).
If we take a look at down signals, especially A, B and Dir, we would see that A and B are high only for one clock tick and only after the counter value reaches the PWMnCMPA value. Dir signal is the one that indicates if the counter is counting from Zero to the Load value (Dir = high value) or from Load value to the Zero (Dir = down value).
Complete project file you can download here.
P.S. All pictures are the ones you can find in: “Stellaris LM3S6965 Microcontroller DATA SHEET“