The supported board 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. Here the pulse width varies through the following values with the help of DMA transfer: one quarter of the period, half of the period, and three quarters of the period. The PWM output can be used to drive a LED. The waveform can also be viewed using an oscilloscope. The output signal is also fed back to another TCC channel by event system, the event stamps are captured and transferred to a buffer by DMA. 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 is set up as follows:
Board | Pin | Connect to |
SAM D21 Xpro | PB30 | LED0 |
SAM R21 Xpro | PA19 | LED0 |
SAM L21 Xpro | PB10 | LED0 |
SAM L22 Xpro | PC27 | LED0 |
SAM DA1 Xpro | PB30 | LED0 |
SAM C21 Xpro | PA15 | LED0 |
SAM HA1G16A Xpro | PA04 | NULL |
The TCC module will be setup 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
- Prescaler is set to 1024
- Single Slope PWM wave generation
- GCLK reload action
- Don't run in standby
- No fault or waveform extensions
- No inversion of waveform output
- No capture enabled
- Count upward
- Don't perform one-shot operations
- Counter starts on 0
- Counter top set to 0x1000
- Channel 0 (on SAM D21 Xpro) or 3 (on SAM R21 Xpro) is set to compare and match value 0x1000*3/4 and generate event
- Channel 1 is set to capture on input event
The event resource of EVSYS module will be setup as follows:
- TCC match capture channel 0 (on SAM D21 Xpro) or 3 (on SAM R21 Xpro) is selected as event generator
- Event generation is synchronous, with rising edge detected
- TCC match capture channel 1 is the event user
The DMA resource of DMAC module will be setup as follows:
- Two DMA resources are used
- Both DMA resources use peripheral trigger
- Both DMA resources perform beat transfer on trigger
- Both DMA resources use beat size of 16 bits
- Both DMA resources are configured to transfer three beats and then repeat again in same buffer
- On DMA resource which controls the compare value
- TCC0 overflow triggers DMA transfer
- The source address increment is enabled
- The destination address is fixed to TCC channel 0 Compare/Capture register
- On DMA resource which reads the captured value
- TCC0 capture on channel 1 triggers DMA transfer
- The source address is fixed to TCC channel 1 Compare/Capture register
- The destination address increment is enabled
- The captured value is transferred to an array in SRAM
Quick Start
Prerequisites
There are no prerequisites for this use case.
Code
Add to the main application source file, before any functions, according to the kit used:
- 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_TCC_CAPTURE_CHANNEL 1
#define CONF_TCC_EVENT_GENERATOR EVSYS_ID_GEN_TCC0_MCX_0
#define CONF_TCC_EVENT_USER EVSYS_ID_USER_TCC0_MC_1
#define CONF_COMPARE_TRIGGER TCC0_DMAC_ID_OVF
#define CONF_CAPTURE_TRIGGER TCC0_DMAC_ID_MC_1
- 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_TCC_CAPTURE_CHANNEL 1
#define CONF_TCC_EVENT_GENERATOR EVSYS_ID_GEN_TCC0_MCX_3
#define CONF_TCC_EVENT_USER EVSYS_ID_USER_TCC0_MC_1
#define CONF_COMPARE_TRIGGER TCC0_DMAC_ID_OVF
#define CONF_CAPTURE_TRIGGER TCC0_DMAC_ID_MC_1
- 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_TCC_CAPTURE_CHANNEL 1
#define CONF_TCC_EVENT_GENERATOR EVSYS_ID_GEN_TCC0_MCX_0
#define CONF_TCC_EVENT_USER EVSYS_ID_USER_TCC0_MC_1
#define CONF_COMPARE_TRIGGER TCC0_DMAC_ID_OVF
- 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_TCC_CAPTURE_CHANNEL 1
#define CONF_TCC_EVENT_GENERATOR EVSYS_ID_GEN_TCC0_MCX_0
#define CONF_TCC_EVENT_USER EVSYS_ID_USER_TCC0_MC_1
#define CONF_COMPARE_TRIGGER TCC0_DMAC_ID_OVF
- 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_TCC_CAPTURE_CHANNEL 1
#define CONF_TCC_EVENT_GENERATOR EVSYS_ID_GEN_TCC0_MCX_0
#define CONF_TCC_EVENT_USER EVSYS_ID_USER_TCC0_MC_1
#define CONF_COMPARE_TRIGGER TCC0_DMAC_ID_OVF
#define CONF_CAPTURE_TRIGGER TCC0_DMAC_ID_MC_1
- 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_TCC_CAPTURE_CHANNEL 1
#define CONF_TCC_EVENT_GENERATOR EVSYS_ID_GEN_TCC0_MCX_0
#define CONF_TCC_EVENT_USER EVSYS_ID_USER_TCC0_MC_1
#define CONF_COMPARE_TRIGGER TCC0_DMAC_ID_OVF
Add to the main application source file, outside of any functions: uint16_t capture_values[3] = {0, 0, 0};
COMPILER_ALIGNED(16) DmacDescriptor capture_dma_descriptor SECTION_DMAC_DESCRIPTOR;
struct events_resource capture_event_resource;
uint16_t compare_values[3] = {
(0x1000 / 4), (0x1000 * 2 / 4), (0x1000 * 3 / 4)
};
COMPILER_ALIGNED(16) DmacDescriptor compare_dma_descriptor SECTION_DMAC_DESCRIPTOR;
Copy-paste the following setup code to your user application: static void config_event_for_capture(void)
{
struct events_config config;
events_get_config_defaults(&config);
config.generator = CONF_TCC_EVENT_GENERATOR;
config.edge_detect = EVENTS_EDGE_DETECT_RISING;
config.path = EVENTS_PATH_SYNCHRONOUS;
events_allocate(&capture_event_resource, &config);
events_attach_user(&capture_event_resource, CONF_TCC_EVENT_USER);
}
static void config_dma_for_capture(void)
{
config.peripheral_trigger = CONF_CAPTURE_TRIGGER;
descriptor_config.block_transfer_count = 3;
descriptor_config.src_increment_enable = false;
descriptor_config.source_address =
(uint32_t)&CONF_PWM_MODULE->CC[CONF_TCC_CAPTURE_CHANNEL];
descriptor_config.destination_address =
(uint32_t)capture_values + sizeof(capture_values);
}
static void config_dma_for_wave(void)
{
config.peripheral_trigger = CONF_COMPARE_TRIGGER;
descriptor_config.block_transfer_count = 3;
descriptor_config.dst_increment_enable = false;
descriptor_config.source_address =
(uint32_t)compare_values + sizeof(compare_values);
#if (SAMR21) || (SAMD21) || (SAMDA1) || (SAMHA1)
descriptor_config.destination_address =
(uint32_t)&CONF_PWM_MODULE->CC[CONF_PWM_CHANNEL];
#else
descriptor_config.destination_address =
(uint32_t)&CONF_PWM_MODULE->CCBUF[CONF_PWM_CHANNEL];
#endif
}
static void configure_tcc(void)
{
config_tcc.counter.period = 0x1000;
config_tcc.compare.channel_function[CONF_TCC_CAPTURE_CHANNEL] =
config_tcc.compare.match[CONF_PWM_CHANNEL] = compare_values[2];
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;
.input_config[1].modify_action = false,
.output_config.modify_generation_selection = false,
.generate_event_on_channel[CONF_PWM_CHANNEL] = true,
.on_event_perform_channel_action[CONF_TCC_CAPTURE_CHANNEL] = true
};
config_event_for_capture();
config_dma_for_capture();
config_dma_for_wave();
}
Add to user application initialization (typically the start of main()
):
Workflow
Configure the TCC
- Create a module software instance structure for the TCC module to store the TCC driver state while it is in use.
- Note
- This should never go out of scope as long as the module is in use. In most cases, this should be global.
- Create a TCC module configuration struct, which can be filled out to adjust the configuration of a physical TCC peripheral.
- Initialize the TCC 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.
- Alter the TCC settings to configure the counter width, wave generation mode, and the compare channel 0 value.
config_tcc.counter.period = 0x1000;
config_tcc.compare.channel_function[CONF_TCC_CAPTURE_CHANNEL] =
config_tcc.compare.match[CONF_PWM_CHANNEL] = compare_values[2];
- 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;
- Configure the TCC module with the desired settings.
- Configure and enable the desired events for the TCC module.
.input_config[1].modify_action = false,
.output_config.modify_generation_selection = false,
.generate_event_on_channel[CONF_PWM_CHANNEL] = true,
.on_event_perform_channel_action[CONF_TCC_CAPTURE_CHANNEL] = true
};
Configure the Event System
Configure the EVSYS module to wire channel 0 event to channel 1.
- Create an event resource instance.
struct events_resource capture_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.
- Create an event resource configuration struct.
struct events_config config;
- Initialize the event resource configuration struct with 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.
- Adjust the event resource configuration to desired values.
config.generator = CONF_TCC_EVENT_GENERATOR;
config.edge_detect = EVENTS_EDGE_DETECT_RISING;
config.path = EVENTS_PATH_SYNCHRONOUS;
- Allocate and configure the resource using the configuration structure.
events_allocate(&capture_event_resource, &config);
- Attach a user to the resource.
events_attach_user(&capture_event_resource, CONF_TCC_EVENT_USER);
Configure the DMA for Capture TCC Channel 1
Configure the DMAC module to obtain captured value from TCC channel 1.
- Create a DMA resource instance.
- Note
- This should never go out of scope as long as the resource is in use. In most cases, this should be global.
- Create a DMA resource configuration struct.
- Initialize the DMA resource configuration struct with default values.
- Note
- This should always be performed before using the configuration struct to ensure that all values are initialized to known default settings.
- Adjust the DMA resource configurations.
config.peripheral_trigger = CONF_CAPTURE_TRIGGER;
- Allocate a DMA resource with the configurations.
- Prepare DMA transfer descriptor.
- Create a DMA transfer descriptor.
COMPILER_ALIGNED(16) DmacDescriptor capture_dma_descriptor SECTION_DMAC_DESCRIPTOR;
- Note
- When multiple descriptors are linked, the linked item should never go out of scope before it is loaded (to DMA Write-Back memory section). In most cases, if more than one descriptors are used, they should be global except the very first one.
- Create a DMA transfer descriptor struct.
- Create a DMA transfer descriptor configuration structure, which can be filled out to adjust the configuration of a single DMA transfer.
- Initialize the DMA transfer descriptor configuration struct with default values.
- Note
- This should always be performed before using the configuration struct to ensure that all values are initialized to known default settings.
- Adjust the DMA transfer descriptor configurations.
descriptor_config.block_transfer_count = 3;
descriptor_config.src_increment_enable = false;
descriptor_config.source_address =
(uint32_t)&CONF_PWM_MODULE->CC[CONF_TCC_CAPTURE_CHANNEL];
descriptor_config.destination_address =
(uint32_t)capture_values + sizeof(capture_values);
- Create the DMA transfer descriptor with the given configuration.
- Start DMA transfer job with prepared descriptor.
- Add the DMA transfer descriptor to the allocated DMA resource.
- Note
- When adding multiple descriptors, the last one added is linked at the end of the descriptor queue. If ringed list is needed, just add the first descriptor again to build the circle.
- Start the DMA transfer job with the allocated DMA resource and transfer descriptor.
Configure the DMA for Compare TCC Channel 0
Configure the DMAC module to update TCC channel 0 compare value. The flow is similar to last DMA configure step for capture.
- Allocate and configure the DMA resource.
config.peripheral_trigger = CONF_COMPARE_TRIGGER;
- Prepare DMA transfer descriptor.
COMPILER_ALIGNED(16) DmacDescriptor compare_dma_descriptor SECTION_DMAC_DESCRIPTOR;
descriptor_config.block_transfer_count = 3;
descriptor_config.dst_increment_enable = false;
descriptor_config.source_address =
(uint32_t)compare_values + sizeof(compare_values);
#if (SAMR21) || (SAMD21) || (SAMDA1) || (SAMHA1)
descriptor_config.destination_address =
(uint32_t)&CONF_PWM_MODULE->CC[CONF_PWM_CHANNEL];
#else
descriptor_config.destination_address =
(uint32_t)&CONF_PWM_MODULE->CCBUF[CONF_PWM_CHANNEL];
#endif
- Start DMA transfer job with prepared descriptor.
- Enable the TCC module to start the timer and begin PWM signal generation.
Use Case
Code
Copy-paste the following code to your user application:
Workflow
- Enter an infinite loop while the PWM wave is generated via the TCC module.