K80 DMA

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

K80 DMA

2,363 Views
akimata
Contributor IV

Hello,

I never used DMA before but i'm struggling with one thing. I got simple code which should copy one array to another with DMA, trigerred by falling edge on GPIO. It works only for the first time, any other falling edges never copy arrays again. 

I noticed that clearing DMA_CSR_DONE_MASK solves the problem but it's not good idea to clear it all the time in main loop. Shoudn't it reset everytime there is a falling edge on the GPIO? or should i reset it in interrupt generated by DMA?

Thanks

#include <stdio.h>
#include "board.h"
#include "peripherals.h"
#include "pin_mux.h"
#include "clock_config.h"
#include "MK80F25615.h"
#include "fsl_debug_console.h"

volatile uint8_t bufornadawczy[3]={0x0D,0x0C,0x0B};
volatile uint8_t buforodbiorczy[3]={0,0,0};


void dma_init(void)
{
DMAMUX -> CHCFG[0] = (DMAMUX_CHCFG_ENBL_MASK |DMAMUX_CHCFG_SOURCE(51));
DMA0->ERQ |= DMA_ERQ_ERQ0_MASK;

DMA0->TCD[0].SADDR = (uint32_t) &bufornadawczy[0];
DMA0->TCD[0].DADDR = (uint32_t) &buforodbiorczy[0];

DMA0->TCD[0].SOFF = 1;
DMA0->TCD[0].DOFF = 1;

DMA0->TCD[0].ATTR = DMA_ATTR_SSIZE(0) | DMA_ATTR_DSIZE(0);

DMA0->TCD[0].NBYTES_MLNO = 1;

DMA0->TCD[0].CITER_ELINKNO = DMA_CITER_ELINKNO_CITER(3);
DMA0->TCD[0].BITER_ELINKNO = DMA_BITER_ELINKNO_BITER(3);

DMA0->TCD[0].SLAST = -3;
DMA0->TCD[0].DLAST_SGA = -3;


DMA0->TCD[0].CSR |= DMA_CSR_DREQ_MASK;

}


int main(void) {


BOARD_InitBootPins();
BOARD_InitBootClocks();
BOARD_InitBootPeripherals();
BOARD_InitDebugConsole();

SIM->SCGC5 = (SIM_SCGC5_PORTC_MASK|SIM_SCGC5_PORTE_MASK);
SIM->SCGC6 = (SIM_SCGC6_DMAMUX_MASK|SIM_SCGC6_SPI0_MASK);
SIM->SCGC7 = SIM_SCGC7_DMA_MASK;


//led
PORTE-> PCR[7] = PORT_PCR_MUX(0x01);
GPIOE -> PDDR |= GPIO_PDDR_PDD(1<<7);
GPIOE -> PSOR |= GPIO_PSOR_PTSO(1<<7);

PORTC -> PCR[14] |= (PORT_PCR_MUX(0x01)|PORT_PCR_IRQC(2)|PORT_PCR_PE_MASK|PORT_PCR_PS_MASK);//INT2_A, DMA Request, ISR flag

dma_init();

__NVIC_EnableIRQ(61);


while(1)
{



}
return 0 ;
}

Tags (1)
0 Kudos
Reply
4 Replies

2,162 Views
mjbcswitzerland
Specialist V

Hi Grzegorz

DMA0->TCD[0].CSR |= DMA_CSR_DREQ_MASK;

pastedImage_1.png

If you want a burst on each trigger you shouldn't set DREG.

To do this in the uTasker project I do:

#define DMA_CHANNEL_FOR_PORT_EDGE 0 // use DMA channel 0
INTERRUPT_SETUP interrupt_setup;                                     // interrupt configuration parameters
fnConfigDMA_buffer(DMA_CHANNEL_FOR_PORT_EDGE, DMAMUX0_CHCFG_SOURCE_PORTE, sizeof(bufornadawczy), (void *)bufornadawczy, (void *)buforodbiorczy, (DMA_DIRECTION_BUFFER_BUFFER |  DMA_BUFFER_BURST_MODE | DMA_BYTES), 0, 0); // configure the DMA channel without any interrupts (free-runnning)
fnDMA_BufferReset(DMA_CHANNEL_FOR_PORT_EDGE, DMA_BUFFER_START);      // enable the DMA operation (trigger will be enabled later)


// Configure the pin DMA trigger
//
interrupt_setup.int_type       = PORT_INTERRUPT;                     // identifier to configure port interrupt
interrupt_setup.int_port       = PORTE;                              // the port that the interrupt/dma input is on
interrupt_setup.int_port_bits  = PORTE_BIT14;                        // PTE14
interrupt_setup.int_port_sense = (IRQ_FALLING_EDGE | PULLUP_ON | PORT_DMA_MODE); // DMA on falling edge
interrupt_setup.int_handler = 0;                                     // no interrupt handler when using DMA
fnConfigureInterrupt((void *)&interrupt_setup);                      // configure DMA trigger on falling edge

The uTasker simulator allows it to be tested in Visual Studio. See the video that I have attached.

Regards

Mark

2,162 Views
akimata
Contributor IV

So 1 DMA request = 1 minor loop but i want to transfer let's say 8 bytes to SPI per 1 request, can't do it with DMA major int. So the only way to do this is to reconfig line for interrupt and start transfer  in interrupt service using "DMA0->SSRT |= DMA_SSRT_SSRT(0)" 8 times or it can be done any other way?

0 Kudos
Reply

2,162 Views
mjbcswitzerland
Specialist V

Grzegorz

It is possible to do a channel burst transfer to an SPI but it means that the writes are very fast and if the SPI doesn't have adequate FIFO depth (SPI0 has 4 but others have only 1) it will overrun the transmitter and only the first 4 or 1 will be sent.


When you sent to SPI via DMA you therefore need to use the SPIs DMA trigger to control transmission and not the trigger from a pin. You can use the trigger for the pin to set up the transfer (via interrupt) or via DMA.

Below I can show you how a pin DMA trigger can be used to control the start of an SPI DMA transmission burst (as included in the uTasker project). In fact it is used to automate both transmission and reception, transmitting a predefined sequence of bytes, controlling the CS line, reading returned data and finally clearing SPI flags via DMA when the transmission has completed (otherwise it won't work a second time!).

It is useful for external high speed ADCs that indicated data is ready via sync line (triggering the operation to write and read a number of bytes purely using DMA).

    // Initialise SPI interface
    //
    POWER_UP_ATOMIC(6, SPI0);
    _CONFIG_PERIPHERAL(D, 0, (PD_0_SPI0_PCS0 | PORT_SRE_FAST | PORT_DSE_HIGH));
    _CONFIG_PERIPHERAL(D, 1, (PD_1_SPI0_SCK | PORT_SRE_FAST | PORT_DSE_HIGH));
    _CONFIG_PERIPHERAL(D, 2, (PD_2_SPI0_SOUT | PORT_SRE_FAST | PORT_DSE_HIGH));
    _CONFIG_PERIPHERAL(D, 3, (PD_3_SPI0_SIN));
    SPI0_MCR = (SPI_MCR_MSTR | SPI_MCR_DCONF_SPI | SPI_MCR_CLR_RXF | SPI_MCR_CLR_TXF | SPI_MCR_PCSIS_CS0 | SPI_MCR_PCSIS_CS1 | SPI_MCR_PCSIS_CS2 | SPI_MCR_PCSIS_CS3 | SPI_MCR_PCSIS_CS4 | SPI_MCR_PCSIS_CS5);
    SPI0_RSER = (SPI_SRER_TFFF_DIRS | SPI_SRER_TFFF_RE | SPI_SRER_RFDF_DIRS | SPI_SRER_RFDF_RE); // enable rx and tx DMA requests
    SPI0_CTAR0 = (SPI_CTAR_DBR | SPI_CTAR_FMSZ_8 | SPI_CTAR_PDT_7 | SPI_CTAR_BR_4 | SPI_CTAR_CPHA | SPI_CTAR_CPOL); // for 50MHz bus, 25MHz speed and 140ns min de-select time

    interrupt_setup.int_port       = PORTE;                              // the port that the interrupt/dma input is on
    interrupt_setup.int_port_bits = PORTE_BIT4;
    interrupt_setup.int_port_sense = (IRQ_FALLING_EDGE | PULLUP_ON | PORT_DMA_MODE); // DMA on falling edge
    interrupt_setup.int_handler = 0;                                     // no interrupt handler when using DMA
    {
        // Configure the DMA trigger from an input pin edge to start an SPI transfer
        //
        #define DMA_CHANNEL_FOR_CS_END    6                              // use DMA channel 6 for CS end trigger
        #define DMA_CHANNEL_FOR_PORT_EDGE 7                              // use DMA channel 7 for SPI sequence start trigger
        #define DMA_CHANNEL_FOR_SPI_TX    8                              // use DMA channel 8 for SPI Tx
        #define DMA_CHANNEL_FOR_SPI_RX    9                              // use DMA channel 9 for SPI Rx
        static const unsigned long ulSPI_TX[8] = {                       // fixed SPI transmission (0x01, 0x02,.. 0x08) with CS asserted throughout the frame
                (0x01 | SPI_PUSHR_CONT | SPI_PUSHR_PCS0 | SPI_PUSHR_CTAS_CTAR0),
                (0x02 | SPI_PUSHR_CONT | SPI_PUSHR_PCS0 | SPI_PUSHR_CTAS_CTAR0),
                (0x03 | SPI_PUSHR_CONT | SPI_PUSHR_PCS0 | SPI_PUSHR_CTAS_CTAR0),
                (0x04 | SPI_PUSHR_CONT | SPI_PUSHR_PCS0 | SPI_PUSHR_CTAS_CTAR0),
                (0x05 | SPI_PUSHR_CONT | SPI_PUSHR_PCS0 | SPI_PUSHR_CTAS_CTAR0),
                (0x06 | SPI_PUSHR_CONT | SPI_PUSHR_PCS0 | SPI_PUSHR_CTAS_CTAR0),
                (0x07 | SPI_PUSHR_CONT | SPI_PUSHR_PCS0 | SPI_PUSHR_CTAS_CTAR0),
                (0x08 | SPI_PUSHR_EOQ  | SPI_PUSHR_PCS0 | SPI_PUSHR_CTAS_CTAR0), // final byte negates CS after transmission
        };
        static volatile unsigned char ucRxData[128] = {0};               // circular reception buffer
        static const unsigned char ucDMA_start = ((DMA_ERQ_ERQ0 << (DMA_CHANNEL_FOR_SPI_TX - 8)) | (DMA_ERQ_ERQ0 << (DMA_CHANNEL_FOR_SPI_RX - 8))); // the value to be written to start the SPI DMA transfer
        static const unsigned long ulSPI_clear = (SPI_SR_RFDF | SPI_SR_RFOF | SPI_SR_TFUF | SPI_SR_EOQF | SPI_SR_TCF); // this is written to the SPI status register after the CS negates in order to clear flags and allow subsequent transfers

        fnConfigDMA_buffer(DMA_CHANNEL_FOR_SPI_TX,    DMAMUX0_CHCFG_SOURCE_SPI0_TX, sizeof(ulSPI_TX),    (void *)ulSPI_TX,       (void *)SPI0_PUSHR_ADDR,                      (DMA_DIRECTION_OUTPUT | DMA_LONG_WORDS | DMA_SINGLE_CYCLE), 0, 0); // source is the tx buffer and destination is the SPI transmit register without interrupts (free-running)
        fnConfigDMA_buffer(DMA_CHANNEL_FOR_SPI_RX,    DMAMUX0_CHCFG_SOURCE_SPI0_RX, sizeof(ucRxData),    (void *)SPI0_POPR_ADDR, (void *)ucRxData,                             (DMA_DIRECTION_INPUT | DMA_BYTES | DMA_HALF_BUFFER_INTERRUPT), spi_half_buffer, PRIORITY_DMA9); // source is the SPI reception register and destination is the input buffer with interrupt at half- and full buffer
        fnConfigDMA_buffer(DMA_CHANNEL_FOR_PORT_EDGE, DMAMUX0_CHCFG_SOURCE_PORTE,  sizeof(ucDMA_start), (void *)&ucDMA_start,   (void *)(((unsigned char *)DMA_ERQ_ADDR) + 1), (DMA_FIXED_ADDRESSES | DMA_BYTES), 0, 0); // use DMA channel without any interrupts (free-runnning)
        fnDMA_BufferReset(DMA_CHANNEL_FOR_PORT_EDGE,  DMA_BUFFER_START); // enable the DMA operation - a falling edge on the port will now trigger SPI Tx and Rx DMA operation
        fnConfigureInterrupt((void *)&interrupt_setup);                  // configure cs port DMA (falling edge)
        interrupt_setup.int_port = PORTD;                                // the port that the interrupt input is on
        interrupt_setup.int_port_bits = PORTD_BIT0;
        interrupt_setup.int_port_sense = (IRQ_RISING_EDGE | PULLUP_ON | PORT_DMA_MODE | PORT_KEEP_PERIPHERAL); // DMA on rising edge (keep CS peripheral to trigger on the end of a transfer
        fnConfigDMA_buffer(DMA_CHANNEL_FOR_CS_END, DMAMUX0_CHCFG_SOURCE_PORTD, sizeof(ulSPI_clear), (void *)&ulSPI_clear, (void *)SPI0_SR_ADDR, (DMA_FIXED_ADDRESSES | DMA_LONG_WORDS), 0, 0); // use DMA channel without any interrupts (free-runnning)
        fnDMA_BufferReset(DMA_CHANNEL_FOR_CS_END, DMA_BUFFER_START);     // enable the DMA operation - a rising edge on the port will now clear the the SPI status register
        fnConfigureInterrupt((void *)&interrupt_setup);                  // configure cs port DMA (rising edge)
    }

This uses 4 DMA channels and represents just one of probably numerous possibilities to do more or less the same thing. There may be more efficient ones but this was simple to set up using the uTasker DMA and port interrupt/DMA APIs.

Regards

Mark

0 Kudos
Reply

2,162 Views
akimata
Contributor IV

I made some progress in my code, using some tips You gave me. It's working perfectly fine when there is just one senor connected to SPI. It get worse when i got 2 sensors, 2 interrupts and i need to reconfigurate DMA every time there is new interrupt (both sensors requires different data to be sent) and stored in different arrays.

So here is my new question: is it a good idea to reconfigurate DMA so often?

Is there any easier way to send  8 bytes when INT1 is active and 7 bytes when INT2 is active and then repeat it also with receiving data to one array when INT1 is active and when INT2 is to another or just DMA is better when sending same packs of data all the time?

0 Kudos
Reply