0

Pico, MAX4466 mic, and ADC free run
Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.

Pico, MAX4466 mic, and ADC free run

by fapplin on Fri Feb 12, 2021 6:12 pm

Hi,

I'm using your MAX4466 mic with a Raspberry Pi Pico. I'm using the free run mode of one of the ADCs. My question is: how can I get a running decibel reading? I have tried 2 different methods, but neither seem correct. Can anyone help me, please?

Code: Select all | TOGGLE FULL SIZE
#include <math.h>
#include <stdio.h>

#include "hardware/adc.h"
#include "hardware/dma.h"
#include "kiss_fftr.h"
#include "pico/stdlib.h"
#include "hardware/uart.h"

// set this to determine sample rate
// 0     = 500,000 Hz
// 960   = 50,000 Hz
// 9600  = 5,000 Hz
#define CLOCK_DIV 960
#define FSAMP 50000

// Channel 0 is GPIO26
#define CAPTURE_CHANNEL 0

#define NSAMP 1000

dma_channel_config cfg;
uint dma_chan;
float freqs[NSAMP];

void setup();
void sample(uint16_t *capture_buf);

int main()
{
    uint16_t cap_buf[NSAMP];
   

    setup();

    while (1)
    {
        uint64_t sum = 0;
        double sumWConvFactor = 0.0; 
        const float conversion_factor = 3.3f / (1 << 12);
        uint16_t samp;
        uint64_t sumRMS = 0;

        sample(cap_buf);
       
        for (int i = 0; i < NSAMP; i++)
        {
            samp = cap_buf[i];
           
            sumRMS += (samp * samp);

           sumWConvFactor += cap_buf[i] * conversion_factor;
        } //end for
       
        double rms = sqrt((double)sumRMS / (double)NSAMP /2.0);
        double decibel = 20 * log(rms);

        printf("dB1: %f\n", decibel);

        double avgVolts = (double) sumWConvFactor / NSAMP;
        double dB = 20 * log((avgVolts));

        printf("dB2: %f\n", dB);
    } //end while

}  //end main

void sample(uint16_t *capture_buf)
{
    adc_fifo_drain();
    adc_run(false);

    dma_channel_configure(dma_chan, &cfg,
                          capture_buf,   // dst
                          &adc_hw->fifo, // src
                          NSAMP,         // transfer count
                          true           // start immediately
    );

    adc_run(true);
    dma_channel_wait_for_finish_blocking(dma_chan);
} //end sample

void setup()
{
    stdio_init_all();

    adc_gpio_init(26 + CAPTURE_CHANNEL);

    adc_init();
    adc_select_input(CAPTURE_CHANNEL);
    adc_fifo_setup(true,  // Write each completed conversion to the sample FIFO
                   true,  // Enable DMA data request (DREQ)
                   1,     // DREQ (and IRQ) asserted when at least 1 sample present
                   false, // We won't see the ERR bit because of 8 bit reads; disable.
                   false   // Shift each sample to 8 bits when pushing to FIFO
    );

    // set sample rate
    adc_set_clkdiv(CLOCK_DIV);

    sleep_ms(1000);
    // Set up the DMA to start transferring data as soon as it appears in FIFO
    uint dma_chan = dma_claim_unused_channel(true);
    cfg = dma_channel_get_default_config(dma_chan);

    // Reading from constant address, writing to incrementing byte addresses
    channel_config_set_transfer_data_size(&cfg, DMA_SIZE_16);
    channel_config_set_read_increment(&cfg, false);
    channel_config_set_write_increment(&cfg, true);

    // Pace transfers based on availability of ADC samples
    channel_config_set_dreq(&cfg, DREQ_ADC);

   
} //end setup


Code: Select all | TOGGLE FULL SIZE
cmake_minimum_required(VERSION 3.12)

include(pico_sdk_import.cmake)

project(adc_fft_project)

pico_sdk_init()

add_executable(test test.c)


pico_enable_stdio_usb(test 1)
pico_enable_stdio_uart(test 1)

pico_add_extra_outputs(test)

target_link_libraries(test
   pico_stdlib
   hardware_adc
   hardware_dma
   )


fapplin
 
Posts: 17
Joined: Mon Sep 23, 2013 8:47 am

Re: Pico, MAX4466 mic, and ADC free run

by adafruit_support_mike on Mon Mar 01, 2021 1:00 am

For starters, it looks like your RMS calculation is off:

Code: Select all | TOGGLE FULL SIZE
            sumRMS += (samp * samp);
That expression only works when the samples are centered around zero. The ADC readings will always be positive and centered somewhere around half the ADC's reference voltage, so you're adding and squaring a DC offset.

Officially you should take the sum of all the samples and find the average of that, then subtract that from all the samples and square the results.

You can simplify that with an approximation just by setting the no-input value as a constant, but then you can end up with a small DC offset if your constant doesn't match the real zero-input voltage. A decent compromise is to start with the constant and then keep a running average based on the actual samples:

Code: Select all | TOGGLE FULL SIZE
    double process_mean = SETPOINT;
   
    [ . . . ]
        double sample_mean = process_mean / NSAMP;
        uint64_t process_sum = 0;
   
        for (int i = 0; i < NSAMP; i++)
        {
            samp = cap_buf[ i ] - sample_mean;
            sumRMS += ( samp * samp );

            process_sum += cap_buf[ i ];
           
            [ . . . ]
        } //end for
       
        process_mean = (( 99.0 * process_mean ) + (double)process_sum ) / 100.0;
That tracks the average of the actual samples over the last hundred sets without forcing you to keep a huge buffer. Working in chunks means you only have to do one type conversion and calculation per buffer, not one per sample.

adafruit_support_mike
 
Posts: 63052
Joined: Thu Feb 11, 2010 2:51 pm

Re: Pico, MAX4466 mic, and ADC free run

by fapplin on Mon Mar 08, 2021 12:56 pm

I'm sorry for the late thank you. I got off on another project.

I'm adding it in, now! Thanks!

fapplin
 
Posts: 17
Joined: Mon Sep 23, 2013 8:47 am

Please be positive and constructive with your questions and comments.