AnsweredAssumed Answered

SPI WITH DMA NOT WORKING ON MK22FN512

Question asked by Richard Mensah on Jan 5, 2019
Latest reply on Jan 8, 2019 by Hui_Ma

Hello, can someone help me to get this project to work. I have been trying for several days to get SPI to trasfer data using DMA controller on MK22FN512 microcontroller without success. I am using MCUxpresso IDE and SDK 2.5 and I have read the manual for SPI, DMA and DMAMUX . My idea is to get the spi to issue a Transmit FIFO fill request when the program begins,the DMA should respond by coping  Command+Data from memory into the PUSHR register of the SPI. then the SPI should send that data. I am using a Major loop count of 1 and channel linking on major loop complete. What i want to do is to have the first channel transfer control to an Always on channel which should be responsible for copying the received data from SPI_POPR register to memory. After 16 data copies into memory, I want the second channel to generate an interrupt. I have managed to get the spi to send the data, put the interrupts are not firing. Can someone guide me what I am doing wrong.


#include "clock_config.h"
#include "MK22F51212.h"
#include "fsl_dspi.h"
#include "fsl_gpio.h"
#include "fsl_port.h"
#include "fsl_clock.h"
#include "fsl_edma.h"
#include "fsl_dmamux.h"
#include "fsl_pit.h"
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include "timers.h"
#include "queue.h"




enum PINS {


     PTE3   = 3,                   // Encoder_a Data
     PTE0   = 0,                   // Encoder_a chip select
     PTE2   = 2,                   // Encoder_a Clk

     PTB23  = 23,                  // Encoder_b Data
     PTB20  = 20,                  // Encoder_b chip select
     PTB21  = 21,                  // Encoder_b Clk

};


void Delay(volatile uint32_t k) {
     while (k > 0) {
          k--;
          asm("");
     }
}

volatile uint32_t masterCommand0;

uint32_t master_data[1] = {0};
uint16_t destination_data[16] = {100,100,100,100,200,300,15,20,100,600,900,100,500,100,600,20};

void ActuatorControl(void* parameters);
TaskHandle_t                ActuatorAhandle  = NULL;
SemaphoreHandle_t           DMA_type_mutex = NULL;
int main(void) {

     {
          BOARD_BootClockHSRUN();
          CLOCK_EnableClock(kCLOCK_PortE);
          PORT_SetPinMux(PORTE, PTE0, kPORT_MuxAlt2);  // SPI0_PCS1
          PORT_SetPinMux(PORTE, PTE2, kPORT_MuxAlt2);  // SPI1_SCK
          PORT_SetPinMux(PORTE, PTE3, kPORT_MuxAlt2);  // SPI1_SIN
     }


     {
          dspi_command_data_config_t commandData;
          commandData.isPcsContinuous = false;
          commandData.whichCtar = kDSPI_Ctar0;
          commandData.whichPcs = kDSPI_Pcs1;
          commandData.isEndOfQueue = false;
          commandData.clearTransferCount = false;

          masterCommand0 = DSPI_MasterGetFormattedCommand(&commandData);
          master_data[0]     = masterCommand0|SPI_PUSHR_TXDATA(300);                             // preparing SPI commands for DMA to copy to SPI memory

          dspi_master_config_t master_config;
          master_config.whichCtar = kDSPI_Ctar0;
          master_config.ctarConfig.bitsPerFrame = 13;
          master_config.ctarConfig.baudRate =  800000;
          master_config.ctarConfig.direction = kDSPI_MsbFirst;
          master_config.ctarConfig.cpol = kDSPI_ClockPolarityActiveLow;
          master_config.ctarConfig.cpha = kDSPI_ClockPhaseSecondEdge;
          master_config.ctarConfig.pcsToSckDelayInNanoSec = 700;
          master_config.ctarConfig.lastSckToPcsDelayInNanoSec = 700;
          master_config.ctarConfig.betweenTransferDelayInNanoSec = 700;
          master_config.enableRxFifoOverWrite = false;
          master_config.enableContinuousSCK = false;
          master_config.whichPcs = kDSPI_Pcs1;
          master_config.pcsActiveHighOrLow = kDSPI_PcsActiveLow;
          master_config.samplePoint = kDSPI_SckToSin0Clock;
          master_config.enableModifiedTimingFormat = false;

          DSPI_MasterInit(SPI1,&master_config,CLOCK_GetBusClkFreq());
          DSPI_SetFifoEnable(SPI1,false,false);
          DSPI_StopTransfer(SPI1);
          DSPI_EnableDMA(SPI1,kDSPI_TxDmaEnable);                                      // allow the SPI to issue Transmit FIFO Fill DMA request
          DSPI_StartTransfer(SPI1);

     }

     {
          DMAMUX_Init(DMAMUX);
          DMAMUX_SetSource(DMAMUX,0,(uint8_t)kDmaRequestMux0SPI1);
          DMAMUX_SetSource(DMAMUX,1,(uint8_t)kDmaRequestMux0AlwaysOn60);

          DMAMUX_DisablePeriodTrigger(DMAMUX,0);
          DMAMUX_DisablePeriodTrigger(DMAMUX,1);

          DMAMUX_EnableChannel(DMAMUX,0);
          DMAMUX_EnableChannel(DMAMUX,1);

     }

     {
          CLOCK_EnableClock(kCLOCK_Dma0);
          DMA0->CR                                     = DMA_CR_EMLM(0b0)|DMA_CR_CLM(0b0)|DMA_CR_ERCA(0b1)|DMA_CR_EDBG(0b1)|DMA_CR_HOE(0b1);                           // minorloopmapping_disabled+continouslinkmode_disabled
                                                                                                                                                                                                                   //+halt_on_error_true+debug_on+roundrobbin

          DMA0->SEEI                                    = DMA_SEEI_SEEI(0b0)|DMA_SEEI_SAEE(0b0)|DMA_SEEI_NOP(0b0);                                                         // enable error interrupt on channel  0
          DMA0->INT                                    |= DMA_INT_INT0(0b0);                                                                                                          // disable other interrupts on channel 0

          DMA0->SEEI                                    = DMA_SEEI_SEEI(0b1)|DMA_SEEI_SAEE(0b0)|DMA_SEEI_NOP(0b0);                                                         // enable error interrupt on channel  1
          DMA0->INT                                    |= DMA_INT_INT1(0b1);                                                                                                          // enable other interrupts on channel 1
     }

     {
          EDMA_ResetChannel(DMA0,0);
          DMA0->TCD[0].SADDR                          = DMA_SADDR_SADDR(master_data);                                                                                             // address of the first element of the array. which is held in the variable name master_data
          DMA0->TCD[0].SOFF                         = DMA_SOFF_SOFF(0);                                                                                                             // what is the sizeof each element of the array. should evaluate to 4 bytes
          DMA0->TCD[0].DADDR                         = DMA_DADDR_DADDR(&(SPI1->PUSHR));                                                                                     // should evaluate to the address of the PUSHR register
          DMA0->TCD[0].DOFF                         = DMA_DOFF_DOFF(0);                                                                                                    // no offset since we are writing to the same register
          DMA0->TCD[0].ATTR                         = DMA_ATTR_DSIZE(0b010)|DMA_ATTR_DMOD(0x0)|DMA_ATTR_SSIZE(0b010)|DMA_ATTR_SMOD(0x00);                         // we are copying 4 bytes of data per basic transfer
          DMA0->TCD[0].NBYTES_MLNO               = DMA_NBYTES_MLNO_NBYTES(sizeof(master_data[0]));                                                                      // should evaluate to bytesize of element x number of elements to copy per minorloop. should evaluate to 4 bytes
          DMA0->TCD[0].CITER_ELINKYES               = DMA_CITER_ELINKYES_CITER(0x1)|DMA_CITER_ELINKYES_ELINK(0b1);                                                    // setting major loop linking to 1, CITER size is 1
          DMA0->TCD[0].BITER_ELINKYES               = DMA_BITER_ELINKYES_BITER(0x1)|DMA_BITER_ELINKYES_ELINK(0b1);                                                    // setting major loop linking to 1, BITER SIZE is 1
          DMA0->TCD[0].SLAST                         = DMA_SLAST_SLAST(0);                                                                                                       // should evaluate to the negative of bytesize of element x number of elements to copy x BITER SIZE
          DMA0->TCD[0].DLAST_SGA                    = DMA_DLAST_SGA_DLASTSGA(0x0);

          DMA0->CDNE                                   = DMA_CDNE_CDNE(0b00)|DMA_CDNE_CADN(0b0)|DMA_CDNE_NOP(0b0);
          DMA0->TCD[0].CSR                         = DMA_CSR_START(0b0)|DMA_CSR_INTMAJOR(0b0)|DMA_CSR_DREQ(0b0)|DMA_CSR_MAJORELINK(0b1)|DMA_CSR_MAJORLINKCH(0b1); // hardware_initiates_start+donot_intpt_on_major_loop_complete+
                                                                                                                                                                                                 //donot_disable_channelrequest_afer_majorloop+major_link_enabled+
     }

     {
          EDMA_ResetChannel(DMA0,1);
          DMA0->TCD[1].SADDR                          = DMA_SADDR_SADDR(&(SPI1->POPR));                                                                                        // address of the SPI1 POPR register
          DMA0->TCD[1].SOFF                         = DMA_SOFF_SOFF(0x0);                                                                                                        // 0 bytes.
          DMA0->TCD[1].DADDR                         = DMA_DADDR_DADDR(destination_data);                                                                                     // address of the first element of the array. which is held in the variable name destination_data
          DMA0->TCD[1].DOFF                         = DMA_DOFF_DOFF(sizeof(destination_data[0]));                                                                      // what is the sizeof each element of the array. should evaluate to 2 bytes
          DMA0->TCD[1].ATTR                         = DMA_ATTR_DSIZE(0b001)|DMA_ATTR_DMOD(0x0)|DMA_ATTR_SSIZE(0b001)|DMA_ATTR_SMOD(0x00);                         // we are coping 2 bytes of data per basic transfer
          DMA0->TCD[1].NBYTES_MLNO               = DMA_NBYTES_MLNO_NBYTES(sizeof(destination_data[0]));                                                                 // should evaluate to bytesize of element x number of elements to copy per minor loop. should evaluate to 2 bytes
          DMA0->TCD[1].CITER_ELINKNO               = DMA_CITER_ELINKNO_CITER(0x10)|DMA_CITER_ELINKNO_ELINK(0b0);                                                         // setting major loop linking to false, CITER size is 4
          DMA0->TCD[1].BITER_ELINKNO               = DMA_BITER_ELINKNO_BITER(0x10)|DMA_BITER_ELINKNO_ELINK(0b0);                                                         // setting major loop linking to false, BITER SIZE is 4
          DMA0->TCD[1].SLAST                         = DMA_SLAST_SLAST(0x0);                                                                                                       // using the same POPR address. set to 0
          DMA0->TCD[1].DLAST_SGA                    = DMA_DLAST_SGA_DLASTSGA(-1*sizeof(destination_data[0])*0x10);                                                  // should evaluate to bytesize of element x BITERSIZE x DOFF. should evaluate to -32 bytes

          DMA0->CDNE                                   = DMA_CDNE_CDNE(0b1)|DMA_CDNE_CADN(0b0)|DMA_CDNE_NOP(0b0);
          DMA0->TCD[1].CSR                         = DMA_CSR_START(0b0)|DMA_CSR_INTMAJOR(0b1)|DMA_CSR_DREQ(0b0)|DMA_CSR_MAJORELINK(0b0)|DMA_CSR_MAJORLINKCH(0b1); // hardware_initiates_start+intpt_on_major_loop_complete+
                                                                                                                                                                                                 //dont_disable_channelrequest_afer_majorloop+major_link_disabled+
     }

     NVIC_SetPriority(DMA1_IRQn, 7);
     NVIC_ClearPendingIRQ(DMA1_IRQn);
     NVIC_EnableIRQ(DMA1_IRQn);


     if (xTaskCreate(ActuatorControl, "Actuator_A", 2000, NULL, 4,
          &ActuatorAhandle) == errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY)
     exit(-1);

     vTaskStartScheduler();

     while(true)
     {

     }

    return 0 ;
}

void DMA1_IRQHandler()
{

     static BaseType_t xHigherPriorityTaskWoken = pdFALSE;


     if ( (DMA0->ERR) & 0b10) // an error has occurred on the channel
     {
          DMA0->CERR = DMA_CERR_CERR(0b10)|DMA_CERR_CAEI(0b0)|DMA_CERR_NOP(0b0);           // clear the error register
     }

     if (  DMA0->TCD[1].CSR &  DMA_CSR_DONE(0b1)  )                                                    // this interrupt is because a major loop has finished
     {
          EDMA_DisableChannelRequest(DMA0,0);                                                             // disable channel 0 so no TFFX is honored. done to enable encoder to rest
          EDMA_DisableChannelRequest(DMA0,1);                                                             // disable channel 0 so no TFFX is honored. done to enable encoder to rest
          xSemaphoreGiveFromISR(DMA_type_mutex,&xHigherPriorityTaskWoken);                          // give mutex here since the channel has already been disabled
                                                                                           // by hardware
     }

     NVIC_ClearPendingIRQ(DMA1_IRQn);
     NVIC_DisableIRQ(DMA1_IRQn);


}


void ActuatorControl(void* parameters)
{
     portTickType xBoardLastWakeTime;
     xBoardLastWakeTime = xTaskGetTickCount();
     uint8_t blocktime = 1;

     DMA_type_mutex = xSemaphoreCreateBinary();

     EDMA_EnableChannelRequest(DMA0,0);
     EDMA_EnableChannelRequest(DMA0,1);

     for(;;)
     {


          if (xSemaphoreTake(DMA_type_mutex,0))
          {
               EDMA_EnableChannelRequest(DMA0,1);
               EDMA_EnableChannelRequest(DMA0,0);
               NVIC_EnableIRQ(DMA1_IRQn);

          }

          vTaskDelayUntil(&xBoardLastWakeTime, blocktime / portTICK_RATE_MS);

     }

     vTaskDelete( NULL);

}

Outcomes