0
0
Power-electronicsHow-ToBeginner · 4 min read

How to Generate PWM Signal in Embedded C: Simple Guide

To generate a PWM signal in embedded C, configure a timer peripheral to toggle an output pin at a set frequency and duty cycle. Use timer registers to set the period and duty cycle, then enable the timer and output pin to produce the PWM waveform.
📐

Syntax

Generating PWM involves configuring a timer's registers to set the frequency and duty cycle, then enabling the output pin. The key steps are:

  • Set timer period: Defines the PWM frequency.
  • Set duty cycle: Controls how long the signal stays high in each period.
  • Enable timer and output: Starts the PWM signal on the pin.
c
void PWM_Init(unsigned int period, unsigned int duty_cycle) {
    TIMER_PERIOD_REGISTER = period;       // Set PWM period
    TIMER_COMPARE_REGISTER = duty_cycle; // Set duty cycle
    TIMER_CONTROL_REGISTER |= ENABLE_PWM; // Enable PWM mode
    OUTPUT_PIN_DIRECTION_REGISTER |= PIN_MASK; // Set pin as output
}
💻

Example

This example shows how to generate a 1 kHz PWM signal with 50% duty cycle on a microcontroller pin using embedded C. It assumes a timer peripheral and registers named generically.

c
#include <stdint.h>

#define TIMER_PERIOD_REGISTER (*(volatile uint16_t*)0x4000)
#define TIMER_COMPARE_REGISTER (*(volatile uint16_t*)0x4002)
#define TIMER_CONTROL_REGISTER (*(volatile uint8_t*)0x4004)
#define OUTPUT_PIN_DIRECTION_REGISTER (*(volatile uint8_t*)0x5000)
#define OUTPUT_PIN_PORT (*(volatile uint8_t*)0x5001)
#define PIN_MASK 0x01
#define ENABLE_PWM 0x01

void PWM_Init(uint16_t period, uint16_t duty_cycle) {
    TIMER_PERIOD_REGISTER = period;       // Set PWM period
    TIMER_COMPARE_REGISTER = duty_cycle; // Set duty cycle
    TIMER_CONTROL_REGISTER |= ENABLE_PWM; // Enable PWM mode
    OUTPUT_PIN_DIRECTION_REGISTER |= PIN_MASK; // Set pin as output
}

int main() {
    uint16_t period = 1000;      // Timer counts for 1 kHz frequency
    uint16_t duty_cycle = 500;   // 50% duty cycle

    PWM_Init(period, duty_cycle);

    while(1) {
        // Main loop - PWM runs automatically
    }

    return 0;
}
Output
No console output; PWM signal generated on output pin at 1 kHz, 50% duty cycle
⚠️

Common Pitfalls

Common mistakes when generating PWM signals include:

  • Not setting the output pin as an output, so no signal appears.
  • Incorrect timer period or duty cycle values causing wrong frequency or no visible PWM.
  • Forgetting to enable the timer or PWM mode in control registers.
  • Using integer math without considering timer clock frequency, leading to wrong timing.
c
/* Wrong: Not setting pin as output */
void PWM_Init_Wrong(uint16_t period, uint16_t duty_cycle) {
    TIMER_PERIOD_REGISTER = period;
    TIMER_COMPARE_REGISTER = duty_cycle;
    TIMER_CONTROL_REGISTER |= ENABLE_PWM;
    // Missing: OUTPUT_PIN_DIRECTION_REGISTER |= PIN_MASK;
}

/* Correct: Set pin as output */
void PWM_Init_Correct(uint16_t period, uint16_t duty_cycle) {
    TIMER_PERIOD_REGISTER = period;
    TIMER_COMPARE_REGISTER = duty_cycle;
    TIMER_CONTROL_REGISTER |= ENABLE_PWM;
    OUTPUT_PIN_DIRECTION_REGISTER |= PIN_MASK;
}
📊

Quick Reference

Tips for PWM generation in embedded C:

  • Calculate timer period from desired PWM frequency and timer clock.
  • Calculate duty cycle as a value between 0 and period.
  • Always configure the output pin as output.
  • Enable PWM mode and timer before starting.
  • Use volatile keyword for hardware registers.

Key Takeaways

Configure timer period and duty cycle registers to set PWM frequency and signal width.
Always set the output pin as an output to see the PWM signal.
Enable the timer and PWM mode in control registers to start signal generation.
Calculate timer values carefully based on your microcontroller clock.
Use volatile pointers for hardware registers to ensure correct operation.