S5D9 PWM Deadtime

Can anyone help me figure out how to configure the PWM deadtime. I have not had any luck. Phase B waveform always comes out incorrect. I must be doing something wrong. Below is a scope shot of the PWM as expexted without deadtime.

Top trace is timer 3 overflow ISR. Yellow and Red traces are T3 CHA and CHB respectively. Blue and Green traces are T2 CHA and CHB respectively. I need to configure deadtime between CHA and CHB for each timer. I am having issues getting the deadtime to work correctly.

Any help would be appreciated.

void hardware::initInverterPwm( void)
{
    const uint32_t period = ( 120000000 / 60) / 2 ;
    const uint32_t alpha  = uint32_t( float( period) * 0.10) ;

    gpt_instance_ctrl_t *gptCtrl ;
    R_GPTA0_Type *gptRegs ;

    g_timer2.p_api->open( g_timer2.p_ctrl, g_timer2.p_cfg) ;
    g_timer3.p_api->open( g_timer3.p_ctrl, g_timer3.p_cfg) ;

    //=============================================================================
    // config timer 3
    //=============================================================================
    gptCtrl = ( gpt_instance_ctrl_t *)g_timer3.p_ctrl ;
    gptRegs = ( R_GPTA0_Type *)gptCtrl->p_reg ;

    for ( uint8_t i = 0; i < 2; i++)
    {
        gptRegs->GTWP = 0x0000A500 ;                // enable access to timer registers
        gptRegs->GTCR_b.MD = 1 ;                    // sawtooth pwm mode - oneshot
        gptRegs->GTCR_b.TPCS  = 0 ;                 // pclkd / 1
        gptRegs->GTPR = period - 1 ;                // period register
        gptRegs->GTCNT = i * 2 * alpha ;            // init counter value - T3 = 0 and T2 = 2*alpha
        gptRegs->GTIOR_b.GTIOA = 3 ;
        gptRegs->GTIOR_b.GTIOB = 19 ;

        // configure this for deadtime - this is the problem
        gptRegs->GTDTCR_b.TDE = 0 ;                 // Use GTDVU and GTDVD to set the compare match value for negative-phase waveform with dead time automatically in GTCCRB
        gptRegs->GTDTCR_b.TDBUE = 0 ;               // GTDVU Buffer Operation Enable
        gptRegs->GTDTCR_b.TDBDE = 0 ;               // GTDVD Buffer Operation Enable
        gptRegs->GTDTCR_b.TDFER = 0 ;               // Automatically set the value written to GTDVU to GTDVD

        gptRegs->GTDBU = 120 ;                      // sets deadtime buffer to 1us
        gptRegs->GTDBD = 120 ;                      // sets deadtime buffer to 1us

        gptRegs->GTCCRC = alpha ;                   // GTCCRC to GTCCRA at the cycle end
        gptRegs->GTCCRE = alpha ;                   // GTCCRE to GTCCRB at the cycle end

        gptRegs->GTCCRD = alpha ;                   // GTCCRD to temporary register A at the cycle end
        gptRegs->GTCCRF = alpha ;                   // GTCCRF to temporary register B at the cycle end
                                                    // Temporary register A to GTCCRA at a GTCCRA compare match
                                                    // Temporary register B to GTCCRB at a GTCCRB compare match.

        gptRegs->GTSSR_b.CSTRT = 1 ;                // enable software start

        //=============================================================================
        // config timer 2
        //=============================================================================
        gptCtrl = ( gpt_instance_ctrl_t *)g_timer2.p_ctrl ;
        gptRegs = ( R_GPTA0_Type *)gptCtrl->p_reg ;
    }

    gptRegs->GTSTR = ( 1 << 3) + ( 1 << 2) ;        // start timer 2 and 3 at the same time
}
  • I'll be honest I've never got PWM one shot mode working.  It seems to be a very complicated PWM mode master!

    Please have a look on the media gallery where I've uploaded "Triangle-wave PWM mode 1.zip"

    It was written & tested for a S7G2-SK, but it will be easily ported to the S5D9 as the GPT is the same across S7 & S5 families.

    The project demonstrates Triangle-wave PWM mode 1.

    When running, the PWM duty values are calculated / updated in either the GPT overflow interrupt, or in the while(1) processing loop. This can be selected by the #define in the source file.

    The example generates continuously variable PWM suitable for a sine-wave output (can be seen when filtered)

    Carrier frequency is 20kHz

    The cdoe only generates PWM on a single channel (GPT0),  but the code can be duplicated for additional channels as required by yourself.

    I hope this helps

    Regards,

    Richard

     

  • In reply to Richard:

    Richard,

    Thank you for the response. I agree, it is very complicated. I was able to get it to work using triangle pwm, however for my application I was thinking it would be better to use saw-wave one-shot pulse mode. I will also check out the project you posted.

    Thanks,
    Jeff
  • In reply to Richard:

    Hi Jeff,

    I tested automatic dead-time setup using saw-wave one-shot mode and managed to get it to work. One "gotcha" is that you cannot use the bit-field to set GTDTCR.TDE bit to 1 - the compiler will emit 16-bit write instruction which will leave this register unmodified. Performing GTDTCR = 1UL; addresses this issue. Here's the code snippet to configure timer with all of the required settings:

     R_GPTA0_Type * t2 = (R_GPTA0_Type *) ((gpt_instance_ctrl_t *) tmr2.p_ctrl)->p_reg;

    t2->GTWP = GPT_GTWP_KEY;

    t2->GTCR_b.MD = 0b001; // Set saw-wave one-shot mode
    t2->GTUDDTYC_b.UD = 0b1; // Count up

    t2->GTCR_b.TPCS = 0b000; // Set clock source to PCLKD/1

    t2->GTPR_b.GTPR = (uint32_t) (period - 1); // Set maximum count value

    t2->GTIOR_b.GTIOA = 0b00011; // 4 Initial output low
    // 3:2 Retain output at cycle end
    // 1:0 Toggle output at Compare Match A

    t2->GTIOR_b.GTIOB = 0b10011; // 4 Initial output high
    // 3:2 Retain output at cycle end
    // 1:0 Toggle output at Compare Match B

    t2->GTIOR_b.OAE = 0b1; // Enable pin GTIOCA
    t2->GTIOR_b.OBE = 0b1; // Enable pin GTIOCB

    #if APP_TIMER_FORCE_SYMMETRY
    uint32_t rise = offset + (APP_TIMER_DT_CYCLES / 2);
    uint32_t fall = offset + ((period - APP_TIMER_DT_CYCLES) / 2);

    t2->GTCCRC_b.GTCCRC = rise; // Set buffer for GTCCRA
    t2->GTCCRD_b.GTCCRD = fall; // Set buffer for GTCCRA
    #else
    t2->GTCCRC_b.GTCCRC = offset; // Set buffer for GTCCRA
    t2->GTCCRD_b.GTCCRD = offset + (period / 2); // Set buffer for GTCCRA
    #endif

    t2->GTBER_b.CCRSWT = 0b1; // Force buffer transfers now

    t2->GTDTCR = 1UL; // Enable automatic dead-time adjustment in GTCCRB

    t2->GTDVU_b.GTDVU = APP_TIMER_DT_CYCLES; // Dead time first half
    t2->GTDVD_b.GTDVD = APP_TIMER_DT_CYCLES; // Dead time second half

    t2->GTSSR_b.CSTRT = 0b1; // Enable sofware start by GTSTR
    t2->GTPSR_b.CSTOP = 0b1; // Enable sofware stop by GTSTP
    t2->GTCSR_b.CCLR = 0b1; // Enable sofware stop by GTCLR

    t2->GTSTR |= ((1 << 2); // start the timer

    When APP_TIMER_FORCE_SYMMETRY is set to non-zero value, GTCCRA value is compensated to maintain equal duty cycle between outputs A and B

    Regards