// Copyright 2009 Olivier Gillet.
//
// Author: Olivier Gillet (ol.gillet@gmail.com)
//
// 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 .
//
// -----------------------------------------------------------------------------
//
// An alternative gpio library based on templates.
//
// Examples of use:
//
// NumberedGpio<3>::set_mode(DIGITAL_INPUT)
// NumberedGpio<4>::set_mode(DIGITAL_OUTPUT)
// NumberedGpio<3>::value()
// NumberedGpio<4>::High()
// NumberedGpio<4>::Low()
// NumberedGpio<4>::set_value(1)
// NumberedGpio<4>::set_value(0)
#ifndef AVRLIB_GPIO_H_
#define AVRLIB_GPIO_H_
#include
#include "avrlib/avrlib.h"
#include "avrlib/timer.h"
namespace avrlib {
enum PinMode {
DIGITAL_INPUT = 0,
DIGITAL_OUTPUT = 1,
PWM_OUTPUT = 2
};
// All the registers used in the following definitions are wrapped here.
IORegister(DDRB);
IORegister(DDRC);
IORegister(DDRD);
IORegister(PORTB);
IORegister(PORTC);
IORegister(PORTD);
IORegister(PINB);
IORegister(PINC);
IORegister(PIND);
// Represents a i/o port, which has input, output and mode registers.
template
struct Port {
typedef InputRegister Input;
typedef OutputRegister Output;
typedef ModeRegister Mode;
};
// Definition of I/O ports.
typedef Port PortB;
typedef Port PortC;
typedef Port PortD;
#if defined(ATMEGA164P) || defined(ATMEGA324P) || defined(ATMEGA644P) || defined(ATMEGA1284P) || defined(ATMEGA2560)
IORegister(DDRA);
IORegister(PORTA);
IORegister(PINA);
typedef Port PortA;
#endif
#if defined (ATMEGA640) || defined (ATMEGA1280) || defined(ATMEGA2560)
IORegister(DDRE);
IORegister(DDRF);
IORegister(DDRG);
IORegister(DDRH);
IORegister(DDRJ);
IORegister(DDRK);
IORegister(DDRL);
IORegister(PORTE);
IORegister(PORTF);
IORegister(PORTG);
IORegister(PORTH);
IORegister(PORTJ);
IORegister(PORTK);
IORegister(PORTL);
IORegister(PINE);
IORegister(PINF);
IORegister(PING);
IORegister(PINH);
IORegister(PINJ);
IORegister(PINK);
IORegister(PINL);
typedef Port PortE;
typedef Port PortF;
typedef Port PortG;
typedef Port PortH;
typedef Port PortJ;
typedef Port PortK;
typedef Port PortL;
#endif
// The actual implementation of a pin, not very convenient to use because it
// requires the actual parameters of the pin to be passed as template
// arguments.
template
struct GpioImpl {
typedef BitInRegister ModeBit;
typedef BitInRegister OutputBit;
typedef BitInRegister InputBit;
typedef PwmChannel Pwm;
static inline void set_mode(uint8_t mode) {
if (mode == DIGITAL_INPUT) {
ModeBit::clear();
} else if (mode == DIGITAL_OUTPUT || mode == PWM_OUTPUT) {
ModeBit::set();
}
if (mode == PWM_OUTPUT) {
PwmChannel::Start();
} else {
PwmChannel::Stop();
}
}
static inline void High() {
OutputBit::set();
}
static inline void Low() {
OutputBit::clear();
}
static inline void Toggle() {
OutputBit::toggle();
}
static inline void set_value(uint8_t value) {
if (value == 0) {
Low();
} else {
High();
}
}
static inline void set_pwm_value(uint8_t value) {
if (PwmChannel::has_pwm) {
PwmChannel::Write(value);
} else {
set_value(value);
}
}
static inline uint8_t value() {
return InputBit::value();
}
static inline uint8_t is_high() {
return InputBit::value();
}
static inline uint8_t is_low() {
return InputBit::value() == 0;
}
};
template
struct Gpio {
typedef GpioImpl Impl;
static void High() { Impl::High(); }
static void Low() { Impl::Low(); }
static void Toggle() { Impl::Toggle(); }
static void set_mode(uint8_t mode) { Impl::set_mode(mode); }
static void set_value(uint8_t value) { Impl::set_value(value); }
static void set_pwm_value(uint8_t value) { Impl::set_pwm_value(value); }
static uint8_t value() { return Impl::value(); }
static uint8_t is_low() { return Impl::is_low(); }
static uint8_t is_high() { return Impl::is_high(); }
};
struct DummyGpio {
static void High() { }
static void Low() { }
static void set_mode(uint8_t mode) { }
static void set_value(uint8_t value) { }
static void set_pwm_value(uint8_t value) { }
static uint8_t value() { return 0; }
static uint8_t is_low() { return 0; }
static uint8_t is_high() { return 0; }
};
template
struct Inverter {
static void High() { Gpio::Low(); }
static void Low() { Gpio::High(); }
static void set_mode(uint8_t mode) { Gpio::set_mode(mode); }
static void set_value(uint8_t value) { Gpio::set_value(!value); }
static void set_pwm_value(uint8_t value) { Gpio::set_pwm_value(~value); }
static uint8_t value() { return !Gpio::value(); }
static uint8_t is_low() { return !Gpio::is_low(); }
static uint8_t is_high() { return !Gpio::is_high(); }
};
template
struct DigitalInput {
enum {
buffer_size = 0,
data_size = 1,
};
static void Init() {
gpio::set_mode(DIGITAL_INPUT);
}
static void EnablePullUpResistor() {
gpio::High();
}
static void DisablePullUpResistor() {
gpio::Low();
}
static uint8_t Read() {
return gpio::value();
}
};
// A template that will be specialized for each pin, allowing the pin number to
// be specified as a template parameter.
template
struct NumberedGpioInternal { };
// Macro to make the pin definitions (template specializations) easier to read.
#define SetupGpio(n, port, timer, bit) \
template<> struct NumberedGpioInternal { \
typedef GpioImpl Impl; };
// Pin definitions for ATmega lineup
#if defined(ATMEGA48P) || defined(ATMEGA88P) || defined(ATMEGA168P) || defined(ATMEGA328P)
SetupGpio(0, PortD, NoPwmChannel, 0);
SetupGpio(1, PortD, NoPwmChannel, 1);
SetupGpio(2, PortD, NoPwmChannel, 2);
SetupGpio(3, PortD, PwmChannel2B, 3);
SetupGpio(4, PortD, NoPwmChannel, 4);
SetupGpio(5, PortD, PwmChannel0B, 5);
SetupGpio(6, PortD, PwmChannel0A, 6);
SetupGpio(7, PortD, NoPwmChannel, 7);
SetupGpio(8, PortB, NoPwmChannel, 0);
SetupGpio(9, PortB, PwmChannel1A, 1);
SetupGpio(10, PortB, PwmChannel1B, 2);
SetupGpio(11, PortB, PwmChannel2A, 3);
SetupGpio(12, PortB, NoPwmChannel, 4);
SetupGpio(13, PortB, NoPwmChannel, 5);
SetupGpio(14, PortC, NoPwmChannel, 0);
SetupGpio(15, PortC, NoPwmChannel, 1);
SetupGpio(16, PortC, NoPwmChannel, 2);
SetupGpio(17, PortC, NoPwmChannel, 3);
SetupGpio(18, PortC, NoPwmChannel, 4);
SetupGpio(19, PortC, NoPwmChannel, 5);
SetupGpio(255, PortB, NoPwmChannel, 0);
typedef Gpio SpiSCK;
typedef Gpio SpiMISO;
typedef Gpio SpiMOSI;
typedef Gpio SpiSS;
typedef Gpio UartSpi0XCK;
typedef Gpio UartSpi0TX;
typedef Gpio UartSpi0RX;
#define HAS_USART0
#elif defined(ATMEGA164P) || defined(ATMEGA324P) || defined(ATMEGA644P) || defined(ATMEGA1284P)
SetupGpio(0, PortB, NoPwmChannel, 0);
SetupGpio(1, PortB, NoPwmChannel, 1);
SetupGpio(2, PortB, NoPwmChannel, 2);
SetupGpio(3, PortB, PwmChannel0A, 3);
SetupGpio(4, PortB, PwmChannel0B, 4);
SetupGpio(5, PortB, NoPwmChannel, 5);
SetupGpio(6, PortB, NoPwmChannel, 6);
SetupGpio(7, PortB, NoPwmChannel, 7);
SetupGpio(8, PortD, NoPwmChannel, 0);
SetupGpio(9, PortD, NoPwmChannel, 1);
SetupGpio(10, PortD, NoPwmChannel, 2);
SetupGpio(11, PortD, NoPwmChannel, 3);
SetupGpio(12, PortD, PwmChannel1B, 4);
SetupGpio(13, PortD, PwmChannel1A, 5);
SetupGpio(14, PortD, PwmChannel2B, 6);
SetupGpio(15, PortD, PwmChannel2A, 7);
SetupGpio(16, PortC, NoPwmChannel, 0);
SetupGpio(17, PortC, NoPwmChannel, 1);
SetupGpio(18, PortC, NoPwmChannel, 2);
SetupGpio(19, PortC, NoPwmChannel, 3);
SetupGpio(20, PortC, NoPwmChannel, 4);
SetupGpio(21, PortC, NoPwmChannel, 5);
SetupGpio(22, PortC, NoPwmChannel, 6);
SetupGpio(23, PortC, NoPwmChannel, 7);
SetupGpio(255, PortB, NoPwmChannel, 0);
typedef Gpio SpiSCK;
typedef Gpio SpiMISO;
typedef Gpio SpiMOSI;
typedef Gpio SpiSS;
typedef Gpio UartSpi0XCK;
typedef Gpio UartSpi0TX;
typedef Gpio UartSpi0RX;
typedef Gpio UartSpi1XCK;
typedef Gpio UartSpi1TX;
typedef Gpio UartSpi1RX;
#define HAS_USART0
#define HAS_USART1
#if defined(ATMEGA1284P) || defined(ATMEGA640) || defined(ATMEGA1280) \
|| defined(ATMEGA2560)
#define HAS_TIMER3
#endif
#elif defined (ATMEGA640) || defined(ATMEGA1280) || defined(ATMEGA2560)
SetupGpio(0, PortB, NoPwmChannel, 0);
SetupGpio(1, PortB, NoPwmChannel, 1);
SetupGpio(2, PortB, NoPwmChannel, 2);
SetupGpio(3, PortB, NoPwmChannel, 3);
SetupGpio(4, PortB, PwmChannel2A, 4);
SetupGpio(5, PortB, PwmChannel1A, 5);
SetupGpio(6, PortB, PwmChannel1B, 6);
SetupGpio(7, PortB, PwmChannel0A, 7);
SetupGpio(8, PortD, NoPwmChannel, 0);
SetupGpio(9, PortD, NoPwmChannel, 1);
SetupGpio(10, PortD, NoPwmChannel, 2);
SetupGpio(11, PortD, NoPwmChannel, 3);
SetupGpio(12, PortD, NoPwmChannel, 4);
SetupGpio(13, PortD, NoPwmChannel, 5);
SetupGpio(14, PortD, NoPwmChannel, 6);
SetupGpio(15, PortD, NoPwmChannel, 7);
SetupGpio(16, PortC, NoPwmChannel, 0);
SetupGpio(17, PortC, NoPwmChannel, 1);
SetupGpio(18, PortC, NoPwmChannel, 2);
SetupGpio(19, PortC, NoPwmChannel, 3);
SetupGpio(20, PortC, NoPwmChannel, 4);
SetupGpio(21, PortC, NoPwmChannel, 5);
SetupGpio(22, PortC, NoPwmChannel, 6);
SetupGpio(23, PortC, NoPwmChannel, 7);
SetupGpio(24, PortE, NoPwmChannel, 0);
SetupGpio(25, PortE, NoPwmChannel, 1);
SetupGpio(26, PortE, NoPwmChannel, 2);
SetupGpio(27, PortE, NoPwmChannel, 3);
SetupGpio(28, PortE, NoPwmChannel, 4);
SetupGpio(29, PortE, NoPwmChannel, 5);
SetupGpio(30, PortE, NoPwmChannel, 6);
SetupGpio(31, PortE, NoPwmChannel, 7);
SetupGpio(32, PortF, NoPwmChannel, 0);
SetupGpio(33, PortF, NoPwmChannel, 1);
SetupGpio(34, PortF, NoPwmChannel, 2);
SetupGpio(35, PortF, NoPwmChannel, 3);
SetupGpio(36, PortF, NoPwmChannel, 4);
SetupGpio(37, PortF, NoPwmChannel, 5);
SetupGpio(38, PortF, NoPwmChannel, 6);
SetupGpio(39, PortF, NoPwmChannel, 7);
SetupGpio(40, PortG, NoPwmChannel, 0);
SetupGpio(41, PortG, NoPwmChannel, 1);
SetupGpio(42, PortG, NoPwmChannel, 2);
SetupGpio(43, PortG, NoPwmChannel, 3);
SetupGpio(44, PortG, NoPwmChannel, 4);
SetupGpio(45, PortG, NoPwmChannel, 5);
SetupGpio(46, PortG, NoPwmChannel, 6);
SetupGpio(47, PortG, NoPwmChannel, 7);
SetupGpio(48, PortH, NoPwmChannel, 0);
SetupGpio(49, PortH, NoPwmChannel, 1);
SetupGpio(50, PortH, NoPwmChannel, 2);
SetupGpio(51, PortH, NoPwmChannel, 3);
SetupGpio(52, PortH, NoPwmChannel, 4);
SetupGpio(53, PortH, NoPwmChannel, 5);
SetupGpio(54, PortH, NoPwmChannel, 6);
SetupGpio(55, PortH, NoPwmChannel, 7);
SetupGpio(56, PortJ, NoPwmChannel, 0);
SetupGpio(57, PortJ, NoPwmChannel, 1);
SetupGpio(58, PortJ, NoPwmChannel, 2);
SetupGpio(59, PortJ, NoPwmChannel, 3);
SetupGpio(60, PortJ, NoPwmChannel, 4);
SetupGpio(61, PortJ, NoPwmChannel, 5);
SetupGpio(62, PortJ, NoPwmChannel, 6);
SetupGpio(63, PortJ, NoPwmChannel, 7);
SetupGpio(64, PortK, NoPwmChannel, 0);
SetupGpio(65, PortK, NoPwmChannel, 1);
SetupGpio(66, PortK, NoPwmChannel, 2);
SetupGpio(67, PortK, NoPwmChannel, 3);
SetupGpio(68, PortK, NoPwmChannel, 4);
SetupGpio(69, PortK, NoPwmChannel, 5);
SetupGpio(70, PortK, NoPwmChannel, 6);
SetupGpio(71, PortK, NoPwmChannel, 7);
SetupGpio(72, PortL, NoPwmChannel, 0);
SetupGpio(73, PortL, NoPwmChannel, 1);
SetupGpio(74, PortL, NoPwmChannel, 2);
SetupGpio(75, PortL, NoPwmChannel, 3);
SetupGpio(76, PortL, NoPwmChannel, 4);
SetupGpio(77, PortL, NoPwmChannel, 5);
SetupGpio(78, PortL, NoPwmChannel, 6);
SetupGpio(79, PortL, NoPwmChannel, 7);
typedef Gpio SpiSS;
typedef Gpio SpiSCK;
typedef Gpio SpiMOSI;
typedef Gpio SpiMISO;
typedef Gpio UartSpi0XCK;
typedef Gpio UartSpi0TX;
typedef Gpio UartSpi0RX;
typedef Gpio UartSpi1XCK;
typedef Gpio UartSpi1TX;
typedef Gpio UartSpi1RX;
typedef Gpio UartSpi2XCK;
typedef Gpio UartSpi2TX;
typedef Gpio UartSpi2RX;
typedef Gpio UartSpi3XCK;
typedef Gpio UartSpi3TX;
typedef Gpio UartSpi3RX;
#define HAS_USART0
#define HAS_USART1
#define HAS_USART2
#define HAS_USART3
#else
#error Unsupported MCU type
#endif
// Two specializations of the numbered pin template, one which clears the timer
// for each access to the PWM pins, as does the original Arduino wire lib,
// the other that does not (use with care!).
template
struct NumberedGpio {
typedef typename NumberedGpioInternal::Impl Impl;
static void High() { Impl::High(); }
static void Low() { Impl::Low(); }
static void set_mode(uint8_t mode) { Impl::set_mode(mode); }
static void set_value(uint8_t value) { Impl::set_value(value); }
static void set_pwm_value(uint8_t value) { Impl::set_pwm_value(value); }
static uint8_t value() { return Impl::value(); }
};
template
struct PwmOutput {
enum {
buffer_size = 0,
data_size = 8,
};
static void Init() {
NumberedGpio::set_mode(PWM_OUTPUT);
}
static void Write(uint8_t value) {
return NumberedGpio::set_pwm_value(value);
}
static void Stop() {
NumberedGpio::Impl::Pwm::Stop();
}
static void Start() {
NumberedGpio::Impl::Pwm::Start();
}
};
} // namespace avrlib
#endif // AVRLIB_GPIO_H_