# Atmel SAMD21 DMA Transfer Complete Interrupt not firing

I have configured the SAMD21 peripherals so that incoming audio data from I2S will be copied into a memory buffer with DMA. As the controller allows linked DMA descriptors, I have two descriptors linked back to back, each writing to a different buffer.

When a block transfer has come to its end, BTCNT has reached zero, the Valid bit in the Block Transfer Control register will be written to zero in the internal transfer descriptor for the active channel before the entire transfer descriptor is written to the write-back memory. The optional interrupts, Channel Transfer Complete and Channel Suspend, and the optional output event, Block, will be generated if configured and enabled. If it was the last block transfer in a transaction, Next Address (DESCADDR) register will hold the value 0x00000000, and the DMA channel will either be suspended or disabled, depending on the configuration in the Block Action bit group in the Block Transfer Control register(BTCTRL.BLOCKACT)

This excerpt from the datasheet coupled with the image suggests that the interrupt will be fired after each block transfer, implying the ISR being executed each time the next linked descriptor is loaded. But in my configuration, the ISR doesn't execute at all. After I configured the linked chain to have a termination, the ISR fired after the block transfer of the chain. It seems like the interrupt fires only after a complete transcation , not after each block transfer as the datasheet suggests.

Am I interpreting the datasheet wrong, is there a way to get the ISR routine to execute at each link?

static void _config_dma_for_rx(void)
{
struct dma_resource_config config;
dma_get_config_defaults(&config);

config.trigger_action = DMA_TRIGGER_ACTION_BEAT;
config.peripheral_trigger = CONF_RX_TRIGGER;

dma_allocate(&rx_dma_resource, &config);

// Configure DMA RX Descriptor A
struct dma_descriptor_config descriptor_config;

dma_descriptor_get_config_defaults(&descriptor_config);

descriptor_config.block_transfer_count = 32;
descriptor_config.beat_size = DMA_BEAT_SIZE_HWORD;
descriptor_config.step_selection = DMA_STEPSEL_SRC;
descriptor_config.src_increment_enable = false;
descriptor_config.destination_address = (uint32_t)rx_values_a+ sizeof(rx_values_a);
descriptor_config.source_address = (uint32_t)&CONF_I2S_MODULE->DATA[1];

dma_descriptor_create(&rx_dma_descriptor_a, &descriptor_config);

rx_dma_descriptor_a.DESCADDR.reg = 0;// (uint32_t)&rx_dma_descriptor_b;

// Configure DMA RX Descriptor B

dma_descriptor_get_config_defaults(&descriptor_config);

descriptor_config.block_transfer_count = 32;
descriptor_config.beat_size = DMA_BEAT_SIZE_HWORD;
descriptor_config.step_selection = DMA_STEPSEL_SRC;
descriptor_config.src_increment_enable = false;
descriptor_config.destination_address = (uint32_t)rx_values_b +  sizeof(rx_values_b);
descriptor_config.source_address = (uint32_t)&CONF_I2S_MODULE->DATA[1];

dma_descriptor_create(&rx_dma_descriptor_b, &descriptor_config);

rx_dma_descriptor_b.DESCADDR.reg = (uint32_t)&rx_dma_descriptor_a;

dma_add_descriptor(&rx_dma_resource, &rx_dma_descriptor_a);

dma_register_callback(&rx_dma_resource, rx_done_callback, DMA_CALLBACK_TRANSFER_DONE);
dma_enable_callback(&rx_dma_resource, DMA_CALLBACK_TRANSFER_DONE);

dma_start_transfer_job(&rx_dma_resource);
}


## 1 Answer

The block transfer control register has a field BLOCKACT that indicates the action to take after each block. After configuring that to INT (Channel in normal operation and block interrupt) the DMA kept operating continuously, while triggering the ISR at end of each block transfer. I had to add the following line to the configuration of each of the two descriptors.

descriptor_config.block_action = DMA_BLOCK_ACTION_INT;