 // but rather a ringbuffer. There is an index to the currently used entry.
 //
 // DMA transfers can be triggered by the FIFO index being at 0 and
-// the FIFO watermark. This code uses these triggers to transfer 4 entries
-// at a time. The lower half of the FIFO (entries 0 - 3) is updated when
-// the FIFO index reaches 4 (the watermark), the upper half (entries 4 - 7)
+// the FIFO watermark. This code uses these triggers to transfer 8 entries
+// at a time. The lower half of the FIFO (entries 0 - 7) is updated when
+// the FIFO index reaches 11 (maximum - 4) (= the watermark), the upper half (entries 8 - 15)
 // is updated when the FIFO index wraps to 0.
 //
 // The FIFO DMA destination is set up using the DMA modulo feature,

static_assert(buffer.size() % 8 == 0, "Buffer size must be multiple of DAC buffer.");

void initBuffer() {
    for(size_t i = 0; i < buffer.size(); i += 4) {
        buffer[i+0] = 0;
        buffer[i+1] = 1000;
        buffer[i+2] = 2000;
        buffer[i+3] = 3000;
    }
}


    DAC0_C0 |= DAC_C0_DACRFS;     // use 3.3V VDDA as reference voltage
    DAC0_C0 |= DAC_C0_DACBWIEN;   // enable DMA trigger at watermark
    DAC0_C0 |= DAC_C0_DACBTIEN;   // enable DMA trigger at 0
    DAC0_C1 = DAC_C1_DACBFWM(3) | // watermark for DMA trigger
                                  // --> DMA triggered when DAC buffer index is 4 words away from DACBFUP, that means at index 11 here
                                  // which gives DMA still enough time to refill the ring buffer from index 0 to 7
              DAC_C1_DMAEN      | // enable DMA
              DAC_C1_DACBFEN;     // enable DAC buffer

    DAC0_C2 = DAC_C2_DACBFUP(15);  // set buffer size to 16

    DAC0_SR &= ~(DAC_SR_DACBFWMF); // clear watermark flag
    DAC0_SR &= ~(DAC_SR_DACBFRTF); // clear top pos flag
    DAC0_SR &= ~(DAC_SR_DACBFRBF); // clear bottom pos flag

    // Init DAC FIFO with the last 16 buffer elements. This makes setting up the circular
    // DMA transfer easier.
    using aliased_uint16 = uint16_t __attribute__((__may_alias__));
    using aliased_uint16_vptr = volatile aliased_uint16*;
    for(size_t i = 0; i < 16; i++) {
        ((aliased_uint16_vptr) &DAC0_DAT0L)[i] = buffer[buffer.size() - 16 + i];
    }

    // The modulo feature of the DMA controller is used. The destination
    // pointer wraps at +16 bytes.
    dma.destinationCircular(((volatile uint16_t*) &DAC0_DAT0L), 32);

    dma.TCD->SADDR = buffer.data();
    dma.TCD->SOFF = 2;
    dma.TCD->ATTR_SRC = 1;
    dma.TCD->NBYTES = 16;
    dma.TCD->SLAST = -buffer_byte_count;
    dma.TCD->BITER = buffer_byte_count / 16;
    dma.TCD->CITER = buffer_byte_count / 16;

    dma.triggerAtHardwareEvent(DMAMUX_SOURCE_DAC0);
    dma.enable();
