Measuring VBAT on stm32wb55cgu with ADC
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2024-06-08 08:20 AM - edited ‎2024-06-08 08:38 AM
Hi,
i am currently trying to read out VBAT of a STM32WB55CGU6 Board (Custom).
VBAT is connected to a 3V Battery, and all other VDD pins are connected to the same battery since the board is only powered by this battery.
I have setup ADC1 and enabled the VBatChannel (at least in CubeMX).
My Question is how to read out the ADC Value of VBAT?
So far i have tried the following:
static 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 */
/** 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_SOFTWARE_START;
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc1.Init.DMAContinuousRequests = DISABLE;
hadc1.Init.Overrun = ADC_OVR_DATA_PRESERVED;
hadc1.Init.OversamplingMode = DISABLE;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
Error_Handler();
}
/** Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_VBAT;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_2CYCLES_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 */
}
AND:
MX_GPIO_Init();
MX_ADC1_Init();
MX_I2C1_Init();
MX_RTC_Init();
MX_RF_Init();
/* USER CODE BEGIN 2 */
HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED);
/* USER CODE END 2 */
/* Init code for STM32_WPAN */
MX_APPE_Init();
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
// ADC starten und messen
HAL_ADC_Start(&hadc1);
if (HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY) == HAL_OK) {
uint32_t adcValue = HAL_ADC_GetValue(&hadc1);
float voltage = (adcValue / 4095.0) * 3.0;
float percentage = (voltage / 3.0) * 100;
printf("%.2f", percentage);
/* USER CODE BEGIN 3 */
}
}
But it seems like this is wrong. At least i get values like 33% battery power at 3V VBat.
The raw adcValue is around 1165.
I have read that it is may needed to explicit enable VBAT with the flag VBATEN?
Where would i need to do it? Do you have an example line how this flag is set?
Thank you
My CubeMX ADC Config
Clock configs:
Solved! Go to Solution.
- Labels:
-
STM32WB series
Accepted Solutions
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2024-06-09 01:17 AM
>what hinders you to copy the code snippet above, add one or two lines and then the topic is through?
Perhaps your way of being grateful for offered help could influence my motivation.
>most likely a different platform is more sensible for that
Ask the Ai , it needs no motivation, so cannot loose motivation.
So this is your program ?
vbat = readadc(bla) * ?? * vref+?
If "bla" is the refint channel you set with Cube , then :
HAL_ADC_Start(&hadc1);
while ((HAL_ADC_GetState(&hadc1), HAL_ADC_STATE_READY) == 0);
// adc value = vrefint (1.212 V)
vbat = 1.212f * 4096 / (float) HAL_ADC_GetValue(&hadc1) ;
To get good/best precision, you have to adjust the "1.21" to 1.1x.. 1.3x , to match the real vbat you see on a DMM .
Note: It is recommended to perform a calibration after each power-up. (adc_ex_calibration_start(..))
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2024-06-08 10:14 AM - edited ‎2024-06-08 10:23 AM
Hi,
> At least i get values like 33% battery power at 3V VBat.
see rm : ADC->VBAT
So 1/3 vbat is perfect.. :)
BUT...you write:
VBAT is connected to a 3V Battery, and all other VDD pins are connected to the same battery since the board is only powered by this battery.
So also VREF+ to the ADC ? Then you always just check the 1/3 divider !
VREF+ has to be at a fix reference level, to check the supply or bat voltage, otherwise the adc-full-reference scales with the applied voltage and you can get no useful info.
So if leaving VREF+ at VBAT , measure the (fixed) internal voltage and calculate Vbat and Vref from this:
read in rm...
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2024-06-08 11:49 AM - edited ‎2024-06-08 11:53 AM
My MCU seems to not have a dedicated VREF+ pin. I am using the 48 pin package where this seems to be not present. Only in the bigger packages i can see a VREF+ pin.
I didn't see the formula about VREF+ though, but if seeing this reading the manual does not bring me anything since i still don't know how to translate this into "code".
Where to get VREFINT_CAL and VREFINT_DATA from?
And what is VREF+_Charac ?
There is no example in the wide world of STM of how to do this. At least i couldn't find any
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2024-06-08 12:27 PM
>Where to get VREFINT_CAL and VREFINT_DATA from?
>And what is VREF+_Charac ?
>There is no example in the wide world of STM of how to do this. At least i couldn't find any
Just - did you look at the rm at all ?
The next lines after the Fig.91 (i showed) should tell it
Again : read in rm...
next: see also register descriptions:
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2024-06-08 01:02 PM
So, again -> how to translate this now into code?
ADC1_COMMON->CCR |= ADC_CCR_VBATEN;
uint32_t calfact_register_value = ADC1->CALFACT;
uint32_t calfact_s_value = calfact_register_value & 0x7F;
uint16_t ts_cal1 = *(uint16_t*)0x1FFF75AA; // ???
vref+ = ?? * ts_cal1 / calfact_s_value
// maybe i now have vref+?
// now how should i use it to calculate vbat?
vbat = readadc(bla) * ?? * vref+?
Is there any documentation / tutorial on the code side of things?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2024-06-08 02:36 PM - edited ‎2024-06-08 02:46 PM
>So, again -> how to translate this now into code?
Read rm + ds , formula also there , i showed you .
You have the mini-case, so vref+ is Vdda -- you have vbat there.
+
> Is there any documentation / tutorial on the code side of things?
You see the tutorials on STM WB55 ? + examples like
https://github.com/STMicroelectronics/p-nucleo-wb55-nucleo-bsp
If you need help, to write the simple calculation :
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2024-06-08 03:23 PM
Ok. I give up :)
If it is such an easy formula then for such an highly skilled hardware dev like you it should be easy to guide me through the code side of the things.
But instead you just hear: rm + ds. See some examples here and there (none of them contains what i need).
Ya i read it. But still there is no line of code showing HOW (not the calculations, BUT getting/collecting the data togehter). How i can in code retrieve VBAT when the hole system is powered by just this battery.
If it is so easy, what hinders you to copy the code snippet above, add one or two lines and then the topic is through?
Actually this would be benefical for me since i then can learn this better and also understand it better.
But I think this forum is not for people who are just starting with this stuff which is quite sad, most likely a different platform is more sensible for that - but still didn't find one.
Thanks and have a nice day. :crossed_fingers:
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2024-06-09 01:17 AM
>what hinders you to copy the code snippet above, add one or two lines and then the topic is through?
Perhaps your way of being grateful for offered help could influence my motivation.
>most likely a different platform is more sensible for that
Ask the Ai , it needs no motivation, so cannot loose motivation.
So this is your program ?
vbat = readadc(bla) * ?? * vref+?
If "bla" is the refint channel you set with Cube , then :
HAL_ADC_Start(&hadc1);
while ((HAL_ADC_GetState(&hadc1), HAL_ADC_STATE_READY) == 0);
// adc value = vrefint (1.212 V)
vbat = 1.212f * 4096 / (float) HAL_ADC_GetValue(&hadc1) ;
To get good/best precision, you have to adjust the "1.21" to 1.1x.. 1.3x , to match the real vbat you see on a DMM .
Note: It is recommended to perform a calibration after each power-up. (adc_ex_calibration_start(..))
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2024-06-09 01:46 AM
Thank you for this hint with the code!
This actually lets the whole thing appear much simpler now.
I thought i would need to somehow "enable" VREFINT in some registers first to tell the ADC to actually use it and also first fetch from somewhere the other data you mentioned before.
Some notes in the RM and DS tell you: Pins CAN be connected internally, Some say you need to SET VBATEN first, others say you need to SET a Flag CHSEL_18 etc.
This turns out to be a bit confusing specially when there is no obvious way when / where to set those and also if they are really necessary or not.
AI is nice, AI is great -> but would i actually trust it? I dunno. I asked it already a couple of times but i can not take this as a proof, if this is correct. At least it was not able after tons of conversations to put me into this direction like you did now!
Anyway thank you for helping :clapping_hands: