Go Down

Topic: Single pin Port Manipulation (Read 2989 times) previous topic - next topic

John_S

I'm wondering if it is possible to use port manipulation for only one pin and digitalRead/Write for the other pins. Everything I've read leads me to believe that you can only use port manipulation only on an entire port, not individual pins.

I want to use port manipulation on pin 0 where I have an IR led used as a remote (standalone 328, no Serial used). Can I do this?

Here's the applicable code.

Code: [Select]
//Sample program to trigger camera via IR
//Circuit: IR LED from pin 0, to 150 Ohm resistor, to ground

int IRpin = 0;

void setup(){


  pinMode(IRpin, OUTPUT);
 
  FireIR(7330);

}

void loop(){}

void FireIR(int IRdelay){
  //send first 16 bursts
  //for (int j=0; j<2; j++){
  for(int i=0; i<16; i++) {
    digitalWrite(IRpin, HIGH);
    delayMicroseconds(10);
    digitalWrite(IRpin, LOW);
    delayMicroseconds(10);
  }

  delayMicroseconds(IRdelay);

  //send second 16 bursts
  for(int i=0; i<16; i++) {
    digitalWrite(IRpin, HIGH);
    delayMicroseconds(10);
    digitalWrite(IRpin, LOW);
    delayMicroseconds(10);
  } 
}

http://jsrintervalometers.blogspot.ca

Morris Dovey

You can do both. Just be careful not to disturb the other seven bits when you make changes to pin 0.
There's always a better way!

John_S

Thank you.
I found this Instructable: http://www.instructables.com/id/Arduino-is-Slow-and-how-to-fix-it/step2/A-and-B-watch-out-C-is-here/

I try and take instructables with a spoon of salt... Here's what they say:

PORTB |= _BV(PB5);  //write port B5 HIGH
PORTB &= ~_BV(PB5);  //write port B5 LOW

Can that just be simply substituted for a digitalWrite command?

Can I  use a pinMode(IRpin, OUTPUT) in setup as normal? or do I need to set the pinMode with Port Manipulation for that one pin?
http://jsrintervalometers.blogspot.ca

James C4S

Use bit-wise operators.  

Use Bitwise-AND to set the pins you want low.  Use Bitwise-OR to set the pins you want HIGH.  The mask determines which bit changes.

Use PORTD as an example (Uno Pins 0 to 7).  You want to only change Arduino Pin 3, which is PORTD bit 3.

To set the pin high you would do this:
PORTD = PORTD | 0x08
(the mask is: 0000 1000)
Any pins already set to a 1 will stay a 1.  Pin 3 / Bit 3 will be force High no matter what.

To set the pin low you would do this:
PORTD = PORTD & 0xF7
(the mask is: 1111 0111)

Don't worry about whether some of the pins on the port are set to Input.  PORTD is used to determine if the internal pull-up resistor is turned on or not.  So these operations will have no affect on them.
Capacitor Expert By Day, Enginerd by night.  ||  Personal Blog: www.baldengineer.com  || Electronics Tutorials for Beginners:  www.addohms.com

magagna

Yes, yes, and no.

The only thing I'd add is you're going to use PORTD and _BV(PD0) since you want to change D0, but you probably already knew that.

Good luck!
http://en.wiktionary.org/wiki/magagna <-- My last name.  Pretty apt.

James C4S


PORTB |= _BV(PB5);  //write port B5 HIGH
PORTB &= ~_BV(PB5);  //write port B5 LOW

Can that just be simply substituted for a digitalWrite command?

Yes it can.  Which would make me wonder why you want to do port maniuplation if you are okay with using digitalWrite().


Can I  use a pinMode(IRpin, OUTPUT) in setup as normal? or do I need to set the pinMode with Port Manipulation for that one pin?

Port Manipulation isn't a special mode.  It is just a faster way to do what digitalWrite() and digitalRead() does.  If you look at the code for those functions, you'll see they are using direct port manipulation themselves.

pinMode() sets the appropriate DDRx bits for which direction the bit / pin is going to be.  Since this usually isn't a time critical function it is normal to see people set the direction with pinMode() while accessing the bits directly.
Capacitor Expert By Day, Enginerd by night.  ||  Personal Blog: www.baldengineer.com  || Electronics Tutorials for Beginners:  www.addohms.com

John_S

Thanks for the help all.
Quote
Which would make me wonder why you want to do port maniuplation if you are okay with using digitalWrite().
I'm having a hard time tweaking the IR code using digitalWrite. Sometimes it works, sometimes it doesn't. I think writing to the ports will allow more accurate timing.
http://jsrintervalometers.blogspot.ca

James C4S


I think writing to the ports will allow more accurate timing.


It allows for faster writing.  I'm not sure it is more accurate.  Accuracy will probably depend more on what you are doing around the write operation.
Capacitor Expert By Day, Enginerd by night.  ||  Personal Blog: www.baldengineer.com  || Electronics Tutorials for Beginners:  www.addohms.com

John_S

So, I wrote a short blink sketch. I used #define to set the hex values, now using them in the program is easy.
This compiles to 846 bytes (IDE 0023) but using digitalWrite() to do the same thing compiles to 1034 bytes. After seeing this, it's hard to think of a good reason to use digitalWrite anymore XD
Code: [Select]
#define D13high  | 0x20;  //hex code to write Digital Pin 13 HIGH
#define D13low & 0xDF;  //hex code to write Digital Pin 13 LOW

void setup()
{
  pinMode(13, OUTPUT);
}
void loop()
{
  for(int i = 0; i < 10; i++)
  {
    PORTB = PORTB D13high;  //set Digital 13 HIGH
    delay(100);
    PORTB = PORTB D13low;   //set Digital 13 LOW
    delay(100);
  }
  while(1);
}
http://jsrintervalometers.blogspot.ca

bill2009


So, I wrote a short blink sketch. I used #define to set the hex values, now using them in the program is easy.
This compiles to 846 bytes (IDE 0023) but using digitalWrite() to do the same thing compiles to 1034 bytes. After seeing this, it's hard to think of a good reason to use digitalWrite anymore XD
Code: [Select]
#define D13high  | 0x20;  //hex code to write Digital Pin 13 HIGH
#define D13low & 0xDF;  //hex code to write Digital Pin 13 LOW

void setup()
{
  pinMode(13, OUTPUT);
}
void loop()
{
  for(int i = 0; i < 10; i++)
  {
    PORTB = PORTB D13high;  //set Digital 13 HIGH
    delay(100);
    PORTB = PORTB D13low;   //set Digital 13 LOW
    delay(100);
  }
  while(1);
}


Until you want to change the pin you're using!

James C4S

code using digitalWrites() will work on any Arduino board.  Direct port manipulation won't.
Capacitor Expert By Day, Enginerd by night.  ||  Personal Blog: www.baldengineer.com  || Electronics Tutorials for Beginners:  www.addohms.com

John_S

#11
Feb 25, 2012, 07:59 pm Last Edit: Feb 25, 2012, 08:10 pm by John_S Reason: 1
Quote
Until you want to change the pin you're using!


Maybe you would like this a little better then?
Code: [Select]
int LEDpin = 13;  //set LED pin

void setup()
{
 pinMode(LEDpin, OUTPUT);  //set LEDpin as output
}
void loop()
{
 for(int i = 0; i < 10; i++)
 {
   portWrite(LEDpin, HIGH); //Write LEDpin HIGH
   delay(100);
   portWrite(LEDpin, LOW);  //Write LEDpin LOW
   delay(100);
 }
 while(1);
}


void portWrite(int pin, int state){
 if (state == 1){ //write HIGH
 switch (pin){
   case 0: PORTD = PORTD | 0x01; break;
   case 1: PORTD = PORTD | 0x02; break;
   case 2: PORTD = PORTD | 0x04; break;
   case 3: PORTD = PORTD | 0x08; break;
   case 4: PORTD = PORTD | 0x10; break;
   case 5: PORTD = PORTD | 0x20; break;
   case 6: PORTD = PORTD | 0x40; break;
   case 7: PORTD = PORTD | 0x80; break;
   case 8: PORTB = PORTB | 0x01; break;
   case 9: PORTB = PORTB | 0x02; break;
   case 10: PORTB = PORTB | 0x04; break;
   case 11: PORTB = PORTB | 0x08; break;
   case 12: PORTB = PORTB | 0x10; break;
   case 13: PORTB = PORTB | 0x20; break;
   case 14: PORTC = PORTC | 0x01; break;
   case 15: PORTC = PORTC | 0x02; break;
   case 16: PORTC = PORTC | 0x04; break;
   case 17: PORTC = PORTC | 0x08; break;
   case 18: PORTC = PORTC | 0x10; break;
   case 19: PORTC = PORTC | 0x20; break;
 }
 } else {// write low
 switch (pin){
   case 0: PORTD = PORTD & 0xFE; break;
   case 1: PORTD = PORTD & 0xFD; break;
   case 2: PORTD = PORTD & 0xFB; break;
   case 3: PORTD = PORTD & 0xF7; break;
   case 4: PORTD = PORTD & 0xEF; break;
   case 5: PORTD = PORTD & 0xDF; break;
   case 6: PORTD = PORTD & 0xBF; break;
   case 7: PORTD = PORTD & 0x7F; break;
   case 8: PORTB = PORTB & 0xFE; break;
   case 9: PORTB = PORTB & 0xFD; break;
   case 10: PORTB = PORTB & 0xFB; break;
   case 11: PORTB = PORTB & 0xF7; break;
   case 12: PORTB = PORTB & 0xEF; break;
   case 13: PORTB = PORTB & 0xDF; break;
   case 14: PORTC = PORTC & 0xFE; break;
   case 15: PORTC = PORTC & 0xFD; break;
   case 16: PORTC = PORTC & 0xFB; break;
   case 17: PORTC = PORTC & 0xF7; break;
   case 18: PORTC = PORTC & 0xEF; break;
   case 19: PORTC = PORTC & 0xDF; break;  
   }
 }
}

8)

edit: I just ran a speed test, and the portWrite command is 30% faster than the digitalWrite command, but almost 8 times slower than the straight port manipulation.
http://jsrintervalometers.blogspot.ca

Coding Badly

Quote
I just ran a speed test


Using code similar to what you posted?

John_S

Speed test code:

Code: [Select]
#define D13high | 0x20;
#define D13low  & 0xDF;

int LEDpin = 13;

void setup()
{
Serial.begin(9600);
}
void loop()
{
unsigned long initial = 0;
unsigned long final = 0;
initial = micros();
for(int i = 0; i < 500; i++)
{
    digitalWrite(LEDpin,HIGH);
    digitalWrite(LEDpin,LOW);
}
final = micros();
Serial.print("Time for digitalWrite(): ");
Serial.print(final-initial);
Serial.println("");
initial = micros();
for(int i = 0; i < 500; i++)
{
    PORTB = PORTB | 0x20;
    PORTB = PORTB & 0xDF;
}
final = micros();
Serial.print("Time for true c command: ");
Serial.println(final-initial);

initial = micros();
for(int i = 0; i < 500; i++)
{
    PORTB = PORTB D13high;
    PORTB = PORTB D13low;
}
final = micros();
Serial.print("Time for true c command using #define: ");
Serial.println(final-initial);

initial = micros();
for(int i = 0; i < 500; i++)
{
    portWrite(LEDpin, HIGH);
    portWrite(LEDpin, LOW);
}
final = micros();
Serial.print("Time for portWrite(): ");
Serial.println(final-initial);
while(1);
}


void portWrite(int pin, int state){
  if (state == 1){ //write HIGH
  switch (pin){
    case 0: PORTD = PORTD | 0x01; break;
    case 1: PORTD = PORTD | 0x02; break;
    case 2: PORTD = PORTD | 0x04; break;
    case 3: PORTD = PORTD | 0x08; break;
    case 4: PORTD = PORTD | 0x10; break;
    case 5: PORTD = PORTD | 0x20; break;
    case 6: PORTD = PORTD | 0x40; break;
    case 7: PORTD = PORTD | 0x80; break;
    case 8: PORTB = PORTB | 0x01; break;
    case 9: PORTB = PORTB | 0x02; break;
    case 10: PORTB = PORTB | 0x04; break;
    case 11: PORTB = PORTB | 0x08; break;
    case 12: PORTB = PORTB | 0x10; break;
    case 13: PORTB = PORTB | 0x20; break;
    case 14: PORTC = PORTC | 0x01; break;
    case 15: PORTC = PORTC | 0x02; break;
    case 16: PORTC = PORTC | 0x04; break;
    case 17: PORTC = PORTC | 0x08; break;
    case 18: PORTC = PORTC | 0x10; break;
    case 19: PORTC = PORTC | 0x20; break;
  }
  } else {// write low
  switch (pin){
    case 0: PORTD = PORTD & 0xFE; break;
    case 1: PORTD = PORTD & 0xFD; break;
    case 2: PORTD = PORTD & 0xFB; break;
    case 3: PORTD = PORTD & 0xF7; break;
    case 4: PORTD = PORTD & 0xEF; break;
    case 5: PORTD = PORTD & 0xDF; break;
    case 6: PORTD = PORTD & 0xBF; break;
    case 7: PORTD = PORTD & 0x7F; break;
    case 8: PORTB = PORTB & 0xFE; break;
    case 9: PORTB = PORTB & 0xFD; break;
    case 10: PORTB = PORTB & 0xFB; break;
    case 11: PORTB = PORTB & 0xF7; break;
    case 12: PORTB = PORTB & 0xEF; break;
    case 13: PORTB = PORTB & 0xDF; break;
    case 14: PORTC = PORTC & 0xFE; break;
    case 15: PORTC = PORTC & 0xFD; break;
    case 16: PORTC = PORTC & 0xFB; break;
    case 17: PORTC = PORTC & 0xF7; break;
    case 18: PORTC = PORTC & 0xEF; break;
    case 19: PORTC = PORTC & 0xDF; break; 
    }
  }
}



Results:
Code: [Select]
Time for digitalWrite(): 4120
Time for true c command: 348
Time for true c command using #define: 344
Time for portWrite(): 2864



http://jsrintervalometers.blogspot.ca

Coding Badly


[font=Courier New]static const int LEDpin = 13;[/font]

...and then rerun the test.

Go Up