cancel
Showing results for 
Search instead for 
Did you mean: 

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

johnotraffic
Associate II

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.

1 ACCEPTED SOLUTION

Accepted Solutions
johnotraffic
Associate II

 

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.

View solution in original post

4 REPLIES 4
TDK
Super User

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.

If you feel a post has answered your question, please click "Accept as Solution".
johnotraffic
Associate II

 

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.

Saket_Om
ST Employee

Hello @johnotraffic 

Does the DMA transfer complete interrupt occur?

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

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