/* ---------------------------------------------------------------------------- | |
* ATMEL Microcontroller Software Support | |
* ---------------------------------------------------------------------------- | |
* Copyright (c) 2008, Atmel Corporation | |
* | |
* All rights reserved. | |
* | |
* Redistribution and use in source and binary forms, with or without | |
* modification, are permitted provided that the following conditions are met: | |
* | |
* - Redistributions of source code must retain the above copyright notice, | |
* this list of conditions and the disclaimer below. | |
* | |
* Atmel's name may not be used to endorse or promote products derived from | |
* this software without specific prior written permission. | |
* | |
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR | |
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE | |
* DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, | |
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, | |
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, | |
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
* ---------------------------------------------------------------------------- | |
*/ | |
/// Disable traces for this file | |
#undef TRACE_LEVEL | |
#define TRACE_LEVEL 0 | |
//------------------------------------------------------------------------------ | |
// Headers | |
//------------------------------------------------------------------------------ | |
#include "pio_it.h" | |
#include "pio.h" | |
#include <board.h> | |
#include <irq/irq.h> | |
#include <utility/assert.h> | |
#include <utility/trace.h> | |
//------------------------------------------------------------------------------ | |
// Local definitions | |
//------------------------------------------------------------------------------ | |
/// \exclude | |
/// Maximum number of interrupt sources that can be defined. This | |
/// constant can be increased, but the current value is the smallest possible | |
/// that will be compatible with all existing projects. | |
#define MAX_INTERRUPT_SOURCES 7 | |
//------------------------------------------------------------------------------ | |
// Local types | |
//------------------------------------------------------------------------------ | |
//------------------------------------------------------------------------------ | |
/// \exclude | |
/// Describes a PIO interrupt source, including the PIO instance triggering the | |
/// interrupt and the associated interrupt handler. | |
//------------------------------------------------------------------------------ | |
typedef struct { | |
/// Pointer to the source pin instance. | |
const Pin *pPin; | |
/// Interrupt handler. | |
void (*handler)(const Pin *); | |
} InterruptSource; | |
//------------------------------------------------------------------------------ | |
// Local variables | |
//------------------------------------------------------------------------------ | |
/// List of interrupt sources. | |
static InterruptSource pSources[MAX_INTERRUPT_SOURCES]; | |
/// Number of currently defined interrupt sources. | |
static unsigned int numSources; | |
//------------------------------------------------------------------------------ | |
// Local functions | |
//------------------------------------------------------------------------------ | |
//------------------------------------------------------------------------------ | |
/// Handles all interrupts on the given PIO controller. | |
/// \param id PIO controller ID. | |
/// \param pPio PIO controller base address. | |
//------------------------------------------------------------------------------ | |
static void PioInterruptHandler(unsigned int id, AT91S_PIO *pPio) | |
{ | |
unsigned int status; | |
unsigned int i; | |
// Read PIO controller status | |
status = pPio->PIO_ISR; | |
status &= pPio->PIO_IMR; | |
// Check pending events | |
if (status != 0) { | |
TRACE_DEBUG("PIO interrupt on PIO controller #%d\n\r", id); | |
// Find triggering source | |
i = 0; | |
while (status != 0) { | |
// There cannot be an unconfigured source enabled. | |
SANITY_CHECK(i < numSources); | |
// Source is configured on the same controller | |
if (pSources[i].pPin->id == id) { | |
// Source has PIOs whose statuses have changed | |
if ((status & pSources[i].pPin->mask) != 0) { | |
TRACE_DEBUG("Interrupt source #%d triggered\n\r", i); | |
pSources[i].handler(pSources[i].pPin); | |
status &= ~(pSources[i].pPin->mask); | |
} | |
} | |
i++; | |
} | |
} | |
} | |
//------------------------------------------------------------------------------ | |
/// Generic PIO interrupt handler. Single entry point for interrupts coming | |
/// from any PIO controller (PIO A, B, C ...). Dispatches the interrupt to | |
/// the user-configured handlers. | |
//------------------------------------------------------------------------------ | |
void PIO_IT_InterruptHandler(void) | |
{ | |
#if defined(AT91C_ID_PIOA) | |
// Treat PIOA interrupts | |
PioInterruptHandler(AT91C_ID_PIOA, AT91C_BASE_PIOA); | |
#endif | |
#if defined(AT91C_ID_PIOB) | |
// Treat PIOB interrupts | |
PioInterruptHandler(AT91C_ID_PIOB, AT91C_BASE_PIOB); | |
#endif | |
#if defined(AT91C_ID_PIOC) | |
// Treat PIOC interrupts | |
PioInterruptHandler(AT91C_ID_PIOC, AT91C_BASE_PIOC); | |
#endif | |
#if defined(AT91C_ID_PIOD) | |
// Treat PIOD interrupts | |
PioInterruptHandler(AT91C_ID_PIOD, AT91C_BASE_PIOD); | |
#endif | |
#if defined(AT91C_ID_PIOE) | |
// Treat PIOE interrupts | |
PioInterruptHandler(AT91C_ID_PIOE, AT91C_BASE_PIOE); | |
#endif | |
#if defined(AT91C_ID_PIOABCD) | |
// Treat PIOABCD interrupts | |
#if !defined(AT91C_ID_PIOA) | |
PioInterruptHandler(AT91C_ID_PIOABCD, AT91C_BASE_PIOA); | |
#endif | |
#if !defined(AT91C_ID_PIOB) | |
PioInterruptHandler(AT91C_ID_PIOABCD, AT91C_BASE_PIOB); | |
#endif | |
#if !defined(AT91C_ID_PIOC) | |
PioInterruptHandler(AT91C_ID_PIOABCD, AT91C_BASE_PIOC); | |
#endif | |
#if !defined(AT91C_ID_PIOD) | |
PioInterruptHandler(AT91C_ID_PIOABCD, AT91C_BASE_PIOD); | |
#endif | |
#endif | |
#if defined(AT91C_ID_PIOABCDE) | |
// Treat PIOABCDE interrupts | |
#if !defined(AT91C_ID_PIOA) | |
PioInterruptHandler(AT91C_ID_PIOABCDE, AT91C_BASE_PIOA); | |
#endif | |
#if !defined(AT91C_ID_PIOB) | |
PioInterruptHandler(AT91C_ID_PIOABCDE, AT91C_BASE_PIOB); | |
#endif | |
#if !defined(AT91C_ID_PIOC) | |
PioInterruptHandler(AT91C_ID_PIOABCDE, AT91C_BASE_PIOC); | |
#endif | |
#if !defined(AT91C_ID_PIOD) | |
PioInterruptHandler(AT91C_ID_PIOABCDE, AT91C_BASE_PIOD); | |
#endif | |
#if !defined(AT91C_ID_PIOE) | |
PioInterruptHandler(AT91C_ID_PIOABCDE, AT91C_BASE_PIOE); | |
#endif | |
#endif | |
#if defined(AT91C_ID_PIOCDE) | |
// Treat PIOCDE interrupts | |
#if !defined(AT91C_ID_PIOC) | |
PioInterruptHandler(AT91C_ID_PIOCDE, AT91C_BASE_PIOC); | |
#endif | |
#if !defined(AT91C_ID_PIOD) | |
PioInterruptHandler(AT91C_ID_PIOCDE, AT91C_BASE_PIOD); | |
#endif | |
#if !defined(AT91C_ID_PIOE) | |
PioInterruptHandler(AT91C_ID_PIOCDE, AT91C_BASE_PIOE); | |
#endif | |
#endif | |
} | |
//------------------------------------------------------------------------------ | |
// Global functions | |
//------------------------------------------------------------------------------ | |
//------------------------------------------------------------------------------ | |
/// Initializes the PIO interrupt management logic. The desired priority of PIO | |
/// interrupts must be provided. Calling this function multiple times result in | |
/// the reset of currently configured interrupts. | |
/// \param priority PIO controller interrupts priority. | |
//------------------------------------------------------------------------------ | |
void PIO_InitializeInterrupts(unsigned int priority) | |
{ | |
TRACE_DEBUG("PIO_Initialize()\n\r"); | |
// SANITY_CHECK((priority & ~AT91C_AIC_PRIOR) == 0); | |
// Reset sources | |
numSources = 0; | |
#ifdef AT91C_ID_PIOA | |
// Configure PIO interrupt sources | |
TRACE_DEBUG("PIO_Initialize: Configuring PIOA\n\r"); | |
AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_PIOA; | |
AT91C_BASE_PIOA->PIO_ISR; | |
AT91C_BASE_PIOA->PIO_IDR = 0xFFFFFFFF; | |
IRQ_ConfigureIT(AT91C_ID_PIOA, priority, PIO_IT_InterruptHandler); | |
IRQ_EnableIT(AT91C_ID_PIOA); | |
#endif | |
#ifdef AT91C_ID_PIOB | |
TRACE_DEBUG("PIO_Initialize: Configuring PIOB\n\r"); | |
AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_PIOB; | |
AT91C_BASE_PIOB->PIO_ISR; | |
AT91C_BASE_PIOB->PIO_IDR = 0xFFFFFFFF; | |
IRQ_ConfigureIT(AT91C_ID_PIOB, priority, PIO_IT_InterruptHandler); | |
IRQ_EnableIT(AT91C_ID_PIOB); | |
#endif | |
#ifdef AT91C_ID_PIOC | |
TRACE_DEBUG("PIO_Initialize: Configuring PIOC\n\r"); | |
AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_PIOC; | |
AT91C_BASE_PIOC->PIO_ISR; | |
AT91C_BASE_PIOC->PIO_IDR = 0xFFFFFFFF; | |
IRQ_ConfigureIT(AT91C_ID_PIOC, priority, PIO_IT_InterruptHandler); | |
IRQ_EnableIT(AT91C_ID_PIOC); | |
#endif | |
#ifdef AT91C_ID_PIOD | |
TRACE_DEBUG("PIO_Initialize: Configuring PIOD\n\r"); | |
AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_PIOD; | |
AT91C_BASE_PIOC->PIO_ISR; | |
AT91C_BASE_PIOC->PIO_IDR = 0xFFFFFFFF; | |
IRQ_ConfigureIT(AT91C_ID_PIOD, priority, PIO_IT_InterruptHandler); | |
IRQ_EnableIT(AT91C_ID_PIOD); | |
#endif | |
#ifdef AT91C_ID_PIOE | |
TRACE_DEBUG("PIO_Initialize: Configuring PIOE\n\r"); | |
AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_PIOE; | |
AT91C_BASE_PIOC->PIO_ISR; | |
AT91C_BASE_PIOC->PIO_IDR = 0xFFFFFFFF; | |
IRQ_ConfigureIT(AT91C_ID_PIOE, priority, PIO_IT_InterruptHandler); | |
IRQ_EnableIT(AT91C_ID_PIOE); | |
#endif | |
#if defined(AT91C_ID_PIOABCD) | |
// Treat PIOABCD interrupts | |
#if !defined(AT91C_ID_PIOA) \ | |
&& !defined(AT91C_ID_PIOB) \ | |
&& !defined(AT91C_ID_PIOC) \ | |
&& !defined(AT91C_ID_PIOD) | |
TRACE_DEBUG("PIO_Initialize: Configuring PIOABCD\n\r"); | |
AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_PIOABCD; | |
AT91C_BASE_PIOA->PIO_ISR; | |
AT91C_BASE_PIOA->PIO_IDR = 0xFFFFFFFF; | |
IRQ_ConfigureIT(AT91C_ID_PIOABCD, priority, PIO_IT_InterruptHandler); | |
IRQ_EnableIT(AT91C_ID_PIOABCD); | |
#endif | |
#endif | |
#if defined(AT91C_ID_PIOABCDE) | |
// Treat PIOABCDE interrupts | |
#if !defined(AT91C_ID_PIOA) \ | |
&& !defined(AT91C_ID_PIOB) \ | |
&& !defined(AT91C_ID_PIOC) \ | |
&& !defined(AT91C_ID_PIOD) \ | |
&& !defined(AT91C_ID_PIOE) | |
TRACE_DEBUG("PIO_Initialize: Configuring PIOABCDE\n\r"); | |
AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_PIOABCDE; | |
AT91C_BASE_PIOA->PIO_ISR; | |
AT91C_BASE_PIOA->PIO_IDR = 0xFFFFFFFF; | |
IRQ_ConfigureIT(AT91C_ID_PIOABCDE, priority, PIO_IT_InterruptHandler); | |
IRQ_EnableIT(AT91C_ID_PIOABCDE); | |
#endif | |
#endif | |
#if defined(AT91C_ID_PIOCDE) | |
// Treat PIOCDE interrupts | |
#if !defined(AT91C_ID_PIOC) \ | |
&& !defined(AT91C_ID_PIOD) \ | |
&& !defined(AT91C_ID_PIOE) | |
TRACE_DEBUG("PIO_Initialize: Configuring PIOC\n\r"); | |
AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_PIOCDE; | |
AT91C_BASE_PIOC->PIO_ISR; | |
AT91C_BASE_PIOC->PIO_IDR = 0xFFFFFFFF; | |
IRQ_ConfigureIT(AT91C_ID_PIOCDE, priority, PIO_IT_InterruptHandler); | |
IRQ_EnableIT(AT91C_ID_PIOCDE); | |
#endif | |
#endif | |
} | |
//------------------------------------------------------------------------------ | |
/// Configures a PIO or a group of PIO to generate an interrupt on status | |
/// change. The provided interrupt handler will be called with the triggering | |
/// pin as its parameter (enabling different pin instances to share the same | |
/// handler). | |
/// \param pPin Pointer to a Pin instance. | |
/// \param handler Interrupt handler function pointer. | |
//------------------------------------------------------------------------------ | |
void PIO_ConfigureIt(const Pin *pPin, void (*handler)(const Pin *)) | |
{ | |
InterruptSource *pSource; | |
TRACE_DEBUG("PIO_ConfigureIt()\n\r"); | |
SANITY_CHECK(pPin); | |
ASSERT(numSources < MAX_INTERRUPT_SOURCES, | |
"-F- PIO_ConfigureIt: Increase MAX_INTERRUPT_SOURCES\n\r"); | |
// Define new source | |
TRACE_DEBUG("PIO_ConfigureIt: Defining new source #%d.\n\r", numSources); | |
pSource = &(pSources[numSources]); | |
pSource->pPin = pPin; | |
pSource->handler = handler; | |
numSources++; | |
} | |
//------------------------------------------------------------------------------ | |
/// Enables the given interrupt source if it has been configured. The status | |
/// register of the corresponding PIO controller is cleared prior to enabling | |
/// the interrupt. | |
/// \param pPin Interrupt source to enable. | |
//------------------------------------------------------------------------------ | |
void PIO_EnableIt(const Pin *pPin) | |
{ | |
TRACE_DEBUG("PIO_EnableIt()\n\r"); | |
SANITY_CHECK(pPin); | |
#ifndef NOASSERT | |
unsigned int i = 0; | |
unsigned char found = 0; | |
while ((i < numSources) && !found) { | |
if (pSources[i].pPin == pPin) { | |
found = 1; | |
} | |
i++; | |
} | |
ASSERT(found, "-F- PIO_EnableIt: Interrupt source has not been configured\n\r"); | |
#endif | |
pPin->pio->PIO_ISR; | |
pPin->pio->PIO_IER = pPin->mask; | |
#if defined(AT91C_PIOA_AIMMR) | |
//PIO3 with additional interrupt support | |
//configure additional interrupt mode registers | |
if(pPin->mask&pPin->itMode.itMask) { | |
//enable additional interrupt mode | |
pPin->pio->PIO_AIMER = pPin->itMode.itMask; | |
if(pPin->mask&pPin->itMode.edgeLvlSel) | |
//if bit field of selected pin is 1, set as Level detection source | |
pPin->pio->PIO_LSR = pPin->itMode.edgeLvlSel; | |
else | |
//if bit field of selected pin is 0, set as Edge detection source | |
pPin->pio->PIO_ESR = ~(pPin->itMode.edgeLvlSel); | |
if(pPin->mask&pPin->itMode.lowFallOrRiseHighSel) | |
//if bit field of selected pin is 1, set as Rising Edge/High level detection event | |
pPin->pio->PIO_REHLSR = pPin->itMode.lowFallOrRiseHighSel; | |
else | |
//if bit field of selected pin is 0, set as Falling Edge/Low level detection event | |
pPin->pio->PIO_FELLSR = ~(pPin->itMode.lowFallOrRiseHighSel); | |
} | |
#endif | |
} | |
//------------------------------------------------------------------------------ | |
/// Disables a given interrupt source, with no added side effects. | |
/// \param pPin Interrupt source to disable. | |
//------------------------------------------------------------------------------ | |
void PIO_DisableIt(const Pin *pPin) | |
{ | |
SANITY_CHECK(pPin); | |
TRACE_DEBUG("PIO_DisableIt()\n\r"); | |
pPin->pio->PIO_IDR = pPin->mask; | |
#if defined(AT91C_PIOA_AIMMR) | |
if(pPin->mask & pPin->itMode.itMask) | |
//disable additional interrupt mode | |
pPin->pio->PIO_AIMDR = pPin->mask & pPin->itMode.itMask; | |
#endif | |
} | |
#if defined(cortexm3) | |
//------------------------------------------------------------------------------ | |
/// Override cortex-m3's default PIOA irq handler | |
//------------------------------------------------------------------------------ | |
void PIOA_IrqHandler(void) | |
{ | |
#if defined(AT91C_ID_PIOA) | |
// Treat PIOA interrupts | |
PioInterruptHandler(AT91C_ID_PIOA, AT91C_BASE_PIOA); | |
#endif | |
} | |
//------------------------------------------------------------------------------ | |
/// Override cortex-m3's default PIOB irq handler | |
//------------------------------------------------------------------------------ | |
void PIOB_IrqHandler(void) | |
{ | |
#if defined(AT91C_ID_PIOB) | |
// Treat PIOA interrupts | |
PioInterruptHandler(AT91C_ID_PIOB, AT91C_BASE_PIOB); | |
#endif | |
} | |
//------------------------------------------------------------------------------ | |
/// Override cortex-m3's default PIOC irq handler | |
//------------------------------------------------------------------------------ | |
void PIOC_IrqHandler(void) | |
{ | |
#if defined(AT91C_ID_PIOC) | |
// Treat PIOA interrupts | |
PioInterruptHandler(AT91C_ID_PIOC, AT91C_BASE_PIOC); | |
#endif | |
} | |
#endif |