4.1 Bare Metal Implementation

  1. The Clock peripheral must run at a frequency of 20 MHz. The default Clock speed is 3.33 MHz and has, by default, a prescaler division of six. This code example needs to disable the prescaler and use the clock to maximum speed.
    _PROTECTED_WRITE(CLKCTRL.MCLKCTRLB, CLKCTRL.MCLKCTRLB & ~CLKCTRL_PEN_bm);
    Figure 4-1. MCLKCTRLB register of CLKCTRL
  2. The TCE and WEX corresponding register in Port Multiplexer can be set to route the modules outputs to different ports. In this case, Port A is chosen, which is also the default port.
    PORTMUX.TCEROUTEA = 0x0;
    Figure 4-2. PORTMUX Control for TCE and WEX
  3. The CTRLB register of TCE contains the Enable bits of the compare channels and the bit field that determines the Waveform Generation mode. In this example channels 0, 1, 2, and 3 are used with a Single-Slope PWM mode.
    TCE0.CTRLB |= (TCE_CMP0EN_bm | TCE_CMP1EN_bm | TCE_CMP2EN_bm | TCE_CMP3EN_bm);
    TCE0.CTRLB |= TCE_WGMODE_SINGLESLOPE_gc;
    Figure 4-3. CTRLB Register of TCE
  4. Set the DIR bit of the CTRLECLR register of TCE to ‘1’ to set the timer to count clock ticks down (decrementing). The default value of the DIR bit is ‘0’.
    TCE0.CTRLECLR = TCE_DIR_bm;
    Figure 4-4. CTRLECLR Register of TCE
  5. PER is the buffer of the Period register of TCE. It is used to set the frequency of the PWM signal using EQ5.1:
    f S S P W M ( H z ) = f C L K ( H z ) T C E p r e s c a l e r × ( T C E p e r i o d + 1 )

    Considering the targeted values for this example:

    T C E p e r i o d = f C L K ( H z ) T C E p r e s c a l e r × f S S P W M ( H z ) = 20000000 1 × 20000 1000 = 0 x 3 E 8
    /* the PER register is always set with the desired value -1, 1000 - 1 = 999, the desired value for a 50us period */
    TCE0.PER  = 0x3E7;
  6. The Compare registers are updated using its buffer to set the duty cycle. The values in the Compare registers can be set to have random duty cycles like: 20%, 40%, 60%, and 80%. However, these values are overridden at run-time by updating the CMP registers using their buffers after the first interrupt will occur. So, we can set these values at 0.
    TCE0.CMP0 = 0x00;
    TCE0.CMP1 = 0x00;
    TCE0.CMP2 = 0x00;
    TCE0.CMP3 = 0x00;
  7. After starting the timer, the duty cycles will increase every time a compare register’s value match the counter value. The duty cycles range will be between 0-100%. During the ISR, this happens for every compare channel. ISR routine for Compare 0 channel:
    ISR(TCE0_CMP0_vect)
    {
        /* clear the interrupt flag */
        TCE0.INTFLAGS = TCE_CMP0_bm;
        
        static uint16_t duty_cycle = 0;
        
        /* duty cycle update in interrupt */
        duty_cycle += 5;
        if(duty_cycle >= MAX_DUTY_CYCLE)
        duty_cycle = 0;
        TCE0.CMP0BUF = duty_cycle;
    }
  8. Enable interrupts on compare match for each of the compare registers of TCE.
    TCE0.INTCTRL = (TCE_CMP0_bm | TCE_CMP1_bm | TCE_CMP2_bm | TCE_CMP3_bm);
    Figure 4-5. INTCTRL Register of TCE
  9. Set the initial value for the TCE’s counter to 0.
    TCE0.CNT = 0x00;
  10. Set the prescaler to 1 by changing the CLKSEL bit field in the CTRLA register. To start the counter, the user has to set the Enable bit in the same register.
    TCE0.CTRLA = TCE_CLKSEL_DIV1_gc;
    The last step for configuring the TCE is to enable the module.
    TCE0.CTRLA |= TCE_ENABLE_bm;
    Figure 4-6. CTRLA Register of TCE
  11. Set the output routing matrix for the WEX in the Direct mode and enable dead-time insertion for each pair of two complementary signals. After making these settings, the four PWM signals generated from TCE divide into eight complementary signals by the WEX.
    WEX0.CTRLA = WEX_INMX_DIRECT_gc | WEX_DTI0EN_bm | WEX_DTI1EN_bm | 
                          WEX_DTI2EN_bm | WEX_DTI3EN_bm;
    Figure 4-7. CTRLA Register of WEX
  12. Set the desired values for dead-time using the dead-time registers. The dead-time is measured in peripheral clock ticks. The equation below explains how the dead-time is calculated:
    WEX0.DTLS = 0x03;
    WEX0.DTHS = 0x05;
    Figure 4-8. Dead time Registers of WEX: dead time Low-Side, High-Side and Both Sides

    The last register can be used if a symmetrical dead-time is desired for the application. In this example the low-side and high-side dead-time registers are used separately with an asymmetrical dead-time.

    Equations used to calculate the dead-time:

    EQ5.2

    c l o c k _ t i c k ( n s ) = 1 f C L K ( H z ) × T C E p r e s c a l e r × 10 9

    Converting clock ticks to nano seconds, for example:

    f c l k ( H z ) = 20 M H z
    T C E p r e s c a l e r = 4
    d e s i r e d _ d e a d _ t i m e ( n s ) = 500
    c l o c k _ t i c k ( n s ) = 1 20000000 × 4 × 10 9 = 250
    r e g i s t e r _ v a l u e = d e a d _ t i m e ( n s ) ÷ c l o c k _ t i c k ( n s ) = 500 ÷ 250 = 2
  13. Set the Fault Restart mode condition and fault signal driving pattern in FAULTCTRL Register.
     
    /* set fault restart mode to latched mode - fault is active as long as the fault condition is active */
    /* to restart from fault in latched mode the user must use a software fault clear command */
    WEX0.FAULTCTRL = WEX_FDMODE_LATCHED_gc;
    /* drive all pins to low '0' logic when a fault is detected */
    WEX0.FAULTCTRL |= WEX_FDACT_LOW_gc;
    Figure 4-9. FAULTCTRL Register of WEX
  14. Enable the event input A of WEX using EVCTRLA Register.
    WEX0.EVCTRLA = WEX_FAULTEI_bm;
    Figure 4-10. EVCTRLA Register of WEX
  15. Enable fault interrupt (fault detection) and clear the fault flags during the WEX’s ISR.
    WEX0.INTCTRL = WEX_FAULTDET_bm;
    /* WEX fault ISR */
    ISR(WEX0_FDFEVA_vect)
    {
        /* clear the interrupt flag and fault event flag */
        WEX0.INTFLAGS = WEX_FDFEVA_bm | WEX_FAULTDET_bm;
    }
    Figure 4-11. INTCTRL and INTFLAGS Registers of WEX
  16. Configure the EVENT SYSTEM (EVSYS) peripheral to generate events on Channel 0. Configure WEX to be the user of the event generator from Channel 0. Generate a fault using a software event. To stop the fault, turn off the fault generator channel from EVSYS and clear the fault state using a software command.
    /* set the WEX0 peripheral as the user of the event generator from channel 0, which is a software event in this example*/
    EVSYS.USERWEXA = EVSYS_USER_CHANNEL0_gc;
    /* software fault creation, repeat in main loop to see it on LogicA. This is an event generated using a software command */
    EVSYS.SWEVENTA = EVSYS_SWEVENTA_CH0_gc;
    /* clear fault condition using a software command */
    WEX0.CTRLC = WEX_CMD_FAULTCLR_gc;
  17. Enable WEX’s outputs in order to generate the eight PWM signals using the OUTOVEN Register.
    /* enables WEX's outputs */
    WEX0.OUTOVEN = WEX_OUTOVEN0_bm | WEX_OUTOVEN1_bm | WEX_OUTOVEN2_bm | WEX_OUTOVEN3_bm | 
                   WEX_OUTOVEN4_bm | WEX_OUTOVEN5_bm | WEX_OUTOVEN6_bm | WEX_OUTOVEN7_bm;
    Figure 4-12. OUTOVEN Register of WEX
  18. Then, the Port A Pins 0-7 (PA0-7) are set as output by writing a ‘1’ to the corresponding bit in the Direction register of the port.
    PORTA.DIRSET = PIN0_bm | PIN1_bm | PIN2_bm | PIN3_bm | 
                            PIN4_bm | PIN5_bm | PIN6_bm | PIN7_bm;
  19. After all the modules are configured, enable global interrupts.
    /* enable global interrupts */
        sei();