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.