Initial Release

This commit is contained in:
graham sanderson
2021-01-20 10:49:34 -06:00
commit 46078742c7
245 changed files with 21157 additions and 0 deletions

5
dma/CMakeLists.txt Normal file
View File

@@ -0,0 +1,5 @@
if (NOT PICO_NO_HARDWARE)
add_subdirectory(channel_irq)
add_subdirectory(control_blocks)
add_subdirectory(hello_dma)
endif ()

View File

@@ -0,0 +1,18 @@
add_executable(dma_channel_irq
channel_irq.c
)
pico_generate_pio_header(dma_channel_irq ${CMAKE_CURRENT_LIST_DIR}/pio_serialiser.pio)
target_link_libraries(dma_channel_irq
pico_stdlib
hardware_dma
hardware_irq
hardware_pio
)
# create map/bin/hex file etc.
pico_add_extra_outputs(dma_channel_irq)
# add url via pico_set_program_url
example_auto_set_url(dma_channel_irq)

View File

@@ -0,0 +1,91 @@
/**
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
// Show how to reconfigure and restart a channel in a channel completion
// interrupt handler.
//
// Our DMA channel will transfer data to a PIO state machine, which is
// configured to serialise the raw bits that we push, one by one. We're going
// to use this to do some crude LED PWM by repeatedly sending values with the
// right balance of 1s and 0s. (note there are better ways to do PWM with PIO
// -- see the PIO PWM example).
//
// Once the channel has sent a predetermined amount of data, it will halt, and
// raise an interrupt flag. The processor will enter the interrupt handler in
// response to this, where it will reconfigure and restart the channel. This
// repeats.
#include <stdio.h>
#include "hardware/dma.h"
#include "hardware/irq.h"
#include "pio_serialiser.pio.h"
// PIO sends one bit per 10 system clock cycles. DMA sends the same 32-bit
// value 10 000 times before halting. This means we cycle through the 32 PWM
// levels roughly once per second.
#define PIO_SERIAL_CLKDIV 10.f
#define PWM_REPEAT_COUNT 10000
#define N_PWM_LEVELS 32
int dma_chan;
void dma_handler() {
static int pwm_level = 0;
static uint32_t wavetable[N_PWM_LEVELS];
static bool first_run = true;
// Entry number `i` has `i` one bits and `(32 - i)` zero bits.
if (first_run) {
first_run = false;
for (int i = 0; i < N_PWM_LEVELS; ++i)
wavetable[i] = ~(~0u << i);
}
// Clear the interrupt request.
dma_hw->ints0 = 1u << dma_chan;
// Give the channel a new wave table entry to read from, and re-trigger it
dma_channel_set_read_addr(dma_chan, &wavetable[pwm_level], true);
pwm_level = (pwm_level + 1) % N_PWM_LEVELS;
}
int main() {
// Set up a PIO state machine to serialise our bits
uint offset = pio_add_program(pio0, &pio_serialiser_program);
pio_serialiser_program_init(pio0, 0, offset, PICO_DEFAULT_LED_PIN, PIO_SERIAL_CLKDIV);
// Configure a channel to write the same word (32 bits) repeatedly to PIO0
// SM0's TX FIFO, paced by the data request signal from that peripheral.
dma_chan = dma_claim_unused_channel(true);
dma_channel_config c = dma_channel_get_default_config(dma_chan);
channel_config_set_transfer_data_size(&c, DMA_SIZE_32);
channel_config_set_read_increment(&c, false);
channel_config_set_dreq(&c, DREQ_PIO0_TX0);
dma_channel_configure(
dma_chan,
&c,
&pio0_hw->txf[0], // Write address (only need to set this once)
NULL, // Don't provide a read address yet
PWM_REPEAT_COUNT, // Write the same value many times, then halt and interrupt
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_handler);
irq_set_enabled(DMA_IRQ_0, true);
// Manually call the handler once, to trigger the first transfer
dma_handler();
// Everything else from this point is interrupt-driven. The processor has
// time to sit and think about its early retirement -- maybe open a bakery?
while (true)
tight_loop_contents();
}

View File

@@ -0,0 +1,27 @@
;
; Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
;
; SPDX-License-Identifier: BSD-3-Clause
;
.program pio_serialiser
; Just serialise a stream of bits. Take 32 bits from each FIFO record. LSB-first.
.wrap_target
out pins, 1
.wrap
% c-sdk {
static inline void pio_serialiser_program_init(PIO pio, uint sm, uint offset, uint data_pin, float clk_div) {
pio_gpio_init(pio, data_pin);
pio_sm_set_consecutive_pindirs(pio, sm, data_pin, 1, true);
pio_sm_config c = pio_serialiser_program_get_default_config(offset);
sm_config_set_out_pins(&c, data_pin, 1);
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
sm_config_set_clkdiv(&c, clk_div);
sm_config_set_out_shift(&c, true, true, 32);
pio_sm_init(pio, sm, offset, &c);
pio_sm_set_enabled(pio, sm, true);
}
%}

View File

@@ -0,0 +1,11 @@
add_executable(dma_control_blocks
control_blocks.c
)
target_link_libraries(dma_control_blocks pico_stdlib hardware_dma)
# create map/bin/hex file etc.
pico_add_extra_outputs(dma_control_blocks)
# add url via pico_set_program_url
example_auto_set_url(dma_control_blocks)

View File

@@ -0,0 +1,111 @@
/**
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
// Use two DMA channels to make a programmed sequence of data transfers to the
// UART (a data gather operation). One channel is responsible for transferring
// the actual data, the other repeatedly reprograms that channel.
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/dma.h"
#include "hardware/structs/uart.h"
// These buffers will be DMA'd to the UART, one after the other.
const char word0[] = "Transferring ";
const char word1[] = "one ";
const char word2[] = "word ";
const char word3[] = "at ";
const char word4[] = "a ";
const char word5[] = "time.\n";
// Note the order of the fields here: it's important that the length is before
// the read address, because the control channel is going to write to the last
// two registers in alias 3 on the data channel:
// +0x0 +0x4 +0x8 +0xC (Trigger)
// Alias 0: READ_ADDR WRITE_ADDR TRANS_COUNT CTRL
// Alias 1: CTRL READ_ADDR WRITE_ADDR TRANS_COUNT
// Alias 2: CTRL TRANS_COUNT READ_ADDR WRITE_ADDR
// Alias 3: CTRL WRITE_ADDR TRANS_COUNT READ_ADDR
//
// This will program the transfer count and read address of the data channel,
// and trigger it. Once the data channel completes, it will restart the
// control channel (via CHAIN_TO) to load the next two words into its control
// registers.
const struct {uint32_t len; const char *data;} control_blocks[] = {
{count_of(word0) - 1, word0}, // Skip null terminator
{count_of(word1) - 1, word1},
{count_of(word2) - 1, word2},
{count_of(word3) - 1, word3},
{count_of(word4) - 1, word4},
{count_of(word5) - 1, word5},
{0, NULL} // Null trigger to end chain.
};
int main() {
stdio_init_all();
puts("DMA control block example:");
// ctrl_chan loads control blocks into data_chan, which executes them.
int ctrl_chan = dma_claim_unused_channel(true);
int data_chan = dma_claim_unused_channel(true);
// The control channel transfers two words into the data channel's control
// registers, then halts. The write address wraps on a two-word
// (eight-byte) boundary, so that the control channel writes the same two
// registers when it is next triggered.
dma_channel_config c = dma_channel_get_default_config(ctrl_chan);
channel_config_set_transfer_data_size(&c, DMA_SIZE_32);
channel_config_set_read_increment(&c, true);
channel_config_set_write_increment(&c, true);
channel_config_set_ring(&c, true, 3); // 1 << 3 byte boundary on write ptr
dma_channel_configure(
ctrl_chan,
&c,
&dma_hw->ch[data_chan].al3_transfer_count, // Initial write address
&control_blocks[0], // Initial read address
2, // Halt after each control block
false // Don't start yet
);
// The data channel is set up to write to the UART FIFO (paced by the
// UART's TX data request signal) and then chain to the control channel
// once it completes. The control channel programs a new read address and
// data length, and retriggers the data channel.
c = dma_channel_get_default_config(data_chan);
channel_config_set_transfer_data_size(&c, DMA_SIZE_8);
channel_config_set_dreq(&c, DREQ_UART0_TX + 2 * PICO_DEFAULT_UART);
// Trigger ctrl_chan when data_chan completes
channel_config_set_chain_to(&c, ctrl_chan);
// Raise the IRQ flag when 0 is written to a trigger register (end of chain):
channel_config_set_irq_quiet(&c, true);
dma_channel_configure(
data_chan,
&c,
&(PICO_DEFAULT_UART ? uart1_hw : uart0_hw)->dr,
NULL, // Initial read address and transfer count are unimportant;
0, // the control channel will reprogram them each time.
false // Don't start yet.
);
// Everything is ready to go. Tell the control channel to load the first
// control block. Everything is automatic from here.
dma_start_channel_mask(1u << ctrl_chan);
// The data channel will assert its IRQ flag when it gets a null trigger,
// indicating the end of the control block list. We're just going to wait
// for the IRQ flag instead of setting up an interrupt handler.
while (!(dma_hw->intr & 1u << data_chan))
tight_loop_contents();
dma_hw->ints0 = 1u << data_chan;
puts("DMA finished.");
}

View File

@@ -0,0 +1,11 @@
add_executable(hello_dma
hello_dma.c
)
target_link_libraries(hello_dma pico_stdlib hardware_dma)
# create map/bin/hex file etc.
pico_add_extra_outputs(hello_dma)
# add url via pico_set_program_url
example_auto_set_url(hello_dma)

49
dma/hello_dma/hello_dma.c Normal file
View File

@@ -0,0 +1,49 @@
/**
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
// Use the DMA to copy data between two buffers in memory.
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/dma.h"
// Data will be copied from src to dst
const char src[] = "Hello, world! (from DMA)";
char dst[count_of(src)];
int main() {
stdio_init_all();
// Get a free channel, panic() if there are none
int chan = dma_claim_unused_channel(true);
// 8 bit transfers. Both read and write address increment after each
// transfer (each pointing to a location in src or dst respectively).
// No DREQ is selected, so the DMA transfers as fast as it can.
dma_channel_config c = dma_channel_get_default_config(chan);
channel_config_set_transfer_data_size(&c, DMA_SIZE_8);
channel_config_set_read_increment(&c, true);
channel_config_set_write_increment(&c, true);
dma_channel_configure(
chan, // Channel to be configured
&c, // The configuration we just created
dst, // The initial write address
src, // The initial read address
count_of(src), // Number of transfers; in this case each is 1 byte.
true // Start immediately.
);
// We could choose to go and do something else whilst the DMA is doing its
// thing. In this case the processor has nothing else to do, so we just
// wait for the DMA to finish.
dma_channel_wait_for_finish_blocking(chan);
// The DMA has now copied our text from the transmit buffer (src) to the
// receive buffer (dst), so we can print it out from there.
puts(dst);
}