Configuring SAM D21 Interrupts

Last modified by Microchip on 2023/11/14 18:26

SAM D21 interrupt operation must be carefully initialized by the application developer. This page summarizes the key initialization and usage steps required for using peripheral interrupts in an application. We will use the GNU Compiler Collection (GCC), running within the Atmel Studio 7 IDE. Examples use the ATSAMD21J18A MCU.

Visit the following page if you would like to try a simple, working SAM D21 interrupt project:

Include the required files

For a GCC C Executable-type project in Atmel Studio 7, all required interrupt support header/source files are included when you first execute Build > Rebuild-All.
Build > Rebuild-All


Provide the interrupt handler routine

Default handler functions are defined in startup_samd21.c.

Default handler functionsNote that these functions are defined as “weak”, so you can override them with your own implementation.

Acknowledge the Interrupt. Within the handler, you must acknowledge (reset) the interrupt flag in the INTFLAG register which is the cause of the interrupt. Here is an example of a Timer/Counter 3 handler, which is triggered by a compare match event:

void TC3_Handler(void){

// Interrupt processing code

// Acknowledge the interrupt (clear MC0 interrupt flag to re-arm)
TC3->COUNT16.INTFLAG.reg |= 0b00010000;

}

Back to top


Enable a specific interrupt on the peripheral

We will briefly review the SAM D21 Timer/Counter module in this section in order to provide a specific example of enabling a peripheral interrupt. In this example, we configure the Timer/Counter 3 to generate compare interrupts every 100 ms, using a 1 MHz CPU Clock and GCLK0.

Timer/Counter Overview

The Timer/Counter (TC) module provides a set of timing and counting-related functionality, such as the generation of periodic waveforms, the capturing of a periodic waveform's frequency/duty cycle, and software timekeeping for periodic operations. TC modules can be configured to use an 8-, 16-, or 32-bit counter size.

16-bit Compare Mode Configuration

In this mode, the module uses a compare channel (channel 0 in this example) to create a simple, periodic time delay function:

16-bit Compare Mode Configuration

The compare, Waveform Generation mode is set to Match Frequency Operation, whereby the period time is controlled by the value in the CC0 register.

Match Frequency Operation

  • COUNT is reset to 0 on each match
  • WO[0] toggles on each match, generating an interrupt event

WO[0] may be optionally connected to an external MCU pin.

Peripheral Interrupt Registers

Each peripheral module has its own set of interrupt registers, which must be set to enable interrupt generation.

  • INTENSET - Atomic interrupt enable
  • INTENCLR - Atomic interrupt disable
  • INTFLAG - Interrupt flag status and clear

These registers may be accessed in C using an indirect method as shown:

/* Enable TC3 Overflow Interrupt Signal */
TC3->COUNT16.INTENSET.bit.OVF = 1;

/* Enable TC3 Error Interrupt Signal */
TC3->COUNT16.INTENSET.bit.ERR = 1;
Configure the TC3 Interrupt Generation

Follow these coding steps:

Configure an asynchronous clock source (GCLK_TC)
Configure a synchronous bus clock
Configure Count Mode (16-bit)
Configure Prescaler
Configure Compare Mode
Initialize compare value (CC0)
Enable TCx compare mode interrupts
Enable TCx module

Here is the code snippet that performs these steps (note: a 1 MHz CPU Clock and 1 MHz GCLK0 Generator are already enabled and running):

// Configure asynchronous clock source
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID_TCC2_TC3_Val;    // select TC3 peripheral channel
GCLK->CLKCTRL.reg |= GCLK_CLKCTRL_GEN_GCLK0;        // select source GCLK_GEN[0]
GCLK->CLKCTRL.bit.CLKEN = 1;            // enable TC3 generic clock

// Configure synchronous bus clock
PM->APBCSEL.bit.APBCDIV = 0;            // no prescaler
PM->APBCMASK.bit.TC3_ = 1;                // enable TC3 interface

// Configure Count Mode (16-bit)
TC3->COUNT16.CTRLA.bit.MODE = 0x0;

// Configure Prescaler for divide by 2 (500kHz clock to COUNT)
TC3->COUNT16.CTRLA.bit.PRESCALER = 0x1;

// Configure TC3 Compare Mode for compare channel 0
TC3->COUNT16.CTRLA.bit.WAVEGEN = 0x1;            // "Match Frequency" operation

// Initialize compare value for 100mS @ 500kHz
TC3->COUNT16.CC[0].reg = 50000;

// Enable TC3 compare mode interrupt generation
TC3->COUNT16.INTENSET.bit.MC0 = 0x1;    // Enable match interrupts on compare channel 0

// Enable TC3
TC3->COUNT16.CTRLA.bit.ENABLE = 1;

// Wait until TC3 is enabled
while(TC3->COUNT16.STATUS.bit.SYNCBUSY == 1);

Back to top


Set the priority and enable the NVIC line

Set the NVIC interrupt priority

To set the NVIC interrupt priority, first set the TC3 interrupt priority level (0, 1, 2, 3), call the NVIC_SetPriority() CMSIS function included in the core_cm0plus.h file.

/* Set TC3 Interrupt Priority to Level 3 */
NVIC_SetPriority(TC3_IRQn, 3);

Recall that a higher priority number parameter corresponds to a lower interrupt priority.

higher priority number parameter corresponds to a lower interrupt priority

Enable the NVIC Line

To enable the TC3 interrupt, call the NVIC_EnableIRQ() CMSIS function included in the core_cm0plus.h file.

/* Enable TC3 NVIC Interrupt Line */
NVIC_EnableIRQ(TC3_IRQn);

Back to top


Enable global interrupts

Finally, the CMSIS file, cmsis_gcc.h, provides the following intrinsic functions for globally enabling/disabling IRQs:

/* Disable Interrupts */
void __disable_irq(void);

/* Enable Interrupts */
void __enable_irq(void);

Learn More

Go to the NVIC example project to see a complete working project, or the NVIC overview page to review the Cortex M0+ exception model and NVIC functionality.

Back to top