This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Issues in reading analog mic data over SAADC pin of nRF52810.

I have an expansion board by ST mounting an analog mic (MP23ABS1). The mic out pin (via a diff. amplifier) is given on a connector. I take that signal and feed it to the AIN5 (P0.29) on nrf52810. I want to continuously sample the analog mic data at 8000Hz and store the data in a buffer and transmit that over BLE. I've implemented a solution but I'm not getting the right values (already tested analog signal independently over oscilloscope). What I have done is, I initialized the ADC and the configured the channel with default values in single ended mode. Created an application timer with repeat mode and period of timer as 0.125ms(1/8000Hz) and in the timer callback function I sample the ADC(nrf_drv_saadc_sample()). After buffer (96 bytes of int16_t) is full, I check for NRFX_SAADC_EVT_DONE  and convert the buffer, in the saadc handler. And this process repeats after every 0.125ms.

The issue is that the buffer is filled with starting 4-5 bytes as 0x0000 and some random values in between as 0x0000 which get filled gradually. Also the digital values don't change much with changes in the sound I make (while I check in the buffer in the debug mode). Can you help me identify where I am going wrong with the implementation ?

Init Routine:

bool SDL_Mic_Init(void)
{
  uint32_t ret_code=false;
  
  
  ret_code=nrf_drv_saadc_init(NULL, mic_callback);  //ADC Initialization
  
  if(ret_code==NRFX_SUCCESS)
  { 
    nrf_saadc_channel_config_t adc_channel=NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(MIC_INPUT_DATA_PIN);
    
    ret_code=nrf_drv_saadc_channel_init(MIC_ADC_CHANNEL,&adc_channel);
    if(ret_code==NRFX_SUCCESS)
      ret_code=true;
    
   app_timer_create(&test_timer,APP_TIMER_MODE_REPEATED,sampling_callback);
    
    nrf_saadc_continuous_mode_enable(2000); //for 8K sample rate (16000000/2000=8000Hz)
  }
  
  return ret_code;
}

When I send a start streaming command from app

bool Mic_DataAcq_Start(void)
{
  uint32_t ret_code=false;
  //Set the buffer in RAM that will acquire adc data(raw datamic)
    ret_code=nrf_drv_saadc_buffer_convert(mic_handler.audio_sample , AUDIO_SAMPLES_BUFFER_SIZE);
        if(ret_code==NRFX_SUCCESS)
    {
        ret_code=true;
    }
    
    app_timer_start(test_timer, ticks, NULL); //ticks=APP_TIMER_TICKS(0.125)

  return ret_code;
}

After 0.125ms, timer handler will call nrf_drv_saadc_sample() and after buffer is full the following will be called

void mic_callback(const nrf_drv_saadc_evt_t  * p_event)
{
  if(p_event->type==NRFX_SAADC_EVT_DONE)
  {
    //copy mic_handler.audio_samples buffer into a copy buffer
   // memcpy(SRV_DATA_state.txLogFrame3.txStreamFrame.mic_rawaudio.audio_raw_val, mic_handler.audio_sample, AUDIO_SAMPLES_BUFFER_SIZE);
    //Set a flag to indicate FSM process that data is ready to be sent over BLE
    //MSG_SetFlag(MSG_TYPE_SRV_DATA, DATA_FLAG_AMIC_DATA);
    
    nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer , AUDIO_SAMPLES_BUFFER_SIZE);
  }
  
}

Parents Reply Children
  • Thanks overbekk, for the example. I had a quick glance at the code and was curious as to why have you enabled the LFCLK ? Wouldn't the internal ADC timer use the HFCLK(16MHz) ?
    Also, 

    For triggering ADC samples it is recommended to use a separate TIMER or RTC module

    Can you please explain me how I can use a separate RTC module to trigger ADC sample (using PPI only) given that nRF52810 has only RTC0(used by softdevice) and RTC1(used by app_timer) ?

  • Hi Manish

    Manish Kaul said:
    I had a quick glance at the code and was curious as to why have you enabled the LFCLK

    This was a remnant from the original example I based it on. After I shared the example with you I cleaned it up a bit, and asked my colleague to include it in his repository. 

    It seems he hasn't included it there yet, but you can find the cleaned up version here:

    nrfx_saadc_continuous_sampling_v2.zip

    Manish Kaul said:
    Can you please explain me how I can use a separate RTC module to trigger ADC sample (using PPI only) given that nRF52810 has only RTC0(used by softdevice) and RTC1(used by app_timer) ?

    There is no need to use a separate timer if you want to sample at 8kHz, then you can use the SAMPLERATE register as the example shows. Is this not the frequency you need?

    If you don't have any RTC's available you can also use a TIMER module to trigger ADC sampling. Generally the TIMER modules use more current than RTC's (while offering better accuracy), but if you are running the ADC already the difference is unlikely to be that large (unless you want to sample the ADC at a slow rate, then an RTC or the app_timer module is preferred).  

    Best regards
    Torbjørn

  • but you can find the cleaned up version here

    Thank you very much.

    Is this not the frequency you need?

    Yes, still need to sample at 8KHz but that's not the only sample rate I need to work on. Will go as low as 1KHz or even 500Hz as well.

    but if you are running the ADC already the difference is unlikely to be that large (unless you want to sample the ADC at a slow rate, then an RTC or the app_timer module is preferred). 

    I see. But I really need to lower current consumption while sampling at the same rate (8KHz) and lower. So I was thinking of using Low power timer (RTC) for ADC. In case I want to confirm how much difference do I get with TIMER vs. RTC (since I do intend to also lower the sample rate as mentioned above), can I use RTC1 (already used by app_timer) by modifying the app_timer.c by, for example, adding a function to it which set's the CC1 register (CC0 used already by app timer) and ties the COMPARE1 register to PPI ? Or any other way you suggest I can use the RTC1 timer to not interrupt the app timer routines in my application and at the same time be able to use it for sampling via PPI ?

  • Hi Manish

    The slower the sample rate the larger the relative difference will be, since it is the sleep currents (not the active currents) that is most affected by running a timer. 

    If you want to compare the two you could always use the app_timer itself to sample the ADC, as long as the frequency is not too large. At 1kHz or lower the app_timer should be able to schedule callbacks fast enough. 

    Then you can compare the current consumption either when using the app_timer or when using a TIMER module, and see if it is big enough to warrant concern. 

    Best regards
    Torbjørn

Related