При работе с микроконтроллером порой возникает ситуация, когда необходимо измерить напряжение питания. Например, это может понадобиться при анализе процессов заряда и разряда аккумулятора, который питает всю схему. Либо, если мы хотим скорректировать наши измерения АЦП, то тут не обойтись без знания точного значения питающего напряжения.
И, казалось бы, что может быть проще - берем один канал АЦП и спокойно замеряем. Но нельзя упускать из виду один ключевой момент. И заключается он в том, что в качестве опорного используется чаще всего именно напряжение питания. Конечно, можно использовать выводы V_{REF+} и V_{REF-} для того, чтобы подавать опорное напряжение на микроконтроллер отдельно. Но эти выводы есть далеко не у всех контроллеров, тем более если говорить о экземплярах в корпусах с небольшим количеством выводов.
А к чему это все приведет при измерении напряжения питания? А к тому, что на выходе АЦП мы всегда будем иметь одно и то же значение, равное 0xFFF (максимальное значение для 12-ти битного аналого-цифрового преобразователя), поскольку сигнал на входе АЦП будет равен опорному напряжению.
К счастью, эта ситуация далеко не безвыходная. В микроконтроллерах от ST есть свой собственный внутренний источник опорного напряжения. Его величина составляет примерно 1.2 В. К примеру, для микроконтроллера STM32F103C8T6:
Кроме того, для некоторых контроллеров это значения измерено на заводе и сохранено в специальный регистр, из которого его можно прочитать. Возьмем, к примеру, STM32F030x4/x6/x8/xC. Для них это калибровочное значение находится в регистре VREFINT_CAL:
Аналогично, для STM32F303:
Наш же сегодняшний герой - STM32F103C8, как и в целом контроллеры семейства STM32F10x, этих данных не хранит. Поэтому мы будем использовать стандартное значение, указанное в даташите и равное 1.2 В. Также и контроллеры STM32F4x этой опции не имеют в отличие от F0 и F3. В общем, при использовании конкретного микроконтроллера информацию об этом регистре можно найти в документации. Только не в reference manual на семейство, а именно в даташите на выбранный контроллер.
Итак, с этим все понятно. Давайте разберемся, как нам поможет это внутреннее напряжение в решении нашей задачи. Идея заключается в следующем...
Напряжение V_{REFINT} с этого источника опорного напряжения заведено аппаратно на один из каналов АЦП, а именно на 17-й канал ADC1. То есть мы можем в любой момент его измерить. И по полученному значению определить, чему равно в данным момент напряжение питания. Зависимость будет выглядеть следующим образом:
V_{пит} = \frac{4095}{{ADC}_{изм}} * 1.2 В
Здесь 4095 - это максимальное значение АЦП. Эта формула вытекает из пропорции:
\begin{matrix} 1.2 В & \implies & {ADC}_{изм} \\ V_{пит} & \implies & 4095 \end{matrix}
Конечно же, нельзя обойтись без реального практического примера, чем и займемся. Будем опрашивать этот канал АЦП (ADC1_IN17) и анализировать измеренное значение. Для этого включаем ADC1 и нужный канал:
Кроме того, сделаем этот процесс максимально автономным. И тут, естественно, нам поможет DMA, настраиваем на постоянный опрос:
В программе нам нужно будет один раз запустить измерение и в дальнейшем 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; uint16_t adcData = 0; /* USER CODE END PV */
В adcData
будем сохранять результат аналого-цифрового преобразования, а в переменной mcuVoltage
уже значение напряжения питания в Вольтах. Запускаем АЦП, добавив вызов функции калибровки:
/* USER CODE BEGIN 2 */ HAL_ADCEx_Calibration_Start(&hadc1); HAL_ADC_Start_DMA(&hadc1, (uint32_t*)&adcData, 1); /* USER CODE END 2 */
А в main()
в цикле while(1)
рассчитываем требуемое значение:
mcuVoltage = ADC_MAX * ADC_REFERENCE_VOLTAGE / adcData;
Запускаем отладчик и проверяем полученное значение:
Все соответствует действительности 👍
Вот так довольно просто и изящно можно получить точное значение напряжения питания, которое позволит скорректировать любые преобразования с других каналов АЦП. В следующей статье, пока свежи воспоминания, разберем еще один практический пример как раз с измерением напряжения аккумулятора.
Полный проект - MT_SupplyVoltageMeasurement.