2025-07-14 9:15 AM
Hello everyone,
I’m working with an STM32H743VIHX and CubeMX 6.13.0-RC5, and I’m trying to capture a square-wave clock on PE9 (mapped to TIM1_CH1) so that each rising edge:
Increments the TIM1 counter (External Clock Mode 1 via TI1FP1)
Fires a DMA transfer from GPIOE->IDR into a circular buffer
Invokes my HAL_DMA_RxHalfCpltCallback() / HAL_DMA_RxCpltCallback()
Even though I can see the TIM1 counter increment (and even wrap around), my DMA callbacks never get called.
Here’s what I’ve set up:
PE9 → AF1 → TIM1_CH1
Slave Mode
Slave Mode = External Clock Mode 1
Trigger = TI1FP1
Polarity = Rising
Channel 1
ICSelection = Direct TI1
ICPolarity = Rising
Filter = 0
DMA1_Stream1 is used for TIM1_CH1
NVIC: DMA1_Stream1_IRQn enabled
Has anyone successfully used TIM1_CH1 → DMA1_Stream1 on an STM32H7?
I’ll attach my full main.cand stm32h7xx_hal_msp.c, files below. Any help would be greatly appreciated!
Thank you in advance for your insights.
Solved! Go to Solution.
2025-07-15 1:35 AM
Update: Solved! Here’s what finally worked for TIM1_CH1 → DMA on every PE9 rising edge
The key was turning the CC1 events into TRGO pulses and wiring that into a trigger‐DMA stream.
Here’s the summary of what I changed:
static void MX_TIM1_Init(void)
{
/* USER CODE BEGIN TIM1_Init 0 */
/* USER CODE END TIM1_Init 0 */
TIM_SlaveConfigTypeDef sSlaveConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_IC_InitTypeDef sConfigIC = {0};
/* USER CODE BEGIN TIM1_Init 1 */
/* USER CODE END TIM1_Init 1 */
htim1.Instance = TIM1;
htim1.Init.Prescaler = 0;
htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
htim1.Init.Period = 65535;
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim1.Init.RepetitionCounter = 0;
htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim1) != HAL_OK)
{
Error_Handler();
}
// 1) Input-Capture on CH1 so we get CC1 events from TI1FP1
if (HAL_TIM_IC_Init(&htim1) != HAL_OK) {
Error_Handler();
}
sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
sConfigIC.ICFilter = 0;
if (HAL_TIM_IC_ConfigChannel(&htim1, &sConfigIC, TIM_CHANNEL_1) != HAL_OK) {
Error_Handler();
}
// 2) External clock mode 1: TI1FP1 (PE9 rising edge) → counter + CC1
sSlaveConfig.SlaveMode = TIM_SLAVEMODE_EXTERNAL1;
sSlaveConfig.InputTrigger = TIM_TS_TI1FP1;
sSlaveConfig.TriggerPolarity = TIM_TRIGGERPOLARITY_RISING;
sSlaveConfig.TriggerFilter = 0;
if (HAL_TIM_SlaveConfigSynchro(&htim1, &sSlaveConfig) != HAL_OK) {
Error_Handler();
}
// 3) Make CC1 mismatches ALSO generate a TRGO
sMasterConfig.MasterOutputTrigger = TIM_TRGO_OC1; // TRGO on CC1
sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_ENABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK) {
Error_Handler();
}
/* USER CODE BEGIN TIM1_Init 2 */
// 4) Arm the trigger-DMA (TIM1_TRIG) to copy GPIOE->IDR → portE_samples[]
// and install your half/full callbacks in MSP-init.
if (HAL_DMA_Start_IT(&hdma_tim1_trig,
(uint32_t)&GPIOE->IDR,
(uint32_t)portE_samples,
PORT_E_SAMPLES_BUFFER_SIZE) != HAL_OK)
{
Error_Handler();
}
__HAL_TIM_ENABLE_DMA(&htim1, TIM_DMA_TRIGGER);
// 5) Finally start the timer itself
HAL_TIM_Base_Start(&htim1);
/* USER CODE END TIM1_Init 2 */
}
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* htim_base)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(htim_base->Instance==TIM1)
{
/* USER CODE BEGIN TIM1_MspInit 0 */
/* USER CODE END TIM1_MspInit 0 */
/* Peripheral clock enable */
__HAL_RCC_TIM1_CLK_ENABLE();
__HAL_RCC_GPIOE_CLK_ENABLE();
/**TIM1 GPIO Configuration
PE9 ------> TIM1_CH1
*/
GPIO_InitStruct.Pin = L_CLK_RX_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF1_TIM1;
HAL_GPIO_Init(L_CLK_RX_GPIO_Port, &GPIO_InitStruct);
/* TIM1 DMA Init */
/* TIM1_TRIG Init */
hdma_tim1_trig.Instance = DMA1_Stream1;
hdma_tim1_trig.Init.Request = DMA_REQUEST_TIM1_TRIG;
hdma_tim1_trig.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_tim1_trig.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_tim1_trig.Init.MemInc = DMA_MINC_ENABLE;
hdma_tim1_trig.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
hdma_tim1_trig.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
hdma_tim1_trig.Init.Mode = DMA_CIRCULAR;
hdma_tim1_trig.Init.Priority = DMA_PRIORITY_HIGH;
hdma_tim1_trig.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
if (HAL_DMA_Init(&hdma_tim1_trig) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(htim_base,hdma[TIM_DMA_ID_TRIGGER],hdma_tim1_trig);
/* USER CODE BEGIN TIM1_MspInit 1 */
extern void HAL_DMA_RxHalfCpltCallback(DMA_HandleTypeDef *hdma);
extern void HAL_DMA_RxCpltCallback(DMA_HandleTypeDef *hdma);
// Register your callbacks now, before you ever start the DMA:
HAL_DMA_RegisterCallback(&hdma_tim1_trig,
HAL_DMA_XFER_HALFCPLT_CB_ID,
HAL_DMA_RxHalfCpltCallback);
HAL_DMA_RegisterCallback(&hdma_tim1_trig,
HAL_DMA_XFER_CPLT_CB_ID,
HAL_DMA_RxCpltCallback);
// Enable the IRQ line for that stream
HAL_NVIC_SetPriority(DMA1_Stream1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Stream1_IRQn);
/* USER CODE END TIM1_MspInit 1 */
}
}
PE9 rising edge → TI1FP1 external clock tick
CC1 match → Because CC1S=01, the timer captures and also generates TRGO (MMS=OC1)
TRGO → Fires the TIM1_TRIG DMA request
DMA (Circular) → Reads GPIOE->IDR into portE_samples[]
HAL_DMA_RxHalfCpltCallback / HAL_DMA_RxCpltCallback now fire as expected!
Hope this helps anyone else wrestling with TIMx + trigger-DMA on the H7 series.
2025-07-14 10:13 AM - edited 2025-07-14 10:14 AM
Ensure buffer is somewhere that DMA can access.
When code fails to behave as expected, debug it and examine the state handles for evidence of errors. Implement error callbacks.
2025-07-15 1:35 AM
Update: Solved! Here’s what finally worked for TIM1_CH1 → DMA on every PE9 rising edge
The key was turning the CC1 events into TRGO pulses and wiring that into a trigger‐DMA stream.
Here’s the summary of what I changed:
static void MX_TIM1_Init(void)
{
/* USER CODE BEGIN TIM1_Init 0 */
/* USER CODE END TIM1_Init 0 */
TIM_SlaveConfigTypeDef sSlaveConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_IC_InitTypeDef sConfigIC = {0};
/* USER CODE BEGIN TIM1_Init 1 */
/* USER CODE END TIM1_Init 1 */
htim1.Instance = TIM1;
htim1.Init.Prescaler = 0;
htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
htim1.Init.Period = 65535;
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim1.Init.RepetitionCounter = 0;
htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim1) != HAL_OK)
{
Error_Handler();
}
// 1) Input-Capture on CH1 so we get CC1 events from TI1FP1
if (HAL_TIM_IC_Init(&htim1) != HAL_OK) {
Error_Handler();
}
sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
sConfigIC.ICFilter = 0;
if (HAL_TIM_IC_ConfigChannel(&htim1, &sConfigIC, TIM_CHANNEL_1) != HAL_OK) {
Error_Handler();
}
// 2) External clock mode 1: TI1FP1 (PE9 rising edge) → counter + CC1
sSlaveConfig.SlaveMode = TIM_SLAVEMODE_EXTERNAL1;
sSlaveConfig.InputTrigger = TIM_TS_TI1FP1;
sSlaveConfig.TriggerPolarity = TIM_TRIGGERPOLARITY_RISING;
sSlaveConfig.TriggerFilter = 0;
if (HAL_TIM_SlaveConfigSynchro(&htim1, &sSlaveConfig) != HAL_OK) {
Error_Handler();
}
// 3) Make CC1 mismatches ALSO generate a TRGO
sMasterConfig.MasterOutputTrigger = TIM_TRGO_OC1; // TRGO on CC1
sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_ENABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK) {
Error_Handler();
}
/* USER CODE BEGIN TIM1_Init 2 */
// 4) Arm the trigger-DMA (TIM1_TRIG) to copy GPIOE->IDR → portE_samples[]
// and install your half/full callbacks in MSP-init.
if (HAL_DMA_Start_IT(&hdma_tim1_trig,
(uint32_t)&GPIOE->IDR,
(uint32_t)portE_samples,
PORT_E_SAMPLES_BUFFER_SIZE) != HAL_OK)
{
Error_Handler();
}
__HAL_TIM_ENABLE_DMA(&htim1, TIM_DMA_TRIGGER);
// 5) Finally start the timer itself
HAL_TIM_Base_Start(&htim1);
/* USER CODE END TIM1_Init 2 */
}
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* htim_base)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(htim_base->Instance==TIM1)
{
/* USER CODE BEGIN TIM1_MspInit 0 */
/* USER CODE END TIM1_MspInit 0 */
/* Peripheral clock enable */
__HAL_RCC_TIM1_CLK_ENABLE();
__HAL_RCC_GPIOE_CLK_ENABLE();
/**TIM1 GPIO Configuration
PE9 ------> TIM1_CH1
*/
GPIO_InitStruct.Pin = L_CLK_RX_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF1_TIM1;
HAL_GPIO_Init(L_CLK_RX_GPIO_Port, &GPIO_InitStruct);
/* TIM1 DMA Init */
/* TIM1_TRIG Init */
hdma_tim1_trig.Instance = DMA1_Stream1;
hdma_tim1_trig.Init.Request = DMA_REQUEST_TIM1_TRIG;
hdma_tim1_trig.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_tim1_trig.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_tim1_trig.Init.MemInc = DMA_MINC_ENABLE;
hdma_tim1_trig.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
hdma_tim1_trig.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
hdma_tim1_trig.Init.Mode = DMA_CIRCULAR;
hdma_tim1_trig.Init.Priority = DMA_PRIORITY_HIGH;
hdma_tim1_trig.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
if (HAL_DMA_Init(&hdma_tim1_trig) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(htim_base,hdma[TIM_DMA_ID_TRIGGER],hdma_tim1_trig);
/* USER CODE BEGIN TIM1_MspInit 1 */
extern void HAL_DMA_RxHalfCpltCallback(DMA_HandleTypeDef *hdma);
extern void HAL_DMA_RxCpltCallback(DMA_HandleTypeDef *hdma);
// Register your callbacks now, before you ever start the DMA:
HAL_DMA_RegisterCallback(&hdma_tim1_trig,
HAL_DMA_XFER_HALFCPLT_CB_ID,
HAL_DMA_RxHalfCpltCallback);
HAL_DMA_RegisterCallback(&hdma_tim1_trig,
HAL_DMA_XFER_CPLT_CB_ID,
HAL_DMA_RxCpltCallback);
// Enable the IRQ line for that stream
HAL_NVIC_SetPriority(DMA1_Stream1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Stream1_IRQn);
/* USER CODE END TIM1_MspInit 1 */
}
}
PE9 rising edge → TI1FP1 external clock tick
CC1 match → Because CC1S=01, the timer captures and also generates TRGO (MMS=OC1)
TRGO → Fires the TIM1_TRIG DMA request
DMA (Circular) → Reads GPIOE->IDR into portE_samples[]
HAL_DMA_RxHalfCpltCallback / HAL_DMA_RxCpltCallback now fire as expected!
Hope this helps anyone else wrestling with TIMx + trigger-DMA on the H7 series.
2025-07-15 2:29 AM
Hello @johnotraffic
Does the DMA transfer complete interrupt occur?
2025-07-15 2:44 AM
Hi @Saket_Om ,
Yes, both HAL_DMA_RxHalfCpltCallback and HAL_DMA_RxCpltCallback are called.