so it was actually because i was using dma_channel_set_read_addr instead of dma_channel_set_write_addr.
the fifo fits the 128bits, dma should be fast enough to read it all before the next 128bit read for the current slow rate I trigger CONVST on the adc
DREQ is only good for keeping pace, not for starting transfers, so what ended up happening when i tried it is that dma would do empty transfers at an unlimited rate (not at the pio spi read/autopush rate)
I think irq at end of spi read is perfectly reasonable to drain the fifo, i tried both at start (after busy trigger) and at end, no difference in terms of getting data
now i just need to make sense of the data
latest code im running:
ad7606_spi.piomain.c
the fifo fits the 128bits, dma should be fast enough to read it all before the next 128bit read for the current slow rate I trigger CONVST on the adc
DREQ is only good for keeping pace, not for starting transfers, so what ended up happening when i tried it is that dma would do empty transfers at an unlimited rate (not at the pio spi read/autopush rate)
I think irq at end of spi read is perfectly reasonable to drain the fifo, i tried both at start (after busy trigger) and at end, no difference in terms of getting data
now i just need to make sense of the data
latest code im running:
ad7606_spi.pio
Code:
; ad7606_spi.pio; PIO program for 128-bit SPI read from AD7606 with CS control; Assumes:; - CS is the first side-set pin; - SCK is the second side-set pin; - MISO is the input pin;; - Wait for busy pin high and fall to start read; - Read 128bit: 4 outer loops, 32 inner loops to read, 32bit push to rx fifo; - On read complete, Interupt 3 to start DMA transfer.program ad7606_spi_128bit_read.side_set 2.wrap_targetwait 1 gpio 10side 1wait 0 gpio 10side 1irq nowait 0side 1set y, 7side 1 outer:set x, 15side 0 [1];bitloop:in pins, 1side 2 [1]; Read one bit from miso with CS low, SCK high.jmp x-- bitloopside 0 [1]; Continue reading, SCK toggles.jmp y-- outerside 1 [1]irq nowait 3side 1.wrap% c-sdk {// this is a raw helper function for use by the user which sets up the GPIO output, and configures the SM to output on a particular pinvoid ad7606_spi_128bit_init(PIO pio, uint sm, uint offset, float clkdiv, uint pin_sck, uint pin_miso, uint pin_cs, uint pin_busy) {pio_sm_config c = ad7606_spi_128bit_read_program_get_default_config(offset);pio_gpio_init(pio, pin_cs);pio_gpio_init(pio, pin_sck);pio_gpio_init(pio, pin_miso);pio_gpio_init(pio, pin_busy);sm_config_set_in_shift(&c, false, true, 32);sm_config_set_sideset_pins(&c, pin_cs);pio_sm_set_in_pins(pio, sm, pin_miso);pio_sm_set_consecutive_pindirs(pio, sm, pin_cs, 2, true);//pio_sm_set_consecutive_pindirs(pio, sm, pin_miso, 1, false);//sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX);sm_config_set_clkdiv(&c, clkdiv);pio_sm_init(pio, sm, offset, &c);}%}
Code:
#include <stdio.h>#include <string.h>#include <math.h>#include <stdlib.h>#include "pico/stdlib.h"#include "pico/double.h"#include "hardware/pio.h"#include "hardware/dma.h"#include "hardware/pwm.h"#include "hardware/irq.h"#include "hardware/gpio.h"#include "hardware/clocks.h"#include "ad7606_spi.pio.h"// Define GPIO pins#define PIN_SCK 14#define PIN_MISO 12#define PIN_CS 13#define PIN_BUSY 10#define PIN_CONVST 9#define PIN_RESET 11// DMA buffer and ring buffer definitions#define CHANNELS 8#define RING_BUFFER_LEN 128//512 // Length of the ring buffer#define WINDOW_SIZE 32 //64 // Number of buffers to accumulate for RMS calculation#define SKIP_FACTOR 5 // Skip factor for overflow handling#define PWM_FREQ 1000 // 7.5 - 200000.0#define PIO_FREQ 500000.0 //125000000.0#define REF 5.0typedef struct { uint16_t data[CHANNELS]; absolute_time_t timestamp; uint sample_index;} RingBuffer;RingBuffer ring_buffer[RING_BUFFER_LEN];volatile uint ring_buffer_head = 0;volatile uint ring_buffer_tail = 0;volatile bool overflow_flag = false;volatile uint skip_counter = 0;volatile uint64_t current_sample_index = 0; // Global sample indexvolatile uint64_t total_samples_processed = 0;volatile uint64_t total_processing_time_us = 0;uint pulsecount = 0;uint dmacounter = 0;uint piocount = 0;PIO pio = pio0;uint sm = 0;int dma_chan;typedef struct { bool is_voltage; // True if the channel measures voltage, false if current double conversion_factor; // Conversion factor for the ADC reading, int pairedWith; // Index of the paired channel, -1 indicates no valid pairing (for power calculation) } ChannelConfig;// Configuration for each channelChannelConfig channel_config[CHANNELS] = { {true, 1, 1}, // Channel 1 - Voltage {false, 1, 0}, // Channel 2 - Current {true, 1, 3}, // Channel 3 - Voltage {false, 1, 2}, // Channel 4 - Current {true, 1, 6}, // Channel 5 - Voltage {true, 1, 7}, // Channel 6 - Voltage {false, 1, 4}, // Channel 7 - Current {false, 1, 5}, // Channel 8 - Current};void pio_irq0_handler() { piocount++; bool int0 = pio_interrupt_get(pio, 0); bool int1 = pio_interrupt_get(pio, 1); bool int2 = pio_interrupt_get(pio, 2); bool int3 = pio_interrupt_get(pio, 3); //printf("piocount: %d, int: %d %d %d %d\n", piocount, int0, int1, int2, int3); // Clear the interrupt flag (if necessary) if(int0) { pio_interrupt_clear(pio, 0); dma_channel_start(dma_chan); } if(int1) { pio_interrupt_clear(pio, 1); } if(int2) pio_interrupt_clear(pio, 2); if(int3) { pio_interrupt_clear(pio, 3); //printf("start dma transfer\n"); //asm volatile("nop \n nop \n nop"); //sleep_us(0.1); //start dma transfer }}// SPI PIO program initializationvoid init_pio_spi(PIO pio, uint sm, uint offset, uint pin_sck, uint pin_miso, uint pin_cs, uint pin_busy, float freq) { printf("Initializing PIO SPI at offset:%d\n", offset); uint32_t sysClock = clock_get_hz(clk_sys);float clkdiv = (sysClock / freq); printf("pio clkdiv: %f \n", clkdiv); ad7606_spi_128bit_init(pio, sm, offset, clkdiv, pin_sck, pin_miso, pin_cs, pin_busy); pio_set_irq0_source_mask_enabled(pio, 3840, true); //setting all 4 at once // Register the PIO interrupt handler irq_set_exclusive_handler(PIO0_IRQ_0, pio_irq0_handler); // Use PIO1_IRQ_0 for pio1 irq_set_enabled(PIO0_IRQ_0, true); // Enable the interrupt //pio_sm_clear_fifos(pio, sm); pio_sm_set_enabled(pio, sm, true);}// PWM setup for CONVSTvoid setup_pwm_for_convst(uint gpio, float freq, u_int16_t duty) { gpio_set_function(gpio, GPIO_FUNC_PWM); uint slice_num = pwm_gpio_to_slice_num(gpio); u_int16_t wrap = freq < 1000 ? 65535 - ((freq * freq) / 1000000.0 * 65000) : 500; printf("pwm f: %f w: %u c: %f\n", freq, wrap, 125000000.0 / (freq * wrap)); pwm_set_clkdiv(slice_num, 125000000.0 / (freq * wrap)); // Set the divider pwm_set_wrap(slice_num, wrap - 1); // Set the wrap value pwm_set_chan_level(slice_num, pwm_gpio_to_channel(gpio), ceil(wrap * (duty / 100.0))); // Set duty cycle pwm_set_enabled(slice_num, true); // Enable PWM}// DMA IRQ handlervoid dma_irq_handler() { dma_hw->ints0 = 1u << dma_chan; // Record the timestamp and sample index for the completed DMA transfer dmacounter++; //if(dmacounter % 100 == 0) { for (int i = 0; i < CHANNELS; i++) { printf("%8.6f, ", ring_buffer[ring_buffer_head].data[i] / 65535.0 * 2 * REF); // } printf("\n"); //printf(" dma handler %d %u %u\n", dmacounter, ring_buffer_head, ring_buffer_tail); //} ring_buffer[ring_buffer_head].timestamp = get_absolute_time(); ring_buffer[ring_buffer_head].sample_index = current_sample_index++; // Advance the ring buffer head ring_buffer_head = (ring_buffer_head + 1) % RING_BUFFER_LEN; // Calculate the number of unprocessed buffers uint buffer_count = (ring_buffer_head + RING_BUFFER_LEN - ring_buffer_tail) % RING_BUFFER_LEN; // Set overflow flag if approaching capacity (e.g., 1 or 2 windows size before full) if (buffer_count >= RING_BUFFER_LEN - WINDOW_SIZE * 2) { // Adjust this value as needed overflow_flag = true; //printf("overflow_flag\n"); } // Set up the next DMA transfer //dma_channel_start(dma_chan); //dma_channel_set_read_addr(dma_chan, &databuffer/*&ring_buffer[ring_buffer_head].data*/, false); dma_channel_set_write_addr(dma_chan, ring_buffer[ring_buffer_head].data, false);}// DMA setup for data transferint setup_dma() { int dma_chan = dma_claim_unused_channel(true); dma_channel_config config = dma_channel_get_default_config(dma_chan); channel_config_set_transfer_data_size(&config, DMA_SIZE_32); //channel_config_set_ring(&config, true, 8); channel_config_set_read_increment(&config, false); //was false channel_config_set_write_increment(&config, true); //was true channel_config_set_dreq(&config, pio_get_dreq(pio, sm, false)); dma_channel_configure( dma_chan, &config, ring_buffer[ring_buffer_head].data, // Destination pointer &pio->rxf[sm], // Source pointer (PIO RX FIFO) CHANNELS/2, // Transfer count (8 samples of uint16_t each) false // Don't start yet ); // Tell the DMA to raise IRQ line 0 when the channel finishes a block dma_channel_set_irq0_enabled(dma_chan, true); // Configure the processor to run dma_handler() when DMA IRQ 0 is asserted irq_set_exclusive_handler(DMA_IRQ_0, dma_irq_handler); irq_set_enabled(DMA_IRQ_0, true); return dma_chan;}void process_accumulated_data() { absolute_time_t start_time = get_absolute_time(); double vrms[CHANNELS] = {0}, irms[CHANNELS] = {0}, prms[CHANNELS] = {0}; double sum_square[CHANNELS] = {0}; uint actual_processed_buffers = 0; int64_t total_time_diff_us = 0; absolute_time_t last_time; // Start from the oldest buffer in the window uint buffer_index = ring_buffer_tail; for (uint i = 0; i < WINDOW_SIZE; ++i) { if (overflow_flag && i % (SKIP_FACTOR + 1) != 0) { // Skip this data point in RMS calculations buffer_index = (buffer_index + 1) % RING_BUFFER_LEN; continue; } // Accumulate squared values for each channel for (int ch = 0; ch < CHANNELS; ++ch) { double value = ring_buffer[buffer_index].data[ch] * channel_config[ch].conversion_factor; sum_square[ch] += value * value; } if (actual_processed_buffers > 0) { int64_t time_diff_us = absolute_time_diff_us(last_time, ring_buffer[buffer_index].timestamp); total_time_diff_us += time_diff_us; } last_time = ring_buffer[buffer_index].timestamp; buffer_index = (buffer_index + 1) % RING_BUFFER_LEN; actual_processed_buffers++; } // Calculate RMS values for each channel for (int i = 0; i < CHANNELS; ++i) { if (channel_config[i].is_voltage) vrms[i] = sqrt(sum_square[i] / actual_processed_buffers); else irms[i] = sqrt(sum_square[i] / actual_processed_buffers); } // Calculate the average time interval double average_time_interval = actual_processed_buffers > 1 ? total_time_diff_us / 1.0e6 / (actual_processed_buffers - 1) : 0; // Calculate power (Prms) only for valid pairings for (int i = 0; i < CHANNELS; ++i) { if (channel_config[i].is_voltage && channel_config[i].pairedWith != -1) { int paired_channel = channel_config[i].pairedWith; // Ensure paired channel is within bounds and is a current channel if (paired_channel >= 0 && paired_channel < CHANNELS && !channel_config[paired_channel].is_voltage) prms[i] = (vrms[i] / average_time_interval) * (irms[paired_channel] / average_time_interval); } } // Display or log the results //display_results(vrms, irms, prms); // Move the tail forward by the window size ring_buffer_tail = (ring_buffer_tail + WINDOW_SIZE) % RING_BUFFER_LEN; absolute_time_t end_time = get_absolute_time(); int64_t processing_time_us = absolute_time_diff_us(start_time, end_time); total_samples_processed += WINDOW_SIZE; // Total number of 128bit samples processed total_processing_time_us += processing_time_us;}void process_adc_data() { // Check if there are enough buffers for processing uint buffer_count = (ring_buffer_head + RING_BUFFER_LEN - ring_buffer_tail) % RING_BUFFER_LEN; if (buffer_count >= WINDOW_SIZE) process_accumulated_data();}void setup_gpio() { gpio_init(PIN_RESET); gpio_set_dir(PIN_RESET, GPIO_OUT); gpio_init(PIN_CONVST); gpio_set_dir(PIN_CONVST, GPIO_OUT); gpio_put(PIN_CONVST, 0); // Set the pin to low initially // gpio_init(PIN_BUSY); // gpio_set_dir(PIN_BUSY, GPIO_IN); // gpio_pull_up(PIN_BUSY);}void setup() { setup_gpio(); // Load the PIO program into the PIO memory uint offset = pio_add_program(pio, &ad7606_spi_128bit_read_program); // Initialize the PIO and state machine init_pio_spi(pio, sm, offset, PIN_SCK, PIN_MISO, PIN_CS, PIN_BUSY, PIO_FREQ); // Initialize DMA for data transfer dma_chan = setup_dma(); // Initialize PWM for CONVST setup_pwm_for_convst(PIN_CONVST, PWM_FREQ, 96);}int main() { sleep_ms(100); stdio_init_all(); printf("\nhello\n"); sleep_ms(3000); printf("init\n"); absolute_time_t last_metric_time = get_absolute_time(); int64_t timediff; setup(); //dma_channel_start(dma_chan); while (true) { process_adc_data(); //tight_loop_contents(); // Check if one second has passed timediff = absolute_time_diff_us(last_metric_time, get_absolute_time()); //if(pulsecount % 1000 == 0) // pio_status_check(); sleep_ms(10); if (timediff > 1000000) { // Display performance metrics //display_throughput_metrics(); //display_buffer_utilization(); if(pulsecount || dmacounter || piocount) { /* for (int i = 0; i < CHANNELS; i++) { printf("%u ", databuffer[i]); ///65535 * 2 * REF } printf(" dma handler %d %u %u\n", dmacounter, ring_buffer_head, ring_buffer_tail); */ //printf("dma: %d %f/s, pio: %d %f/s, fifo: %d %d of: %d %u %u\n", dmacounter, dmacounter/0.5, piocount, piocount/0.5, pio_sm_is_rx_fifo_empty(pio, sm), pio_sm_is_tx_fifo_empty(pio, sm), overflow_flag, ring_buffer_head, ring_buffer_tail); pulsecount = 0; dmacounter = 0; piocount = 0; } // Reset the timer last_metric_time = get_absolute_time(); // Reset cumulative metrics if necessary //total_samples_processed = 0; //total_processing_time_us = 0; } } return 0;}
Statistics: Posted by gaby_64 — Sat Jan 27, 2024 6:24 am