Overview
The TM4C123GH6PM includes two PWM modules (PWM0 and PWM1), each containing four generators for a total of eight generators and sixteen PWM output signals (M0PWM0–M0PWM7 and M1PWM0–M1PWM7). These hardware PWM outputs generate precise, CPU-independent waveforms used for motor control, LED dimming, and signal generation.
Beginner Level — What & Why
What is PWM?
PWM (Pulse Width Modulation) is a technique for controlling power delivery by rapidly switching a signal between ON and OFF. The ratio of ON time to total period is called the duty cycle (0%–100%). By changing the duty cycle, you control the average power delivered to a load — dimming an LED or controlling a motor’s speed.
Real-World Analogy
Imagine a light switch you flick on and off very rapidly. If it is ON half the time and OFF half the time, the bulb appears half as bright. Doing this 50 times per second (50 Hz) is invisible to the eye — the bulb just looks dimmer. That is PWM.
What Problem Does It Solve?
- LED brightness control without power-wasting resistors
- DC motor speed control
- Servo motor position control (50 Hz, 1–2 ms pulse width = 0°–180°)
- Tone/frequency generation for buzzers
Key Terms
| Term |
Meaning |
| Period |
Total cycle time (1 / frequency) |
| Duty cycle |
Fraction of period the output is HIGH |
| Generator |
Hardware block producing 2 complementary outputs |
| Count-down |
Counter counts from LOAD to 0 (simplest mode) |
| Count-up/down |
Counter counts up then down (for complementary/dead-band) |
| PWMDIV |
System clock prescaler for PWM clock |
PWM Module Architecture
PWM0 Module:
Generator 0 → M0PWM0 (PB6) + M0PWM1 (PB7)
Generator 1 → M0PWM2 (PB4) + M0PWM3 (PB5)
Generator 2 → M0PWM4 (PE4) + M0PWM5 (PE5)
Generator 3 → M0PWM6 (PC4) + M0PWM7 (PC5)
PWM1 Module:
Generator 0 → M1PWM0 (PD0) + M1PWM1 (PD1)
Generator 1 → M1PWM2 (PA6) + M1PWM3 (PA7)
Generator 2 → M1PWM4 (PF0) + M1PWM5 (PF1)
Generator 3 → M1PWM6 (PF2) + M1PWM7 (PF3)
Key Registers (Per Generator)
| Register |
Offset from Gen |
Description |
| PWMCTL |
0x000 |
Generator control (enable, mode) |
| PWMINTEN |
0x004 |
Interrupt enable |
| PWMRIS |
0x008 |
Raw interrupt status |
| PWMISC |
0x00C |
Interrupt clear |
| PWMLOAD |
0x010 |
Load value (sets period) |
| PWMCOUNT |
0x014 |
Current counter value |
| PWMCMPA |
0x018 |
Compare A (PWMxA duty) |
| PWMCMPB |
0x01C |
Compare B (PWMxB duty) |
| PWMGENA |
0x020 |
Generator A action (what to do at compare events) |
| PWMGENB |
0x024 |
Generator B action |
| PWMDBCTL |
0x028 |
Dead-band control |
Module-level registers:
| Register |
Offset |
Description |
| PWMENABLE |
0x008 |
Enable PWM outputs |
| PWMCTL |
0x000 |
Global sync |
TivaWare DriverLib API
#include "driverlib/pwm.h"
#include "driverlib/pin_map.h"
/* Step 1: Configure PWM clock = system clock / 64 = 1.25 MHz */
SysCtlPWMClockSet(SYSCTL_PWMDIV_64);
/* Step 2: Enable PWM1 and Port F */
SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM1);
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
while (!SysCtlPeripheralReady(SYSCTL_PERIPH_PWM1));
while (!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOF));
/* Step 3: Configure PF1 as M1PWM5 output */
GPIOPinConfigure(GPIO_PF1_M1PWM5);
GPIOPinTypePWM(GPIO_PORTF_BASE, GPIO_PIN_1);
/* Step 4: Configure Generator 2 count-down, no sync */
PWMGenConfigure(PWM1_BASE, PWM_GEN_2,
PWM_GEN_MODE_DOWN | PWM_GEN_MODE_NO_SYNC);
/* Step 5: Set period
* PWM clock = 80 MHz / 64 = 1.25 MHz
* For 50 Hz: period = 1.25 MHz / 50 = 25,000 ticks */
PWMGenPeriodSet(PWM1_BASE, PWM_GEN_2, 25000);
/* Step 6: Set duty cycle
* 1.5 ms pulse at 50 Hz = 1.5 ms × 1.25 MHz = 1875 ticks */
PWMPulseWidthSet(PWM1_BASE, PWM_OUT_5, 1875);
/* Step 7: Enable output */
PWMOutputState(PWM1_BASE, PWM_OUT_5_BIT, true);
/* Step 8: Start generator */
PWMGenEnable(PWM1_BASE, PWM_GEN_2);
Advanced Level — Deep Dive
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "inc/hw_pwm.h"
/* PWM1 Generator 2 base offset = 0x40029080 */
#define PWM1_GEN2_BASE (PWM1_BASE + PWM_GEN_2_OFFSET)
void PWM1_Servo_Init(void)
{
/* PWM clock = SYSCLK / 64 = 80 MHz / 64 = 1.25 MHz */
HWREG(SYSCTL_RCC) |= SYSCTL_RCC_USEPWMDIV;
HWREG(SYSCTL_RCC) &= ~SYSCTL_RCC_PWMDIV_M;
HWREG(SYSCTL_RCC) |= SYSCTL_RCC_PWMDIV_64;
/* Enable PWM1 and Port F clocks */
HWREG(SYSCTL_RCGCPWM) |= (1U << 1); // PWM1
HWREG(SYSCTL_RCGCGPIO) |= (1U << 5); // Port F
while (!(HWREG(SYSCTL_PRPWM) & (1U << 1)));
while (!(HWREG(SYSCTL_PRGPIO) & (1U << 5)));
/* Configure PF1 for M1PWM5 (alternate function 5) */
HWREG(GPIO_PORTF_BASE + GPIO_O_AFSEL) |= GPIO_PIN_1;
HWREG(GPIO_PORTF_BASE + GPIO_O_PCTL) =
(HWREG(GPIO_PORTF_BASE + GPIO_O_PCTL) & ~0xF0) | 0x50; // PMC1=5
HWREG(GPIO_PORTF_BASE + GPIO_O_DEN) |= GPIO_PIN_1;
/* Disable generator while configuring */
HWREG(PWM1_GEN2_BASE + PWM_O_X_CTL) = 0;
/* Count-down mode */
HWREG(PWM1_GEN2_BASE + PWM_O_X_GENA) =
PWM_X_GENA_ACTCMPAD_ONE | // go HIGH when count == CMPA (descending)
PWM_X_GENA_ACTLOAD_ZERO; // go LOW when counter loads (i.e., at LOAD)
/* Period: 25,000 ticks = 20 ms at 1.25 MHz */
HWREG(PWM1_GEN2_BASE + PWM_O_X_LOAD) = 25000 - 1;
/* Duty: 1875 ticks = 1.5 ms (90 degrees centre position) */
HWREG(PWM1_GEN2_BASE + PWM_O_X_CMPA) = 25000 - 1875;
/* Enable generator */
HWREG(PWM1_GEN2_BASE + PWM_O_X_CTL) = PWM_X_CTL_ENABLE;
/* Enable output 5 */
HWREG(PWM1_BASE + PWM_O_ENABLE) |= PWM_OUT_5_BIT;
}
/* Set servo angle 0-180 degrees
* Pulse: 1 ms (0°) to 2 ms (180°) at 1.25 MHz = 1250 to 2500 ticks */
void Servo_SetAngle(uint8_t degrees)
{
uint32_t ticks = 1250 + ((uint32_t)degrees * 1250 / 180);
HWREG(PWM1_GEN2_BASE + PWM_O_X_CMPA) = 25000 - ticks;
}
Calculating PWM Parameters
PWM clock = System clock / PWMDIV
= 80 MHz / 64 = 1.25 MHz
Ticks per period = PWM clock / Desired frequency
For 50 Hz servo: 1,250,000 / 50 = 25,000 ticks
Duty cycle ticks = Period ticks × (duty% / 100)
For 50% duty: 25,000 × 0.5 = 12,500 ticks
Servo pulse width in ticks:
1.0 ms = 1,250 ticks (0 degrees)
1.5 ms = 1,875 ticks (90 degrees)
2.0 ms = 2,500 ticks (180 degrees)
Common PWMDIV Values
| Macro |
Divisor |
PWM Clock at 80 MHz |
SYSCTL_PWMDIV_1 |
1 |
80 MHz |
SYSCTL_PWMDIV_2 |
2 |
40 MHz |
SYSCTL_PWMDIV_4 |
4 |
20 MHz |
SYSCTL_PWMDIV_8 |
8 |
10 MHz |
SYSCTL_PWMDIV_16 |
16 |
5 MHz |
SYSCTL_PWMDIV_32 |
32 |
2.5 MHz |
SYSCTL_PWMDIV_64 |
64 |
1.25 MHz |
Gotchas
- PWM uses its own clock divider (SYSCTL_RCC PWMDIV) — it is separate from the system clock divider.
- PWMPulseWidthSet accepts the number of ticks the output is HIGH — do not confuse with the LOAD register value.
- In count-down mode, CMPA acts on the descending count, so
CMPA = LOAD - pulse_ticks for correct behaviour in bare-metal.
- PWMOutputState must be called to actually enable the output pin —
PWMGenEnable only starts the counter.
- PF0 conflict: M1PWM4 shares PF0 with the NMI function — unlock PF0 as shown in the GPIO chapter before using it for PWM.
Step-by-Step Example
/*
* pwm_servo.c
* Sweeps a servo from 0 to 180 degrees and back using M1PWM5 on PF1
* Board : TM4C123GXL EK LaunchPad
* SDK : TivaWare_C_Series-2.2.x
* Wiring : PF1 → servo signal wire, 5V to servo power (external supply)
*/
#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_memmap.h"
#include "driverlib/sysctl.h"
#include "driverlib/gpio.h"
#include "driverlib/pwm.h"
#include "driverlib/pin_map.h"
/* PWM clock = 80 MHz / 64 = 1.25 MHz, period = 25000 ticks = 20 ms (50 Hz) */
#define PWM_PERIOD_TICKS 25000
#define SERVO_MIN_TICKS 1250 /* 1.0 ms = 0 degrees */
#define SERVO_MAX_TICKS 2500 /* 2.0 ms = 180 degrees */
void Servo_SetDegrees(uint8_t deg)
{
/* Map 0-180 degrees to 1250-2500 ticks */
uint32_t ticks = SERVO_MIN_TICKS +
((uint32_t)deg * (SERVO_MAX_TICKS - SERVO_MIN_TICKS)) / 180;
PWMPulseWidthSet(PWM1_BASE, PWM_OUT_5, ticks);
}
int main(void)
{
uint8_t deg;
/* Step 1: 80 MHz system clock */
SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL |
SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);
/* Step 2: PWM clock = system / 64 = 1.25 MHz */
SysCtlPWMClockSet(SYSCTL_PWMDIV_64);
/* Step 3: Enable PWM1 and Port F */
SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM1);
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
while (!SysCtlPeripheralReady(SYSCTL_PERIPH_PWM1));
while (!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOF));
/* Step 4: PF1 → M1PWM5 */
GPIOPinConfigure(GPIO_PF1_M1PWM5);
GPIOPinTypePWM(GPIO_PORTF_BASE, GPIO_PIN_1);
/* Step 5: Generator 2, count-down mode */
PWMGenConfigure(PWM1_BASE, PWM_GEN_2,
PWM_GEN_MODE_DOWN | PWM_GEN_MODE_NO_SYNC);
/* Step 6: 50 Hz period */
PWMGenPeriodSet(PWM1_BASE, PWM_GEN_2, PWM_PERIOD_TICKS);
/* Step 7: Centre position (90 degrees = 1.5 ms) */
PWMPulseWidthSet(PWM1_BASE, PWM_OUT_5, 1875);
/* Step 8: Enable output and start generator */
PWMOutputState(PWM1_BASE, PWM_OUT_5_BIT, true);
PWMGenEnable(PWM1_BASE, PWM_GEN_2);
/* Sweep 0 → 180 → 0 degrees continuously */
while (1)
{
for (deg = 0; deg <= 180; deg++)
{
Servo_SetDegrees(deg);
SysCtlDelay(SysCtlClockGet() / 3 / 50); // ~20 ms per step
}
for (deg = 180; deg > 0; deg--)
{
Servo_SetDegrees(deg);
SysCtlDelay(SysCtlClockGet() / 3 / 50);
}
}
}
Summary
| Key Point |
Details |
| PWM modules |
PWM0 (4 generators, 8 outputs) + PWM1 (4 generators, 8 outputs) |
| PWM clock |
System clock / PWMDIV (64 options) |
| Servo frequency |
50 Hz (PERIOD = 25,000 at 1.25 MHz) |
| Servo 0° |
1.0 ms pulse = 1,250 ticks |
| Servo 90° |
1.5 ms pulse = 1,875 ticks |
| Servo 180° |
2.0 ms pulse = 2,500 ticks |
| Enable output |
PWMOutputState + PWMGenEnable |
| Duty cycle |
PWMPulseWidthSet(base, output, ticks) |
Next Chapter
SSI Interface