This is something that I started a while ago. The register names used for microcontrollers tend to be very cryptic, and in order to understand some of the code involving interrupts, it is almost always necessary to read the data sheet for the microcontroller. This can make code difficult to follow and read, so I wrapped some of the low-level interrupt code with more verbose or expressive templates.
The code basically allows one to write:
// Enables INT0
avt::external_interrupt<0>::enable();
// Trigger interrupt on rising edge.
avt::external_interrupt<0>::state(avt::Rising);
// Disables INT0
avt::external_interrupt<0>::disable();
instead of (I really don't remember what this does)
MCUCR = (MCUCR & ~((1 << ISC00) | (1 << ISC01))) | (mode << ISC00);
GIMSK |= (1 << INT0);
Here is the code. It is not finished. I wrote this last year and completely forgot about it.
/*
*
* Copyright (c) 2010 - 2011 Devin DeLong
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef AVT_INTERRUPT_EXTERNAL_INTERRUPT_HPP
#define AVT_INTERRUPT_EXTERNAL_INTERRUPT_HPP
#include <avr/io.h>
#include <stdint.h>
#if defined(__AVR_ATmega8__)
#ifndef EICRA
#define EICRA MCUCR
#endif
#ifndef EIMSK
#define EIMSK GICR
#endif
#endif
/** Generates INTN preprocessor names, ex: INT0.
*/
#define GEN_INT(__PIN) (INT ## __PIN)
/** Generates the ISC(N)(E) preprocessor names, ex: ISC01.
*/
#define GEN_ISC(__PIN, __E) (ISC ## __PIN ## __E)
/** Generates the EICRL preprocessor names, ex: EICRA.
*/
#define GEN_EICR(__PORT_GROUP) (EICR ## __PORT_GROUP)
/** Resets the EICRA register.
*/
#define RESET_EXTERNAL_INTERRUPT_CONTROL_REGISTER(__PIN, __PORT_GROUP) \
GEN_EICR(__PORT_GROUP) &= ~((1 << GEN_ISC(__PIN, 1)) | (1 << GEN_ISC(__PIN, 0)));
/** Sets the EICRA register.
*/
#define SET_EXTERNAL_INTERRUPT_CONTROL_REGISTER(__PIN, __T1, __T2, __PORT_GROUP) \
GEN_EICR(__PORT_GROUP) = (__T1 << GEN_ISC(__PIN, 1)) | (__T2 << GEN_ISC(__PIN, 0));
/** Enables external interrupts on INTN.
*/
#define enableExternalInterrupt(__PIN) \
EIMSK |= (1 << GEN_INT(__PIN));
/** Disables external interrupts on INTN.
*/
#define disableExternalInterrupt(__PIN) \
EIMSK &= ~(1 << GEN_INT(__PIN));
#define GENERATE_EXTERNAL_INTERRUPT(__PIN, __PORT_GROUP)\
template <>\
class external_interrupt<__PIN> \
{\
public:\
static void enable() { enableExternalInterrupt(__PIN) }\
static void disable() { disableExternalInterrupt(__PIN) }\
\
static void state(State state)\
{\
RESET_EXTERNAL_INTERRUPT_CONTROL_REGISTER(__PIN, __PORT_GROUP)\
switch (state)\
{\
case Rising: SET_EXTERNAL_INTERRUPT_CONTROL_REGISTER(__PIN, 1, 1, __PORT_GROUP)\
break;\
case Falling: SET_EXTERNAL_INTERRUPT_CONTROL_REGISTER(__PIN, 1, 0, __PORT_GROUP)\
break;\
case Change: SET_EXTERNAL_INTERRUPT_CONTROL_REGISTER(__PIN, 0, 1, __PORT_GROUP)\
break;\
case Low:\
break;\
}\
}\
};
namespace avt
{
namespace interrupt
{
enum State
{
Rising,
Falling,
Change,
Low
};
// Incomplete type.
template <uint8_t N>
class external_interrupt;
GENERATE_EXTERNAL_INTERRUPT(0, A)
GENERATE_EXTERNAL_INTERRUPT(1, A)
#if defined(__AVR_ATmega1280__)
GENERATE_EXTERNAL_INTERRUPT(2, A)
GENERATE_EXTERNAL_INTERRUPT(3, A)
GENERATE_EXTERNAL_INTERRUPT(4, B)
GENERATE_EXTERNAL_INTERRUPT(5, B)
GENERATE_EXTERNAL_INTERRUPT(6, B)
GENERATE_EXTERNAL_INTERRUPT(7, B)
#endif
}
using interrupt::external_interrupt;
using interrupt::Rising;
using interrupt::Falling;
using interrupt::Change;
using interrupt::Low;
using interrupt::State;
}
#undef GEN_ISC
#undef GEN_INT
#undef GEN_EICR
#undef RESET_EXTERNAL_INTERRUPT_CONTROL_REGISTER
#undef SET_EXTERNAL_INTERRUPT_CONTROL_REGISTER
#undef GENERATE_EXTERNAL_INTERRUPT
#endif /* AVT_INTERRUPT_EXTERNAL_INTERRUPT_HPP */
And yes, I know about attachInterrupt(). I like exploring different ways templates can be used in embedded projects.