5 years, 10 months ago.

How to Use maximum Sampling rate of ADC in mbed LPC1768.

Hello, Thank you very much in advance. I'm trying to read my analog signal (it usually has abrupt changes and peaks) and store it in the buffer around 3000 samples when it exceeds my threshold value. After that, I'm trying to send the data to my PC using printf outside the loop.

Here is the problem when I read the time interval between samples it is around 22us i.e 45.5kHz sampling rate but in the user manual, it was mentioned ADC can go up to 200kHz ...Is there any way to increase the sampling rate as I don't need to miss any peaks between these samples. It will be very helpful if u have any idea to resolve that problem and I have attached my code below.

 #include <FastAnalogIn.h>
#include <mbed.h>
 
 #define NUM_SAMPLES 3000
AnalogIn ain0(p20);
DigitalOut sysled(LED1); 
DigitalOut sysled1(LED2);
DigitalOut sysled2(LED3);
Serial pc(USBTX,USBRX); 
float interval,final;
 Timer t1;
 
int main()
{       
        //Say the System is Ready to Run
         sysled = 1;
         float buffer[NUM_SAMPLES],aout[1],buffer1[NUM_SAMPLES];
         
         unsigned int  j;
        
         pc.baud(115200);
       
         t1.start(); //run the timer
        
         pc.printf("apply shock to measure");
         while(1)
         {
                       
                      aout[0]=ain0.read();
                      
         //storing into buffer            
          if (aout[0]>0.5)                          //value that is scaled  to 3.3*my therhold value
          { 
                          sysled1 =1;
                          
                         while(j<3000)
                         
                         {
                             interval=t1.read_us();    //get time interval between two sets of measurements
                             t1.reset();               //reading next intrval 
                             buffer[j]=ain0.read();
                             buffer1[j]=interval;
                             j=j+1;
                          }
                    
          }                                       
        
       //sending the data to my PC
        if(j>0&&j<3001)
        {
                                        j--;
                                        sysled2 = 1;
                                        final= ((buffer[j]*3.3)-1.625)/0.002;                //getting the original shock value                                      
                                        pc.printf("%d %1.3f %1.3f \n\r",j,final,buffer1[j]);                                     
                                      
         }       
                              
                sysled2=0;
        
    }    
        
}        

Regards, Ranga.

2 Answers

5 years, 9 months ago.

Move as much processing as possible out of the loop.

The first thing to do would be use ain0.read_u16() rather than ain0.read() and change buffer[] to be of type uint16_t rather than float. The read function reads a uint16 from the hardware (* see below) and then does a floating point division to convert it to a number between 0 and 1. The LPC 1768 doesn't have floating point hardware which means that calculation is slow. If you use the raw 16 bit value then you both gain a speed up and halve the amount of memory needed to store the data. If you need the result as a float then divide by 65535.0f in your output loop where speed isn't critical.

Secondly rather than resetting your timer each time around the loop just read the value out of it, if you want the time difference then calculate that when outputting not during the time critical part. Also don't store the time which is an int32_t in a float array, you will slow things down and introduce rounding errors, buffer1[] should be of type int32_t.

If you only care about average sample rate then you could simplify further, only measure the time taken for all 3000 samples rather than for each sample.

You still won't reach the value given in the datasheet, to do that you'd probably have to bypass a lot of the mbed library and talk directly to the CPU registers in order to get the speed up. But hopefully these simple changes will get things fast enough without adding significant complexity.

(* Actually the read_u16() reads a 12 bit (or is it 10 bit on the LPC1768?) value from the ADC since it's not a 16 bit ADC. It then shifts this left and pads the least significant bits with a pattern to ensure equal spacing across the 16 bit range. This way all the different ADCs on different processors end up giving a 16 bit value making code more compatible cross platform. Fortunately bit manipulation is fast and so this doesn't introduce a significant time penalty.)

I really appreciate your help and I have tried to move out most of the stuff out of the loop and modified all those things and still, I couldn't see any improvement. Maybe DMA is the only option where I think I can improve the performance. I will share the answer once I resolve my problem. I have looked into different sources like Arduino even that is also giving around 40us and do u have any idea any other board can solve this problem. Once again thank you very much for your help.

regards, RR

posted by RJ Steve 02 Jul 2018
5 years, 9 months ago.

Fastest ADC mode: Use DMA peripheral to memory. Read manufacturer's documentation. DMA is not supported directly from mbed os.

Hi Mark, Thank you for your suggestion and now I'm looking into it and what does it mean "DMA is not supported directly from mbed os" {i couldn't get the exact meaning what ur saying).

posted by RJ Steve 02 Jul 2018

Let's start from the beginning: A microcontroller is similar to other computers, it has CPU, RAM, ROM, etc. Usually its CPU is much less powerful than others, but it is rich in peripherals. To set up a microcontroller (CPU clock, UART, timers, DMA, ADC, etc), you have to write numeric values into registers. (Usually there are C libraries from the manufacturer to help you.) There is no such thing in an average mbed cpp file. Before main() starts, everything is configured to a default value, for example maximum CPU clock, no sleep modes, etc. The mbed framewrok configures the microcontroller via the manufacturer's libraries for you. The same thing happens when you write AnalogIn ain0(p20); The constructor of this object calls all the necessary code to set up ADC in polling mode. You can check ADC setup without mbed here (lines 100 to 140): https://github.com/scottellis/lpc17xx.cmsis.driver.library/blob/master/Examples/ADC/Polling/adc_polling_test.c Every microcontroller has to be set up in a different way, mbed is a common framwork above manufacturer's libraries to help the developers to start a new application quickly. It is waste of CPU cycles to poll ADC from the main loop. The ADC is much more slower than the CPU, and your program could do more useful things than busy waiting for ADC to finish sampling. From the other side, the ADC could do its best when sampling without interruption. So you should set up Direct Memory Access between ADC and memory, start sampling, and your program will get a callback when the required number of samples have been written into memory. There is no such thing in mbed, you could not write ain0.startDmaTransfer(&myBuffer, myBufferSize, callbackWhenFinished); But you can always access the manufacturer's libraries from your mbed code and set up MCU features unvailable via mbed API. Check LPC17xx ADC DMA example here: https://github.com/scottellis/lpc17xx.cmsis.driver.library/tree/master/Examples/ADC/DMA

posted by Mark Peter Vargha 02 Jul 2018