/** @file dma.h * Module handling Direct Memory Access controller * * The module provides functions to configure the dma channels and controller * transfers */ //--includes-------------------------------------------------------------------- #include "dma.h" #include "dma_regs.h" #include "nvic.h" #include "rcc.h" #include "stddef.h" //--local definitions----------------------------------------------------------- static void configure_dma(volatile struct DMA* dma, enum DmaChannel channel, enum DmaConfig config_mask, volatile void* periph, void* mem, uint16_t size); //--local variables------------------------------------------------------------- static volatile struct DMA* const dma1 = (struct DMA*)DMA1_BASE_ADDRESS; static volatile struct DMA* const dma2 = (struct DMA*)DMA2_BASE_ADDRESS; static DmaCallback dma1_callbacks[7]; static volatile void* dma1_cb_params[7]; static DmaCallback dma2_callbacks[5]; static volatile void* dma2_cb_params[5]; //--public functions------------------------------------------------------------ void dma_configure(enum DmaPeriph dma, enum DmaChannel channel, enum DmaConfig config_mask, volatile void* periph, void* mem, uint16_t size, DmaCallback callback, volatile void* cb_param) { //reset peripheral first, to ensure proper configuration dma_reset(dma, channel); switch (dma) { case DMA_PERIPH_1: rcc_enable(RCC_AHB_DMA1, RCC_APB1_NONE, RCC_APB2_NONE); configure_dma(dma1, channel, config_mask, periph, mem, size); if (callback) { dma1_callbacks[channel] = callback; dma1_cb_params[channel] = cb_param; nvic_enable(NVIC_IRQ_DMA1_CHANNEL1 + channel); } break; case DMA_PERIPH_2: rcc_enable(RCC_AHB_DMA2, RCC_APB1_NONE, RCC_APB2_NONE); configure_dma(dma2, channel, config_mask, periph, mem, size); if (callback) { dma2_callbacks[channel] = callback; dma2_cb_params[channel] = cb_param; nvic_enable(NVIC_IRQ_DMA2_CHANNEL1 + channel); } break; default: break; } } void dma_reset(enum DmaPeriph dma, enum DmaChannel channel) { volatile struct DMA* periph; //first, disable IRQs switch (dma) { case DMA_PERIPH_1: periph = dma1; dma1_callbacks[channel] = NULL; nvic_disable(NVIC_IRQ_DMA1_CHANNEL1 + channel); break; case DMA_PERIPH_2: periph = dma2; dma2_callbacks[channel] = NULL; nvic_disable(NVIC_IRQ_DMA2_CHANNEL1 + channel); break; default: return; break; } //then, set all registers to reset value volatile struct DMA_CHANNEL* regs = &periph->CHANNELS[channel]; regs->CCR.word = 0; regs->CNDTR.word = 0; regs->CMAR = 0; regs->CPAR = 0; } void dma_exit_critical(enum DmaPeriph dma, enum DmaChannel channel) { switch (dma) { case DMA_PERIPH_1: nvic_enable(NVIC_IRQ_DMA1_CHANNEL1 + channel); break; case DMA_PERIPH_2: nvic_enable(NVIC_IRQ_DMA2_CHANNEL1 + channel); break; default: return; break; } } void dma_enable(enum DmaPeriph dma, enum DmaChannel channel) { switch (dma) { case DMA_PERIPH_1: reg_set(dma1->CHANNELS[channel].CCR, DMA_CCR_EN); if (dma1_callbacks[channel]) { nvic_enable(NVIC_IRQ_DMA1_CHANNEL1 + channel); } break; case DMA_PERIPH_2: reg_set(dma2->CHANNELS[channel].CCR, DMA_CCR_EN); if (dma2_callbacks[channel]) { nvic_enable(NVIC_IRQ_DMA2_CHANNEL1 + channel); } break; default: return; break; } } void dma_enter_critical(enum DmaPeriph dma, enum DmaChannel channel) { switch (dma) { case DMA_PERIPH_1: nvic_disable(NVIC_IRQ_DMA1_CHANNEL1 + channel); break; case DMA_PERIPH_2: nvic_disable(NVIC_IRQ_DMA2_CHANNEL1 + channel); break; default: return; break; } } void dma_disable(enum DmaPeriph dma, enum DmaChannel channel) { switch (dma) { case DMA_PERIPH_1: reg_reset(dma1->CHANNELS[channel].CCR, DMA_CCR_EN); if (dma1_callbacks[channel]) { nvic_disable(NVIC_IRQ_DMA1_CHANNEL1 + channel); } break; case DMA_PERIPH_2: reg_reset(dma2->CHANNELS[channel].CCR, DMA_CCR_EN); if (dma2_callbacks[channel]) { nvic_disable(NVIC_IRQ_DMA2_CHANNEL1 + channel); } break; default: return; break; } } uint16_t dma_get_remaining(enum DmaPeriph dma, enum DmaChannel channel) { switch (dma) { case DMA_PERIPH_1: return dma1->CHANNELS[channel].CNDTR.NDT; break; case DMA_PERIPH_2: return dma2->CHANNELS[channel].CNDTR.NDT; break; default: return 0; break; } } //--local functions------------------------------------------------------------- static void configure_dma(volatile struct DMA* dma, enum DmaChannel channel, enum DmaConfig config_mask, volatile void* periph, void* mem, uint16_t size) { volatile struct DMA_CHANNEL* regs = &dma->CHANNELS[channel]; //registers should already be at reset value, apply new config regs->CCR.word = config_mask; reg_write(regs->CNDTR, DMA_CNDTR_NDT, size); regs->CPAR = (uint32_t)periph; regs->CMAR = (uint32_t)mem; //only enable channel when everything is configured reg_set(regs->CCR, DMA_CCR_EN); } //--ISRs------------------------------------------------------------------------ void hdr_dma1_channel1(void) { nvic_clear_pending(NVIC_IRQ_DMA1_CHANNEL1); enum DmaIRQSource src = (dma1->IFCR.word >> 1) & 0x7; reg_set(dma1->IFCR, DMA_IFCR_CGIF1); dma1_callbacks[0](src, dma1_cb_params[0]); } void hdr_dma1_channel2(void) { nvic_clear_pending(NVIC_IRQ_DMA1_CHANNEL2); enum DmaIRQSource src = (dma1->IFCR.word >> 5) & 0x7; reg_set(dma1->IFCR, DMA_IFCR_CGIF2); dma1_callbacks[1](src, dma1_cb_params[1]); } void hdr_dma1_channel3(void) { nvic_clear_pending(NVIC_IRQ_DMA1_CHANNEL3); enum DmaIRQSource src = (dma1->IFCR.word >> 9) & 0x7; reg_set(dma1->IFCR, DMA_IFCR_CGIF3); dma1_callbacks[2](src, dma1_cb_params[2]); } void hdr_dma1_channel4(void) { nvic_clear_pending(NVIC_IRQ_DMA1_CHANNEL4); enum DmaIRQSource src = (dma1->IFCR.word >> 13) & 0x7; reg_set(dma1->IFCR, DMA_IFCR_CGIF4); dma1_callbacks[3](src, dma1_cb_params[3]); } void hdr_dma1_channel5(void) { nvic_clear_pending(NVIC_IRQ_DMA1_CHANNEL5); enum DmaIRQSource src = (dma1->IFCR.word >> 17) & 0x7; reg_set(dma1->IFCR, DMA_IFCR_CGIF5); dma1_callbacks[4](src, dma1_cb_params[4]); } void hdr_dma1_channel6(void) { nvic_clear_pending(NVIC_IRQ_DMA1_CHANNEL6); enum DmaIRQSource src = (dma1->IFCR.word >> 21) & 0x7; reg_set(dma1->IFCR, DMA_IFCR_CGIF6); dma1_callbacks[5](src, dma1_cb_params[5]); } void hdr_dma1_channel7(void) { nvic_clear_pending(NVIC_IRQ_DMA1_CHANNEL7); enum DmaIRQSource src = (dma1->IFCR.word >> 25) & 0x7; reg_set(dma1->IFCR, DMA_IFCR_CGIF7); dma1_callbacks[6](src, dma1_cb_params[6]); } void hdr_dma2_channel1(void) { nvic_clear_pending(NVIC_IRQ_DMA2_CHANNEL1); enum DmaIRQSource src = (dma2->IFCR.word >> 1) & 0x7; reg_set(dma2->IFCR, DMA_IFCR_CGIF1); dma2_callbacks[0](src, dma2_cb_params[0]); } void hdr_dma2_channel2(void) { nvic_clear_pending(NVIC_IRQ_DMA2_CHANNEL2); enum DmaIRQSource src = (dma2->IFCR.word >> 5) & 0x7; reg_set(dma2->IFCR, DMA_IFCR_CGIF2); dma2_callbacks[1](src, dma2_cb_params[1]); } void hdr_dma2_channel3(void) { nvic_clear_pending(NVIC_IRQ_DMA2_CHANNEL3); enum DmaIRQSource src = (dma2->IFCR.word >> 9) & 0x7; reg_set(dma2->IFCR, DMA_IFCR_CGIF3); dma2_callbacks[2](src, dma2_cb_params[2]); } void hdr_dma2_channel4_5(void) { nvic_clear_pending(NVIC_IRQ_DMA2_CHANNEL4_5); enum DmaIRQSource src = (dma2->IFCR.word >> 13) & 0x7; if (src != 0) { reg_set(dma2->IFCR, DMA_IFCR_CGIF4); dma1_callbacks[3](src, dma2_cb_params[3]); } src = (dma2->IFCR.word >> 17) & 0x7; if (src != 0) { reg_set(dma2->IFCR, DMA_IFCR_CGIF5); dma1_callbacks[4](src, dma2_cb_params[4]); } }