Add ADC DMA example
This commit is contained in:
parent
46078742c7
commit
40dc8e2b17
@ -1,5 +1,6 @@
|
|||||||
if (NOT PICO_NO_HARDWARE)
|
if (NOT PICO_NO_HARDWARE)
|
||||||
add_subdirectory(adc_console)
|
add_subdirectory(adc_console)
|
||||||
|
add_subdirectory(dma_capture)
|
||||||
add_subdirectory(hello_adc)
|
add_subdirectory(hello_adc)
|
||||||
add_subdirectory(joystick_display)
|
add_subdirectory(joystick_display)
|
||||||
endif ()
|
endif ()
|
||||||
|
|||||||
20
adc/dma_capture/CMakeLists.txt
Normal file
20
adc/dma_capture/CMakeLists.txt
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
add_executable(adc_dma_capture
|
||||||
|
dma_capture.c
|
||||||
|
)
|
||||||
|
|
||||||
|
pico_generate_pio_header(adc_dma_capture ${CMAKE_CURRENT_LIST_DIR}/resistor_dac.pio)
|
||||||
|
|
||||||
|
target_link_libraries(adc_dma_capture
|
||||||
|
pico_stdlib
|
||||||
|
hardware_adc
|
||||||
|
hardware_dma
|
||||||
|
# For the dummy output:
|
||||||
|
hardware_pio
|
||||||
|
pico_multicore
|
||||||
|
)
|
||||||
|
|
||||||
|
# create map/bin/hex file etc.
|
||||||
|
pico_add_extra_outputs(adc_dma_capture)
|
||||||
|
|
||||||
|
# add url via pico_set_program_url
|
||||||
|
example_auto_set_url(adc_dma_capture)
|
||||||
136
adc/dma_capture/dma_capture.c
Normal file
136
adc/dma_capture/dma_capture.c
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2021 Raspberry Pi (Trading) Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "pico/stdlib.h"
|
||||||
|
// For ADC input:
|
||||||
|
#include "hardware/adc.h"
|
||||||
|
#include "hardware/dma.h"
|
||||||
|
// For resistor DAC output:
|
||||||
|
#include "pico/multicore.h"
|
||||||
|
#include "hardware/pio.h"
|
||||||
|
#include "resistor_dac.pio.h"
|
||||||
|
|
||||||
|
// This example uses the DMA to capture many samples from the ADC.
|
||||||
|
//
|
||||||
|
// - We are putting the ADC in free-running capture mode at 0.5 Msps
|
||||||
|
//
|
||||||
|
// - A DMA channel will be attached to the ADC sample FIFO
|
||||||
|
//
|
||||||
|
// - Configure the ADC to right-shift samples to 8 bits of significance, so we
|
||||||
|
// can DMA into a byte buffer
|
||||||
|
//
|
||||||
|
// This could be extended to use the ADC's round robin feature to sample two
|
||||||
|
// channels concurrently at 0.25 Msps each.
|
||||||
|
//
|
||||||
|
// It would be nice to have some analog samples to measure! This example also
|
||||||
|
// drives waves out through a 5-bit resistor DAC, as found on the reference
|
||||||
|
// VGA board. If you have that board, you can take an M-F jumper wire from
|
||||||
|
// GPIO 26 to the Green pin on the VGA connector (top row, next-but-rightmost
|
||||||
|
// hole). Or you can ignore that part of the code and connect your own signal
|
||||||
|
// to the ADC input.
|
||||||
|
|
||||||
|
// Channel 0 is GPIO26
|
||||||
|
#define CAPTURE_CHANNEL 0
|
||||||
|
#define CAPTURE_DEPTH 1000
|
||||||
|
|
||||||
|
uint8_t capture_buf[CAPTURE_DEPTH];
|
||||||
|
|
||||||
|
void core1_main();
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
stdio_init_all();
|
||||||
|
|
||||||
|
// Send core 1 off to start driving the "DAC" whilst we configure the ADC.
|
||||||
|
multicore_launch_core1(core1_main);
|
||||||
|
|
||||||
|
// Init GPIO for analogue use: hi-Z, no pulls, disable digital input buffer.
|
||||||
|
adc_gpio_init(26 + CAPTURE_CHANNEL);
|
||||||
|
|
||||||
|
adc_init();
|
||||||
|
adc_select_input(CAPTURE_CHANNEL);
|
||||||
|
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, // We won't see the ERR bit because of 8 bit reads; disable.
|
||||||
|
true // Shift each sample to 8 bits when pushing to FIFO
|
||||||
|
);
|
||||||
|
|
||||||
|
// Divisor of 0 -> full speed. Free-running capture with the divider is
|
||||||
|
// equivalent to pressing the ADC_CS_START_ONCE button once per `div + 1`
|
||||||
|
// cycles (div not necessarily an integer). Each conversion takes 96
|
||||||
|
// cycles, so in general you want a divider of 0 (hold down the button
|
||||||
|
// continuously) or > 95 (take samples less frequently than 96 cycle
|
||||||
|
// intervals). This is all timed by the 48 MHz ADC clock.
|
||||||
|
adc_set_clkdiv(0);
|
||||||
|
|
||||||
|
printf("Arming DMA\n");
|
||||||
|
sleep_ms(1000);
|
||||||
|
// Set up the DMA to start transferring data as soon as it appears in FIFO
|
||||||
|
uint 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_8);
|
||||||
|
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,
|
||||||
|
capture_buf, // dst
|
||||||
|
&adc_hw->fifo, // src
|
||||||
|
CAPTURE_DEPTH, // transfer count
|
||||||
|
true // start immediately
|
||||||
|
);
|
||||||
|
|
||||||
|
printf("Starting capture\n");
|
||||||
|
adc_run(true);
|
||||||
|
|
||||||
|
// Once DMA finishes, stop any new conversions from starting, and clean up
|
||||||
|
// the FIFO in case the ADC was still mid-conversion.
|
||||||
|
dma_channel_wait_for_finish_blocking(dma_chan);
|
||||||
|
printf("Capture finished\n");
|
||||||
|
adc_run(false);
|
||||||
|
adc_fifo_drain();
|
||||||
|
|
||||||
|
// Print samples to stdout so you can display them in pyplot, excel, matlab
|
||||||
|
for (int i = 0; i < CAPTURE_DEPTH; ++i) {
|
||||||
|
printf("%-3d, ", capture_buf[i]);
|
||||||
|
if (i % 10 == 9)
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Code for driving the "DAC" output for us to measure
|
||||||
|
|
||||||
|
// Core 1 is just going to sit and drive samples out continously. PIO provides
|
||||||
|
// consistent sample frequency.
|
||||||
|
|
||||||
|
#define OUTPUT_FREQ_KHZ 5
|
||||||
|
#define SAMPLE_WIDTH 5
|
||||||
|
// This is the green channel on the VGA board
|
||||||
|
#define DAC_PIN_BASE 6
|
||||||
|
|
||||||
|
void core1_main() {
|
||||||
|
PIO pio = pio0;
|
||||||
|
uint sm = pio_claim_unused_sm(pio0, true);
|
||||||
|
uint offset = pio_add_program(pio0, &resistor_dac_5bit_program);
|
||||||
|
resistor_dac_5bit_program_init(pio0, sm, offset,
|
||||||
|
OUTPUT_FREQ_KHZ * 1000 * 2 * (1 << SAMPLE_WIDTH), DAC_PIN_BASE);
|
||||||
|
while (true) {
|
||||||
|
// Triangle wave
|
||||||
|
for (int i = 0; i < (1 << SAMPLE_WIDTH); ++i)
|
||||||
|
pio_sm_put_blocking(pio, sm, i);
|
||||||
|
for (int i = 0; i < (1 << SAMPLE_WIDTH); ++i)
|
||||||
|
pio_sm_put_blocking(pio, sm, (1 << SAMPLE_WIDTH) - 1 - i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
38
adc/dma_capture/resistor_dac.pio
Normal file
38
adc/dma_capture/resistor_dac.pio
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
;
|
||||||
|
; Copyright (c) 2021 Raspberry Pi (Trading) Ltd.
|
||||||
|
;
|
||||||
|
; SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
;
|
||||||
|
|
||||||
|
.program resistor_dac_5bit
|
||||||
|
|
||||||
|
; Drive one of the 5-bit resistor DACs on the VGA reference board. (this isn't
|
||||||
|
; a good way to do VGA -- just want a nice sawtooth for the ADC example!)
|
||||||
|
|
||||||
|
out pins, 5
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
% c-sdk {
|
||||||
|
#include "hardware/clocks.h"
|
||||||
|
static inline void resistor_dac_5bit_program_init(PIO pio, uint sm, uint offset,
|
||||||
|
uint sample_rate_hz, uint pin_base) {
|
||||||
|
|
||||||
|
pio_sm_set_pins_with_mask(pio, sm, 0, 0x1fu << pin_base);
|
||||||
|
pio_sm_set_pindirs_with_mask(pio, sm, ~0u, 0x1fu << pin_base);
|
||||||
|
for (int i = 0; i < 5; ++i)
|
||||||
|
pio_gpio_init(pio, pin_base + i);
|
||||||
|
|
||||||
|
pio_sm_config c = resistor_dac_5bit_program_get_default_config(offset);
|
||||||
|
sm_config_set_out_pins(&c, pin_base, 5);
|
||||||
|
// Shift to right, autopull threshold 5
|
||||||
|
sm_config_set_out_shift(&c, true, true, 5);
|
||||||
|
// Deeper FIFO as we're not doing any RX
|
||||||
|
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
|
||||||
|
float div = (float)clock_get_hz(clk_sys) / sample_rate_hz;
|
||||||
|
sm_config_set_clkdiv(&c, div);
|
||||||
|
|
||||||
|
pio_sm_init(pio, sm, offset, &c);
|
||||||
|
pio_sm_set_enabled(pio, sm, true);
|
||||||
|
}
|
||||||
|
%}
|
||||||
Reference in New Issue
Block a user