A simple library to access the DMA functionality.

Fork of SimpleDMA by Erik -

SimpleDMA_KL25.cpp

Committer:
Sissors
Date:
2013-12-26
Revision:
4:c3a84c6c432c
Parent:
3:34f5bf8adfa0
Child:
5:d9f46ef80e20

File content as of revision 4:c3a84c6c432c:

#include "SimpleDMA.h"



SimpleDMA *SimpleDMA::irq_owner[4] = {NULL};

SimpleDMA::SimpleDMA(int channel) {
    this->channel(channel);
       
    //Enable DMA
    SIM->SCGC6 |= 1<<1;     //Enable clock to DMA mux
    SIM->SCGC7 |= 1<<8;     //Enable clock to DMA
    
    trigger(Trigger_ALWAYS);
    
    DCR = (1<<29) + (1<<30);   //Set to always use DMAMUX (If no trigger is needed we route via alwayson)
   
    NVIC_SetVector(DMA0_IRQn, (uint32_t)&irq_handler0);
    NVIC_SetVector(DMA1_IRQn, (uint32_t)&irq_handler1);
    NVIC_SetVector(DMA2_IRQn, (uint32_t)&irq_handler2);
    NVIC_SetVector(DMA3_IRQn, (uint32_t)&irq_handler3);
    NVIC_EnableIRQ(DMA0_IRQn);
    NVIC_EnableIRQ(DMA1_IRQn);
    NVIC_EnableIRQ(DMA2_IRQn);
    NVIC_EnableIRQ(DMA3_IRQn);
}

int SimpleDMA::setAddress(uint32_t address, int wordsize, bool source, bool autoinc) {
    //Check if it is an allowed address
    switch ((uint32_t) address >> 20) {
        case 0x000:
        case 0x1FF:
        case 0x200:
        case 0x400:
        break;
        default:
            return -1;
        }
    
    char _size;

    switch (wordsize) {
        case 8:
            _size = 1;
            break;
        case 16:
            _size = 2;
            break;
        case 32:
            _size = 0;
            break;
        default:
            _size = 1;
        }
    
    //Check if source or destination
    if (source) {
        SAR = address;
        DCR &= ~(7<<20);
        DCR |= autoinc << 22;
        DCR |= _size << 20;
    } else {
        DAR = address;
        DCR &= ~(7<<17);
        DCR |= autoinc << 19;
        DCR |= _size << 17;
    }

    return 0;
};

int SimpleDMA::trigger(SimpleDMA_Trigger trig){ 
    
    CHCFG = trig;
    return 0;
}


int SimpleDMA::start(int length) {  
    if (auto_channel)
        _channel = getFreeChannel();
    else
        while(isBusy());
    
    if (length > 0xFFFFF)
        return -1;

    DCR |= (1UL<<31);
        irq_owner[_channel] = this;
    
    //Set registers:
    DMA0->DMA[_channel].SAR = SAR;
    DMA0->DMA[_channel].DAR = DAR;
    DMA0->DMA[_channel].DCR = DCR;
    
    //Set trigger
    DMAMUX0->CHCFG[_channel] = CHCFG;
    
    //Set length
    DMA0->DMA[_channel].DSR_BCR = length;
        
    //Start
    DMAMUX0->CHCFG[_channel] |= 1<<7;
    
    return 0;
}

void SimpleDMA::channel(int chan) {
    if (chan == -1) {
        auto_channel = true;
        _channel = 0;
    } else {
        auto_channel = false;
        if (chan >= 0 && chan < DMA_CHANNELS)
            _channel = chan;
        else
            _channel = 3;
    }
}

bool SimpleDMA::isBusy( int channel ) {
    //Busy bit doesn't work as I expect it to do, so just check if counter is at zero
    //return (DMA0->DMA[_channel].DSR_BCR & (1<<25) == 1<<25);
    if (channel == -1)
        channel = _channel;
    
    return (DMA0->DMA[channel].DSR_BCR & 0xFFFFFF);
}

void SimpleDMA::irq_handler(void) {
    DMAMUX0->CHCFG[_channel] = 0;
    DMA0->DMA[_channel].DSR_BCR |= DMA_DSR_BCR_DONE_MASK ; 
    _callback.call();
}