cancel
Showing results for 
Search instead for 
Did you mean: 

Timer triggering DMA

con3
Senior

Morning everyone,

I'm trying to reconfigure a timer configuration that generates dma requests.

The DMA request, moves data from the GPIO pins to memory, so peripheral to memory. Currently I have two timers cascaded, the one(being timer 8 is fed with an external clock) and produces a waveform when it is triggered. It outputs this waveform, which is then fed to timer 1 which is in input capture and generates the DMA requests.

This seems wasteful as the time it takes to toggle the pin and generate the DMA request from cascaded timers slows down the process.

To improve this, I'd like to only use timer 8 in output capture(no output mode) to trigger the dma whenever the counter overflows.

Here is the current timer 8 configuration:

 static void MX_TIM8_Init(void) {
 
 
   TIM_ClockConfigTypeDef sClockSourceConfig;
 
   TIM_SlaveConfigTypeDef sSlaveConfig;
 
   TIM_MasterConfigTypeDef sMasterConfig;
 
   TIM_OC_InitTypeDef sConfigOC;
 
   TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig;
 
 
   htim8.Instance = TIM8;
 
   htim8.Init.Prescaler = 0;
 
   htim8.Init.CounterMode = TIM_COUNTERMODE_UP;
 
   htim8.Init.Period = 15;
 
   htim8.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
 
   htim8.Init.RepetitionCounter = 17;
 
   htim8.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
 
   if (HAL_TIM_Base_Init(&htim8) != HAL_OK) {
 
      _Error_Handler(__FILE__, __LINE__);
 
   }
 
 
   sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_ETRMODE2;
 
   sClockSourceConfig.ClockPolarity = TIM_CLOCKPOLARITY_INVERTED;
 
   sClockSourceConfig.ClockPrescaler = TIM_CLOCKPRESCALER_DIV1;
 
   sClockSourceConfig.ClockFilter = 0;
 
   if (HAL_TIM_ConfigClockSource(&htim8, &sClockSourceConfig) != HAL_OK) {
 
      _Error_Handler(__FILE__, __LINE__);
 
   }
 
 
   if (HAL_TIM_OC_Init(&htim8) != HAL_OK) {
 
      _Error_Handler(__FILE__, __LINE__);
 
   }
 
 
   sSlaveConfig.SlaveMode = TIM_SLAVEMODE_COMBINED_RESETTRIGGER;
 
   sSlaveConfig.InputTrigger = TIM_TS_TI1FP1;
 
   sSlaveConfig.TriggerPolarity = TIM_TRIGGERPOLARITY_FALLING;
 
   sSlaveConfig.TriggerFilter = 0;
 
   if (HAL_TIM_SlaveConfigSynchronization(&htim8, &sSlaveConfig) != HAL_OK) {
 
      _Error_Handler(__FILE__, __LINE__);
 
   }
 
 
   sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
 
   sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_RESET;
 
   sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
 
   if (HAL_TIMEx_MasterConfigSynchronization(&htim8, &sMasterConfig)
 
         != HAL_OK) {
 
      _Error_Handler(__FILE__, __LINE__);
 
   }
 
 
   sConfigOC.OCMode = TIM_OCMODE_RETRIGERRABLE_OPM1;
 
   sConfigOC.Pulse = 8;
 
   sConfigOC.OCPolarity = TIM_OCPOLARITY_LOW;
 
   sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
 
   sConfigOC.OCFastMode = TIM_OCFAST_ENABLE;
 
   sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
 
   sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
 
   if (HAL_TIM_OC_ConfigChannel(&htim8, &sConfigOC, TIM_CHANNEL_2) != HAL_OK) {
 
      _Error_Handler(__FILE__, __LINE__);
 
   }
 
 
   sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;
 
   sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;
 
   sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;
 
   sBreakDeadTimeConfig.DeadTime = 0;
 
   sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;
 
   sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
 
   sBreakDeadTimeConfig.BreakFilter = 0;
 
   sBreakDeadTimeConfig.Break2State = TIM_BREAK2_DISABLE;
 
   sBreakDeadTimeConfig.Break2Polarity = TIM_BREAK2POLARITY_HIGH;
 
   sBreakDeadTimeConfig.Break2Filter = 0;
 
   sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
 
   if (HAL_TIMEx_ConfigBreakDeadTime(&htim8, &sBreakDeadTimeConfig)
 
         != HAL_OK) {
 
      _Error_Handler(__FILE__, __LINE__);
 
   }
 
 
}
 
 

Here's the DMA configuration:

     else if(htim_base->Instance==TIM8)
  {
  /* USER CODE BEGIN TIM8_MspInit 0 */
 
  /* USER CODE END TIM8_MspInit 0 */
    /* Peripheral clock enable */
    __HAL_RCC_TIM8_CLK_ENABLE();
  
    /**TIM8 GPIO Configuration    
    PA0-WKUP     ------> TIM8_ETR
    PC6     ------> TIM8_CH1 
    */
    GPIO_InitStruct.Pin = GPIO_PIN_0;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF3_TIM8;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
 
    GPIO_InitStruct.Pin = GPIO_PIN_6;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF3_TIM8;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
 
  /* USER CODE BEGIN TIM8_MspInit 1 */
     hdma_tim8_ch2.Instance = DMA2_Stream3;
     hdma_tim8_ch2.Init.Channel = DMA_CHANNEL_7;
     hdma_tim8_ch2.Init.Direction = DMA_PERIPH_TO_MEMORY;
     hdma_tim8_ch2.Init.PeriphInc = DMA_PINC_DISABLE;
     hdma_tim8_ch2.Init.MemInc = DMA_MINC_ENABLE;
     hdma_tim8_ch2.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
     hdma_tim8_ch2.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
     hdma_tim8_ch2.Init.Mode = DMA_CIRCULAR;
     hdma_tim8_ch2.Init.Priority = DMA_PRIORITY_VERY_HIGH;
     hdma_tim8_ch2.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
     hdma_tim8_ch2.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_HALFFULL;
     hdma_tim8_ch2.Init.MemBurst = DMA_MBURST_INC4;
     hdma_tim8_ch2.Init.PeriphBurst = DMA_PBURST_SINGLE;
     if (HAL_DMA_Init(&hdma_tim8_ch2) != HAL_OK)
     {
       _Error_Handler(__FILE__, __LINE__);
     }
 
     __HAL_LINKDMA(htim_base,hdma[TIM_DMA_ID_CC2],hdma_tim8_ch2);
 
  /* USER CODE END TIM8_MspInit 1 */
}

Here's how I'm starting the timer and DMA:

	hdma_tim8_ch2.XferM1CpltCallback = TransferComplete1;
	hdma_tim8_ch2.XferCpltCallback = TransferComplete;
	hdma_tim8_ch2.XferErrorCallback = TransferError;
	//Setup the DMA in Multibuffer mode with GPIOF as the input for the dataset
	if (HAL_DMAEx_MultiBufferStart_IT(htim8.hdma[TIM_DMA_ID_CC2], GPIOF_IDR,
			(uint32_t) &aDST_Buffer1, (uint32_t) &aDST_Buffer2, 32000)
			!= HAL_OK) {
		/* Transfer Error */
		Error_Handler();
	}
	//Enable the DMA and Timer interrupt
	__HAL_TIM_ENABLE_DMA(&htim8, TIM_DMA_CC2);
	__HAL_TIM_ENABLE_IT(&htim8, TIM_IT_CC2);
 
	HAL_TIM_OC_Start(&htim8, TIM_CHANNEL_2);
TIM_CCxChannelCmd(htim8.Instance, TIM_CHANNEL_2, TIM_CCx_ENABLE);

So This does seem to generate a DMA request, but not for both of these overflow cases:

htim8.Init.Period = 15;
sConfigOC.Pulse = 8;

As far as I can tell at this point, it seems to only generate the request when the counter surpasses the period value, but not for the pulse value.

How could I get the DMA generation to occur for both overflow at the pulse, period and to continue for the repetition counter period?

Any help will really be appreciated!

If any more information is required, please let me know

1 ACCEPTED SOLUTION

Accepted Solutions

> how would I set it up using the HAL_DMAEx_MultiBufferStart_It function?

As I've said, I don't Cube, and I am not going to decipher what does the above function do.

All you need to do is to set up two of the channels to provide the triggers from compare when needed, and then set the respective CCxDE bits in TIMx_DIER.

JW

View solution in original post

10 REPLIES 10

Which mcu?

I don't Cube so won't help with that. Look at your mcu's DMA chapter in RM, the table depicting the . In some STM32 models, some of the timers' DMA requests are merged, in those cases there's no separate TIMx_CHy in the table. In those cases several channels (and also update) could be used to trigger DMA transfers, if they are far away enough in time from each other (I'm not sure what would happen if they are closer together than the transfer duration, but I'd guess on missing transfers).

In 'F4, for example, there is a TIM8_CH1+TIM8_CH2+TIM8_CH3 combo in the DMA triggers, but the TIM8_UP is standalone, i.e. can't be "combined" with other TIM8-originating triggers into the same stream.

JW

con3
Senior

​ Hey @Community member​ !

I'm using the stm32f722ze,

I think you forgot to mention what you are referring to here:

"I don't Cube so won't help with that. Look at your mcu's DMA chapter in RM, the table depicting the "

But I'm assuming it's the DMA mapping table?

0690X0000060ORrQAM.png

From this table, I'm using stream 3, channel 7 which is only mapped to Tim8_ch2.

Which is what I'm using. If I used stream 2,channel 0 I think i would be in the realm of what you are referring to with:

"some STM32 models, some of the timers' DMA requests are merged, in those cases there's no separate TIMx_CHy in the table"

With the way I've set up the DMA and according to the table, shouldn't it be triggering on both the period and pulse overflow?

Thank you again for the help!

> With the way I've set up the DMA and according to the table, shouldn't it be triggering

> on both the period and pulse overflow?

You mean, both on Compare on given channel, and on Update?

No. What leads you to beleive it does?

Trigger from Update is marked as TIM8_UP in the table.

JW

con3
Senior

Hi @Community member​ ,

I messed that up, I see now that there's no way that I can get that up and running.

The update event will trigger a generation on this:

  1. htim8.Init.Period = 15;
    htim8.Init.Period = 15;

and the capture compare will generate a request on this:

sConfigOC.Pulse = 8;

sConfigOC.Pulse = 8;

I'm not sure if there's anyway that I could generate a dma request on both 8 and 15 counts of the external clock and repeat it with the repetition counter while using:

HAL_DMAEx_MultiBufferStart_IT(htim8.hdma[TIM_DMA_ID_CC2], GPIOF_IDR,
			(uint32_t) &aDST_Buffer1, (uint32_t) &aDST_Buffer2, 32000)

I understand that I could use two or more channels to trigger the DMA, for instance in the table I linked, Stream 2 , channel 0 could be triggered by TIm8_CH1,CH2 and CH3. but how would I set it up using the HAL_DMAEx_MultiBufferStart_It function? Would it be possible, or could I only use one request to trigger this?

Thanks for the help!

I really appreciate it

> how would I set it up using the HAL_DMAEx_MultiBufferStart_It function?

As I've said, I don't Cube, and I am not going to decipher what does the above function do.

All you need to do is to set up two of the channels to provide the triggers from compare when needed, and then set the respective CCxDE bits in TIMx_DIER.

JW

con3
Senior

Thank you @Community member​ ,

I'll try to get this going. Really appreciate the help!

I'll let you know if I get it going.

T J
Lead

I use timer 15 to run my ADC DMA

at startup;

void start_Timer15(void) {
    HAL_TIMEx_OCN_Start(&htim15, TIM_CHANNEL_1);
    HAL_TIM_Base_Start(&htim15);
}

T J
Lead

I use the cube like this:

con3
Senior

Thanks for the reply @Community member​ ,

Slightly different than what I'm trying to accomplish.

Although @Community member​ was correct, I could start two channels with capture compare on both to trigger the DMA and setup the multibuffer function like this:

if (HAL_DMAEx_MultiBufferStart_IT( htim8.hdma[TIM_DMA_ID_CC2|TIM_DMA_ID_CC3], GPIOF_IDR,

if (HAL_DMAEx_MultiBufferStart_IT(
			htim8.hdma[TIM_DMA_ID_CC2|TIM_DMA_ID_CC3], GPIOF_IDR,
			(uint32_t) &aDST_Buffer1, (uint32_t) &aDST_Buffer2, 32400)
			!= HAL_OK) {
		/* Transfer Error */
		Error_Handler();
}

@Community member​  I also wanted to ask, you mentioned you don't cube. I'd like to know what the more veteran guys on this forum use to code the mcu's. Do you directly setup everything using HAL/LL?

Thanks again for all the help.