diff --git a/drivers/dma.c b/drivers/dma.c new file mode 100644 index 0000000..0e7f98f --- /dev/null +++ b/drivers/dma.c @@ -0,0 +1,246 @@ +/** @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" + + +//--local definitions----------------------------------------------------------- + +static void configure_dma(volatile struct DMA* dma, enum DmaChannel channel, + enum DmaConfig config_mask, void* periph, void* mem, + uint16_t size); +static uint32_t periph_regs(enum DmaPeriph periph, volatile struct DMA** regs); + + +//--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 dm1_callbacks[7]; +static DmaCallback dm2_callbacks[5]; + + +//--public functions------------------------------------------------------------ + +void dma_configure(enum DmaPeriph dma, enum DmaChannel channel, + enum DmaConfig config_mask, void* periph, void* mem, + uint16_t size, DmaCallback callback) +{ + //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) { + dm1_callbacks[channel] = callback; + 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) { + dm2_callbacks[channel] = callback; + 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; + nvic_disable(NVIC_IRQ_DMA1_CHANNEL1 + channel); + break; + case DMA_PERIPH_2: + periph = dma2; + 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; +} + + +//--local functions------------------------------------------------------------- + +static void configure_dma(volatile struct DMA* dma, enum DmaChannel channel, + enum DmaConfig config_mask, 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); +} + +static uint32_t periph_regs(enum DmaPeriph periph, volatile struct DMA** regs) +{ + switch (periph) { + case DMA_PERIPH_1: + *regs = dma1; + break; + case DMA_PERIPH_2: + *regs = dma2; + break; + default: + return 1; + break; + } + + return 0; +} + + +//--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); + + dm1_callbacks[0](src); +} + +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); + + dm1_callbacks[1](src); +} + +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); + + dm1_callbacks[2](src); +} + +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); + + dm1_callbacks[3](src); +} + +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); + + dm1_callbacks[4](src); +} + +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); + + dm1_callbacks[5](src); +} + +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); + + dm1_callbacks[6](src); +} + +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); + + dm1_callbacks[0](src); +} + +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); + + dm2_callbacks[1](src); +} + +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); + + dm2_callbacks[2](src); +} + +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); + dm1_callbacks[3](src); + } + + src = (dma2->IFCR.word >> 17) & 0x7; + if (src != 0) { + reg_set(dma2->IFCR, DMA_IFCR_CGIF5); + dm1_callbacks[4](src); + } +} + diff --git a/drivers/dma.h b/drivers/dma.h new file mode 100644 index 0000000..05f471e --- /dev/null +++ b/drivers/dma.h @@ -0,0 +1,99 @@ +/** @file dma.h + * Module handling Direct Memory Access controller + * + * The module provides functions to configure the dma channels and controller + * transfers + */ + +#ifndef _DMA_H_ +#define _DMA_H_ + +//--includes-------------------------------------------------------------------- + +#include "stdint.h" + + +//--type definitions------------------------------------------------------------ + +enum DmaPeriph { + DMA_PERIPH_1, + DMA_PERIPH_2, +}; + +enum DmaChannel { + DMA_CHANNEL_1 = 0, + DMA_CHANNEL_2, + DMA_CHANNEL_3, + DMA_CHANNEL_4, + DMA_CHANNEL_5, + DMA_CHANNEL_6, //not available for DMA 2 + DMA_CHANNEL_7, //not available for DMA 2 +}; + +enum DmaConfig { + DMA_CONFIG_IRQ_COMPLETE = (0x1 << 1), + DMA_CONFIG_IRQ_HALF = (0x1 << 2), + DMA_CONFIG_IRQ_ERROR = (0x1 << 3), + DMA_CONFIG_FROM_MEM = (0x1 << 4), + DMA_CONFIG_FROM_PERIPH = (0x0 << 4), + DMA_CONFIG_CIRCULAR = (0x1 << 5), + DMA_CONFIG_INC_PERIPH = (0x1 << 6), + DMA_CONFIG_INC_MEM = (0x1 << 7), + DMA_CONFIG_PSIZE_8BITS = (0x0 << 8), + DMA_CONFIG_PSIZE_16BITS = (0x1 << 8), + DMA_CONFIG_PSIZE_32BITS = (0x2 << 8), + DMA_CONFIG_MSIZE_8BITS = (0x0 << 10), + DMA_CONFIG_MSIZE_16BITS = (0x1 << 10), + DMA_CONFIG_MSIZE_32BITS = (0x2 << 10), + DMA_CONFIG_PRIO_LOW = (0x0 << 12), + DMA_CONFIG_PRIO_MEDIUM = (0x1 << 12), + DMA_CONFIG_PRIO_HIGH = (0x2 << 12), + DMA_CONFIG_PRIO_VHIGH = (0x3 << 12), +}; + +enum DmaConfigM2M { + DMA_CONFIG_M2M_IRQ_COMPLETE = (0x1 << 1), + DMA_CONFIG_M2M_IRQ_HALF = (0x1 << 2), + DMA_CONFIG_M2M_IRQ_ERROR = (0x1 << 3), + DMA_CONFIG_M2M_INC_SRC = (0x1 << 6), + DMA_CONFIG_M2M_INC_DEST = (0x1 << 7), + DMA_CONFIG_M2M_SSIZE_8BITS = (0x0 << 8), + DMA_CONFIG_M2M_SSIZE_16BITS = (0x1 << 8), + DMA_CONFIG_M2M_SSIZE_32BITS = (0x2 << 8), + DMA_CONFIG_M2M_DSIZE_8BITS = (0x0 << 10), + DMA_CONFIG_M2M_DSIZE_16BITS = (0x1 << 10), + DMA_CONFIG_M2M_DSIZE_32BITS = (0x2 << 10), + DMA_CONFIG_M2M_PRIO_LOW = (0x0 << 12), + DMA_CONFIG_M2M_PRIO_MEDIUM = (0x1 << 12), + DMA_CONFIG_M2M_PRIO_HIGH = (0x2 << 12), + DMA_CONFIG_M2M_PRIO_VHIGH = (0x3 << 12), +}; + +enum DmaIRQSource { + DMA_IRQ_SOURCE_COMPLETE = (0x1 << 1), + DMA_IRQ_SOURCE_HALF = (0x1 << 2), + DMA_IQR_SOURCE_ERROR = (0x2 << 3), +}; + +typedef void (*DmaCallback)(enum DmaIRQSource); + + +//--functions------------------------------------------------------------------- + +void dma_configure(enum DmaPeriph dma, enum DmaChannel channel, + enum DmaConfig config_mask, void* periph, void* mem, + uint16_t size, DmaCallback callback); + +void dma_configure_mem2mem(enum DmaPeriph dma, enum DmaChannel channel, + enum DmaConfigM2M config_mask, const void* src, void* dest, + uint16_t size, DmaCallback callback); + +void dma_reset(enum DmaPeriph dma, enum DmaChannel channel); + +void dma_enable(enum DmaPeriph dma, enum DmaChannel channel); + +void dma_disable(enum DmaPeriph dma, enum DmaChannel channel); + + +#endif //_DMA_H_ +