The "Holy Bible" for embedded engineers
Mastering PWM for Embedded Systems
PWM generation, frequency control, duty cycle, and practical applications
PWM is timer compare hardware. Your choice of period (ARR) and clock prescaler sets frequency and duty resolution. Match these to actuator needs and EMC constraints.
// Set PWM to 20 kHz with 10-bit resolution if clock allows
void pwm_init(void){ /* set PSC/ARR; configure CCx mode; enable output */ }
void pwm_set_duty(uint16_t duty){ /* write CCRx, clamp to ARR */ }
PWM Signal Parameters
┌─────────────────────────────────────────────────────────────┐
│ PWM Signal Analysis │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ High Frequency PWM │ │
│ │ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ │ │
│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
│ │ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ │ │
│ │ │<->│ Period │<->│ Period │<->│ Period │<->│ │ │
│ │ │<->│ Duty │<->│ Duty │<->│ Duty │<->│ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Low Frequency PWM │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────┐ │ │
│ │ │ │ │ │ │ │ │ │
│ │ │ │ │ │ │ │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────┘ │ │
│ │ │<---------->│ Period │<---------->│ Period │<->│ │ │
│ │ │<---------->│ Duty │<---------->│ Duty │<->│ │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
Duty Cycle vs Power Output
┌─────────────────────────────────────────────────────────────┐
│ Duty Cycle Control │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 25% Duty │ │ 50% Duty │ │ 75% Duty │ │
│ │ ┌─┐ ┌─┐ │ │ ┌───┐ ┌───┐│ │ ┌─────┐ ┌─┐│ │
│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ││ │
│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ││ │
│ │ └─┘ └─┘ │ │ └───┘ └───┘│ │ └─────┘ └─┘│ │
│ │ │<->│ │ │ │<--->│<--->│ │ │<----->│<->│ │
│ │ │ │ │ │ │ │ │ │ │ │ │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Low │ │ Medium │ │ High │ │
│ │ Power │ │ Power │ │ Power │ │
│ │ (25% Avg) │ │ (50% Avg) │ │ (75% Avg) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘
PWM to Analog Conversion
┌─────────────────────────────────────────────────────────────┐
│ PWM Filtering Process │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Raw PWM Signal │ │
│ │ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ │ │
│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
│ │ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ RC Low-Pass Filter │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ R │ │ C │ │ │ │ │
│ │ │ │ │ │ │ │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Filtered Analog Output │ │
│ │ ┌─────────────────────────────────────────────┐ │ │
│ │ │ │ │ │
│ │ │ │ │ │
│ │ │ │ │ │
│ │ │ │ │ │
│ │ └─────────────────────────────────────────────┘ │ │
│ │ │<---------->│ Average Value │<---------->│ │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
Pulse Width Modulation represents a fundamental technique for digital control of analog systems. By rapidly switching a digital signal between high and low states, PWM creates an effective analog output whose average value is proportional to the duty cycle.
Key Characteristics:
PWM is essential for modern embedded systems:
Designing effective PWM systems involves balancing multiple competing requirements:
// PWM parameters structure
typedef struct {
uint32_t frequency; // PWM frequency in Hz
uint32_t period; // Timer period value
uint32_t prescaler; // Timer prescaler value
uint16_t duty_cycle; // Duty cycle (0-100)
uint16_t resolution; // PWM resolution in bits
} PWM_Config_t;
// Calculate PWM parameters
void pwm_calculate_parameters(PWM_Config_t* config, uint32_t clock_freq, uint32_t target_freq) {
// Calculate prescaler and period for target frequency
uint32_t psc = 1;
uint32_t arr = clock_freq / target_freq;
// Adjust prescaler if period is too large
while (arr > 65535 && psc < 65535) {
psc++;
arr = (clock_freq / psc) / target_freq;
}
config->frequency = target_freq;
config->period = arr - 1;
config->prescaler = psc - 1;
config->resolution = 16; // Assuming 16-bit timer
}
// Convert duty cycle percentage to timer compare value
uint16_t duty_cycle_to_compare(uint16_t duty_cycle, uint16_t period) {
return (uint16_t)((duty_cycle * period) / 100);
}
// Convert timer compare value to duty cycle percentage
uint16_t compare_to_duty_cycle(uint16_t compare_value, uint16_t period) {
return (uint16_t)((compare_value * 100) / period);
}
// Set PWM duty cycle
void pwm_set_duty_cycle(TIM_HandleTypeDef* htim, uint32_t channel, uint16_t duty_cycle) {
uint16_t compare_value = duty_cycle_to_compare(duty_cycle, htim->Init.Period);
__HAL_TIM_SET_COMPARE(htim, channel, compare_value);
}
// Configure timer for PWM output
void pwm_timer_config(TIM_HandleTypeDef* htim, PWM_Config_t* config) {
TIM_OC_InitTypeDef sConfigOC = {0};
// Configure timer base
htim->Instance = TIM2;
htim->Init.Prescaler = config->prescaler;
htim->Init.CounterMode = TIM_COUNTERMODE_UP;
htim->Init.Period = config->period;
htim->Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim->Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
HAL_TIM_Base_Init(htim);
// Configure PWM channel
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 0;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
HAL_TIM_PWM_ConfigChannel(htim, &sConfigOC, TIM_CHANNEL_1);
// Start PWM
HAL_TIM_PWM_Start(htim, TIM_CHANNEL_1);
}
// Configure multiple PWM channels
typedef struct {
TIM_HandleTypeDef* htim;
uint32_t channels[4];
uint8_t channel_count;
PWM_Config_t config;
} MultiChannelPWM_t;
void multi_channel_pwm_init(MultiChannelPWM_t* mpwm, TIM_HandleTypeDef* htim,
uint32_t* channels, uint8_t count, uint32_t frequency) {
mpwm->htim = htim;
mpwm->channel_count = count;
for (int i = 0; i < count; i++) {
mpwm->channels[i] = channels[i];
}
// Calculate PWM parameters
pwm_calculate_parameters(&mpwm->config, SystemCoreClock, frequency);
// Configure timer base
htim->Instance = TIM2;
htim->Init.Prescaler = mpwm->config.prescaler;
htim->Init.CounterMode = TIM_COUNTERMODE_UP;
htim->Init.Period = mpwm->config.period;
htim->Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim->Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
HAL_TIM_Base_Init(htim);
// Configure each channel
TIM_OC_InitTypeDef sConfigOC = {0};
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 0;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
for (int i = 0; i < count; i++) {
HAL_TIM_PWM_ConfigChannel(htim, &sConfigOC, channels[i]);
HAL_TIM_PWM_Start(htim, channels[i]);
}
}
// Configure PWM with update interrupt
void pwm_with_interrupt_config(TIM_HandleTypeDef* htim, PWM_Config_t* config) {
// Configure timer base
htim->Instance = TIM2;
htim->Init.Prescaler = config->prescaler;
htim->Init.CounterMode = TIM_COUNTERMODE_UP;
htim->Init.Period = config->period;
htim->Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim->Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
HAL_TIM_Base_Init(htim);
// Configure PWM channel
TIM_OC_InitTypeDef sConfigOC = {0};
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 0;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
HAL_TIM_PWM_ConfigChannel(htim, &sConfigOC, TIM_CHANNEL_1);
// Enable update interrupt
__HAL_TIM_ENABLE_IT(htim, TIM_IT_UPDATE);
HAL_NVIC_SetPriority(TIM2_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(TIM2_IRQn);
// Start PWM
HAL_TIM_PWM_Start(htim, TIM_CHANNEL_1);
}
// PWM interrupt handler
void TIM2_IRQHandler(void) {
if (__HAL_TIM_GET_FLAG(&htim2, TIM_FLAG_UPDATE) != RESET) {
if (__HAL_TIM_GET_IT_SOURCE(&htim2, TIM_IT_UPDATE) != RESET) {
__HAL_TIM_CLEAR_IT(&htim2, TIM_IT_UPDATE);
// Handle PWM update
pwm_update_callback();
}
}
}
// Smooth duty cycle transition
typedef struct {
TIM_HandleTypeDef* htim;
uint32_t channel;
uint16_t current_duty;
uint16_t target_duty;
uint16_t step_size;
uint32_t transition_time;
uint32_t last_update_time;
} PWM_Transition_t;
void pwm_transition_init(PWM_Transition_t* transition, TIM_HandleTypeDef* htim,
uint32_t channel, uint16_t step_size, uint32_t transition_time_ms) {
transition->htim = htim;
transition->channel = channel;
transition->current_duty = 0;
transition->target_duty = 0;
transition->step_size = step_size;
transition->transition_time = transition_time_ms;
transition->last_update_time = 0;
}
void pwm_transition_set_target(PWM_Transition_t* transition, uint16_t target_duty) {
transition->target_duty = target_duty;
}
void pwm_transition_update(PWM_Transition_t* transition) {
uint32_t current_time = HAL_GetTick();
if (current_time - transition->last_update_time >= transition->transition_time) {
if (transition->current_duty < transition->target_duty) {
transition->current_duty += transition->step_size;
if (transition->current_duty > transition->target_duty) {
transition->current_duty = transition->target_duty;
}
} else if (transition->current_duty > transition->target_duty) {
transition->current_duty -= transition->step_size;
if (transition->current_duty < transition->target_duty) {
transition->current_duty = transition->target_duty;
}
}
pwm_set_duty_cycle(transition->htim, transition->channel, transition->current_duty);
transition->last_update_time = current_time;
}
}
// Duty cycle ramping
typedef struct {
TIM_HandleTypeDef* htim;
uint32_t channel;
uint16_t start_duty;
uint16_t end_duty;
uint16_t current_duty;
uint16_t step_size;
uint32_t ramp_time;
uint32_t step_time;
uint32_t last_step_time;
uint8_t ramping;
} PWM_Ramp_t;
void pwm_ramp_init(PWM_Ramp_t* ramp, TIM_HandleTypeDef* htim, uint32_t channel,
uint16_t step_size, uint32_t ramp_time_ms) {
ramp->htim = htim;
ramp->channel = channel;
ramp->step_size = step_size;
ramp->ramp_time = ramp_time_ms;
ramp->ramping = 0;
ramp->current_duty = 0;
}
void pwm_ramp_start(PWM_Ramp_t* ramp, uint16_t start_duty, uint16_t end_duty) {
ramp->start_duty = start_duty;
ramp->end_duty = end_duty;
ramp->current_duty = start_duty;
ramp->ramping = 1;
ramp->last_step_time = HAL_GetTick();
// Calculate step time
uint16_t total_steps = abs(end_duty - start_duty) / ramp->step_size;
ramp->step_time = ramp->ramp_time / total_steps;
// Set initial duty cycle
pwm_set_duty_cycle(ramp->htim, ramp->channel, ramp->current_duty);
}
void pwm_ramp_update(PWM_Ramp_t* ramp) {
if (!ramp->ramping) return;
uint32_t current_time = HAL_GetTick();
if (current_time - ramp->last_step_time >= ramp->step_time) {
if (ramp->current_duty < ramp->end_duty) {
ramp->current_duty += ramp->step_size;
if (ramp->current_duty > ramp->end_duty) {
ramp->current_duty = ramp->end_duty;
ramp->ramping = 0;
}
} else if (ramp->current_duty > ramp->end_duty) {
ramp->current_duty -= ramp->step_size;
if (ramp->current_duty < ramp->end_duty) {
ramp->current_duty = ramp->end_duty;
ramp->ramping = 0;
}
}
pwm_set_duty_cycle(ramp->htim, ramp->channel, ramp->current_duty);
ramp->last_step_time = current_time;
}
}
// Dynamic frequency control
typedef struct {
TIM_HandleTypeDef* htim;
uint32_t current_frequency;
uint32_t target_frequency;
uint32_t min_frequency;
uint32_t max_frequency;
} PWM_Frequency_Control_t;
void pwm_frequency_control_init(PWM_Frequency_Control_t* freq_ctrl, TIM_HandleTypeDef* htim,
uint32_t min_freq, uint32_t max_freq) {
freq_ctrl->htim = htim;
freq_ctrl->min_frequency = min_freq;
freq_ctrl->max_frequency = max_freq;
freq_ctrl->current_frequency = min_freq;
freq_ctrl->target_frequency = min_freq;
}
void pwm_set_frequency(PWM_Frequency_Control_t* freq_ctrl, uint32_t frequency) {
if (frequency < freq_ctrl->min_frequency) {
frequency = freq_ctrl->min_frequency;
} else if (frequency > freq_ctrl->max_frequency) {
frequency = freq_ctrl->max_frequency;
}
freq_ctrl->target_frequency = frequency;
// Calculate new timer parameters
PWM_Config_t config;
pwm_calculate_parameters(&config, SystemCoreClock, frequency);
// Update timer configuration
freq_ctrl->htim->Init.Prescaler = config.prescaler;
freq_ctrl->htim->Init.Period = config.period;
HAL_TIM_Base_Init(freq_ctrl->htim);
freq_ctrl->current_frequency = frequency;
}
// Frequency sweeping
typedef struct {
PWM_Frequency_Control_t* freq_ctrl;
uint32_t start_frequency;
uint32_t end_frequency;
uint32_t sweep_time;
uint32_t current_time;
uint8_t sweeping;
} PWM_Frequency_Sweep_t;
void pwm_frequency_sweep_init(PWM_Frequency_Sweep_t* sweep, PWM_Frequency_Control_t* freq_ctrl,
uint32_t sweep_time_ms) {
sweep->freq_ctrl = freq_ctrl;
sweep->sweep_time = sweep_time_ms;
sweep->sweeping = 0;
}
void pwm_frequency_sweep_start(PWM_Frequency_Sweep_t* sweep, uint32_t start_freq, uint32_t end_freq) {
sweep->start_frequency = start_freq;
sweep->end_frequency = end_freq;
sweep->current_time = 0;
sweep->sweeping = 1;
// Set initial frequency
pwm_set_frequency(sweep->freq_ctrl, start_freq);
}
void pwm_frequency_sweep_update(PWM_Frequency_Sweep_t* sweep) {
if (!sweep->sweeping) return;
sweep->current_time += 10; // Update every 10ms
// Calculate current frequency
float progress = (float)sweep->current_time / sweep->sweep_time;
if (progress > 1.0f) progress = 1.0f;
uint32_t current_freq = sweep->start_frequency +
(uint32_t)(progress * (sweep->end_frequency - sweep->start_frequency));
pwm_set_frequency(sweep->freq_ctrl, current_freq);
if (progress >= 1.0f) {
sweep->sweeping = 0;
}
}
// LED dimming control
typedef struct {
TIM_HandleTypeDef* htim;
uint32_t channel;
uint16_t brightness;
uint8_t fade_direction;
uint16_t fade_speed;
} LED_Dimmer_t;
void led_dimmer_init(LED_Dimmer_t* dimmer, TIM_HandleTypeDef* htim, uint32_t channel) {
dimmer->htim = htim;
dimmer->channel = channel;
dimmer->brightness = 0;
dimmer->fade_direction = 0; // 0 = fade in, 1 = fade out
dimmer->fade_speed = 1;
}
void led_dimmer_set_brightness(LED_Dimmer_t* dimmer, uint16_t brightness) {
dimmer->brightness = brightness;
pwm_set_duty_cycle(dimmer->htim, dimmer->channel, brightness);
}
void led_dimmer_fade(LED_Dimmer_t* dimmer) {
if (dimmer->fade_direction == 0) {
// Fade in
dimmer->brightness += dimmer->fade_speed;
if (dimmer->brightness >= 100) {
dimmer->brightness = 100;
dimmer->fade_direction = 1;
}
} else {
// Fade out
dimmer->brightness -= dimmer->fade_speed;
if (dimmer->brightness <= 0) {
dimmer->brightness = 0;
dimmer->fade_direction = 0;
}
}
pwm_set_duty_cycle(dimmer->htim, dimmer->channel, dimmer->brightness);
}
// Motor speed control
typedef struct {
TIM_HandleTypeDef* htim;
uint32_t channel;
uint16_t speed;
uint16_t max_speed;
uint16_t min_speed;
uint8_t direction;
} Motor_Controller_t;
void motor_controller_init(Motor_Controller_t* motor, TIM_HandleTypeDef* htim, uint32_t channel,
uint16_t min_speed, uint16_t max_speed) {
motor->htim = htim;
motor->channel = channel;
motor->min_speed = min_speed;
motor->max_speed = max_speed;
motor->speed = 0;
motor->direction = 0;
}
void motor_set_speed(Motor_Controller_t* motor, uint16_t speed) {
if (speed > motor->max_speed) {
speed = motor->max_speed;
} else if (speed < motor->min_speed) {
speed = motor->min_speed;
}
motor->speed = speed;
pwm_set_duty_cycle(motor->htim, motor->channel, speed);
}
void motor_set_direction(Motor_Controller_t* motor, uint8_t direction) {
motor->direction = direction;
// Additional GPIO control for direction if needed
}
// Audio tone generation
typedef struct {
TIM_HandleTypeDef* htim;
uint32_t channel;
uint16_t frequency;
uint16_t volume;
uint8_t playing;
} Audio_Generator_t;
void audio_generator_init(Audio_Generator_t* audio, TIM_HandleTypeDef* htim, uint32_t channel) {
audio->htim = htim;
audio->channel = channel;
audio->frequency = 440; // A4 note
audio->volume = 50;
audio->playing = 0;
}
void audio_generator_play_tone(Audio_Generator_t* audio, uint16_t frequency, uint16_t volume) {
audio->frequency = frequency;
audio->volume = volume;
audio->playing = 1;
// Set frequency
PWM_Config_t config;
pwm_calculate_parameters(&config, SystemCoreClock, frequency);
audio->htim->Init.Prescaler = config.prescaler;
audio->htim->Init.Period = config.period;
HAL_TIM_Base_Init(audio->htim);
// Set volume (duty cycle)
pwm_set_duty_cycle(audio->htim, audio->channel, volume);
}
void audio_generator_stop(Audio_Generator_t* audio) {
audio->playing = 0;
pwm_set_duty_cycle(audio->htim, audio->channel, 0);
}
// Phase-shifted PWM for multi-phase systems
typedef struct {
TIM_HandleTypeDef* htim;
uint32_t channels[3];
uint16_t duty_cycle;
uint16_t phase_shift;
} Phase_Shifted_PWM_t;
void phase_shifted_pwm_init(Phase_Shifted_PWM_t* pspwm, TIM_HandleTypeDef* htim,
uint32_t* channels, uint16_t phase_shift) {
pspwm->htim = htim;
pspwm->phase_shift = phase_shift;
for (int i = 0; i < 3; i++) {
pspwm->channels[i] = channels[i];
}
}
void phase_shifted_pwm_set_duty(Phase_Shifted_PWM_t* pspwm, uint16_t duty_cycle) {
pspwm->duty_cycle = duty_cycle;
// Set duty cycle for each channel with phase shift
for (int i = 0; i < 3; i++) {
uint16_t phase_adjusted_duty = (duty_cycle + (i * pspwm->phase_shift)) % 100;
pwm_set_duty_cycle(pspwm->htim, pspwm->channels[i], phase_adjusted_duty);
}
}
// Dead time insertion for H-bridge control
typedef struct {
TIM_HandleTypeDef* htim;
uint32_t channel1;
uint32_t channel2;
uint16_t dead_time;
} Dead_Time_PWM_t;
void dead_time_pwm_init(Dead_Time_PWM_t* dtpwm, TIM_HandleTypeDef* htim,
uint32_t channel1, uint32_t channel2, uint16_t dead_time) {
dtpwm->htim = htim;
dtpwm->channel1 = channel1;
dtpwm->channel2 = channel2;
dtpwm->dead_time = dead_time;
// Configure dead time
__HAL_TIM_SET_AUTORELOAD(htim, htim->Init.Period);
__HAL_TIM_SET_COMPARE(htim, channel1, 0);
__HAL_TIM_SET_COMPARE(htim, channel2, dead_time);
}
// Buck converter control
typedef struct {
TIM_HandleTypeDef* htim;
uint32_t channel;
uint16_t duty_cycle;
float output_voltage;
float target_voltage;
float input_voltage;
} Buck_Converter_t;
void buck_converter_init(Buck_Converter_t* buck, TIM_HandleTypeDef* htim, uint32_t channel,
float input_voltage) {
buck->htim = htim;
buck->channel = channel;
buck->input_voltage = input_voltage;
buck->output_voltage = 0;
buck->target_voltage = 0;
buck->duty_cycle = 0;
}
void buck_converter_set_voltage(Buck_Converter_t* buck, float target_voltage) {
buck->target_voltage = target_voltage;
// Calculate duty cycle: Vout = Vin * Duty_Cycle
float duty_cycle = (target_voltage / buck->input_voltage) * 100.0f;
if (duty_cycle > 100.0f) duty_cycle = 100.0f;
if (duty_cycle < 0.0f) duty_cycle = 0.0f;
buck->duty_cycle = (uint16_t)duty_cycle;
pwm_set_duty_cycle(buck->htim, buck->channel, buck->duty_cycle);
}
// Servo motor control
typedef struct {
TIM_HandleTypeDef* htim;
uint32_t channel;
uint16_t position;
uint16_t min_position;
uint16_t max_position;
} Servo_Controller_t;
void servo_controller_init(Servo_Controller_t* servo, TIM_HandleTypeDef* htim, uint32_t channel) {
servo->htim = htim;
servo->channel = channel;
servo->min_position = 5; // 0.5ms pulse
servo->max_position = 25; // 2.5ms pulse
servo->position = 15; // Center position
}
void servo_set_position(Servo_Controller_t* servo, uint16_t position) {
if (position < servo->min_position) {
position = servo->min_position;
} else if (position > servo->max_position) {
position = servo->max_position;
}
servo->position = position;
pwm_set_duty_cycle(servo->htim, servo->channel, position);
}
// ❌ Wrong: Incorrect frequency calculation
void pwm_frequency_wrong(TIM_HandleTypeDef* htim, uint32_t frequency) {
htim->Init.Period = 1000 / frequency; // Wrong calculation
}
// ✅ Correct: Proper frequency calculation
void pwm_frequency_correct(TIM_HandleTypeDef* htim, uint32_t frequency) {
uint32_t clock_freq = SystemCoreClock;
uint32_t period = (clock_freq / frequency) - 1;
htim->Init.Period = period;
}
// ❌ Wrong: No dead time for H-bridge
void h_bridge_control_wrong(TIM_HandleTypeDef* htim) {
// Direct PWM control without dead time
}
// ✅ Correct: With dead time
void h_bridge_control_correct(TIM_HandleTypeDef* htim) {
// Configure dead time for safe switching
__HAL_TIM_SET_COMPARE(htim, TIM_CHANNEL_1, 0);
__HAL_TIM_SET_COMPARE(htim, TIM_CHANNEL_2, DEAD_TIME_VALUE);
}
// ❌ Wrong: Low resolution PWM
void pwm_low_resolution_wrong(TIM_HandleTypeDef* htim) {
htim->Init.Period = 100; // Only 100 steps
}
// ✅ Correct: High resolution PWM
void pwm_high_resolution_correct(TIM_HandleTypeDef* htim) {
htim->Init.Period = 4095; // 4096 steps (12-bit)
}
// Use appropriate frequencies for different applications
void configure_pwm_frequency(PWM_Config_t* config, uint8_t application_type) {
switch (application_type) {
case PWM_LED_DIMMING:
config->frequency = 1000; // 1kHz for LED dimming
break;
case PWM_MOTOR_CONTROL:
config->frequency = 20000; // 20kHz for motor control
break;
case PWM_AUDIO:
config->frequency = 44100; // 44.1kHz for audio
break;
case PWM_POWER_SUPPLY:
config->frequency = 100000; // 100kHz for power supply
break;
}
}
// Always use smooth transitions for better performance
void smooth_pwm_transition(TIM_HandleTypeDef* htim, uint32_t channel,
uint16_t start_duty, uint16_t end_duty, uint16_t steps) {
uint16_t step_size = (end_duty - start_duty) / steps;
for (int i = 0; i < steps; i++) {
uint16_t current_duty = start_duty + (i * step_size);
pwm_set_duty_cycle(htim, channel, current_duty);
HAL_Delay(10); // Small delay for smooth transition
}
pwm_set_duty_cycle(htim, channel, end_duty);
}
// Monitor PWM performance and efficiency
typedef struct {
uint32_t frequency;
uint16_t duty_cycle;
float efficiency;
uint32_t switching_losses;
} PWM_Performance_t;
void pwm_performance_monitor(PWM_Performance_t* perf, TIM_HandleTypeDef* htim) {
// Calculate switching losses
perf->switching_losses = perf->frequency * perf->duty_cycle / 100;
// Calculate efficiency (simplified)
perf->efficiency = 100.0f - (perf->switching_losses / 1000.0f);
}
Next Topic: Timer/Counter Programming → External Interrupts