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 pages if you are interested in learning the details on the Nested Vectored Interrupt Controller (NVIC) on SAM D21, or if you would like to try a simple, working SAM D21 interrupt project:
Step 1. 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.
Step 2. Provide the interrupt handler routine
Default handler functions are defined in startup_samd21.c.
Note 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 for 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;
}
Step 3. 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:
The compare, "waveform generation" mode is set to 'Match Frequency Operation', whereby the period time is controlled by the value in the CC0 register.
- 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 their 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 here:
/* 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);
Step 4. 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.
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);
Step 5. 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.
Table of Contents
|