116 lines
4.6 KiB
C
116 lines
4.6 KiB
C
/**
|
|
* 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() {
|
|
#ifndef uart_default
|
|
#warning dma/control_blocks example requires a UART
|
|
#else
|
|
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 * uart_get_index(uart_default));
|
|
// 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,
|
|
&uart_get_hw(uart_default)->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.");
|
|
#endif
|
|
}
|