Как известно, ATmega может питаться от широкого диапазона напряжений, поэтому может оставаться "в строю" даже в случае постепенного разряда батареи, выражающегося в уменьшении напряжения. Ситуация эта в робототехнике более чем стандартная, за примером далеко ходить не надо: возьмем проект Voyager. Это робот-космический аппарат, запущенный за пределы солнечной системы и потому лишенный способности питаться от солнечных батарей. На него установлены радиоизотопные термические генераторы (ядерные батарейки), которые на момент старта выдавали 30В / 470Вт, но каждый год они теряют 0.78% своей мощности. Соответственно, в настоящий момент осталось около 60% от первоначальной, и приходится включать исследовательские подсистемы поочередно, чтобы не перегрузить генераторы.
В Arduino нельзя просто взять и подключить Vcc к аналоговому пину напрямую - по умолчанию AREF связан с Vcc и вы всегда будете получать максимальное значение 1023, от какого бы напряжения вы не питались. Спасает подключение к AREF источника напряжения с заранее известным, стабильным напряжением, но это - лишний элемент в схеме.
Еще можно соединить Vcc с AREF через диод: падение напряжение на диоде заранее известно, поэтому вычислить Vcc не составит труда. Однако, при такой схеме через диод постоянно протекает ток, сокращая жизнь батареи, что тоже не очень удачно.
Как же выкрутиться из положения, не добавляя новых элементов в схему и не сокращая время работы батареи? Оказывается, выход - есть, и поможет нам внутренний источник опорного напряжения 1.1В в ATmega (в документации он проходит как bandgap reference), которое не зависит от Vcc. Получается такая формула:
V_BAT=(1.1*1024)/analogRead(14);
где V_BAT - это напряжение Vcc в вольтах, а analogRead(14) - результат прямого чтения АЦП из канала 14.
В Arduino разрешены чтения только из каналов 0-7 (не удивляйтесь, если нашли на своем Diecimila или Duemilanove только 0-5, возьмите, к примеру, Seeeduino ;)
Чтобы появилась возможность отправлять данные в другие каналы, в том числе в 14, надо изменить маску в библиотеке-ядре Arduino. Для этого откройте файл hardware\cores\arduino\wiring_analog.c и найдите там строку:
ADMUX = (analog_reference << 6) | (pin & 0x07);
замените ее на:
ADMUX = (analog_reference << 6) | (pin & 0x0f);
После этого можно написать вот такой скетч:
uint16_t raw_bandgap = 0; // значение внутреннего bandgap
float volt_battery = 0.0;
void setup(){
Serial.begin(57600);
}
void loop(){
// Чтение напряжения батареи
analogReference(DEFAULT); // использовать Vcc как AREF
raw_bandgap = analogRead(14); // холостое чтение после смены AREF (см. 23.5.2 в руководстве)
raw_bandgap = analogRead(14); // измерить значение внутреннего bandgap
volt_battery = (1.1 * 1024) / raw_bandgap; // вычислить Vcc
Serial.print(volt_battery);
Serial.println(" v_bat");
delay(1000);
}
Тем, кто хочет знать напряжение на батарее, стоит иметь ввиду, что при наличии регулятора (типа L7805CV), мы будем измерять напряжение Vout регулятора, а в случае наличия входного диода - надо учесть напряжение, которое на нем падает.
Вот так выглядят вычисляемые значения в Seeeduino, в момент переключения с питания 5В на 3,3В:
... при этом мой вольтметр показывает меньшие значения. Но, тем не менее - скетч работает ;)
UPD: Нашел примеры, в которых вместо константы 1.1 используется 1.05. Результат получается гораздо ближе к показаниям вольтметра, ищу теоретическую базу, способную объяснить этот факт...
добрый день уважаемый!подскажи пожалуйста
ОтветитьУдалитькак вычислять среднее значение,мне пришлось вот че нагородить,я правда не уверен что он это считает,но если раз в секунду мерить сильнее разброс,чем так
(это термостат на лм335з,работает кстати :)
а float-я думал покажет мне температуру с десятыми долями градуса :D
float fgh1= 0;
float fgh = 0;
float a=0;
float b=0;
float c=0;
float d=0;
float e=0;
float f=0;
float g=0;
float h=0;
float i=0;
float k=0;
float L=0;
float a1=0;
float b2=0;
float c3=0;
float d4=0;
float e5=0;
float f6=0;
float g7=0;
float h8=0;
float i9=0;
float k10=0;
float L11=0;
float cnst = 271;
void setup()
{
pinMode(7, OUTPUT);
Serial.begin(9600);
pinMode(8, OUTPUT);
}
void loop()
{
digitalWrite(8, HIGH);
a= analogRead(0);
delay(100);
b= analogRead(0);
delay(100);
c= analogRead(0);
delay(100);
d= analogRead(0);
delay(100);
e= analogRead(0);
delay(100);
f= analogRead(0);
delay(100);
g= analogRead(0);
delay(100);
h= analogRead(0);
delay(100);
i= analogRead(0);
delay(100);
k= analogRead(0);
delay(100);
L= analogRead(0);
delay(100);
a1= analogRead(0);
delay(100);
b2= analogRead(0);
delay(100);
c3= analogRead(0);
delay(100);
d4= analogRead(0);
delay(100);
e5= analogRead(0);
delay(100);
f6= analogRead(0);
delay(100);
g7= analogRead(0);
delay(100);
h8= analogRead(0);
delay(100);
i9= analogRead(0);
delay(100);
k10= analogRead(0);
delay(100);
L11= analogRead(0);
delay(100);
fgh=(a+b+c+d+e+g+h+i+k+f+L+a1+b2+c3+d4+e5+g7+h8+i9+k10+f6+L11)/22*0.48828125;
if (fgh<=20+cnst){
digitalWrite(7, 0);
delay(10000);
}
if (fgh>21+cnst){
digitalWrite(7, 1);
}
Serial.println(fgh-cnst,DEC);
}
Среднее можно здесь считать так:
ОтветитьУдалитьunsigned int step = 1;
float average = analogRead(0);
for (int i=0;i<22;i++) {
delay(100);
step++;
average = (step-1)/step*average + analogRead(0)/step;
}
благодарю!буду разбираться:)
ОтветитьУдалитьна тот случай если кто сюда забредет,я выше какой то кривой скетч выложил,вот рабочий:
float fgh2 = 0;
float fgh = 0;
float a=0;
float b=0;
float c=0;
float d=0;
float e=0;
float f=0;
float g=0;
float h=0;
float i=0;
float k=0;
float cnst = 271;
void setup()
{
pinMode(13, OUTPUT);
Serial.begin(9600);
}
void loop()
{
a= analogRead(0);
delay(10);
b= analogRead(0);
delay(10);
c= analogRead(0);
delay(10);
d= analogRead(0);
delay(10);
e= analogRead(0);
delay(10);
f= analogRead(0);
delay(10);
g= analogRead(0);
delay(10);
h= analogRead(0);
delay(10);
i= analogRead(0);
delay(10);
k= analogRead(0);
delay(10);
fgh2=(a+b+c+d+e+f+g+h+i+k)/10;
fgh =fgh2*0.48828125-cnst;
Serial.println(fgh,DEC);
if (fgh <28){
digitalWrite(13, 1);
delay(1000);
Позволю себе несколько замечаний... возможно они окажутся полезными.
ОтветитьУдалить1. analogRead() возвращает целое значение, следовательно хранить его можно в uint16_t (word).
2. для хранения однотипных данных можно использовать массив, например word measurents[22];
3. для обработки массивов можно использовать цикл, например так:
for (int i=0;i<22;i++) {
measurements[i] = analogRead(0);
}
4. Для вычисления среднего вообще не обязательно запоминать все измерения - этот способ я описал в моем предыдущем комментарии. Для 10-20 значений это несущественно, но когда их 100-200... можно получить проблемы с памятью, особенно в атмеге.
Ребят, у меня по рандому выдает результат:
ОтветитьУдалить1.10 v_bat
1.10 v_bat
1.71 v_bat
4.64 v_bat
375.47 v_bat
0.00 v_bat
0.00 v_bat
16.81 v_bat
2.07 v_bat
Все сделал как написано, в чем дело? Не надо разве цеплять вход с Vcc?
Наличие встроенного bandgap reference, сколько на нем падает и на каком канале АЦП он висит - зависит от конкретной модели МК.
УдалитьУ Вас какая ATmega?
Здравствуйте, все сделал как в примере, но значения выводятся какие попало. Использую Iteaduino V1.1 (ATmega 328). Если плата работает в режиме 5V то показывает 2,8-3,2V, а если переключаю в режим 3,3V, то вообще котовасия: от 8V до 15V, питается от USB. Подскажите, пожалуйста в чем может быть причина?
ОтветитьУдалитьДоброго времени суток!
ОтветитьУдалитьЕсть необходимость мониторить ардуинку запитаную от 3-х элементов АА(4,5 В) по питанию и при падении напряжения до 3-х и меньше Вольт подавать сигнал(пищалку включать или мигать светиком). Наткнулся на Вашу статейку, с ходу получаю такие результаты:
3.79 v_bat
3.78 v_bat
3.74 v_bat
3.73 v_bat
3.73 v_bat
3.73 v_bat
3.73 v_bat
3.74 v_bat
3.77 v_bat
3.79 v_bat
3.79 v_bat
3.77 v_bat
3.74 v_bat
3.73 v_bat
Питание в данный момент от USB. Мультиметр показывает 5.02 В на ножках питания ATMega328P-PU.
Еще непонятен вопрос относительно замера:
V_BAT=(1.1*1024)/analogRead(14);
и
analogReference(DEFAULT);
raw_bandgap = analogRead(14);
analogReference(DEFAULT) - говорит использовать питание Vcc как опорное http://arduino.ru/Reference/AnalogReference.
Далее , поправьте если не прав, снимаем показания с 14 канала который подключен к Vcc внутри самого контроллера?
Каким образом тогда тут притянуто 1.1 В ?
Прошу знатоков помочь в вопросе!
Заранее благодарен.
Дело в том, что автор вместо использования analogReference(INTERNAL) правит библиотеку-ядро ардуино, перенаправляя дефолтное референсное напряжение на источник 1,1В. Честно говоря, не совсем понимаю, почему нельзя использовать именно analogReference(INTERNAL)
Удалитьздравсвуйте, вольтметр показывает каждый раз при измерении значение 12.1
ОтветитьУдалить12.2
12.1
12.3
12.5
и т.д. т.е. быстро меныется как можно програмно осуществить плавную смену показаний на дисплее?
Смотря чего хочется от дисплея: можно загрубить (отбросить младшую цифру), можно реже считывать и выводить значения, а можно вообще усреднять последние N значений - т.е. выводить только это среднее значение.
Удалить