Add ADC DMA example
This commit is contained in:
parent
46078742c7
commit
40dc8e2b17
@ -1,5 +1,6 @@
|
||||
if (NOT PICO_NO_HARDWARE)
|
||||
add_subdirectory(adc_console)
|
||||
add_subdirectory(dma_capture)
|
||||
add_subdirectory(hello_adc)
|
||||
add_subdirectory(joystick_display)
|
||||
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