A while back, macros had to be put in place to avoid letting the compiler directly use the bitfields. This was necessary because the compiler used strb instruction which only write bytes. On the AHB bus, byte writes are transformed into word writes by repeating the byte, which caused mayhem in the registers. After a lot of research, turns out the packed attribute stops the compiler from does optimal (word) writes and isn't needed anyway. Removing them fixes the issue
336 lines
7.6 KiB
C
336 lines
7.6 KiB
C
/** @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);
|
|
static void start_dma(volatile struct DMA* dma, enum DmaChannel channel,
|
|
volatile 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,
|
|
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);
|
|
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);
|
|
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_start(enum DmaPeriph dma, enum DmaChannel channel,
|
|
volatile void* mem, uint16_t size)
|
|
{
|
|
switch (dma) {
|
|
case DMA_PERIPH_1:
|
|
if (dma1_callbacks[channel]) {
|
|
nvic_enable(NVIC_IRQ_DMA1_CHANNEL1 + channel);
|
|
}
|
|
start_dma(dma1, channel, mem, size);
|
|
break;
|
|
case DMA_PERIPH_2:
|
|
if (dma2_callbacks[channel]) {
|
|
nvic_enable(NVIC_IRQ_DMA2_CHANNEL1 + channel);
|
|
}
|
|
start_dma(dma2, channel, mem, size);
|
|
break;
|
|
default:
|
|
return;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void dma_stop(enum DmaPeriph dma, enum DmaChannel channel)
|
|
{
|
|
switch (dma) {
|
|
case DMA_PERIPH_1:
|
|
dma1->CHANNELS[channel].CCR.EN = 0;
|
|
if (dma1_callbacks[channel]) {
|
|
nvic_disable(NVIC_IRQ_DMA1_CHANNEL1 + channel);
|
|
}
|
|
break;
|
|
case DMA_PERIPH_2:
|
|
dma2->CHANNELS[channel].CCR.EN;
|
|
if (dma2_callbacks[channel]) {
|
|
nvic_disable(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_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;
|
|
}
|
|
}
|
|
|
|
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-------------------------------------------------------------
|
|
|
|
/**
|
|
* Applies the given configuration mask to the given DMA channel
|
|
*/
|
|
static void configure_dma(volatile struct DMA* dma, enum DmaChannel channel,
|
|
enum DmaConfig config_mask, volatile void* periph)
|
|
{
|
|
volatile struct DMA_CHANNEL* regs = &dma->CHANNELS[channel];
|
|
|
|
//registers should already be at reset value, apply new config
|
|
regs->CCR.word = config_mask;
|
|
regs->CPAR = (uint32_t)periph;
|
|
}
|
|
|
|
/**
|
|
* Starts the given DMA channel using the given parameters
|
|
*/
|
|
static void start_dma(volatile struct DMA* dma, enum DmaChannel channel,
|
|
volatile void* mem, uint16_t size)
|
|
{
|
|
volatile struct DMA_CHANNEL* regs = &dma->CHANNELS[channel];
|
|
|
|
//registers should already be configured, apply transfer config
|
|
regs->CNDTR.NDT = size;
|
|
regs->CMAR = (uint32_t)mem;
|
|
|
|
//only start transfer when everything is configured
|
|
regs->CCR.EN = 1;
|
|
}
|
|
|
|
//--ISRs------------------------------------------------------------------------
|
|
|
|
void hdr_dma1_channel1(void)
|
|
{
|
|
nvic_clear_pending(NVIC_IRQ_DMA1_CHANNEL1);
|
|
|
|
enum DmaIRQSource src = (dma1->IFCR.word >> 1) & 0x7;
|
|
dma1->IFCR.CGIF1 = 1;
|
|
|
|
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;
|
|
dma1->IFCR.CGIF2 = 1;
|
|
|
|
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;
|
|
dma1->IFCR.CGIF3 = 1;
|
|
|
|
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;
|
|
dma1->IFCR.CGIF4 = 1;
|
|
|
|
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;
|
|
dma1->IFCR.CGIF5 = 1;
|
|
|
|
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;
|
|
dma1->IFCR.CGIF6 = 1;
|
|
|
|
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;
|
|
dma1->IFCR.CGIF7 = 1;
|
|
|
|
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;
|
|
dma2->IFCR.CGIF1 = 1;
|
|
|
|
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;
|
|
dma2->IFCR.CGIF2 = 1;
|
|
|
|
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;
|
|
dma2->IFCR.CGIF3 = 1;
|
|
|
|
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) {
|
|
dma2->IFCR.CGIF4 = 1;
|
|
dma1_callbacks[3](src, dma2_cb_params[3]);
|
|
}
|
|
|
|
src = (dma2->IFCR.word >> 17) & 0x7;
|
|
if (src != 0) {
|
|
dma2->IFCR.CGIF5 = 1;
|
|
dma1_callbacks[4](src, dma2_cb_params[4]);
|
|
}
|
|
}
|
|
|