0
0
Power-electronicsHow-ToBeginner · 4 min read

How to Create Software PWM in Embedded C: Simple Guide

To create software PWM in Embedded C, use a timer or delay loop to toggle a digital output pin on and off with precise timing. Control the duty cycle by adjusting the on and off durations within a fixed period. This simulates PWM without hardware support.
📐

Syntax

Software PWM involves toggling a GPIO pin manually in code. The key parts are:

  • Period: Total time of one PWM cycle.
  • Duty cycle: Percentage of time the pin stays HIGH in one period.
  • Delay functions: Used to create the timing for HIGH and LOW states.

Basic syntax pattern:

while(1) {
  set_pin_high();
  delay(on_time);  // ON duration based on duty cycle
  set_pin_low();
  delay(off_time); // OFF duration = period - on_time
}
c
while(1) {
  set_pin_high();
  delay(on_time);  // ON duration based on duty cycle
  set_pin_low();
  delay(off_time); // OFF duration = period - on_time
}
💻

Example

This example shows a software PWM on a microcontroller pin using a simple delay loop. It sets a 50% duty cycle with a 1 second period.

c
#include <stdint.h>
#include <stdbool.h>

// Assume these functions control the hardware pin
void set_pin_high(void);
void set_pin_low(void);
void delay_ms(uint32_t ms);

int main(void) {
    const uint32_t period_ms = 1000; // 1 second period
    const uint32_t duty_cycle_percent = 50; // 50% duty cycle

    uint32_t on_time = (period_ms * duty_cycle_percent) / 100;
    uint32_t off_time = period_ms - on_time;

    while(1) {
        set_pin_high();
        delay_ms(on_time);
        set_pin_low();
        delay_ms(off_time);
    }
    return 0;
}

// Dummy implementations for demonstration
void set_pin_high(void) {
    // Code to set GPIO pin HIGH
}
void set_pin_low(void) {
    // Code to set GPIO pin LOW
}
void delay_ms(uint32_t ms) {
    // Simple blocking delay loop (not precise)
    volatile uint32_t count;
    while(ms--) {
        for(count = 0; count < 3000; count++) {
            __asm__("nop");
        }
    }
}
⚠️

Common Pitfalls

Common mistakes when creating software PWM include:

  • Using blocking delays: This stops other code from running and reduces responsiveness.
  • Inaccurate timing: Delay loops depend on CPU speed and compiler optimizations, causing PWM frequency drift.
  • Not using timers: Hardware timers provide more precise timing than software loops.
  • Ignoring CPU load: Software PWM can consume CPU cycles heavily, affecting other tasks.

Better approach is to use hardware timers or interrupts if available.

c
/* Wrong approach: blocking delay in main loop */
while(1) {
  set_pin_high();
  delay_ms(500); // blocks CPU
  set_pin_low();
  delay_ms(500); // blocks CPU
}

/* Better approach: use timer interrupt to toggle pin without blocking */
// Pseudocode for timer ISR
void timer_ISR(void) {
  static bool pin_state = false;
  if(pin_state) {
    set_pin_low();
    set_timer_period(off_time);
  } else {
    set_pin_high();
    set_timer_period(on_time);
  }
  pin_state = !pin_state;
}
📊

Quick Reference

Tips for software PWM in Embedded C:

  • Calculate on_time and off_time from desired duty cycle and period.
  • Use hardware timers or interrupts for better accuracy and CPU efficiency.
  • Keep delay functions calibrated for your CPU clock speed.
  • Test PWM output with an oscilloscope or logic analyzer for correct timing.

Key Takeaways

Software PWM toggles a pin on and off with delays to simulate analog output.
Duty cycle controls how long the pin stays HIGH in each PWM period.
Blocking delays reduce CPU availability; use timers or interrupts for better design.
Calibrate delay loops carefully to maintain accurate PWM frequency.
Test output timing with hardware tools to ensure correct PWM signals.