#include "zerodma.h" #include struct dmacdescriptor { uint16_t btctrl; uint16_t btcnt; uint32_t srcaddr; uint32_t dstaddr; uint32_t descaddr; }; // 12 channels volatile dmacdescriptor g_wrb[12] __attribute__ ((aligned (16))); dmacdescriptor g_descriptor_section[12] __attribute__ ((aligned (16))); volatile uint32_t g_dmadone; bool g_dma_is_initialized = false; void DMAC_Handler() { // interrupts DMAC_CHINTENCLR_TERR DMAC_CHINTENCLR_TCMPL DMAC_CHINTENCLR_SUSP uint8_t active_channel; __disable_irq(); active_channel = DMAC->INTPEND.reg & DMAC_INTPEND_ID_Msk; // get channel number DMAC->CHID.reg = DMAC_CHID_ID(active_channel); g_dmadone = DMAC->CHINTFLAG.reg; DMAC->CHINTFLAG.reg = DMAC_CHINTENCLR_TCMPL; // clear DMAC->CHINTFLAG.reg = DMAC_CHINTENCLR_TERR; DMAC->CHINTFLAG.reg = DMAC_CHINTENCLR_SUSP; __enable_irq(); } static void zerodma_init() { // probably on by default PM->AHBMASK.reg |= PM_AHBMASK_DMAC ; PM->APBBMASK.reg |= PM_APBBMASK_DMAC ; NVIC_EnableIRQ( DMAC_IRQn ) ; DMAC->BASEADDR.reg = (uint32_t)descriptor_section; DMAC->WRBADDR.reg = (uint32_t)wrb; DMAC->CTRL.reg = DMAC_CTRL_DMAENABLE | DMAC_CTRL_LVLEN(0xf); g_dma_is_initialized = true; } // ------------------------ Channel abstraction ------------------------------------- ZeroDmaChannel::ZeroDmaChannel(uint32_t chnl = 0) : m_channel(chnl) { if (!g_dma_is_initialized) zerodma_init(); DMAC->CHID.reg = DMAC_CHID_ID(m_channel); DMAC->CHCTRLA.reg &= ~DMAC_CHCTRLA_ENABLE; DMAC->CHCTRLA.reg = DMAC_CHCTRLA_SWRST; } void ZeroDmaChannel::copy32(void *dst, const void *src, size_t n) { dmacdescriptor descriptor __attribute__ ((aligned (16))); DMAC->CHID.reg = DMAC_CHID_ID(m_channel); DMAC->CHINTENSET.reg = DMAC_CHINTENSET_MASK ; // enable all 3 interrupts g_dmadone[m_channel] = 0; descriptor.descaddr = 0; descriptor.dstaddr = (uint32_t)dst + n; descriptor.srcaddr = (uint32_t)src + n; descriptor.btcnt = n/4; descriptor.btctrl = DMAC_BTCTRL_BEATSIZE_WORD | DMAC_BTCTRL_DSTINC | DMAC_BTCTRL_SRCINC | DMAC_BTCTRL_VALID; memcpy(&descriptor_section[m_channel],&descriptor, sizeof(dmacdescriptor)); DMAC->CHCTRLA.reg |= DMAC_CHCTRLA_ENABLE; DMAC->SWTRIGCTRL.reg |= (1 << chnl); // trigger channel while(!g_dmadone[m_channel]); // await DMA done isr } void ZeroDmaChannel::waitComplete() { while(!g_dmadone); } // -------------------------------- SPI --------------------------------------- ZeroDmaSPI::ZeroDmaSPI(uint32_t txChannel, uint32_t rxChannel) : m_txchannel(txChannel), m_rxchannel(rxChannel) { if (!g_dma_is_initialized) zerodma_init(); } void ZeroDmaSPI::beginTransfer(const uint8_t *tx, uint8_t *rx, size_t n) { dmacdescriptor descriptor __attribute__ ((aligned (16))); uint32_t temp_CHCTRLB_reg; // set up transmit channel DMAC->CHID.reg = DMAC_CHID_ID(m_txchannel); DMAC->CHCTRLA.reg &= ~DMAC_CHCTRLA_ENABLE; DMAC->CHCTRLA.reg = DMAC_CHCTRLA_SWRST; DMAC->SWTRIGCTRL.reg &= (uint32_t)(~(1 << m_txchannel)); temp_CHCTRLB_reg = DMAC_CHCTRLB_LVL(0) | DMAC_CHCTRLB_TRIGSRC(SERCOM4_DMAC_ID_TX) | DMAC_CHCTRLB_TRIGACT_BEAT; DMAC->CHCTRLB.reg = temp_CHCTRLB_reg; DMAC->CHINTENSET.reg = DMAC_CHINTENSET_MASK ; // enable all 3 interrupts descriptor.descaddr = 0; descriptor.dstaddr = (uint32_t) &sercom->SPI.DATA.reg; descriptor.btcnt = n; descriptor.srcaddr = (uint32_t) tx; descriptor.btctrl = DMAC_BTCTRL_VALID; if (tx != nullptr) { descriptor.srcaddr += n; descriptor.btctrl |= DMAC_BTCTRL_SRCINC; } memcpy(&descriptor_section[m_txchannel], &descriptor, sizeof(dmacdescriptor)); // rx channel DMAC->CHID.reg = DMAC_CHID_ID(m_rxchannel); DMAC->CHCTRLA.reg &= ~DMAC_CHCTRLA_ENABLE; DMAC->CHCTRLA.reg = DMAC_CHCTRLA_SWRST; DMAC->SWTRIGCTRL.reg &= (uint32_t)(~(1 << m_rxchannel)); temp_CHCTRLB_reg = DMAC_CHCTRLB_LVL(0) | DMAC_CHCTRLB_TRIGSRC(SERCOM4_DMAC_ID_RX) | DMAC_CHCTRLB_TRIGACT_BEAT; DMAC->CHCTRLB.reg = temp_CHCTRLB_reg; DMAC->CHINTENSET.reg = DMAC_CHINTENSET_MASK ; // enable all 3 interrupts g_dmadone = 0; descriptor.descaddr = 0; descriptor.srcaddr = (uint32_t) &sercom->SPI.DATA.reg; descriptor.btcnt = n; descriptor.dstaddr = (uint32_t) rx; descriptor.btctrl = DMAC_BTCTRL_VALID; if (rx != nullptr) { descriptor.dstaddr += n; descriptor.btctrl |= DMAC_BTCTRL_DSTINC; } memcpy(&descriptor_section[m_rxchannel],&descriptor, sizeof(dmacdescriptor)); // ordering? DMAC->CHID.reg = DMAC_CHID_ID(m_txchannel); DMAC->CHCTRLA.reg |= DMAC_CHCTRLA_ENABLE; DMAC->CHID.reg = DMAC_CHID_ID(m_rxchannel); DMAC->CHCTRLA.reg |= DMAC_CHCTRLA_ENABLE; } bool ZeroDmaSPI::completed() { return g_dmadone; } void ZeroDmaSPI::endTransfer() { while (!g_dmadone); // disable DMA to allow lib SPI DMAC->CHID.reg = DMAC_CHID_ID(m_txchannel); DMAC->CHCTRLA.reg &= ~DMAC_CHCTRLA_ENABLE; DMAC->CHID.reg = DMAC_CHID_ID(m_rxchannel); DMAC->CHCTRLA.reg &= ~DMAC_CHCTRLA_ENABLE; }