Microchip® Advanced Software Framework

Quick Start Guide for TCC - Non-Recoverable Fault

The supported kit list:

  • SAM D21/R21/L21/L22/DA1/C21/HA1G16A Xplained Pro

In this use case, the TCC will be used to generate a PWM signal, with a varying duty cycle. Here the pulse width is increased each time the timer count matches the set compare value. There is a non-recoverable fault input which controls PWM output. When this fault is active (low) the PWM output will be forced to be high. When fault is released (input high) the PWM output will go on.

When the PWM signal connects to LED, LED will light. If fault input is from a button, the LED will be off when the button is down and on when the button is up. To see the PWM waveform, you may need an oscilloscope.

SAMHA1G16A Xpro LED is PA00 which isn't connected out, use PA04 instead, so we can't see LED blink but only see the waveform from oscilloscope.

The PWM output and fault input is set up as follows:

Board Pin Connect to
SAM D21 Xpro PB30 LED0
SAM D21 Xpro PA15 SW0
SAM R21 Xpro PA19 LED0
SAM R21 Xpro PA28 SW0
SAM L21 Xpro PB10 LED0
SAM L21 Xpro PA16 SW0
SAM L22 Xpro PC27 LED0
SAM L22 Xpro PC01 SW0
SAM DA1 Xpro PB30 LED0
SAM DA1 Xpro PA15 SW0
SAM C21 Xpro PA15 LED0
SAM C21 Xpro PA28 SW0
SAM HA1G16A Xpro PA04 NULL
SAM HA1G16A Xpro PB03 SW0

The TCC module will be set up as follows:

  • GCLK generator 0 (GCLK main) clock source
  • Use double buffering write when set top, compare, or pattern through API
  • No dithering on the counter or compare
  • No prescaler
  • Single Slope PWM wave generation
  • GCLK reload action
  • Don't run in standby
  • No waveform extentions
  • No inversion of waveform output
  • No capture enabled
  • Count upward
  • Don't perform one-shot operations
  • No event input except TCC event0 enabled
  • No event action except TCC event0 acts as Non-Recoverable Fault
  • No event generation enabled
  • Counter starts on 0

Quick Start

Prerequisites

There are no prerequisites for this use case.

Code

Add to the main application source file, before any functions:

  • SAM D21 Xplained Pro
    #define CONF_PWM_MODULE LED_0_PWM4CTRL_MODULE
    #define CONF_PWM_CHANNEL LED_0_PWM4CTRL_CHANNEL
    #define CONF_PWM_OUTPUT LED_0_PWM4CTRL_OUTPUT
    #define CONF_PWM_OUT_PIN LED_0_PWM4CTRL_PIN
    #define CONF_PWM_OUT_MUX LED_0_PWM4CTRL_MUX
    #define CONF_FAULT_EIC_PIN SW0_EIC_PIN
    #define CONF_FAULT_EIC_PIN_MUX SW0_EIC_PINMUX
    #define CONF_FAULT_EIC_LINE SW0_EIC_LINE
    #define CONF_FAULT_EVENT_GENERATOR EVSYS_ID_GEN_EIC_EXTINT_15
    #define CONF_FAULT_EVENT_USER EVSYS_ID_USER_TCC0_EV_0
  • SAM R21 Xplained Pro
    #define CONF_PWM_MODULE LED_0_PWM4CTRL_MODULE
    #define CONF_PWM_CHANNEL LED_0_PWM4CTRL_CHANNEL
    #define CONF_PWM_OUTPUT LED_0_PWM4CTRL_OUTPUT
    #define CONF_PWM_OUT_PIN LED_0_PWM4CTRL_PIN
    #define CONF_PWM_OUT_MUX LED_0_PWM4CTRL_MUX
    #define CONF_FAULT_EIC_PIN SW0_EIC_PIN
    #define CONF_FAULT_EIC_PIN_MUX SW0_EIC_PINMUX
    #define CONF_FAULT_EIC_LINE SW0_EIC_LINE
    #define CONF_FAULT_EVENT_GENERATOR EVSYS_ID_GEN_EIC_EXTINT_8
    #define CONF_FAULT_EVENT_USER EVSYS_ID_USER_TCC0_EV_0
  • SAM L21 Xplained Pro
    #define CONF_PWM_MODULE LED_0_PWM4CTRL_MODULE
    #define CONF_PWM_CHANNEL LED_0_PWM4CTRL_CHANNEL
    #define CONF_PWM_OUTPUT LED_0_PWM4CTRL_OUTPUT
    #define CONF_PWM_OUT_PIN LED_0_PWM4CTRL_PIN
    #define CONF_PWM_OUT_MUX LED_0_PWM4CTRL_MUX
    #define CONF_FAULT_EIC_PIN SW0_EIC_PIN
    #define CONF_FAULT_EIC_PIN_MUX SW0_EIC_PINMUX
    #define CONF_FAULT_EIC_LINE SW0_EIC_LINE
    #define CONF_FAULT_EVENT_GENERATOR EVSYS_ID_GEN_EIC_EXTINT_2
    #define CONF_FAULT_EVENT_USER EVSYS_ID_USER_TCC0_EV_0
  • SAM L22 Xplained Pro
    #define CONF_PWM_MODULE LED_0_PWM4CTRL_MODULE
    #define CONF_PWM_CHANNEL LED_0_PWM4CTRL_CHANNEL
    #define CONF_PWM_OUTPUT LED_0_PWM4CTRL_OUTPUT
    #define CONF_PWM_OUT_PIN LED_0_PWM4CTRL_PIN
    #define CONF_PWM_OUT_MUX LED_0_PWM4CTRL_MUX
    #define CONF_FAULT_EIC_PIN SW0_EIC_PIN
    #define CONF_FAULT_EIC_PIN_MUX SW0_EIC_PINMUX
    #define CONF_FAULT_EIC_LINE SW0_EIC_LINE
    #define CONF_FAULT_EVENT_GENERATOR EVSYS_ID_GEN_EIC_EXTINT_9
    #define CONF_FAULT_EVENT_USER EVSYS_ID_USER_TCC0_EV_0
  • SAM DA1 Xplained Pro
    #define CONF_PWM_MODULE LED_0_PWM4CTRL_MODULE
    #define CONF_PWM_CHANNEL LED_0_PWM4CTRL_CHANNEL
    #define CONF_PWM_OUTPUT LED_0_PWM4CTRL_OUTPUT
    #define CONF_PWM_OUT_PIN LED_0_PWM4CTRL_PIN
    #define CONF_PWM_OUT_MUX LED_0_PWM4CTRL_MUX
    #define CONF_FAULT_EIC_PIN SW0_EIC_PIN
    #define CONF_FAULT_EIC_PIN_MUX SW0_EIC_PINMUX
    #define CONF_FAULT_EIC_LINE SW0_EIC_LINE
    #define CONF_FAULT_EVENT_GENERATOR EVSYS_ID_GEN_EIC_EXTINT_15
    #define CONF_FAULT_EVENT_USER EVSYS_ID_USER_TCC0_EV_0
  • SAM C21 Xplained Pro
    #define CONF_PWM_MODULE LED_0_PWM4CTRL_MODULE
    #define CONF_PWM_CHANNEL LED_0_PWM4CTRL_CHANNEL
    #define CONF_PWM_OUTPUT LED_0_PWM4CTRL_OUTPUT
    #define CONF_PWM_OUT_PIN LED_0_PWM4CTRL_PIN
    #define CONF_PWM_OUT_MUX LED_0_PWM4CTRL_MUX
    #define CONF_FAULT_EIC_PIN SW0_EIC_PIN
    #define CONF_FAULT_EIC_PIN_MUX SW0_EIC_PINMUX
    #define CONF_FAULT_EIC_LINE SW0_EIC_LINE
    #define CONF_FAULT_EVENT_GENERATOR EVSYS_ID_GEN_EIC_EXTINT_8
    #define CONF_FAULT_EVENT_USER EVSYS_ID_USER_TCC0_EV_0
  • SAM HA1G16A Xplained Pro:
    #define CONF_PWM_MODULE LED_0_PWM4CTRL_MODULE
    #define CONF_PWM_CHANNEL LED_0_PWM4CTRL_CHANNEL
    #define CONF_PWM_OUTPUT LED_0_PWM4CTRL_OUTPUT
    #define CONF_PWM_OUT_PIN LED_0_PWM4CTRL_PIN
    #define CONF_PWM_OUT_MUX LED_0_PWM4CTRL_MUX
    #define CONF_FAULT_EIC_PIN SW0_EIC_PIN
    #define CONF_FAULT_EIC_PIN_MUX SW0_EIC_PINMUX
    #define CONF_FAULT_EIC_LINE SW0_EIC_LINE
    #define CONF_FAULT_EVENT_GENERATOR EVSYS_ID_GEN_EIC_EXTINT_3
    #define CONF_FAULT_EVENT_USER EVSYS_ID_USER_TCC0_EV_0
    Add to the main application source file, before any functions:
    #include <string.h>
    Add to the main application source file, outside of any functions:
    struct tcc_module tcc_instance;
    struct events_resource event_resource;
    Copy-paste the following callback function code to your user application:
    static void tcc_callback_to_change_duty_cycle(
    struct tcc_module *const module_inst)
    {
    static uint32_t delay = 10;
    static uint32_t i = 0;
    if (--delay) {
    return;
    }
    delay = 10;
    i = (i + 0x0800) & 0xFFFF;
    tcc_set_compare_value(module_inst,
    (TCC_MATCH_CAPTURE_CHANNEL_0 + CONF_PWM_CHANNEL),
    i + 1);
    }
    static void eic_callback_to_clear_halt(void)
    {
    if (port_pin_get_input_level(CONF_FAULT_EIC_PIN)) {
    tcc_clear_status(&tcc_instance,
    }
    }
    Copy-paste the following setup code to your user application:
    static void configure_tcc(void)
    {
    struct tcc_config config_tcc;
    tcc_get_config_defaults(&config_tcc, CONF_PWM_MODULE);
    config_tcc.counter.period = 0xFFFF;
    config_tcc.compare.wave_generation = TCC_WAVE_GENERATION_SINGLE_SLOPE_PWM;
    config_tcc.compare.match[CONF_PWM_CHANNEL] = 0xFFFF;
    config_tcc.wave_ext.non_recoverable_fault[0].output = TCC_FAULT_STATE_OUTPUT_1;
    config_tcc.pins.enable_wave_out_pin[CONF_PWM_OUTPUT] = true;
    config_tcc.pins.wave_out_pin[CONF_PWM_OUTPUT] = CONF_PWM_OUT_PIN;
    config_tcc.pins.wave_out_pin_mux[CONF_PWM_OUTPUT] = CONF_PWM_OUT_MUX;
    tcc_init(&tcc_instance, CONF_PWM_MODULE, &config_tcc);
    struct tcc_events events;
    memset(&events, 0, sizeof(struct tcc_events));
    events.on_input_event_perform_action[0] = true;
    events.input_config[0].modify_action = true;
    events.input_config[0].action = TCC_EVENT_ACTION_NON_RECOVERABLE_FAULT;
    tcc_enable_events(&tcc_instance, &events);
    tcc_enable(&tcc_instance);
    }
    static void configure_tcc_callbacks(void)
    {
    &tcc_instance,
    tcc_callback_to_change_duty_cycle,
    (enum tcc_callback)(TCC_CALLBACK_CHANNEL_0 + CONF_PWM_CHANNEL));
    tcc_enable_callback(&tcc_instance,
    (enum tcc_callback)(TCC_CALLBACK_CHANNEL_0 + CONF_PWM_CHANNEL));
    }
    static void configure_eic(void)
    {
    struct extint_chan_conf config;
    config.filter_input_signal = true;
    config.detection_criteria = EXTINT_DETECT_BOTH;
    config.gpio_pin = CONF_FAULT_EIC_PIN;
    config.gpio_pin_mux = CONF_FAULT_EIC_PIN_MUX;
    extint_chan_set_config(CONF_FAULT_EIC_LINE, &config);
    struct extint_events events;
    memset(&events, 0, sizeof(struct extint_events));
    events.generate_event_on_detect[CONF_FAULT_EIC_LINE] = true;
    extint_register_callback(eic_callback_to_clear_halt,
    CONF_FAULT_EIC_LINE, EXTINT_CALLBACK_TYPE_DETECT);
    extint_chan_enable_callback(CONF_FAULT_EIC_LINE,
    }
    static void configure_event(void)
    {
    struct events_config config;
    events_get_config_defaults(&config);
    config.generator = CONF_FAULT_EVENT_GENERATOR;
    config.path = EVENTS_PATH_ASYNCHRONOUS;
    events_allocate(&event_resource, &config);
    events_attach_user(&event_resource, CONF_FAULT_EVENT_USER);
    }
    Add to user application initialization (typically the start of main()):
    configure_tcc();
    configure_tcc_callbacks();
    configure_eic();
    configure_event();

Workflow

Configure TCC

  1. Create a module software instance struct for the TCC module to store the TCC driver state while it is in use.
    struct tcc_module tcc_instance;
    Note
    This should never go out of scope as long as the module is in use. In most cases, this should be global.
  2. Create a TCC module configuration struct, which can be filled out to adjust the configuration of a physical TCC peripheral.
    struct tcc_config config_tcc;
  3. Initialize the TCC configuration struct with the module's default values.
    tcc_get_config_defaults(&config_tcc, CONF_PWM_MODULE);
    Note
    This should always be performed before using the configuration struct to ensure that all values are initialized to known default settings.
  4. Alter the TCC settings to configure the counter width, wave generation mode, and the compare channel 0 value and fault options. Here the Non-Recoverable Fault output is enabled and set to high level (1).
    config_tcc.counter.period = 0xFFFF;
    config_tcc.compare.wave_generation = TCC_WAVE_GENERATION_SINGLE_SLOPE_PWM;
    config_tcc.compare.match[CONF_PWM_CHANNEL] = 0xFFFF;
    config_tcc.wave_ext.non_recoverable_fault[0].output = TCC_FAULT_STATE_OUTPUT_1;
  5. Alter the TCC settings to configure the PWM output on a physical device pin.
    config_tcc.pins.enable_wave_out_pin[CONF_PWM_OUTPUT] = true;
    config_tcc.pins.wave_out_pin[CONF_PWM_OUTPUT] = CONF_PWM_OUT_PIN;
    config_tcc.pins.wave_out_pin_mux[CONF_PWM_OUTPUT] = CONF_PWM_OUT_MUX;
  6. Configure the TCC module with the desired settings.
    tcc_init(&tcc_instance, CONF_PWM_MODULE, &config_tcc);
  7. Create a TCC events configuration struct, which can be filled out to enable/disable events and configure event settings. Reset all fields to zero.
    struct tcc_events events;
    memset(&events, 0, sizeof(struct tcc_events));
  8. Alter the TCC events settings to enable/disable desired events, to change event generating options and modify event actions. Here TCC event0 will act as Non-Recoverable Fault input.
    events.on_input_event_perform_action[0] = true;
    events.input_config[0].modify_action = true;
    events.input_config[0].action = TCC_EVENT_ACTION_NON_RECOVERABLE_FAULT;
  9. Enable and apply events settings.
    tcc_enable_events(&tcc_instance, &events);
  10. Enable the TCC module to start the timer and begin PWM signal generation.
    tcc_enable(&tcc_instance);
  11. Register the Compare Channel 0 Match callback functions with the driver.
    &tcc_instance,
    tcc_callback_to_change_duty_cycle,
    (enum tcc_callback)(TCC_CALLBACK_CHANNEL_0 + CONF_PWM_CHANNEL));
  12. Enable the Compare Channel 0 Match callback so that it will be called by the driver when appropriate.
    tcc_enable_callback(&tcc_instance,
    (enum tcc_callback)(TCC_CALLBACK_CHANNEL_0 + CONF_PWM_CHANNEL));

Configure EXTINT for fault input

  1. Create an EXTINT module channel configuration struct, which can be filled out to adjust the configuration of a single external interrupt channel.
    struct extint_chan_conf config;
  2. Initialize the channel configuration struct with the module's default values.
    Note
    This should always be performed before using the configuration struct to ensure that all values are initialized to known default settings.
  3. Adjust the configuration struct to configure the pin MUX (to route the desired physical pin to the logical channel) to the board button, and to configure the channel to detect both rising and falling edges.
    config.filter_input_signal = true;
    config.detection_criteria = EXTINT_DETECT_BOTH;
    config.gpio_pin = CONF_FAULT_EIC_PIN;
    config.gpio_pin_mux = CONF_FAULT_EIC_PIN_MUX;
  4. Configure external interrupt channel with the desired channel settings.
    extint_chan_set_config(CONF_FAULT_EIC_LINE, &config);
  5. Create a TXTINT events configuration struct, which can be filled out to enable/disable events. Reset all fields to zero.
    struct extint_events events;
    memset(&events, 0, sizeof(struct extint_events));
  6. Adjust the configuration struct, set the channels to be enabled to true. Here the channel to the board button is used.
    events.generate_event_on_detect[CONF_FAULT_EIC_LINE] = true;
  7. Enable the events.
  8. Define the EXTINT callback that will be fired when a detection event occurs. For this example, when fault line is released, the TCC fault state is cleared to go on PWM generating.
    static void eic_callback_to_clear_halt(void)
    {
    if (port_pin_get_input_level(CONF_FAULT_EIC_PIN)) {
    tcc_clear_status(&tcc_instance,
    }
    }
  9. Register a callback function eic_callback_to_clear_halt() to handle detections from the External Interrupt Controller (EIC).
    extint_register_callback(eic_callback_to_clear_halt,
    CONF_FAULT_EIC_LINE, EXTINT_CALLBACK_TYPE_DETECT);
  10. Enable the registered callback function for the configured External Interrupt channel, so that it will be called by the module when the channel detects an edge.

Configure EVENTS for fault input

  1. Create an event resource instance struct for the EVENTS module to store.
    struct events_resource event_resource;
    Note
    This should never go out of scope as long as the resource is in use. In most cases, this should be global.
  2. Create an event channel configuration struct, which can be filled out to adjust the configuration of a single event channel.
    struct events_config config;
  3. Initialize the event channel configuration struct with the module's default values.
    events_get_config_defaults(&config);
    Note
    This should always be performed before using the configuration struct to ensure that all values are initialized to known default settings.
  4. Adjust the configuration struct to request that the channel will be attached to the specified event generator, and that the asynchronous event path will be used. Here the EIC channel connected to board button is the event generator.
    config.generator = CONF_FAULT_EVENT_GENERATOR;
    config.path = EVENTS_PATH_ASYNCHRONOUS;
  5. Allocate and configure the channel using the configuration structure.
    events_allocate(&event_resource, &config);
    Note
    The existing configuration struct may be re-used, as long as any values that have been altered from the default settings are taken into account by the user application.
  6. Attach a user to the channel. Here the user is TCC event0, which has been configured as input of Non-Recoverable Fault.
    events_attach_user(&event_resource, CONF_FAULT_EVENT_USER);

Use Case

Code

Copy-paste the following code to your user application:

while (true) {
}

Workflow

  1. Enter an infinite loop while the PWM wave is generated via the TCC module.
    while (true) {
    }