cancel
Showing results for 
Search instead for 
Did you mean: 

stm32f411 project TIM2 triggers ADC to sample 4 channels and DMA to transfer to main

PRenn.1
Associate III

Guys, it was really hard giving birth to this proof of concept: 
- Every 100ms the timer hits the ceiling , lessons leaned : trigger event selection setting is irrelevant, it'll work
- in the TIM2 ISR (more precisely : the user callback TIM2's ISR is calling) I start ADC-with-DMA (DMA destination buffer and length given)
- DMA shovels as much results ("conversions" ) as one has confiured in the ADC(!) to DMA Buffer, irrespective of DMA Buffer size.

The weird part is: you can have a 4 word DMA buffer and define only 1 conversion in the ADC setup.
Every call DMA-start of the ADC will fill the NEXT space in the DMA buffer! 
E.g. DMA memorizes, that 100ms ago, it shoveled the (single channel) ADC result at say buffer[2] as as it was instructed that the buffer were 4 words long.   Thus this time it will fill buffer[3] with the result value.  Next iteration, even if a whopping 100ms later it will loop back to buffer[0].

You can define an arbitrary number  of conversions at ADC, say, 4, all of them done from channel1 !
Then DMA will transfer 4 values (channel1, channel1, channel1, channel1) into your 4 word buffer for a single ADC start.
Or - what you probably want - 4 conversions for 4 different channels (ch1 - ch4). Then one start of the ADC will fill your buffer with the values of the four confiured channels.

What a crock you might think, well that was my knee-jerk reaction, too.    But no, it is very smart and flexible:
Setting up say two conversion (channel 1 and channel 2) and a DMA buffer of 8 words you'll get not only the "current" values of the two channels but you still have the 3 historic values of these channels in your DMAbuffer!

The only thing one would need is an indication of "buffer place written to recently".

How to discover the above:
Configure USB_CDC and use CDC_Transmit_FS to ship your debug into back to your PC.
That is what the soap-box ST_Link I've ordere should be able via SWO, the nifty, tiny chinese ST_Links do not feature the SWO backconnunication.

 

The hard part is to come up with the correct callcback function names :

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef* htim)

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)

 



Here is the sourcecode:

 

 

 

#include "main.h"
#include "usb_device.h"
#include "usbd_cdc_if.h"
ADC_HandleTypeDef hadc1;
DMA_HandleTypeDef hdma_adc1;
TIM_HandleTypeDef htim2;

char *buffer[ 200 ];
uint32_t  adc_data[  ] = { 22L, 33l, 44l, 55l };   // recognizable initialisation ;)
uint32_t  adc_convs;
uint32_t  tim_convs;

void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_ADC1_Init(void);
static void MX_TIM2_Init(void);

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef* htim){
	tim_convs++;
	HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);  // LED on
	HAL_ADC_Start_DMA( &hadc1, adc_data, 4  );
}
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)    // one call per group !
{
	adc_convs ++;
	HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);  // LED off
}

int main(void)
{
  uint32_t cnt = 0l;
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_ADC1_Init();
  MX_USB_DEVICE_Init();
  MX_TIM2_Init();

  HAL_TIM_Base_Start_IT(&htim2);   // TIM2_Init does NOT start the timer

  while (1)
  {
      cnt ++;
      HAL_Delay( 20 );    // should be faster than the timer so monitoring doesn't miss 
      sprintf( buffer, "LOOP %ld TIM %ld ADC %ld : %ld, %ld, %ld, %ld\r\n" 
             , cnt, tim_convs, adc_convs
             , adc_data[0], adc_data[1], adc_data[2], adc_data[3] );
      CDC_Transmit_FS( buffer, strlen( buffer ) );
  }
}

void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 25;
  RCC_OscInitStruct.PLL.PLLN = 192;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 4;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_3) != HAL_OK)
  {
    Error_Handler();
  }
}
static void MX_ADC1_Init(void)
{
  ADC_ChannelConfTypeDef sConfig = {0};
  hadc1.Instance = ADC1;
  hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
  hadc1.Init.Resolution = ADC_RESOLUTION_12B;
  hadc1.Init.ScanConvMode = ENABLE;
  hadc1.Init.ContinuousConvMode = DISABLE;
  hadc1.Init.DiscontinuousConvMode = DISABLE;
  hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
  hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc1.Init.NbrOfConversion = 4;
  hadc1.Init.DMAContinuousRequests = DISABLE;
  hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;
  if (HAL_ADC_Init(&hadc1) != HAL_OK)
  {
    Error_Handler();
  }
  sConfig.Channel = ADC_CHANNEL_1;
  sConfig.Rank = 1;
  sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sConfig.Channel = ADC_CHANNEL_2;
  sConfig.Rank = 2;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sConfig.Channel = ADC_CHANNEL_3;
  sConfig.Rank = 3;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sConfig.Channel = ADC_CHANNEL_4;
  sConfig.Rank = 4;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
}
static void MX_TIM2_Init(void)
{
  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};
  htim2.Instance = TIM2;
  htim2.Init.Prescaler = 48000;
  htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim2.Init.Period = 200;
  htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
  {
    Error_Handler();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
}
static void MX_DMA_Init(void)
{
  __HAL_RCC_DMA2_CLK_ENABLE();
  HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);
}

static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOH_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();
  HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);
  GPIO_InitStruct.Pin = GPIO_PIN_13;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
}
void Error_Handler(void)
{
  __disable_irq();
  while (1)
  {
  }
}

 

 

 

 

0 REPLIES 0