Go Down

Topic: Port manipulation PINx commands with Arduino Due ? (Read 30667 times) previous topic - next topic

blazek

#30
May 19, 2013, 09:22 pm Last Edit: May 19, 2013, 09:29 pm by blazek Reason: 1
Hi guys,
I would like to ask if is possible to generate square waves with digitalWriteDirect?
this code
Code: [Select]
void loop() {
 digitalWriteDirect(9, HIGH);          
 digitalWriteDirect(9, LOW);        
}

produces these waves (sry for bad quality)
http://imageshack.us/f/838/19052013i.jpg/
thanks

stimmer

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

Code: [Select]
void loop() {
  for(;;){
    digitalWriteDirect(9, HIGH);           
    digitalWriteDirect(9, LOW);         
  }
}
Due VGA library - http://arduino.cc/forum/index.php/topic,150517.0.html

JoeN

Code: [Select]
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.
I will never ask you to do anything that I wouldn't do myself.

blazek

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

MsClaude

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

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

blazek

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

MsClaude

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.

Code: [Select]

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); }
}

iz4afl


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

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

Code: [Select]

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

Professor_Oak

#38
Jun 11, 2014, 12:48 am Last Edit: Jun 11, 2014, 01:05 am by Professor_Oak Reason: 1
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:
http://arduino.cc/en/Hacking/PinMappingSAM3X

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

Code: [Select]

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

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.

Code: [Select]

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.

Code: [Select]

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);

Measureino

Code: [Select]

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!

iana

Code: [Select]

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?





AlfaOmega

#41
Feb 24, 2015, 03:05 am Last Edit: Feb 24, 2015, 03:05 am by AlfaOmega

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

iana

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.


Palliser

#43
Mar 19, 2015, 11:17 pm Last Edit: Mar 19, 2015, 11:19 pm by Palliser
Hello iana,

To manipulate individual pins in Arduino Due you have to use Graynomad pinout diagram...

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

...with the following code lines:

Code: [Select]
// Pin HIGH
REG_PIO[port]_SODR = 0x1 << [port Pin];

// Pin LOW
REG_PIO[port]_CODR = 0x1 << [port Pin];


For example, if you want to set HIGH pin 13, look at its port pin (clear yellow background) which is B.27. This indicates pin 27 of port B. Thus,

Code: [Select]
REG_PIOB_SODR = 0x1 << 27;

if you want to set LOW pin 36, its port pin is C.4. Thus,

Code: [Select]
REG_PIOC_CODR = 0x1 << 4;

if you want to set LOW pin A7, its port pin is A.2. Thus,

Code: [Select]
REG_PIOA_CODR = 0x1 << 2;

and so on....

p


jowen

The other way of doing direct port manipulation looks like this:
Code: [Select]
PIOx->register=value;  
long r = PIOx->register;

where x is A,B,C or D and register is as in the data sheet

For example:
Code: [Select]

PIOB->PIO_SODR=1<<27; //lights the LED
PIOB->PIO_CODR=1<<27; //clears it
boolean p12=!!(PIOD->PIO_PDSR & (1<<8)); //read pin 12


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

Code: [Select]

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);
}

void setup() {                
  pinMode(13, OUTPUT);    
  pinMode(12, INPUT);    
  Serial.begin(9600);
}

void loop() {
  digitalWriteDirect(13, HIGH);  
  delay(1000);          
  digitalWriteDirect(13, LOW);    
  delay(1000);          
  Serial.println(digitalReadDirect(12));
}


I am currently trying to run the command digitalReadDirect() in my program.

Each iteration in the loop, the command is run to see if there is a LOW or HIGH state, and a variable is set to that state. In other words, I'm trying to synchronize a certain operation with an input square wave, changing from 0V to 3V. After the program changes the state of a variable 'volatile int state' to HIGH or LOW, I have an if(state == LOW) statement to determine whether an operation should run at that time.

The if(LOW) statement seems to operate correctly asynchronously to the square wave, so I had high hopes. The operation ran when the square wave was LOW, and stopped when the square wave was HIGH.

When I attempted the if(HIGH) statement, the operation was never triggered, and the signal remained LOW regardless of the square wave input.

Can anyone explain why the if(HIGH) statement might not correctly trigger the operation ?

I was using digital pin 12 as a square wave input, and I am trying to trigger an operation output on DAC0. I am using this command because I need my output to trigger quickly, and it did work asynchronously.

I think that was clear enough, but ask questions if needed, I will check up on this.

Go Up