Иногда возникает необходимость контроля напряжения питания в автономном устройстве на микроконтроллере, то есть измерение напряжения на аккумуляторе или батарейке, в процессе длительной работы устройства. Поначалу может показаться, что эта несложная задача, но не все так просто. Можно подключить к линии питания делитель из двух резисторов, и в точке их соединения измерять напряжение с помощью АЦП, далее с учетом коэффициента делителя и величины опорного напряжения рассчитать реальное напряжение. Этот вариант подошел бы для устройства с внешним питанием, но в автономном устройстве резистивный делитель будет постоянно потреблять энергию.
Максимальное сопротивление аналогового источника на входе АЦП микроконтроллеров PIC16 не должно превышать 10 кОм, отсюда при напряжении питания 4В получим ток через резистивный делитель 400 мкА, для сравнения, микроконтроллер в спящем режиме потребляет всего 1 мкА. Для автономного устройства это лишний расход энергии, поэтому такой метод измерения напряжения не подойдет.
Существует другой способ решения этой задачи, это измерение напряжения на источнике стабильного напряжения с известной величиной, а в качестве источника опорного напряжения для АЦП использовать напряжение питания. В таком варианте напряжение на линии питания можно рассчитать по следующей формуле:
Uпит = (Uоп*255) / Xацп
где Uоп – величина напряжения на стабильном источнике, Xацп – результат измерения АЦП (число 0-255). Напряжение Uоп можно считать константой, таким образом, чтобы узнать напряжение питания необходимо разделить известную константу на результат преобразования АЦП.
Для реализации этой идеи я использовал микроконтроллер PIC16F676 с встроенным модулем АЦП. Для отображения величины напряжения использовал цифровое табло на драйвере MAX7219. Схема подключения приведена ниже:
В качестве источника стабильного напряжения используется стабилизатор напряжения на регулируемом стабилитроне TL431, который обладает достаточно высокой стабильностью. Стабилизатор управляется от линии RA2 микроконтроллера, резистор R1 ограничивает ток через стабилитрон, конденсатор C3 сглаживающий. Стабилизация напряжения сохраняется при изменении тока в широком диапазоне от 1 до 100 мА. Стабилизатор настроен на минимальное напряжение в 2,5В, эта величина опорного напряжения стабилитрона TL431. Напряжение измеряется на линии RA0 микроконтроллера, которая подключена к катоду стабилитрона.
Ниже представлен код программы микроконтроллера:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 |
#include <P16F676.INC> LIST p=16F676 __CONFIG b'11111110000100' ;Конфигурация микроконтроллера Sec equ 20h ;регистры хранения временных данных для Sec1 equ 21h ;подпрограмм паузы Sec2 equ 22h ; varLL equ 24h ;вспомогательные регистры подпрограммы varLH equ 25h ;деления чисел tmpLL equ 26h ; rezLL equ 2Dh ; rezLH equ 2Eh ; shet equ 23h ;вспомогательные регистры подпрограммы bcd1 equ 27h ;преобразования двоичного числа в bcd2 equ 28h ;десятичное по разрядам bcd3 equ 29h ; sotni equ 2Ah ; desiat equ 2Bh ; edin equ 2Ch ; adr_ind equ 30h ;вспомогательные регистры для передачи dat_ind equ 31h ;данных на цифровой индикатор scetbit equ 32h ; #DEFINE acp_on PORTA,2 ;линия включения источника опорного напряжения #DEFINE datai PORTC,2 ;линия входа данных драйвера MAX7219 #DEFINE cs PORTC,1 ;линия выбора драйвера MAX7219 #DEFINE clk PORTC,0 ;линия тактирования драйвера MAX7219 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; org 0000h ;начать выполнение программы с адреса 0000h goto Start ;переход на метку Start ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;Основная программа Start movlw b'00000000' ;Настройка выходных защелок порта A movwf PORTA ; movlw b'00000010' ;Настройка выходных защелок порта С movwf PORTC ; movlw b'00000111' ;выключение компараторов movwf CMCON ; bsf STATUS,RP0 ;выбрать 1-й банк movlw b'11111011' ;настройка линий ввода\вывода порта movwf TRISA ; movlw b'11111000' ;настройка линий ввода\вывода порта movwf TRISC ; movlw b'01010000' ;настройка времени преобразования АЦП (Fosc/16 = 4 мкс) movwf ADCON1 ; movlw b'00000001' ;Настройка аналоговых/цифровых входов movwf ANSEL ;RA0 - аналоговый вход bcf STATUS,RP0 ;выбрать 0-й банк movlw b'00000000' ;Настройка модуля АЦП, канал AN0, левое выравнивание, movwf ADCON0 ;источник опорного напряжения АЦП от линии питания call init_lcd ;вызов подпрограммы инициализации цифрового индикатора Start_1 call battery ;вызов подпрограммы измерения и расчета величины напряжения call vivod ;вызов пожпрограммы вывода значения напряжения на индикаторы call pause ;вызов подпрограммы паузы 2 секунды goto Start_1 ;переход на метку Start_1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; vivod movlw 0x08 ;очистка 8-го индикатора movwf adr_ind ; movlw 0x0F ; movwf dat_ind ; call send ; movlw 0x07 ;очистка 7-го индикатора movwf adr_ind ; movlw 0x0F ; movwf dat_ind ; call send ; movlw 0x06 ;очистка 6-го индикатора movwf adr_ind ; movlw 0x0F ; movwf dat_ind ; call send ; movlw 0x05 ;очистка 5-го индикатора movwf adr_ind ; movlw 0x0F ; movwf dat_ind ; call send ; movlw 0x04 ;очистка 4-го индикатора movwf adr_ind ; movlw 0x0F ; movwf dat_ind ; call send ; movlw 0x03 ;вывод значения регистра sotni на 3-й индикатор movwf adr_ind ;целая часть величины напряжения movf sotni,W ; movwf dat_ind ; bsf dat_ind,7 ;установка десятичного знака call send ; movlw 0x02 ;вывод значения регистра desiat на 2-й индикатор movwf adr_ind ;дробная часть величины напряжения, первая цифра movf desiat,W ;после запятой movwf dat_ind ; call send ; movlw 0x01 ;вывод значения регистра edin на 1-й индикатор movwf adr_ind ;дробная часть величины напряжения, вторая цифра movf edin,W ;после запятой movwf dat_ind ; call send ; return ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; battery bsf acp_on ;включение источника опорного напряжения (TL431) bsf ADCON0,ADON ;включение модуля АЦП call pause ;вызов подпрограммы паузы 2 секунды bsf ADCON0,GO_DONE ;установка бита GO_DONE, запуск преобразования АЦП batt_1 btfsc ADCON0,GO_DONE ;опрос бита GO_DONE goto batt_1 ;бит GO_DONE не равен 0, преобразование не ;закончено, переход на метку batt_1 movf ADRESH,W ;преобразование закончено, копирование значения movwf tmpLL ;в регистр tmpLL bcf acp_on ;выключение источника опорного напряжения (TL431) bcf ADCON0,ADON ;выключение модуля АЦП movlw .7 ;запись числа 62475 в регистры varLH, varLL для movwf varLL ;последующего деления movlw .248 ; movwf varLH ; call del ;вызов подпрограммы деления числа 62475 на результат преобразования АЦП call bin2bcd ;вызов подпрограммы преобразования двоичного числа в десятичное по разрядам return ;выход из подпрограммы ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;Подпрограмма деления двухбайтного числа на однобайтное (varLH, varLL):(tmpLL) ;Результат деления в регистре rezLH, rezLL, деление целочисленное без дробной части ;на ноль делить нельзя, произойдет выход из подпрограммы без изменений del clrf rezLL ;очистка регистров rezLL, rezLH (эквивалентно записи нуля) clrf rezLH ; movlw .0 ;проверка равенства нулю числа лежащего в регистре tmpLL xorwf tmpLL,W ;(на ноль делить нельзя) btfsc STATUS,Z ; return ;число в регистре tmpLL равно нулю: выход из подпрограммы ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; d1 movf tmpLL,W ;число в tmpLL не равно нулю: вычитаем число лежащее в регистре subwf varLL,F ;tmpLL из числа в регистрах varLH, varLL: это операция вычитания btfsc STATUS,C ;однобайтного числа из двухбайтного goto d2 ;при отрицательном результате происходит выход из подпрограммы movlw .1 ;при положительном результате инкрементируем счетчик вычитаний subwf varLH,F ;переходя на метку d2 btfss STATUS,C ; return ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; d2 incfsz rezLL,F ;инкремент rezLL с проверкой на переполнение goto d1 ;нет переполнения rezLL: переход на метку d1 incf rezLH,F ;переполнение rezLL: инкремент регистра rezLH (регистры rezLL, rezLH ;выступают в качестве счетчика вычитания и содержат результат деления) goto d1 ;переход на метку d1 для повторного вычитания, операция деления ;представляет собой многократное вычитание ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; bin2bcd movlw .16 ;Подпрограмма преобразования двоичного числа movwf shet ;в десятичное clrf bcd1 ;Двухбайтное число предварительно загружается clrf bcd2 ;в регисты rezLH, rezLL clrf bcd3 ;Результат преобразования: goto bin2bcd_1 ;единицы в младшем полубайте bcd3 ;десятки в старшем полубайте bcd3 adjdec movlw 0x33 ;сотни в младшем полубайте bcd2 addwf bcd1,F ;тысячи в старшем полубайте bcd2 addwf bcd2,F ;десятки тысяч в младшем полубайте bcd1 addwf bcd3,F ; ; movlw 0x03 ; btfss bcd1,3 ; subwf bcd1,F ; btfss bcd2,3 ; subwf bcd2,F ; btfss bcd3,3 ; subwf bcd3,F ; ; movlw 0x30 ; btfss bcd1,7 ; subwf bcd1,F ; btfss bcd2,7 ; subwf bcd2,F ; btfss bcd3,7 ; subwf bcd3,F ; ; bin2bcd_1 rlf rezLL,F ; rlf rezLH,F ; rlf bcd3,F ; rlf bcd2,F ; rlf bcd1,F ; decfsz shet,F ; goto adjdec ; ; movf bcd3,W ;копирование значения единиц из младшего andlw b'00001111' ;полубайта bcd3 в регистр edin movwf edin ; swapf bcd3,W ;копирование значения десятков из старшего andlw b'00001111' ;полубайта bcd3 в регистр desiat movwf desiat ; movf bcd2,W ;копирование значения сотен из младшего andlw b'00001111' ;полубайта bcd2 в регистр sotni movwf sotni ; return ;выход из подпрограммы ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;Подпрограмма инициализации драйвера(MAX7219) цифрового табло init_lcd call pauslcd ;вызов подпрограммы паузы 2 мс movlw 0x0F ;выключить тестовый режим movwf adr_ind ; movlw 0x00 ; movwf dat_ind ; call send ; movlw 0x0C ;включение индикатора movwf adr_ind ; movlw 0x01 ; movwf dat_ind ; call send ; movlw 0x0A ;интенсивность 15/32 movwf adr_ind ; movlw 0x07 ; movwf dat_ind ; call send ; movlw 0x09 ;использовать BCD Code B для всех индикаторов movwf adr_ind ; movlw 0xFF ; movwf dat_ind ; call send ; movlw 0x0B ;использовать 8 индикаторов movwf adr_ind ; movlw 0x07 ; movwf dat_ind ; call send ; return ;выход из подпрограммы ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;Подпрограмма отправки 2-х байт на драйвер (MAX7219) цифрового табло по пртоколу SPI send bcf cs ;Сбросить линию выбора драйвера CS movlw .8 ;Отправка содержимого адресного байта adr_ind movwf scetbit ; povtor bcf clk ; btfsc adr_ind,7 ; bsf datai ; btfss adr_ind,7 ; bcf datai ; bsf clk ; rlf adr_ind,F ; decfsz scetbit,F ; goto povtor ; movlw .8 ;Отправка содержимого байта данных dat_ind movwf scetbit ; povtr1 bcf clk ; btfsc dat_ind,7 ; bsf datai ; btfss dat_ind,7 ; bcf datai ; bsf clk ; rlf dat_ind,F ; decfsz scetbit,F ; goto povtr1 ; bcf clk ; bsf cs ;установить в 1 линию выбора драйвера CS return ;выход из подпрограммы ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; pauslcd movlw .4 ;подпрограмма пауза 2 мс movwf Sec1 ; p_l2 movlw .166 ; movwf Sec ; p_l1 decfsz Sec,F ; goto p_l1 ; decfsz Sec1,F ; goto p_l2 ; return ;выход из подпрограммы pause movlw .11 ; movwf Sec2 ; p_3 movlw .255 ;подпрограмма пауза 2 сек movwf Sec1 ; p_1 movlw .255 ; movwf Sec ; p_2 decfsz Sec,F ; goto p_2 ; decfsz Sec1,F ; goto p_1 ; decfsz Sec2,F ; goto p_3 ; return ;выход из подпрограммы end ;конец всей программы ; |
Измерение напряжения выполняется каждые 4 секунды, сначала включается стабилизатор, через 2 секунды измеряется напряжение, после чего стабилизатор отключается на 2 секунды. Временные задержки установлены для того чтобы конденсатор C3 успевал зарядиться. Если нет необходимости в экономии энергии, то стабилизатор можно не отключать. Результат преобразования АЦП 10-битной число, но я использую только 8 бит для упрощения арифметических операций.
Константа (Uоп*255) в моем варианте равна 2,49*255=634,95; но я использую значение константы умноженное на 100 (63495), чтобы в итоге при делении получить значение напряжения с двумя знаками после запятой. После вычисления, значение напряжения передается в подпрограмму преобразования двоичного числа в десятичное по разрядам, а далее выводится на цифровой индикатор. Затем все этапы повторяется по циклу.
Я сравнил измеренные значения напряжения с показаниями цифрового мультиметра, в среднем разница составляет ±0,02В, (иногда скачет в пределах ±0,06В ) что весьма неплохо. Таким образом, с помощью данного способа можно измерить напряжение питания микроконтроллера в автономном устройстве, не увеличивая энергопотребления.
Добрый день. А как реализовать такое же устройство, но с питанием от аккумулятора 12в? Идея состоит в том, чтобы видео регистратор в автомобиле оставался работать после выключения зажигания, а мк следил, чтобы акб не разрядился ниже программно установленного уровня. А если это все будет на pic16f628a то совсем замечательно будет
В PIC16F628A нет АЦП, но там можно воспользоваться встроенным компаратором, на один вход компаратора подать напряжение с резистивного делителя подключенного к 12В, а на второй вход тоже резистивный делитель только с переменным резистором, чтобы регулировать напряжение срабатывания, хотя второй вход можно подключить к внутреннему источнику опорного напряжения, и программно регулировать напряжение срабатывания. Сам микроконтроллер придется подключать к 12В через стабилизатор напряжения 5В. Резистивный делитель нужен для снижения напряжения 11-14В до приемлемого уровня 0-5В, чтобы не повредить микроконтроллер.
Если брать микроконтроллер с встроенным АЦП, просто через резистивный делитель подавать напряжение на вход АЦП, и измерять напряжение. Резистивный делитель будет потреблять около 1мА, микроконтроллер примерно также, стандартный стабилизатор напряжения 5мА, итого 7мА.
Здравствуйте. Есть вопрос по измерению. Ситуация следующая. Я собрал простенькую схему измерения напряжения в диапозоне 0 – 50 В на PIC16F676. Источник опорного напряжения – напряжение питания, от стабилизатора 7805. Так же от него питается индикатор. Через резистивный делитель подается измеряемое напряжени на один из входов. При первом измерении выводится одно значение, во втором другое, и в дальнейшем случайным образом выводятся эти два значения, отличаются друг от друга на 0,2 В. Причиной этих колебаний может быть то, что индикатор и контроллер питаются от одного источника?? И еще не можете ли мне подсказать кратко какие бывают причины погрешности измерений.
Из даташита на PIC контроллеры, погрешность АЦП составляет 1/2LSB при соблюдении условия временной задержки перед измерением, расчет задержки можно посмотреть в даташите. Для 10-битного разрешения и Uоп=5В получим погрешность (1/2)*(5В/1024)=2,44 мВ.
Резистивный делитель также добавляет погрешность, возьмем делитель на 10, при изменении напряжения на 5В (например с 30 до 35В) на выходе делителя напряжение изменится всего на 0,5В. Выше рассчитанная погрешность в этом случае увеличится в 10 раз 2,44 мВ*10=24,4 мВ, то есть прямо пропорционально коэффициенту делителя напряжения.
Кроме этого погрешность зависит от стабильности источника опорного напряжения, стабилизатор 7805 в качестве опорного не подойдет для более-менее точных измерений.
В вашем случае я не могу точно сказать что именно влияет на результат, скорее всего один из факторов нестабильность опорного напряжения, возможно само измеряемое напряжение гуляет, а может программный расчет величины напряжения дает ошибки (я же не знаю какой у вас там код расчета).
Привет! Поясни пожалуйста, откуда взялась формула Uпит = (Uоп*255) / Xацп ?
На мой взгляд Uоп надо поделить на 255 и умножить на полученное Xацп, разве нет?
Да, кстати, в комментариях кода вызова подпрограммы деления почему то указано число 62475, а не рассчитанное 63495 – ну я думаю это просто опечатка.
И как рассчитать обвязку схемы TL431, в даташите не нашел такой схемы. Да и потом, вроде везде пишут, что нельзя конденсатор на выходе TL431 ставить – приводит к самовозбуждению. В общем ниче не понятно 🙂
В стандартном варианте для АЦП измеряемое напряжение рассчитывается из пропорции Uизм = (Uоп*Xацп) / 255, но здесь в качестве опорного напряжения используется напряжение питания микроконтроллера: Uоп=Uпит , а измеряем мы стабильный источник напряжения (назовем его Uоп), так что приравниваем Uизм=Uоп. В итоге получим Uоп = (Uпит*Xацп) / 255, отсюда Uпит = (Uоп*255) / Xацп.
Да, в коде опечатка
В даташите на TL431 приведена формула:
правой частью формулы (Iref*R1) можно пренебречь
В том же даташите приводятся графики стабильности в зависимости от емкости конденсатора и тока через TL431, исходя из этого, думаю что конденсаторы можно ставить.
Сразу все стало на свои места, пасибо