Top.Mail.Ru

Мониторинг напряжения аккумулятора на микроконтроллере STM32.

Сегодня, в продолжение предыдущей статьи, мы реализуем еще один похожий проект. А именно решим задачу измерения напряжения аккумулятора, питающего всю схему, на микроконтроллере STM32.

И для начала рассмотрим типичную схему организации питания:

Схема питания микроконтроллера

Здесь напряжение с аккумулятора приходит на вход преобразователя, который обеспечивает на выходе 3.3 В для питания нашего микроконтроллера. С аккумулятора, если мы говорим о наиболее часто использующихся литий-полимерных (Li-Pol), будет приходить 2.8 - 3.7 В в зависимости от уровня заряда. Нижний порог будет зависеть от использующейся схемы защиты аккумулятора от глубокого разряда, обычно эта схема отключает батарею при напряжениях 2.8 - 3 В.

В целом, мы получаем следующее - нам необходимо обеспечить измерение напряжений от 2.8 до 3.7 В. Поскольку питание микроконтроллера - 3.3 В, то мы не можем подать на вход АЦП 3.7 В напрямую. Что же, используем простейший делитель:

Измерение напряжения аккумулятора.

Казалось бы, на этом и все, но нужно учесть еще один нюанс. При использовании самого обычного LDO-преобразователя (в данном случае MIC5504) на его выходе будет напряжение:

V_{OUT} = \begin{cases}
   3.3 \medspace В &\text{если } V_{IN} \medspace - \medspace V_{DO} \geqslant 3.3 \medspace В \\
   V_{IN} \medspace - \medspace V_{DO} &\text{если } V_{IN} \medspace - \medspace V_{DO} \lt 3.3 \medspace В
\end{cases}

Здесь V_{DO} - это напряжение, падающее непосредственно на самом преобразователе. Для нашей микросхемы:

Dropout voltage.

То есть если на входе у нас достаточный уровень сигнала (V_{IN} \medspace - \medspace V_{DO} \geqslant 3.3 \medspace В), то микросхема обеспечит нам железные 3.3 В на выходе, что нас полностью устраивает.

Но по мере разряда аккумулятора напряжение будет естественным образом падать, что приведет к тому, что на выходе MIC5504 уже не будет этих 3.3 В. Уровень будет ниже. И это, как мы обсуждали в предыдущей статье, приведет к тому, что измеренное напряжение (после делителя) уже не будет верным.

Вот по этой причине нам и нужно предпринять дополнительные действия, чтобы обеспечить корректность измерений. А действия эти заключаются в том, что мы снова используем внутренний источник опорного напряжения STM32, который аппаратно заведен на 17-й канал ADC1.

Алгоритм действий будет таким:

  • Определяем реальный уровень напряжения питания микроконтроллера по формуле:
 V_{пит} = \frac{4095}{{ADC}_{изм \medspace пит}} * 1.2 В
  • И, следующим шагом, зная точное значение V_{пит}, ничего нам не мешает рассчитать верное значение напряжения аккумулятора:
 V_{акк} = 2 * \frac{{ADC}_{изм \medspace акк}}{4095} \medspace * \medspace V_{пит}

Здесь мы умножаем на 2 потому что на вход АЦП сигнал подается через делитель 1:2.

Итак, разобрали теорию и план действий, переходим к практической реализации. Подключим напряжение с делителя на 1-й канал ADC1 (PA1).

Переходим в STM32CubeMx и настраиваем нужные каналы ADC, а также активируем DMA. АЦП будет постоянно опрашивать входные каналы, а результат будет сохраняться в буфер при помощи ДМА:

STM32CubeMx ADC.
STM32CubeMx DMA.

После всех настроек генерируем и открываем проект. Объявляем все, что нам понадобится:

/* USER CODE BEGIN PD */
#define ADC_REFERENCE_VOLTAGE                                           1.2
#define ADC_MAX                                                         0xFFF

/* USER CODE END PD */

/* USER CODE BEGIN PV */
float mcuVoltage = 0;
float batteryVoltage = 0;
uint16_t adcData[2];

/* USER CODE END PV */

После инициализации всей периферии запускаем АЦП:

/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_ADC1_Init();
/* USER CODE BEGIN 2 */
HAL_ADCEx_Calibration_Start(&hadc1);
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adcData, 2);

/* USER CODE END 2 */

После чего в цикле while(1) нам остается только производить расчеты:

/* Infinite loop *//* USER CODE BEGIN WHILE */
while (1)
{
	/* USER CODE END WHILE */

	/* USER CODE BEGIN 3 */
	mcuVoltage = ADC_MAX * ADC_REFERENCE_VOLTAGE / adcData[0];
	batteryVoltage = 2 * adcData[1] * mcuVoltage / ADC_MAX;
}
/* USER CODE END 3 */

Теперь в переменных mcuVoltage и batteryVoltage у нас будут соответственно значения напряжений питания и аккумулятора. Наша цель достигнута, и на этом на сегодня все, спасибо за внимание и до скорых встреч!

Ссылка на полный проект - MT_BatteryMeasurement.

Подписаться
Уведомить о
guest

10 комментариев
Старые
Новые
Межтекстовые Отзывы
Посмотреть все комментарии
Павел
Павел
3 лет назад

Спасибо.

Алексей
Алексей
3 лет назад

Спасибо! Полезная информация. Применил на практике))

Дмитрий
Дмитрий
3 лет назад

Через делитель постоянно течёт ток и сажает батарейку? в случае небольших емкостей может быть неприятностью

Дмитрий
Дмитрий
Ответ на комментарий  Aveal
3 лет назад

Допустим требования жесткие да. Была задача разработки ПДУ с ограниченным размером, предполагалось что зарядка раз в один-два месяца...

вы имеете ввиду допустим n-канальный полевой ключ для подключения R2 на землю? тогда при отключении ключа все напряжение батареи в 4.2 вольта пойдет на вход, судя по даташиту на f103 допустим входы АЦП не толерантны к 5В. Если только если p - канал перед R1, закрыться впринципе должен от 3.3в. Если не верно рассуждаю поправьте)

Дмитрий
Дмитрий
Ответ на комментарий  Aveal
3 лет назад

Спасибо , тоже думал об этом , только заместо биполяра тот же n канальный полевик тоже норм )

Сергей
Сергей
2 лет назад

Вопрос для повышения уровня образованности: А для чего конденсатор ?

10
0
Оставьте комментарий! Напишите, что думаете по поводу статьи.x