PORTD on Due?!

Hi!

Yesterday I wrote short program on Uno, with seting PORTD to whatever number needed, Works.
The code is simple:

void loop() {
for (int i=0; i<8; i++)
{
PORTD = i;
delay(cekamo);
}}

Then I tried to run this simple code on Due. But every time I get an error message:
Arduino: 1.7.7 (Windows 7), Плата"Arduino Due (Programming Port)"

PORTD_02.ino: In function 'void loop()':
PORTD_02.ino:19:5: error: 'PORTD' was not declared in this scope
Ошибка компиляции.

I tried to declare the "PORTD" but it is useless.

If somebody knows how to program this in Arduino language, without C or C++, then please help.

Thank you!

The Due is not based on an 8 bit AVR processor. You cannot expect code that directly accesses AVR hardware to work when you compile it for Due which has a 32 bit ARM Cortex M3 processor in it. The hardware access is totally different. This has nothing at all to do with Arduino. Instead of directly trying to use PORTD why don't you use the actual Arduino function digitalWrite(pin, state)? You can do this: "digitalWrite(1, HIGH);" and it sets D1 high for you. No need to worry about the underlying hardware and it compiles perfectly fine on both AVR and Cortex based boards.

Hi! Thank you for answer. You are right, I can use digitalWrite() function, but as you can see on the page

the author says that digitalWrite() function is very slow. So, if I need to set 8 pins, it means 8 calls to function digitalWrite(), which will significantly slow down the execution process.

Second, it is much more conveniant to control a bunch of pins, like 8 pins, 16, 24 or whatever number of pins in just one operation, like a call to PORTD.

It seems to me that there has to be a way to do it.

Yes, there is a way to do it. But, before you go off running with scissors in your hand and a smile on your face consider whether it is a good idea.

Look here:

http://forum.arduino.cc/index.php?topic=132130.0

That's a chart with all the pins on the Due and the port they belong to. Notice that the ports jump all over the place? You would have to be really careful which pins you use because they don't stay on the same port for long as you progress through the sequence of digital outputs.

Also, the Due is a LOT faster than an 8MHz or 16MHz 8 bit AVR. It runs at 84Mhz and can execute a full instruction most every clock. The way that digitalWrite works is a bit different as well. You really should try digitalWrite first and see if it is fast enough. If it is then don't make extra work for yourself.

So, let's say you end up finding out that digitalWrite really isn't fast enough for you. In that case you will have to be careful to group all of your outputs into the same port. Configure all of the pins as outputs. Then, if you really insist you can use

PIOA->SODR = 0x1000; //set pin 12 to be driven high
PIOA->CODR = 0x2000; //set pin 13 to be driven low

You can use PIOA, PIOB, PIOC, PIOD. SODR sets output high for any bits that are 1 or on and CODR sets output low for any bits that are 1. Using these methods you could set up to 32 pins at once. Technically there is a way to set both high and low on the same port at the same time if you really need precision but it's potentially a little more complicated. In theory you should be able to toggle pins on the Due at about 1MHz.

Collin80, thank you for example with key words SODR and CODR!

PIOA->SODR = 0x1000; //set pin 12 to be driven high
PIOA->CODR = 0x2000; //set pin 13 to be driven low

But, I tried that code, but the Arduino compiler (1.7.7) complains, gives an error.

So I did search on key words "SODR" and "CODR", serch give me an interesting results:

from a page:
http://forum.arduino.cc/index.php?topic=129868.35

I got this code with SODR and CODR to blink pin 13:

REG_PIOB_SODR = 0x1 << 27;
delay(1000);
REG_PIOB_CODR = 0x1 << 27;
delay(1000);

Yes, it does work on Arduino Due.

Also, I found other interesting pages on Arduino forum, like speed measurment results:
http://forum.arduino.cc/index.php?topic=160653.5

It confirms that function digitalWrite on Arduino Due is very slow.

So much for today, will do the speed measurments tomorrow - after tomorrow on both Uno and Due,
then will write here.

The right way should be:

PIOA->PIO_SODR = 0x1000; //set pin 12 to be driven high
PIOA->PIO_CODR = 0x2000; //set pin 13 to be driven low

From the datasheet:

Synchronous Data Output
Clearing one (or more) PIO line(s) and setting another one (or more) PIO line(s) synchronously cannot be done by using PIO_SODR and PIO_CODR registers. It requires two successive write operations into two different registers. To overcome this, the PIO Controller offers a direct con- trol of PIO outputs by single write access to PIO_ODSR (Output Data Status Register).Only bits unmasked by PIO_OWSR (Output Write Status Register) are written. The mask bits in PIO_OWSR are set by writing to PIO_OWER (Output Write Enable Register) and cleared by writing to PIO_OWDR (Output Write Disable Register).
After reset, the synchronous data output is disabled on all the I/O lines as PIO_OWSR resets at 0x0.

So, you should be able to write up to 32bits at a time via the PIO_ODSR register. IIRC, the original Due core files did NOT initialize the OWSR mask register, causing the obvious use of ODSR to not work as expected. But if you need that last bit of speed, making sure the PIO is configured shouldn't be that much trouble. (also, you can use the mask to your advantage.) Of course, the actual pins are still in inconvenient order with "holes" and such, but that was true of the AVR-based Arduinos as well.

Wurstnase:
The right way should be:

PIOA->PIO_SODR = 0x1000; //set pin 12 to be driven high

PIOA->PIO_CODR = 0x2000; //set pin 13 to be driven low

Wurstnase, I tried that code, but it does not work.

I have LED connected to pin 13. According to the Due pinout diagram, pin 13 is port pin B.27

Here is the code, but on my setup it does not work, LED does not blink:

void setup() {
  pinMode(13, OUTPUT);
}

void loop() {
  PIOB->PIO_SODR = 0x2000; //set pin 13 to be driven high
  delay(1000);
  PIOB->PIO_CODR = 0x2000; //set pin 13 to be driven low
  delay(1000);
}

The LED stays dark, it is never high, never blinks.


Here is more code I tried but it does not work either, the LED does not blink but is always on, no blinking, just steady red light coming out of it:

  void setup() {
  pinMode(13, OUTPUT);
}
void loop() {
  PIOB->PIO_ODSR = 0x2000;
  delay(5000);
  PIOB->PIO_ODSR = 0x0;
  delay(500);
}

One more example of a code, this code does work, it turns pin 13 on and off, but again, with that code I can affect only one pin at a time - pin 27 on port B, but I need to affect all the pins with just one comand:

  void setup() {
  pinMode(13, OUTPUT);
}

void loop() {
  REG_PIOB_SODR = 0x1 << 27;
  delay(300);
  REG_PIOB_CODR = 0x1 << 27;
  delay(700);
}

If somebody knows how the code should be written in order to affect all the pins on port B, then please post it here.

God gave you digitalWrite(). Ok, it only works on one pin at a time. But you know that it will work on every Arduino.

Explain what you really want to do. e.g. write a pattern to D2-D9 pins. This involves writes to PIOB, PIOC, PIOA.

If you want to write to D46-D53, this also involves PIOB, PIOC. None of the header pins seem to be very conveniently arranged.

Even if they are not very convenient, you can still write values to a whole port.

I suspect that you really want to use analogWrite(). Which should just use the hardware DAC. ( I have not checked)

Of course, you can simply connect all the PIOC pins in the order that you want to some external hardware.

I am not familiar with the Due. I first saw your questions on the Zero forum. I presume that you no longer want to use the DAC on the Zero.

Oh, there is no PORTD on Due or Zero. The register naming is different on each processor.

David.

The following "blink" works on my Due:

#define LEDBIT (1<<27)
void setup() {
  pinMode(13, OUTPUT);
  PIOB->PIO_OWER |= LEDBIT;  // Enabled direct writing to ODSR
}
void loop() {
  PIOB->PIO_ODSR = LEDBIT;   // turn the LED on (HIGH is the voltage level)
  delay(1000);              // wait for a second
  PIOB->PIO_ODSR = 0;    // turn the LED off by making the voltage LOW
  delay(1000);              // wait for a second
}

Note the setting of PIO_OWER in setup. This has been mentioned in this thread before, and is in the datasheet... This "protection" is actually somewhat convenient, as it lets you prevent bits that aren't part of your direct write from being modified by the 32-bit write.

with this code I can affect only one pin at a time - pin 27 on port B, but I need to affect all the pins with just one comand:

REG_PIOB_SODR = 0x1 << 27;

Why do you keep repeating this "can only affect one bit at a time" thing? SODR/CODR can set or clear ANY number of bits in the register at one time. To set all 32 bit from a waveform table, you could do something like:

   REG_PIOB_CODR = 0xFFFFFFFF;   // Clear all bits from previous output
   REG_PIOB_SODR = waveform[i];     // Set all the bits that should now be ones.

westfw:
The following "blink" works on my Due:

#define LEDBIT (1<<27)

void setup() {
  pinMode(13, OUTPUT);
  PIOB->PIO_OWER |= LEDBIT;  // Enabled direct writing to ODSR
}
void loop() {
  PIOB->PIO_ODSR = LEDBIT;  // turn the LED on (HIGH is the voltage level)
  delay(1000);              // wait for a second
  PIOB->PIO_ODSR = 0;    // turn the LED off by making the voltage LOW
  delay(1000);              // wait for a second
}

Yes, this code works on my Due too. Thank you.

westfw:
Note the setting of PIO_OWER in setup. This has been mentioned in this thread before, and is in the datasheet... This "protection" is actually somewhat convenient, as it lets you prevent bits that aren't part of your direct write from being modified by the 32-bit write.

I noticed that.

westfw:
Why do you keep repeating this "can only affect one bit at a time" thing? SODR/CODR can set or clear ANY number of bits in the register at one time.

I keep repeating because I did not know how to do it.

And I did not totally understand the REG_PIO*_SODR comand. It was not clear until now...

If you notice this:
REG_PIOB_SODR = 0x1 << 27
the last part of it "0x1 << 27" clearly says that only bit number 27 in port B should be affected, which is pin 13...

So I was thinking that "REG_PIO*_SODR" comand can affect only 1 bit in given port.

westfw:
To set all 32 bit from a waveform table, you could do something like:

   REG_PIOB_CODR = 0xFFFFFFFF;   // Clear all bits from previous output

REG_PIOB_SODR = waveform[i];    // Set all the bits that should now be ones.

Yes, after this code, I now understand how to write a code so all the bits in given port will be affected.

Here is the code I have now, it runs just fine, althought not as fast as one would expect, given the declared speed of Due's CPU - 84 MHZ, versus Uno's 16 MHZ... more about it in next message:

void setup() {
  pinMode(25, OUTPUT);
  pinMode(26, OUTPUT);
}
void loop() {
  // digital 0, on port D pins 0 and 1:
  REG_PIOD_CODR = 0xFFFFFFFF;
  REG_PIOD_SODR = 0x00000000;
  delay(5000);
  
  // digital 1, on port D pins 0 and 1:
  REG_PIOD_CODR = 0xFFFFFFFF;
  REG_PIOD_SODR = 0x00000001;
  delay(5000);

  // digital 2, on port D pins 0 and 1:
  REG_PIOD_CODR = 0xFFFFFFFF;
  REG_PIOD_SODR = 2;
  delay(5000);

  // digital 3, on port D pins 0 and 1:
  REG_PIOD_CODR = 0xFFFFFFFF;
  REG_PIOD_SODR = 0x00000003;
  delay(5000);  
}

Question:
How to use PIO_OVER and PIO_ODSR to affect more then just one bit on a port but not all 32 bit of a port?
In a example you give:
#define LEDBIT (1<<27)
only one bit will be affected, but what if I need more then one bit to be high?
Can we write something like:
#define LEDBIT (1<<27, 28, 01, 02)
or
REG_PIOB_SODR = 0x1 << 27 << 28 << 01 << 02;

No, I suggest that you study your C and C++ textbooks to see how to specify multiple bits in an expression.

Yes, you can write to all the Due pins that use PIOC in one go. Then do the same with other ports.
It will never be as straightforward as a Uno where D0-D7 actually correspond to PORTD.

Pre-calculated values can be written to the Due pins in one go. If you are mapping an 8-bit variable value to 8 adjacent pins like D0-D7, you need to do a lot of masks and shifts. Not as slow as digitalWrite() but still going to be worse than a single assignment. For example, a Uno TFT shield is not much faster on a Due.

OTOH, if you have a purpose built Due shield, or use the Due hardware peripherals like DAC, SPI, DMA, ... you will see a massive improvement in performance over a Uno.

David.

Collin80:
Also, the Due is a LOT faster than an 8MHz or 16MHz 8 bit AVR. It runs at 84Mhz and can execute a full instruction most every clock.

Collin, no, Due is only 33% faster then Uno that runs on 16 MHZ.

I was doing some testing few days ago, before testing I was thinking that my Uno is on 8 MHZ,
but testing clearly says that Uno is running on 16 MHZ.

Here is the speed test program on Uno:

void setup() {
  pinMode(0, OUTPUT);
  pinMode(1, OUTPUT);
  pinMode(2, OUTPUT);
}

void loop() {
  for (float i=0; i<5000000; i++)
  {
    PORTD = B00000001;
    PORTD = B00000010;
    PORTD = B00000100;
  }
  PORTD = B00000000;
  delay(5000);
}

The program would run 5 million loops with only one comand (PORTD = B00000001;) with LED high,
then it will set off the LED for 5 seconds, enough to prepare for the measurments of the next LED on period,
then again run 5 million loops, and so on...

The time needed for this 5 million loops to execute is 64,45 seconds, that is the average time done manually with a stopwatch with 7 samples taken.

Then, to measure the number of comands "PORTD = B00000001;" done in 1 second,
I simply added the comands in for loop:
PORTD = B00000001;
PORTD = B00000010;
PORTD = B00000100;
19 times, so total of 57 additional lines/ "PORTD=" comands, total of 60 "PORTD=" comands in for loop now.

The time measurments, in 7 samples with stopwatch, shows that total time for the for loop to execute
was 82,44 seconds, which is 17,99 seconds more then if there are only 3 "PORTD=" comands in loop.

So, 5.000.000 loops multiply by 57 additional lines = 285.000.000 comands, done in 17,99 seconds.

285 millions comands divided by 17,99 seconds gives us 15.842.134 executed "PORTD=" comands per second!

The close proximity of that number to 16 MHZ means that my Uno's CPU is running on 16 MHZ,
and also important conclusion is that comand "PORTD=" takes only one CPU's clock tick. Only one.

Now, lets compare the similar speed measurment on Due...

And we see that if we compare Due with Uno, Due is only 33% faster then Uno, when executing the similar operation.

Here is the code for time testing on Due:

void setup() {
  pinMode(25, OUTPUT);
}
void loop() { 
  for (float i=0; i<5000000; i++)
  {
  // digital 1, on port D pins 0:
  REG_PIOD_CODR = 0xFFFFFFFF;
  REG_PIOD_SODR = 0x00000001;
  }
    REG_PIOD_CODR = 0XFFFFFFFF;
  delay(5000);
}

The 5 million loops will now execute in 12,787 seconds, according to the 7 samples of time measurment with manual stopwatch.

Then, I added the comand, acctually the pair of comands:
REG_PIOD_CODR = 0xFFFFFFFF;
REG_PIOD_SODR = 0x00000001;
100 times into the for loop, simply by copy and paste. The final code is very long, so I'm not going to post it here.

After adding 100 comands, acctually 100 pairs of comands, the time needed to execute for loop with 5 millions loops, is 36,478 seconds.

The simple math to calculate number of comands executed per 1 second:

5 million loops with additional 100 comands takes additional 23,691 seconds.
So, 500 million comands divided by 23,691 seconds gives us 21.105.061 comands per second.

Just little over 21 million comands per second.

As 21 millions is 4 times smaller then 84 MHZ declared speed of Due,
the conclusion is:

Each comand, in fact 2 comands:
REG_PIOD_CODR = 0xFFFFFFFF;
REG_PIOD_SODR = 0x00000001;
takes 4 CPU clock ticks, and each comand separately takes 2 CPU clock ticks to execute!

Finally, comparing Due to Uno:

We are comparing 21.105.061 comands in Due with 15.842.134 comands executed per one second on Uno.
The Due is only 33% faster then Uno.

Taking into consideration that pins on ports of Uno are simplier to work with, they are in order, not jumping around like it is in Due, the Uno might be better choise to work on the project of simple function generator.

Hi,

One of the things that surprised me the most when i first started using the Uno board was that there was no Port setting instruction, such as:
SetPort(D,B10001000);

I could not believe it when i searched and searched, only to find that it was not a built in.

I cant understand why anyone would write a library for a microcontroller (not a cement mixer) and leave this out. Microcontrollers were meant to be versatile and fast, not restrictive and slow.

I come from the world of PIC where i did everything in ASM so you have direct access to everything, but of course it requires much more study of the device being used before you can do anything. But port access was one of the most important things you would have to do. To have access to only one pin at a time would be just plain silly.

Applications that could benefit from this are just too numerous to list, but i will list a few...

  • As i think had been mentioned, waveform generators.
  • Keyboard scanners.
  • Multiplexers.

Keep in mind that having the ability to set an entire port very fast does not mean that we need to change the pins that fast, it may just mean that we can do other things once we have set all the pins, then come back and test what input results we got from that change later. If we had to do each and every pin, it could take a minimum of 32 times longer. That's just stupid that's all.

How hard could it be to add this basic functionality to the Arduino IDE. It would have to be added into the pin arrangement for each processor maybe each board. It would be best to do this as soon as possible because the board count is low right now, but some time in the future there could be hundreds of boards.

Without that we have to investigate how to do it using the port register variables themselves which takes a lot more time and effort.

MrAl:
Hi,

One of the things that surprised me the most when i first started using the Uno board was that there was no Port setting instruction, such as:
SetPort(D,B10001000);

I could not believe it when i searched and searched, only to find that it was not a built in.

I cant understand why anyone would write a library for a microcontroller (not a cement mixer) and leave this out. Microcontrollers were meant to be versatile and fast, not restrictive and slow.

I agree with you 100%.

It was my impression that Arduino Due is "cripled", so although it comes 32 bit processor on faster clock, it does not perform as one would expect him to do.

Why would anybody want to "criple" the processor" I have no idea.

Originally, I learned simple assembly on 4-bit MPU.
It was so easy to set pins on whole port:
MOV A, #0AH
OUT A

The whole idea of CPUs and microcontrollers is to have total and easy control of everything in that machine.

MrAl:
Without that we have to investigate how to do it using the port register variables themselves which takes a lot more time and effort.

Exactly.

Hi Zemovski

Here's an another example of how to use the ODSR register. The contents of this register are output directly on to the pins of the Due, just like your UNO/Mega example. This is in contrast to using the SAM3X8E's set (SODR) and clear (CODR) output registers.

void setup() {
  REG_PIOD_OWER = 0x0000000F;     // Enable writes to lowest 4-bits of PortD's 32-bit, Output Data Status (ODSR) register: (B00000000000000000000000000001111)
  REG_PIOD_OER =  0x0000000F;     // Set the lowest 4-bits of PortD to outputs: (B00000000000000000000000000001111)
}

void loop() {   
  // Using the register definition: REG_PIOD_ODSR, could also use pointer to register contents: PIOD->PIO_ODSR
  REG_PIOD_ODSR = PIO_ODSR_P0;   // Set the output on P0 (digital pin 25) high, other outputs low: (B00000000000000000000000000000001)
  REG_PIOD_ODSR = PIO_ODSR_P1;   // Set the output on P1 (digital pin 26) high, other outputs low: (B00000000000000000000000000000010)
  REG_PIOD_ODSR = PIO_ODSR_P2;   // Set the output on P2 (digital pin 27) high, other outputs low: (B00000000000000000000000000000100)
  REG_PIOD_ODSR = PIO_ODSR_P3;   // Set the output on P3 (digital pin 28) high, other outputs low: (B00000000000000000000000000001000)
}

The reason for the SODR and CODR registers on the Due, (that are absent from the Uno/Mega), is that it's possible to set and clear outputs without having to logically OR your bit mask with the register to set, or logically AND the inverse bit mask with the register to clear. These logical operations require a read-modify-write to be performed for register bit manipulation. For example on the Mega:

PORTA |= _BV(PORTA3);        // Set bit by logical OR with bit mask
PORTA &= ~_BV(PORTA3);       // Clear bit by logical AND with inverse bit mask

On the Due:

REG_PIOD_SODR = PIO_SODR_P3;      // Set bit without logical OR with bit mask
REG_PIOD_CODR = PIO_CODR_P3;      // Clear bit without logical AND with inverse bit mask

This issue with using the whole of port D on the Due is that some of the pins are used by the Arduino core by other functions, for example port D pins P4 and P5 are used for Serial3 pins TX3 and RX3.

Can we write something like:
#define LEDBIT (1<<27, 28, 01, 02)
or
REG_PIOB_SODR = 0x1 << 27 << 28 << 01 << 02;

It would look like:

 REG_PIOB_SODR = (1<<27) | (1<<28) | (1<<2);
  for (float i=0; i<5000000; i++)
  {
  // digital 1, on port D pins 0:
  REG_PIOD_CODR = 0xFFFFFFFF;
  REG_PIOD_SODR = 0x00000001;
  }

"float"?? Really? You're lucky that your loop terminates - at some point, adding 1 to a large floating point number doesn't change the number...

It should go slightly faster if you use the PIOD pointer syntax:

  PIOD->PIO_CODR = 0xFFFFFFFF;
  PIOD->PIO_SODR = 0x00000001;

Note that you're setting four times as many bits in the Due example. (and yes, it matters.)

285 millions comands divided by 17,99 seconds gives us 15.842.134 executed "PORTD=" comands per second!

I guess that's almost believable, for certain optimized cases. The AVR "out" instruction takes one cycle, assuming you've managed to pre-load other registers with the values you are outputting.

[Each Due C output instruction] takes 4 CPU clock ticks, and each comand separately takes 2 CPU clock ticks to execute!

Also about right, for predictable circumstances...

Don't be fooled into thinking that the Due is only 33% faster for "arbitrary" code. Some things it will do much faster than an AVR. Other things, it won't. Pin IO is rarely highly optimized. Once your pin toggle rate gets to about 10MHz, you get degraded signals anyway - pin drivers just don't go that fast...

the Uno might be better choise to work on the project of simple function generator.

could be. The only thing that isn't "simpler" on an AVR than an ARM is probably integer math with values larger than 8 bits... (and, for a "function generator", you might run into RAM shortages creating waveform tables.)
(a non-simple function generator on Due would use DMA with the D2A peripheral, allowing it to do the background wave output in ... about ZERO cpu cycles...)

we have to investigate how to do it using the port register variables themselves which takes a lot more time and effort.

Compared to learning assembly language on PICs or 4bit CPUs? And you wish there was a function "setport(D, val)" instead of "PORTD = val"?

Personally, I think the Arduino focus on individual "pins" instead of byte-wide (or larger) "ports" was one of the most important things that they did. Because binary values are confusing, especially for beginners (And this conversation is a good example: Zemovski has experience with 4-bit assembly (much more experience than the Arduino target audience, but he couldn't figure out how to put together a multibit constant in C.) "Advanced" boards like MEGA and Due have grouped similar-function "pins" (all PWM, all Serial, etc) on the board, which tends to be disruptive of port-wide access (since microcontrollers tend to scatter such peripherals across multiple ports.) But it's not "wrong."
(I do wish that the "uncommitted" (right-edge connector) had grouped pins port-wise, though. (although, see also: http://community.atmel.com/forum/samd10-pinout-venting ))

Hi,

Wow, i thought i was the only one old enough here to remember those days :slight_smile:

I learned my first assembler on the 8004, although it was all in theory with that and the 8008, until i actually started programming after building a board with the 8080 processor, which mimicked today's controller boards.
I then moved to the Z80 which i loved because it had so many instructions, and built a controller board around that with I/O and UVEPROM and the like. That was a whole board about 4 inches by 6 inches that did about the same as the single chip Atmel 328 today used in the Uno.

So i guess i will have to investigate how to use the ports directly too then as i know for sure i will have to do that at some point.

I am happy to see digitalWrite() in the core library though, as that helps many people including myself get started on the Arduino boards. On the Uno 16Mhz I clocked it at 115kHz max though, and by direct port access on the Uno i could get better than 2MHz, i think i got up to 5Mhz or something one time, just toggling the pins for the tests. So we see the limitation right away there.
If there was also a port access instruction that would be nice too, although it would probably have to be considered 'safe' to run with the other instructions and initializations. That could be what is holding it back.
Alternately, the instruction would have to be placed in an "Advanced" instruction category for advanced users.

I am still not sure i know how to do it on the Due yet so i'll have to read this thread over again :slight_smile:

[LATER]
I found a library called "DigitalWriteFast" that supposedly writes 20 times faster than digitalWrite(). I am not sure if it is meant for the Due yet however.
The download file is "DWF.zip" and found on the web after a search for "digitalwritefast".

digitalWriteFast() for (most CPUs that it is implemented on) relies on the arguments being constants. Because, on many 8-bit microcontrollers, there is a special instruction for setting or clearing an IO pin. ("sbi port, bitno" on AVR.) But typically, the port number and bit number are hard-coded into the clear or set instruction, and if you wanted either the port, bitnumber, or result value to be variable, you would be faced with much more complex code. On top of that, the Arduino code has special cases for "what if the user had previously done an 'analogWrite()' on the pin that they now want to do a digitalWrite() on?" All complexities included, the AVR digitalWrite function is about 50 instructions.

Now, the ARM isn't specifically designed to be a microcontroller, and it doesn't have ANY special instructions. It doesn't even have the concept of "IO pins." The peripherals are treated just like memory locations, and nothing actually operates directly on memory locations, so the way to change one bit involves loading the memory contents into a register, loading the bit into another register, doing a register/register operation, and storing the results. That'd be some 4 or more instructions (some of them being double-length, or using data words as well.) And there are atomicity problems, too. Thus, on ARM chips, the peripherals are made smarter, with some bitwise smarts built into the peripheral instead of the cpu. (thus the "bit set", "bit clear", and (sometimes) "bit toggle" registers, as well as the "actual bits".) Even with the smarter peripherals, setting or clearing a single IO pin on ARM is a much more expensive operation than on an 8-bit microcontroller. (usually still about 4 instructions, probably 6 clocks or so.)
BUT! Remember that there were no special cases for bit number or port or value. That means that expanding a function like digitalWrite() to accept variables is EASIER on an ARM than on an AVR. In fact, digitalWrite() for Due is only about 30 instructions, instead of 50...

So... It's complicated.