digitalReadFast

I started to play with Paul Stoffgren’s stuff from issue#140. i certainly feel over my head with the extraordinarily complex macros! but just cutting and pasting digitalWriteFast worked right off. then I tried to extend it for pinModeFast and digitalReadFast. pinModeFast ran. digitalReadFast did not compile. I get an error expected primary-expression before ‘if’ on the line of my code that uses digitalReadFast–there’s no if close to that line, so its probably buried in the macro. the parentheses in the macro all seem to balance. I’d appreciate any insight!

#if !defined(__AVR_ATmega1280__)

        // Standard Arduino Pins
#define digitalPinToPortReg(P) \
        (((P) >= 0 && (P) <= 7) ? &PORTD : (((P) >= 8 && (P) <= 13) ? &PORTB : &PORTC))
#define digitalPinToDDRReg(P) \
        (((P) >= 0 && (P) <= 7) ? &DDRD : (((P) >= 8 && (P) <= 13) ? &DDRB : &DDRC))
#define digitalPinToPINReg(P) \
        (((P) >= 0 && (P) <= 7) ? &PIND : (((P) >= 8 && (P) <= 13) ? &PINB : &PINC))
#define digitalPinToBit(P) \
        (((P) >= 0 && (P) <= 7) ? (P) : (((P) >= 8 && (P) <= 13) ? (P) - 8 : (P) - 14))

#if defined(__AVR_ATmega8__)

        // 3 PWM
#define digitalPinToTimer(P) \
        (((P) ==  9 || (P) == 10) ? &TCCR1A : (((P) == 11) ? &TCCR2 : 0))
#define digitalPinToTimerBit(P) \
        (((P) ==  9) ? COM1A1 : (((P) == 10) ? COM1B1 : COM21))
#else

        // 6 PWM
#define digitalPinToTimer(P) \
        (((P) ==  6 || (P) ==  5) ? &TCCR0A : \
        (((P) ==  9 || (P) == 10) ? &TCCR1A : \
        (((P) == 11 || (P) ==  3) ? &TCCR2A : 0)))
#define digitalPinToTimerBit(P) \
        (((P) ==  6) ? COM0A1 : (((P) ==  5) ? COM0B1 : \
        (((P) ==  9) ? COM1A1 : (((P) == 10) ? COM1B1 : \
        (((P) == 11) ? COM2A1 : COM2B1)))))
#endif

#else
        // Arduino Mega Pins
#define digitalPinToPortReg(P) \
        (((P) >= 22 && (P) <= 29) ? &PORTA : \
        ((((P) >= 10 && (P) <= 13) || ((P) >= 50 && (P) <= 53)) ? &PORTB : \
        (((P) >= 30 && (P) <= 37) ? &PORTC : \
        ((((P) >= 18 && (P) <= 21) || (P) == 38) ? &PORTD : \
        ((((P) >= 0 && (P) <= 3) || (P) == 5) ? &PORTE : \
        (((P) >= 54 && (P) <= 61) ? &PORTF : \
        ((((P) >= 39 && (P) <= 41) || (P) == 4) ? &PORTG : \
        ((((P) >= 6 && (P) <= 9) || (P) == 16 || (P) == 17) ? &PORTH : \
        (((P) == 14 || (P) == 15) ? &PORTJ : \
        (((P) >= 62 && (P) <= 69) ? &PORTK : &PORTL))))))))))
        
#define digitalPinToDDRReg(P) \
        (((P) >= 22 && (P) <= 29) ? &DDRA : \
        ((((P) >= 10 && (P) <= 13) || ((P) >= 50 && (P) <= 53)) ? &DDRB : \
        (((P) >= 30 && (P) <= 37) ? &DDRC : \
        ((((P) >= 18 && (P) <= 21) || (P) == 38) ? &DDRD : \
        ((((P) >= 0 && (P) <= 3) || (P) == 5) ? &DDRE : \
        (((P) >= 54 && (P) <= 61) ? &DDRF : \
        ((((P) >= 39 && (P) <= 41) || (P) == 4) ? &DDRG : \
        ((((P) >= 6 && (P) <= 9) || (P) == 16 || (P) == 17) ? &DDRH : \
        (((P) == 14 || (P) == 15) ? &DDRJ : \
        (((P) >= 62 && (P) <= 69) ? &DDRK : &DDRL))))))))))
        
#define digitalPinToPINReg(P) \
        (((P) >= 22 && (P) <= 29) ? &PINA : \
        ((((P) >= 10 && (P) <= 13) || ((P) >= 50 && (P) <= 53)) ? &PINB : \
        (((P) >= 30 && (P) <= 37) ? &PINC : \
        ((((P) >= 18 && (P) <= 21) || (P) == 38) ? &PIND : \
        ((((P) >= 0 && (P) <= 3) || (P) == 5) ? &PINE : \
        (((P) >= 54 && (P) <= 61) ? &PINF : \
        ((((P) >= 39 && (P) <= 41) || (P) == 4) ? &PING : \
        ((((P) >= 6 && (P) <= 9) || (P) == 16 || (P) == 17) ? &PINH : \
        (((P) == 14 || (P) == 15) ? &PINJ : \
        (((P) >= 62 && (P) <= 69) ? &PINK : &PINL))))))))))
        
#define digitalPinToBit(P) \
        (((P) >=  7 && (P) <=  9) ? (P) - 3 : \
        (((P) >= 10 && (P) <= 13) ? (P) - 6 : \
        (((P) >= 22 && (P) <= 29) ? (P) - 22 : \
        (((P) >= 30 && (P) <= 37) ? 37 - (P) : \
        (((P) >= 39 && (P) <= 41) ? 41 - (P) : \
        (((P) >= 42 && (P) <= 49) ? 49 - (P) : \
        (((P) >= 50 && (P) <= 53) ? 53 - (P) : \
        (((P) >= 54 && (P) <= 61) ? (P) - 54 : \
        (((P) >= 62 && (P) <= 69) ? (P) - 62 : \
        (((P) == 0 || (P) == 15 || (P) == 17 || (P) == 21) ? 0 : \
        (((P) == 1 || (P) == 14 || (P) == 16 || (P) == 20) ? 1 : \
        (((P) == 19) ? 2 : \
        (((P) == 5 || (P) == 6 || (P) == 18) ? 3 : \
        (((P) == 2) ? 4 : \
        (((P) == 3 || (P) == 4) ? 5 : 7)))))))))))))))

        // 15 PWM
#define digitalPinToTimer(P) \
        (((P) == 13 || (P) ==  4) ? &TCCR0A : \
        (((P) == 11 || (P) == 12) ? &TCCR1A : \
        (((P) == 10 || (P) ==  9) ? &TCCR2A : \
        (((P) ==  5 || (P) ==  2 || (P) ==  3) ? &TCCR3A : \
        (((P) ==  6 || (P) ==  7 || (P) ==  8) ? &TCCR4A : \
        (((P) == 46 || (P) == 45 || (P) == 44) ? &TCCR5A : 0))))))
#define digitalPinToTimerBit(P) \
        (((P) == 13) ? COM0A1 : (((P) ==  4) ? COM0B1 : \
        (((P) == 11) ? COM1A1 : (((P) == 12) ? COM1B1 : \
        (((P) == 10) ? COM2A1 : (((P) ==  9) ? COM2B1 : \
        (((P) ==  5) ? COM3A1 : (((P) ==  2) ? COM3B1 : (((P) ==  3) ? COM3C1 : \
        (((P) ==  6) ? COM4A1 : (((P) ==  7) ? COM4B1 : (((P) ==  8) ? COM4C1 : \
        (((P) == 46) ? COM5A1 : (((P) == 45) ? COM5B1 : COM5C1))))))))))))))

#endif


#define digitalWriteFast(P, V) \
        if (__builtin_constant_p(P) && __builtin_constant_p(V)) { \
                if (digitalPinToTimer(P)) \
                        bitClear(*digitalPinToTimer(P), digitalPinToTimerBit(P)); \
                bitWrite(*digitalPinToPortReg(P), digitalPinToBit(P), (V)); \
        } else { \
                digitalWrite((P), (V)); \
        }
#define pinModeFast(P, V) \
        if (__builtin_constant_p(P) && __builtin_constant_p(V)) { \
                if (digitalPinToTimer(P)) \
                        bitClear(*digitalPinToTimer(P), digitalPinToTimerBit(P)); \
                bitWrite(*digitalPinToDDRReg(P), digitalPinToBit(P), (V)); \
        } else { \
                pinMode((P), (V)); \
        } 
        
#define digitalReadFast(P ) \
        if (__builtin_constant_p(P) ) { \
                if (digitalPinToTimer(P)) \
                       bitClear(*digitalPinToTimer(P), digitalPinToTimerBit(P)); \
                bitRead(*digitalPinToPINReg(P), digitalPinToBit(P)); \
        } else { \
                digitalRead((P)); \
        }        


#include <LiquidCrystal440.h>

LiquidCrystal lcd(49,47,45, 43,41,39,37, 35,33,31,29); // LCD away from shield
//data pins  Ports c2,c4,c6,a7  rs=L0,rw=L2,enL4
byte LCDPOWER5V = 53;
byte LCDGND = 255;

void setup(void) {
  if (LCDGND != 255) {
    pinMode(LCDGND, OUTPUT);          //We're using a digital out as a 5V power source for the LCD
    digitalWrite(LCDGND, LOW);
  }
  pinMode(LCDPOWER5V, OUTPUT);          //We're using a digital out as a GND source for the LCD
  digitalWrite(LCDPOWER5V, HIGH);

  lcd.begin(16,4);
  lcd.clear();                  // start with a blank screen
  lcd.setCursor(0,0);           // set cursor to column 0, row 0
  Serial.begin(38400);
  Serial.flush(); 
 pinMode(13,OUTPUT); 
}

void loop (void){
  long baseMillis=millis();
  int i=30000;
  while (i--) {
    pinModeFast(13,OUTPUT);
    digitalWriteFast(13,HIGH);
    pinModeFast(13,INPUT);
    int x = digitalReadFast(13);      //<==== error: expected primary-expression before 'if'
  }
  long bench = millis()-baseMillis;
  lcd.setCursor(0,0);
  lcd.print((int)bench);
  long baseMillis2=millis();
  i=30000;
  while (i--) {
    pinMode(13,OUTPUT);
    digitalWrite(13,HIGH);
    pinMode(13,INPUT);
    x = digitalRead(13);
  }
   bench = millis()-baseMillis;
  lcd.setCursor(0,1);
  lcd.print((int)bench);

}

I thought I'd explain the goal and plan for this. the GOAL is to have a header file in the library you can include to get the speed of the assembly (PORT,DDR,PIN) commands with the syntax of digitalWrite, pinMode, digitalRead. The plan will be, once it compiles, to read the disassembled output and see if it looks right, then to write a test program. the physical setup for the test program will probably involve connecting resistors between pins on the Arduino and seeing if things can be read and written appropriately. There's a toggle trick with writing to an input pin that I think I'll need to learn and use to test pinModeFast. I've only got an Arduino Mega; once I get a test program done on that I would need help from the community on testing it for the various other boards. So even though the macro seems overwhelmingly complex, i do have hope of producing a tested .h file. I don't have an EE background; criticism on the thoroughness of my test program from that perspective will be very welcome.

Without the digitalReadFast and digitalRead lines, the little benchmark loop runs about 9 times faster in the fast version. I don't know whether its doing the right thing for pinModeFast, however.

Can you maybe go back to your original post, click "modify", highlight all the code and then click the "#" button the editor toolbar, please?

if I try simplifying digitalReadFast to

define digitalReadFast(P) bitRead(*digitalPinToPINReg(P), digitalPinToBit(P))

the program compiles, so it is in there. I'd tried deleting the 2nd if last night without success.

I put the pinModeFast and digitalWriteFast macros into TextWrangler in the mode that shows 'invisible' characters and coudn't see anything relavent.

Perhaps because I'm a newbie, it occurred to me that the macros are so complex that I might be overflowing a buffer or stack in GCC. so I tried rearranging the order of things--moving both the definition and the call to the macros around. the error message followed the digitalReadFast line. So that was a wacky newbie idea.

I actually played a little with code generated using the simplified digitalReadFast definition above. That is not tenable for the long run since it MUST default to the standard digitalRead when the pin number is not known at compile time. Anyway things seem to work right based on some very limited tests.

I think the problem is that digitalReadFast invocations end up looking like:  a = if (foo) { bar };and "if" statements don't have a value, even if all the parts are constant and can go away at compile-time. You might have better luck with a "?" operator:

#define digitalReadFast(P ) \
        (__builtin_constant_p(P) ) ? ( \
                digitalPinToTimer(P) ? ( \
                       bitClear(*digitalPinToTimer(P), digitalPinToTimerBit(P)) ,  \
                            bitRead(*digitalPinToPINReg(P), digitalPinToBit(P))) : \
                bitRead(*digitalPinToPINReg(P), digitalPinToBit(P))) : \
                digitalRead((P))

But as you see it is ugly, requires the "," operator as well (icky), and I decline to spend time debugging it. It's supposed to be:

#define dRF(P) constant(P) ? (timer(P) ? timeroff(),bitread() : bitread() : digitalRead(P)

if that's any clearer...

Why bother with the macros at all? Why not go for direct port manipulation immediately?

http://www.arduino.cc/en/Reference/PortManipulation

If you want to set more than one bit this gives you even more performance.

Cheers, Udo

Thanks for the input. I don't see yet why 'if' would work in digitalWriteFast and not in digitalReadFast but working through it with the ? syntax will be a fresh way to look at it, for sure. And I surely need a fresh perspective.

I do understand that there is potentially another order of magnitude performance improvement by manipulating 8 digital pins at once. The point is to preserve the clear syntax of digitalWrite etc and get the first order of magnitude speedup. Paul Stoffgren had done the heavy lifting on this before I even heard of an Arduino and it seemed like useful work as well as very educational work.

Thanks.

I don't see yet why 'if' would work in digitalWriteFast and not in digitalReadFast

It's a matter of having the "if" statement be on the right side of an assignment statement, which is not allowed:

int main (char **argv, int argc)
{
    int i,j;

    i = if (argc > 0) 1:0;

    j = argc ? 1:0;
}

Generates errors for the "i =" statement... for digitalRead, there's not assignment.

Got it. Thanks!

Just a thought - have you tried declaring it as an inline function, rather than a macro?

I hadn't thought about an inline function.

the thought that occurred to me as I began to fumble through designing something to test it is that, at least for my own use, I want it to tell me when I inadvertently write something that would default to the standard commands. I certainly don't want to spend time testing 50 pins as though I was testing the fast version and find out later that it had defaulted to the standard version!

Whether that survives to the final version seems like a matter of philosophy. My opinion would be to be told if fast couldn't be used and require the user to delete the 4 letters; then you're not surprised by a performance deficit that you have to hunt down.

Other people would decide differently. A month or so ago, on the developer mailing list, someone realized that in LiquidCrystal a uint8_t =-1 was being used as a flag and that the compiler was quietly optimizing that branch out of existence because an unsigned couldn't be negative. That bug was fixed in Arduino 18.

My perspective is that a good tool would have told you that an unsigned couldn't be negative. (I imagine there's some sort of option for GCC that in fact would make it work that way.) Anyway, this seems like a similar issue philosophically.

I don't believe that inline functions allow you to be conditional on "__builtin_constant_p", which is the whole point of the xxxFAST routines: "if the pin numbers (and perhaps the value) are constant, you can figure out the port and bit at compile time and reduce the dozen+ instructions of digitalXXX to only one or two."

after looking at some disassembled code and the wiring.c source I realize that currently pinMode does not clear the timer functions but digitalRead and digitalWrite both do that for PWM pins every time. So the consistent thing is to include that, as Paul Stofgren did, for digitalReadFast and digitalWriteFast and eliminate it for pinModeFast.

However, a comment in wiring.c suggests that performance would improve if it was only done in pinMode. That makes a huge amount of sense to me. In many situations you can limit pinMode to the setup() function and it certainly seems redundant to do several extra instructions every time you read or write.

On the other hand, those instructions got added to digitalWriteFast after a discussion among wiser, more experienced people on the developer mailing list. It occurs to me that I could do it both ways with relatively little extra effort. If I put both ways in the same .h file and test them as part of the same physical process, it actually won’t add much to the test procedure. I’m rapidly approaching the point of wanting to automate the writing of the test program; sticking the tests into subroutines doesn’t work because pins numbers where the rubber meets the road need to be known at compile time. Anyway it seems reasonable to me to offer two forms of the fast macros more or less as follows

#define digitalWriteFast(P, V) \
if (__builtin_constant_p(P) && __builtin_constant_p(V)) { \
                if (digitalPinToTimer(P)) \
                        bitClear(*digitalPinToTimer(P), digitalPinToTimerBit(P)); \
                bitWrite(*digitalPinToPortReg(P), digitalPinToBit(P), (V)); \
        } else { \
                digitalWrite((P), (V)); \
        }
#define pinModeFast(P, V) \
if (__builtin_constant_p(P) && __builtin_constant_p(V)) { \
                bitWrite(*digitalPinToDDRReg(P), digitalPinToBit(P), (V)); \
        } else {  \
                pinMode((P), (V)); \
        } 

#define digitalReadFast(P ) \
(__builtin_constant_p(P) ) ? ( \
                digitalPinToTimer(P) ? ( \
                       bitClear(*digitalPinToTimer(P), digitalPinToTimerBit(P)) ,  \
                            bitRead(*digitalPinToPINReg(P), digitalPinToBit(P))) : \
                bitRead(*digitalPinToPINReg(P), digitalPinToBit(P))) : \
                digitalRead((P))

#define digitalWriteFast2(P, V) \
if (__builtin_constant_p(P) && __builtin_constant_p(V)) { \
                bitWrite(*digitalPinToPortReg(P), digitalPinToBit(P), (V)); \
        } else { \
                digitalWrite((P), (V)); \
        }
#define pinModeFast2(P, V) \
if (__builtin_constant_p(P) && __builtin_constant_p(V)) { \
                if (digitalPinToTimer(P)) \
                        bitClear(*digitalPinToTimer(P), digitalPinToTimerBit(P)); \
                bitWrite(*digitalPinToDDRReg(P), digitalPinToBit(P), (V)); \
        } else {  \
                pinMode((P), (V)); \
        } 

#define digitalReadFast2(P ) \
(__builtin_constant_p(P) ) ? ( \
                bitRead(*digitalPinToPINReg(P), digitalPinToBit(P))) : \
                digitalRead((P))

I've almost got the test program done and plan to get to run it tomorrow night after work; I'm out of town this weekend. I wold like to get any EE input on the planned test procedure; is it safe and sufficient?

The plan is to connect pairs of pins with resistors in the range of 1K to 10K. Every 3rd pin works pretty well based on the spacing of pins and the size of 1/4 watt resistors.

The code for one PWM pin pair looks like this. For non-PWM pins the analogWrite statements get left out. I've included pin 44 as PWM although I'm not sure it truly is after reading this http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1242837044 As I've said before I can only test the mega that I have. except for the seeduino mega, the test procedure should be much less involved for anything else. My attitude is to test thoroughly now rather than leaving bugs so Murphy can get me when I'm not expecting to look here. Comments of looks OK to me will be welcome; I'm not an EE and I'd really appreciate any input.

analogWrite(2,254)
analogWrite(5,254)
pinModeFast(2,INPUT);
digitalWriteFast(2,HIGH);  //set the pullup resistor in input mode
pinModeFast(5,OUTPUT);
digitalWriteFast(5,LOW);
if (digitalReadFast(2) != LOW) error(2,5,1);

analogWrite(2,254)
analogWrite(5,254)
pinModeFast2(2,INPUT);
digitalWriteFast2(2,HIGH);  //set the pullup resistor in input mode
pinModeFast2(5,OUTPUT);
digitalWriteFast2(5,LOW);
if (digitalReadFast2(2) != LOW) error(2,5,1);

analogWrite(5,254)
analogWrite(2,254)
pinModeFast(5,INPUT);
digitalWriteFast(5,HIGH);  //set the pullup resistor in input mode
pinModeFast(2,OUTPUT);
digitalWriteFast(2,LOW);
if (digitalReadFast(5) != LOW) error(5,2,2);

analogWrite(5,254)
analogWrite(2,254)
pinModeFast2(5,INPUT);
digitalWriteFast2(5,HIGH);  //set the pullup resistor in input mode
pinModeFast2(2,OUTPUT);
digitalWriteFast2(2,LOW);
if (digitalReadFast2(5) != LOW) error(5,2,2);

analogWrite(5,1)
analogWrite(2,1)
pinModeFast(5,INPUT);
digitalWriteFast(5,LOW);  //set the pullup resistor in input mode
pinModeFast(2,OUTPUT);
digitalWriteFast(2,HIGH);
if (digitalReadFast(5) != HIGH) error(5,2,3);

analogWrite(5,1)
analogWrite(2,1)
pinModeFast2(5,INPUT);
digitalWriteFast2(5,LOW);  //set the pullup resistor in input mode
pinModeFast2(2,OUTPUT);
digitalWriteFast2(2,HIGH);
if (digitalReadFast2(5) != HIGH) error(5,2,3);

analogWrite(2,1)
analogWrite(5,1)
pinModeFast(2,INPUT);
digitalWriteFast(2,LOW);  //set the pullup resistor in input mode
pinModeFast(5,OUTPUT);
digitalWriteFast(5,HIGH);
if (digitalReadFast(2) != HIGH) error(2,5,4);

analogWrite(2,1)
analogWrite(5,1)
pinModeFast2(2,INPUT);
digitalWriteFast2(2,LOW);  //set the pullup resistor in input mode
pinModeFast2(5,OUTPUT);
digitalWriteFast2(5,HIGH);
if (digitalReadFast2(2) != HIGH) error(2,5,4);

Ugh. Writing test code is hard; it requires a certain mindset that is pretty rare even among programmers (thus the large number of bugs in production software!)

These functions might be better tested by examining the code produced, rather than trying to have the cpu examine its own inputs.

Your current test code doesn’t test the important cases where the pin and state for the new function are NOT constants.

The current code is difficult to follow; it needs to become commented, modularized and table-driven, perhaps along the lines of:

//  Possible states of the pin before we do anything with it:
typedef enum initstate {
  INPUT0,  // pin is input without pullup
  INPUT1,  // pin is input WITH pullup
  OUTPUT0, // pin is output with 0 written
  OUTPUT1, // pin is output with 1 written
  ANALOG, // pin had analogWrite
//  :   more ?
  INITSTATE_MAX } initstate_t;

void set_initstate(int pin, initstate_t state)
{
  switch (state) {
  case INPUT0:
    pinMode(pin, INPUT);
    digitalWrite(pin, 0);
    break;
  case INPUT1:
    pinMode(pin, INPUT);
    digitalWrite(pin, 1);
    break;
// etc
  }
}

void testInput(byte testpin, byte drivepin)
{
  for (initstate_t is=INPUT0; is < INITSTATE_MAX; is++) {
    set_initstate(testpin, is);
    digitalWrite(drivepin, 0);
    delay(1);  // just in case
    val = fastDigitalRead(testpin);
    if (val != 0) {
       report_error(testpin, "Expected 0, got ", val);
    } 
    set_initstate(testpin, is);
    digitalWrite(drivepin, 1);
    delay(1);  // just in case
    val = fastDigitalRead(testpin);
    if (val != 1) {
       report_error(testpin, "Expected 1, got ", val);
    }
  } // for is
}

Of course, part of the difficulty is that the table has to be in the program that writes the program so that the pin numbers for most of the cases are known at compile time. You are certainly correct that I was missing the common case of the pin number not being known! I'll look at this carefully.

Thank you very much. This is VERY valuable.

BTW why was examining the code made harder in Arduino 18? When I did that I reverted to 17 where the applet folder was created alongside my sketch. that doesn't happen in 18.

why was examining the code made harder in Arduino 18? When I did that I reverted to 17 where the applet folder was created alongside my sketch.

This was discussed somewhat under the version-18 announcement: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1264808201

Basically: 1) The build directory was moved from sketch/applet to a temporary directory to avoid problems with the examples on read-only filesystems. 2) About the same time, "temporary directories" became really obscure due to changes in the java runtime environment.

51 pins of success. Plus 3/4 of my test cases with the other 2; the 2 pins that fail are 10 and 13. the test cases that fail still fail when I replace the digitalWriteFast,pinModeFast, and digitalReadFast with the nonfast old style version. So I guess that its something I didn't understand about pin 13 which has a built in LED and resistor to keep from burning it out.

After searching I come up with this: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1236829276

Here's what's failing:

analogWrite(13,1);
pinMode(13,INPUT);
digitalWriteFast(13,LOW); 
pinMode(10,OUTPUT);
digitalWrite(10,HIGH);
delay(10);
q=digitalRead(13);
if (q != HIGH) error(13,10,3);

analogWrite(10,1);
pinMode(13,INPUT);
digitalWrite(13,LOW); 
pinMode(10,OUTPUT);
digitalWrite(10,HIGH);
delay(10);
q=digitalRead(13);
if (q != HIGH) error(13,10,3);

pinMode(13,INPUT);
digitalWrite(13,LOW); 
pinMode(10,OUTPUT);
digitalWrite(10,HIGH);
delay(10);
q=digitalRead(13);
if (q != HIGH) error(13,10,3);

pinMode(13,INPUT);
digitalWrite(13,LOW);
pinMode(10,OUTPUT);
digitalWrite(10,HIGH);
delay(10);
q=digitalRead(13);
if (q != HIGH) error(13,10,3);

analogWrite(13,1);
pinModeFast2(13,INPUT);
digitalWriteFast2(13,LOW);
pinModeFast2(10,OUTPUT);
digitalWriteFast2(10,HIGH);
delay(10);
q=digitalReadFast2(13);
if (q != HIGH) error(13,10,3);

analogWrite(10,1);
pinModeFast2(13,INPUT);
digitalWriteFast2(13,LOW);
pinModeFast2(10,OUTPUT);
digitalWriteFast2(10,HIGH);
delay(10);
q=digitalReadFast2(13);
if (q != HIGH) error(13,10,3);

There was one odd thing along the way that troubles me a bit. I was having really no success when my tests were written as: if (digitalReadFast(pin) != HIGH ) err

when I reverted to Arduino17 and started looking at the disassembly I thought it would be easier to understand if I atomized it by adding q as above. I didn't think I'd fix anything but it fixed EVERYTHING. I still don't get what happened. Worries me.

I still need to write a separate test for the case when the pin number isn't known at compile time. That's a pretty degenerate situation since every port and pin do the same thing.