112 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			112 lines
		
	
	
		
			4.5 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() {
 | 
						|
    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.");
 | 
						|
}
 |