Go Down

Topic: speed of analogRead (Read 83805 times) previous topic - next topic

adrianka

#60
Jun 09, 2018, 05:10 pm Last Edit: Jun 09, 2018, 05:36 pm by adrianka
So is speed critical if I just want to find out what lets my arduino crash?
I don't mind if some measurements get tossed away.

Why can't I switch from SerialUSB to Serial if I just want to debug?

MorganS

What is wrong with:
Code: [Select]
void loop() {
  SerialUSB.print(micros());
  SerialUSB.print(',');
  SerialUSB.println(analogRead(A0));
}


?

Does this run fast enough for you? How fast do you actually need for your application?
"The problem is in the code you didn't post."

adrianka

#62
Jun 13, 2018, 03:36 pm Last Edit: Jun 13, 2018, 04:04 pm by adrianka
What is wrong with:
Code: [Select]
void loop() {
  SerialUSB.print(micros());
  SerialUSB.print(',');
  SerialUSB.println(analogRead(A0));
}


?

Does this run fast enough for you? How fast do you actually need for your application?
Hi MorganS,

thanks for your reply!
Unfortunately, this is too slow. I'd be happy to get a sample rate of 1 Msps, 250ksps being the absolute minimum.

The good thing is, I am reading values by analogRead. I still couldn't figure out why SerialUSB stopped working.
Any ideas?

Any help is very much appreciated!

Now, I tried this under a windows machine, just to test the serial connection:

Code: [Select]
#!/usr/bin/python
import serial
# import syslog
import time

#The following line is for serial over GPIO
usb_ports = ('COM3')
s = serial.Serial(usb_ports)

print("starting while")
while 1:
    try:
        # Serial read section
        print("opening file")
        file = open("debugging.txt", "w")
        print("reading line")
        msg = str(s.readline())
        print("writing to file")
        file.write(msg + "\n")
        print(msg + "\n")
        print("closing file")
        file.close()
    except KeyboardInterrupt:
        file = open("debugging.txt", "w")
        print("closing file and exiting program")
        file.close()
        exit()


Arduino code:
Code: [Select]
void setup() {
  SerialUSB.begin(0);
  while(!SerialUSB);
}

void loop() {
  SerialUSB.print(micros());
  SerialUSB.print(',');
  SerialUSB.println(analogRead(A0));
}


result in the shell:
Code: [Select]
starting while
opening file
reading line



Again: The code that stimmer posted delivers no output anymore. It says voltage constantly 0, rate 0. (At the beginning it worked, now I'm looking for a solution to this problem but can't find it.)

MorganS

Quote
Unfortunately, this is too slow.
How much slower? I'm not going to do the measurement myself.

If it's close then perhaps there's some simple things that can be done to increase the speed.

If it's a long way away then we have make major changes, such as maybe not using any Arduino.

Quote
Again: The code that stimmer posted delivers no output anymore. It says voltage constantly 0, rate 0. (At the beginning it worked, now I'm looking for a solution to this problem but can't find it.)
What did you change?
"The problem is in the code you didn't post."

adrianka

Thanks for the help. The problem was in the patches posted on github that I haven't read at that point.


Code: [Select]
// patch 1.1 start
  while((obufn + 1)%4==bufn);// wait for buffer to be full
  // patch 1.1 end


Code: [Select]
  // patch 1.2 start
  //bufn=obufn=1;
  bufn=1;
  obufn=0;
  // patch 1.2 end


and

Code: [Select]
   // patch 2 start
   //ADC->ADC_RNPR=(uint32_t)buf[bufn];
   ADC->ADC_RNPR=(uint32_t)buf[(bufn+1)&3];
   // patch 2 end


it's running fine now. apart from the digital write. Which has no effect.

Code: [Select]
#undef HID_ENABLED

// Arduino Due ADC->DMA->USB 1MSPS
// by stimmer
// from http://forum.arduino.cc/index.php?topic=137635.msg1136315#msg1136315
// Input: Analog in A0
// Output: Raw stream of uint16_t in range 0-4095 on Native USB Serial/ACM

// on linux, to stop the OS cooking your data:
// stty -F /dev/ttyACM0 raw -iexten -echo -echoe -echok -echoctl -echoke -onlcr

// source: https://gist.github.com/pklaus/5921022
// applied patch 1 from: http://forum.arduino.cc/index.php?topic=137635.msg2526475#msg2526475
// applied patch 2 from:  https://gist.github.com/pklaus/5921022 comment on Apr 2, 2017 from borisbarbour

volatile int bufn,obufn;
uint16_t buf[4][256];    // 4 buffers of 256 readings

void ADC_Handler(){     // move DMA pointers to next buffer
  int f=ADC->ADC_ISR;
  if (f&(1<<27)){
   bufn=(bufn+1)&3;
   // patch 2 start
   //ADC->ADC_RNPR=(uint32_t)buf[bufn];
   ADC->ADC_RNPR=(uint32_t)buf[(bufn+1)&3];
   // patch 2 end
   ADC->ADC_RNCR=256;
  }
}

void setup() {
 
  SerialUSB.begin(0);
  while(!SerialUSB);
 
 
  pmc_enable_periph_clk(ID_ADC);
  adc_init(ADC, SystemCoreClock, ADC_FREQ_MAX, ADC_STARTUP_FAST);
  ADC->ADC_MR |=0x80; // free running

  ADC->ADC_CHER=0x80;

  NVIC_EnableIRQ(ADC_IRQn);
  ADC->ADC_IDR=~(1<<27);
  ADC->ADC_IER=1<<27;
  ADC->ADC_RPR=(uint32_t)buf[0];   // DMA buffer
  ADC->ADC_RCR=256;
  ADC->ADC_RNPR=(uint32_t)buf[1]; // next DMA buffer
  ADC->ADC_RNCR=256;
  // patch 1.2 start
  //bufn=obufn=1;
  bufn=1;
  obufn=0;
  // patch 1.2 end
  ADC->ADC_PTCR=1;
  ADC->ADC_CR=2;

  // this has no effect:
  pinMode(52, OUTPUT);
  digitalWrite(52, HIGH);
}

void loop(){
  // patch 1.1 start
  while((obufn + 1)%4==bufn);// wait for buffer to be full
  // patch 1.1 end
  SerialUSB.write((uint8_t *)buf[obufn],512); // send it - 512 bytes = 256 uint16_t
  obufn=(obufn+1)&3;   
}


does somebody know why or can offer any help?

It is highly appreciated.

Thanks in advance and kind wishes!

adrianka

Arduino has been waiting for serial connection...  :smiley-roll-sweat:

adrianka

#66
Jun 29, 2018, 02:07 pm Last Edit: Jun 29, 2018, 02:26 pm by adrianka
Hello!

So now I want to read from two channels. I this change to the Arduino code only:

Code: [Select]
ADC->ADC_CHER=0xC0; // 0x80 for pin a0, 0xC0 for pin a0 and a1

It is running and sending data to the PC but I can't make any sense of the data.
After reading this thread I thought data is being written to the buffer alternately. Is this correct? I can't seem to get the correct data out of it.

My python code is this:

Code: [Select]
#!/usr/bin/env python

# from http://forum.arduino.cc/index.php?topic=137635.msg1270996#msg1270996

import pyqtgraph as pg
import time, threading, sys
import serial
import numpy as np

# script takes 2 arguments.
# Argument 1: duration; determines for how long values shall be gathered
# value must be in seconds
# Argument 2: procedure; determines which procedure is going to be called and to which file data is saved
# possible procedures are:
# - startup
# - idle
# - readseq
# - readrnd
# - writeseq
# - writernd

# voltage references
aref1 = 1.0 # for 5V measurements
aref2 = 2.5 # for 12V measurements
arefDue = 3.3

resolution = 12 #resolution in bits

class SerialReader(threading.Thread):
    """ Defines a thread for reading and buffering serial data.
    By default, about 5MSamples are stored in the buffer.
    Data can be retrieved from the buffer by calling get(N)"""

    #dataStorage = list()

    def __init__(self, port, chunkSize=1024, chunks=5000):
        threading.Thread.__init__(self)
        # circular buffer for storing serial data until it is
        # fetched by the GUI
        self.buffer = np.zeros(chunks*chunkSize, dtype=np.uint16)
       
        self.chunks = chunks        # number of chunks to store in the buffer
        self.chunkSize = chunkSize  # size of a single chunk (items, not bytes)
        self.ptr = 0                # pointer to most (recently collected buffer index) + 1
        self.port = port            # serial port handle
        self.sps = 0.0              # holds the average sample acquisition rate
        self.exitFlag = False
        self.exitMutex = threading.Lock()
        self.dataMutex = threading.Lock()
        #self.countLoop = 0
        self.dataStorage = list()
       
       

    def run(self):
        exitMutex = self.exitMutex
        dataMutex = self.dataMutex
        buffer = self.buffer
        port = self.port
        count = 0
        sps = None
        lastUpdate = pg.ptime.time()
       
        while True:
            # see whether an exit was requested
            with exitMutex:
                if self.exitFlag:
                    break
           
            # read one full chunk from the serial port
            data = port.read(self.chunkSize*2)
            # convert data to 16bit int numpy array
            data = np.fromstring(data, dtype=np.uint16)
           
            # write data to file
            data = data.astype(np.float32) * (arefDue / 2**resolution)
           
            self.dataStorage.extend(data)

            # keep track of the acquisition rate in samples-per-second
            count += self.chunkSize
            now = pg.ptime.time()
            dt = now-lastUpdate
            if dt > 1.0:
                # sps is an exponential average of the running sample rate measurement
                if sps is None:
                    sps = count / dt
                else:
                    sps = sps * 0.9 + (count / dt) * 0.1
                count = 0
                lastUpdate = now
               
            # write the new chunk into the circular buffer
            # and update the buffer pointer
            with dataMutex:
                buffer[self.ptr:self.ptr+self.chunkSize] = data
                self.ptr = (self.ptr + self.chunkSize) % buffer.shape[0]
                if sps is not None:
                    self.sps = sps

    def getData(self):
        return self.dataStorage

    def exit(self):
        """ Instruct the serial thread to exit."""
        with self.exitMutex:
            self.exitFlag = True

# Calling update() will request a copy of the most recently-acquired
# samples and plot them.
def update():
   
    if timer2.elapsed() >= int(sys.argv[1])*1000:
        timer.stop()
        dataList = thread.getData()
        thread.exit()
        prepfile(dataList)
        exit()

# Get handle to serial port
# (your port string may vary; windows users need 'COMn')

def prepfile(dataList):
    title = sys.argv[2]
    file0 = open('dual_' + title + '_pin0' + ".txt", "w")
    file1 = open('dual_' + title + '_pin1' + ".txt", 'w')
    i = 0
    while(i<len(dataList)):
        file0.write(str(dataList[i]) + "\n")
        i+=1
        file1.write(str(dataList[i]) + "\n")
        i+=1
    file0.close()
    file1.close()

print("running " + sys.argv[2] + " for " + sys.argv[1] + " seconds")

usb_ports = ('/dev/ttyACM0', '/dev/ttyACM1')
for usb in usb_ports:
    try:
        s = serial.Serial(usb)
        break
    except serial.SerialException:
        print('Arduino not found at ' + usb)
        pass

# check if number of arguments matches
if len(sys.argv) != 3:
    print("Wrong number of arguments. Make sure to pass \'duration\' and \'procedure\'")
    exit()

if not sys.argv[1].isdigit():
    print("Wrong duration. Please pass number of seconds.")
    exit()
   
# Create the GUI
app = pg.mkQApp()


# Set up a timer with 0 interval so Qt will call update()
# as rapidly as it can handle.

# Create thread to read and buffer serial data.

thread = SerialReader(s)
thread.start()
timer2 = pg.QtCore.QElapsedTimer()
timer2.start()

timer = pg.QtCore.QTimer()
timer.timeout.connect(update)
timer.start(0)

# Start Qt event loop.   
if sys.flags.interactive == 0:
    app.exec_()


I don't plot the data with the script but write it to a file directly.

I'm having a sinus with 228kHz that goes into the Arduino. When I'm only reading from pinA0 python receives the sinus correctly. However, when I use pinA0 and A1 the sinus is gone.

I am referring to this post:
I think that to use a second channel, all you have to do is enable it. You are right, the samples end up alternated in the buffers.
Where is my mistake?

ard_newbie


1 MHz is the maximum sampling ADC frequency (with ADC_PRESCAL(1)) in Free Running Mode (and with a PDC DMA). when you enable n channels, the ADC peripheral multiplies the selected frequency by n to be able to sample each channel at the selected frequency. For 1 channel, it's OK, BUT for more than 1 channel, the ADC peripheral can't sample at 2 MHz (for 2 channels), 3 MHz (for 3 channels), .... You see the issue ?  

Conclusion: you can sample several channels (with ADC_CHER) and simply select the frequency you want for each channel (with a Timer Counter or a PWM Event Line) as long as the selected frequency times the number of channels do not exceed 1 MHz.


Sadoo2010

Code: [Select]
[quote author=PakARD link=msg=1138713 date=1362158574]

if (channels_2)
  {
    ADC->ADC_MR |= 0x1280; // these lines set free running mode on adc 7 (pin A0) and adc 6(pin A1) PRESCAL at 50kHz
  }
  else
  {
    ADC->ADC_MR |= 0x2680; // these lines set free running mode on adc 7 (pin A0) PRESCAL at 50kHz
  }





[/quote]



HI
I'm confused

 Why do you say 50 Hz FOR PRESCAL:
According to Data Sheet that says:
ADCClock = MCK / ( (PRESCAL+1) * 2 )
THEN assuming 0X26=0b00100110 THEN  ADCCLOCK= 84000000 / ((32+6)+1)*2)=1,076,923HZ
Even with the assumption that we put 26 in the equation:
ADCClock = MCK / ( (PRESCAL+1) * 2 )
THEN ADCCLOCK= 84000000 / ((26+1)*2)=1,555,555hz
not 50 hz!!!!!!

Please guide me how to calculate the ADC CLOCK
I need this number
Thank you,
sadoo2010@gmail.com

maria90

Thanks for this code! I've got my Due pulling 1MS/s as well. I wrote a very simple python script to display data as it is fetched from the serial port. It buffers the data in a separate thread, then plots as quickly as it can (it downsamples 1 second of data to 10k points and plots in realtime). It can also display the power spectrum of the signal by selecting Plot Options->Transform->FFT from the context menu.  Requirements are python, pyserial (http://pyserial.sourceforge.net/), and pyqtgraph (http://pyqtgraph.org/). I'd like to develop this code further to provide display-triggering and controls for configuring the sample rate and channels.

Code: [Select]

import pyqtgraph as pg
import time, threading, sys
import serial
import numpy as np


class SerialReader(threading.Thread):
    """ Defines a thread for reading and buffering serial data.
    By default, about 5MSamples are stored in the buffer.
    Data can be retrieved from the buffer by calling get(N)"""
    def __init__(self, port, chunkSize=1024, chunks=5000):
        threading.Thread.__init__(self)
        # circular buffer for storing serial data until it is
        # fetched by the GUI
        self.buffer = np.zeros(chunks*chunkSize, dtype=np.uint16)
       
        self.chunks = chunks        # number of chunks to store in the buffer
        self.chunkSize = chunkSize  # size of a single chunk (items, not bytes)
        self.ptr = 0                # pointer to most (recently collected buffer index) + 1
        self.port = port            # serial port handle
        self.sps = 0.0              # holds the average sample acquisition rate
        self.exitFlag = False
        self.exitMutex = threading.Lock()
        self.dataMutex = threading.Lock()
       
       
    def run(self):
        exitMutex = self.exitMutex
        dataMutex = self.dataMutex
        buffer = self.buffer
        port = self.port
        count = 0
        sps = None
        lastUpdate = pg.ptime.time()
       
        while True:
            # see whether an exit was requested
            with exitMutex:
                if self.exitFlag:
                    break
           
            # read one full chunk from the serial port
            data = port.read(self.chunkSize*2)
            # convert data to 16bit int numpy array
            data = np.fromstring(data, dtype=np.uint16)
           
            # keep track of the acquisition rate in samples-per-second
            count += self.chunkSize
            now = pg.ptime.time()
            dt = now-lastUpdate
            if dt > 1.0:
                # sps is an exponential average of the running sample rate measurement
                if sps is None:
                    sps = count / dt
                else:
                    sps = sps * 0.9 + (count / dt) * 0.1
                count = 0
                lastUpdate = now
               
            # write the new chunk into the circular buffer
            # and update the buffer pointer
            with dataMutex:
                buffer[self.ptr:self.ptr+self.chunkSize] = data
                self.ptr = (self.ptr + self.chunkSize) % buffer.shape[0]
                if sps is not None:
                    self.sps = sps
               
               
    def get(self, num, downsample=1):
        """ Return a tuple (time_values, voltage_values, rate)
          - voltage_values will contain the *num* most recently-collected samples
            as a 32bit float array.
          - time_values assumes samples are collected at 1MS/s
          - rate is the running average sample rate.
        If *downsample* is > 1, then the number of values returned will be
        reduced by averaging that number of consecutive samples together. In
        this case, the voltage array will be returned as 32bit float.
        """
        with self.dataMutex:  # lock the buffer and copy the requested data out
            ptr = self.ptr
            if ptr-num < 0:
                data = np.empty(num, dtype=np.uint16)
                data[:num-ptr] = self.buffer[ptr-num:]
                data[num-ptr:] = self.buffer[:ptr]
            else:
                data = self.buffer[self.ptr-num:self.ptr].copy()
            rate = self.sps
       
        # Convert array to float and rescale to voltage.
        # Assume 3.3V / 12bits
        # (we need calibration data to do a better job on this)
        data = data.astype(np.float32) * (3.3 / 2**12)
        if downsample > 1:  # if downsampling is requested, average N samples together
            data = data.reshape(num/downsample,downsample).mean(axis=1)
            num = data.shape[0]
            return np.linspace(0, (num-1)*1e-6*downsample, num), data, rate
        else:
            return np.linspace(0, (num-1)*1e-6, num), data, rate
   
    def exit(self):
        """ Instruct the serial thread to exit."""
        with self.exitMutex:
            self.exitFlag = True


# Get handle to serial port
# (your port string may vary; windows users need 'COMn')
s = serial.Serial('/dev/ttyACM0')

# Create the GUI
app = pg.mkQApp()
plt = pg.plot()
plt.setLabels(left=('ADC Signal', 'V'), bottom=('Time', 's'))
plt.setYRange(0.0, 3.3)
           
# Create thread to read and buffer serial data.
thread = SerialReader(s)
thread.start()

# Calling update() will request a copy of the most recently-acquired
# samples and plot them.
def update():
    global plt, thread
    t,v,r = thread.get(1000*1024, downsample=100)
    plt.plot(t, v, clear=True)
    plt.setTitle('Sample Rate: %0.2f'%r)
   
    if not plt.isVisible():
        thread.exit()
        timer.stop()

# Set up a timer with 0 interval so Qt will call update()
# as rapidly as it can handle.
timer = pg.QtCore.QTimer()
timer.timeout.connect(update)
timer.start(0)

# Start Qt event loop.   
if sys.flags.interactive == 0:
    app.exec_()


data = data.reshape(num/downsample,downsample).mean(axis=1)
            num = data.shape[0]
"float object cannot be interpreted as an integer "
I'm getting this error.
does anyone know why?

Go Up