Hi, i'm using NRF52840 DK connected with SPI on a 12 bits ADC reader : MAX11665
My objective is to perform a 256 values continuous reading at 44.1 khz on SPI and perform an FFT on the result set.
I'm using nrf connect with zephyr 2.4.2.
My concern is to precisely get 128 (or even 256) values every 1/44100 seconds, meaning every 22 microseconds.
Goal Algoritm :
While (not finish reading 256 values) Get current time Read ADC Wait until time elapsed with previous current time = 22 microseconds End While Do FFT transform
I didn't find a good way to get elapsed time in microseconds, as zephyr function k_uptime_delta i only in miliseconds precision
So i change with a timer and callback solution as it seem to take microseconds in the parameter with this call :
But it seam that the overal process is very time consuming and it didn't perform the whole adc reading in the given time.
What is the good practice to do it ? Do i have to use k_uptime_ticks ?
Is it a stable constant as it relay on the system speed ?
Here's my code :
prj.conf :
CONFIG_SPI=y CONFIG_NRFX_SPI1=y CONFIG_MAIN_STACK_SIZE=4048 CONFIG_FPU=y CONFIG_NEWLIB_LIBC=y CONFIG_NEWLIB_LIBC_FLOAT_PRINTF=y CONFIG_CMSIS_DSP_TRANSFORM=y CONFIG_CMSIS_DSP=y CONFIG_CMSIS_DSP_COMPLEXMATH=y CONFIG_CMSIS_DSP_STATISTICS=y
nrf52840.overlay :
&pinctrl { spi1_default: spi1_default { group1 { psels = <NRF_PSEL(SPIM_SCK, 0, 31)>, <NRF_PSEL(SPIM_MOSI, 0, 30)>, <NRF_PSEL(SPIM_MISO, 1, 15)>;//dout }; }; spi1_sleep: spi1_sleep { group1 { psels = <NRF_PSEL(SPIM_SCK, 0, 31)>, <NRF_PSEL(SPIM_MOSI, 0, 30)>, <NRF_PSEL(SPIM_MISO, 1, 15)>; }; }; }; my_spi_device: &spi1 { compatible = "nordic,nrf-spi"; status = "okay"; pinctrl-0 = <&spi1_default>; pinctrl-1 = <&spi1_sleep>; cs-gpios = <&gpio1 7 GPIO_ACTIVE_LOW>; pinctrl-names = "default", "sleep"; clock-frequency = <8000000>; reg_my_spi_master: spi-dev-a@0 { reg = <0>; compatible = "spi-device"; spi-max-frequency = < 800000 >; }; };main.c with timer : (but too long to process)
#include <zephyr/kernel.h> #include <zephyr/drivers/gpio.h> #include <zephyr/drivers/spi.h> #include <zephyr/devicetree.h> #include <arm_math.h> #include <arm_const_structs.h> #define SPI1_NODE DT_NODELABEL(spi1) #define MY_SPI_DEVICE DT_NODELABEL(my_spi_device) #define FFT_SIZE 128 #define FS 44100 // ADC Fequency #define INTERVAL (1000000 / FS) // #define MY_GPIO1 DT_NODELABEL(gpio1) #define GPIO_1_CS 7 static const struct device *gpio1_dev = DEVICE_DT_GET(MY_GPIO1); volatile int current_index = 0; const struct device *spi_dev; arm_rfft_fast_instance_f32 fft_instance ; struct spi_cs_control spim_cs = { .gpio = SPI_CS_GPIOS_DT_SPEC_GET(DT_NODELABEL(reg_my_spi_master)), .delay = 0, }; struct k_work adc_work; uint8_t tx_buffer[2] = {0}; uint8_t rx_buffer[2] = {0}; struct k_timer adc_timer; static struct spi_config spi_cfg = { .frequency = 8000000, .operation = SPI_OP_MODE_MASTER | SPI_TRANSFER_MSB | SPI_WORD_SET(8) | SPI_LINES_SINGLE | SPI_MODE_CPOL | SPI_MODE_CPHA, .slave = 0, .cs = &spim_cs}; // input data float32_t fftInput[FFT_SIZE]; float32_t fftOutput[FFT_SIZE]; float32_t fftMagnitude[FFT_SIZE/2]; static void spi_init(void) { spi_dev = DEVICE_DT_GET(MY_SPI_DEVICE); if (!device_is_ready(spi_dev)) { printk("SPI master device not ready!\n"); } if (!device_is_ready(spim_cs.gpio.port)) { printk("SPI master chip select device not ready!\n"); } } void adc_timer_handler(struct k_timer *dummy) { k_work_submit(&adc_work); } void adc_work_handler(struct k_work *work) { uint16_t adc_value; read_adc_value(&adc_value); fftInput[current_index] = (float32_t)adc_value; current_index = (current_index + 1) % FFT_SIZE; } void read_adc_value(uint16_t *value) { const struct spi_buf tx_buf = { .buf = tx_buffer, .len = sizeof(tx_buffer)}; const struct spi_buf_set tx = { .buffers = &tx_buf, .count = 1}; struct spi_buf rx_buf = { .buf = rx_buffer, .len = sizeof(rx_buffer)}; const struct spi_buf_set rx = { .buffers = &rx_buf, .count = 1}; tx_buffer[0] = 0; tx_buffer[1] = 0; rx_buffer[0] = 0; rx_buffer[1] = 0; int error = spi_transceive(spi_dev, &spi_cfg, &tx, &rx); if (error != 0) { printk("SPI transceive error: %i cfg=%d\n", error, spi_cfg.frequency); return; } *value = ((rx_buffer[0] << 8) | rx_buffer[1]) >> 4; } int main(void) { arm_status status; status = ARM_MATH_SUCCESS; if (!device_is_ready(gpio1_dev)) { printk("GPIO device not found!\n"); return 1; } printk("%s started\n\n", CONFIG_BOARD); spi_init(); if (!device_is_ready(gpio1_dev)) { printk("GPIO device not found!\n"); return 1; } gpio_pin_configure(gpio1_dev, GPIO_1_CS, GPIO_OUTPUT_ACTIVE | GPIO_ACTIVE_LOW); if (!device_is_ready(spi_dev)) { printk("SPI device not found!\n"); return 1; } status = arm_rfft_fast_init_f32(&fft_instance, FFT_SIZE); if (status != ARM_MATH_SUCCESS) { printf(" RFFT init error : %i\n",status); return -1; } k_work_init(&adc_work, adc_work_handler); k_timer_init(&adc_timer, adc_timer_handler, NULL); k_timer_start(&adc_timer, K_USEC(INTERVAL), K_USEC(INTERVAL)); while (1) { while(current_index != 0); // RFFT execution arm_rfft_fast_f32(&fft_instance, fftInput, fftOutput, 0); // magnitude calculation arm_cmplx_mag_f32(fftOutput, fftMagnitude, FFT_SIZE/2); // find max index. uint32_t maxIndex; float32_t maxValue; fftMagnitude[0]=0;//exlude low frequency arm_max_f32(fftMagnitude, FFT_SIZE/2, &maxValue, &maxIndex); // main frequency float32_t dominantFrequency = maxIndex * (FS / FFT_SIZE); printf("%f %f\n", dominantFrequency, maxValue); } return 0; }
main.c in simple continuous mode (but i need to wait 22 microseconds every adc reads)
#include <zephyr/kernel.h> #include <zephyr/drivers/gpio.h> #include <zephyr/drivers/spi.h> #include <zephyr/devicetree.h> #include <arm_math.h> #include <arm_const_structs.h> #define SPI1_NODE DT_NODELABEL(spi1) #define MY_SPI_DEVICE DT_NODELABEL(my_spi_device) #define FFT_SIZE 256 #define FS 44100 // Freaq reading #define INTERVAL (1000000 / FS) // #define MY_GPIO1 DT_NODELABEL(gpio1) #define GPIO_1_CS 7 static const struct device *gpio1_dev = DEVICE_DT_GET(MY_GPIO1); const struct device *spi_dev; arm_rfft_fast_instance_f32 fft_instance ;//= &arm_cfft_sR_f32_len16; //arm_rfft_fast_instance_f32 struct spi_cs_control spim_cs = { .gpio = SPI_CS_GPIOS_DT_SPEC_GET(DT_NODELABEL(reg_my_spi_master)), .delay = 0, }; uint8_t tx_buffer[2] = {0}; uint8_t rx_buffer[2] = {0}; static void spi_init(void) { spi_dev = DEVICE_DT_GET(MY_SPI_DEVICE); if (!device_is_ready(spi_dev)) { printk("SPI master device not ready!\n"); } if (!device_is_ready(spim_cs.gpio.port)) { printk("SPI master chip select device not ready!\n"); } } static struct spi_config spi_cfg = { .frequency = 8000000, .operation = SPI_OP_MODE_MASTER | SPI_TRANSFER_MSB | SPI_WORD_SET(8) | SPI_LINES_SINGLE | SPI_MODE_CPOL | SPI_MODE_CPHA, .slave = 0, .cs = &spim_cs}; // reading arrays float32_t fftInput[FFT_SIZE]; float32_t fftOutput[FFT_SIZE]; float32_t fftMagnitude[FFT_SIZE/2]; void read_adc_value(uint16_t *value) { const struct spi_buf tx_buf = { .buf = tx_buffer, .len = sizeof(tx_buffer)}; const struct spi_buf_set tx = { .buffers = &tx_buf, .count = 1}; struct spi_buf rx_buf = { .buf = rx_buffer, .len = sizeof(rx_buffer)}; const struct spi_buf_set rx = { .buffers = &rx_buf, .count = 1}; tx_buffer[0] = 0; tx_buffer[1] = 0; rx_buffer[0] = 0; rx_buffer[1] = 0; int error = spi_transceive(spi_dev, &spi_cfg, &tx, &rx); if (error != 0) { printk("SPI transceive error: %i cfg=%d\n", error, spi_cfg.frequency); return; } *value = ((rx_buffer[0] << 8) | rx_buffer[1]) >> 4; // printk("value final => %d\n", *value); } int main(void) { arm_status status; status = ARM_MATH_SUCCESS; if (!device_is_ready(gpio1_dev)) { printk("GPIO device not found!\n"); return 1; } printk("%s started\n\n", CONFIG_BOARD); spi_init(); if (!device_is_ready(gpio1_dev)) { printk("GPIO device not found!\n"); return 1; } gpio_pin_configure(gpio1_dev, GPIO_1_CS, GPIO_OUTPUT_ACTIVE | GPIO_ACTIVE_LOW); if (!device_is_ready(spi_dev)) { printk("SPI device not found!\n"); return 1; } status = arm_rfft_fast_init_f32(&fft_instance, FFT_SIZE); if (status != ARM_MATH_SUCCESS) { printf("RFFT init error : %i\n",status); return -1; } while (1) { for (int i = 0; i < FFT_SIZE; i++) { uint16_t adc_value; read_adc_value(&adc_value); fftInput[i] = (float32_t)adc_value; //k_usleep(INTERVAL); // NEED TO SLEEP HERE ! } arm_rfft_fast_f32(&fft_instance, fftInput, fftOutput, 0); arm_cmplx_mag_f32(fftOutput, fftMagnitude, FFT_SIZE/2); uint32_t maxIndex; float32_t maxValue; fftMagnitude[0]=0; arm_max_f32(fftMagnitude, FFT_SIZE/2, &maxValue, &maxIndex); float32_t dominantFrequency = maxIndex * (FS / FFT_SIZE); printf("%f %f\n", dominantFrequency, maxValue); } return 0; }
Regards