AnsweredAssumed Answered

STM32L432KC - Timer DMA Triggering ADC DMA?

Question asked by Nolan L on Feb 17, 2017
Latest reply on Feb 19, 2017 by KIC8462852 EPIC204278916

Hi,

On my larger project, I would like to be able to generate a timer that will trigger an ADC read on each rising edge. I was able to tackle this problem by creating a smaller project and executing it! Super excited.

 

My excitement was a bit dampened when I realized how many interrupts were firing from the timer. For this test, I am currently operating my timer at ~123kHz which equates to roughly 8.13uS. While this is fine running on a while(1) loop example, I don't think it will go well with my larger project running FreeRTOS.

 

Now comes the question:

Is there a way to set this up where the timer can generate a DMA to trigger the ADC read using the DMA? I would imagine this is the best path forward to minimize CPU usage if possible. That way, I am not firing my interrupts every 8.13uS. If not, what is the best engineering practice?

 

Any example code is greatly appreciated! I just couldn't seem to find any post in this community trying to user a timer with DMA access to trigger an ADC DMA.

 

According to RM0393 (Page 776), it looks as if I should be able to do an event trigger on TIM15:

• Interrupt/DMA generation on the following events:

   – Update: counter overflow, counter initialization (by software or internal/external trigger)

   – Trigger event (counter start, stop, initialization or count by internal/external trigger)

   – Input capture

   – Output compare

   – Break input (interrupt request)

 

Note: I am currently not DMA-ing the ADC in this example. This will be done on the larger project!

         I am not actually grabbing the ADC value but toggling a pin to know that the ADC is being called on the          rising edge of the timer.

 

Main:

int main(void)
{
  /* Clock Inits*/

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_TIM15_Init();
  MX_ADC1_Init();

  /* USER CODE BEGIN 2 */
  HAL_TIM_Base_Start_IT(&htim15);
  HAL_ADC_Start_IT(&hadc1);
  /* USER CODE END 2 */

  /* Infinite loop */
}

 

Callbacks:

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
  if (hadc->Instance==ADC1)
  {
       HAL_GPIO_TogglePin(USER_ADC_TOGGLE_PIN_GPIO_Port, USER_ADC_TOGGLE_PIN_Pin);
  }
}

 

Interrupt:

void TIM1_BRK_TIM15_IRQHandler(void)
{
  HAL_GPIO_TogglePin(USER_TIMER_TOGGLE_PIN_GPIO_Port, USER_TIMER_TOGGLE_PIN_Pin);
  HAL_TIM_IRQHandler(&htim15);
}

 

Timer / ADC Setup:

static void MX_TIM15_Init(void)
{

  TIM_MasterConfigTypeDef sMasterConfig;
  TIM_OC_InitTypeDef sConfigOC;
  TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig;

  htim15.Instance = TIM15;
  htim15.Init.Prescaler = 47;
  htim15.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim15.Init.Period = 1;
  htim15.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim15.Init.RepetitionCounter = 0;
  if (HAL_TIM_PWM_Init(&htim15) != HAL_OK)
  {
    Error_Handler();
  }

  sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_ENABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim15, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }

  sConfigOC.OCMode = TIM_OCMODE_PWM1;
  sConfigOC.Pulse = 0;
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
  sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
  sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
  if (HAL_TIM_PWM_ConfigChannel(&htim15, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
  {
    Error_Handler();
  }

  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.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
  if (HAL_TIMEx_ConfigBreakDeadTime(&htim15, &sBreakDeadTimeConfig) != HAL_OK)
  {
    Error_Handler();
  }

}

static void MX_ADC1_Init(void)
{

  ADC_ChannelConfTypeDef sConfig;

    /**Common config
    */
  hadc1.Instance = ADC1;
  hadc1.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;
  hadc1.Init.Resolution = ADC_RESOLUTION_12B;
  hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
  hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;
  hadc1.Init.LowPowerAutoWait = DISABLE;
  hadc1.Init.ContinuousConvMode = DISABLE;
  hadc1.Init.NbrOfConversion = 1;
  hadc1.Init.DiscontinuousConvMode = ENABLE;
  hadc1.Init.NbrOfDiscConversion = 1;
  hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIG_T15_TRGO;
  hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
  hadc1.Init.DMAContinuousRequests = DISABLE;
  hadc1.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
  hadc1.Init.OversamplingMode = DISABLE;
  if (HAL_ADC_Init(&hadc1) != HAL_OK)
  {
    Error_Handler();
  }

    /**Configure Regular Channel
    */
  sConfig.Channel = ADC_CHANNEL_8;
  sConfig.Rank = 1;
  sConfig.SamplingTime = ADC_SAMPLETIME_640CYCLES_5;
  sConfig.SingleDiff = ADC_SINGLE_ENDED;
  sConfig.OffsetNumber = ADC_OFFSET_NONE;
  sConfig.Offset = 0;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }

}

Outcomes