Why I can't generate an TPM overflow request to DMA?

cancel
Showing results for 
Search instead for 
Did you mean: 

Why I can't generate an TPM overflow request to DMA?

148 Views
Contributor II

Hi everybody.

I'm using the FRDM-KL46z with MCUXpresso and I want to control a NEO-Pixel LED but I need to use the TPM and DMA.

I configure the DMA with config-tools as follow.

pastedImage_1.png  And the TPM is the same and as follow.

pastedImage_2.png

#include <stdio.h>
#include "board.h"
#include "peripherals.h"
#include "pin_mux.h"
#include "clock_config.h"
#include "fsl_gpio.h"
#include "fsl_port.h"
#include "MKL46Z4.h"
/* TODO: insert other include files here. */
#define LOGIC_LED_ON 0U
#define LOGIC_LED_OFF 1U
#ifndef BOARD_LED_RED_GPIO
#define BOARD_LED_RED_GPIO GPIOE
#endif
#ifndef BOARD_LED_RED_GPIO_PIN
#define BOARD_LED_RED_GPIO_PIN 29U
#endif
#define BOARD_TPM_CHANNEL 5U
#ifndef TPM_LED_ON_LEVEL
 #define TPM_LED_ON_LEVEL kTPM_LowTrue
#endif
uint16_t Val_Cn = 10000; //the Value of duty cycle
tpm_status_flags_t flag_t = 0; //the flag of TPM status
dma_transfer_config_t TPM_CONF; //struct to config the DMA transfer to TPM
uint32_t flag_dma = 0; //the flag of DMA status.

/* TODO: insert other definitions and declarations here. */
void DMA_Callback(dma_handle_t * handle, void* param)
{
 __asm volatile ("nop"); //only for a breakpoint
}
void DMA_1_DMA_CH_INT_DONE_0_IRQHANDLER()
{
 __asm volatile ("nop"); //only for a breakpoint
 flag_t = TPM_GetStatusFlags(TPM0);
 flag_dma = DMA_GetChannelStatusFlags(DMA0, DMA_1_DMA_TPM_DMA_CHANNEL);
 DMA_ClearChannelStatusFlags(DMA0, DMA_1_DMA_TPM_DMA_CHANNEL, flag_dma);
}‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

/*
 * @brief Application entry point.
 */
int main(void) {
 /* Init board hardware. */
 BOARD_InitBootPins();
 BOARD_InitBootClocks();
 BOARD_InitBootPeripherals();


 TPM0->SC |= TPM_SC_DMA(1); //Enable the DMA on TPM
 TPM0->CONTROLS[5].CnSC |= TPM_CnSC_DMA(1); //Enable the DMA transfer on TPM channel 5

 DMA0->DMA[0].DCR |= DMA_DCR_ERQ_MASK; //enable the peripheral request

 DMA_PrepareTransfer(&TPM_CONF, &Val_Cn, sizeof(uint16_t),
 &(TPM0->CONTROLS[5].CnV), sizeof(uint16_t), 2, kDMA_MemoryToPeripheral); //prepare the struct transfer

 DMA_SubmitTransfer(&DMA_1_DMA_TPM_Handle, &TPM_CONF, kDMA_EnableInterrupt);//prepare the handle DMA_TPM

 //DMA_StartTransfer(&DMA_1_DMA_TPM_Handle);

 TPM_StartTimer(TPM0, kTPM_SystemClock); //Init the timer without interrupt request

 /* Enter an infinite loop, just incrementing a counter. */
 while(1) {

 __asm volatile ("nop");//only for a breakpoint

 flag_dma = DMA_GetChannelStatusFlags(DMA0, DMA_1_DMA_TPM_DMA_CHANNEL); //I check the status flag DMA
 flag_t = TPM_GetStatusFlags(TPM0); //I check the status flag TPM

 if(flag_t == (kTPM_TimeOverflowFlag | kTPM_Chnl5Flag)){
 GPIO_PortToggle(BOARD_LED_RED_GPIO, 1U <<
 BOARD_LED_RED_GPIO_PIN);

 TPM_ClearStatusFlags(TPM0, kTPM_TimeOverflowFlag | kTPM_Chnl5Flag); //I clean the status flags
 }
 }
 return 0 ;
}‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

But the overflow in TPM don't generate the request DMA to send the data to cnV counter of TPM only if I just not comment the Line 60 "DMA_StartTransfer" the counter register refresh it but this only execution it a once and I want to refresh the Counter register channel every time that an overflow occurs.

Thanks.

Best regards.

Labels (1)
2 Replies

7 Views
Contributor II

Hello nxf54944 thanks for your answer, I solved this problem some days ago, but now I don't use the overflow DMA request, I only use the the TPM channel request and my code is this.

 

#include <stdio.h>
#include "board.h"
#include "peripherals.h"
#include "pin_mux.h"
#include "clock_config.h"
#include "fsl_gpio.h"
#include "fsl_port.h"
#include "MKL46Z4.h"
/* TODO: insert other include files here. */

#define LOGIC_LED_ON 0U
#define LOGIC_LED_OFF 1U

#ifndef BOARD_LED_RED_GPIO
#define BOARD_LED_RED_GPIO GPIOE
#endif

#ifndef BOARD_LED_RED_GPIO_PIN
#define BOARD_LED_RED_GPIO_PIN 29U
#endif

#define BOARD_TPM_CHANNEL 5U
#ifndef TPM_LED_ON_LEVEL
  #define TPM_LED_ON_LEVEL kTPM_LowTrue
#endif

#define Cntr_Val 1000
uint16_t Val_Cn[16] =
{
  Cntr_Val*0,  Cntr_Val*1,  Cntr_Val*2,  Cntr_Val*3,
  Cntr_Val*4,  Cntr_Val*5,  Cntr_Val*6,  Cntr_Val*7,
  Cntr_Val*8,  Cntr_Val*9,  Cntr_Val*10, Cntr_Val*11,
  Cntr_Val*12, Cntr_Val*13, Cntr_Val*14, Cntr_Val*15
};   //the Value of duty cycle
tpm_status_flags_t flag_t = 0; //the flag of TPM status

dma_transfer_config_t TPM_CONF; //struct to config the DMA transfer to TPM
uint32_t flag_dma = 0;   //the flag of DMA status.
uint32_t bytes_endless = 0;
/* TODO: insert other definitions and declarations here. */
void DMA_Callback(dma_handle_t * handle, void* param)
{
 __asm volatile ("nop");  //only for a breakpoint
}

void DMA_1_DMA_CH_INT_DONE_0_IRQHANDLER()
{
 __asm volatile ("nop");    //only for a breakpoint

 /*
  * See the status flags of the timer and the DMA, 
  * it should be noted that the status flags should be 
  * cleaned once they are activated with any event ...
  */
 
 flag_t = TPM_GetStatusFlags(TPM0);
 TPM_ClearStatusFlags(TPM0, kTPM_TimeOverflowFlag | kTPM_Chnl5Flag);

 flag_dma = DMA_GetChannelStatusFlags(DMA0, DMA_1_DMA_TPM_DMA_CHANNEL);
 DMA_ClearChannelStatusFlags(DMA0, DMA_1_DMA_TPM_DMA_CHANNEL, flag_dma);

 bytes_endless = DMA_GetRemainingBytes(DMA0, DMA_1_DMA_TPM_DMA_CHANNEL);
 DMA_SetTransferConfig(DMA0, DMA_1_DMA_TPM_DMA_CHANNEL, &TPM_CONF);

}

void TPM_1_IRQHANDLER()
{
 __asm volatile ("nop");   //only for a breakpoint

 /*
  * Initially this interruption was used by overflow, 
  * but it is not necessary when in this program the DMA is used, 
  * thus this way we release CPU load.
  */

 flag_t = TPM_GetStatusFlags(TPM0);
 //Si ambas banderas de status se activaron entonces se limpian ambas.
 if( flag_t == (kTPM_TimeOverflowFlag | kTPM_Chnl5Flag) ){

  GPIO_PortToggle(BOARD_LED_RED_GPIO, 1U << BOARD_LED_RED_GPIO_PIN);
  TPM_ClearStatusFlags(TPM0, kTPM_TimeOverflowFlag  | kTPM_Chnl5Flag);

 }
}
/*
 * @brief   Application entry point.
 */
int main(void) {
   /* Init board hardware. */
    BOARD_InitBootPins();
    BOARD_InitBootClocks();
    BOARD_InitBootPeripherals();

    /*
     * The configurations are currently written to the peripheral register. 
     * Because these options cannot be configured from the "config-tools" interface.
     *
     * TODO: Maybe they can be configured in the configuration structure ...
     */
    TPM0->SC |= TPM_SC_DMA(1);
    TPM0->CONTROLS[5].CnSC |=  TPM_CnSC_DMA(1);

    /*
     * A structure is created to configure the DMA transfer
     */

    dma_transfer_config_t transferConfig;

    memset(&transferConfig, 0, sizeof(transferConfig));
    transferConfig.srcAddr = (uint32_t)&Val_Cn[0];
    transferConfig.destAddr = (uint32_t)&(TPM0->CONTROLS[5].CnV);
    transferConfig.enableSrcIncrement = true;
    transferConfig.enableDestIncrement = false;
    transferConfig.srcSize = kDMA_Transfersize16bits;
    transferConfig.destSize = kDMA_Transfersize16bits;
    transferConfig.transferSize = sizeof(uint16_t) * 16;

    /*
     * TODO: Yes... is stupid but works for now its ok...
     */
    TPM_CONF = transferConfig;

 DMA_SetModulo(DMA0, DMA_1_DMA_TPM_DMA_CHANNEL, 32, 0);
 DMA_SetTransferSize(DMA0, DMA_1_DMA_TPM_DMA_CHANNEL, 2);
 DMA_SetTransferConfig(DMA0, DMA_1_DMA_TPM_DMA_CHANNEL, &transferConfig);
    DMA_EnableChannelRequest(DMA0, DMA_1_DMA_TPM_DMA_CHANNEL);
    DMA_EnableAsyncRequest(DMA0, DMA_1_DMA_TPM_DMA_CHANNEL, true);
    DMA0->DMA[0].DCR |= DMA_DCR_D_REQ(0);

    /*
     *
     * The direct configuration of these records was changed to Enable Channel Request, 
     * EnableAsyncRequest ...
     *
     * DMA0->DMA[0].DCR |= DMA_DCR_ERQ(1);
     * DMA0->DMA[0].DCR |= DMA_DCR_EADREQ(1);
     *
     **/

    TPM_StartTimer(TPM0, kTPM_SystemClock);

    /* Enter an infinite loop, just incrementing a counter. */
    printf("DMA working without CPU");
    while(1) {

        /* 'Dummy' NOP to allow source level single stepping of
            tight while() loop */
     __asm volatile ("nop");

     /*
      * This was written to display the flags in "Real time" while debugging the program ...
      */
     
     flag_dma = DMA_GetChannelStatusFlags(DMA0, DMA_1_DMA_TPM_DMA_CHANNEL);
     flag_t = TPM_GetStatusFlags(TPM0);
     bytes_endless = DMA_GetRemainingBytes(DMA0, DMA_1_DMA_TPM_DMA_CHANNEL);

     if( flag_t == (kTPM_TimeOverflowFlag | kTPM_Chnl5Flag) ){

   GPIO_PortToggle(BOARD_LED_RED_GPIO, 1U << BOARD_LED_RED_GPIO_PIN);
   TPM_ClearStatusFlags(TPM0, kTPM_TimeOverflowFlag | kTPM_Chnl5Flag);

     }
    }
    return 0 ;
}

 

And I disabled this parameter on DMA config.

pastedImage_8.png

I could tell that a MCUXpresso's function disabled my configuration on the DMA Control Register, and the fuction is...

status_t DMA_SubmitTransfer(dma_handle_t *handle, const dma_transfer_config_t *config, uint32_t options)
{
    assert(handle != NULL);
    assert(config != NULL);

    /* Check if DMA is busy */
    if (handle->base->DMA[handle->channel].DSR_BCR & DMA_DSR_BCR_BSY_MASK)
    {
        return kStatus_DMA_Busy;
    }
    DMA_ResetChannel(handle->base, handle->channel);     //This clean the Control Register and any configuration!!
    DMA_SetTransferConfig(handle->base, handle->channel, config);
    if (options & kDMA_EnableInterrupt)
    {
        DMA_EnableInterrupts(handle->base, handle->channel);
    }
    return kStatus_Success;
}

And I changed this function for this other.

void DMA_SetTransferConfig(DMA_Type *base, uint32_t channel, const dma_transfer_config_t *config)
{
    assert(channel < FSL_FEATURE_DMA_MODULE_CHANNEL);
    assert(config != NULL);

    uint32_t tmpreg;

    /* Set source address */
    base->DMA[channel].SAR = config->srcAddr;
    /* Set destination address */
    base->DMA[channel].DAR = config->destAddr;
    /* Set transfer bytes */
    base->DMA[channel].DSR_BCR = DMA_DSR_BCR_BCR(config->transferSize);
    /* Set DMA Control Register */
    tmpreg = base->DMA[channel].DCR;
    tmpreg &= ~(DMA_DCR_DSIZE_MASK | DMA_DCR_DINC_MASK | DMA_DCR_SSIZE_MASK | DMA_DCR_SINC_MASK);
    tmpreg |= (DMA_DCR_DSIZE(config->destSize) | DMA_DCR_DINC(config->enableDestIncrement) |
               DMA_DCR_SSIZE(config->srcSize) | DMA_DCR_SINC(config->enableSrcIncrement));
    base->DMA[channel].DCR = tmpreg;
}

And now I achieved make I wanted, I hope that this help someone

Best regards

0 Kudos

7 Views
NXP TechSupport
NXP TechSupport

Hello Abraham Rosas

Checking your configuration I noted that the TPM interruption is not set. Please try checking these boxes.

 pastedImage_2.png

If the interruption is not set then the DMA will not know if the TPM has overflowed.

Let me know if this helps.

Best regards,

Omar