2026-01-11 10:54 AM
Hello everyone, I am trying to implement the 6-Step Trapezoidal Control using the COMG event in STM32F411RE as said in the datasheet.
Situation:
I am using 3 half bridges each driven by a single IR2110, IR2110 requires only one input signal to be high at a time to drive each half bridge as planned. I am using TIM1 PWM mode CH1-3 for HINs and CH1N-3N for LINs of each half bridge's IR2110. So in theory, for example, for first commutation, I only need CH1 & CH3N to be active. But as far as my understanding goes, if CH1 is active, I will be getting a complementary signal at CH1N as well. If IR2110 receives signal at HIN and LIN, it might cause overshoot for MOSFETs. There is a dead-time between both signals but, the point is I don't need the signal from CH1N so in my understanding the best way is to set the CC1NE bit to 0.
Problem : When I set CC1NE bit to 0, MCU still outputs the complementary signal although it shouldn't. I suspect this because I set the OSSR bit in BDTR to 1 - which might be keeping the complementary channel active.
The above is all my understanding and analysis. I would love to be corrected. I am pasting my code below.
What am I doing wrong? or it's more about my concept and approach of enabling/disabling channels is wrong? I am not sure how else it could be done. I just don't want the complementary signal to be there when the main channel is active. Out of 6 channels of TIM1, CH1,CH1N,CH2,CH2N,CH3,CH3N, I only want 2 of them active at one time and they must not be from the same channel.
timer.c
void TIM1_PWM(void){
---------------------- Reset TIM1 ----------------------
RCC->APB2RSTR |= RCC_APB2RSTR_TIM1RST;
RCC->APB2RSTR &= ~RCC_APB2RSTR_TIM1RST;
// ---------------------- TIMER BASE CONFIGURATION ----------------------
TIM1->PSC = 0; // Prescaler = 0 -> Timer clock = 16 MHz (no division)
TIM1->ARR = 800 - 1; // Auto Reload = 799 -> 16 MHz / 800 = 20 kHz PWM frequency
TIM1->CNT = 0; // Clear counter to start from 0
---------------------- CHANNEL 1 CONFIGURATION ----------------------
TIM1->CCMR1 &= ~(0b111 << 4); // Clear bits [6:4] to ensure OC1M is empty
TIM1->CCMR1 |= (0b110 << 4); // Set OC1M = 110 (PWM mode 1)
TIM1->CCMR1 |= (1U << 3); // Set OC1PE = 1 (Preload enable)
TIM1->CCER &= ~ (1U << 1); // Clear CC1P = 0 -> Active High
TIM1->CCER |= (1U << 3); // CC1NP = 1 -> Complementary output active low
---------------------- CHANNEL 2 CONFIGURATION ----------------------
TIM1->CCMR1 &= ~(0b111 << 12); // Clear bits [14:12]
TIM1->CCMR1 |= (0b110 << 12); // Set OC2M = 110 (PWM mode 1)
TIM1->CCMR1 |= (1U << 11); // Set OC2PE = 1 (Preload enable)
TIM1->CCER &= ~(1U << 5); // Clear CC2P = 0 -> Active High
TIM1->CCER |= (1U << 7); // CC2NP = 1 -> Complementary output active low
---------------------- CHANNEL 3 CONFIGURATION ------------------------
TIM1->CCMR2 &= ~(0b111 << 4); // Clear bits [6:4]
TIM1->CCMR2 |= (0b110 << 4); // Set OC3M = 110 (PWM mode 1)
TIM1->CCMR2 |= (1U << 3); // Set OC3PE = 1 (Preload enable)
TIM1->CCER &= ~(1U << 9); // Clear CC3P = 0 -> Active High
TIM1->CCER |= (1U << 11); // CC3NP = 1 -> Complementary output active low
---------------------- BDTR CONFIGURATION ----------------------
TIM1->BDTR = 0;
TIM1->BDTR |= (1U << 15); --Set MOE = 1 (Output signals enabled)
TIM1->BDTR |= (1U << 11); --Set OSSR = 1 (Off-State Selection for Run mode)
TIM1->BDTR |= (0x20 << 0); -- 2µs dead time for 16MHz
// ---------------------- CR1 OPTIONAL SETTINGS ----------------------
TIM1->CR1 &=~ (0b11<<5); // CMS = 00
TIM1->CR1 &=~ (1U<<4);
// ---------------------- CR2 OPTIONAL SETTINGS ----------------------
TIM1->CR2 &= ~(0x3F << 8); // Clear OIS bits [13:8] for CH1–CH3
TIM1->CR2 &= ~ (1U << 8); // OIS1 = 0 -> CH1 idle low
TIM1->CR2 &= ~(1U << 9); // OIS1N = 0 -> CH1N idle low
TIM1->CR2 &= ~(1U << 10); // OIS2 = 0 -> CH2 idle low
TIM1->CR2 &= ~(1U << 11); // OIS2N = 0 -> CH2N idle low
TIM1->CR2 &= ~(1U << 12); // OIS3 = 0 -> CH3 idle low
TIM1->CR2 &= ~(1U << 13); // OIS3N = 0 -> CH3N idle low
TIM1->CR2 |= (1U << 2); // CCUS = 1 -> capture/compare control bits are preloaded
TIM1->CR2 |= (1U << 0); // CCPC = 1 -> CCxE, CCxNE and OCxM bits are preloaded
// ---------------------- SET DUTY CYCLES ----------------------
TIM1->CCR1 = 200; // 20% Duty Cycle for Channel 1
TIM1->CCR2 = 200; // 20% Duty Cycle for Channel 2
TIM1->CCR3 = 200; // 20% Duty Cycle for Channel 3
// ---------------------- FINAL ENABLE SEQUENCE ----------------------
TIM1->CR1 |= (1U << 7); // ARPE = 1 -> Auto-Reload Preload Enable
TIM1->EGR = TIM_EGR_UG; // Generate update to load preload registers
TIM1->CR1 |= (1U << 0); // CEN = 1 -> Enable Counter, start PWM generation
TIM1->SR &= ~TIM_SR_UIF; // Clear the UG Flag in the SR
}
motor.c
#define Phase_A_High (TIM_CCER_CC1E)
#define Phase_A_Low (TIM_CCER_CC1NE)
#define Phase_B_High (TIM_CCER_CC2E)
#define Phase_B_Low (TIM_CCER_CC2NE)
#define Phase_C_High (TIM_CCER_CC3E)
#define Phase_C_Low (TIM_CCER_CC3NE)
#define CCER_ENABLE_MASK (Phase_A_High | Phase_A_Low | Phase_B_High | Phase_B_Low | Phase_C_High | Phase_C_Low)
static uint8_t step = 0;
static Step_Configuration config_table[6] = {
// 0. A+, B-
{
.ccer = (Phase_A_High | Phase_B_Low),
//.ccr = {200,200,0},
},
// 1. A+, C-
{
.ccer = Phase_A_High | Phase_C_Low,
//.ccr = {200,0,200},
},
// 2. B+, C-
{
.ccer = Phase_B_High | Phase_C_Low,
//.ccr = {0,200,200},
},
// 3. B+, A-
{
.ccer = Phase_B_High | Phase_A_Low,
//.ccr = {200,200,0},
},
// 4. C+, A-
{
.ccer = Phase_C_High | Phase_A_Low,
//.ccr = {200,0,200},
},
// 5. C+, B-
{
.ccer = Phase_C_High | Phase_B_Low,
//.ccr = {0,200,200},
},
};
void commutation(void){
//Step_Configuration* config = &config_table[step];
TIM1->CCER |= (TIM1->CCER &~ CCER_ENABLE_MASK) | config_table[step].ccer;
// TIM1->CCR1 = config->ccr[0];
// TIM1->CCR2 = config->ccr[1];
// TIM1->CCR3 = config->ccr[2];
TIM1->EGR = TIM_EGR_COMG;
delay_us(10);
step++;
if(step >= 6){ // this is for testing only
step = 4;
}
}
main.c
..... code ....
TIM1_PWM();
while(1){
commutation();
delay_us(100);
}