AnsweredAssumed Answered

FTM DMA output jitter (ESC Dshot)

Question asked by Christian Lees on May 26, 2019
Latest reply on May 27, 2019 by Mark Butcher

I'm trying to get RC ESC DSHOT protocol to work on a MK12DX256.  I'm usinf the FTM to trigger 3 DMA channels.  The first transition is triggered from the FTM output pin, the second (if it is a zero then the signal goes low early) by FTM0 ch 0, the second by ch 1 (if it's a one it is later).

 

I'm seeing two issues, firstly it looks to miss the first bit completely in terms of the line switching low, it goes high and stays high.  Secondly the rest of the data has jitter on the leading edges.  There isn't a lot else going on with the MCU so I wouldn't expect this much jitter considering it is DMA driven.

 

dshot_init() sets up the DMA and FTM.  When you want to send the data you call send_shot().  There are other routines that populate dshot_data array that says if the bit is a one or a zero.  The mask determines what pins on the port are sending data, in this case it is set to 0xFF.

 

I'm seeing the frequency bounce between 301.9kHz and 296.3kHz.  If I change the code to use three FTM channels to control the transitions it doesn't miss the first one but still has jitter.

 

dshot_init()

{

ch[0].chnlNumber = 0;
ch[0].level = kFTM_HighTrue;
ch[0].dutyCyclePercent = 37;
ch[0].firstEdgeDelayPercent = 0;


ch[1].chnlNumber = 1;
ch[1].level = kFTM_HighTrue;
ch[1].dutyCyclePercent = 74;
ch[1].firstEdgeDelayPercent = 0;

 

FTM_GetDefaultConfig(&ftm_config);
ftm_config.prescale = kFTM_Prescale_Divide_1;

FTM_Init(FTM0, &ftm_config);
FTM_EnableInterrupts(FTM0, 7);

PORT_SetPinInterruptConfig(PORTC, 1, kPORT_DMARisingEdge);

 

DMAMUX_Init(DMAMUX);
DMAMUX_SetSource(DMAMUX, 0, kDmaRequestMux0PortC); //kDmaRequestMux0FTM0Channel0); //
DMAMUX_SetSource(DMAMUX, 1, kDmaRequestMux0FTM0Channel0);
DMAMUX_SetSource(DMAMUX, 2, kDmaRequestMux0FTM0Channel1);

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

 

edma_config_t edma_config;

EDMA_GetDefaultConfig(&edma_config);
edma_config.enableDebugMode = false;
edma_config.enableRoundRobinArbitration = false;
EDMA_Init(DMA0, &edma_config);


EDMA_CreateHandle(&han[0], DMA0, 0);
EDMA_CreateHandle(&han[1], DMA0, 1);
EDMA_CreateHandle(&han[2], DMA0, 2);

 

ds[0].srcAddr = (uint32_t)&dshot_mask;
ds[0].srcOffset = 0;
ds[0].destAddr = (uint32_t)&GPIOD->PSOR;
ds[0].destOffset = 0;
ds[0].minorLoopBytes = 1;
ds[0].majorLoopCounts = 16;
ds[0].srcTransferSize = kEDMA_TransferSize1Bytes;
ds[0].destTransferSize = kEDMA_TransferSize1Bytes;

 

ds[1].srcAddr = (uint32_t)&dshot_data[0];
ds[1].srcOffset = 1;
ds[1].destAddr = (uint32_t)&GPIOD->PCOR;
ds[1].destOffset = 0;
ds[1].minorLoopBytes = 1;
ds[1].majorLoopCounts = 16;
ds[1].srcTransferSize = kEDMA_TransferSize1Bytes;
ds[1].destTransferSize = kEDMA_TransferSize1Bytes;

 

ds[2].srcAddr = (uint32_t)&dshot_mask;
ds[2].srcOffset = 0;
ds[2].destAddr = (uint32_t)&GPIOD->PCOR;
ds[2].destOffset = 0;
ds[2].minorLoopBytes = 1;
ds[2].majorLoopCounts = 16;
ds[2].srcTransferSize = kEDMA_TransferSize1Bytes;
ds[2].destTransferSize = kEDMA_TransferSize1Bytes;


EDMA_SetCallback(&han[2], dshot_done_callback, NULL);


EDMA_SubmitTransfer(&han[0], &ds[0]);
EDMA_SubmitTransfer(&han[1], &ds[1]);
EDMA_SubmitTransfer(&han[2], &ds[2]);

DMA0->TCD[1].SLAST = -16;
DMA0->DCHPRI0 = 0;
DMA0->DCHPRI1 = 1;
DMA0->DCHPRI2 = 2;

NVIC_SetPriority(DMA0_IRQn, 0);

}

 

void send_dshot()
{

FTM_StopTimer(FTM0);

FTM_EnableDmaTransfer(FTM0, 0, false);
FTM_EnableDmaTransfer(FTM0, 1, false);
FTM_EnableDmaTransfer(FTM0, 2, false);

 

FTM0->SC = 0;
FTM0->CNT = 100;
FTM0->MOD = 0;
FTM0->STATUS = 0;


FTM_SetupPwm(FTM0, ch, 2, kFTM_EdgeAlignedPwm, 300000, CLOCK_GetFreq(kCLOCK_BusClk));

EDMA_StartTransfer(&han[0]);
EDMA_StartTransfer(&han[1]);
EDMA_StartTransfer(&han[2]);

FTM_EnableDmaTransfer(FTM0, 0, true);
FTM_EnableDmaTransfer(FTM0, 1, true);
FTM_EnableDmaTransfer(FTM0, 2, true);

dshot_done = 0;

FTM_StartTimer(FTM0, kFTM_SystemClock);

}

 


static inline void FTM_EnableDmaTransfer(FTM_Type *base, ftm_chnl_t chnlNumber, bool enable)
{
   if (enable)
   {
      /* Enable DMA transfer */
      base->CONTROLS[chnlNumber].CnSC |= FTM_CnSC_DMA_MASK;
   }
   else
   {
      /* Disable DMA transfer */
      base->CONTROLS[chnlNumber].CnSC &= ~FTM_CnSC_DMA_MASK;
   }
}

Outcomes