I am trying to find out the fastest way to update select pins on a PORTx at once on Due.
The select pins are selected once during setup and would like to update the data on those select pins in the loop. I have read through this informational thread and am still confused as to which way is the most efficient. Looks like there are multiple ways to achieve the same. Hope someone familiar with Due can shed an insight here.
The PORT I am interested in is PORTC bits 1 to 9 and 12 to 14
Do we have to declare all those pins as outputs (one by one)?
Do you have a 13bit value (or 10bit + 3bit) that you're trying to output, on individual one-bit-at-a-time values that you want to do as quickly as possible?
The former is potentially much faster, but less compatible with The Arduino Way.
Using pinMode() on one bit at a time is probably the easiest way to set them all to outputs; it only has to be done once, so its speed doesn't matter.
Will try to add a functioning code snippet when I get a chance
Yes, exactly, I have a 12bit value that I send to a 12bit multiplying DAC within an ISR running at 16KHz to 140KHz depending on user settings. Since for some reason Arduino decided to skip routing BIT10 and BIT11 of PORTC to header pins, I got around this by doing left shifting of 12bit data while generating them and all ready to go when ISR runs.
#define interruptPinTTL 2
volatile int total_pts = 520;
volatile int inc_pts = 512;
volatile unsigned short counter = 0;
unsigned long bitBuf[520];
void setup()
{
// Initiate serial communication
Serial.begin(9600);
generateBitbuffer();
//Set all 12bit pins to DAC as outputs
//PIN33-PC1 to PIN41-PC9 and PIN51-PC12 to PIN49-PC14
pinMode(33, OUTPUT);
pinMode(34, OUTPUT);
pinMode(35, OUTPUT);
pinMode(36, OUTPUT);
pinMode(37, OUTPUT);
pinMode(38, OUTPUT);
pinMode(39, OUTPUT);
pinMode(40, OUTPUT);
pinMode(41, OUTPUT);
pinMode(51, OUTPUT);
pinMode(50, OUTPUT);
pinMode(49, OUTPUT);
REG_PIOC_OWER = 0x000073FE;
REG_PIOC_OWDR = 0xFFFF8C01;
// Setup interrupt pins and their behavior
pinMode(interruptPinTTL, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(interruptPinTTL), incrementBit, RISING);
}
void loop()
{
}
void generateBitbuffer() {
unsigned long data, temp;
float step_size = (4095 / (inc_pts - 1.0));
float val = 0;
for (int i = 0; i < inc_pts; i++, val += step_size)
{
data = round(val);
data <<= 1;
temp = (data & 0x1C00)<<2;
data &= 0x3FF;
bitBuf[i] = data | temp;
Serial.println(bitBuf[i]);
}
if (total_pts > inc_pts)
{
step_size = (4095 / (total_pts - inc_pts + 1.0));
val = 4095 - step_size;
for (int i = inc_pts; i < total_pts; i++, val -= step_size)
{
data = round(val);
data <<= 1;
temp = (data & 0x1C00)<<2;
data &= 0x3FF;
bitBuf[i] = data | temp;
Serial.println(bitBuf[i]);
}
}
counter = 0;
}
void incrementBit() {
if (++counter > total_pts)
{
counter = 0;
}
REG_PIOC_ODSR = bitBuf[counter];
}
This should compile if anyone would like to give it a try.
Another related question, once you disable write enabling a bit using OWDR command, can we still use that bit to write (output high/low) using digitalwrite command?
Good question. IIRC, digitalWrite() uses CODR/SODR, which do not require OWSR bits to be set. But it would definitely interfere with any other code trying to write ODSR directly.
You can work around it by clearing all the "interesting" bits with CODR and setting the relevant bits with SODR"
This would of course generate a (very short) glitch when all the output bits were 0, so it'd be better if there were an additional stobe signal. The glitch would be much less problematic than writing one bit at a time...
Or you could modify the OWSR as needed.
Once you're writing more than one register in PIOC in close proximity, you'd get a slight gain by using the structure form to access the individual registers (as per the recent discussion WRT SAMD21.)