PIT DMA and MCUExpresso

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

PIT DMA and MCUExpresso

1,725 Views
tobias_mrs
Contributor III

Hello
I try would change a IO Pin very time syncron. For this I try to use DMA and the PIT.
To startup use the example twrke18f_edma_memory_to_memory. This example work.
Now I tried to expand this example with the working LPIT example and try to configure the LPIT to trigger the DMA.
But I don't work.
Were is my mistake?

/*******************************************************************************
 * Definitions
 ******************************************************************************/
#define DMAMUX0 DMAMUX
#define EXAMPLE_DMA DMA0
#define EXAMPLE_DMAMUX DMAMUX0

#define BUFF_LENGTH 4U

#define DEMO_LPIT_BASE LPIT0
#define DEMO_LPIT_IRQn LPIT0_Ch0_IRQn
#define DEMO_LPIT_IRQHandler LPIT0_Ch0_IRQHandler
#define LPIT_SOURCECLOCK CLOCK_GetFreq(kCLOCK_ScgSircAsyncDiv2Clk)

/*******************************************************************************
 * Prototypes
 ******************************************************************************/

/*******************************************************************************
 * Variables
 ******************************************************************************/
edma_handle_t g_EDMA_Handle;
volatile bool g_Transfer_Done = false;

/*******************************************************************************
 * Code
 ******************************************************************************/

/* User callback function for EDMA transfer. */
void EDMA_Callback(edma_handle_t *handle, void *param, bool transferDone, uint32_t tcds)
{
 if (transferDone)
 {
 g_Transfer_Done = true;
 }
}

void DEMO_LPIT_IRQHandler(void)
{
 /* Clear interrupt flag.*/
 LPIT_ClearStatusFlags(DEMO_LPIT_BASE, kLPIT_Channel0TimerFlag);
/* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F Store immediate overlapping
 exception return operation might vector to incorrect interrupt */
#if defined __CORTEX_M && (__CORTEX_M == 4U)
 __DSB();
#endif
}

AT_NONCACHEABLE_SECTION_INIT(uint32_t srcAddr[BUFF_LENGTH]) = {0x01, 0x02, 0x03, 0x04};
AT_NONCACHEABLE_SECTION_INIT(uint32_t destAddr[BUFF_LENGTH]) = {0x00, 0x00, 0x00, 0x00};

/*!
 * @brief Main function
 */
int main(void)
{
 uint32_t i = 0;
 edma_transfer_config_t transferConfig;
 edma_config_t userConfig;

 /* Structure of initialize LPIT */
 lpit_config_t lpitConfig;
 lpit_chnl_params_t lpitChannelConfig;

 BOARD_InitPins();
 BOARD_BootClockRUN();
 BOARD_InitDebugConsole();

 /* Configure LPIT */
 /* Set the source for the LPIT module */
 CLOCK_SetIpSrc(kCLOCK_Lpit0, kCLOCK_IpSrcSircAsync);

 /*
 * lpitConfig.enableRunInDebug = false;
 * lpitConfig.enableRunInDoze = false;
 */
 LPIT_GetDefaultConfig(&lpitConfig);

 /* Init lpit module */
 LPIT_Init(DEMO_LPIT_BASE, &lpitConfig);

 lpitChannelConfig.chainChannel = false;
 lpitChannelConfig.enableReloadOnTrigger = false;
 lpitChannelConfig.enableStartOnTrigger = false;
 lpitChannelConfig.enableStopOnTimeout = false;
 lpitChannelConfig.timerMode = kLPIT_PeriodicCounter;
 /* Set default values for the trigger source */
 lpitChannelConfig.triggerSelect = kLPIT_Trigger_TimerChn0;
 lpitChannelConfig.triggerSource = kLPIT_TriggerSource_External;
 /* Init lpit channel 0 */
 LPIT_SetupChannel(DEMO_LPIT_BASE, kLPIT_Chnl_0, &lpitChannelConfig);

 /* Set timer period for channel 0 */
 LPIT_SetTimerPeriod(DEMO_LPIT_BASE, kLPIT_Chnl_0, USEC_TO_COUNT(500000U, LPIT_SOURCECLOCK));

 /* Enable timer interrupts for channel 0 */
 LPIT_EnableInterrupts(DEMO_LPIT_BASE, kLPIT_Channel0TimerInterruptEnable);

 /* Enable at the NVIC */
 EnableIRQ(DEMO_LPIT_IRQn);

 /* Print source buffer */
 PRINTF("EDMA memory to memory transfer example begin.\r\n\r\n");
 PRINTF("Destination Buffer:\r\n");
 for (i = 0; i < BUFF_LENGTH; i++)
 {
 PRINTF("%d\t", destAddr[i]);
 }
 /* Configure DMAMUX */
 DMAMUX_Init(EXAMPLE_DMAMUX);
#if defined(FSL_FEATURE_DMAMUX_HAS_A_ON) && FSL_FEATURE_DMAMUX_HAS_A_ON
 DMAMUX_EnableAlwaysOn(EXAMPLE_DMAMUX, 0, true);
#else
 // DMAMUX_SetSource(EXAMPLE_DMAMUX, 0, 63);
 DMAMUX_SetSource(EXAMPLE_DMAMUX, 0, 60); // LPIT0
#endif /* FSL_FEATURE_DMAMUX_HAS_A_ON */
 DMAMUX_EnableChannel(EXAMPLE_DMAMUX, 0);
 /* Configure EDMA one shot transfer */
 /*
 * userConfig.enableRoundRobinArbitration = false;
 * userConfig.enableHaltOnError = true;
 * userConfig.enableContinuousLinkMode = false;
 * userConfig.enableDebugMode = false;
 */
 EDMA_GetDefaultConfig(&userConfig);
 EDMA_Init(EXAMPLE_DMA, &userConfig);
 EDMA_CreateHandle(&g_EDMA_Handle, EXAMPLE_DMA, 0);
 EDMA_SetCallback(&g_EDMA_Handle, EDMA_Callback, NULL);
 EDMA_PrepareTransfer(&transferConfig, srcAddr, sizeof(srcAddr[0]), destAddr, sizeof(destAddr[0]),
 sizeof(srcAddr[0]), sizeof(srcAddr), kEDMA_MemoryToMemory);
 EDMA_SubmitTransfer(&g_EDMA_Handle, &transferConfig);

// EDMA_EnableChannelRequest(EXAMPLE_DMA, 0);
 DMAMUX_EnablePeriodTrigger(EXAMPLE_DMAMUX, 0);


 /* Start channel 0 */
 PRINTF("\r\nStarting channel No.0 ...");
 LPIT_StartTimer(DEMO_LPIT_BASE, kLPIT_Chnl_0);

// EDMA_StartTransfer(&g_EDMA_Handle);


 /* Wait for EDMA transfer finish */
 while (g_Transfer_Done != true)
 {
 }
 /* Print destination buffer */
 PRINTF("\r\n\r\nEDMA memory to memory transfer example finish.\r\n\r\n");
 PRINTF("Destination Buffer:\r\n");
 for (i = 0; i < BUFF_LENGTH; i++)
 {
 PRINTF("%d\t", destAddr[i]);
 }
 while (1)
 {
 }
}


I get the LPIT interrupt but never the DMA interrupt and the copy also don't work.
Thanks for your help.
Tobias

0 Kudos
9 Replies

1,596 Views
tobias_mrs
Contributor III

 Now I have a problem with setup the DMA to use GPIO

EDMA_PrepareTransfer( &dma_transfer_config,
                      &port_output_value[0], sizeof(uint32_t),
                      (void *)&( GPIOC->PDOR ), sizeof(uint32_t),
                      sizeof(uint32_t),
                      sizeof(uint32_t),
                      kEDMA_MemoryToPeripheral);

Is this setup correct?
If I use the  (void *)&( GPIOC->PDOR ), sizeof(uint32_t), as destination I get a hard fault at startup?

0 Kudos

1,596 Views
tobias_mrs
Contributor III

I don't know what I have changed but now it work with the same settings:

  EDMA_PrepareTransfer( &dma_transfer_config,
                         &port_output_value[0], sizeof(uint32_t),
                         (void *) &( GPIOC->PDOR), sizeof(uint32_t),
                         sizeof(uint32_t),
                         sizeof(uint32_t),
                         kEDMA_MemoryToPeripheral);

The timer trigger the DMA and the DMA write to the port.

0 Kudos

1,596 Views
mjbcswitzerland
Specialist V

Tobias

The DMA setup solution used in the uTasker method works on any port as follows. I don't think &(GPIOC->PDOR) is correct but if you get a hard fault it means that the code doing the setup fails and not that the DMA fails - the DMA would fail if addresses are not correct but not with a hard fault but rather with a channel error.

fnConfigDMA_buffer(PIT_settings->ucPIT,
         (DMAMUX0_DMA0_CHCFG_SOURCE_PIT0 + PIT_settings->ucPIT),
         sizeof(ulPortBits[PIT_settings->ucPIT]),
         &ulPortBits[PIT_settings->ucPIT],
         (unsigned long *)(GPIO_BLOCK + (0x040 * PIT_settings->ucPortRef) + 0x00c),
         (DMA_DIRECTION_OUTPUT | DMA_LONG_WORDS | DMA_FIXED_ADDRESSES | DMA_NO_MODULO),
         0, 0); // source is the port bit and destination is the GPIO toggle register (DMA_FIXED_ADDRESSES and DMA_NO_MODULO are used only by KL parts)
‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

Note that the advantage of fnConfigDMA_buffer() over EDMA_PrepareTransfer() is that it works on all DMA controllers and so existing and otherwise proven code doesn't need to be ported when moving between different Kinetis (or i.MX RT) families.

Regards

Mark

[uTasker project developer for Kinetis and i.MX RT]

0 Kudos

1,596 Views
tobias_mrs
Contributor III

Now, with the help of Mark I solved a part of my problem.
Here is my source code for all others with the same problem.

/*******************************************************************************
 * Variables
 ******************************************************************************/
edma_handle_t g_EDMA_Handle;
volatile bool g_Transfer_Done = false;

/*******************************************************************************
 * Code
 ******************************************************************************/

/* User callback function for EDMA transfer. */
void EDMA_Callback(edma_handle_t *handle, void *param, bool transferDone, uint32_t tcds)
{
    if (transferDone)
    {
        g_Transfer_Done = true;
    }
}

void DEMO_LPIT_IRQHandler(void)
{
    /* Clear interrupt flag.*/
    LPIT_ClearStatusFlags(DEMO_LPIT_BASE, kLPIT_Channel0TimerFlag);
/* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F Store immediate overlapping
  exception return operation might vector to incorrect interrupt */
#if defined __CORTEX_M && (__CORTEX_M == 4U)
    __DSB();
#endif
}

AT_NONCACHEABLE_SECTION_INIT(uint32_t srcAddr[BUFF_LENGTH])  = {0x01, 0x02, 0x03, 0x04};
AT_NONCACHEABLE_SECTION_INIT(uint32_t destAddr[BUFF_LENGTH]) = {0x00, 0x00, 0x00, 0x00};

/*!
 * @brief Main function
 */
int main(void)
{
    uint32_t i = 0;
    edma_transfer_config_t transferConfig;
    edma_config_t userConfig;

    /* Structure of initialize LPIT */
    lpit_config_t lpitConfig;
    lpit_chnl_params_t lpitChannelConfig;

    BOARD_InitPins();
    BOARD_BootClockRUN();
    BOARD_InitDebugConsole();

    /* Configure LPIT */
    /* Set the source for the LPIT module */
 CLOCK_SetIpSrc(kCLOCK_Lpit0, kCLOCK_IpSrcSircAsync);

 /*
  * lpitConfig.enableRunInDebug = false;
  * lpitConfig.enableRunInDoze = false;
  */
 LPIT_GetDefaultConfig(&lpitConfig);

 /* Init lpit module */
 LPIT_Init(DEMO_LPIT_BASE, &lpitConfig);

 lpitChannelConfig.chainChannel          = false;
 lpitChannelConfig.enableReloadOnTrigger = false;
 lpitChannelConfig.enableStartOnTrigger  = false;
 lpitChannelConfig.enableStopOnTimeout   = false;
 lpitChannelConfig.timerMode             = kLPIT_PeriodicCounter;
 /* Set default values for the trigger source */
 lpitChannelConfig.triggerSelect = kLPIT_Trigger_TimerChn0;
 lpitChannelConfig.triggerSource = kLPIT_TriggerSource_External;
 /* Init lpit channel 0 */
 LPIT_SetupChannel(DEMO_LPIT_BASE, kLPIT_Chnl_0, &lpitChannelConfig);

 /* Set timer period for channel 0 */
 LPIT_SetTimerPeriod(DEMO_LPIT_BASE, kLPIT_Chnl_0, USEC_TO_COUNT(500000U, LPIT_SOURCECLOCK));

 /* Enable timer interrupts for channel 0 */
 //LPIT_EnableInterrupts(DEMO_LPIT_BASE, kLPIT_Channel0TimerInterruptEnable);
 /* Enable at the NVIC */
 //EnableIRQ(DEMO_LPIT_IRQn);

    // trigger MUX
 TRGMUX_SetTriggerSource(TRGMUX0, kTRGMUX_Dmamux0, kTRGMUX_TriggerInput0, kTRGMUX_SourceLpit0Ch0); // Connect the LPIT trigger source to the DMAMUX

    /* Print source buffer */
    PRINTF("EDMA memory to memory transfer example begin.\r\n\r\n");
    PRINTF("Destination Buffer:\r\n");
    for (i = 0; i < BUFF_LENGTH; i++)
    {
        PRINTF("%d\t", destAddr[i]);
    }
    /* Configure DMAMUX */
    DMAMUX_Init(EXAMPLE_DMAMUX);
#if defined(FSL_FEATURE_DMAMUX_HAS_A_ON) && FSL_FEATURE_DMAMUX_HAS_A_ON
    DMAMUX_EnableAlwaysOn(EXAMPLE_DMAMUX, 0, true);
#else
   // DMAMUX_SetSource(EXAMPLE_DMAMUX, 0, 63);
    DMAMUX_SetSource(EXAMPLE_DMAMUX, 0, 60);  // LPIT0
    DMAMUX_EnablePeriodTrigger(EXAMPLE_DMAMUX, 0);
#endif /* FSL_FEATURE_DMAMUX_HAS_A_ON */
    DMAMUX_EnableChannel(EXAMPLE_DMAMUX, 0);
    /* Configure EDMA one shot transfer */
    /*
     * userConfig.enableRoundRobinArbitration = false;
     * userConfig.enableHaltOnError = true;
     * userConfig.enableContinuousLinkMode = false;
     * userConfig.enableDebugMode = false;
     */
    EDMA_GetDefaultConfig(&userConfig);
    EDMA_Init(EXAMPLE_DMA, &userConfig);
    EDMA_CreateHandle(&g_EDMA_Handle, EXAMPLE_DMA, 0);
    EDMA_SetCallback(&g_EDMA_Handle, EDMA_Callback, NULL);
    EDMA_PrepareTransfer(&transferConfig, srcAddr, sizeof(srcAddr[0]), destAddr, sizeof(destAddr[0]),
                         sizeof(srcAddr[0]), sizeof(srcAddr), kEDMA_MemoryToMemory);
    EDMA_SubmitTransfer(&g_EDMA_Handle, &transferConfig);

 EDMA_EnableChannelRequest(EXAMPLE_DMA, 0);

 //EDMA_EnableChannelInterrupts(EXAMPLE_DMA, 0, kEDMA_MajorInterruptEnable);
 //EDMA_EnableChannelInterrupts(EXAMPLE_DMA, 0, kEDMA_ErrorInterruptEnable);
 //EDMA_EnableChannelInterrupts(EXAMPLE_DMA, 0, kEDMA_HalfInterruptEnable);

 /* Start channel 0 */
 PRINTF("\r\nStarting channel No.0 ...");
 LPIT_StartTimer(DEMO_LPIT_BASE, kLPIT_Chnl_0);

//    EDMA_StartTransfer(&g_EDMA_Handle);


    /* Wait for EDMA transfer finish */
    while (g_Transfer_Done != true)
    {
    }
    /* Print destination buffer */
    PRINTF("\r\n\r\nEDMA memory to memory transfer example finish.\r\n\r\n");
    PRINTF("Destination Buffer:\r\n");
    for (i = 0; i < BUFF_LENGTH; i++)
    {
        PRINTF("%d\t", destAddr[i]);
    }
    while (1)
    {
    }
}
0 Kudos

1,596 Views
mjbcswitzerland
Specialist V

Hi

Have you respected that LPIT channel 0 can trigger only DMA on DMA channel 0 (LPIT 1 -> DMA channel 1; LPIT 2 -> DMA channel 2; LPIT 3 -> DMA channel 3;)?

Regards

Mark
[uTasker project developer for Kinetis and i.MX RT]

0 Kudos

1,596 Views
tobias_mrs
Contributor III

I hope I have done this with line 115

DMAMUX_SetSource(EXAMPLE_DMAMUX, 0, 60); // LPIT0

Ore how do I set  and enable the DMA source to LPIT correct at MCUExpresso SDK

0 Kudos

1,596 Views
mjbcswitzerland
Specialist V

Schönen guten Tag Tobias

LPIT0 DMA trigger source is 60 but it also needs the trigger enabled (0x40) which means that it needs to be entered with
(60 | 0x40).

The next thing that is probably missing is connecting the LPIT trigger source to the DMAMUX via the TRGMUX.

Since I don't use the SDK you will possibly have to first find an example that shows using the TRGMUX and then add the relevant part to your configuration.

In the uTasker project there is a standardised method to toggle GPIOs from PIT DMA triggers so the details (and Kinetis part differences) don't need to be understood and controlled by users so I can do it using on any Kinetis device with PIT or LPIT (and DMA) or even on iMX RT parts..

    PIT_SETUP pit_setup;                         // PIT interrupt configuration parameters
    pit_setup.int_type = PIT_INTERRUPT;
    pit_setup.mode = PIT_PERIODIC;
    pit_setup.int_handler = 0;                   // no interrupt due to DMA
    pit_setup.ulPortBits = (PORTD_BIT0 | PORTD_BIT7); // toggle PTD0 and PTD7 on each PIT trigger
    pit_setup.ucPortRef = PORTD;
    pit_setup.ucPIT = 1;                         // use PIT1
    pit_setup.count_delay = PIT_MS_DELAY(20);    // 20ms period
    fnConfigureInterrupt((void *)&pit_setup);    // configure PIT
‍‍‍‍‍‍‍‍‍‍

In the case of TRGMUX being available it controls the routing with the following code (in its DMA driver) which may give you some ideas as to what is needed if you don't find anything relevant in the standard SDK.

    if ((usDmaTriggerSource & DMAMUX_CHCFG_TRIG) != 0) {  // triggered source (LPIT)
        TRGMUX_DMAMUX0 |= ((TRGMUX_SEL_LPIT0_CHANNEL_0 + (usDmaTriggerSource - DMAMUX0_DMA0_CHCFG_SOURCE_PIT0)) << ((usDmaTriggerSource - DMAMUX0_DMA0_CHCFG_SOURCE_PIT0) * 8)); // Connect the LPIT trigger source to the DMAMUX
    }
    {                                                     // set DMAMUX trigger
        DMA_MUX_REGISTER *ptrDMAMUX = (DMA_MUX_REGISTER *)DMAMUX0_BLOCK;
        ptrDMAMUX += ucDMA_channel;
        *ptrDMAMUX = (DMA_MUX_REGISTER)(usDmaTriggerSource | DMAMUX_CHCFG_ENBL); // connect trigger source to DMA channel
    }
‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

In fact only line 2 is needed for the TRGMUX since the following lines are for the standard DMA MUX setting.

Grüsse aus der Schweiz

 

Mark
[uTasker project developer for Kinetis and i.MX RT]

1,596 Views
tobias_mrs
Contributor III

Hallo Mark
vielen Dank für diene Hilfe.

The LPIT DMA trigger would be enabled with line 134.

DMAMUX_EnablePeriodTrigger(EXAMPLE_DMAMUX, 0);

static inline void DMAMUX_EnablePeriodTrigger(DMAMUX_Type *base, uint32_t channel)
{
    assert(channel < (uint32_t)FSL_FEATURE_DMAMUX_MODULE_CHANNEL);

    base->CHCFG[channel] |= DMAMUX_CHCFG_TRIG_MASK; // =>(0x40U)
}


The advice with the TRGMUX was very good. I added the line

// trigger MUX
TRGMUX_SetTriggerSource(TRGMUX0, kTRGMUX_Dmamux0, kTRGMUX_TriggerInput0, kTRGMUX_SourceLpit0Ch0); // Connect the LPIT trigger source to the DMAMUX

and also enabled the Channel at DMA

EDMA_EnableChannelRequest(EXAMPLE_DMA, 0);

And know the LPIT trigger the DMA.
Know I have to check how to use the GPIO with the DMA.

Vielen Danke für diene Hilfe.
Grüße
Tobias

0 Kudos

484 Views
shaozhongliangs
NXP Employee
NXP Employee

Attached files are two simple demonstration programs that use PIT to trigger DMA transfer.

0 Kudos