Hello Guys,
I am running an application which is sampling 3 ADC channels at 50 MHz. I am using sample buffers of size 150 and PPI to generate a sample event every 20 microseconds. At lower sampler rates, the data position in the buffers is consistent. But as I go below a sample period of 100 microseconds, the buffer order is corrupted.
This seems to be a very common and important issue but there is really no clear cut instructions on how to fix the issue.
I have tried the tips provided here, but none of them seem to work.
void saadc_callback(nrf_drv_saadc_evt_t const * p_event)
{
if (p_event->type == NRF_DRV_SAADC_EVT_DONE)
{
ret_code_t err_code;
err_code = nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, SAMPLES_IN_BUFFER);
APP_ERROR_CHECK(err_code);
/* Get average battery value */
uint16_t bat_total = 0;
/* Create Bucket from ADC Data */
sample_bucket_t* pbucket = malloc(sizeof(sample_bucket_t));
if(pbucket == NULL) return;
for(uint16_t i=0; i<SAMPLE_BUCKET_SIZE; i++){
int16_t mic_val = p_event->data.done.p_buffer[i*3];
int16_t acc_val = p_event->data.done.p_buffer[i*3+1];
bat_total += p_event->data.done.p_buffer[i*3+2];
printf("%d\n", mic_val);
pbucket->samples[i].data[0] = (uint8_t)(mic_val);
pbucket->samples[i].data[1] = (uint8_t)((mic_val >> 8) & 0x0F) | ( (uint8_t)(acc_val) << 4 );
pbucket->samples[i].data[2] = (uint8_t)(acc_val >> 4);
}
battery_val = bat_total / SAMPLE_BUCKET_SIZE;
/* Run Sampling FSM */
switch(device_status.sampling_state){
case SAMPLE_NONE:
free(pbucket);
break;
case SAMPLE_POLLING:
ring_buffer_add(buckets_before, pbucket); /* Add bucket to buckets before buffer */
/* Check if accel threshold is exceeded */
uint16_t accel_val = bucket_acc_average(pbucket);
//printf("%d\n", accel_val);
if(accel_val > fusion_config.shot_detection_acc_threshold){
//LOGI("Accelerometer threshold exceeded");
device_status.sampling_state = SAMPLE_ACCEL_THRESHOLD;
device_status.sampling_status.acc_exceed_time = global_ticks;
}
break;
case SAMPLE_ACCEL_THRESHOLD:
/* Continue adding buckets to before_buffer even after accel threshold
has been exceeded. */
ring_buffer_add(buckets_before, pbucket);
/* Check if threshold has been exceeded */;
uint16_t audio_val = bucket_mic_average(pbucket);
if(audio_val > fusion_config.shot_detection_aud_threshold){
//LOGI("Shot Detected !");
device_status.sampling_state = SAMPLE_SHOT_DETECTED;
}
/* Check if time has been elapsed */
else if(TIME_ELAPSED(device_status.sampling_status.acc_exceed_time, AUDIO_WAIT_TIMEOUT)){
//LOGI("Audio Signal not detected ");
device_status.sampling_state = SAMPLE_POLLING;
}
break;
case SAMPLE_SHOT_DETECTED:
/* Add buckets to buckets_after ring buffer */
//printf("%d\n", buckets_after->size);
if(buckets_after->size < buckets_after->capacity){
ring_buffer_add(buckets_after, pbucket);
}
/* When bucket is full, create shot_data_t */
else{
free(pbucket); /* free bucket or add to ring buffer */
uint8_t shot_id = get_shot_count();
//device_status.sampling_state = SAMPLE_SHOT_COOL_DOWN;
//break;
/* Create ble packets from bucket data */
/* Iterate through all buckets in buckets before */
while(buckets_before->size){
sample_bucket_t* bucket = front(buckets_before);
ble_packet_t* ble_packet = malloc(sizeof(ble_packet_t));
//if(ble_packet == NULL) continue;
ble_packet->buf[0] = shot_id;
memcpy(ble_packet->buf+2, &bucket->samples, 150);
ble_packet->packet_length = 152;
ble_add_to_queue(LOW_PRIORITY_PACKET, ble_packet);
dequeue(buckets_before);
free(bucket);
}
/* Iterate through buckets_after */
while(buckets_after->size){
sample_bucket_t* bucket = front(buckets_after);
ble_packet_t* ble_packet = malloc(sizeof(ble_packet_t));
//if(ble_packet == NULL) continue;
ble_packet->buf[0] = shot_id;
memcpy(ble_packet->buf+2, &bucket->samples, 150);
ble_packet->packet_length = 152;
ble_add_to_queue(LOW_PRIORITY_PACKET, ble_packet);
dequeue(buckets_after);
/* Add bucket to buckets before buffer so that next shot can use it */
ring_buffer_add(buckets_before, bucket);
}
/* Go To Cool Down Phase */
device_status.sampling_state = SAMPLE_SHOT_COOL_DOWN;
//LOGD("SHOT_DETECTED -> SHOT_COOL_DOWN");
}
break;
case SAMPLE_SHOT_COOL_DOWN:
device_status.sampling_state = SAMPLE_POLLING;
free(pbucket);
break;
default:
free(pbucket);
break;
}
}
}
/**
* Initialize ADC (saadc)
* Params:
* bool bat_only: Flag to determine if only Battery ADC should be enabled
* Ret: None
*/
static void init_adc(bool bat_only){
ret_code_t err_code;
nrf_saadc_channel_config_t channel_config_mic =
NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(MIC_ADC);
channel_config_mic.acq_time = NRF_SAADC_ACQTIME_3US;
nrf_saadc_channel_config_t channel_config_acc =
NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(ACC_ADC);
channel_config_acc.acq_time = NRF_SAADC_ACQTIME_3US;
nrf_saadc_channel_config_t channel_config_bat =
NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(BAT_ADC);
channel_config_bat.acq_time = NRF_SAADC_ACQTIME_3US;
nrf_drv_saadc_config_t saadc_config = {
.resolution = NRF_SAADC_RESOLUTION_12BIT,
.oversample = NRF_SAADC_OVERSAMPLE_DISABLED,
.interrupt_priority = 2,
.low_power_mode = false
};
err_code = nrf_drv_saadc_init(&saadc_config, saadc_callback);
APP_ERROR_CHECK(err_code);
/* Initialize channels */
err_code = nrf_drv_saadc_channel_init(0, &channel_config_mic);
APP_ERROR_CHECK(err_code);
err_code = nrf_drv_saadc_channel_init(1, &channel_config_acc);
APP_ERROR_CHECK(err_code);
err_code = nrf_drv_saadc_channel_init(BAT_CHANNEL, &channel_config_bat);
APP_ERROR_CHECK(err_code);
/* Set buffers */
err_code = nrf_drv_saadc_buffer_convert(buffer_pool[0], SAMPLES_IN_BUFFER);
APP_ERROR_CHECK(err_code);
/* Initialize all buffers only when accel and microphone are being sampled */
err_code = nrf_drv_saadc_buffer_convert(buffer_pool[1], SAMPLES_IN_BUFFER);
APP_ERROR_CHECK(err_code);
APP_ERROR_CHECK(nrf_drv_ppi_init());
nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;
timer_cfg.bit_width = NRF_TIMER_BIT_WIDTH_32;
err_code = nrf_drv_timer_init(&adc_timer, &timer_cfg, timer_handler);
APP_ERROR_CHECK(err_code);
/* If only battery sampling is needed, use a lower sampling rate
* All channels will be sampled on every iteration.
*/
uint32_t ticks;
if(bat_only){
ticks = nrf_drv_timer_ms_to_ticks(&adc_timer, ADC_BATT_SAMPLE_INTERVAL);
}
else{
ticks = nrf_drv_timer_us_to_ticks(&adc_timer, ADC_FULL_SAMPLE_INTERVAL);
}
nrf_drv_timer_extended_compare(&adc_timer,
NRF_TIMER_CC_CHANNEL0,
ticks,
NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK,
false);
uint32_t timer_compare_event_addr = nrf_drv_timer_compare_event_address_get(&adc_timer,
NRF_TIMER_CC_CHANNEL0);
uint32_t saadc_sample_task_addr = nrf_drv_saadc_sample_task_get();
/* setup ppi channel so that timer compare event is triggering sample task in SAADC */
err_code = nrf_drv_ppi_channel_alloc(&ppi_channel);
APP_ERROR_CHECK(err_code);
err_code = nrf_drv_ppi_channel_alloc(&ppi_channel2);
APP_ERROR_CHECK(err_code);
err_code = nrf_drv_ppi_channel_assign(ppi_channel,
timer_compare_event_addr,
saadc_sample_task_addr);
APP_ERROR_CHECK(err_code);
err_code = nrf_drv_ppi_channel_assign(ppi_channel2,
nrf_saadc_event_address_get(NRF_SAADC_EVENT_END),
nrf_saadc_task_address_get(NRF_SAADC_TASK_START)
);
APP_ERROR_CHECK(err_code);
nrf_drv_timer_enable(&adc_timer);
periph_status.adc_status = (bat_only) ? ADC_BATTERY_ONLY : ADC_FULL ;
}
/**
* Start ADC Sampling
* Params: None
* Ret: None
*/
static void start_adc_sampling(void){
APP_ERROR_CHECK(nrf_drv_ppi_channel_enable(ppi_channel));
APP_ERROR_CHECK(nrf_drv_ppi_channel_enable(ppi_channel2));
}
The SAADC runs with priority level = 2, and there is not much else that interrupts it other than the softdevice. In the near future I will incoorporate a feature to send a BLE packet every 10 ms.
Please can you shed more light on how the solution 1 (Use PPI to trigger START task on an END event. This will avoid the delayed triggering og the START task due to a queued interrupt generated by the END event,) from the link above should be implemented. I have tried the implied implementation but this doesnt solve this problem.
I am using the latest nRF17.0.0 SDK so I am suprised this issue still persists after soo many years.
Please what is the best workaround for this?
Is a sample rate of 50 kHz achievable using CPU interrupts?