Hi,
This has been asked before, but I can't really tell if it's answered. I want to write to 2 gpio pins at the same time, one going high and other low in the same clock cycle. I currently use:
PIOD->PIO_CODR = 0b011u ;
PIOD->PIO_SODR = 0b100u ;
to clear then set pins in port D. This has about a 25ns delay, or a 75ns delay. I don't know why it's different.
Is there a way to write a byte, 16b word or 32b word to port D and set the values I want in one cycle?
I don't care about the other bits on the port, unless they effect the Due in some way.
How are you measuring that?
Scope. I'm doing a PWM. Here's the interrupt code:
void TC7_Handler() {
static uint32_t ramp=0, delta=20;
TC2->TC_CHANNEL[1].TC_SR; // <-- Is this to reset the timer?
ramp+=delta;
ramp%=500;
if (ramp > value) {
// This executes 50ns faster, why?
digitalWrite(LED_BUILTIN, LOW );
// This sets D.2 low.
PIOD->PIO_CODR = 0b101u ;
PIOD->PIO_SODR = 0b010u ;
return;
}
digitalWrite(LED_BUILTIN, HIGH );
//This sets D.2 high.
PIOD->PIO_CODR = 0b011u ;
PIOD->PIO_SODR = 0b100u ;
}
That one executes faster is irritating, but I really want 0ns delay. Is that possible?
Let's be perfectly clear. Between what and what?
BTW one executes faster because the first section is conditional and contains a function return (which short circuits the rest of the code) and the second section is unconditional. Did you forget an 'else'? Like:
if (ramp > value) {
digitalWrite(LED_BUILTIN, LOW );
// This sets D.2 low.
PIOD->PIO_CODR = 0b101u ;
PIOD->PIO_SODR = 0b010u ;
}
else
{
digitalWrite(LED_BUILTIN, HIGH );
//This sets D.2 high.
PIOD->PIO_CODR = 0b011u ;
PIOD->PIO_SODR = 0b100u ;
}
PIOD_ODSR = 0x12345678;
will write all 32bit, assuming that the PIO Write Protect Status Register (PIOD_WPMR) has been set appropriately, and the Output Enable Register (PIOD_OER) has those bits set. (I think the latter can be used to ensure that you only write some bits.)
It'll take more than a single cycle (wait states, bus cycles, time to load constants), but should always be faster than separate set/clear operations.
I had the "else" originally, I thought it might be why there was a difference in the the number of cycles. It wasn't. Short circuiting with a "return" is intentional.
I don't see why there is a difference in execution between setting D.2 high and low. Going low, the code in the "if" executes faster.
Oh, wait, I see, it's not any different. But I find it not as straightforward as the version I posted. They both do the same thing.
Most processors take different times to execute a test-and-branch depending on whether a program branch is taken or not. That may account for at least part of the mentioned 50ns difference.
If you fully understand the code, please explain it for our benefit. It appears that you are not clearing and setting the same pins. Or, reference the section/page in the processor manual...
It gives more people a chance at helping, vs. waiting around for a "Due specialist" to wander by.
Such inefficient code doesn't belong in any ISR. Modulus division is orders of magnitude slower than a simple increment, test, and reset if over range.
You may wonder about the delta remainder... but you can maintain it like this:
ramp+=delta;
if (ramp >= 500) ramp -= 500;
instead of
ramp+=delta;
if (ramp >= 500) ramp = 0;
I think I will need a Due specialist. I will change the modulus to a test, but the question is still:
Can you toggle 2 GPIO pins in the same cycle on a Due?
This took care of the 75ns difference between falling and rising:
void TC7_Handler() {
static uint32_t ramp=0, delta=20;
uint32_t set, clear;
bool led;
TC2->TC_CHANNEL[1].TC_SR;
ramp+=delta;
ramp %=500;
if (ramp > value) {
led = LOW;
// This sets D.2 low.
clear = 0b101u ;
set = 0b010u ;
} else {
//This sets D.2 high.
led = HIGH;
clear = 0b011u ;
set = 0b100u ;
}
PIOD->PIO_CODR = clear ;
PIOD->PIO_SODR = set ;
digitalWrite(LED_BUILTIN, led );
}
As far as writing to the ports, it can be done with Atmels ASF functions. These aren't available in the Arduino libs AFAIK.
Hi,
Have you Googled;
arduino due port manipulation
Tom..
PIOC->PIO_SODR = OUTPINS;
delay(1000);
PIOC->PIO_CODR = OUTPINS;
delay(1000);
I am using the clear and set commands, it takes 2 cycles minimum to toggle two pins. I think it's just the way it is with Due and Arduino.
Several posts mention a "PORT_IOBUS" with SAMD21. Apparently you can toggle as well as set or clear. Doesn't seem to be available on SAM3.
I didn't see this reply. Where is this defined?
oops. That'd be PIOD->PIO_ODSR
It's in `...tools/CMSIS-Atmel/1.2.0/CMSIS/Device/ATMEL/sam3xa/include/component/pio.h
And section 32.7.12 "PIO Controller Output Data Status Register" in the data sheet.
Should have thought of that. Great I'll try it!
That did it! I'd seen this:
RwReg PIO_ODSR; /**< \brief (Pio Offset: 0x0038) Output Data Status Register */
but I thought is status meant the input output state or something.
Thanks!
31.5.5 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 control 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.
Please edit your post and remove the formatting to make it normal text so it is readable.