Hello,
I want to record audio with 8kHz sampling rate using ADC in nRF52832. Based on specification I should be able to reach up to 200ksps for ADC, but I'm getting a high delay on ADC conversion which lower the sample rate. I'm sending data through UART (later it should be send through BLE), which is fast enough to get all captured data in the receiver side (computer).
This is my main.c code :
/* * Copyright (c) 2020 Libre Solar Technologies GmbH * * SPDX-License-Identifier: Apache-2.0 */ #include <inttypes.h> #include <stddef.h> #include <stdint.h> #include <stdio.h> #include <zephyr/device.h> #include <zephyr/devicetree.h> #include <zephyr/drivers/adc.h> #include <zephyr/kernel.h> #include <zephyr/sys/printk.h> #include <zephyr/sys/util.h> #include <zephyr/drivers/uart.h> static const struct adc_dt_spec adc_channel = ADC_DT_SPEC_GET(DT_PATH(zephyr_user)); #define THREAD0_STACKSIZE 8192 #define THREAD0_PRIORITY 2 int err; int16_t buf; struct adc_sequence sequence = { .buffer = &buf, /* buffer size in bytes, not number of samples */ .buffer_size = sizeof(buf), }; static const struct device *uart_dev; const struct uart_config uart_cfg = { .baudrate = 230400, .parity = UART_CFG_PARITY_NONE, .data_bits = UART_CFG_DATA_BITS_8, .stop_bits = UART_CFG_STOP_BITS_1, .flow_ctrl = UART_CFG_FLOW_CTRL_NONE, }; void main(void) { // UART Init uart_dev = DEVICE_DT_GET(DT_NODELABEL(uart0)); if (!device_is_ready(uart_dev)) { printk("UART device is not ready"); return; } if (uart_configure(uart_dev, &uart_cfg)) { printk("UART configuration failed"); } // ADC Init if (!device_is_ready(adc_channel.dev)) { printk("ADC controller device not ready\n"); return; } err = adc_channel_setup_dt(&adc_channel); if (err < 0) { printk("Could not setup channel \n"); return; } (void)adc_sequence_init_dt(&adc_channel, &sequence); } void thread0 (void) { while(1){ int32_t val_mv; uint8_t num_samples = 100; uint8_t pcg[num_samples*2]; for (int i = 0; i < num_samples; i++) { err = adc_read(adc_channel.dev, &sequence); if (err < 0) { printk("Could not read (%d)\n", err); continue; } else { // printk("%"PRId16, buf); } pcg[i*2] = (uint8_t)buf; pcg[i*2 + 1] = (uint8_t)(buf >> 8); } uart_tx(uart_dev, pcg, sizeof(pcg), SYS_FOREVER_US); } } K_THREAD_DEFINE(thread0_id, THREAD0_STACKSIZE, thread0, NULL, NULL, NULL, THREAD0_PRIORITY, 0, 0);
I'm using zephyr ADC sample code located in zephyr/samples/drivers/adc and changed it based on my application. The maximum acceptable sample rate I'm getting is about 7.5 kHz which is not bad (the quality of signal is not still acceptable) but it is using all of my cpu load. I want the thread related to ADC run let say every 25 ms, to give cpu resource to another i2c sensor which is not added yet, but if ADC consumes all the cpu time it's not possible. I've changed parameters like zephyr,acquisition-time and zephyr,oversampling to lower numbers which increase the sample rate, but it will ruin the quality of signal. This is my devicetree overlay :
/ { zephyr,user { io-channels = <&adc 0>; }; }; &adc { #address-cells = <1>; #size-cells = <0>; channel@0 { reg = <0>; zephyr,gain = "ADC_GAIN_1_6"; zephyr,reference = "ADC_REF_INTERNAL"; zephyr,acquisition-time = <ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 5)>; zephyr,input-positive = <NRF_SAADC_AIN6>; zephyr,resolution = <12>; zephyr,oversampling = <4>; }; };
and prj.conf :
CONFIG_ADC=y CONFIG_LOG=y # UART DRIVER CONFIG_SERIAL=y CONFIG_UART_ASYNC_API=y
I've measured the time it takes to acquire 100 samples using k_uptime_get and k_uptime_delta. With provided ADC paramteres (acq time=5us and oversampling=4), I'm measuring about 13 ms which technically gives me a maximum sample rate of 100/13ms = 7700 samples/sec (almost the rate I've had on the receiver side) ,and also I've measured the delta time for uart_tx which is less than a millisecond, so the bottle neck is adc conversion time.
Is there any way to reduce the ADC conversion time without getting a noisy output ?
My setup :
HW : nRF52 DK
SW : NCS 2.1.1