AnsweredAssumed Answered

FRDM K64F FTM + DMA for SPWM Generation in complementary combined mode with deadtime

Question asked by James Green on Jun 5, 2019
Latest reply on Jun 6, 2019 by James Green

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.

 

// globals
// Buffs used by DMA to hold the data that will be passed to the FTM.
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)
{

     /**********************************************************************************
      * PORT SETUP
      * ********************************************************************************
      */


     // System Clock Gating Control Register 5 (SIM_SCGC5), switch on clock for port A
     *(volatile unsigned long *)(SIM_BLOCK + SIM_PORT_CLK_GATE_OFFSET) |= 0x00000200;

     // FTM. Ensure that the FlexTimer/TPM module is powered up
    *(volatile unsigned long *)(SIM_BLOCK + 0x103c) |= 0x01000000;

    // FTM0_CH6 on PTA1 (alt. function 3) and high drive strength
    *(volatile unsigned long *)(PORTA_BLOCK + 0x04) = (0x00000300 | 0x00000040);

    // FTM0_CH7 on PTA2 (alt. function 3)
     *(volatile unsigned long *)(PORTA_BLOCK + 0x08) = (0x00000300 | 0x00000040);

     /**********************************************************************************
      * Timer Channel n Setup
      * ********************************************************************************
      */


    // allow timer to continue operating when debugging
    *(unsigned long *)(FTM0_BASE_ADDR + 0x084) = 0x000000c0;

    // Disable Write protection on the FTMx_MODE.
    *(unsigned long *)(FTM0_BASE_ADDR + 0x054) = 0x00000004;

    // Features Mode Selection (FTMx_MODE) Switch on the enhanced FTM features and keep write protection disabled
    *(unsigned long *)(FTM0_BASE_ADDR + 0x054) = (0x00000004 | 0x00000001);

    // Synchronisation (FTMx_SYNC). Enable loading at maximum (i.e. CNT == MOD and CNT == CNTIN) and software triggering is enabled.
    *(unsigned long *)(FTM0_BASE_ADDR + 0x58) = (0x00000003 | 0x80);

    // Synchronization Configuration (FTMx_SYNCONF)
    // set SYNCMODE = 1 (Enhanced PWM synchronization is selected.)
    // set SWRSTCNT = 1 (FTM counter synchronization is activated by the software trigger)
    // set SWWRBUF = 1 (MOD, CNTIN, and CV registers synchronization is activated by the software trigger.)
    *(unsigned long *)(FTM0_BASE_ADDR + 0x8C) = (0x80 | 0x100 | 0x200);

    // Channel (n) Status And Control (FTMx_CnSC) high-true pulses/enable DMA trigger from this channel
    //*(volatile unsigned long *)(FTM0_BASE_ADDR + FTM0_C6SC_OFFSET) = (0x00000020 | 0x00000008 | 0x00000001 | 0x00000040);

    // Channel (n) Status And Control (FTMx_CnSC) Combined mode with DMA and Channel Interrupts Enabled (needed for DMA request generation)
    *(volatile unsigned long *)(FTM0_BASE_ADDR + FTM0_C6SC_OFFSET) = (0x0000008 | 0x0000040 | 0x00000001);

    // Set counter initial value to zero
    *(volatile unsigned long *)(FTM0_BASE_ADDR + 0x04c) = 0;

    // Set counter MOD register to the value that provides the frequency of interest.
    *(unsigned long *)(FTM0_BASE_ADDR + FTM0_MOD) = 0x1770; // 10 khz with a prescaler of 1 and a 120MHz clock.

    //  FTM External Trigger (FTMx_EXTTRIG) trigger from CH0 ( is this how we should try to trigger ADC?)
    //*(volatile unsigned long *)(FTM0_BASE_ADDR + 0x6C) = 0x10;

    // prepare first PWM value (place it in the CnV register)
    *(volatile unsigned long *)(FTM0_BASE_ADDR + FTM0_C6V_OFFSET) = *ptrPWMABuffer;

     /**********************************************************************************
      * Timer Channel n+1 Setup
      * ********************************************************************************
      */


    // Channel (n) Status And Control (FTMx_CnSC) high-true pulses/enable DMA trigger from this channel
    //*(volatile unsigned long *)(FTM0_BASE_ADDR + FTM0_C7SC_OFFSET) = (0x00000020 | 0x00000008 | 0x00000001 | 0x00000040);

    // Channel (n) Status And Control (FTMx_CnSC)
    // Set SnSC to Combined PWM mode with DMA
    *(volatile unsigned long *)(FTM0_BASE_ADDR + FTM0_C7SC_OFFSET) = (0x0000008 | 0x0000040 | 0x00000001);

    // Function For Linked Channels (FTMx_COMBINE).
    // Set the channels to combined mode and set them as complimentary and switch on synchronisation
    *(unsigned long *)(FTM0_BASE_ADDR + 0x64) = (0x20000000 | 0x1000000 | 0x2000000);

    // prepare first PWM value (place it in the CnV register)
    *(volatile unsigned long *)(FTM0_BASE_ADDR + FTM0_C7V_OFFSET) = *ptrPWMBBuffer;

     /**********************************************************************************
      * Channel n DMA Setup
      * ********************************************************************************
      */


    // eDMA TCD Signed Source Address Offset (DMA_TCD1_SOFF) i.e. source address increment per DMA minor loop
    *(signed short *)(eDMA_DESCRIPTORS + 0x024) = sizeof(unsigned short);

    // eDMA TCD Signed Destination Address Offset (DMA_TCD1_DOFF) i.e. destination address is not incremented
    *(signed short *)(eDMA_DESCRIPTORS + 0x034) = 0;

    // eDMA TCD Last Destination Address Adjustment/Scatter Gather Address (DMA_TCD1_DLASTSGA). No destination displacement on transmit buffer completion
    *(volatile signed long *)(eDMA_DESCRIPTORS + 0x038) = 0;

    // eDMA TCD Last Source Address Adjustment (DMA_TCD1_SLAST). When the buffer has been transmitted set the destination back to the start of the buffer.
    *(volatile unsigned long *)(eDMA_DESCRIPTORS + 0x02c) = (-(signed long)(ulBufferLength));

    // eDMA TCD Transfer Attributes (DMA_TCD1_ATTR). Transfer size is half-words i.e. 16 bits, the same as the destination register width.
    *(unsigned short *)(eDMA_DESCRIPTORS + 0x026) = (0x0001 | 0x0100);

    // eDMA TCD Source Address (DMA_TCD1_SADDR).
    *(volatile unsigned long *)(eDMA_DESCRIPTORS + 0x020) = (unsigned long)ptrPWMABuffer;

    // eDMA TCD Control and Status (DMA_TCD1_CSR). Free-running mode without any interrupt
    *(volatile unsigned short *)(eDMA_DESCRIPTORS + 0x03c) = 0;

    // eDMA TCD Destination Address (DMA_TCD1_DADDR). Destination address is the FTM CnV register (the PWM register)
    *(volatile unsigned long *)(eDMA_DESCRIPTORS + 0x030) = (unsigned long)((FTM0_BASE_ADDR + FTM0_C6V_OFFSET));

    // eDMA TCD Minor Byte Count (Minor Loop Mapping Disabled) (DMA_TCD1_NBYTES_MLNO). Each request starts a single transfer of this size (minor byte transfer count)
    *(volatile unsigned long *)(eDMA_DESCRIPTORS + 0x028) = sizeof(unsigned short);

    // eDMA TCD Beginning Minor Loop Link, Major Loop Count (Channel Linking Disabled) (DMA_TCD1_BITER_ELINKNO).
    // The number of service requests to be performed each buffer cycle.
    *(unsigned short *)(eDMA_DESCRIPTORS + 0x03e) = *(volatile signed short *)(eDMA_DESCRIPTORS + 0x036) = (signed short)(ulBufferLength / sizeof(unsigned short));


     /**********************************************************************************
      * Channel n+1 DMA Setup
      * ********************************************************************************
      */


    // eDMA TCD Signed Source Address Offset (DMA_TCD2_SOFF) i.e. source address increment per DMA minor loop
    *(signed short *)(eDMA_DESCRIPTORS + 0x044) = sizeof(unsigned short);

    // eDMA TCD Signed Destination Address Offset (DMA_TCD2_DOFF) i.e. destination address is not incremented
    *(signed short *)(eDMA_DESCRIPTORS + 0x054) = 0;

    // eDMA TCD Last Destination Address Adjustment/Scatter Gather Address (DMA_TCD2_DLASTSGA). No destination displacement on transmit buffer completion
    *(volatile signed long *)(eDMA_DESCRIPTORS + 0x058) = 0;

    // eDMA TCD Last Source Address Adjustment (DMA_TCD2_SLAST). When the buffer has been transmitted set the destination back to the start of the buffer.
    *(volatile unsigned long *)(eDMA_DESCRIPTORS + 0x04c) = (-(signed long)(ulBufferLength));

    // eDMA TCD Transfer Attributes (DMA_TCD2_ATTR). Transfer size is half-words i.e. 16 bits, the same as the destination register width.
    *(unsigned short *)(eDMA_DESCRIPTORS + 0x046) = (0x0001 | 0x0100);

    // eDMA TCD Source Address (DMA_TCD2_SADDR).
    *(volatile unsigned long *)(eDMA_DESCRIPTORS + 0x040) = (unsigned long)ptrPWMBBuffer;

    // eDMA TCD Control and Status (DMA_TCD2_CSR). Free-running mode without any interrupt
    *(volatile unsigned short *)(eDMA_DESCRIPTORS + 0x05c) = 0;

    // eDMA TCD Destination Address (DMA_TCD2_DADDR). Destination address is the FTM CnV register (the PWM register)
    *(volatile unsigned long *)(eDMA_DESCRIPTORS + 0x050) = (unsigned long)((FTM0_BASE_ADDR + FTM0_C7V_OFFSET));

    // eDMA TCD Minor Byte Count (Minor Loop Mapping Disabled) (DMA_TCD2_NBYTES_MLNO). Each request starts a single transfer of this size (minor byte transfer count)
    *(volatile unsigned long *)(eDMA_DESCRIPTORS + 0x048) = sizeof(unsigned short);

    // eDMA TCD Beginning Minor Loop Link, Major Loop Count (Channel Linking Disabled) (DMA_TCD2_BITER_ELINKNO).
    // The number of service requests to be performed each buffer cycle.
    *(unsigned short *)(eDMA_DESCRIPTORS + 0x05e) = *(volatile signed short *)(eDMA_DESCRIPTORS + 0x056) = (signed short)(ulBufferLength / sizeof(unsigned short));


     /**********************************************************************************
      * Sync Channel DMA Setup
      * ********************************************************************************
      */


    // eDMA TCD Signed Source Address Offset (DMA_TCD3_SOFF) i.e. source address increment per DMA minor loop
    *(signed short *)(eDMA_DESCRIPTORS + 0x064) = sizeof(uint8_t);

    // eDMA TCD Signed Destination Address Offset (DMA_TCD3_DOFF) i.e. destination address is not incremented
    *(signed short *)(eDMA_DESCRIPTORS + 0x074) = 0;

    // eDMA TCD Last Destination Address Adjustment/Scatter Gather Address (DMA_TCD3_DLASTSGA). No destination displacement on transmit buffer completion
    *(volatile signed long *)(eDMA_DESCRIPTORS + 0x078) = 0;

    // eDMA TCD Last Source Address Adjustment (DMA_TCD3_SLAST). When the buffer has been transmitted set the destination back to the start of the buffer.
    *(volatile unsigned long *)(eDMA_DESCRIPTORS + 0x06c) = (-(signed long)(syncBufferLength));

    // eDMA TCD Transfer Attributes (DMA_TCD3_ATTR). Transfer size is bytes i.e. 8 bits, the same as the destination register width.
    *(unsigned short *)(eDMA_DESCRIPTORS + 0x066) = 0x000;

    // eDMA TCD Source Address (DMA_TCD3_SADDR).
    *(volatile unsigned long *)(eDMA_DESCRIPTORS + 0x060) = (unsigned long)ptrSYNCBuffer;

    // eDMA TCD Control and Status (DMA_TCD3_CSR). Free-running mode without any interrupt
    *(volatile unsigned short *)(eDMA_DESCRIPTORS + 0x07c) = 0;

    // eDMA TCD Destination Address (DMA_TCD3_DADDR). Destination address is the FTM
    *(volatile unsigned long *)(eDMA_DESCRIPTORS + 0x070) = (unsigned long)((FTM0_BASE_ADDR + 0x58));

    // eDMA TCD Minor Byte Count (Minor Loop Mapping Disabled) (DMA_TCD3_NBYTES_MLNO). Each request starts a single transfer of this size (minor byte transfer count)
    *(volatile unsigned long *)(eDMA_DESCRIPTORS + 0x068) = sizeof(uint8_t);

    // eDMA TCD Beginning Minor Loop Link, Major Loop Count (Channel Linking Disabled) (DMA_TCD3_BITER_ELINKNO).
    // The number of service requests to be performed each buffer cycle.
    *(unsigned short *)(eDMA_DESCRIPTORS + 0x07e) = *(volatile signed short *)(eDMA_DESCRIPTORS + 0x076) = (signed short)(syncBufferLength / sizeof(uint8_t));



     /**********************************************************************************
      * Channel n DMA Multiplexer Setup
      * ********************************************************************************
      */


    // System Clock Gating Control Register 6 (SIM_SCGC6). Enable DMA Multiplexer.
     *(volatile unsigned long *)(SIM_BLOCK + 0x103c) |= 0x00000002;

    // Channel Configuration register (DMAMUX_CHCFG1). Connect trigger source to DMA channel 1.
     *(unsigned char *)(DMAMUX0_BLOCK + 0x01) = (unsigned char)(DMAMUX_SOURCE_SLOT_FTM0_CH6 | 0x80);


     /**********************************************************************************
      * Channel n+1 DMA Multiplexer Setup
      * ********************************************************************************
      */


    // Channel Configuration register (DMAMUX_CHCFG2). Connect trigger source to DMA channel 2.
     *(unsigned char *)(DMAMUX0_BLOCK + 0x02) = (unsigned char)(DMAMUX_SOURCE_SLOT_FTM0_CH7 | 0x80);


       /**********************************************************************************
        * Sync Channel DMA Multiplexer Setup
        * ********************************************************************************
        */


     // Channel Configuration register (DMAMUX_CHCFG2). Connect trigger source to DMA channel 3.
      *(unsigned char *)(DMAMUX0_BLOCK + 0x03) = (unsigned char)(DMAMUX_SOURCE_SLOT_FTM0_CH6 | 0x80);



     /**********************************************************************************
      * Enable DMA Channels
      * ********************************************************************************
      */


    // eDMA Enable Request Register (DMA_ERQ). Enable the DMA channel 1 operation for FTM channel n
     *(volatile unsigned long *)(eDMA_BASE_ADDR + 0x00c) |= 0x00000002;

    // eDMA Enable Request Register (DMA_ERQ). Enable the DMA channel 2 operation for FTM channel n + 1
     *(volatile unsigned long *)(eDMA_BASE_ADDR + 0x00c) |= 0x00000004;

     // eDMA Enable Request Register (DMA_ERQ). Enable the DMA channel 3 operation for FTM channel Sync
      *(volatile unsigned long *)(eDMA_BASE_ADDR + 0x00c) |= 0x00000008;

     /**********************************************************************************
      * Enable Clock to FTM module.
      * ********************************************************************************
      */


    // FTM Status And Control (FTM0_SC). Enable FTM0 operation (system clock)
    *(volatile unsigned long *)(FTM0_BASE_ADDR + 0x000) = 0x00000008;
}

int main(void) {
      int i = 0;
     // Duty cycle in percent. This represents one cycle of a sine wave it's a 16 bit representation
     unsigned short dutyCyclePercent[BUFF_LENGTH] = {50, 69, 85, 96, 99, 96, 85, 69, 50, 31, 15, 4, 1, 4, 15, 31};

     // ensure that the FlexTimer/TPM module is powered up
    *(volatile unsigned long *)(SIM_BLOCK + 0x103c) |= 0x01000000;

    // Set counter MOD register to the value that provides the frequency of interest.
    *(unsigned long *)(FTM0_BASE_ADDR + FTM0_MOD) = 0x1770;

     unsigned long *mod;
     mod = (FTM0_BASE_ADDR + FTM0_MOD);

     // write the data that will be passed by the DMA to the FTM CnV registers.
     for(i = 0 ; i<BUFF_LENGTH; i++)
     {
          CnVData[i] = 0; //(*mod * (dutyCyclePercent[i]) / 100);
          CnVPlusData[i] = (*mod * (dutyCyclePercent[i]) / 100); //(*mod * (dutyCyclePercent[i]) / 100);
          SyncData[i] = 131;//
     }

     fnBareMetalSPWM(CnVData, CnVPlusData, SyncData, sizeof(CnVData), sizeof(SyncData));
}

 

Cheers,

 

James

Outcomes