cancel
Showing results for 
Search instead for 
Did you mean: 

ADC does not output intended values

minion_SLAYER
Associate II

This should be easy but somehow I cannot make it work? I am working with the ADC of my STM23F4 Board trying to read the input from an AC voltage sensor. My previous code was using HAL and it was outputting my desired values (around 2000), but when I translated the code to standard register programming, the output is wrong (only around 25). What could be the problem with my initialization? There were other ADC initializations in the HAL code for the injected channels but I figured I dont need them in my other code. I am also using the system clock to implement delays with the systick handler, so that shouldn't be a problem anymore. Are there other important details that I missed? Thank you in advance for your help.

 

 

 

#include <stdint.h>
#include <stm32f4xx.h>
#include <stm32f411xe.h>
uint16_t adc_value = 0b0000;
uint16_t temp_bit = 0b0000;
uint16_t mask = 0b0000;
uint16_t move = 0b0000;

void GPIO_init(void);
void ADC_init(void);
void ADC_enable(void);
void ADC_startconv(void);
void ADC_waitconv(void);
int ADC_GetVal(void);
void delay_ms(int delay);


void GPIO_init(void) {
	//enable GPIOA clock
	RCC->AHB1ENR |= (1 << 0);

	//configure PA1 to analog mode
	GPIOA->MODER |= (1 << 3);
	GPIOA->MODER |= (1 << 2);

	//enable GPIO B clock
	RCC->AHB1ENR |= (1 << 1);
	//configure PB1 as output
	GPIOB->MODER &= ~(1 << 3);
	GPIOB->MODER |= (1 << 2);
	//configure PB1 as push-pull output
	GPIOB->OTYPER &= ~(1 << 1);
	//set to high as initial state
	GPIOB->ODR |= (1 << 1);
}

void ADC_init(void) {
	//enable adc clock
	RCC->APB2ENR |= (1 << 8);

	//prescaler div 4
	ADC->CCR |= (1 << 16);
	ADC->CCR &= ~(1 << 17);

	//configure ADC resolution = 12bits (00)
	ADC1->CR1 &= ~(1 << 25);
	ADC1->CR1 &= ~(1 << 24);

	//Disable Scan mode
	ADC1->CR1 &= ~(1 << 8);

	//Disable Continuous conversion mode
	ADC1->CR2 &= ~(1<<1);

	//Disable Discontinuous Conversion Mode
	ADC1->CR1 &= ~(1<<11);

	//No external trigger conversion edge
	ADC1->CR2 &= ~(1<<28);
	ADC1->CR2 &= ~(1<<29);

	//ADC conversion at software start (EXTSEL config doesn't matter because exten is 00
	ADC1->CR2 &= ~(1<<27);
	ADC1->CR2 &= ~(1<<26);
	ADC1->CR2 &= ~(1<<25);
	ADC1->CR2 &= ~(1<<24);

	//External trigger conversion set to software start

//	ADC1->CR1 |= (1 << 5);

	//configure sampling time of channel 1 to 15 per cycle
	ADC1->SMPR2 &= ~(1 << 5);
	ADC1->SMPR2 &= ~(1 << 4);
	ADC1->SMPR2 |= (1 << 3);

	//reqular sequence rank 1 to channel 1


	//configure data alignment
	ADC1->CR2 &= ~(1 << 11);

	//total number of conversions in the channel conversion sequence set to 1
	ADC1->SQR1 &= ~(1 << 23);
	ADC1->SQR1 &= ~(1 << 22);
	ADC1->SQR1 &= ~(1 << 21);
	ADC1->SQR1 &= ~(1 << 20);

	//DMA Continuous Requests DISABLE
	ADC1->CR2 &= ~(1<<9);

	//EOCS Single Conversion
	ADC1->CR2 &= ~(1<<10);


	//assign channel for first conversion
	//ADC1->SQR3 |= (1 << 0);
}

void ADC_enable(void) {
	ADC1->CR2 |= (1 << 0); //enable the adc
	delay_ms(1); // required to ensure adc stable
}

void ADC_startconv(void) {
	ADC1->CR2 |= (1 << 30);
}

void ADC_waitconv(void) {
	//wait for the end of conversion
	while (!((ADC1->SR) & (1 << 1))) {
		;
	}
}

int ADC_GetVal(void) {
	return ADC1->DR; //read the value contained at the data register
}

 

 

 The HAL version is this:

 

 

static void MX_ADC1_Init(void)
{

  /* USER CODE BEGIN ADC1_Init 0 */

  /* USER CODE END ADC1_Init 0 */

  ADC_ChannelConfTypeDef sConfig = {0};
  ADC_InjectionConfTypeDef sConfigInjected = {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; //done
  hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4; //done
  hadc1.Init.Resolution = ADC_RESOLUTION_12B; //done
  hadc1.Init.ScanConvMode = DISABLE; //done
  hadc1.Init.ContinuousConvMode = DISABLE; //done
  hadc1.Init.DiscontinuousConvMode = DISABLE;
  hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
  hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START; 
  hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT; //done
  hadc1.Init.NbrOfConversion = 1; //done
  hadc1.Init.DMAContinuousRequests = DISABLE; //done?? ADC1->CR2 &= ~(1<<9);
  hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV; //done
  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_0;
  sConfig.Rank = 1;
  sConfig.SamplingTime = ADC_SAMPLETIME_15CYCLES;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }

  /** Configures for the selected ADC injected channel its corresponding rank in the sequencer and its sample time
  */
  sConfigInjected.InjectedChannel = ADC_CHANNEL_0;
  sConfigInjected.InjectedRank = 1;
  sConfigInjected.InjectedNbrOfConversion = 1;
  sConfigInjected.InjectedSamplingTime = ADC_SAMPLETIME_3CYCLES;
  sConfigInjected.ExternalTrigInjecConvEdge = ADC_EXTERNALTRIGINJECCONVEDGE_NONE;
  sConfigInjected.ExternalTrigInjecConv = ADC_INJECTED_SOFTWARE_START;
  sConfigInjected.AutoInjectedConv = DISABLE;
  sConfigInjected.InjectedDiscontinuousConvMode = DISABLE;
  sConfigInjected.InjectedOffset = 0;
  if (HAL_ADCEx_InjectedConfigChannel(&hadc1, &sConfigInjected) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN ADC1_Init 2 */

  /* USER CODE END ADC1_Init 2 */

}

 

 

 

1 REPLY 1

You don't want to comment this out for converting CH1:

 

	//assign channel for first conversion
	//ADC1->SQR3 |= (1 << 0);

 

Read out and check/post content of ADC and relevant GPIO registers.

Show, how exactly do you perform the conversion and how do you check the resulting value.

Use the debugger for experimenting with ADC.

JW

PS. Style: do use symbols from the CMSIS-mandated header, write registers values at once not bit-per-bit