Pages: [1]   Go Down
Author Topic: FastWrite for Mega 2560  (Read 585 times)
0 Members and 1 Guest are viewing this topic.
New York
Offline Offline
Sr. Member
****
Karma: 3
Posts: 362
xronosclock.com
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

I've used this custom "fastwrite" function for HT1632 display on Arduino Uno R3 and it worked great. But I just got Mega 2560 and it's not working (even tho I connected to same pins: 6,7,8,9).
Code:
#define fastwrite(pin, val) if (val) switch (pin) {     \
    case 0: PORTD |= 1<<0; break;       \
    case 1: PORTD |= 1<<1; break;       \
    case 2: PORTD |= 1<<2; break;       \
    case 3: PORTD |= 1<<3; break;       \
    case 4: PORTD |= 1<<4; break;       \
    case 5: PORTD |= 1<<5; break;       \
    case 6: PORTD |= 1<<6; break;       \
    case 7: PORTD |= 1<<7; break;       \
    case 8: PORTB |= 1<<0; break;       \
    case 9: PORTB |= 1<<1; break;       \
    case 10: PORTB |= 1<<2; break;      \
    case 11: PORTB |= 1<<3; break;      \
    case 12: PORTB |= 1<<4; break;      \
    case 13: PORTB |= 1<<5; break;      \
    } else switch (pin) {               \
    case 0: PORTD &= ~(1<<0); break;    \
    case 1: PORTD &= ~(1<<1); break;    \
    case 2: PORTD &= ~(1<<2); break;    \
    case 3: PORTD &= ~(1<<3); break;    \
    case 4: PORTD &= ~(1<<4); break;    \
    case 5: PORTD &= ~(1<<5); break;    \
    case 6: PORTD &= ~(1<<6); break;    \
    case 7: PORTD &= ~(1<<7); break;    \
    case 8: PORTB &= ~(1<<0); break;    \
    case 9: PORTB &= ~(1<<1); break;    \
    case 10: PORTB &= ~(1<<2); break;   \
    case 11: PORTB &= ~(1<<3); break;   \
    case 12: PORTB &= ~(1<<4); break;   \
    case 13: PORTB &= ~(1<<5); break;   \
    }
Is it because pins have different name?  What would be equivalent fastWrite for Mega?
I admit I'm not really understanding how it works (I know it activates ports directly by name somehow instead of using IDE translation)..
Logged

Xronos Clock - A talking arduino based alarm clock is now available. Check out xronosclock.com for pictures, source code, schematics, and purchasing info smiley

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 452
Posts: 18694
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

There are spreadsheets and diagrams around that show which pins are which ports. Here's one I found:

https://spreadsheets.google.com/pub?key=rtHw_R6eVL140KS9_G8GPkA&gid=0

Logged

Texas
Offline Offline
Newbie
*
Karma: 1
Posts: 17
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Did you select the Mega 2560 as the board in tools/board?
Logged

Dubai, UAE
Offline Offline
Edison Member
*
Karma: 21
Posts: 1670
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi,

You could use forum users 'FastLib16's digitalPin and/or digitalIO libraries, these do something similar but already handle the differences between Uno, Mega, Leonardo etc.

http://code.google.com/p/beta-lib/downloads/detail?name=DigitalPinBeta20120804.zip&can=2&q=

I am happily using them on a mega based synth project which I will port to UNO shortly - its nice not having to think about ports and pins as part of the conversion.

Duane B

rcarduino.blogspot.com




Logged


New York
Offline Offline
Sr. Member
****
Karma: 3
Posts: 362
xronosclock.com
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

There are spreadsheets and diagrams around that show which pins are which ports. Here's one I found:
https://spreadsheets.google.com/pub?key=rtHw_R6eVL140KS9_G8GPkA&gid=0
Thank you so much! It helped a lot!  I remapped all PORT names and values according to the spreadsheet and it worked like a charm.
Here's fastWrite macro for Mega 2560 board (pins 0-13), hope it will be useful for someone smiley
Code:
/Arduino MEGA compabilte verison of fastWrite
#define fWriteA(pin, val) if (val) switch (pin) {     \
    case 0: PORTE |= 1<<0; break;       \
    case 1: PORTE |= 1<<1; break;       \
    case 2: PORTE |= 1<<4; break;       \
    case 3: PORTE |= 1<<5; break;       \
    case 4: PORTG |= 1<<5; break;       \
    case 5: PORTE |= 1<<3; break;       \
    case 6: PORTH |= 1<<3; break;       \
    case 7: PORTH |= 1<<4; break;       \
    case 8: PORTH |= 1<<5; break;       \
    case 9: PORTH |= 1<<6; break;       \
    case 10: PORTB |= 1<<4; break;      \
    case 11: PORTB |= 1<<5; break;      \
    case 12: PORTB |= 1<<6; break;      \
    case 13: PORTB |= 1<<7; break;      \
    } else switch (pin) {               \
    case 0: PORTE &= ~(1<<0); break;    \
    case 1: PORTE &= ~(1<<1); break;    \
    case 2: PORTE &= ~(1<<4); break;    \
    case 3: PORTE &= ~(1<<5); break;    \
    case 4: PORTG &= ~(1<<5); break;    \
    case 5: PORTE &= ~(1<<3); break;    \
    case 6: PORTH &= ~(1<<3); break;    \
    case 7: PORTH &= ~(1<<4); break;    \
    case 8: PORTH &= ~(1<<5); break;    \
    case 9: PORTH &= ~(1<<6); break;    \
    case 10: PORTB &= ~(1<<4); break;   \
    case 11: PORTB &= ~(1<<5); break;   \
    case 12: PORTB &= ~(1<<6); break;   \
    case 13: PORTB &= ~(1<<7); break;   \
    }
Did you select the Mega 2560 as the board in tools/board?

Yes smiley
Hi,

You could use forum users 'FastLib16's digitalPin and/or digitalIO libraries, these do something similar but already handle the differences between Uno, Mega, Leonardo etc.

http://code.google.com/p/beta-lib/downloads/detail?name=DigitalPinBeta20120804.zip&can=2&q=

I am happily using them on a mega based synth project which I will port to UNO shortly - its nice not having to think about ports and pins as part of the conversion.

Duane B

rcarduino.blogspot.com

Thanks Duane! I was gonna use that library as plan B if I couldn't remap ports. Right now I'm trying avoid using too many libraries to sketch my to keep it simple smiley  Fortunately fWrite macro did the trick.
Logged

Xronos Clock - A talking arduino based alarm clock is now available. Check out xronosclock.com for pictures, source code, schematics, and purchasing info smiley

Dubai, UAE
Offline Offline
Edison Member
*
Karma: 21
Posts: 1670
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

I was skeptical looking at the macro, but there it is, doesn't get much more efficient than that -

Quote
void loop()
{
  fWriteA(3,HIGH);
 12e:   75 9a          sbi   0x0e, 5   ; 14
  fWriteA(3,LOW);
 130:   75 98          cbi   0x0e, 5   ; 14
}

Duane B
Logged


New York
Offline Offline
Sr. Member
****
Karma: 3
Posts: 362
xronosclock.com
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

I was skeptical looking at the macro, but there it is, doesn't get much more efficient than that -

Quote
void loop()
{
  fWriteA(3,HIGH);
 12e:   75 9a          sbi   0x0e, 5   ; 14
  fWriteA(3,LOW);
 130:   75 98          cbi   0x0e, 5   ; 14
}


Duane B
smiley-eek  What does it do?
Logged

Xronos Clock - A talking arduino based alarm clock is now available. Check out xronosclock.com for pictures, source code, schematics, and purchasing info smiley

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 452
Posts: 18694
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Using a single hardware instruction to set or clear one bit (2 clock cycles each).
Logged

Dubai, UAE
Offline Offline
Edison Member
*
Karma: 21
Posts: 1670
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

If like me you don't know much about the way in which compilers optimise your code its actually a bit impressive.

Your code is saying -

if(val)
{
 switch(pin)
 {
   ...
   case 3:
    PORTE |= 1<<5;
   Break;
   ...
}
else
{
  ...
  case 3:
   PORTE &= ~(1<<5);
  break;
  ...
}

Which is something like  -

1) If val use this set of cases, else use that set of cases
2) based on which one of 13 pins this is, do the following
3) use a given port
4) shift a bit n places to the left
5) or the port with the bit shifted in 3

Each time your code calls this macro the compiler is able to say -

1) I know val and its isnt going to change, so I will not use the if, instead I will just go straight to the correct switch/case statement
2) I know the pin and it isnt going to change so I will just choose the correct case statement and forget about the switch and other case statements - I wont be using them because the pin isnt going to change

So now we have just this

PORTE |= 1<<5;

At this point the compiler knows -

1) ok, you want me to shift 1 five places to the left
2) Then you want me to or this with the value of a port

It then figures out, ok, all you actually want me to do with all of this code, is to simply change the value of one bit in a register, I have a dedicated instruction for that which is SBI, so thats what I will use.

The entire macro get reduced to a single instruction to set or clear a bit in a port.

To me, its pretty impressive that the compiler is doing all of that work to optimise out if else,switch case, shift, or and assignment and replace it all with a single instruction.

I am sure its all run of the mill stuff and compilers can be far more impressive in thier optimisation, but it does also illustrate what a nonsense it is for beginners to stress about optimising C code rather than writing well formed code when the compiler already has you pretty well covered on the optimisation side.

Duane B

rcarduino.blogspot.com
Logged


New York
Offline Offline
Sr. Member
****
Karma: 3
Posts: 362
xronosclock.com
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks for the explanation Duane!  That is pretty impressive!
Logged

Xronos Clock - A talking arduino based alarm clock is now available. Check out xronosclock.com for pictures, source code, schematics, and purchasing info smiley

0
Offline Offline
Shannon Member
****
Karma: 161
Posts: 10434
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Since ports are different on different processors, but the Arduino system already provides code to map pin numbers to ports and bits,
it is perfectly possible to pre-compute the port and bit and hold them in variables to allow direct port manipulation:

Code:
#include <pins_arduino.h>

volatile byte * DC_PORT ;
byte DC_BIT ;

void setup_port (int pin)
{
  DC_PORT = portOutputRegister (digitalPinToPort (pin)) ;
  DC_BIT = digitalPinToBitMask (pin) ;
}

void manipulate ()  // toggle pin using pre-computed port address and bit mask
{
  cli () ;   // disable interrupts if other code has interrupt routines using the same port.
  * DC_PORT &= ~DC_BIT ; // digitalWrite (pin, LOW) ;
  * DC_PORT |= DC_BIT ; // digitalWrite (pin, HIGH) ;
  * DC_PORT &= ~DC_BIT ; // digitalWrite (pin, LOW) ;
  * DC_PORT |= DC_BIT ; // digitalWrite (pin, HIGH) ;
  sei () ;
}

void setup ()
{
  pinMode (2, OUTPUT) ;
  setup_port (2) ;
}

void loop ()
{
  manipulate () ;
}

This isn't quite as fast as code using PORTD etc directly as the variable has to be referenced to get the port address, and
the bit masks aren't constant either, but its a whole lot faster than digitalWrite (which has to call portOutputRegister(), digitalPinToPort (), digitalPinToBitMask() every time and check for PWM pins).

This code takes about 0.7us per pin toggle, whereas digitalWrite() takes about 4.5us for 16MHz Arduino (see below for even faster)

If you have a number of pins to write then you'll need more variables to hold them, but this code should be portable between
all Arduinos since it uses the same mechanism as digitalWrite().   You can get even faster access by caching the
variables in local variables before doing any manipulation (local vars are faster to read).

This version takes  0.125us per pin toggle (5 instructions at 16MHz)
Code:
void faster_manipulate ()
{
  volatile byte * d = DC_PORT ;   // cache variable locally
  cli () ;   // disable interrupts if other code has interrupt routines using the same port.
  byte lval = *d ;
  lval &= ~DC_BIT ;    // precompute bit mask for pin low
  byte hval = lval | DC_BIT ;   // precompute bit mask for pin high
    // each pin operation is a write, no reads needed
  * d = lval ; // digitalWrite (pin, LOW) ;
  * d = hval ; // digitalWrite (pin, HIGH) ;
  * d = lval ; // digitalWrite (pin, LOW) ;
  * d = hval ; // digitalWrite (pin, HIGH) ;
  sei () ;
}
Logged

[ I won't respond to messages, use the forum please ]

Pages: [1]   Go Up
Jump to: