2025-01-01 01:11 AM - edited 2025-01-01 01: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:
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.
2025-01-02 03: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.
2025-01-02 01: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.
2025-01-02 03: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.