/* Prototype fast inline I/O for AVR Copyright (C) 2014 Jeff Epler This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include // BLACK MAGIC STARTS HERE template struct IOPORT { enum { port = port_, pin = pin_, ddr = ddr_ }; }; #pragma push_macro("_MMIO_BYTE") #undef _MMIO_BYTE #define _MMIO_BYTE(x) (x) #ifdef PORTA typedef IOPORT PORT_A; #endif #ifdef PORTB typedef IOPORT PORT_B; #endif #ifdef PORTC typedef IOPORT PORT_C; #endif #ifdef PORTD typedef IOPORT PORT_D; #endif #ifdef PORTE typedef IOPORT PORT_E; #endif #ifdef PORTF typedef IOPORT PORT_F; #endif #ifdef PORTG typedef IOPORT PORT_G; #endif #ifdef PORTH typedef IOPORT PORT_H; #endif #ifdef PORTI typedef IOPORT PORT_I; #endif #ifdef PORTJ typedef IOPORT PORT_J; #endif #ifdef PORTK typedef IOPORT PORT_K; #endif #ifdef PORTL typedef IOPORT PORT_L; #endif #pragma pop_macro("_MMIO_BYTE") #define FNORD __attribute__((always_inline, flatten, optimize("Ofast"))) template struct IOPIN { enum { mask = (1< inline bool IOPIN::read() { return _MMIO_BYTE(ioport::pin) & mask; } template inline void IOPIN::input() { _MMIO_BYTE(ioport::ddr) &= ~mask; } template inline void IOPIN::output() { _MMIO_BYTE(ioport::ddr) |= mask; } template inline void IOPIN::set() { _MMIO_BYTE(ioport::port) |= mask; } template inline void IOPIN::clear() { _MMIO_BYTE(ioport::port) &= ~mask; } template inline void IOPIN::write(bool val) { if(val) set(); else clear(); } template inline void IOPIN::toggle() { _MMIO_BYTE(ioport::pin) |= mask; } // we can also provide some support for open collector pins, where // the PORT and DDR bit are manipulated in tandem to emulate an open collector template struct OCPIN { typedef IOPIN io; enum { mask = (1< inline bool OCPIN::read() { return io::read(); } template inline void OCPIN::set() { io::input(); if(pu) io::set(); } template inline void OCPIN::clear() { io::clear(); io::output(); } template inline void OCPIN::write(bool val) { if(val) set(); else clear(); } // from wiring or something extern int digitalWrite(int8_t i, int8_t v); // show how digitalWrite can be turned into an efficient inline when the // arguments are constant, a quite good inline when the pin is constant but // the value isn't, and a call when the pin is variable inline void fastDigitalWrite(int8_t i, int8_t v) __attribute__((always_inline, flatten, optimize("Ofast"))); inline void fastDigitalWrite(int8_t i, int8_t v) { if(__builtin_constant_p(i)) { // n.b. check this to match Arduino silkscreen for all pins if(i == 0) { if(v) IOPIN::set(); else IOPIN::clear(); return; } else if(i == 1) { if(v) IOPIN::set(); else IOPIN::clear(); return; } } else { digitalWrite(i, v); } } // force functions to be visible so we can disassemble them int en() __attribute__((externally_visible)); int fn() __attribute__((externally_visible)); int gn() __attribute__((externally_visible)); int hn() __attribute__((externally_visible)); int jn(bool b) __attribute__((externally_visible)); int kn() __attribute__((externally_visible)); int a(int i, int j) __attribute__((externally_visible)); int b(int i) __attribute__((externally_visible)); int c(int i) __attribute__((externally_visible)); int d() __attribute__((externally_visible)); // BLACK MAGIC ENDS -- resume comprehensible code below // You can declare a port (it uses no RAM) typedef IOPIN iob0; typedef IOPIN iob1; typedef OCPIN ocb1; // .. and use it efficiently (instruction counts never include ret) int en() { iob0::output(); } // 1 instruction int fn() { iob0::toggle(); } // 1 instruction int gn() { ocb1::set(); } // 2 instructions int hn() { ocb1::clear(); } // 2 instructions int jn(bool b) { iob0::write(b); } // 5 instructions int kn() { iob0::write(1); } // 1 instruction // you can use fastDigitalWrite just like digitalWrite int c(int i) { fastDigitalWrite(1, i); } // 5 instructions int d() { fastDigitalWrite(1, 1); } // 1 instruction // these have a variable port so they still turn into calls to digitalWrite int a(int i, int j) { fastDigitalWrite(i, j); } // call int b(int i) { fastDigitalWrite(i, 1); } // call