Skip to main content
Visitor II
July 14, 2025
Solved

Using TIM1 CH1 External Clock on PE9 to Trigger DMA on GPIOE→IDR (Callbacks Never Fire)

  • July 14, 2025
  • 3 replies
  • 417 views

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:

  1. Increments the TIM1 counter (External Clock Mode 1 via TI1FP1)

  2. Fires a DMA transfer from GPIOE->IDR into a circular buffer

  3. 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.

    This topic has been closed for replies.
    Best answer by johnotraffic

     

    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:


    1. TIM1 Initialization + Start DMA

    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 */
    
    }

    2. DMA (Triggered by TRGO)

     
    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 */
    
     }
    
    }

    Result

    • 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.

    3 replies

    Super User
    July 14, 2025

    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.

    johnotrafficAuthorAnswer
    Visitor II
    July 15, 2025

     

    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:


    1. TIM1 Initialization + Start DMA

    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 */
    
    }

    2. DMA (Triggered by TRGO)

     
    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 */
    
     }
    
    }

    Result

    • 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.

    Technical Moderator
    July 15, 2025

    Hello @johnotraffic 

    Does the DMA transfer complete interrupt occur?

    Visitor II
    July 15, 2025

    Hi @Saket_Om ,
    Yes, both HAL_DMA_RxHalfCpltCallback and HAL_DMA_RxCpltCallback are called.