Pages: [1]   Go Down
Author Topic: Arduino Due digital read speed  (Read 1502 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Newbie
*
Karma: 0
Posts: 23
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hey guys,

I just bought an Arudino Due because I wanted the higher performance.  I was wondering though, is the Due capable of reading digital inputs at 15MHz?  I figure with a clock speed of 84MHz, it's gotta be able to read digital inputs at some respectable fraction of that.  I've tried looking for this information in the Arduino pages but didn't find anything that clearly answered this question

Thanks for reading
Logged

The Netherlands
Offline Offline
Full Member
***
Karma: 1
Posts: 139
MKDS hacker and Programmer
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hey guys,

I just bought an Arudino Due because I wanted the higher performance.  I was wondering though, is the Due capable of reading digital inputs at 15MHz?  I figure with a clock speed of 84MHz, it's gotta be able to read digital inputs at some respectable fraction of that.  I've tried looking for this information in the Arduino pages but didn't find anything that clearly answered this question

Thanks for reading

It should be possible. But I think you'll have to use a bit of assembly for it. And it is not possible to time it correctly. You can also use DMA, but that is probably too fast.
Logged

Subscribe my youtube channel: http://www.youtube.com/user/MKDS3

Offline Offline
Newbie
*
Karma: 0
Posts: 23
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I see.  Do you know what the minimum time between digital reads would be then, without having to use assembly?  As in, could I read the same digital input pin at 3MHz?  15MHz is probably a pipe dream considering all the other processes the arduino must handle
Logged

Germany
Offline Offline
Newbie
*
Karma: 0
Posts: 29
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi,
take a look at my solution at http://forum.arduino.cc/index.php?PHPSESSID=aj4qa33h62ub4vcbkie2qef1t1&topic=247158.0.(Reply #9).

There's a schemata for input and output within about 0.4 µs. Surely you can make it a little bit faster. And instead of 8 Bit size you can - with some limitations - use 16 ore more bits without significant longer time because the bus has a size of 4 bytes.
Tom
« Last Edit: July 03, 2014, 01:10:07 am by Measureino » Logged

The Netherlands
Offline Offline
Full Member
***
Karma: 1
Posts: 139
MKDS hacker and Programmer
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I see.  Do you know what the minimum time between digital reads would be then, without having to use assembly?  As in, could I read the same digital input pin at 3MHz?  15MHz is probably a pipe dream considering all the other processes the arduino must handle
As far as I know should the pio be able to read at 21MHz max. The problem is that you can try to read the inputs as fast as possible, but you can't control the speed. That's a big problem. DMA had been great if you could control it's speed.

You can use noInterrupts(); to disable interrupts. You need to use loop unrolling and assembly if you want to go that fast using the cpu. The DMA is (depending on the channel used) probably somewhere between 84 and 21 MHz or something. But I am nit sure.
Logged

Subscribe my youtube channel: http://www.youtube.com/user/MKDS3

0
Offline Offline
Shannon Member
****
Karma: 207
Posts: 12189
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Its quite involved setting up DMA, re-reading the datasheet a few times in the relevant
sections is a start.  However it should be able to do fancy stuff like this AFAICT.

Assembler is not needed, all IO is memory mapped so C is fine for all the low
level stuff on the ARM which is nice!  Its worth getting to look at the relevant
files in libsam for each piece of hardware to get a feel for things - you have all
the source code after all!
Logged

[ I won't respond to messages, use the forum please ]

Offline Offline
Newbie
*
Karma: 0
Posts: 23
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

After reading the huge manual as suggested I found out a solution. In short:

//read digPins  25,26,27,28,14,15,29,11 (PortD D0..D7)
//because of digPin 14 and 15 Serial3 can't be used further
//write 33,34,35,36,37,38,39,40 (PortC C1...C8)
//First define  above input and output Pins ins Setup()
// Then
    void loop() {
       input_data_8= REG_PIOD_PDSR &0xFF;  //read PortD (=input Pins)
       output_data=0x3B  //example only
       PIOC->PIO_OWDR = 0xFFFFFFFF;    // (D)isable all C_Bits for write
       PIOC->PIO_OWER = 0x01FE;             // (E)nable C1..C8 for write
       PIOC->PIO_ODSR=0x3B<<1;               //write 0x3B to output Pins
     }

This runs in less than 0.4 mikroseconds!

Hey, thanks for the replies guys.  Measureino - this is pretty much port manipulation for the Arduino Due, right?  I recently started looking into all that (kind of a n00b with all arduino) to accomplish my fast input read speed.  Seems like this worked for you huh?  Very promising, I'll check it out and see if I can't do the same thing!  I want to modify it to read 10 bits from two 8-bit registers, and write to one pin in the second 8-bit register.  Does that seem feasible to you?  Just sanity checking haha
Logged

The Netherlands
Offline Offline
Full Member
***
Karma: 1
Posts: 139
MKDS hacker and Programmer
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Its quite involved setting up DMA, re-reading the datasheet a few times in the relevant
sections is a start.  However it should be able to do fancy stuff like this AFAICT.

Assembler is not needed, all IO is memory mapped so C is fine for all the low
level stuff on the ARM which is nice!  Its worth getting to look at the relevant
files in libsam for each piece of hardware to get a feel for things - you have all
the source code after all!
I made some code to make using DMA much simpler. I can post it here if you want.
Logged

Subscribe my youtube channel: http://www.youtube.com/user/MKDS3

Offline Offline
Newbie
*
Karma: 0
Posts: 23
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset


I made some code to make using DMA much simpler. I can post it here if you want.

That'd be awesome!  I'm totally new to the concept of DMA - would you care to provide a brief explanation on how your DMA library works?

Would be much appreciated!
Logged

Germany
Offline Offline
Newbie
*
Karma: 0
Posts: 29
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

@FingerBuckley Reply #6
You should use PIOC if you want to read more than 8 Bit. For example:
C1...C8, C19...C12  means dig. Pins 33...40,44...51
There's no time difference when reading 8 or 16 bit, as IO always reads 32 bits. The only difference lies in some extra bit manipulations. You have to do input >> 1 to get low part and input >>12 to get high part of input.
Take attention to the different order C19,C18...C12  but dig Pin 44...51
I think it's a risk to use i.e. C for input and(!) output or you should be very careful to avoid reading from an output pin or writing to an input pin.
Logged

The Netherlands
Offline Offline
Full Member
***
Karma: 1
Posts: 139
MKDS hacker and Programmer
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset


I made some code to make using DMA much simpler. I can post it here if you want.

That'd be awesome!  I'm totally new to the concept of DMA - would you care to provide a brief explanation on how your DMA library works?

Would be much appreciated!

dma.cpp:
Code:
#include <Arduino.h>
#include "dma.h"
void DMA_Init()
{
  REG_PMC_PCER1 = 1<<7;
  REG_DMAC_WPMR = DMAC_WPMR_WPKEY(0x444D4143);//Disable write protection
  REG_DMAC_EN = 1;//Enable
  REG_DMAC_GCFG = 0x00000010;
}
void DMA_SetupTransfer(int channel, void* src, int src_length, int src_width, int src_incr, void* dst, int dst_width, int dst_incr, int flow_control)
{
  REG_DMAC_EBCIDR |= 0x10101 << channel;//Disable Interupts
  *((uint32_t*)(0x400C403C + 0x28 * channel)) = (uint32_t)src;
  *((uint32_t*)(0x400C4040 + 0x28 * channel)) = (uint32_t)dst;
  *((uint32_t*)(0x400C4044 + 0x28 * channel)) = 0;
  *((uint32_t*)(0x400C4048 + 0x28 * channel)) = ((src_length >> src_width) & 0xFFFF) | (6 << 16) | (0 << 20) | ((src_width & 0x3) << 24) | ((dst_width & 0x3) << 28);
  *((uint32_t*)(0x400C404C + 0x28 * channel)) = (0 << 16) | (0 << 20) | ((flow_control & 0x3) << 21) | ((src_incr & 0x3) << 24) | ((dst_incr & 0x3) << 28);
  *((uint32_t*)(0x400C4050 + 0x28 * channel)) = 0 | (0 << 4) | (1 << 9) | (1 << 13) | (1 << 16) | (0 << 20) | (0 << 21) | (0 << 22) | (0 << 24) | (1 << 28);
}

void DMA_SetupMultiTransfer(int channel, dma_transfer_descriptor_t* desc)
{
  REG_DMAC_EBCIDR |= 0x10101 << channel;//Disable Interupts
  *((uint32_t*)(0x400C403C + 0x28 * channel)) = 0;
  *((uint32_t*)(0x400C4040 + 0x28 * channel)) = 0;
  *((uint32_t*)(0x400C4044 + 0x28 * channel)) = (uint32_t)desc;
  *((uint32_t*)(0x400C4048 + 0x28 * channel)) = 0;
  *((uint32_t*)(0x400C404C + 0x28 * channel)) = 0;
  *((uint32_t*)(0x400C4050 + 0x28 * channel)) = 0 | (0 << 4) | (1 << 9) | (1 << 13) | (0 << 16) | (0 << 20) | (0 << 21) | (0 << 22) | (0 << 24) | (1 << 28);
}

void DMA_EnableChannel(int channel)
{
  REG_DMAC_CHER |= 1 << channel;
}

void DMA_DisableChannel(int channel)
{
  REG_DMAC_CHDR |= 1 << channel;
}

int DMA_IsChannelEnabled(int channel)
{
  return (REG_DMAC_CHSR >> channel) & 1;
}

dma.h:
Code:
#ifndef __DMA_H__
#define __DMA_H__
#include <Arduino.h>
#define DMA_WIDTH_BYTE  0
#define DMA_WIDTH_HALF_WORD  1
#define DMA_WIDTH_WORD  2

#define DMA_FC_MEM2MEM  0
#define DMA_FC_MEM2PER  1
#define DMA_FC_PER2MEM  2
#define DMA_FC_PER2PER  2

#define DMA_INCR_INCREMENTING  0
#define DMA_INCR_DECREMENTING  1
#define DMA_INCR_FIXED  2

typedef struct {
  uint32_t ul_source_addr;      /**< Source buffer address */
  uint32_t ul_destination_addr; /**< Destination buffer address */
  uint32_t ul_ctrlA;            /**< Control A register settings */
  uint32_t ul_ctrlB;            /**< Control B register settings */
  uint32_t ul_descriptor_addr;  /**< Next descriptor address */
}
dma_transfer_descriptor_t;

void DMA_Init();
void DMA_SetupTransfer(int channel, void* src, int src_length, int src_width, int src_incr, void* dst, int dst_width, int dst_incr, int flow_control);
void DMA_SetupMultiTransfer(int channel, dma_transfer_descriptor_t* desc);
void DMA_EnableChannel(int channel);
void DMA_DisableChannel(int channel);
int DMA_IsChannelEnabled(int channel);
#endif

This reads 512 samples from D0-D7, and sends them over the serial port (I have not tested, but I think it should work):
Code:
#include "dma.h"
byte result[512];
void setup()
{
    Serial.begin(9600);
    pinMode(25, INPUT);//D0
    pinMode(26, INPUT);//D1
    pinMode(27, INPUT);//D2
    pinMode(28, INPUT);//D3
    pinMode(14, INPUT);//D4
    pinMode(15, INPUT);//D5
    pinMode(29, INPUT);//D6
    pinMode(11, INPUT);//D7
    DMA_Init();
    DMA_SetupTransfer(1, (void*)&REG_PIOD_PDSR, 512, DMA_WIDTH_BYTE, DMA_INCR_FIXED, &result[0], DMA_WIDTH_BYTE, DMA_INCR_INCREMENTING, DMA_FC_MEM2MEM);
    DMA_EnableChannel(1);
    while(DMA_IsChannelEnabled(1));
    for(int i = 0; i < 512; i++)
   {
      Serial.println(result[i], HEX);
   }
}

...
I think it's a risk to use i.e. C for input and(!) output or you should be very careful to avoid reading from an output pin or writing to an input pin.
That's no problem. The pins are written using a different register than that is used for reading.
Logged

Subscribe my youtube channel: http://www.youtube.com/user/MKDS3

Offline Offline
Newbie
*
Karma: 0
Posts: 23
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

@Measureino - Smart idea!  I changed everything to port C, and instead of doing bit manipulations during the sampling process, I am instead doing it after I've collected the number of samples I care about.  So, this code here:

Code:
for(count=0;count<N;count++){
    //sample[count] = get_ADC_2::get_ADC();  //"can't convert get_ADC_2 to "unsigned int" wtf
    PIOC->PIO_ODSR = 0x0;       // write 0 to CLK line (Analog input 11 on Arduino Due used as CLK for ADC)
    //delay(.00015);
    PIOC->PIO_ODSR = 1<<15;  // write 1 to CLK line (Applies 3.3V to ADC)
    c[count] = REG_PIOC_PDSR;
  }

is what i'm working with.  This gives me ~3.4MHz sampling rate max.  seems kind of slow, eh?

@Gericom- Thanks!!  Any idea how fast it can sample?
« Last Edit: July 05, 2014, 09:49:35 pm by FingerBuckley » Logged

The Netherlands
Offline Offline
Full Member
***
Karma: 1
Posts: 139
MKDS hacker and Programmer
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

@Measureino - Smart idea!  I changed everything to port C, and instead of doing bit manipulations during the sampling process, I am instead doing it after I've collected the number of samples I care about.  So, this code here:

Code:
for(count=0;count<N;count++){
    //sample[count] = get_ADC_2::get_ADC();  //"can't convert get_ADC_2 to "unsigned int" wtf
    PIOC->PIO_ODSR = 0x0;       // write 0 to CLK line (Analog input 11 on Arduino Due used as CLK for ADC)
    //delay(.00015);
    PIOC->PIO_ODSR = 1<<15;  // write 1 to CLK line (Applies 3.3V to ADC)
    c[count] = REG_PIOC_PDSR;
  }

is what i'm working with.  This gives me ~3.4MHz sampling rate max.  seems kind of slow, eh?

@Gericom- Thanks!!  Any idea how fast it can sample?
I have no idea. As I said, you can't control the DMA speed, so you should just try.

Also, you can better use PIOD, since D0-D10 are all available on the due, so you don't have to shift.

Here is that bit of code in ASM (also, not tested):
Code:
noInterrupts();
asm volatile("push r0-r5");
asm volatile("ldr r0,=N");//Change N to the amount of samples you want
asm volatile("mov r1, #0x8000");
asm volatile("ldr r2,=0x400E1238");//REG_PIOC_ODSR
asm volatile("ldr r3,=c");//if you define c as an array, this will be it's address
asm volatile("add r0, r3, lsl #2");//I assume c is a 32 bit array?
asm volatile("mov r4, #0");
asm volatile("1:");
asm volatile("str r4, [r2]");
asm volatile("str r1, [r2]");
asm volatile("ldr r5, [r2, #4]");
asm volatile("str r5, [r3], #4");//I assume c is a 32 bit array?
asm volatile("cmp r0, r3");
asm volatile("bne 1b");
asm volatile("pop r0-r5");
interrupts();
Logged

Subscribe my youtube channel: http://www.youtube.com/user/MKDS3

Pages: [1]   Go Up
Jump to: