Skip to content

Commit 40dc8e2

Browse files
committed
Add ADC DMA example
1 parent 4607874 commit 40dc8e2

File tree

4 files changed

+195
-0
lines changed

4 files changed

+195
-0
lines changed

adc/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
if (NOT PICO_NO_HARDWARE)
22
add_subdirectory(adc_console)
3+
add_subdirectory(dma_capture)
34
add_subdirectory(hello_adc)
45
add_subdirectory(joystick_display)
56
endif ()

adc/dma_capture/CMakeLists.txt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
add_executable(adc_dma_capture
2+
dma_capture.c
3+
)
4+
5+
pico_generate_pio_header(adc_dma_capture ${CMAKE_CURRENT_LIST_DIR}/resistor_dac.pio)
6+
7+
target_link_libraries(adc_dma_capture
8+
pico_stdlib
9+
hardware_adc
10+
hardware_dma
11+
# For the dummy output:
12+
hardware_pio
13+
pico_multicore
14+
)
15+
16+
# create map/bin/hex file etc.
17+
pico_add_extra_outputs(adc_dma_capture)
18+
19+
# add url via pico_set_program_url
20+
example_auto_set_url(adc_dma_capture)

adc/dma_capture/dma_capture.c

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
/**
2+
* Copyright (c) 2021 Raspberry Pi (Trading) Ltd.
3+
*
4+
* SPDX-License-Identifier: BSD-3-Clause
5+
*/
6+
7+
#include <stdio.h>
8+
#include "pico/stdlib.h"
9+
// For ADC input:
10+
#include "hardware/adc.h"
11+
#include "hardware/dma.h"
12+
// For resistor DAC output:
13+
#include "pico/multicore.h"
14+
#include "hardware/pio.h"
15+
#include "resistor_dac.pio.h"
16+
17+
// This example uses the DMA to capture many samples from the ADC.
18+
//
19+
// - We are putting the ADC in free-running capture mode at 0.5 Msps
20+
//
21+
// - A DMA channel will be attached to the ADC sample FIFO
22+
//
23+
// - Configure the ADC to right-shift samples to 8 bits of significance, so we
24+
// can DMA into a byte buffer
25+
//
26+
// This could be extended to use the ADC's round robin feature to sample two
27+
// channels concurrently at 0.25 Msps each.
28+
//
29+
// It would be nice to have some analog samples to measure! This example also
30+
// drives waves out through a 5-bit resistor DAC, as found on the reference
31+
// VGA board. If you have that board, you can take an M-F jumper wire from
32+
// GPIO 26 to the Green pin on the VGA connector (top row, next-but-rightmost
33+
// hole). Or you can ignore that part of the code and connect your own signal
34+
// to the ADC input.
35+
36+
// Channel 0 is GPIO26
37+
#define CAPTURE_CHANNEL 0
38+
#define CAPTURE_DEPTH 1000
39+
40+
uint8_t capture_buf[CAPTURE_DEPTH];
41+
42+
void core1_main();
43+
44+
int main() {
45+
stdio_init_all();
46+
47+
// Send core 1 off to start driving the "DAC" whilst we configure the ADC.
48+
multicore_launch_core1(core1_main);
49+
50+
// Init GPIO for analogue use: hi-Z, no pulls, disable digital input buffer.
51+
adc_gpio_init(26 + CAPTURE_CHANNEL);
52+
53+
adc_init();
54+
adc_select_input(CAPTURE_CHANNEL);
55+
adc_fifo_setup(
56+
true, // Write each completed conversion to the sample FIFO
57+
true, // Enable DMA data request (DREQ)
58+
1, // DREQ (and IRQ) asserted when at least 1 sample present
59+
false, // We won't see the ERR bit because of 8 bit reads; disable.
60+
true // Shift each sample to 8 bits when pushing to FIFO
61+
);
62+
63+
// Divisor of 0 -> full speed. Free-running capture with the divider is
64+
// equivalent to pressing the ADC_CS_START_ONCE button once per `div + 1`
65+
// cycles (div not necessarily an integer). Each conversion takes 96
66+
// cycles, so in general you want a divider of 0 (hold down the button
67+
// continuously) or > 95 (take samples less frequently than 96 cycle
68+
// intervals). This is all timed by the 48 MHz ADC clock.
69+
adc_set_clkdiv(0);
70+
71+
printf("Arming DMA\n");
72+
sleep_ms(1000);
73+
// Set up the DMA to start transferring data as soon as it appears in FIFO
74+
uint dma_chan = dma_claim_unused_channel(true);
75+
dma_channel_config cfg = dma_channel_get_default_config(dma_chan);
76+
77+
// Reading from constant address, writing to incrementing byte addresses
78+
channel_config_set_transfer_data_size(&cfg, DMA_SIZE_8);
79+
channel_config_set_read_increment(&cfg, false);
80+
channel_config_set_write_increment(&cfg, true);
81+
82+
// Pace transfers based on availability of ADC samples
83+
channel_config_set_dreq(&cfg, DREQ_ADC);
84+
85+
dma_channel_configure(dma_chan, &cfg,
86+
capture_buf, // dst
87+
&adc_hw->fifo, // src
88+
CAPTURE_DEPTH, // transfer count
89+
true // start immediately
90+
);
91+
92+
printf("Starting capture\n");
93+
adc_run(true);
94+
95+
// Once DMA finishes, stop any new conversions from starting, and clean up
96+
// the FIFO in case the ADC was still mid-conversion.
97+
dma_channel_wait_for_finish_blocking(dma_chan);
98+
printf("Capture finished\n");
99+
adc_run(false);
100+
adc_fifo_drain();
101+
102+
// Print samples to stdout so you can display them in pyplot, excel, matlab
103+
for (int i = 0; i < CAPTURE_DEPTH; ++i) {
104+
printf("%-3d, ", capture_buf[i]);
105+
if (i % 10 == 9)
106+
printf("\n");
107+
}
108+
}
109+
110+
// ----------------------------------------------------------------------------
111+
// Code for driving the "DAC" output for us to measure
112+
113+
// Core 1 is just going to sit and drive samples out continously. PIO provides
114+
// consistent sample frequency.
115+
116+
#define OUTPUT_FREQ_KHZ 5
117+
#define SAMPLE_WIDTH 5
118+
// This is the green channel on the VGA board
119+
#define DAC_PIN_BASE 6
120+
121+
void core1_main() {
122+
PIO pio = pio0;
123+
uint sm = pio_claim_unused_sm(pio0, true);
124+
uint offset = pio_add_program(pio0, &resistor_dac_5bit_program);
125+
resistor_dac_5bit_program_init(pio0, sm, offset,
126+
OUTPUT_FREQ_KHZ * 1000 * 2 * (1 << SAMPLE_WIDTH), DAC_PIN_BASE);
127+
while (true) {
128+
// Triangle wave
129+
for (int i = 0; i < (1 << SAMPLE_WIDTH); ++i)
130+
pio_sm_put_blocking(pio, sm, i);
131+
for (int i = 0; i < (1 << SAMPLE_WIDTH); ++i)
132+
pio_sm_put_blocking(pio, sm, (1 << SAMPLE_WIDTH) - 1 - i);
133+
}
134+
}
135+
136+

adc/dma_capture/resistor_dac.pio

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
;
2+
; Copyright (c) 2021 Raspberry Pi (Trading) Ltd.
3+
;
4+
; SPDX-License-Identifier: BSD-3-Clause
5+
;
6+
7+
.program resistor_dac_5bit
8+
9+
; Drive one of the 5-bit resistor DACs on the VGA reference board. (this isn't
10+
; a good way to do VGA -- just want a nice sawtooth for the ADC example!)
11+
12+
out pins, 5
13+
14+
15+
16+
% c-sdk {
17+
#include "hardware/clocks.h"
18+
static inline void resistor_dac_5bit_program_init(PIO pio, uint sm, uint offset,
19+
uint sample_rate_hz, uint pin_base) {
20+
21+
pio_sm_set_pins_with_mask(pio, sm, 0, 0x1fu << pin_base);
22+
pio_sm_set_pindirs_with_mask(pio, sm, ~0u, 0x1fu << pin_base);
23+
for (int i = 0; i < 5; ++i)
24+
pio_gpio_init(pio, pin_base + i);
25+
26+
pio_sm_config c = resistor_dac_5bit_program_get_default_config(offset);
27+
sm_config_set_out_pins(&c, pin_base, 5);
28+
// Shift to right, autopull threshold 5
29+
sm_config_set_out_shift(&c, true, true, 5);
30+
// Deeper FIFO as we're not doing any RX
31+
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
32+
float div = (float)clock_get_hz(clk_sys) / sample_rate_hz;
33+
sm_config_set_clkdiv(&c, div);
34+
35+
pio_sm_init(pio, sm, offset, &c);
36+
pio_sm_set_enabled(pio, sm, true);
37+
}
38+
%}

0 commit comments

Comments
 (0)