STM32N6 GPIO DMA Pattern Gen by Timer trigger
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2025-01-01 1:11 AM - edited ‎2025-01-01 1:12 AM
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.
Solved! Go to Solution.
- Labels:
-
DMA
-
STM32N6 Series
-
TIM
Accepted Solutions
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2025-01-02 3:04 AM
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2025-01-02 1:41 AM
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2025-01-02 3:04 AM
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.
