10 Getting Started with TCD
10.1 Introduction
Author: Catalin Visan, Microchip Technology Inc. |
The AVR® microcontrollers are equipped with powerful timers designed to cover a wide area of applications, from signal measurement to events synchronization and waveform generation.
The Timer/Counter type D (TCD) is a 12-bit timer available in tinyAVR® 0- and 1-series, megaAVR® 0-series and AVR® Dx and AVR DA devices. The TCD is designed to cover the need for a fast timer with multiple waveform generation capabilities in common embedded applications, such as motor control or Switch mode power supplies. One of the key features of the TCD is that in addition to running from the main clock, it can be set up to run directly from the internal 20 MHz oscillator (OSC20M) or used with an external clock source. Moreover, it has multiple configurable Waveform Generation modes.
- Generating complementary driving signals:
Initialize the timer to generate two complementary signals with 50 kHz frequency and 100 ns dead-time.
- Controlling synchronous signals using input
events:
Initialize the timer to generate four PWM signals with 10 kHz frequency and approximately 50% duty cycle, synchronized in pairs. Configure an input channel for fault detection purposes.
10.2 Overview
The TCD core is asynchronous to the system clock. The timer/counter consist of two compare/capture units, each with a separate waveform output. In addition, there are two extra waveform outputs that can be equal to the output from one of the units. The compare registers CMPxSET and CMPxCLR are stored in the respective registers (TCD.CMPxSET, TCD.CMPxCLR), which consist of both a low and a high byte. The registers are synchronized to the TCD domain after writing to the registers. During normal operation, the counter value is continuously compared to the compare registers. This is used to generate both interrupts and events.
The TCD can use the input events in ten different input modes, selected separately for the two input events (see Figure 10-129). The input mode defines how the input event will affect the outputs.
10.3 Generating Complementary Driving Signals
- The maximum counter value is stored in the CMPBLCR register (see Figure 10-119)
- The WOA output is set when the TCD counter counts down and matches the CMPASET value
- WOA is cleared when the TCD counter counts up and matches the CMPASET value
- The WOB output is set when the TCD counter counts up and matches the CMPBSET value
- WOB is cleared when the TCD counter counts down and matches the CMPBSET value
-
The Waveform Generation mode will be set to dual slope using the CTRLB register.
TCD0.CTRLB = TCD_WGMODE_DS_gc;
-
The signal’s period must be deduced from the following formula and written to
the CMPBCLR register.
The peripheral clock will be set as the 20 MHz internal oscillator and the counter prescaler will be set to ‘
1
’.TCD0.CMPBCLR = 0xC8;
-
To set the dead-time, the counting period must be determined, as well as the
number of counting periods that match the dead-time.
TCD0.CMPBSET = 0x65; TCD0.CMPASET = 0x63;
-
Before enabling the timer, the ENRDY bit of the STATUS register must be verified to have the value
‘
1
’.The hardware sets this bit when it is safe to start the timer. This mechanism prevents synchronization issues between the TCD time domain and the system time domain.while(!(TCD0.STATUS & TCD_ENRDY_bm)) { ; }
-
After the ENRDY bit is set, the timer can be enabled from the CTRLA register.
From the same register, the clock source and the prescaler can also be set. All the bits from CTRLA except the ENABLE bit are enable-protected, they can only be written when ENABLE is set to ‘
0
’ before the writing operation.TCD0.CTRLA = TCD_CLKSEL_20MHZ_gc | TCD_CNTPRES_DIV1_gc | TCD_ENABLE_bm;
-
To enable the output channels, certain bits in the FAULTCTRL register may be set.
This register is under Configuration Change Protection (CCP). Thus, a key must be written in the CCP register of the CPU before writing to the FAULTCTRL register. In this case, only channels A and B will be enabled.
void TCD0_enableOutputChannels(void) { CPU_CCP = CCP_IOREG_gc; TCD0.FAULTCTRL = TCD_CMPAEN_bm | TCD_CMPBEN_bm; }
-
In the targeted microcontroller, the TCD output channels A and B are linked to
PA4 and PA5. These pins must be configured as outputs:
PORTA.DIR |= PIN4_bm | PIN5_bm;
An MCC generated code example for AVR128DA48, with the same functionality as the one described in this section, can be found here:
10.4 Controlling Synchronous Signals Using Input Events
In motor control applications, it is often necessary to use perfectly synchronized signals. Also, there is a narrow class of applications that, for driving purposes, requires just a little bit more current than a pin can provide. In these cases, the possibility to map the same signal on multiple pins can reduce the bill of materials.
In most embedded systems there are different feedback loops and fault control components that ensure everything works properly. For reliability reasons, it is better to design these feedback mechanisms without the use of the MCU core. TCD has two event inputs that can be used to monitor the activity of other peripherals and components, and alter the output accordingly.
In this example, the TCD instance will be configured to generate four PWM signals with 10 kHz frequency and approximately 50% duty cycle, synchronized in pairs. In case of a fault signal on an input channel, the timer will stop and wait until the signal changes to the Safe state (no fault detected).
-
The timer will be configured from the CTRLB register (as in the previous use case), but this
time in the Four Ramp mode:
TCD0.CTRLB = TCD_WGMODE_FOURRAMP_gc;
-
By default, channels C and D are linked to channel A. For the signals to be
synchronized in pairs, the CMPDSEL bit from the CTRLC register must be set to link channel D to channel B (see Figure 10-127):
TCD0.CTRLC = TCD_CMPDSEL_bm;
-
The frequency can be computed using the following formula:
The targeted duty cycle is approximately 50%, so CMPACLR = CMPBCLR. For most applications, there is a settling time. Thus, the user must consider that and manually configure the peripheral to include a little dead-time. So CMPASET = CMPBSET = 2 for 0.4 µs settling time:
TCD0.CMPASET = 0x02; TCD0.CMPACLR = 0xF6; TCD0.CMPBSET = 0x02; TCD0.CMPBCLR = 0xF6;
-
For fault detection purposes, input channel A of the timer will be configured
to be active-low and the digital filter of the channel will be enabled to filter
the potential spikes (see Figure 10-128).
For this use case, the fault signal will be externally triggered by the press of a button, connected to pin PC5:
TCD0.EVCTRLA = TCD_CFG_FILTER_gc | TCD_EDGE_FALL_LOW_gc | TCD_TRIGEI_bm;
-
The timer can be configured to respond to the input signal in various ways,
using the INPUTCTRLA register.
In this case, when a falling edge is detected on channel A, the timer is stopped and reset. The counter will restart at the next rising edge of the input signal.
-
The ENRDY bit of the STATUS register must be ‘
1
’ before starting the timer from the CTRLA register.Also, the 20 MHz clock is selected from CTRLA together with a prescaler of 4.while(!(TCD0.STATUS & TCD_ENRDY_bm)) { ; } TCD0.CTRLA = TCD_CLKSEL_20MHZ_gc | TCD_CNTPRES_DIV4_gc | TCD_ENABLE_bm;
In this example, all four output channels are used. The procedure is detailed within the previous use case.void TCD0_enableOutputChannels(void) { CPU_CCP = CCP_IOREG_gc; TCD0.FAULTCTRL = TCD_CMPAEN_bm | TCD_CMPBEN_bm | TCD_CMPCEN_bm | TCD_CMPDEN_bm; }
-
The event system needs to be configured to link the event generator to the TCD
instance.
In this case, the fault signal is simulated by pressing a button that is connected to PC5. The initialization code is provided below, but the event system configuration details are beyond the scope of this document.
void EVENT_SYSTEM_init(void) { EVSYS.ASYNCCH2 = EVSYS_ASYNCCH2_PORTC_PIN5_gc; EVSYS.ASYNCUSER6 = EVSYS_ASYNCUSER6_ASYNCCH2_gc; }
-
Channels A and B are linked with PA4 and PA5 pins, and channels C and D are
linked with PC0 and PC1 pins. These pins must be configured as outputs.
PC5, which is configured as input, is connected to the ATtiny817 Xplained Mini user button and has an internal pull-up resistor enabled.
A pull-up resistor provides a default state of ‘1
’ to the pin. Thus, the button will be used to drive the pin low (logical ‘0
’) when it is pressed.PORTA.DIR |= PIN4_bm | PIN5_bm; PORTC.DIR |= PIN0_bm | PIN1_bm; PORTC.DIR &= ~PIN5_bm; PORTC.PIN5CTRL = PORT_PULLUPEN_bm;
This application will configure the TCD instance to generate four PWM signals with 10 kHz frequency at an approximately 50% duty cycle, synchronized in pairs.
An MCC generated code example for AVR128DA48, with the same functionality as the one described in this section, can be found here:
10.5 References
- ATtiny817 web page: www.microchip.com/wwwproducts/en/ATTINY817
- ATtiny417/817 - AVR® Microcontroller with Core Independent Peripherals and picoPower® Technology (DS40001901)
- ATtiny817 Xplained Mini web page: www.microchip.com/DevelopmentTools/ProductDetails/ATTINY817-XMINI
- AVR128DA48 product page: www.microchip.com/wwwproducts/en/AVR128DA48
- AVR128DA48 Curiosity Nano Evaluation Kit product page: https://www.microchip.com/Developmenttools/ProductDetails/DM164151
- AVR128DA28/32/48/64 Data Sheet
- Getting Started with the AVR® DA Family
10.6 Appendix
Generating Complementary Driving Samples Source Code
#define SIGNAL_PERIOD_EXAMPLE_VALUE (0xC8) #define SIGNAL_DUTY_CYCLE_EXAMPLE_VALUE (0x64) #include <avr/io.h> /*Using default clock 3.3 MHz */ void TCD0_init(void); void TCD0_enableOutputChannels(void); void PORT_init(void); void TCD0_init(void) { /* set the waveform mode */ TCD0.CTRLB = TCD_WGMODE_DS_gc; /* set the signal period */ TCD0.CMPBCLR = SIGNAL_PERIOD_EXAMPLE_VALUE; /* the signals are alternatively active and a small symmetric dead time is needed */ TCD0.CMPBSET = SIGNAL_DUTY_CYCLE_EXAMPLE_VALUE + 1; TCD0.CMPASET = SIGNAL_DUTY_CYCLE_EXAMPLE_VALUE - 1; /* ensure the ENRDY bit is set */ while(!(TCD0.STATUS & TCD_ENRDY_bm)) { ; } TCD0.CTRLA = TCD_CLKSEL_20MHZ_gc /* choose the timer’s clock */ | TCD_CNTPRES_DIV1_gc /* choose the prescaler */ | TCD_ENABLE_bm; /* enable the timer */ } void TCD0_enableOutputChannels(void) { /* enable write-protected register */ CPU_CCP = CCP_IOREG_gc; TCD0.FAULTCTRL = TCD_CMPAEN_bm /* enable channel A */ | TCD_CMPBEN_bm; /* enable channel B */ } void PORT_init(void) { PORTA.DIR |= PIN4_bm /* set pin 4 as output */ | PIN5_bm; /* set pin 5 as output */ } int main(void) { PORT_init(); TCD0_enableOutputChannels(); TCD0_init(); /* Replace with your application code */ while (1) { ; } }
Controlling Synchronous Signals Using Input Events Source Code
#define SETTLING_TIME_EXAMPLE_VALUE (0x02) #define DUTY_CYCLE_EXAMPLE_VALUE (0xF6) #include <avr/io.h> /*Using default clock 3.3 MHz */ void TCD0_init(void); void TCD0_enableOutputChannels(void); void EVENT_SYSTEM_init(void); void PORT_init(void); void TCD0_init(void) { /* set the waveform mode */ TCD0.CTRLB = TCD_WGMODE_FOURRAMP_gc; /* set channel D to match channel B */ TCD0.CTRLC = TCD_CMPDSEL_bm; /* set the settling time and duty cycle for the signals*/ TCD0.CMPASET = SETTLING_TIME_EXAMPLE_VALUE; TCD0.CMPACLR = DUTY_CYCLE_EXAMPLE_VALUE; TCD0.CMPBSET = SETTLING_TIME_EXAMPLE_VALUE; TCD0.CMPBCLR = DUTY_CYCLE_EXAMPLE_VALUE; TCD0.EVCTRLA = TCD_CFG_FILTER_gc /* set the anti-spike filter */ | TCD_EDGE_FALL_LOW_gc /* set the ‘fault’ state */ | TCD_TRIGEI_bm; /* enable input channel A */ /* set the input mode */ TCD0.INPUTCTRLA = TCD_INPUTMODE_WAIT_gc; /* ensure the ENRDY bit is set */ while(!(TCD0.STATUS & TCD_ENRDY_bm)) { ; } TCD0.CTRLA = TCD_CLKSEL_20MHZ_gc /* choose the timer’s clock */ | TCD_CNTPRES_DIV4_gc /* choose the prescaler */ | TCD_ENABLE_bm; /* enable the timer */ } void TCD0_enableOutputChannels(void) { /* enable write-protected register */ CPU_CCP = CCP_IOREG_gc; TCD0.FAULTCTRL = TCD_CMPAEN_bm /* enable channel A */ | TCD_CMPBEN_bm /* enable channel B */ | TCD_CMPCEN_bm /* enable channel C */ | TCD_CMPDEN_bm; /* enable channel D */ } void EVENT_SYSTEM_init(void) { EVSYS.ASYNCCH2 = EVSYS_ASYNCCH2_PORTC_PIN5_gc; EVSYS.ASYNCUSER6 = EVSYS_ASYNCUSER6_ASYNCCH2_gc; } void PORT_init(void) { /* set pin 4 and pin 5 of port A as output */ PORTA.DIR |= PIN4_bm | PIN5_bm; /* set pin 0 and pin 1 of port C as output */ PORTC.DIR |= PIN0_bm | PIN1_bm; /* set pin 5 of port C as input */ PORTC.DIR &= ~PIN5_bm; /* enable pull-up resistor for pin 5 of port C */ PORTC.PIN5CTRL = PORT_PULLUPEN_bm; } int main(void) { PORT_init(); EVENT_SYSTEM_init(); TCD0_enableOutputChannels(); TCD0_init(); /* Replace with your application code */ while (1) { ; } }