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;
}
}
Christian
Experiment with the crossbar switch settings to see whether you can optiise jitter for your requirement.
It my not be relevant if you don't have parallel DMA operation but may never the less be worth testing.
My default setting is changed from the the chip's default to avoid memory to memory DMA busts from causing potentially high latency to peripheral DMA use (I find a round-robin strategy the best general compromise):
AXBS_CRS0 = AXBS_CRS_ARB_ROUND_ROBIN;
AXBS_CRS1 = AXBS_CRS_ARB_ROUND_ROBIN;
Regards
Mark
Complete Kinetis solutions for professional needs, training and support:http://www.utasker.com/kinetis.html
Kinetis K60/K10:
- http://www.utasker.com/kinetis/TWR-K60N512.html
- http://www.utasker.com/kinetis/TWR-K60D100M.html
- http://www.utasker.com/kinetis/TWR-K60F120M.html
- http://www.utasker.com/kinetis/ELZET80_NET-KBED.html
- http://www.utasker.com/kinetis/ELZET80_NET-K60.html
uTasker: supporting >1'000 registered Kinetis users get products faster and cheaper to market