Quantcast
Channel: Raspberry Pi Forums
Viewing all articles
Browse latest Browse all 4763

SDK • Re: ADC PIO 128bit read, DMA gets stuck after 200 transfers

$
0
0
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.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);}%}
main.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



Viewing all articles
Browse latest Browse all 4763

Trending Articles