STM32 ADC code using stm32duino

Hey guys,

I’ve spent some time in the last few weeks trying to implement a simple yet efficient way to read the adc for the stm32 boards, and more specifically to read the adc efficiently enough to be able to run it in each cycle of the PWM (for low side sensing).

I am trying to avoid using DMA, now I am not sure if this is going to be a final verdict, but at the moment I would like to create the simplest possible code which works :smiley:
And pooling seems like the reasonable solution.

The problem is that analogRead takes around 60us per phase. which is far too long.
I was able to come to around 10us for 3 phases using better adc settings. And this works pretty well and this basically solves the problem, the only problem is that since I am using polling the execution time is not deterministic. So it gives me some strange behavior. (sometimes it polls for millisecond before it receives a value).

Here is the latest code I came up to, it is for nucleo f401re, but it should work more or less on all the stm chips with stm32duino package. If someone has more experience setting these adc’s more efficiently I would be happy to hear your thoughts. The community is really limited in this sense and I am having a hard time to find good resources.


// some sytm32duino specific code not important for understanding
// --------------------------------------------------------------------------------------
#ifndef ADC_CLOCK_DIV
#ifdef ADC_CLOCK_SYNC_PCLK_DIV4
#define ADC_CLOCK_DIV       ADC_CLOCK_SYNC_PCLK_DIV4
#elif ADC_CLOCK_SYNC_PCLK_DIV2
#define ADC_CLOCK_DIV       ADC_CLOCK_SYNC_PCLK_DIV2
#elif defined(ADC_CLOCK_ASYNC_DIV1)
#define ADC_CLOCK_DIV       ADC_CLOCK_ASYNC_DIV1
#endif
#endif /* !ADC_CLOCK_DIV */

#ifndef ADC_SAMPLINGTIME
#if defined(ADC_SAMPLETIME_8CYCLES_5)
#define ADC_SAMPLINGTIME        ADC_SAMPLETIME_8CYCLES_5;
#elif defined(ADC_SAMPLETIME_12CYCLES_5)
#define ADC_SAMPLINGTIME        ADC_SAMPLETIME_12CYCLES_5;
#elif defined(ADC_SAMPLETIME_13CYCLES_5)
#define ADC_SAMPLINGTIME        ADC_SAMPLETIME_13CYCLES_5;
#elif defined(ADC_SAMPLETIME_15CYCLES)
#define ADC_SAMPLINGTIME        ADC_SAMPLETIME_15CYCLES;
#elif defined(ADC_SAMPLETIME_16CYCLES)
#define ADC_SAMPLINGTIME        ADC_SAMPLETIME_16CYCLES;
#elif defined(ADC_SAMPLETIME_19CYCLES_5)
#define ADC_SAMPLINGTIME        ADC_SAMPLETIME_19CYCLES_5;
#endif
#endif /* !ADC_SAMPLINGTIME */
// --------------------------------------------------------------------------------------

ADC_HandleTypeDef hadc;
void setup()
{
  pinMode(13,OUTPUT);
  pinMode(A0,INPUT);
  pinMode(A1,INPUT);
  pinMode(A2,INPUT);
  Serial.begin(115200);

  delay(2000);
  MX_ADC_Init();
}
int value[3]={0};
void loop()
{
  digitalWrite(13, HIGH);
  //adc_read(&hadc, value, 3);
  
  HAL_ADC_Start(&hadc);
  HAL_ADC_PollForConversion(&hadc, 1); // polling timeout 1ms - cannot go lower
  value[0] = HAL_ADC_GetValue(&hadc);
  HAL_ADC_PollForConversion(&hadc, 1);
  value[1] = HAL_ADC_GetValue(&hadc);
  HAL_ADC_PollForConversion(&hadc, 1);
  value[2] = HAL_ADC_GetValue(&hadc);
  
  digitalWrite(13, LOW);
  Serial.print(value[0]);
  Serial.print("\t");
  Serial.print(value[1]);
  Serial.print("\t");
  Serial.println(value[2]);
}

void MX_ADC_Init()
{
  ADC_ChannelConfTypeDef sConfig;
  /**Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion) 
  */
  hadc.Instance = ADC1;
  hadc.Init.ClockPrescaler = ADC_CLOCK_DIV;
  hadc.Init.Resolution = ADC_RESOLUTION_12B;
  hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc.Init.ScanConvMode = ENABLE;
  hadc.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
  hadc.Init.ContinuousConvMode = DISABLE;
  hadc.Init.NbrOfConversion = 3;
  hadc.Init.DiscontinuousConvMode = ENABLE; 
  hadc.Init.NbrOfDiscConversion = 3;
  // this is the important bit
  hadc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
  hadc.Init.ExternalTrigConv =  ADC_SOFTWARE_START; 
  hadc.Init.DMAContinuousRequests = DISABLE;
  HAL_ADC_Init(&hadc);
  /**Configure for the selected ADC regular channel to be converted. 
  */
  sConfig.Channel = ADC_CHANNEL_0;
  sConfig.Rank = 1;
  sConfig.SamplingTime = ADC_SAMPLINGTIME;
  HAL_ADC_ConfigChannel(&hadc, &sConfig);
     
  /**Configure for the selected ADC regular channel to be converted. 
  */
  sConfig.Channel = ADC_CHANNEL_1;
  sConfig.Rank = 2;
  HAL_ADC_ConfigChannel(&hadc, &sConfig);
  /**Configure for the selected ADC regular channel to be converted. 
  */
  sConfig.Channel = ADC_CHANNEL_4;
  sConfig.Rank = 3;
  HAL_ADC_ConfigChannel(&hadc, &sConfig);

  /* Configure ADC GPIO pin */
  pinmap_pinout(analogInputToPinName(A0), PinMap_ADC);
  pinmap_pinout(analogInputToPinName(A1), PinMap_ADC);
  pinmap_pinout(analogInputToPinName(A2), PinMap_ADC);
}
1 Like
  1. I wrote “ADC + DMA” of stm32f407 on “keilv5” three months ago, and it works very well.

2, I’m verifying to “simple_FOC” added “torque control loop”, ADC is necessary, so I am trying alone.

  1. The accessories are incomplete, but ADC + DMA and as5048 can work well, and drv8301 can also work.

adc.c

#include "adc.h"
//#include "stm32f4xx_hal_adc_ex.h"
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

ADC_HandleTypeDef hadc1;
DMA_HandleTypeDef hdma_adc1;
	 
uint32_t Adc_Val_Buf[ADC_CHANNEL_CNT*ADC_CHANNEL_FRE] = {0}; 
uint32_t Adcx_aver_val[ADC_CHANNEL_CNT] = {0}; 
	 

/* ADC1 init function */
void MX_ADC1_Init(void)
{

  /* USER CODE BEGIN ADC1_Init 0 */

  /* USER CODE END ADC1_Init 0 */

  ADC_ChannelConfTypeDef sConfig = {0};

  /* USER CODE BEGIN ADC1_Init 1 */

  /* USER CODE END ADC1_Init 1 */
  /** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion) 
  */
  hadc1.Instance = ADC1;
  hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
  hadc1.Init.Resolution = ADC_RESOLUTION_12B;
  hadc1.Init.ScanConvMode = ENABLE;
  hadc1.Init.ContinuousConvMode = ENABLE;
  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 = 2;
  hadc1.Init.DMAContinuousRequests = ENABLE;
  hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
  if (HAL_ADC_Init(&hadc1) != HAL_OK)
  {
    Error_Handler();
  }
  /** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time. 
  */
  sConfig.Channel = ADC_CHANNEL_2;
  sConfig.Rank = 1;
  sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time. 
  */
  sConfig.Channel = ADC_CHANNEL_3;
  sConfig.Rank = 2;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN ADC1_Init 2 */

  /* USER CODE END ADC1_Init 2 */

}

void Get_Adc_Channel_Val(void)
{
	uint8_t i = 0;
	
	/* ADC´æ´¢ÇøÇåÁã */
	for(i=0;i<ADC_CHANNEL_CNT;i++)
	{
		Adc_Val_Buf[i] = 0;
	}
	/* È¡³ö¶ÔӦͨµÀÇóºÍ*/
	for(i=0;i<ADC_CHANNEL_FRE;i++)
	{
		Adcx_aver_val[0] +=  Adc_Val_Buf[i*2+0];
		Adcx_aver_val[1] +=  Adc_Val_Buf[i*2+1];
		
	}
	/* ¶ÔӦͨµÀƽ¾ùÖµ */
	for(i=0;i<ADC_CHANNEL_CNT;i++)
	{
		Adcx_aver_val[i] /= ADC_CHANNEL_FRE;
	}
}

adc.h

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "dma.h"

#define ADC_CHANNEL_CNT 2 	//ADCͨµÀÊýÁ¿
#define ADC_CHANNEL_FRE 20	//µ¥¸öͨµÀ²ÉÑù´ÎÊý£¬ÓÃÓÚÇó¾ùÖµ
 
 
extern uint32_t Adc_Val_Buf[ADC_CHANNEL_CNT*ADC_CHANNEL_FRE]; 	//DMA´æ·ÅÊý×éµÄ¿Õ¼ä
extern uint32_t Adcx_aver_val[ADC_CHANNEL_CNT]; 								//±£´æµ¥¸öͨµÀ²ÉÑùƽ¾ùÖµ
 

/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

extern ADC_HandleTypeDef hadc1;
extern DMA_HandleTypeDef hdma_adc1;
/* USER CODE BEGIN Private defines */

/* USER CODE END Private defines */

void MX_ADC1_Init(void);
void Get_Adc_Channel_Val(void);
/* USER CODE BEGIN Prototypes */

/* USER CODE END Prototypes */

dma.c

#include "dma.h"

/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/*----------------------------------------------------------------------------*/
/* Configure DMA                                                              */
/*----------------------------------------------------------------------------*/

/* USER CODE BEGIN 1 */

/* USER CODE END 1 */

/** 
  * Enable DMA controller clock
  */
void MX_DMA_Init(void) 
{

  /* DMA controller clock enable */
  __HAL_RCC_DMA2_CLK_ENABLE();

  /* DMA interrupt init */
  /* DMA2_Stream0_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);

}

dma.h

#include "main.h"

/* DMA memory to memory transfer handles -------------------------------------*/

/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* USER CODE BEGIN Private defines */

/* USER CODE END Private defines */

void MX_DMA_Init(void);

/* USER CODE BEGIN Prototypes */

/* USER CODE END Prototypes */

main.c


int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
	
	LED_Init();
	as5048a_setup(&hspi2,AS5048_NSS_GPIO_PORT,AS5048_NSS_PIN);
		
	MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
	

	MX_DMA_Init();
  MX_ADC1_Init();
  
	/*Æô¶¯ADC1_DMA2*/
	HAL_ADC_Start_DMA(&hadc1,(uint32_t*) &Adc_Val_Buf, (ADC_CHANNEL_CNT*ADC_CHANNEL_FRE));
  printf("\n\r ***** START PRINTF ADC INFO ******** \r\n\n");
  /* USER CODE END 2 */
  MX_TIM1_Init();
  MX_TIM8_Init();
	printf("\n\r ***** START TIM_PWM INFO ******** \r\n\n");
	HAL_Delay(1000);
 
  while (1)
  {
    /* USER CODE END WHILE */
		AS5048_ReadData();
		Get_Adc_Channel_Val();
	
		printf("PA2 = %f\r\n",Adcx_aver_val[0] * 3.3 / 4096);
		printf("PA3 = %f\r\n",Adcx_aver_val[1] * 3.3 / 4096);
		
		printf("AS5048 Value %f\r\n",position_val);
		//if (as5048a_ReadData()) 	printf("AS5048 Value %f\r\n",position_val);
		//else printf("AS5048 is worng!");
		LED_Toggle();
		HAL_Delay(1000);
		
		
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}


I don’t know if this code will work for you?
I can’t upload the compressed file… So I can only post it one by one
-------------------------------------------------------------------------------------------