cancel
Showing results for 
Search instead for 
Did you mean: 

STM32N6 GPIO DMA Pattern Gen by Timer trigger

filipxsikora
Associate II

Hello,

I'm trying to do pattern generation on GPIO port using DMA timed by TIM trigger, but I cannot make it running.

My code:

TIM2 INIT

 

 

void MX_TIM2_Init(void)
{
    __HAL_RCC_TIM2_CLK_ENABLE();

    HAL_NVIC_SetPriority(TIM2_IRQn, 3, 0);
    HAL_NVIC_EnableIRQ(TIM2_IRQn);

    TIM_ClockConfigTypeDef sClockSourceConfig = { 0 };
    TIM_MasterConfigTypeDef sMasterConfig = { 0 };

    htim2.Instance = TIM2;
    htim2.Init.Prescaler = 399;
    htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
    htim2.Init.Period = 999;
    htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
    htim2.Init.RepetitionCounter = 0;

    if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
    {
        Error_Handler();
    }

    sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;

    if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
    {
        Error_Handler();
    }

    sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
    sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;

    if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
    {
        Error_Handler();
    }
}

 

 

 DMA INIT

 

 

void MX_GPDMA1_Init(void)
{
    DMA_TriggerConfTypeDef TriggerConfig = { 0 };

    __HAL_RCC_GPDMA1_CLK_ENABLE();

    HAL_NVIC_SetPriority(GPDMA1_Channel0_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(GPDMA1_Channel0_IRQn);

    handle_GPDMA1_Channel0.Instance = GPDMA1_Channel0;
    handle_GPDMA1_Channel0.Init.Request = DMA_REQUEST_SW;
    handle_GPDMA1_Channel0.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
    handle_GPDMA1_Channel0.Init.Direction = DMA_MEMORY_TO_PERIPH;
    handle_GPDMA1_Channel0.Init.SrcInc = DMA_SINC_INCREMENTED;
    handle_GPDMA1_Channel0.Init.DestInc = DMA_DINC_FIXED;
    handle_GPDMA1_Channel0.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_HALFWORD;
    handle_GPDMA1_Channel0.Init.DestDataWidth = DMA_DEST_DATAWIDTH_HALFWORD;
    handle_GPDMA1_Channel0.Init.Priority = DMA_LOW_PRIORITY_HIGH_WEIGHT;
    handle_GPDMA1_Channel0.Init.SrcBurstLength = 1;
    handle_GPDMA1_Channel0.Init.DestBurstLength = 1;
    handle_GPDMA1_Channel0.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0 | DMA_DEST_ALLOCATED_PORT0;
    handle_GPDMA1_Channel0.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
    handle_GPDMA1_Channel0.Init.Mode = DMA_NORMAL;

    if (HAL_DMA_Init(&handle_GPDMA1_Channel0) != HAL_OK)
    {
        Error_Handler();
    }

    TriggerConfig.TriggerMode = DMA_TRIGM_SINGLE_BURST_TRANSFER;
    TriggerConfig.TriggerPolarity = DMA_TRIG_POLARITY_RISING;
    TriggerConfig.TriggerSelection = GPDMA1_TRIGGER_TIM2_TRGO;

    if (HAL_DMAEx_ConfigTrigger(&handle_GPDMA1_Channel0, &TriggerConfig) != HAL_OK)
    {
        Error_Handler();
    }

    HAL_GPIO_ConfigPinAttributes(GPIOD, GPIO_PIN_10, GPIO_PIN_SEC | GPIO_PIN_PRIV);

    if (HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel0,
            DMA_CHANNEL_PRIV | DMA_CHANNEL_SEC | DMA_CHANNEL_SRC_SEC | DMA_CHANNEL_DEST_SEC) != HAL_OK)
    {
        Error_Handler();
    }

    HAL_DMA_RegisterCallback(&handle_GPDMA1_Channel0, HAL_DMA_XFER_CPLT_CB_ID, TransferComplete);
}

 

 

Reduced main:

 

 

__attribute((aligned(32)))  uint16_t _output[128];
...
int main(void)
{
    ...
    MX_TIM2_Init();
    MX_GPDMA1_Init();
    ...
    for (uint16_t i = 0; i < 128; i++)
    {
        _output[i] = i % 2 == 0 ? GPIO_PIN_10 : 0;
    }

    SCB_CleanDCache_by_Addr(_output, 256);

    if (HAL_DMA_Start_IT(&handle_GPDMA1_Channel0, (uint32_t) &_output[0], (uint32_t) &GPIOD->ODR, 256) != HAL_OK)
    {
        Error_Handler();
    }

    if (HAL_TIM_Base_Start_IT(&htim2) != HAL_OK)
    {
        /* Starting Error */
        Error_Handler();
    }
    ...
}

void TransferComplete(DMA_HandleTypeDef *hdma)
{
    asm volatile ("nop"); //breakpoint
}

 

 

However, when I run the code, nothing happens, GPIOD_Pin10 stays low and the TransferComplete function never gets called.

I have verified:

  • The Timer is running and working, trigerring at 1ms as expected
  • The GPIOD PIN10 is set as output and I can toggle it "manually" by code
  • When I change the DMA DIR to MEM_TO_MEM, DEST_INC to INCREMENT but leave everything the same and call it with two arrays

 

 

HAL_DMA_Start_IT(&handle_GPDMA1_Channel0, (uint32_t) &_output[0], (uint32_t) &_newOutput[0], 256)

 

 

This works, I get the TransferComplete callback and I can see the contents of data were copied to the new buffer.

 

I have tried many many other things, playing with the parameters of the DMA when it's set to GPIO, but with no success.

Anybody has any idea where I'm wrong?

Thank you.

1 ACCEPTED SOLUTION

Accepted Solutions
filipxsikora
Associate II

Hello,

thank you for the reply. However I haven't explained it correctly. I need to produce parallel pattern on the GPIO port, not just one signal. In my code, I'm toggling just one pin for testing purposes, but I need to toggle (almost) the whole port at once (Data lines + clock), hence the DMA - TIM - GPIO combination.

Nevertheless I figured it out, just set the GPDMA to MEM_to_MEM, DEST_INC to FIXED and then it works as expected. Basically "pretend", that the GPIOn->ODR is a memory.

static void MX_GPDMA1_Init(void)
{
    DMA_TriggerConfTypeDef TriggerConfig = { 0 };

    __HAL_RCC_GPDMA1_CLK_ENABLE();

    HAL_NVIC_SetPriority(GPDMA1_Channel0_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(GPDMA1_Channel0_IRQn);

    handle_GPDMA1_Channel0.Instance = GPDMA1_Channel0;
    handle_GPDMA1_Channel0.Init.Request = DMA_REQUEST_SW;
    handle_GPDMA1_Channel0.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
    handle_GPDMA1_Channel0.Init.Direction = DMA_MEMORY_TO_MEMORY;
    handle_GPDMA1_Channel0.Init.SrcInc = DMA_SINC_INCREMENTED;
    handle_GPDMA1_Channel0.Init.DestInc = DMA_DINC_FIXED;
    handle_GPDMA1_Channel0.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_HALFWORD;
    handle_GPDMA1_Channel0.Init.DestDataWidth = DMA_DEST_DATAWIDTH_HALFWORD;
    handle_GPDMA1_Channel0.Init.Priority = DMA_HIGH_PRIORITY;
    handle_GPDMA1_Channel0.Init.SrcBurstLength = 1;
    handle_GPDMA1_Channel0.Init.DestBurstLength = 1;
    handle_GPDMA1_Channel0.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0 | DMA_DEST_ALLOCATED_PORT1;
    handle_GPDMA1_Channel0.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
    handle_GPDMA1_Channel0.Init.Mode = DMA_NORMAL;

    if (HAL_DMA_Init(&handle_GPDMA1_Channel0) != HAL_OK)
    {
        Error_Handler();
    }

    TriggerConfig.TriggerMode = DMA_TRIGM_SINGLE_BURST_TRANSFER;
    TriggerConfig.TriggerPolarity = DMA_TRIG_POLARITY_RISING;
    TriggerConfig.TriggerSelection = GPDMA1_TRIGGER_TIM2_TRGO;

    if (HAL_DMAEx_ConfigTrigger(&handle_GPDMA1_Channel0, &TriggerConfig) != HAL_OK)
    {
        Error_Handler();
    }

    if (HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel0,
            DMA_CHANNEL_PRIV | DMA_CHANNEL_SEC | DMA_CHANNEL_SRC_SEC | DMA_CHANNEL_DEST_SEC) != HAL_OK)
    {
        Error_Handler();
    }

    HAL_DMA_RegisterCallback(&handle_GPDMA1_Channel0, HAL_DMA_XFER_CPLT_CB_ID, TransferComplete);
}

And then start with

HAL_DMA_Start_IT(&handle_GPDMA1_Channel0, (uint32_t)&_data[0], (uint32_t)&GPIOD->ODR, 256);

 

The Timer config and everything stays the same as in the first post.

 

This works and I can get as high as 15MHz on the output pins.

View solution in original post

2 REPLIES 2
Sarra.S
ST Employee

Hello @filipxsikora

Have you checked this: Custom Signal generation using PWM and DMA - STMicroelectronics Community 

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.

filipxsikora
Associate II

Hello,

thank you for the reply. However I haven't explained it correctly. I need to produce parallel pattern on the GPIO port, not just one signal. In my code, I'm toggling just one pin for testing purposes, but I need to toggle (almost) the whole port at once (Data lines + clock), hence the DMA - TIM - GPIO combination.

Nevertheless I figured it out, just set the GPDMA to MEM_to_MEM, DEST_INC to FIXED and then it works as expected. Basically "pretend", that the GPIOn->ODR is a memory.

static void MX_GPDMA1_Init(void)
{
    DMA_TriggerConfTypeDef TriggerConfig = { 0 };

    __HAL_RCC_GPDMA1_CLK_ENABLE();

    HAL_NVIC_SetPriority(GPDMA1_Channel0_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(GPDMA1_Channel0_IRQn);

    handle_GPDMA1_Channel0.Instance = GPDMA1_Channel0;
    handle_GPDMA1_Channel0.Init.Request = DMA_REQUEST_SW;
    handle_GPDMA1_Channel0.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
    handle_GPDMA1_Channel0.Init.Direction = DMA_MEMORY_TO_MEMORY;
    handle_GPDMA1_Channel0.Init.SrcInc = DMA_SINC_INCREMENTED;
    handle_GPDMA1_Channel0.Init.DestInc = DMA_DINC_FIXED;
    handle_GPDMA1_Channel0.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_HALFWORD;
    handle_GPDMA1_Channel0.Init.DestDataWidth = DMA_DEST_DATAWIDTH_HALFWORD;
    handle_GPDMA1_Channel0.Init.Priority = DMA_HIGH_PRIORITY;
    handle_GPDMA1_Channel0.Init.SrcBurstLength = 1;
    handle_GPDMA1_Channel0.Init.DestBurstLength = 1;
    handle_GPDMA1_Channel0.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0 | DMA_DEST_ALLOCATED_PORT1;
    handle_GPDMA1_Channel0.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
    handle_GPDMA1_Channel0.Init.Mode = DMA_NORMAL;

    if (HAL_DMA_Init(&handle_GPDMA1_Channel0) != HAL_OK)
    {
        Error_Handler();
    }

    TriggerConfig.TriggerMode = DMA_TRIGM_SINGLE_BURST_TRANSFER;
    TriggerConfig.TriggerPolarity = DMA_TRIG_POLARITY_RISING;
    TriggerConfig.TriggerSelection = GPDMA1_TRIGGER_TIM2_TRGO;

    if (HAL_DMAEx_ConfigTrigger(&handle_GPDMA1_Channel0, &TriggerConfig) != HAL_OK)
    {
        Error_Handler();
    }

    if (HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel0,
            DMA_CHANNEL_PRIV | DMA_CHANNEL_SEC | DMA_CHANNEL_SRC_SEC | DMA_CHANNEL_DEST_SEC) != HAL_OK)
    {
        Error_Handler();
    }

    HAL_DMA_RegisterCallback(&handle_GPDMA1_Channel0, HAL_DMA_XFER_CPLT_CB_ID, TransferComplete);
}

And then start with

HAL_DMA_Start_IT(&handle_GPDMA1_Channel0, (uint32_t)&_data[0], (uint32_t)&GPIOD->ODR, 256);

 

The Timer config and everything stays the same as in the first post.

 

This works and I can get as high as 15MHz on the output pins.