/*** *** This Example has no copyright and can be used by anyone. *** The following example is based on Device File Package *** required to compile the macro definitions used. *** The Device File Package is avaiblable by downloading Atmel Studio 7. ***/ /*** Define SPI Channels ***/ #define SPI_TX_CHANNEL 1 #define SPI_RX_CHANNEL 0 /*** Create DMA descriptor in global variable (128-bit aligned) ***/ volatile __attribute__((__aligned__(128))) DmacDescriptor descriptor_SPI[2]; volatile __attribute__((__aligned__(128))) DmacDescriptor write_back_descriptor_SPI[2]; /*** DMA RX Descriptor Initialization step by step *** - Validate the Descriptor *** - No Event out generated by the DMA *** - Channel will be disabled if it is the last block *** transfer in the transaction and block interrupt *** - 8-bit bus transfer (BYTE) *** - No increment from the source address (SRCINC=0) *** - Enable Increment from the destination address (DSTINC=1) *** - Step size settings apply to the Destination address *** - Step Size => Next ADDR = ADDR + (BEATSIZE+1) * 1 *** - DMA_BITCOUNT_VALUE SPI_DATA_SIZE transfer before waking up the core ***/ void DMA_RX_descriptor_init(void) { descriptor_SPI[SPI_RX_CHANNEL].BTCTRL.bit.VALID = 1; descriptor_SPI[SPI_RX_CHANNEL].BTCTRL.bit.EVOSEL = DMAC_BTCTRL_EVOSEL_DISABLE_Val; descriptor_SPI[SPI_RX_CHANNEL].BTCTRL.bit.BLOCKACT = DMAC_BTCTRL_BLOCKACT_INT_Val; descriptor_SPI[SPI_RX_CHANNEL].BTCTRL.bit.BEATSIZE = DMAC_BTCTRL_BEATSIZE_BYTE_Val; descriptor_SPI[SPI_RX_CHANNEL].BTCTRL.bit.SRCINC = 0; descriptor_SPI[SPI_RX_CHANNEL].BTCTRL.bit.DSTINC = 1; descriptor_SPI[SPI_RX_CHANNEL].BTCTRL.bit.STEPSEL = DMAC_BTCTRL_STEPSEL_DST_Val; descriptor_SPI[SPI_RX_CHANNEL].BTCTRL.bit.STEPSIZE = DMAC_BTCTRL_STEPSIZE_X1_Val; descriptor_SPI[SPI_RX_CHANNEL].BTCNT.reg = SPI_DATA_SIZE+1; /*** Set source address and destination address *** source address is the SPI Data register (SERCOM0->SPI.DATA) *** destination address in the RAM is the rx buffer (spi_rx_buffer[SPI_DATA_SIZE]) ***/ descriptor_SPI[SPI_RX_CHANNEL].SRCADDR.reg = (uint32_t) (&(SERCOM0->SPI.DATA)); descriptor_SPI[SPI_RX_CHANNEL].DSTADDR.reg = (uint32_t) (&(spi_rx_buffer[SPI_DATA_SIZE])); /*** Set next transfer descriptor_SPI_TX address ***/ descriptor_SPI[SPI_RX_CHANNEL].DESCADDR.reg = (uint32_t) (&descriptor_SPI[SPI_TX_CHANNEL]); } /*** DMA RX Descriptor Initialization step by step *** - Validate the Descriptor *** - No Event out generated by the DMA *** - Channel will be disabled if it is the last block *** transfer in the transaction and block interrupt *** - 8-bit bus transfer (BYTE) *** - Enable Increment from the the source address (SRCINC=1) *** - No increment from the destination address (DSTINC=0) *** - Step size settings apply to the Source address *** - Step Size => Next ADDR = ADDR + (BEATSIZE+1) * 1 *** - DMA_BITCOUNT_VALUE SPI_DATA_SIZE transfer before waking up the core ***/ void DMA_TX_descriptor_init(void) { descriptor_SPI[SPI_TX_CHANNEL].BTCTRL.bit.VALID = 1; descriptor_SPI[SPI_TX_CHANNEL].BTCTRL.bit.EVOSEL = DMAC_BTCTRL_EVOSEL_DISABLE_Val; descriptor_SPI[SPI_TX_CHANNEL].BTCTRL.bit.BLOCKACT = DMAC_BTCTRL_BLOCKACT_INT_Val; descriptor_SPI[SPI_TX_CHANNEL].BTCTRL.bit.BEATSIZE = DMAC_BTCTRL_BEATSIZE_BYTE_Val; descriptor_SPI[SPI_TX_CHANNEL].BTCTRL.bit.SRCINC = 1; descriptor_SPI[SPI_TX_CHANNEL].BTCTRL.bit.DSTINC = 0; descriptor_SPI[SPI_TX_CHANNEL].BTCTRL.bit.STEPSEL = DMAC_BTCTRL_STEPSEL_SRC_Val; descriptor_SPI[SPI_TX_CHANNEL].BTCTRL.bit.STEPSIZE = DMAC_BTCTRL_STEPSIZE_X1_Val; descriptor_SPI[SPI_TX_CHANNEL].BTCNT.reg = SPI_DATA_SIZE+1; /*** Set source address and destination address *** Source address in the RAM is the spi Output buffer (spi_output_data[SPI_DATA_SIZE]) *** Destination address is the SPI Data register (SERCOM0->SPI.DATA) ***/ descriptor_SPI[SPI_TX_CHANNEL].SRCADDR.reg = (uint32_t) (&(spi_output_data[SPI_DATA_SIZE])); descriptor_SPI[SPI_TX_CHANNEL].DSTADDR.reg = (uint32_t) (&(SERCOM0->SPI.DATA)); /*** Set next transfer address to NULL ***/ descriptor_SPI[SPI_TX_CHANNEL].DESCADDR.reg = NULL; } /*** DMA SPI TX and RX Channels initialization ***/ void DMA_init_SPI(void) { /*** *** Setup descriptor base address *** and write back section base address ***/ DMAC->BASEADDR.reg = (uint32_t)descriptor_SPI; DMAC->WRBADDR.reg = (uint32_t)write_back_descriptor_SPI; /*** *** Disable DMAC *** Enable all priority level at the same time *** Round Robin Arbitrer Scheme selected ***/ DMAC->CTRL.reg &= ~(DMAC_CTRL_DMAENABLE); DMAC->CTRL.reg |= DMAC_CTRL_LVLEN(0xf); DMAC->PRICTRL0.bit.RRLVLEN0 = 1; /*** *** Configure The TX SPI DMA Channel: *** DMAC Channel 1 is used *** Disable the channel before configuring it ***/ DMAC->CHID.reg = DMAC_CHID_ID(SPI_TX_CHANNEL); DMAC->CHCTRLA.reg &= ~DMAC_CHCTRLA_ENABLE; /*** *** Configure TX SPI DMA Channel: (continue...) *** DMA is used in STANDBY, therefore needs RUNSTDBY *** Channel Priority Level = 0 *** SERCOM0 (SPI) is used as source to trigger the DMA TX *** The trigger action is done for every DMA BEAT ***/ DMAC->CHCTRLA.reg = DMAC_CHCTRLA_RUNSTDBY; DMAC->CHCTRLB.reg = (DMAC_CHCTRLB_LVL(0x0)| DMAC_CHCTRLB_TRIGSRC(SERCOM0_DMAC_ID_TX)| DMAC_CHCTRLB_TRIGACT_BEAT); /*** Enabling DMA channel TX ***/ DMAC->CHCTRLA.reg |= DMAC_CHCTRLA_ENABLE; while(DMAC->CHSTATUS.bit.BUSY); /*** *** Configure RX SPI DMA Channel: *** DMAC Channel 0 is used *** Disable the channel before configuring it ***/ DMAC->CHID.reg = DMAC_CHID_ID(SPI_RX_CHANNEL); DMAC->CHCTRLA.reg &= ~DMAC_CHCTRLA_ENABLE; /*** *** Configure RX SPI DMA Channel: (continue...) *** DMA is used in STANDBY, therefore needs RUNSTDBY *** Channel Priority Level = 0 *** SERCOM0 (SPI) is used as source to trigger the DMA RX *** The trigger action is done for every DMA BEAT ***/ DMAC->CHCTRLA.reg = DMAC_CHCTRLA_RUNSTDBY; DMAC->CHCTRLB.reg = ( DMAC_CHCTRLB_LVL(0x0)| DMAC_CHCTRLB_TRIGSRC(SERCOM0_DMAC_ID_RX)| DMAC_CHCTRLB_TRIGACT_BEAT ); /*** enabling DMA interrupt on RX transfer completed ***/ DMAC->CHINTENSET.reg = DMAC_CHINTENSET_TCMPL; /*** Enabling DMA channel RX ***/ DMAC->CHCTRLA.reg |= DMAC_CHCTRLA_ENABLE; while(DMAC->CHSTATUS.bit.BUSY); /*** Enabling interrupt at core side ***/ NVIC_EnableIRQ(DMAC_0_IRQn); /*** Finally Enable the DMA *** (Enable Protected) ***/ DMAC->CTRL.reg |= (DMAC_CTRL_DMAENABLE) ; }