Skip to content

Instantly share code, notes, and snippets.

@fabiovila
Last active June 25, 2025 16:03
Show Gist options
  • Save fabiovila/77c544286e9b0260a5a09823b47ba39f to your computer and use it in GitHub Desktop.
Save fabiovila/77c544286e9b0260a5a09823b47ba39f to your computer and use it in GitHub Desktop.
How sync ESP32 Timers MCPWM?
// CHANGE the clock prescale in mcpwm.c file to make possible get 30000hz of pwm frequency (15Khz in center aligned mode)
#define MCPWM_BASE_CLK (2 * APB_CLK_FREQ) //2*APB_CLK_FREQ 160Mhz
#define MCPWM_CLK_PRESCL 1 //MCPWM clock prescale Original = 15 <---------------------
#define TIMER_CLK_PRESCALE 1 //MCPWM timer prescales Original = 9 <---------------------
#define MCPWM_CLK (MCPWM_BASE_CLK/(MCPWM_CLK_PRESCL +1))
#define MCPWM_PIN_IGNORE (-1)
#define OFFSET_FOR_GPIO_IDX_1 6
#define OFFSET_FOR_GPIO_IDX_2 75
mcpwm_config_t pwm_config;
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM0A, GPIO_PWM0A_OUT);
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM0B, GPIO_PWM0B_OUT);
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM1A, GPIO_PWM1A_OUT);
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM1B, GPIO_PWM1B_OUT);
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM2A, GPIO_PWM2A_OUT);
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM2B, GPIO_PWM2B_OUT);
Motor.TabelaLimite = 0;
// Center aligned pwm mode complementary mode
// 15Khz frequency
// 95% initial duty cibly
pwm_config.frequency = 30000; //frequency
pwm_config.cmpr_a = 95.0; // Duty em porcentagem
pwm_config.cmpr_b = pwm_config.cmpr_a;
pwm_config.counter_mode = MCPWM_UP_DOWN_COUNTER;
pwm_config.duty_mode = MCPWM_DUTY_MODE_0;
mcpwm_init(MCPWM_UNIT_0, MCPWM_TIMER_0, &pwm_config); //Configure PWM0A & PWM0B with above settings
mcpwm_init(MCPWM_UNIT_0, MCPWM_TIMER_1, &pwm_config); //Configure PWM1A & PWM1B with above settings
mcpwm_init(MCPWM_UNIT_0, MCPWM_TIMER_2, &pwm_config); //Configure PWM2A & PWM2B with above settings
// deadtime (see clock source changes in mcpwm.c file)
mcpwm_deadtime_enable(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_ACTIVE_HIGH_COMPLIMENT_MODE, 80, 80); // 1us deadtime
mcpwm_deadtime_enable(MCPWM_UNIT_0, MCPWM_TIMER_1, MCPWM_ACTIVE_HIGH_COMPLIMENT_MODE, 80, 80);
mcpwm_deadtime_enable(MCPWM_UNIT_0, MCPWM_TIMER_2, MCPWM_ACTIVE_HIGH_COMPLIMENT_MODE, 80, 80);
// TEZ interrupts (TIMERS in ZERO before ascending)
MCPWM[MCPWM_UNIT_0]->int_ena.val = BIT(3) | BIT(4) | BIT(5); // /*A PWM timer X TEZ event will trigger this interrupt*/
mcpwm_isr_register(MCPWM_UNIT_0, isr_handler, NULL, ESP_INTR_FLAG_IRAM, NULL); //Set ISR Handler
// ------ READ THIS
// mcpwm_sync_signal_t is defined only to SYNC0/1/2 that are for GPIO sync
// In order to sync Timer1 and Timer2 with Timer0
// Pass 1 to mcpwm_sync_signal_t sync_sig in mcpwm_sync_enable
// 1 is equal to "PWM timer0 sync_out" in PWM_TIMER_SYNCI_CFG_REG.PWM_TIMER1/2_SYNCISEL
// Phase (last parameter) is zero.
mcpwm_sync_enable(MCPWM_UNIT_0, MCPWM_TIMER_1, 1, 0);
mcpwm_sync_enable(MCPWM_UNIT_0, MCPWM_TIMER_2, 1, 0);
// Timer0 is our sync out. When it is equal to zero and before ascending sync out is triggered, so Timer1 and Timer2 stands in sync_in with Timer0.
// When Timer0 is zero Timer1 and Timer2 is forced to phase defined in last parameter in functions above (0).
// Make Timer0 sync out in TEZ
MCPWM[MCPWM_UNIT_0]->timer[MCPWM_TIMER_0].sync.out_sel = 1;
// So:
// [TIMER0](When in Zero)->[TIMER1=0]
// ->[TIMER2=0]
// Ref:
// esp32_technical_reference_manual_en.pdf around pages 439
// mcpwm.h
// mcpwm_struct.h
// try and error!!
@Marcarleto
Copy link

Is this in Arduino IDE?

@creatormir
Copy link

Marcarleto,
This is like the complete code of the project, it seems to be esp-idf: https://github.com/fabiovila/ESP32ACMotorInverter_Test/blob/master/main/motor.c

@Prithul0218
Copy link

@fabiovila

Hi. It seems like mcpwm.c is deprecated and now is replaced by mcpwm_prelude.c

Is there any chance you updated your code to be used with ESP-IDF 5.1? I'm struggling to generate 100kHz PWM while maintaining 8 bit resolution.

@JamshidJD
Copy link

JamshidJD commented Jun 22, 2023

I'm trying to use MCPWM_UP_DOWN_COUNTER
and my frequency range is 10K - 100K, i know i have to provide double the frequency to generate the center aligned output. but the factor of twice the frequency changes when i above 12K, for instance if i want to generate 100K i'm providing 200K as frequency but the output is 125K instead. I've also tried to make a loop to see the linearity but by decreasing the value 200K then next jumps comes at around 83K and so on...., this only happens in MCPWM_UP_DOWN_COUNTER if i am using the other mode its fine.
The code snippet is attached below, any help would be appreciated. thanks

mcpwm_config_t pwm_configA;
pwm_configA.frequency = 40000; // frequência = 500Hz,
pwm_configA.cmpr_a = 50; // Ciclo de trabalho (duty cycle) do PWMxA = 0
pwm_configA.cmpr_b = 50; // Ciclo de trabalho (duty cycle) do PWMxb = 0
pwm_configA.counter_mode = MCPWM_UP_DOWN_COUNTER; // Para MCPWM assimetrico
pwm_configA.duty_mode = MCPWM_DUTY_MODE_0; // Define ciclo de trabalho em nível alto

mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM0A, Pulse_Pin_1);
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM0B, Pulse_Pin_2);
mcpwm_init(MCPWM_UNIT_0, MCPWM_TIMER_0, &pwm_configA); // Define PWM0A & PWM0B com as configurações acima

int DutyCycle = 50;
mcpwm_set_duty(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_A, DutyCycle);
mcpwm_set_duty(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_B, 100 - DutyCycle); // Configura a porcentagem do PWM no Operador B (Ciclo de trabalho)
mcpwm_set_duty_type(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_A, MCPWM_DUTY_MODE_0);
mcpwm_set_duty_type(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_B, MCPWM_DUTY_MODE_1);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment