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
  • Hi 

    The problem is that callback configured through the app_timer will be affected by other interrupts and latencies in the system, and is not suited for scheduling very rapid events. 

    The app_timer is normally only used for event that happen at a slower rate, and where you can accept some delays in the execution. 

    For triggering ADC samples it is recommended to use a separate TIMER or RTC module, and have it trigger the ADC in the background using the PPI module. Then you don't have to run any code to trigger ADC sampling, and it will not be affected by other interrupts in the system. 

    For an example of how to implement this please have a look at the saadc example in the SDK:
    \nRF5_SDK_17.0.2_d674dde\examples\peripheral\saadc

    Best regards
    Torbjørn

  • Okay I'll try to do that and take a look at the example as well. But I still don't understand one thing, why do I need to use timer when I want to do continuous sampling. Why can't I just set the sample rate in the register as 8KHz (16Mhz/2000) and trigger ADC_START_TASK (NRF_SAADC_TASK_START)

    As I understand (and also from the comment documentation) , it should convert samples at the specified sample rate till RAM buffer is full and once the RAM buffer is full I should receive NRFX_SAADC_EVT_DONE event and upon this event I just transmit the buffer. 

    When I try to do that, it just converts one sample and stops. Can you please explain that ?

  • Hi 

    Did you make sure to set bit 12 in the SAMPLERATE register to enable the local timer?

    Best regards
    Torbjørn

Reply Children
Related