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?