Port manipulation PINx commands with Arduino Due ?

seppo:
REG_PIOC_ODSR = 0xFF;

which should change all bits 0-15 simultanously to 1, look at my post earlier today, where test results are reported,

BR,
Seppo

No, that will only set bits 0-7 to 1 (0xFF is 11111111 in binary).

yes , of course 8 bits ,

and it is possible to write simultaneously words and double words (32 bits) but
not all the pins of PIO - ports are connected to Due I/O ,

Seppo

stimmer:

inline void digitalWriteDirect(int pin, boolean val){

if(val) g_APinDescription[pin].pPort -> PIO_SODR = g_APinDescription[pin].ulPin;
 else    g_APinDescription[pin].pPort -> PIO_CODR = g_APinDescription[pin].ulPin;
}

inline int digitalReadDirect(int pin){
 return !!(g_APinDescription[pin].pPort -> PIO_PDSR & g_APinDescription[pin].ulPin);
}

Hi.

Ok, I've managed to put library working with your functions, Thank you!

But it's still very slow running DEMO compared to Chipkit. I know that in chipkit, the entire byte is written to port at once and in due I'm writing bit by bit, but I'm not sure that this is enough for a huge difference in run time for demo. In chipkit it takes around 24s, I'm getting with due 44s.

Is there a way to use ODSR in this functions? Can I write to ODSR bit by bit?

From datasheet, ODSR should be used for synchronous transfer. I'm thinking, maybe using ODSR i might get bet speed.

I was trying to do this without success:

		//DB00 on PIN37 -> PIO_PC5
		REG_PIOC_ODSR=((VL&0x01)<<5) & 0x20;
		//DB01 on PIN36 -> PIO_PC4
		REG_PIOC_ODSR=(VL<<3) & 0x10;
		//DB02 on PIN35 -> PIO_PC3
		REG_PIOC_ODSR=(VL<<1) & 0x08;
		//DB03 on PIN34 -> PIO_PC2
		REG_PIOC_ODSR=(VL>>1) & 0x04;
		//DB04 on PIN33 -> PIO_PC1
		REG_PIOC_ODSR=(VL>>3) & 0x02;
		//DB05 on PIN32 -> PIO_PD10
		REG_PIOD_ODSR=(VL<<5) & 0x400;
		//DB06 on PIN31 -> PIO_PA7
		REG_PIOA_ODSR=(VL<<1) & 0x80;
		//DB07 on PIN30 -> PIO_PD9
		REG_PIOD_ODSR=(VL<<2) & 0x200;
		//DB08 on PIN22 -> PIO_PB26
		REG_PIOB_ODSR=(VH<<26) & 0x4000000;
		//DB09 on PIN23 -> PIO_PA14
		REG_PIOA_ODSR=(VH<<13) & 0x4000;
		//DB10 on PIN24 -> PIO_PA15
		REG_PIOA_ODSR=(VH<<13) & 0x8000;
		//DB11 on PIN25 -> PIO_PD0
		REG_PIOD_ODSR=(VH>>3) & 0x01;
		//DB12 on PIN26 -> PIO_PD1
		REG_PIOD_ODSR=(VH>>3) & 0x02;
		//DB13 on PIN27 -> PIO_PD2
		REG_PIOD_ODSR=(VH>>3) & 0x04;
		//DB14 on PIN28 -> PIO_PD3
		REG_PIOD_ODSR=(VH>>3) & 0x08;
		//DB15 on PIN29 -> PIO_PD6
		REG_PIOD_ODSR=(VH>>1) & 0x40;

The working code is:

		//DB00 on PIN37 -> PIO_PC5
		digitalWriteDirect(37,(VL & 0x01));
		//DB01 on PIN36 -> PIO_PC4
		digitalWriteDirect(36,(VL & 0x02));
		//DB02 on PIN35 -> PIO_PC3
		digitalWriteDirect(35,(VL & 0x04));
		//DB03 on PIN34 -> PIO_PC2
		digitalWriteDirect(34,(VL & 0x08));
		//DB04 on PIN33 -> PIO_PC1
		digitalWriteDirect(33,(VL & 0x10));
		//DB05 on PIN32 -> PIO_PD10
		digitalWriteDirect(32,(VL & 0x20));
		//DB06 on PIN31 -> PIO_PA7
		digitalWriteDirect(31,(VL & 0x40));
		//DB07 on PIN30 -> PIO_PD9
		digitalWriteDirect(30,(VL & 0x80));
		//DB08 on PIN22 -> PIO_PB26
		digitalWriteDirect(22,(VH & 0x01));
		//DB09 on PIN23 -> PIO_PA14
		digitalWriteDirect(23,(VH & 0x02));
		//DB10 on PIN24 -> PIO_PA15
		digitalWriteDirect(24,(VH & 0x04));
		//DB11 on PIN25 -> PIO_PD0
		digitalWriteDirect(25,(VH & 0x08));
		//DB12 on PIN26 -> PIO_PD1
		digitalWriteDirect(26,(VH & 0x10));
		//DB13 on PIN27 -> PIO_PD2
		digitalWriteDirect(27,(VH & 0x20));
		//DB14 on PIN28 -> PIO_PD3
		digitalWriteDirect(28,(VH & 0x40));
		//DB15 on PIN29 -> PIO_PD6
		digitalWriteDirect(29,(VH & 0x80));

stimmer:
I had a go at writing some digitalReadDirect/digitalWriteDirect functions but you might want to give them some more thorough testing as I only checked if it worked with the LED :slight_smile:

inline void digitalWriteDirect(int pin, boolean val){

if(val) g_APinDescription[pin].pPort -> PIO_SODR = g_APinDescription[pin].ulPin;
 else    g_APinDescription[pin].pPort -> PIO_CODR = g_APinDescription[pin].ulPin;
}

inline int digitalReadDirect(int pin){
 return !!(g_APinDescription[pin].pPort -> PIO_PDSR & g_APinDescription[pin].ulPin);
}

They seem to work fine. I just used it for my first sketch for my Due, a comparison of port switching and square wave generation using DigitalWrite vs. direct writing using your inline function. Big difference, this is what it looks like on my scope (it overshoots the square because I have a long lead which I corrected for the second measurement):

The waves are a whole lot squarer when running at 1% of the maximum speed. :slight_smile:

Sorry for the bad smartphone pics, I haven't figured out how to properly screen capture on the scope yet and I can't open the manual right now because Adobe Acrobat is having a weird fit.

To use REG_PIOD_ODSR use must first enable the bits with REG_PIOD_OWER (the output write enable).
For instance:

void setup() {
pinMode(12, OUTPUT);
pinMode(14, OUTPUT);
pinMode(15, OUTPUT);
REG_PIOD_OWER = 0xFFFF;
}

void loop() {
REG_PIOD_ODSR = 0x0130;
REG_PIOD_ODSR = 0x0000;
REG_PIOD_ODSR = 0x0130;
}

Will produce a ~23ns low going pulse on pin 12, 14 and 15. For some reason you need to set the pinModes to make this work.

I don't know if any one has posted on this yet, fastest update rate for a DAC(?). Here is some code:

int j = 0, topcount = 5;

void setup() {
// this is a cheat - enable the DAC
analogWrite(DAC0,0);
}

void loop() {
// write directly
dacc_write_conversion_data(DACC_INTERFACE, 0x0000);
for (j = 0;j < topcount;j++);
// delayMicroseconds(1);
dacc_write_conversion_data(DACC_INTERFACE, 0x0400);
for (j = 0;j < topcount;j++);
// delayMicroseconds(1);
dacc_write_conversion_data(DACC_INTERFACE, 0x0800);
for (j = 0;j < topcount;j++);
// delayMicroseconds(1);
dacc_write_conversion_data(DACC_INTERFACE, 0x0C00);
for (j = 0;j < topcount;j++);
// delayMicroseconds(1);
dacc_write_conversion_data(DACC_INTERFACE, 0x0FFF);
for (j = 0;j < topcount;j++);
// delayMicroseconds(1);
dacc_write_conversion_data(DACC_INTERFACE, 0x0C00);
for (j = 0;j < topcount;j++);
// delayMicroseconds(1);
dacc_write_conversion_data(DACC_INTERFACE, 0x0800);
for (j = 0;j < topcount;j++);
// delayMicroseconds(1);
dacc_write_conversion_data(DACC_INTERFACE, 0x0400);
for (j = 0;j < topcount;j++);
// delayMicroseconds(1);
dacc_write_conversion_data(DACC_INTERFACE, 0x0000);
for (j = 0;j < topcount;j++);

}

This code produces a triangular four steps up, four steps down ramp that goes from about 480mV to 2.32V. Each step is about 680ns wide.
The waveform becomes unstable below topcount = 5. Is this the fastest DAC step rate?

seppo:
the reading is easy:

eg.

unsigned long int input_data = REG_PIOC_PDSR;

reads the C -port (32 bit wide) and all the bits simultaneously to input_data variable and fast ,
and I've tested A, B, C and D ports by changing bit states ((work ok with Arduino Due),

but then encountered problems when tried writing in the same way with command eg.

REG_PIOC_ODSR = 0xFF;

which should change all bits 0-15 simultanously to 1, look at my post earlier today, where test results are reported,

BR,
Seppo

How did you read port C? I tried this but it does not work:

    REG_PIOC_ODR = 0x3fc;
    REG_PIOC_PER = 0x3fc;
    int pixelData = REG_PIOC_PDSR >> 2;

Hi guys,
I would like to ask if is possible to generate square waves with digitalWriteDirect?
this code

void loop() {
  digitalWriteDirect(9, HIGH);           
  digitalWriteDirect(9, LOW);         
}

produces these waves (sry for bad quality)

thanks

The squarewave is uneven because of function call overhead. You can lessen this by making your own loop:

void loop() {
  for(;;){
    digitalWriteDirect(9, HIGH);           
    digitalWriteDirect(9, LOW);         
  }
}
void loop() {
  for(;;){
    digitalWriteDirect(9, HIGH);           
    digitalWriteDirect(9, LOW);   
    digitalWriteDirect(9, HIGH);           
    digitalWriteDirect(9, LOW);   
    digitalWriteDirect(9, HIGH);           
    digitalWriteDirect(9, LOW);   
... repeat many times      
  }
}

Even with the tighter loop the square wave will be uneven because of the looping overhead of 3 or 4 machine cycles. You can cheat by repeating that block of code a bunch of times (feel free to hold ctrl-v down and paste it in a few hundred times). The more there is before the eventual hiccup, the better chance your scope will have at stabilizing the wave.

thanks a lot =) now I have nice sinus wave about 20MHz =)
and one more question:
I want to make frequency generator with this utility so is there any documentation about counting ticks? because with msDelay I can do freq. about several thousand KHz (not enough for me =/)..
thanks

I want to make frequency generator with this utility so is there any documentation about counting ticks?

=> Timer Counter (TC)!
You can count the clocks of MCK/2 (=42MHz)

ATMELSAM(datasheet) 37.1 Description
The Timer Counter (TC) includes three identical 32-bit Timer Counter channels.

Start the counter, read the counter (Counter Value register) or generate an interrupt (Interrupt Enable Register) and react
to your choosen number of counts.

Sound easy, but its a it lot of register manipulation. Maybe you can find a lib for he TC?

Could you be a bit more concrete please? I with code above I have got 32Mhz max... At the moment I think there is no working counter for Due, or am I wrong?
thanks

Sorry, haven't been here for some time.
I dunno understand what exactly you want, so here just a short example of running Timer counter TC0
at 42 MHz. Maybe this is something that can fit your needs.
Sketch starts and reads (CV = counter value) counter.

void setup(){  Serial.begin(9600); }

void loop() 
{
unsigned long v[10];
int i;
  PMC->PMC_PCER0 = 1<<27;  // PMC: Enable Timer TC0
  TC0->TC_CHANNEL[0].TC_CMR = 0;  // Control mode reg.: set Clock to MCK/2 = 42 MHz  
  TC0->TC_CHANNEL[0].TC_CCR = 4 + 1;  // activate Timer:  // Start + Enable 
  Serial.println("---------------------------------------------------------");
  Serial.println("-------       delay is not exackt, TC0 is!      ---------");
  Serial.println("---------------------------------------------------------");
  Serial.println("Delayed Readout (24 us -> ~1000 Ticks)");
  TC0->TC_BCR = 1;  // restart(sync) 
  for(i=0; i<10; i++) { v[i]= TC0->TC_CHANNEL[0].TC_CV;  delayMicroseconds(24); }
  for(i=0; i<10; i++) { Serial.print(i); Serial.print(": "); Serial.println(v[i]); }
  delay(5000);  
  Serial.println("---------------------------------------------------------");
  Serial.println("Endless Readout (1s -> +42 000 000)");
  TC0->TC_BCR = 1;  // restart(sync) 
  while(1==1)  { Serial.println(TC0->TC_CHANNEL[0].TC_CV); delay(1000); }
}

stimmer:
The squarewave is uneven because of function call overhead. You can lessen this by making your own loop:

void loop() {

for(;;){
    digitalWriteDirect(9, HIGH);           
    digitalWriteDirect(9, LOW);         
  }
}

To make HIGH and LOW phases simmetric, just add a cuple of NOP (No Operation) after the digitalWriteDirect(9, HIGH) :

#define NOP __asm__ __volatile__ ("nop\n\t");

...

void loop() {
  for(;;){
    digitalWriteDirect(9, HIGH);
    NOP
    ...
    NOP   
    digitalWriteDirect(9, LOW);         
  }
}

Each NOP extends the HIGH phase by 1 clock cycle, i.e. 12 ns on DUE (1/84 MHz).

I know that this is an old thread but I found the answer and thought I'd share.

You can find the register info for each pin on the Arduino website at:

The registers are listed in the file instance_piob.h. These registers are 32 bit since the Due uses 32 bit architecture. Instead of setting a register to 1 and 0 for High and Low, there is a set register (SODR) and a clear register (CODR).

// Enable
REG_PIO[port]_SODR = [Pin mask];
// Disable
REG_PIO[port]_CODR = [Pin mask];

For example to blink pin 13 you could use the following code. From the link provided, the SAM3X pin name of 13 is PB27 so the port is B and the bit is 27

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

Using this method you could set multiple pin simultaneously, etc. There is a simpler way to do this if you do not know the port information because it is stored in the array g_APinDescription for each pin so you could save the reg address in a local variable or just access the array.

g_APinDescription[13].pPort->PIO_SODR = g_APinDescription[13].ulPin; // ulPin is the mask for that one pin so 1 << 27

Speedwise, I did a comparison and the results were 166us vs 5148us which is just over 31 times faster.

time = millis();
  for (int i = 0; i < 1000000; i++) {
    REG_PIOB_SODR = mask;
    REG_PIOB_CODR = mask;
  }
  Serial.println(millis() - time);
  
  time = millis();
  for (int i = 0; i < 1000000; i++) {
    digitalWrite(13, 1);
    digitalWrite(13, 0);
  }
  Serial.println(millis() - time);
1 Like
REG_PIOB_SODR = 0x1 << 27;
delay(1000);
REG_PIOB_CODR = 0x1 << 27;
delay(1000);

Although not having deep knowledges I could follow this.

But I don't know how to use this to
a) convert data on 8 input pins to a one byte value and
b) set other 8 output pins to the value of another byte value
both as quick as possible.
There are no limitations which pins to use as long as I can use Serial Input.

Can you help me?

Thanks!

Measureino:

REG_PIOB_SODR = 0x1 << 27;

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



Although not having deep knowledges I could follow this. 

But I don't know how to use this to 
a) convert data on 8 input pins to a one byte value and
b) set other 8 output pins to the value of another byte value
both as quick as possible.
There are no limitations which pins to use as long as I can use Serial Input.

Can you help me?

Thanks!

Hi, i know this is an old thread, but i still haven't found an easy way to put this together. i'm using an Arduino Mega 1280, with portA and PORTC and it's working ok.

Now i need to take this to Arduino Due, and i couldnt find a way to do it the same way.

coul you explain it again like for very dummies?

Thank you very much Professor_Oak!

iana - in order for this code to work you have to set the pin as output, just like in the Blink example.
_

Ok, the problem is that due layout dose not have the same ports as Arduino UNO or Mega, so there is no way to manipulate them like PORTC or PORTA, there is a way to do the same, but it's a really bad headache to find out how.