Generate Variable Frequency PWM in Embedded C: Simple Guide
To generate variable frequency
PWM in Embedded C, configure a hardware timer to toggle an output pin at the desired frequency by adjusting the timer's period register. Change the timer period dynamically in your code to vary the PWM frequency while keeping the duty cycle controlled by the compare register.Syntax
To generate PWM, you typically use a timer peripheral with these key registers:
- Timer Period Register (e.g.,
PRx): Sets the PWM period, controlling frequency. - Timer Compare Register (e.g.,
OCxRS): Sets the duty cycle by defining the high time. - Timer Control Register (e.g.,
TCON): Starts/stops the timer and sets mode.
Adjusting PRx changes frequency; adjusting OCxRS changes duty cycle.
c
void setup_pwm(unsigned int period, unsigned int duty_cycle) { PRx = period; // Set PWM period OCxRS = duty_cycle; // Set PWM duty cycle TCONbits.TON = 1; // Start timer }
Example
This example shows how to generate a PWM signal with variable frequency by changing the timer period in a loop. It assumes a microcontroller with a 16-bit timer and output compare module.
c
#include <xc.h> #define _XTAL_FREQ 8000000 // 8 MHz clock void setup_pwm(unsigned int period, unsigned int duty_cycle) { PR2 = period; // Timer2 period register CCPR1L = duty_cycle >> 2; // Duty cycle high bits CCP1CONbits.DC1B = duty_cycle & 0x3; // Duty cycle low bits T2CON = 0x01; // Timer2 on, prescaler 1:1 CCP1CONbits.CCP1M = 0xC; // PWM mode } int main() { TRISCbits.TRISC2 = 0; // Set CCP1 pin as output unsigned int freq_periods[] = {124, 249, 499, 999}; // Different periods for freq unsigned int duty = 312; // 25% duty cycle (assuming 1250 max) int i = 0; while(1) { setup_pwm(freq_periods[i], duty); __delay_ms(1000); // Wait 1 second i = (i + 1) % 4; // Cycle through frequencies } return 0; }
Output
No console output; PWM signal frequency changes every second on output pin.
Common Pitfalls
- Not configuring timer prescaler: This can cause incorrect frequency.
- Ignoring clock frequency: Timer period depends on clock speed; wrong assumptions cause wrong PWM frequency.
- Duty cycle exceeding period: Setting duty cycle higher than period causes no PWM or stuck output.
- Not enabling PWM mode: Forgetting to set output compare mode disables PWM output.
c
/* Wrong: Duty cycle > period */ PR2 = 499; CCPR1L = 600; // Wrong: 600 > 499 /* Right: Duty cycle <= period */ PR2 = 499; CCPR1L = 250; // Correct duty cycle
Quick Reference
Tips for variable frequency PWM:
- Calculate timer period from desired frequency:
Period = (F_CPU / (Prescaler * Frequency)) - 1 - Adjust duty cycle as a fraction of period.
- Use hardware timers and output compare modules for stable PWM.
- Update period register to change frequency on the fly.
Key Takeaways
Configure timer period register to set PWM frequency.
Adjust compare register to control PWM duty cycle.
Always consider clock speed and prescaler for correct timing.
Ensure duty cycle does not exceed period to avoid errors.
Use hardware timers for precise and stable PWM signals.