Parallel Read

The EtherDue combines a super powerful 84MHz Cortex M3 with onboard ethernet connectivity. Dual Micro USB, a MicroSD card slot, switchmode power supply. 100% Arduino Due compatible. [Product Page]
Post Reply
wrswrsnz
Posts: 5
Joined: Mon Mar 09, 2015 7:24 am
Location: New Zealand
Contact:

Parallel Read

Post by wrswrsnz » Mon Mar 09, 2015 7:43 am

I'm looking to convert a Ethermega project over to use the EtherDue to gain native 32 bit support and massive extra speed. Doing 16 bit, 32 bit and float math on the Ethermega is very slow by comparisson.

My current design uses 3 high-speed 16 Bit ADC which are read in 16-bit parallel into Port A & C of the Ethermega (Pins 22-29 (PA0-PA7) and Pins 37-30 (PC0-PC7)). To get the actual 16 bit value v, you simply read Port A and add Port C * 256 to it (v = PINA + PINC * 256).

On the EtherDue these port pins appear to be scattered all over the device. This appears to make it impossible to read a simple port value or values and multiply and add. In fact it looks like a very complex set of reads, ands, shifts, multiplies and adds are required to build the 16 bit value from what are 2 simple 8 bit port reads on the Ethermega.

Am I missing something or is this a serious limitation of the EtherDue?

wrswrsnz
Posts: 5
Joined: Mon Mar 09, 2015 7:24 am
Location: New Zealand
Contact:

Re: Parallel Read

Post by wrswrsnz » Mon Mar 09, 2015 8:17 am

Here's the layout with the equivalent port mapping so it's clear what I mean:

PIN - Bit - EtherMega - EtherDue
22 -- 0 ---- PA0 --------- PB26
23 -- 1 ---- PA1 --------- PA14
24 -- 2 ---- PA2 --------- PA15
25 -- 3 ---- PA3 --------- PD0
26 -- 4 ---- PA4 --------- PD1
27 -- 5 ---- PA5 --------- PD2
28 -- 6 ---- PA6 --------- PD3
29 -- 7 ---- PA7 --------- PD6
37 -- 8 ---- PC0 --------- PC5
36 -- 9 ---- PC1 --------- PC4
35 -- 10 --- PC2 --------- PC3
34 -- 11 --- PC3 --------- PC2
33 -- 12 --- PC4 --------- PC1
32 -- 13 --- PC5 --------- PD10
31 -- 14 --- PC6 --------- PA7
30 -- 15 --- PC7 --------- PD9

Probably the easiest and terribly code inefficient way to do this is to do a if(digitalRead(x)) v = v + y; for each input pin.

eg
if(digitalRead(22)) v = 1; else v = 0;
if(digitalRead(23)) v = v + 2;
if(digitalRead(24)) v = v + 4;
|
if(digitalRead(37)) v = v + 256;
|
if(digitalRead(30)) v = v + 32768;

Seems a waste...
Surely there is a better way?

It's probably far more code efficient to access the ports directly as digitalRead is quite inefficient and slow, eg;
if(PINB & 67108864) v = 1; else v= 0;
if(PINA & 16384) v = v + 2;

angusgr
Freetronics Staff
Freetronics Staff
Posts: 853
Joined: Tue Apr 09, 2013 11:19 pm
Location: Melbourne, Australia
Contact:

Re: Parallel Read

Post by angusgr » Tue Mar 10, 2015 1:25 am

Hi wrswrsnz,

You're right that unfortunately these pins don't line up like on the AVR Mega. The Due designers did this, I'm fairly sure, because of the arrangement of pins on the microcontroller itself and keeping the PCB easy enough to route.

You can do use a loop to go through the various pins if they're in order on the Due pins:

Code: Select all

const byte start_pin = 22;
const byte number_pins = 16;
int adc_result = 0;
for(int i = 0; i < number_pins; i++) {
    if(digitalRead(start_pin+i))
      adc_result = adc_result + (1 << i);
}
// adc_result now holds the result
Assuming bit 0 (value 1) is on pin 22, and they go up from there. 1 << i as an expression means "the value 1 shifted left in binary i times", and each shift doubles the value.

If not then you can use a lookup table:

Code: Select all

const byte adc_pins = { 27, 26, 23, 22, 21, 20, etc, etc. }; /* 16 pins total */
int adc_result = 0;
for(int i = 0; i < 16; i++) {
    if(digitalRead(adc_pins[i])
      adc_result = adc_result + (1 << i);
}
// adc_result now holds the result
You're right that this is still slower than doing individual port reads, but I suspect it's still quite fast on the Due.


Angus

wrswrsnz
Posts: 5
Joined: Mon Mar 09, 2015 7:24 am
Location: New Zealand
Contact:

Re: Parallel Read

Post by wrswrsnz » Tue Mar 10, 2015 9:45 am

Thanks for the ideas. It would be interesting to do a comparrison to which is faster - the loop method or the bit-bang method 1 line at a time. For this application code space is not a problem - speed is.

I suspect I may also be able to use a semi-lined up solution. I'm going to have to make an interposing PCB between the PCB with the ADC and signal conditioning to the EtherDue because it's 5V IO (for the EtherMega) and I'll need to add in 3.3V-5V bidirectional level shifting on all the IO between boards. I may as well take the opportunity to re-route the orginal EtherMega pins 22-37 to align as best as possible with EtherDue ports with a section of sequential port pins.
eg;
EtherMega 22-29 (PINA) ----> EtherDue Pin 33-40 (PC1-PC8)
EtherMega 37-30 (PINC) ----> EtherDue Pin 51-44 (PC12-PC19)

Then the 16 bit value v is now:
v = ((PINC & 0x1FE) >> 1) + ((PINC & 0x7F800) >> 3)
2 fast direct port reads, 2 ands and 4 shifts - probably the most code efficient way to do it???

Thoughts?

wrswrsnz
Posts: 5
Joined: Mon Mar 09, 2015 7:24 am
Location: New Zealand
Contact:

Re: Parallel Read

Post by wrswrsnz » Tue Mar 10, 2015 9:52 am

The slightly slower reads won't be a problem at all anyway.

The extra time doing the reads will be nothing compared to the gains being able to do single cycle 32 bit math vs many many cycles for the same calculation on the Mega...
Also, the much faster clock speed will help.

At present I can only sample 3 channels and process true RMS voltage and current at 2kHz on the Mega. I can also only accurately go to the 5th harmonic...

With the Due I suspect I can easily sample at 10kHz+ (maybe even 20kHz) due to native 32 bit support. I'll also be able to calculate to 11th or 13th harmonic or maybe even higher.

Yup, it's a true RMS power monitor doing cycle-by-cycle true RMS power calculation and pushing directly into a PostgreSQL DB.

angusgr
Freetronics Staff
Freetronics Staff
Posts: 853
Joined: Tue Apr 09, 2013 11:19 pm
Location: Melbourne, Australia
Contact:

Re: Parallel Read

Post by angusgr » Wed Mar 11, 2015 5:17 am

wrswrsnz wrote: eg;
EtherMega 22-29 (PINA) ----> EtherDue Pin 33-40 (PC1-PC8)
EtherMega 37-30 (PINC) ----> EtherDue Pin 51-44 (PC12-PC19)

Then the 16 bit value v is now:
v = ((PINC & 0x1FE) >> 1) + ((PINC & 0x7F800) >> 3)
2 fast direct port reads, 2 ands and 4 shifts - probably the most code efficient way to do it???
That will definitely be faster (2 port reads instead of 16 function calls to make port reads.) Good solution!
wrswrsnz wrote: Yup, it's a true RMS power monitor doing cycle-by-cycle true RMS power calculation and pushing directly into a PostgreSQL DB.
Very interesting project! Please let us know how it turns out.


Angus

Post Reply