Go Down

Topic: PORTD on Due?! (Read 8998 times) previous topic - next topic

Zemovski

Hi!


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

Quote
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)"

Quote
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!

AdderD

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.

Zemovski

#2
Oct 20, 2015, 06:00 pm Last Edit: Oct 20, 2015, 06:01 pm by Zemovski
Hi! Thank you for answer. You are right, I can use digitalWrite() function, but as you can see on the page
http://www.auctoris.co.uk/2012/04/13/arduino-function-generator-part-3/

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.

AdderD

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

Code: [Select]

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.

Zemovski

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


Code: [Select]
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:


Code: [Select]
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.

Wurstnase

#5
Oct 22, 2015, 08:09 am Last Edit: Oct 22, 2015, 08:10 am by Wurstnase
The right way should be:

Code: [Select]

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

westfw

From the datasheet:


Quote
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.


Zemovski

#7
Oct 24, 2015, 09:15 pm Last Edit: Oct 25, 2015, 09:55 am by Zemovski
The right way should be:

Code: [Select]

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:

Code: [Select]

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:
Code: [Select]

  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:
Code: [Select]

  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.

david_prentice

#8
Oct 24, 2015, 10:24 pm Last Edit: Oct 24, 2015, 10:25 pm by david_prentice
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.

westfw

The following "blink" works on my Due:

Code: [Select]
#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.



Quote
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:

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



Zemovski

#10
Oct 25, 2015, 03:17 pm Last Edit: Oct 25, 2015, 03:22 pm by Zemovski
The following "blink" works on my Due:

Code: [Select]
#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.


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.

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.

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

Code: [Select]
   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:

Code: [Select]

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;


david_prentice

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.

Zemovski

#12
Oct 25, 2015, 04:37 pm Last Edit: Oct 25, 2015, 04:38 pm by Zemovski
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:
Code: [Select]
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:
Code: [Select]
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.

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 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.

Zemovski

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.

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.

Go Up