Coder Social home page Coder Social logo

Comments (6)

biemster avatar biemster commented on August 22, 2024

I'm implementing exactly this in a ringbuffer in https://github.com/biemster/pico-I2S, but it is not working yet.

from pico-examples.

ajf58 avatar ajf58 commented on August 22, 2024

@jonathangjertsen , could you provide a little more detail on what you'd like to see in an example? For example, where does the data come from that's being scattered to the two buffers? Does completion of the processing trigger the next DMA transfer?

from pico-examples.

jonathangjertsen avatar jonathangjertsen commented on August 22, 2024

@ajf58

A typical example is continuously capturing ADC data at very high speed without any gaps. There is an example of using the DMA and ADC together, but it's just a one-shot.

I like this figure from TI's various processor datasheets (in this case taken from the TM4C1294NCPDT). I think it uses some generic ARM component for the DMA which may or may not be similar to the one used by the Pico

udma

from pico-examples.

ajf58 avatar ajf58 commented on August 22, 2024

Thanks.

One approach to alternating between the two ping and pong buffers is to reconfigure the DMA channel from the IRQ handler once one buffer is full. For example, modifying the example in dma/channel_irq/channel_irq.c, we can can demonstrate ping-ponging between two buffers, allowing the on chip temperature to be oversampled and averaged. N.b. I've not dug into the RP2040 datasheet to determine whether this oversampling and averaging would have the desired effect of improving the resolution in the temperature reading, but it's a useful backdrop for the problem.

#include <stdio.h>
#include <stdint.h>
#include "pico/stdlib.h"

#include "hardware/adc.h"
#include "hardware/dma.h"
#include "hardware/irq.h"
#include "hardware/timer.h"

#define CAPTURE_DEPTH 256

// Replace capture_buf[CAPTURE_DEPTH] with a ping and pong register
uint16_t ping[CAPTURE_DEPTH];
uint16_t pong[CAPTURE_DEPTH];

bool write_ping = true;
int dma_chan;
// Written to in the IRQ context, read from the main loop.
volatile uint32_t sum = 0;

void dma_handler() {
    // Clear the interrupt request.
    dma_hw->ints0 = 1u << dma_chan;

    void *write_addr;
    uint16_t *read_addr;
    if (write_ping) {
        // Ping was being written to, and has completed. Now read from ping, and write to pong.
        write_ping = false;
        read_addr = ping;
        write_addr = pong;
    } else {
        write_ping = true;
        read_addr = pong;
        write_addr = ping;
    }
    // Kick off the next transfer.
    dma_channel_set_write_addr(dma_chan, write_addr, true);
    // Process what's in the read buffer.
    uint32_t temp = 0;
    for (size_t i = 0; i < CAPTURE_DEPTH; i++) {
        // Mask the bottom 12 bits.
        temp += read_addr[i] & 0xFFF;
    }
    // Oversampled, so divide down.
    temp /= CAPTURE_DEPTH;

    // Update.
    sum = temp;
}

int main()
{
    stdio_init_all();
#ifdef PICO_DEFAULT_LED_PIN
    gpio_init(PICO_DEFAULT_LED_PIN);
    gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
#endif

    /* Initialize hardware AD converter, enable onboard temperature sensor and
     *   select its channel (do this once for efficiency, but beware that this
     *   is a global operation). */
    adc_init();
    adc_set_temp_sensor_enabled(true);
    adc_select_input(4);

    adc_fifo_setup(
        true,    // Write each completed conversion to the sample FIFO
        true,    // Enable DMA data request (DREQ)
        1,       // DREQ (and IRQ) asserted when at least 1 sample present
        false,
        false    // Don't shift, masking values is handled in dma_handler()'s processing of the read buffer.
    );

    adc_set_clkdiv(0);

    sleep_ms(1000);
    printf("Arming DMA\n");
    // Set up the DMA to start transferring data as soon as it appears in FIFO
    dma_chan = dma_claim_unused_channel(true);
    dma_channel_config cfg = dma_channel_get_default_config(dma_chan);

    // Reading from constant address, writing to incrementing byte addresses
    channel_config_set_transfer_data_size(&cfg, DMA_SIZE_16);
    channel_config_set_read_increment(&cfg, false);
    channel_config_set_write_increment(&cfg, true);

    // Pace transfers based on availability of ADC samples
    channel_config_set_dreq(&cfg, DREQ_ADC);

    dma_channel_configure(dma_chan, &cfg,
        ping,    // dst
        &adc_hw->fifo,  // src
        CAPTURE_DEPTH,  // transfer count
        false           // do not start, wait for dma_handler() to be called
    );

    printf("Starting capture\n");
    adc_run(true);

    // 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_handler);
    irq_set_enabled(DMA_IRQ_0, true);

    // Manually call the handler once, to trigger the first transfer
    dma_handler();

    float old_temp = 0;
    while (true) {
        /* 12-bit conversion, assume max value == ADC_VREF == 3.3 V */
        const float conversionFactor = 3.3f / (1 << 12);

        float adc = (float)  sum * conversionFactor;
        float tempC = 27.0f - (adc - 0.706f) / 0.001721f;
        if (old_temp != tempC) {
            printf("Onboard temperature = %.03f %c\n", tempC, TEMPERATURE_UNITS);
        }
        old_temp = tempC;

#ifdef PICO_DEFAULT_LED_PIN
        gpio_put(PICO_DEFAULT_LED_PIN, 1);
        sleep_ms(10);
        gpio_put(PICO_DEFAULT_LED_PIN, 0);
#endif
        sleep_ms(490);
    }

    return 0;
}

Some example output (yup, reading the first uninitialized data gives a nonsense value):

Arming DMA
Starting capture
Onboard temperature = 437.227 C
Onboard temperature = 14.499 C
Onboard temperature = 14.967 C
Onboard temperature = 15.435 C

from pico-examples.

kevinjwalters avatar kevinjwalters commented on August 22, 2024

There's a good example of DMA ping-pong writing to two buffers from ADC in https://github.com/zapta/simple_stepper_motor_analyzer/blob/main/platformio/src/acquisition/adc_dma.cpp

There's a messier examlple in https://github.com/kevinjwalters/galactic-bluetooth-audio/blob/spectrogram-tuner-dmaadc/uad/audiosourcedmaadc.cpp

from pico-examples.

wodr avatar wodr commented on August 22, 2024

Here is an other example without interrupt handler in python: https://github.com/wodr/Pico/blob/main/DmaRingBuffer/DmaRingBufferDoubleDma.py

from pico-examples.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.