Permalink
Browse files

Optimized digitalWrite(), etc. from Alvaro Lopez.

  • Loading branch information...
1 parent 80bb16d commit 9dccd634c5ff1fed88459e39b277794dbba039fb @damellis damellis committed Feb 11, 2011
Oops, something went wrong.
@@ -26,8 +26,10 @@
#define Wiring_h
#include <avr/io.h>
+#include <avr/interrupt.h>
#include <stdlib.h>
#include "binary.h"
+#include "pins_arduino.h"
#ifdef __cplusplus
extern "C"{
@@ -106,9 +108,9 @@ typedef uint8_t byte;
void init(void);
-void pinMode(uint8_t, uint8_t);
-void digitalWrite(uint8_t, uint8_t);
-int digitalRead(uint8_t);
+void pinMode_lookup(uint8_t, uint8_t);
+void digitalWrite_lookup(uint8_t, uint8_t);
+int digitalRead_lookup(uint8_t);
int analogRead(uint8_t);
void analogReference(uint8_t mode);
void analogWrite(uint8_t, int);
@@ -128,6 +130,71 @@ void detachInterrupt(uint8_t);
void setup(void);
void loop(void);
+/*
+ * Check if a given pin requires locking.
+ * When accessing lower 32 IO ports we can use SBI/CBI instructions, which are atomic. However
+ * other IO ports require load+modify+store and we need to make them atomic by disabling
+ * interrupts.
+ */
+INLINED int portWriteNeedsLocking(uint8_t pin)
+{
+ /* SBI/CBI instructions only work on lower 32 IO ports */
+ if (inlined_portOutputRegister(inlined_digitalPinToPort(pin)) > (volatile uint8_t*)&_SFR_IO8(0x1F)) {
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * These functions will perform OR/AND on a given register, and are atomic.
+ */
+extern void __digitalWriteOR_locked(volatile uint8_t*out, uint8_t bit);
+extern void __digitalWriteAND_locked(volatile uint8_t*out, uint8_t bit);
+
+INLINED void digitalWrite(uint8_t pin, uint8_t value)
+{
+ if (__builtin_constant_p(pin)) {
+ if (portWriteNeedsLocking(pin)) {
+ if (value==LOW) {
+ __digitalWriteAND_locked(inlined_portOutputRegister(inlined_digitalPinToPort(pin)),~inlined_digitalPinToBitMask(pin));
+ } else {
+ __digitalWriteOR_locked(inlined_portOutputRegister(inlined_digitalPinToPort(pin)),inlined_digitalPinToBitMask(pin));
+ }
+ } else {
+ if (value==LOW) {
+ *inlined_portOutputRegister(inlined_digitalPinToPort(pin)) &= ~(inlined_digitalPinToBitMask(pin));
+ } else {
+ *inlined_portOutputRegister(inlined_digitalPinToPort(pin)) |= inlined_digitalPinToBitMask(pin);
+ }
+ }
+ } else {
+ digitalWrite_lookup(pin,value);
+ }
+}
+
+INLINED void pinMode(uint8_t pin, uint8_t mode)
+{
+ if (__builtin_constant_p(pin)) {
+ if (mode==INPUT) {
+ *inlined_portModeRegister(inlined_digitalPinToPort(pin)) &= ~(inlined_digitalPinToBitMask(pin));
+ } else {
+ *inlined_portModeRegister(inlined_digitalPinToPort(pin)) |= inlined_digitalPinToBitMask(pin);
+ }
+ } else {
+ pinMode_lookup(pin,mode);
+ }
+}
+
+INLINED int digitalRead(uint8_t pin)
+{
+ if (__builtin_constant_p(pin)) {
+ return !! *inlined_portInputRegister(inlined_digitalPinToPort(pin));
+ } else {
+ return digitalRead_lookup(pin);
+ }
+}
+
+
#ifdef __cplusplus
} // extern "C"
#endif
@@ -27,7 +27,7 @@
#include "wiring_private.h"
#include "pins_arduino.h"
-void pinMode(uint8_t pin, uint8_t mode)
+void pinMode_lookup(uint8_t pin, uint8_t mode)
{
uint8_t bit = digitalPinToBitMask(pin);
uint8_t port = digitalPinToPort(pin);
@@ -121,7 +121,23 @@ static void turnOffPWM(uint8_t timer)
}
}
-void digitalWrite(uint8_t pin, uint8_t val)
+void __digitalWriteOR_locked(volatile uint8_t*out, uint8_t bit)
+{
+ uint8_t oldSREG = SREG;
+ cli();
+ *out |= bit;
+ SREG=oldSREG;
+}
+
+void __digitalWriteAND_locked(volatile uint8_t*out, uint8_t bit)
+{
+ uint8_t oldSREG = SREG;
+ cli();
+ *out &= bit; // NOTE - no inversion here, invert before calling!!!
+ SREG=oldSREG;
+}
+
+void digitalWrite_lookup(uint8_t pin, uint8_t val)
{
uint8_t timer = digitalPinToTimer(pin);
uint8_t bit = digitalPinToBitMask(pin);
@@ -136,20 +152,19 @@ void digitalWrite(uint8_t pin, uint8_t val)
out = portOutputRegister(port);
+ uint8_t oldSREG = SREG;
+ cli();
+
if (val == LOW) {
- uint8_t oldSREG = SREG;
- cli();
*out &= ~bit;
- SREG = oldSREG;
} else {
- uint8_t oldSREG = SREG;
- cli();
*out |= bit;
- SREG = oldSREG;
}
+
+ SREG = oldSREG;
}
-int digitalRead(uint8_t pin)
+int digitalRead_lookup(uint8_t pin)
{
uint8_t timer = digitalPinToTimer(pin);
uint8_t bit = digitalPinToBitMask(pin);

0 comments on commit 9dccd63

Please sign in to comment.