This question follows on from thread 504020 (https://community.nxp.com/thread/504020) which relates to the setup of the FTM, eDMA and DMAMUX modules in the processor on the FRDM K64F dev board for SPWM generation from a DMA buffer.
Ultimately I'd like to have two outputs from (for example) FTM0 CH6 and CH7 where the channel 6 and channel 7 outputs are the compliment of each-other both producing SPWM to drive a full bridge. As far as I can see this requires the FTM to be in enhanced mode and to have software triggering enabled (since the DMA is not a hardware trigger). If keep the FTM in the non-enhanced mode FTMx_MODE[FTMEN] = 0; it appears that the combine functionality enabled in FTMx_COMBINE and FTMxCnSC (by setting ELSnB:ELSnA appropriately) can not be used. I have not tried to insert dead-time yet but the registers are self-evident for this.
To cause the FTM to update its buffered registers from their respective buffers FTMx_SYNC bit 7 (SWSYNC) should be set. The FTM hardware will clear it (details in the processor manual) shortly afterwards and the buffered values of CnV and other registers depending on the settings in FTMx_SYNCONF will end up in the registers.
It may be that only one DMA is necessary for the combined channels and this runs into CH n + 1 while CH n value is set to zero (or if phase shifted PWM is desired the CH0 value could be modified, presumably to provide the phase offset). I elected to set up two DMAs running independently i.e. not in combined mode so I could see the two outputs and then add in the code to combine them and if necessary comment out one of the DMAs.
After a good deal of tinkering I've reached the conclusion that FTMx_SYNC bit 7 needs to be raised at every DMA minor loop transfer. This seems a very odd way of going about things as I have solved the question of how to do this by adding a third DMA which runs into the SYNC register and pushes SWSYNC high when the other two DMAs update the C6V and C7V registers.
My question is, is this how the FTM module designer intended this sort of problem to be solved? It seems very inelegant and I'm sure I'm barking up the wrong tree.
Should I, for example, abandon the use of combined mode and run two DMAs with complementary data in their buffers thereby producing the complementary SPWM. If I took this approach what mechanism would be available to ensure synchronisation of the edges and would I still be able to use the dead-time feature of the FTM? - Trying this is the plan for tomorrow...
Code representing the attempt at combined mode and which, naturally, does not work, below.
The code below functions with the FTM operating as the older style timer and producing two identical SPWM signals on two independent channels. To arrange this several lines need commenting and un-commenting. The Synchronisation DMA doesn't flag anything in the DMA error status register.
unsigned short CnVData[BUFF_LENGTH], CnVPlusData[BUFF_LENGTH];
uint8_t SyncData[BUFF_LENGTH];
unsigned short *ptrPWMABuffer = &CnVData[0];
unsigned short *ptrPWMBBuffer = &CnVPlusData[0];
unsigned short *ptrSYNCBuffer = &SyncData[0];
static void fnBareMetalSPWM(unsigned short *ptrPWMABuffer, unsigned short *ptrPWMBBuffer, unsigned short *ptrSYNCBuffer, unsigned long ulBufferLength, unsigned long syncBufferLength)
{
*(volatile unsigned long *)(SIM_BLOCK + SIM_PORT_CLK_GATE_OFFSET) |= 0x00000200;
*(volatile unsigned long *)(SIM_BLOCK + 0x103c) |= 0x01000000;
*(volatile unsigned long *)(PORTA_BLOCK + 0x04) = (0x00000300 | 0x00000040);
*(volatile unsigned long *)(PORTA_BLOCK + 0x08) = (0x00000300 | 0x00000040);
*(unsigned long *)(FTM0_BASE_ADDR + 0x084) = 0x000000c0;
*(unsigned long *)(FTM0_BASE_ADDR + 0x054) = 0x00000004;
*(unsigned long *)(FTM0_BASE_ADDR + 0x054) = (0x00000004 | 0x00000001);
*(unsigned long *)(FTM0_BASE_ADDR + 0x58) = (0x00000003 | 0x80);
*(unsigned long *)(FTM0_BASE_ADDR + 0x8C) = (0x80 | 0x100 | 0x200);
/
/
*(signed short *)(eDMA_DESCRIPTORS + 0x024) = sizeof(unsigned short);
*(signed short *)(eDMA_DESCRIPTORS + 0x034) = 0;
*(volatile signed long *)(eDMA_DESCRIPTORS + 0x038) = 0;
*(volatile unsigned long *)(eDMA_DESCRIPTORS + 0x02c) = (-(signed long)(ulBufferLength));
*(unsigned short *)(eDMA_DESCRIPTORS + 0x026) = (0x0001 | 0x0100);
*(volatile unsigned long *)(eDMA_DESCRIPTORS + 0x020) = (unsigned long)ptrPWMABuffer;
*(volatile unsigned short *)(eDMA_DESCRIPTORS + 0x03c) = 0;
*(volatile unsigned long *)(eDMA_DESCRIPTORS + 0x030) = (unsigned long)((FTM0_BASE_ADDR + FTM0_C6V_OFFSET));
*(volatile unsigned long *)(eDMA_DESCRIPTORS + 0x028) = sizeof(unsigned short);
*(unsigned short *)(eDMA_DESCRIPTORS + 0x03e) = *(volatile signed short *)(eDMA_DESCRIPTORS + 0x036) = (signed short)(ulBufferLength / sizeof(unsigned short));
*(signed short *)(eDMA_DESCRIPTORS + 0x044) = sizeof(unsigned short);
*(signed short *)(eDMA_DESCRIPTORS + 0x054) = 0;
*(volatile signed long *)(eDMA_DESCRIPTORS + 0x058) = 0;
*(volatile unsigned long *)(eDMA_DESCRIPTORS + 0x04c) = (-(signed long)(ulBufferLength));
*(unsigned short *)(eDMA_DESCRIPTORS + 0x046) = (0x0001 | 0x0100);
*(volatile unsigned long *)(eDMA_DESCRIPTORS + 0x040) = (unsigned long)ptrPWMBBuffer;
*(volatile unsigned short *)(eDMA_DESCRIPTORS + 0x05c) = 0;
*(volatile unsigned long *)(eDMA_DESCRIPTORS + 0x050) = (unsigned long)((FTM0_BASE_ADDR + FTM0_C7V_OFFSET));
*(volatile unsigned long *)(eDMA_DESCRIPTORS + 0x048) = sizeof(unsigned short);
*(unsigned short *)(eDMA_DESCRIPTORS + 0x05e) = *(volatile signed short *)(eDMA_DESCRIPTORS + 0x056) = (signed short)(ulBufferLength / sizeof(unsigned short));
*(signed short *)(eDMA_DESCRIPTORS + 0x064) = sizeof(uint8_t);
*(signed short *)(eDMA_DESCRIPTORS + 0x074) = 0;
*(volatile signed long *)(eDMA_DESCRIPTORS + 0x078) = 0;
*(volatile unsigned long *)(eDMA_DESCRIPTORS + 0x06c) = (-(signed long)(syncBufferLength));
*(unsigned short *)(eDMA_DESCRIPTORS + 0x066) = 0x000;
*(volatile unsigned long *)(eDMA_DESCRIPTORS + 0x060) = (unsigned long)ptrSYNCBuffer;
*(volatile unsigned short *)(eDMA_DESCRIPTORS + 0x07c) = 0;
*(volatile unsigned long *)(eDMA_DESCRIPTORS + 0x070) = (unsigned long)((FTM0_BASE_ADDR + 0x58));
*(volatile unsigned long *)(eDMA_DESCRIPTORS + 0x068) = sizeof(uint8_t);
*(unsigned short *)(eDMA_DESCRIPTORS + 0x07e) = *(volatile signed short *)(eDMA_DESCRIPTORS + 0x076) = (signed short)(syncBufferLength / sizeof(uint8_t));
*(volatile unsigned long *)(SIM_BLOCK + 0x103c) |= 0x00000002;
*(unsigned char *)(DMAMUX0_BLOCK + 0x01) = (unsigned char)(DMAMUX_SOURCE_SLOT_FTM0_CH6 | 0x80);
*(unsigned char *)(DMAMUX0_BLOCK + 0x02) = (unsigned char)(DMAMUX_SOURCE_SLOT_FTM0_CH7 | 0x80);
*(unsigned char *)(DMAMUX0_BLOCK + 0x03) = (unsigned char)(DMAMUX_SOURCE_SLOT_FTM0_CH6 | 0x80);
*(volatile unsigned long *)(eDMA_BASE_ADDR + 0x00c) |= 0x00000002;
*(volatile unsigned long *)(eDMA_BASE_ADDR + 0x00c) |= 0x00000004;
*(volatile unsigned long *)(eDMA_BASE_ADDR + 0x00c) |= 0x00000008;
*(volatile unsigned long *)(FTM0_BASE_ADDR + 0x000) = 0x00000008;
}
int main(void) {
int i = 0;
unsigned short dutyCyclePercent[BUFF_LENGTH] = {50, 69, 85, 96, 99, 96, 85, 69, 50, 31, 15, 4, 1, 4, 15, 31};
*(volatile unsigned long *)(SIM_BLOCK + 0x103c) |= 0x01000000;
*(unsigned long *)(FTM0_BASE_ADDR + FTM0_MOD) = 0x1770;
unsigned long *mod;
mod = (FTM0_BASE_ADDR + FTM0_MOD);
for(i = 0 ; i<BUFF_LENGTH; i++)
{
CnVData[i] = 0;
CnVPlusData[i] = (*mod * (dutyCyclePercent[i]) / 100);
SyncData[i] = 131;
}
fnBareMetalSPWM(CnVData, CnVPlusData, SyncData, sizeof(CnVData), sizeof(SyncData));
}
Cheers,
James