
// ****************************************************************************
//
//                                 Main code
//
// ****************************************************************************
// 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.

/*
Debug output
------------
DMA control block example:
Transferring one word at a time.
DMA finished.
*/

#include "include.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();

	// wait to connect terminal
	int ch;
	do {
		printf("Press spacebar to start...\n");
		ch = getchar();
	} while (ch != ' ');

	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.");

	return 0;
}
