text: /**
   * 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.");
  }
  