Skip to content
Snippets Groups Projects
WInterrupts.c 6.15 KiB
/*
  Copyright (c) 2014 Arduino.  All right reserved.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  See the GNU Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include "WInterrupts.h"
#include "variant.h"
#include "wiring_digital.h"

#include <string.h>

#ifdef __cplusplus
extern "C" {
#endif

static struct
{
  uint32_t _ulPin ;
  voidFuncPtr _callback ;
} callbacksInt[EXTERNAL_NUM_INTERRUPTS] ;

/* Configure I/O interrupt sources */
static void __initialize()
{
#if 0
  int i ;

  for ( i = 0 ; i < EXTERNAL_NUM_INTERRUPTS ; i++ )
  {
    callbacksInt[i]._callback = NULL ;
  }
#else
  memset( callbacksInt, 0, sizeof( callbacksInt ) ) ;
#endif

  NVIC_DisableIRQ( EIC_IRQn ) ;
  NVIC_ClearPendingIRQ( EIC_IRQn ) ;
  NVIC_SetPriority( EIC_IRQn, 0 ) ;
  NVIC_EnableIRQ( EIC_IRQn ) ;



  // Enable GCLK for IEC (External Interrupt Controller)
  GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID( GCM_EIC )) ;

/* Shall we do that?
  // Do a software reset on EIC
  EIC->CTRL.SWRST.bit = 1 ;

  while ( (EIC->CTRL.SWRST.bit == 1) && (EIC->STATUS.SYNCBUSY.bit == 1) )
  {
    // Waiting for synchronisation
  }
*/

  // Enable EIC
  EIC->CTRL.bit.ENABLE = 1 ;

  while ( EIC->STATUS.bit.SYNCBUSY == 1 )
  {
    // Waiting for synchronisation
  }

}

/*
 * \brief Specifies a named Interrupt Service Routine (ISR) to call when an interrupt occurs.
 *        Replaces any previous function that was attached to the interrupt.
 */
//void attachInterrupt( uint32_t ulPin, void (*callback)(void), EExt_IntMode mode )
//void attachInterrupt( uint32_t ulPin, voidFuncPtr callback, EExt_IntMode mode )
void attachInterrupt( uint32_t ulPin, voidFuncPtr callback, uint32_t ulMode )
{
  static int enabled = 0 ;
  uint32_t ulConfig ;
  uint32_t ulPos ;

  if ( digitalPinToInterrupt( ulPin ) == NOT_AN_INTERRUPT )
  {
    return ;
  }

  if ( !enabled )
  {
    __initialize() ;
    enabled = 1 ;
  }

  // Assign pin to EIC
  pinPeripheral( ulPin, PIO_EXTINT ) ;

  // Assign callback to interrupt
  callbacksInt[digitalPinToInterrupt( ulPin )]._ulPin = ulPin ;
  callbacksInt[digitalPinToInterrupt( ulPin )]._callback = callback ;

  // Check if normal interrupt or NMI
  if ( ulPin != 2 )
  {
    // Look for right CONFIG register to be addressed
    if ( digitalPinToInterrupt( ulPin ) > EXTERNAL_INT_7 )
    {
      ulConfig = 1 ;
    }
    else
    {
      ulConfig = 0 ;
    }

    // Configure the interrupt mode
    ulPos = ((digitalPinToInterrupt( ulPin ) - (8*ulConfig) ) << 2) ;
    switch ( ulMode )
    {
      case LOW:
        EIC->CONFIG[ulConfig].reg |= EIC_CONFIG_SENSE0_LOW_Val << ulPos ;
      break ;

      case HIGH:
        // EIC->CONFIG[ulConfig].reg = EIC_CONFIG_SENSE0_HIGH_Val << ((digitalPinToInterrupt( ulPin ) >> ulConfig ) << ulPos) ;
        EIC->CONFIG[ulConfig].reg |= EIC_CONFIG_SENSE0_HIGH_Val << ulPos ;
      break ;

      case CHANGE:
        // EIC->CONFIG[ulConfig].reg = EIC_CONFIG_SENSE0_BOTH_Val << ((digitalPinToInterrupt( ulPin ) >> ulConfig ) << ulPos) ;
        EIC->CONFIG[ulConfig].reg |= EIC_CONFIG_SENSE0_BOTH_Val << ulPos ;
      break ;

      case FALLING:
        // EIC->CONFIG[ulConfig].reg = EIC_CONFIG_SENSE0_FALL_Val << ((digitalPinToInterrupt( ulPin ) >> ulConfig ) << ulPos) ;
        EIC->CONFIG[ulConfig].reg |= EIC_CONFIG_SENSE0_FALL_Val << ulPos ;
      break ;

      case RISING:
        // EIC->CONFIG[ulConfig].reg = EIC_CONFIG_SENSE0_RISE_Val << ((digitalPinToInterrupt( ulPin ) >> ulConfig ) << ulPos) ;
        EIC->CONFIG[ulConfig].reg |= EIC_CONFIG_SENSE0_RISE_Val << ulPos ;
      break ;
    }

    // Enable the interrupt
    EIC->INTENSET.reg = EIC_INTENSET_EXTINT( 1 << digitalPinToInterrupt( ulPin ) ) ;
  }
  else // Handles NMI on pin 2
  {
    // Configure the interrupt mode
    switch ( ulMode )
    {
      case LOW:
        EIC->NMICTRL.reg = EIC_NMICTRL_NMISENSE_LOW ;
      break ;

      case HIGH:
        EIC->NMICTRL.reg = EIC_NMICTRL_NMISENSE_HIGH ;
      break ;

      case CHANGE:
        EIC->NMICTRL.reg = EIC_NMICTRL_NMISENSE_BOTH ;
      break ;

      case FALLING:
        EIC->NMICTRL.reg = EIC_NMICTRL_NMISENSE_FALL ;
      break ;

      case RISING:
        EIC->NMICTRL.reg= EIC_NMICTRL_NMISENSE_RISE ;
      break ;
    }

    // Enable the interrupt
    EIC->INTENSET.reg = EIC_INTENSET_EXTINT( 1 << digitalPinToInterrupt( ulPin ) ) ;
  }
}

/*
 * \brief Turns off the given interrupt.
 */
void detachInterrupt( uint32_t ulPin )
{
/*
  // Retrieve pin information
  Pio *pio = g_APinDescription[pin].pPort;
  uint32_t mask = g_APinDescription[pin].ulPin;

  // Disable interrupt
  pio->PIO_IDR = mask;
*/
  if ( digitalPinToInterrupt( ulPin ) == NOT_AN_INTERRUPT )
  {
    return ;
  }

  EIC->INTENCLR.reg = EIC_INTENCLR_EXTINT( 1 << digitalPinToInterrupt( ulPin ) ) ;
}

/*
 * External Interrupt Controller NVIC Interrupt Handler
 */
void EIC_Handler( void )
{
  uint32_t ul ;

  // Test NMI first
  if ( (EIC->NMIFLAG.reg & EIC_NMIFLAG_NMI) == EIC_NMIFLAG_NMI )
  {
    // Call the callback function if assigned
    if ( callbacksInt[EXTERNAL_INT_NMI]._callback != NULL )
    {
      callbacksInt[EXTERNAL_INT_NMI]._callback() ;
    }

    // Clear the interrupt
    EIC->NMIFLAG.reg = EIC_NMIFLAG_NMI ;
  }

  // Test the 16 normal interrupts
  for ( ul = EXTERNAL_INT_0 ; ul < EXTERNAL_INT_NMI ; ul++ )
  {
    if ( (EIC->INTFLAG.reg & ( 1 << ul ) ) != 0 )
    {
      // Call the callback function if assigned
      if ( callbacksInt[ul]._callback != NULL )
      {
        callbacksInt[ul]._callback() ;
      }

      // Clear the interrupt
      EIC->INTFLAG.reg = 1 << ul ;
    }
  }
}

#ifdef __cplusplus
}
#endif