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.