Pin19 analogIn consistently noisier than p20/p18 analogin pins

21 Nov 2012

We have found that pin19 in analogin sense mode (sampling at 25 Hz) is significantly noisier than pin20 and pin18 in analogin sense mode. We've tested this on on 2 mbed-M3 devices.

mbed code:

  1. include "mbed.h"
  2. include "USBSerial.h"
  1. define BUF_SIZE 64 Size of sample buffer in samples
  2. define SAMP_RATE 25 raw sample rate in Hz

Virtual serial port over USB Serial serial(USBTX, USBRX);

interrupt driven timer Ticker flipper; Ticker flipper2;

analog inputs AnalogIn ain_v1(p19); AnalogIn ain_v2(p20);

DigitalOut ref(p18);

DigitalOut led1(LED1); DigitalOut led2(LED2); DigitalOut led3(LED3);

unsigned short p1_cur[BUF_SIZE]; sample buffer unsigned short p2_cur[BUF_SIZE]; sample buffer unsigned char ind_write; queue write index unsigned char ind_read; queue read index

unsigned int t_local;

void get_readings() {

add nothing to the ISR unless it's strictly required. p1_cur[ind_write] = ain_v1.read_u16()>>4; bottom 4 bits are 0 p2_cur[ind_write] = ain_v2.read_u16()>>4; bottom 4 bits are 0

Update the write index if (ind_write==BUF_SIZE-1){ ind_write = 0; } else { ind_write++; } }

int main() {

reset variables

ind_write = 0; set queue index to 0. ind_read = 0;

int samp_cnt=0;

t_local = 0;

ref = 0; set pin to 0 to see if it helps with noise

Setup timer interrupts for samping the ADC channels flipper2.attach_us(&get_readings, 1000000/SAMP_RATE); the function to be attached and the interval in us

spin in a main loop. flipper will interrupt it to call flip while(1) {

if (samp_cnt>(SAMP_RATE>>2)){ blink an led led2 = 0; } else if (samp_cnt<SAMP_RATE){ led2 = 1; } else { samp_cnt = 0; } samp_cnt++;

if (ind_read != ind_write){ new data, send it out over serial.

If you're going to apply some math to the raw samples, it should be done here

serial.printf("%i,%i,%i\r\n", t_local,p1_cur[ind_read],p2_cur[ind_read]); t_local++; increment local time (just a monotonically increasing sample counter that will overflow Update the read index if (ind_read==BUF_SIZE-1){ ind_read = 0; } else { ind_read++; } }

} }

matlab code for noise sampling on pins:

ser_prt = serial('COM11','BaudRate',9600,'Terminator','CR/LF');

fopen(ser_prt);

of = fopen('data.txt','w');

cnt = 0;

samp_rate = 25; % Hz buff_length = 30; % seconds

databuf = zeros(3,samp_rate*buff_length);

while cnt < 1000 tline = fgetl(ser_prt); cnt = cnt+1;

delim_loc = strfind(tline,',');

if isempty(delim_loc) tlocal = tline(1:delim_loc(1)-1); v1 = tline(delim_loc(1)+1:delim_loc(2)-1); v2 = tline(delim_loc(2)+1:end); fprintf(of,'%s,%s,%s\n\r',tlocal,v1,v2);

databuf(:,1:end-1)=databuf(:,2:end); databuf(:,end)=[str2double(tlocal),str2double(v1),str2double(v2)]; end

if mod(cnt,samp_rate) plot((databuf(1,:)-databuf(1,end))/samp_rate,databuf(2,:),(databuf(1,:)-databuf(1,end))/samp_rate,databuf(3,:)) fprintf('cnt %i\n',cnt) end

end

fclose(of);

fclose(ser_prt); delete(ser_prt); clear ser_prt;

X = fft(databuf(2,:)); Y = fft(databuf(3,:));

f = linspace(0,25,length(X));

figure(1) semilogy(f(f<25/2),abs(X(f<25/2)),f(f<25/2),abs(Y(f<25/2)))

figure(2) plot((databuf(1,:)-databuf(1,end))/samp_rate,databuf(2,:),(databuf(1,:)-databuf(1,end))/samp_rate,databuf(3,:))

21 Nov 2012

After looking at your code ..

#include "mbed.h"

DigitalOut myled(LED1);

//analog inputs 
AnalogIn ain_v1(p19); 
AnalogIn ain_v2(p20);

DigitalOut ref(p18);

DigitalOut led1(LED1); 
DigitalOut led2(LED2); 
DigitalOut led3(LED3);

unsigned short p1_cur[BUF_SIZE]; // sample buffer 
unsigned short p2_cur[BUF_SIZE]; // sample buffer 
unsigned char ind_write; // queue write index 
unsigned char ind_read; //  queue read index

unsigned int t_local;

void get_readings() 
{

    //add nothing to the ISR unless it's strictly required. 
    p1_cur[ind_write] = ain_v1.read_u16()>>4; //bottom 4 bits are 0 
    p2_cur[ind_write] = ain_v2.read_u16()>>4; // bottom 4 bits are 0
    
    // Update the write index 
    if (ind_write==BUF_SIZE-1)
    { 
        ind_write = 0; 
    } 
    else 
    { 
        ind_write++; 
    } 
}



int main()
{

    reset variables

    ind_write = 0;
    set queue index to 0. ind_read = 0;

    int samp_cnt=0;

    t_local = 0;

    ref = 0;
    set pin to 0 to see if it helps with noise

    Setup timer interrupts for samping the ADC channels flipper2.attach_us(&get_readings, 1000000/SAMP_RATE);
    the function to be attached and the interval in us

    spin in a main loop. flipper will interrupt it to call flip 
    while(1) 
    {

        if (samp_cnt>(SAMP_RATE>>2)) 
        {
            blink an led led2 = 0;
        } 
        else if (samp_cnt<SAMP_RATE) 
        {
            led2 = 1;
        } 
        else 
        {
            samp_cnt = 0;
        }
        
        samp_cnt++;

        if (ind_read != ind_write) 
        {
            new data, send it out over serial.

            // If you're going to apply some math to the raw samples, it should be done here

            serial.printf("%i,%i,%i\r\n", t_local,p1_cur[ind_read],p2_cur[ind_read]); 
            t_local++; 
            // increment local time (just a monotonically increasing sample counter that will overflow Update the read index 
            
            if (ind_read==BUF_SIZE-1)
            { 
                ind_read = 0; 
            } 
            else 
            { 
                ind_read++; 
            } 
        }

    } 
}


        }

Correct me if I am wrong,

you are only reading 2 ADC Channels,

I belive that the reason is because Pin 20, has a significant amount of time to settle (charge internal capacitors), in comparison to Pin 19.

there are a few possable compromises/solutions.

Best: External ADC Chip, one for each channel, or one with 2/4/8 seperate ADC blocks (none spring to mind)

compromise 1: Interleaving, sample each ADC alternatly,

who++;
if (who && 1)
{
  // Sample ADC 0
}
else
{
  // Sample ADC 1
}

or you could allow a short 'Charging' delay for each channel ..

{
  read.An1();
  wait_us (10);
  result = read.An1();
}

or alturnatly, what I used to do for PIC's ADC (not quite the same ..

{
  // Result_Previous = ReadADC();
  // select MUX .. for next conversion
}

I am taking an educated guess, that most of your nois is related to the actual switching of the multiplexer,

and not fully charding the sample cap.

Hope this was informative.

Ceri