2024-09-10 05:37 AM
Hi,
i have a custom design with a STSPIN32G4 SoC. However, I think my question does refer to the internal microcontroller (STM32G431VBTx) and not the motor controller.
Do you have any ideas what can cause this issue?
I initialize the OPV with CubeMX generated Code:
hopamp3.Instance = OPAMP3;
hopamp3.Init.PowerMode = OPAMP_POWERMODE_NORMALSPEED;
hopamp3.Init.Mode = OPAMP_PGA_MODE;
hopamp3.Init.NonInvertingInput = OPAMP_NONINVERTINGINPUT_IO0;
hopamp3.Init.InternalOutput = ENABLE;
hopamp3.Init.TimerControlledMuxmode = OPAMP_TIMERCONTROLLEDMUXMODE_DISABLE;
hopamp3.Init.PgaConnect = OPAMP_PGA_CONNECT_INVERTINGINPUT_NO;
hopamp3.Init.PgaGain = OPAMP_PGA_GAIN_32_OR_MINUS_31;
hopamp3.Init.UserTrimming = OPAMP_TRIMMING_FACTORY;
if (HAL_OPAMP_Init(&hopamp3) != HAL_OK)
{
Error_Handler();
}
hadc2.Instance = ADC2;
hadc2.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
hadc2.Init.Resolution = ADC_RESOLUTION_12B;
hadc2.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc2.Init.GainCompensation = 0;
hadc2.Init.ScanConvMode = ADC_SCAN_ENABLE;
hadc2.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
hadc2.Init.LowPowerAutoWait = DISABLE;
hadc2.Init.ContinuousConvMode = ENABLE;
hadc2.Init.NbrOfConversion = 4;
hadc2.Init.DiscontinuousConvMode = DISABLE;
hadc2.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc2.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc2.Init.DMAContinuousRequests = DISABLE;
hadc2.Init.Overrun = ADC_OVR_DATA_PRESERVED;
hadc2.Init.OversamplingMode = DISABLE;
if (HAL_ADC_Init(&hadc2) != HAL_OK)
{
Error_Handler();
}
/** Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_3;
sConfig.Rank = ADC_REGULAR_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(&hadc2, &sConfig) != HAL_OK)
{
Error_Handler();
}
/** Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_4;
sConfig.Rank = ADC_REGULAR_RANK_2;
if (HAL_ADC_ConfigChannel(&hadc2, &sConfig) != HAL_OK)
{
Error_Handler();
}
/** Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_8;
sConfig.Rank = ADC_REGULAR_RANK_3;
if (HAL_ADC_ConfigChannel(&hadc2, &sConfig) != HAL_OK)
{
Error_Handler();
}
HAL_OPAMP_SelfCalibrate(&hopamp3);
HAL_OPAMP_Start(&hopamp3);
HAL_ADCEx_Calibration_Start(&hadc2, ADC_SINGLE_ENDED);
// Retriggered in TIM3 Callback
HAL_ADC_Start_DMA(&hadc2, (uint32_t*)&adc_buffer, 4);
Solved! Go to Solution.
2024-09-16 12:43 AM
Fortunately, I also had a NUCLEO-G474RE flying around. The observed behaviour can be reproduced by shorting PB0 (posiitve input of OPAMP3) to ground. It turns out, that the line
HAL_OPAMP_SelfCalibrate(&hopamp3);
does make a difference. Without the calibration function I get the expected output. When I call the function after the initialization, the output is somewhere between 0.4 and 0.5 Volts. So I think that I might use this function wrong or there is some bug inside. For me, I could live without the calibration function.
For everyone else, I attached the NUCLEO-G474 Project to reproduce the observed behaviour. The form does not allow me to upload the exported *.zip-File from STM32CubeIDE. So I can only provide the main.c and the CubeMX configuration.
2024-09-10 06:10 AM
"The adc reads a voltage of 450 mV."
Is input+ connected to anything?
You can configure external GPIO pin as OPA output (connected to both, GPIO and internal ADC) and verify voltage with voltmeter if there 450mV
2024-09-10 06:34 AM
Thank you for your fast response. OPAMP3_INP is my input signal, which is 0 Volt (measured). Unfortunately my design has no connection to the output pin of OPAMP3. However, I was able to contact the pin directly with a needle. I measured a voltage of 3.6 mV at the output pin of OPAMP3, which is within the range of what is possible.
So the error must be from a wrong configuration of the adc.? What can be the reason?
Best regards.
2024-09-10 07:02 AM
I don't see this part in code:
/** Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_VOPAMP3_ADC2;
sConfig.Rank = ADC_REGULAR_RANK_4;
if (HAL_ADC_ConfigChannel(&hadc2, &sConfig) != HAL_OK)
{
Error_Handler();
}
2024-09-11 10:21 PM
Thank you very much. Now it works as intended, although I do not know, why CubeMX does not Auto-Generate this Code.
For bugfixing, this are the software versions I used:
STM32CubeIDE
Version: 1.13.0
Build: 17399_20230707_0829 (UTC)
STM32CubeMX
Version: 6.11.1
MCSDK_v6.3.0-Full
2024-09-11 10:35 PM
Ok. I only thought that this was the solution...
I only measured a smaller voltage for one time. Now, I am still getting the 0.45 V.
2024-09-12 07:10 AM
Common practice to throubsleshoot issues with HAL driver, if it's doesn't generate code properly:
get RM0444, inspect content of the ADC registers. I'd start from ADC_CHSELR, see what channels are really activated in sequence. Same apply to OPA, see what bits are set and if it's correct configuration
2024-09-13 07:46 AM
Now I had a closer look in the registers. For my device the RM0440 is the right one. My ADC does not have an ADC_CHSELR Register.
I wonder a little bit what the second switch in figure 85 is about. As far as I understand, the channel select bits only control the switches on the right side in the following picture:
I wasn't able to find further information on this. According to table 201, the adc input is connected internally to the OPAMP3_VOUT when OPAINTOEN bit is set. Do you think that this might be the second switch? However, the bit is set in OPAMP3_CSR. So everything seems fine here.
I also read Chapter 11 about the periphals interconnect matrix. But unfortunately I did not find anything here (Some connections are disabled in low power modes, which I do not use).
2024-09-13 10:45 AM
I think so, swith is likely the same as shown in this picture:
I don't have G431, but I tested nucleo-G474re, and all perfectly works.
Setings:
/*OPA-3
0x12980145 0x12880000 0x11880000 0x12900000
*/
/*ADC-2
0x0000000B 0x00000010 0x10000005 0x80003003 0x00000000 0x07007E00 0x07000000 0x00000000
0x0FFF0000 0x00FF0000 0x00FF0000 0x00000000 0x122040C3 0x00000000 0x00000000 0x00000000
0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000
0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000
0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000
0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000037 0x00000000 0x00000000
ADC_SQR1:
Reg: 50000130 0001 0010 0010 0000 0100 0000 1100 0011 (0x122040C3)
ADC_SMPR1:
Reg: 50000114 0000 0111 0000 0000 0111 1110 0000 0000 (0x07007E00)
*/
Results with all 4 inputs in parallel with small DC voltage applyed:
PGA: 2
*0 113 116 117 228
PGA: 4
*0 116 104 113 455
PGA: 8
*0 119 111 119 908
PGA: 16
*0 116 115 113 1827
PGA: 32
*0 118 113 116 3646
PGA: 64
*0 115 104 117 4095
Here is the soft, generated by CubeMX but re-configured to run in arduino IDE:
void setup()
{
Serial.begin(115200);
in_String.reserve(200);
Serial.print(F("\n\n\tSketch: g474_pga32. Date: 13 sept. 2024"));
// SystemClock_Config();
Serial.print(F("\n\tSys_clock: "));
Serial.print(((uint32_t) SystemCoreClock /(float) 1000000.0), 3);
Serial.print(F(" MHz."));
Serial.print(F("\n\tcfg_Pins..."));
delay(100);
cfg_Pins1();
Serial.print(F("\tdone."));
Serial.print(F("\n\tDMA..."));
delay(100);
hadc2.Instance = ADC2;
my_DMA_Init( &hadc2 );
Serial.print(F("\tdone."));
Serial.print(F("\n\tADC..."));
delay(100);
my_ADC2_Init();
Serial.print(F("\tdone."));
Serial.print(F("\n\topa3_config..."));
delay(100);
OPA_Config3();
pga_adjust(pga_input);
Serial.print(F("\tdone."));
Serial.print("\n\tADC_start_dma.");
delay(100);
if (HAL_ADC_Start_DMA(&hadc2, (uint32_t *)inp_1, (2 * INP_BUFF)) != HAL_OK) {
Error_Handler();
Serial.print("\n\tError-start-dma.");
}
else
Serial.print("\tDone.");
Serial.print(F("\n\tSetup complete."));
}
void cfg_Pins1(void)
{
GPIO_InitTypeDef GPIO_InitStruct = { 0, 0, 0, 0, 0};
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
/**ADC2 GPIO Configuration
PC2 ------> ADC2_IN8
PA6 ------> ADC2_IN3
PA7 ------> ADC2_IN4
*/
GPIO_InitStruct.Pin = GPIO_PIN_2;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/**OPAMP3 GPIO Configuration
PB13 ------> OPAMP3_VINP
*/
GPIO_InitStruct.Pin = GPIO_PIN_13;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
//DEBUG
GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12;
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);
}
static void OPA_Config3( void )
{
hopamp3.Instance = OPAMP3;
// hopamp3.Init.PowerMode = OPAMP_POWERMODE_HIGHSPEED;
hopamp3.Init.PowerMode = OPAMP_POWERMODE_NORMALSPEED;
hopamp3.Init.Mode = OPAMP_PGA_MODE;
hopamp3.Init.NonInvertingInput = OPAMP_NONINVERTINGINPUT_IO1;
hopamp3.Init.InternalOutput = ENABLE;//DISABLE;
hopamp3.Init.TimerControlledMuxmode = OPAMP_TIMERCONTROLLEDMUXMODE_DISABLE;
hopamp3.Init.PgaConnect = OPAMP_PGA_CONNECT_INVERTINGINPUT_NO;
// hopamp3.Init.PgaConnect = OPAMP_PGA_CONNECT_INVERTINGINPUT_IO0_BIAS;
hopamp3.Init.PgaGain = OPAMP_PGA_GAIN_32_OR_MINUS_31;
//hopamp3.Init.PgaGain = OPAMP_PGA_GAIN_64_OR_MINUS_63;
hopamp3.Init.UserTrimming = OPAMP_TRIMMING_FACTORY;
if (HAL_OPAMP_Init(&hopamp3) != HAL_OK)
{
Error_Handler();
}
if (HAL_OPAMP_Start(&hopamp3) != HAL_OK)
{
Error_Handler();
}
}
void pga_adjust(uint16_t gain_set)
{
uint32_t reg_set = 0x00000001;
gain_set &= 0x0000007E;
if(gain_set == 2) reg_set = OPAMP_PGA_GAIN_2_OR_MINUS_1;
if(gain_set == 4) reg_set = OPAMP_PGA_GAIN_4_OR_MINUS_3;
if(gain_set == reg_set = OPAMP_PGA_GAIN_8_OR_MINUS_7;
if(gain_set == 16) reg_set = OPAMP_PGA_GAIN_16_OR_MINUS_15;
if(gain_set == 32) reg_set = OPAMP_PGA_GAIN_32_OR_MINUS_31;
if(gain_set == 64) reg_set = OPAMP_PGA_GAIN_64_OR_MINUS_63;
hopamp3.Init.PgaGain = reg_set;
if(HAL_OK != HAL_OPAMP_Init(&hopamp3)) {
Error_Handler();
}
}
void my_DMA_Init(ADC_HandleTypeDef *hadc)
{
RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC12;
PeriphClkInit.Adc12ClockSelection = RCC_ADC12CLKSOURCE_SYSCLK;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}
__HAL_RCC_ADC12_CLK_ENABLE();
__HAL_RCC_DMAMUX1_CLK_ENABLE();
__HAL_RCC_DMA1_CLK_ENABLE();
/* ADC2 DMA Init */
/* ADC2 Init */
hdma_adc2.Instance = DMA1_Channel1;
hdma_adc2.Init.Request = DMA_REQUEST_ADC2;
hdma_adc2.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_adc2.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_adc2.Init.MemInc = DMA_MINC_ENABLE;
hdma_adc2.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_adc2.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdma_adc2.Init.Mode = DMA_CIRCULAR;
hdma_adc2.Init.Priority = DMA_PRIORITY_VERY_HIGH;
if (HAL_DMA_Init(&hdma_adc2) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA( hadc, DMA_Handle, hdma_adc2);
HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);
}
void my_ADC2_Init(void)
{
ADC_ChannelConfTypeDef sConfig = {0};
hadc2.Instance = ADC2;
hadc2.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
hadc2.Init.Resolution = ADC_RESOLUTION_12B;
hadc2.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc2.Init.GainCompensation = 0;
// hadc2.Init.ScanConvMode = ADC_SCAN_DISABLE;
hadc2.Init.ScanConvMode = ADC_SCAN_ENABLE;
hadc2.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
hadc2.Init.LowPowerAutoWait = DISABLE;
hadc2.Init.ContinuousConvMode = ENABLE;
// hadc2.Init.ContinuousConvMode = DISABLE;
hadc2.Init.NbrOfConversion = 4; //1;
hadc2.Init.DiscontinuousConvMode = DISABLE;
hadc2.Init.ExternalTrigConv = ADC_SOFTWARE_START;
// hadc2.Init.ExternalTrigConv = ADC_EXTERNALTRIG_T3_TRGO;
// hadc2.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING; //ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc2.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; //ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc2.Init.DMAContinuousRequests = ENABLE;
hadc2.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
// hadc2.Init.OversamplingMode = ENABLE;
hadc2.Init.OversamplingMode = DISABLE;
/*
hadc2.Init.Oversampling.Ratio = ADC_OVERSAMPLING_RATIO_256;
hadc2.Init.Oversampling.RightBitShift = ADC_RIGHTBITSHIFT_4;
hadc2.Init.Oversampling.TriggeredMode = ADC_TRIGGEREDMODE_SINGLE_TRIGGER;
hadc2.Init.Oversampling.OversamplingStopReset = ADC_REGOVERSAMPLING_CONTINUED_MODE;
*/
if (HAL_ADC_Init(&hadc2) != HAL_OK)
{
Error_Handler();
}
sConfig.Channel = ADC_CHANNEL_3;
sConfig.Rank = ADC_REGULAR_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(&hadc2, &sConfig) != HAL_OK)
{
Error_Handler();
}
sConfig.Channel = ADC_CHANNEL_4;
sConfig.Rank = ADC_REGULAR_RANK_2;
if (HAL_ADC_ConfigChannel(&hadc2, &sConfig) != HAL_OK)
{
Error_Handler();
}
sConfig.Channel = ADC_CHANNEL_8;
sConfig.Rank = ADC_REGULAR_RANK_3;
if (HAL_ADC_ConfigChannel(&hadc2, &sConfig) != HAL_OK)
{
Error_Handler();
}
sConfig.Channel = ADC_CHANNEL_VOPAMP3_ADC2;
sConfig.Rank = ADC_REGULAR_RANK_4;
if (HAL_ADC_ConfigChannel(&hadc2, &sConfig) != HAL_OK)
{
Error_Handler();
}
//if (HAL_ADCEx_Calibration_Start(&hadc2, ADC_DIFFERENTIAL_ENDED) != HAL_OK)
if (HAL_ADCEx_Calibration_Start(&hadc2, ADC_SINGLE_ENDED) != HAL_OK)
{
Error_Handler();
}
}
2024-09-16 12:43 AM
Fortunately, I also had a NUCLEO-G474RE flying around. The observed behaviour can be reproduced by shorting PB0 (posiitve input of OPAMP3) to ground. It turns out, that the line
HAL_OPAMP_SelfCalibrate(&hopamp3);
does make a difference. Without the calibration function I get the expected output. When I call the function after the initialization, the output is somewhere between 0.4 and 0.5 Volts. So I think that I might use this function wrong or there is some bug inside. For me, I could live without the calibration function.
For everyone else, I attached the NUCLEO-G474 Project to reproduce the observed behaviour. The form does not allow me to upload the exported *.zip-File from STM32CubeIDE. So I can only provide the main.c and the CubeMX configuration.