Как измерить Arduino mini напряжение своего питания, меньшее 3,3V?
- Войдите на сайт для отправки комментариев
Спроектирован девайс с автономным питанием от лития. Реально напряжение находится в диапазоне от 4,2В до 2,9В (отключение встроенным контроллером). Батарея через делитель 50к/50к заведена на аналоговый пин. Проблема в том, что напряжение аккумулятора RAW при измерении сравнивется с VCC, но это имеет смысл только пока последнее стабилизированное. При снижении напряжения аккумулятора ниже 3,3 стабилизации на VCC нет.
Слышал, что можно подавать опорное напряжение, например, 2В, на аналоговый пин и сравнивать питание с ним. Можно ли так сделать, и означает ли это, что все аналоговые входы будут брать это напряжение как опорное?
И еще вопрос не совсем в тему: надо измерять еще и напряжение батареи инвертора, которая не имеет общей земли с девайсом. Есть ли простые решения, или надо городить ШИМ?
http://arduino.ru/Reference/AnalogReference
По второму вопросу, простого пути не знаю(((
Непонятно. Там стоит стабилизатор на 3.3? Он обязателен только для требовательных к диапазону напряжений потребителей, таких как nRF24L01+, если таковой есть. Питание pro mini может быть на частоте до 8 МГц от 2.7 до 5.5 В.
Но не на аналоговый пин а на AVCC.
Да.
Да.
Можно поставить туда вторую pro mini и передавать без проводов, например ИК светодиодом TSAL6200 и принимать ИК приёмником TSOP1838.
Можно отвязать аналоговый выход трансформатором, понадобится 555й таймер и операционный усилитель, но зависимость этой вещи далека от линейной, может надоесть отлаживать.
Можно просто питать устройство от батареи инвертора, но судя по задаче, это невозможно.
http://arduino.ru/Reference/AnalogReference
Спасибо, будем исползовать внутреннее опорное
2std
У меня контроль питания отключен фьюзами, но дело не в этом. Для измерения напряжения нужно стабилизированное опорное напряжение. По умолчанию оно берется с VCC. Но когда питание ниже VCC, т.е. внутреннего стабилизатора 3,3В, стабилизации нет. Нет и измерения.
На мини нет входа AVCC.
Какие ограничения на выбор номиналов делителя при измерении напряжения? Мне бы суммарное получить 150-200кОм, чтобы ток поменьше. Что-нибудь 150к/50к, делитель 4:1. Как раз максимальное при зарядке 4,2В поделится до внутреннего опорного 1,1В
Какие ограничения на выбор номиналов делителя при измерении напряжения? Мне бы суммарное получить 150-200кОм, чтобы ток поменьше. Что-нибудь 150к/50к, делитель 4:1. Как раз максимальное при зарядке 4,2В поделится до внутреннего опорного 1,1В
Ну так попробуйте или даташит курите, какие там минимальные токи.
Покурил форум, ничего не нашел, кроме того, что входное сопротивление измеряется гигаОмами. Попробовал. Не получилось. Дает одинаковое значение при разных напряжениях питания. При напряжении 3,79В и делителе 150к/50к дает 975, и то же самое при просадке питания до 3,69В.
Может, это связано с тем, что мини 5-вольтовая? Фьюзами отключен контроль напряжения и заодно пропала стабилизация внутреннего референса?
Или может, я не туда ставлю указание референса? Это не мой код, я увы программер совсем никакой, только начал осваивать. Сейчас поставил здесь:
void setup()
{
wdt_disable(); // бесполезная строка до которой не доходит выполнение при bootloop
Serial.begin(SerialSpeed);
if(e){Serial.println("start");}
analogReference(INTERNAL);
pin_init;
radio_init();
Самое простое, возьмите батарейку на 1,5в через потенциометр подключите на измерительный вход и посмотрите, что получается. 10мВ это весьма малая величина, чтобы понять работает или нет.
SergAG, у вас какой-то сумбур. Что, куда включено, зачем делитель? Тут всё просто, если вы хотите питать от лития, то вам вход raw вообще не нужен. Подключаете аккум прямо к Vcc. Напряжение питания Vcc можно измерить внутренней коммутацией, не используя внешние аналоговые входы. Вот рабочий счетч, с оверсемплингом до 14 бит:
01
void
setup
(){
02
Serial
.begin(9600);
03
}
04
05
void
loop
() {
06
float
Vop= (
float
)(1.1*16368)/Vbg() ;
07
Serial
.println (Vop,3);
//
08
}
09
10
11
int
Vbg() {
12
ADMUX = (1<<REFS0)|(0<<REFS1)|(1<<MUX3)|(1<<MUX2)|(1<<MUX1)|(0<<MUX0);
13
long
buffersamp=0;
14
for
(
int
n=0x0; n<=0xff; n++ ) {
15
ADCSRA |= (1<<ADSC)|(1<<ADEN);
//Starts a new conversion
16
while
(bit_is_set(ADCSRA,ADSC));
17
buffersamp += ADC; }
18
buffersamp >>=4;
//16368 full scale 14bit
19
ADCSRA &= ~(1 << ADEN);
// отключаем АЦП
20
return
buffersamp;
21
}
Dimax, преклоняюсь. Это уже другой уровень.
10мВ это весьма малая величина, чтобы понять работает или нет.
Почему 10мВ? 3,79/4=0,95В
2dimax, очень интересно, буду разбираться, куда что можно воткнуть
Ругается на строчку
ADMUX = (1«REFS0)|(0«REFS1)|(1«MUX3)|(1«MUX2)|(1«MUX1)|(0«MUX0);
Прошу прощения, не знаю, как в спойлер закатать:
This report would have more information with
"Show verbose output during compilation"
enabled in File > Preferences.
Arduino: 1.0.6 (Windows 7), Board: "[Optiboot] Arduino Pro or Pro Mini (5V, 16 MHz) w/ ATmega328"
Privod_150226:745: error: stray '\' in program
Privod_150226:745: error: stray '\' in program
Privod_150226:745: error: stray '\' in program
Privod_150226:745: error: stray '\' in program
Privod_150226:745: error: stray '\' in program
Privod_150226:745: error: stray '\' in program
Privod_150226:748: error: stray '\' in program
Privod_150226:751: error: stray '\' in program
Privod_150226:752: error: stray '\' in program
Privod_150226.ino: In function 'int Vbg()':
Privod_150226:745: error: expected `)' before 'u00abREFS0'
Privod_150226:748: error: expected `)' before 'u00abADSC'
Privod_150226:751: error: expected `;' before 'u00bb'
Privod_150226:752: error: expected `)' before 'u00ab'
у меня скомпилировалось и для нано и для меги8. хотя в меге 8 вроде надо будет поправить. там опорник вроде другой
а так да. это другой уровень. без поллитру не разберешься
хотя если каждый день хоть по 10-15мин перед сном читать даташит, через полгодика не хуже наверно можно будет написать
SergAG, думаю вы с ошибками скопировали. Обратите внимания в правом верхнем углу скетча есть вспывающая колонка с функциями работы (Код, скопировать, печать)
2dimax, аж мурашки по коже. Вы что, ясновидящий? Так действительно скомпилировалось, хотя визуально отличий я не увидел.
Правда, пока это достижение не привело к результату. На мониторе в месте, где напряжение, выводится другая цифирь, 4640, что это означает - не знаю, но главное - она не меняется от напряжения. Нагрузил аккум до 3,70В, на сериал выводится все те же 4640 (((
Попробую 3-вольтовую поставить. Посмотрю даташит, можно ли на ее VCC подавать 4,2В?
.... посомотрел, нигде не сказано, что можно на VCC подавать большее напряжение, чем 3,3 или 5, в зависимости от модификации. Не рискну, у меня и так уже шесть трупиков накопилось, уже впритык к потребности проекта. Остальные не паленые, а после манипуляций с оптибутом и дальнейшими прошивками перестали шиться.
Продолжаю пробы. Взял Ваш скетч совсем без правок (до этого в свой код вставлял) и прошил. На мониторе скорострельная колонка с отображением 3.880. Напряжение 3,78, и та же история: при просадке напряжения до 3,65 значение не меняется.
Поймал закономерность: значение меняется сразу после ресета, после чего стабильно вне зависимости от VCC. До следующего ресета. Где-то бага.
Всё уже украдено до вас......
Источник: http://provideyourown.com/2012/secret-arduino-voltmeter-measure-battery-voltage/
01
void
setup
(){
02
Serial
.begin (115200);
03
}
04
void
loop
() {
05
Serial
.println ( (
float
) readVcc()/1000, 3 );
06
delay(500);
07
}
08
long
readVcc() {
09
// Read 1.1V reference against AVcc
10
// set the reference to Vcc and the measurement to the internal 1.1V reference
11
#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
12
ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
13
#elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
14
ADMUX = _BV(MUX5) | _BV(MUX0);
15
#elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
16
ADMUX = _BV(MUX3) | _BV(MUX2);
17
#else
18
ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
19
#endif
20
21
delay(2);
// Wait for Vref to settle
22
ADCSRA |= _BV(ADSC);
// Start conversion
23
while
(bit_is_set(ADCSRA,ADSC));
// measuring
24
25
uint8_t low = ADCL;
// must read ADCL first - it then locks ADCH
26
uint8_t high = ADCH;
// unlocks both
27
28
long
result = (high<<8) | low;
29
30
result = 1125300L / result;
// Calculate Vcc (in mV); 1125300 = 1.1*1023*1000
31
return
result;
// Vcc in millivolts
32
}
SergAG, по поводу Vcc сказано на первой странице в даташите, можно подавать от 1,8 до 5,5 вольт, зависит только от рабочей тактовой частоты. Даже уточнено 0 - 10MHz@2.7 - 5.5.V, так что 8-Мгц ардуино уверенно в этом диапазоне будут работать.
Про баг -да, сорри, когда подчищал скетч от лишних данных смахнул включение ацп, поменяйте 15 строчку на
ADCSRA |= (1<<ADSC)|(1<<ADEN);
поменяйте 15 строчку
Все в порядке, спасибо! У меня на разных входных напряжениях коэффициент 1,01-1,02 в сторону увеличения. Это погрешность тестеров?
SergAG, я не очень понял про что вы. Если про измерение опорного на выводе AREF -то я его измерял один лишь раз. У меня не плохой мультиметр, и он показал точно 1,100в при настройке analogReference(INTERNAL). Если вы измерите свой AREF, и там будет 1,1, но измеренное напряжение Vcc будет немного отличаться от того, что вычислило АЦП -это нормально. У атмела есть целый мануал по калибровке АЦП. Но проще не заморачиваться, а поиграться с множителем 1,1v в формуле рассчёта, подогнав его так, что бы показания мультиметра и ардуино совпали.
dimax, я про разницу между данными, которые отдает Ардуина в сериал, измеряя VCC, и теми, что меряет тестер там же. Но эта разница в 1% в моем случае абсолютно не существенна. Еще раз спасибо. Теперь буду добиваться, чтобы этот кусок заработал в моем скетче. Пока не очень.
у меня скомпилировалось и для нано и для меги8. хотя в меге 8 вроде надо будет поправить. там опорник вроде другой
а так да. это другой уровень. без поллитру не разберешься
хотя если каждый день хоть по 10-15мин перед сном читать даташит, через полгодика не хуже наверно можно будет написать
Жека, согласен на все сто. Если бы еще про это 30 лет назад знал, когда тетя учитель говорила - Учи балбес английский(((
Посмотрите, пожалуйста, не мешает ли эта функция замеру напряжения? После ресета первое значение правильное, потом 16368 до следующего ресета. Код не мой, автор сейчас помочь не может
01
void
Sleep(
int
_sleep_time)
02
{
03
if
(e){
Serial
.println(
"sleep. Number "
+ String(sleeps+1)); delay(10);};
04
//radio_sleep();
05
pin_uninit ();
06
07
// disable ADC
08
ADCSRA = 0;
09
10
// clear various "reset" flags
11
MCUSR = 0;
12
// allow changes, disable reset
13
WDTCSR = bit (WDCE) | bit (WDE);
14
15
// set interrupt mode and an interval
16
/*
17
if (_sleep_time >= 0 && _sleep_time < 16) {WDTCSR = bit (WDIE) | bit (WDP0);} // set WDIE, and 0.032 second delay
18
else if (_sleep_time >= 16 && _sleep_time < 32) {WDTCSR = bit (WDIE) | bit (WDP0);} // set WDIE, and 0.032 second delay
19
else if (_sleep_time >= 32 && _sleep_time < 64) {WDTCSR = bit (WDIE) | bit (WDP0);} // set WDIE, and 0.032 second delay
20
else if (_sleep_time >= 64 && _sleep_time < 125) {WDTCSR = bit (WDIE) | bit (WDP1);} // set WDIE, and 0.064 second delay
21
else if (_sleep_time >= 125 && _sleep_time < 250) {WDTCSR = bit (WDIE) | bit (WDP1) | bit (WDP0);} // set WDIE, and 0.125 second delay
22
else if (_sleep_time >= 250 && _sleep_time < 500) {WDTCSR = bit (WDIE) | bit (WDP2);} // set WDIE, and 0.25 second delay
23
else if (_sleep_time >= 500 && _sleep_time < 1000) {WDTCSR = bit (WDIE) | bit (WDP2) | bit (WDP0);} // set WDIE, and 0.5 second delay
24
else if (_sleep_time >= 1000 && _sleep_time < 2000) {WDTCSR = bit (WDIE) | bit (WDP2) | bit (WDP1);} // set WDIE, and 1.0 second delay
25
else if (_sleep_time >= 2000 && _sleep_time < 4000) {WDTCSR = bit (WDIE) | bit (WDP2) | bit (WDP1) | bit (WDP0);} // set WDIE, and 2.0 second delay
26
else if (_sleep_time >= 4000 && _sleep_time < 8000) {WDTCSR = bit (WDIE) | bit (WDP3);} // set WDIE, and 4.0 second delay
27
else if (_sleep_time >= 8000)
28
{*/
29
WDTCSR = bit (WDIE) | bit (WDP3) | bit (WDP0);
30
//} // set WDIE, and 8 second delay
31
32
/*
33
WDP3 WDP2 WDP1 WDP0 Number of WDT Typical Time-out at Oscillator Cycles VCC = 5.0V
34
35
0 0 0 0 2K (2048) cycles 16 ms
36
0 0 0 1 4K (4096) cycles 32 ms
37
0 0 1 0 8K (8192) cycles 64 ms
38
0 0 1 1 16K (16384) cycles 0.125 s
39
0 1 0 0 32K (32768) cycles 0.25 s
40
0 1 0 1 64K (65536) cycles 0.5 s
41
0 1 1 0 128K (131072) cycles 1.0 s
42
0 1 1 1 256K (262144) cycles 2.0 s
43
1 0 0 0 512K (524288) cycles 4.0 s
44
1 0 0 1 1024K (1048576) cycles 8.0 s
45
*/
46
47
wdt_reset();
// pat the dog
48
49
set_sleep_mode (SLEEP_MODE_PWR_DOWN);
50
sleep_enable();
51
52
// turn off brown-out enable in software
53
MCUCR = bit (BODS) | bit (BODSE);
54
MCUCR = bit (BODS);
55
56
sleep_cpu ();
57
58
// cancel sleep as a precaution
59
sleep_disable();
60
61
pin_init ();
62
//radio_awake();
63
radio_init ();
64
}
// Sleep
SergAG, да, мешает. Он затирает весь регистр управления АЦП. Поменяйте в моём скетче 15 строчку на ADCSRA = 0xc7; должно помочь.
Поменяйте в моём скетче 15 строчку на ADCSRA = 0xc7; должно помочь.
ПОМОГЛООООО!!!!!!! Огромное спасибо, у меня это была большая проблема. Завтра поеду на всех приводах менять скетчи. УФФ!
http://www.stepwood.com/avrsuite/2013/01/21/opredelenie-napryazheniya-pi...
Посмотрел сайтик AVR И все-все по ссылочке выше, весьма неплохой, содержится много полезной информации на русском. Вот это заинтересовало, цитата: "Но погодите радостно потирать руки: к превеликому сожалению широко используемую в Arduino-кругах функцию analogRead() не удастся заставить измерить внутреннее опорное напряжение." Не удастся -это слегка преувеличено) Почему бы и нет? Чуть подправить настройки... В файлике \arduino\hardware\arduino\cores\arduino\wiring_analog.c нужно закомментировать строчку
// if (pin >= 14) pin -= 14; // allow for channel or pin numbers. Что-б не мешала запрашивать адреса до 15-го, после этого нельзя будет аналоговые входы A1, A2, итд называть номерами 14, 15, итд. А оно и не особо то и нужно... И в строке
ADMUX = (analog_reference << 6) | (pin & 0x07); 0x07 поменять на 0x0F, от этой замены совсем никто не страдает. И вполне удалось заставить , даже залил проверил -работает:
1
void
setup
(){
2
Serial
.begin(9600);
3
}
4
void
loop
() {
5
float
Vop= (
float
)(1.1*1023)/analogRead(14) ;
6
Serial
.println (Vop);
//
7
}
Мало ли кто не в курсе, или забыл специфику литиевых батарей. Полезно помнить при проектировании автономных устройств.
У лития чем меньше ток разряда, тем при более высоком напряжении начинается "свал" кривой. Я при расчете ресурса полагал, что аккумулятор будет разряжаться до 2,9-3,2В. ФигВам! Аккумуляторы начинают заваливаться уже при 3,71-3,72В. Причем все (зеленый просто меньшей емкости, красный исходно был плохо заряжен). Остальные тянут до сих пор, но напряжение уже 3,72. Еще пара соток, и начнется завал, после чего девайс живет 3-4 дня и вырубается.
В общем, за что боролся, что называется. Оптибутом прошился, в сон кладу... А аккумулятор при этом меньшую емкость выдает.
А как реализовать индикацию заряда, мене нужно на устройстве вывод на экран заряда акума на 3.7в в процентах чтоб полный заряд оно воспринимало как 100 и дальше до убывания либо светодиод который будет загоратся при низком заряде.
Желательно схему чтоб напряжение акума считывало через аналоговый вход, так можна и другие акумуляторы проверять?
По поводу измеренеия напряжения я бы посоветовал почитать еще вот эту статью: http://tim4dev.com/arduino-secret-true-voltmeter/
Если вкратце, то опорное напряжение ни разу не 1.1В как принято считать, а индивидуально для каждого конкретного устройства и это стоит учитывать.
вот я этот скеч доработал для моего экрана, но он показывает 1 не реагирует не на зарядку не на разрядку, это при том что стоит float Vop= (float)(1.1*1023)/analogRead(14) я так понял это считывание внутрених параметров но когда ставлю float Vop= (float)(1.1*1023)/analogRead(0) то бегают какие то цифры а когда подключаешь напряжение на 0 аналоговый вход то опять 1 показывает ?
01
#include <LCD5110_Graph.h> // библиотека экрана
02
LCD5110 myGLCD(2, 3, 4, 6, 5);
03
04
extern
unsigned
char
MediumNumbers[];
05
void
setup
(){
06
07
myGLCD.InitLCD(60);
//контраст экрана
08
09
myGLCD.setFont(MediumNumbers);
10
Serial
.begin(9600);
11
}
12
void
loop
() {
13
float
Vop= (
float
)(1.1*1023)/analogRead(14) ;
14
Serial
.println (Vop);
//
15
16
myGLCD.clrScr();
// Очистка экрана
17
myGLCD.setFont(MediumNumbers);
//вывод средних цыфр
18
myGLCD.printNumF(Vop,0, CENTER, 23);
19
20
myGLCD.update();
// Вывод вместимого буфера на дисплей
21
delay (100);
// Задержка 0,1 с
22
}
Поднимаю.
1
ADMUX=_BV(REFS0)|_BV(MUX3)|_BV(MUX2)|_BV(MUX1);
2
ADCSRA|=_BV(ADSC);
3
while
(bit_is_set(ADCSRA, ADSC));
4
uint8_t low=ADCL;
5
uint8_t high=ADCH;
6
float
result=(high<<8)|low;
7
result=(1.21546*1023.0*1000)/result;
Итак, здесь мы имеем: 1.21546 - экспериментальный коэффициент погрешности, считаемый в калькуляторе, исходя их того, насколько отличается result и то что реально показывает вольтметр. И всё. Это надо подключить к A1. Через делитель 100k+10k. И уря! сакральный, непостижимый кусок скетча, которому надо поклоняться, работает! А большее мне непонятно. Ни. Хе. Ра.
- как перевесить это на другой порт АЦП, скажем A0? A3? A7?
- как приспособить для Atmega128? для Atmega32u4?
- как масштабировать для других номиналов делителя? для другого макс. напряжения?