I truly don't understand why people use up precious RAM for stuff that doesn't change....
I have a Arduino Template Library (see signature) that I am now moving towards plain AVR level.
My take on GPIO abstraction is for Arduino:
template<const byte BoardPinNumber>
class DigitalOutputPin
{
public:
/*
The ctor sets the pinMode.
*/
DigitalOutputPin()
{
pinMode(BoardPinNumber, OUTPUT);
}
/*
The ctor sets the pinMode and initialValue.
*/
DigitalOutputPin(bool initialValue)
{
pinMode(BoardPinNumber, OUTPUT);
Write(initialValue);
}
/*
Writes the value to the BoardPinNumder.
*/
inline void Write(bool value) const
{
digitalWrite(BoardPinNumber, value);
}
/*
Returns the BoardPinNumber template parameter.
*/
inline byte getPinNumber() const
{
return BoardPinNumber;
}
};
And for plain AVR
template<const Ports PortId, const Pins PinId>
class DigitalOutputPin
{
public:
/*
The ctor sets the Pin on the Port to Output.
*/
DigitalOutputPin()
{
Port<PortId>::SetDirection(PinId, Output);
//Write(false);
}
/*
The ctor sets the pinMode and initialValue.
*/
DigitalOutputPin(bool initialValue)
{
Port<PortId>::SetDirection(PinId, Output);
Write(initialValue);
}
/*
Writes the value to the BoardPinNumder.
*/
inline void Write(bool value) const
{
Port<PortId>::Write(PinId, value);
}
/*
Returns the PortId template parameter.
*/
inline uint8_t getPortNumber() const
{
return PortId;
}
/*
Returns the PinId template parameter.
*/
inline uint8_t getPinNumber() const
{
return PinId;
}
};
Which uses the Port class
enum Ports //: uint8_t
{
#ifdef PORTA
PortA = 0,
#endif //PORTA
#ifdef PORTB
PortB = 1,
#endif //PORTB
#ifdef PORTC
PortC = 2,
#endif //PORTC
#ifdef PORTD
PortD = 3,
#endif //PORTC
#ifdef PORTE
PortE = 4,
#endif //PORTE
#ifdef PORTF
PortF = 5,
#endif //PORTF
};
enum PinIO //: uint8_t
{
Input,
Output
};
enum Pins //: uint8_t
{
Pin0,
Pin1,
Pin2,
Pin3,
Pin4,
Pin5,
Pin6,
Pin7
};
template<const Ports PortId>
class Port
{
private:
Port(){}
public:
// Enables the internal pull-up for inputs.
// Returns false when pin is not an input.
inline static bool EnablePullup(Pins pin, bool enable = true)
{
uint8_t mask = PinToMask(pin);
if ((PortRegDir() & mask) == Input)
{
PortRegOut() |= mask;
return true;
}
return false;
}
inline static void SetDirection(Pins pin, PinIO io)
{
uint8_t mask = PinToMask(pin);
if (io) PortRegDir() |= mask;
else PortRegDir() &= ~mask;
}
inline static void SetDirection(PinIO io7, PinIO io6, PinIO io5, PinIO io4, PinIO io3, PinIO io2, PinIO io1, PinIO io0)
{
PortRegDir() = io7 << 7 | io6 << 6 | io5 << 5 | io4 << 4 | io3 << 3 | io2 << 2 | io1 << 1 | io0;
}
inline static void SetDirection(uint8_t allPinsIO)
{
PortRegDir() = allPinsIO;
}
inline static void Write(Pins pin, bool value)
{
uint8_t mask = PinToMask(pin);
if (value) PortRegOut() |= mask;
else PortRegOut() &= ~mask;
}
inline static bool Read(Pins pin)
{
return (PortRegIn() & PinToMask(pin)) > 0;
}
inline Ports getPort() const
{
return PortId;
}
private:
inline static volatile uint8_t& PortRegIn()
{
return _SFR_IO8(((uint8_t)PortId * 3));
}
inline static volatile uint8_t& PortRegDir()
{
return _SFR_IO8((((uint8_t)PortId * 3) + 0x01));
}
inline static volatile uint8_t& PortRegOut()
{
return _SFR_IO8((((uint8_t)PortId * 3) + 0x02));
}
inline static uint8_t PinToMask(Pins pin)
{
return 1 << (uint8_t)pin;
}
};
You would use this as follows:
//Arduino
DigitalOutputPin<13> Led1;
// AVR
DigitalOutputPin<PortB, Pin0> Led1;
The real strength of this abstraction is that all additional code that is built on top of this, for instance LCD support, does not care if it is an Arduino or AVR implementation.
My Arduino/Avr Template Library focusses on effecient code (not using up any RAM if you don't have to), extensibilty and reuse. This library was born out of the frustration of the typical Arduino Lib that just did not fit my needs/wishes (use of delay, not reusable/extensible etc).
Hope it helps,
Marc