cancel
Showing results for 
Search instead for 
Did you mean: 

stm32L476VE ADC reading opamp channel does not work

DMolo
Senior

I have designed an L476 into a simple controller (BMS) and am trying to use the opamp and ADC and comparators to read the current, and implement a hardware fast overcurrent protection.

The opamp is working fine in PGA mode G16. I can see as I inject 20A across the current sensors, the stm pin goes up and down in voltage on my oscilloscope.

The ADC is set up in scan mode, with 14 conversions, and the rest of the channels read fine; I am reading lots of thermistors and input/output voltages and such like.

The opamp channel is simply returning noise between 475 and 493 counts irrespective of the current I inject (20A injected results in about 100mV shift in the opamp signal)

I have replicated this on a very simple sketch:

 /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_ADC1_Init();
  MX_OPAMP2_Init();
  MX_TIM3_Init();
  /* USER CODE BEGIN 2 */

  HAL_OPAMP_Start(&hopamp2);
  HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED);
  HAL_Delay(100);
  HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
  htim3.Instance->CCR1 = 100;
  HAL_ADC_Start(&hadc1);

DMolo_1-1755334809364.png

 

 

I have configured the opamp and timer and ADC as such:

static void MX_ADC1_Init(void)
{

  /* USER CODE BEGIN ADC1_Init 0 */

  /* USER CODE END ADC1_Init 0 */

  ADC_MultiModeTypeDef multimode = {0};
  ADC_ChannelConfTypeDef sConfig = {0};

  /* USER CODE BEGIN ADC1_Init 1 */

  /* USER CODE END ADC1_Init 1 */

  /** 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_SINGLE_CONV;
  hadc1.Init.LowPowerAutoWait = DISABLE;
  hadc1.Init.ContinuousConvMode = DISABLE;
  hadc1.Init.NbrOfConversion = 1;
  hadc1.Init.DiscontinuousConvMode = DISABLE;
  hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIG_T3_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 the ADC multi-mode
  */
  multimode.Mode = ADC_MODE_INDEPENDENT;
  if (HAL_ADCEx_MultiModeConfigChannel(&hadc1, &multimode) != HAL_OK)
  {
    Error_Handler();
  }

  /** Configure Regular Channel
  */
  sConfig.Channel = ADC_CHANNEL_15;
  sConfig.Rank = ADC_REGULAR_RANK_1;
  sConfig.SamplingTime = ADC_SAMPLETIME_247CYCLES_5;
  sConfig.SingleDiff = ADC_SINGLE_ENDED;
  sConfig.OffsetNumber = ADC_OFFSET_NONE;
  sConfig.Offset = 0;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN ADC1_Init 2 */

  /* USER CODE END ADC1_Init 2 */

}

/**
  * @brief OPAMP2 Initialization Function
  * @PAram None
  * @retval None
  */
static void MX_OPAMP2_Init(void)
{

  /* USER CODE BEGIN OPAMP2_Init 0 */

  /* USER CODE END OPAMP2_Init 0 */

  /* USER CODE BEGIN OPAMP2_Init 1 */

  /* USER CODE END OPAMP2_Init 1 */
  hopamp2.Instance = OPAMP2;
  hopamp2.Init.PowerSupplyRange = OPAMP_POWERSUPPLY_LOW;
  hopamp2.Init.Mode = OPAMP_PGA_MODE;
  hopamp2.Init.NonInvertingInput = OPAMP_NONINVERTINGINPUT_IO0;
  hopamp2.Init.InvertingInput = OPAMP_INVERTINGINPUT_IO0;
  hopamp2.Init.PgaGain = OPAMP_PGA_GAIN_16;
  hopamp2.Init.PowerMode = OPAMP_POWERMODE_NORMALPOWER;
  hopamp2.Init.UserTrimming = OPAMP_TRIMMING_FACTORY;
  if (HAL_OPAMP_Init(&hopamp2) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN OPAMP2_Init 2 */

  /* USER CODE END OPAMP2_Init 2 */

}

/**
  * @brief TIM3 Initialization Function
  * @PAram None
  * @retval None
  */
static void MX_TIM3_Init(void)
{

  /* USER CODE BEGIN TIM3_Init 0 */

  /* USER CODE END TIM3_Init 0 */

  TIM_MasterConfigTypeDef sMasterConfig = {0};
  TIM_OC_InitTypeDef sConfigOC = {0};

  /* USER CODE BEGIN TIM3_Init 1 */

  /* USER CODE END TIM3_Init 1 */
  htim3.Instance = TIM3;
  htim3.Init.Prescaler = 47;
  htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim3.Init.Period = 1000;
  htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_PWM_Init(&htim3) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_OC1REF;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sConfigOC.OCMode = TIM_OCMODE_PWM1;
  sConfigOC.Pulse = 0;
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM3_Init 2 */

  /* USER CODE END TIM3_Init 2 */

}


I have noticed that if I alter the trigger frequency (set PSC to 470 or ARR to 10000) on the timer, that the value changes - it goes down to 380, or 120 if I increase both ARR and PSC - but still does not respond to current and does not affect the opamp output visibly for any value.


The GPIO is configured as such, looking at the registers:

DMolo_0-1755334775142.png

DMolo_2-1755334820167.png

And the ADC (DR is 7B/123 because right now the triggers are slow):

DMolo_3-1755334894702.png

 

Ok, so... What am I getting wrong? Why does it not read the opamp? Any more info required I shall grab it!

 

Thanks! 

 

 

1 ACCEPTED SOLUTION

Accepted Solutions
waclawek.jan
Super User

In the 'L476, you have to switch on an analog-isolation switch (in GPIOx_ASCR), before you can use the ADC.

JW

View solution in original post

6 REPLIES 6
DMolo
Senior

I have just made a board patch using tiny little wires between the opamp output and the ADC input on a different pin (PA5, ADC1 in10).

 

It works instantly. With this setup, the OPAMP channel also responds a bit (I set injected conversions to measure bothPA5in10 and PB0in15) but the PB0 response is about 1/3 of the response from the PA5 pin.

I think there must be some error in the ST HAL/CubeIDE configuration mechanism (or a silicon bug?) that prevents the ADC from reading the opamp when configured with the pin in dual mode.

Modifiet board that successfully reads current on PA5

DMolo_0-1755339150394.png

Un modified board fresh from factory

DMolo_1-1755339178851.png

 

 

waclawek.jan
Super User

The GPIOB registers you've shown indicate, that you don't have GPIOB clock enabled in RCC.

I don't know whether this is the cause of your problems. I don't use Cube/CubeMX.
JW

DMolo_0-1755348749724.png

Still no joy.
The original project did have these enabled, since CAN, GPIO, PB2,PB12,13,14... every PBx pin was enabled.

 

I just enabled them on the dummy project and no change.

 

 

waclawek.jan
Super User

In the 'L476, you have to switch on an analog-isolation switch (in GPIOx_ASCR), before you can use the ADC.

JW

Well it is late at night now and the office is locked down otherwise I would be sprinting back there to try... I think this is it. Too bad the CubeMX thingy did not think it necessary to set this little gotcha bit.

What possible use is that randomly placed analog switch??? 

Thanks so much, I was banging my head against the wall on this. Everything else was dropping together so smoothly...

To be fair, buried in the plain text of page 298 of the reference manual it does say:

Additional functions:
– For the ADC, DAC, OPAMP and COMP, configure the desired I/O in analog mode
in the GPIOx_MODER register and configure the required function in the ADC,
DAC, OPAMP, and COMP registers. For the ADC, it is necessary to configure the
GPIOx_ASCR register (only for STM32L47x/L48x).

Thanks again.

I just added

  GPIOB->ASCR |=0x01;

after the opamp start and before the ADC start and it works perfectly.

Thanks Jan.