Как измерить Arduino mini напряжение своего питания, меньшее 3,3V?

SergAG
Offline
Зарегистрирован: 22.12.2012

Спроектирован девайс с автономным питанием от лития. Реально напряжение находится в диапазоне от 4,2В до 2,9В (отключение встроенным контроллером). Батарея через делитель 50к/50к заведена на аналоговый пин. Проблема в том, что напряжение аккумулятора RAW при измерении сравнивется с VCC, но это имеет смысл только пока последнее стабилизированное. При снижении напряжения аккумулятора ниже 3,3 стабилизации на VCC нет.

Слышал, что можно подавать опорное напряжение, например, 2В, на аналоговый пин и сравнивать питание с ним. Можно ли так сделать, и означает ли это, что все аналоговые входы будут брать это напряжение как опорное?

И еще вопрос не совсем в тему: надо измерять еще и напряжение батареи инвертора, которая не имеет общей земли с девайсом. Есть ли простые решения, или надо городить ШИМ?

bwn
Offline
Зарегистрирован: 25.08.2014

http://arduino.ru/Reference/AnalogReference

По второму вопросу, простого пути не знаю(((

std
Offline
Зарегистрирован: 05.01.2012

SergAG пишет:
RAW при измерении сравнивется с VCC, но это имеет смысл только пока последнее стабилизированное. При снижении напряжения аккумулятора ниже 3,3 стабилизации на VCC нет.

Непонятно. Там стоит стабилизатор на 3.3? Он обязателен только для требовательных к диапазону напряжений потребителей, таких как nRF24L01+, если таковой есть. Питание pro mini может быть на частоте до 8 МГц от 2.7 до 5.5 В.

SergAG пишет:
можно подавать опорное напряжение, например, 2В

Но не на аналоговый пин а на AVCC.

SergAG пишет:
Можно ли так сделать

Да.

SergAG пишет:
все аналоговые входы будут брать это напряжение как опорное?

Да.

SergAG пишет:
надо измерять еще и напряжение батареи инвертора, которая не имеет общей земли с девайсом. Есть ли простые решения, или надо городить ШИМ?

Можно поставить туда вторую pro mini и передавать без проводов, например ИК светодиодом TSAL6200 и принимать ИК приёмником TSOP1838.

Можно отвязать аналоговый выход трансформатором, понадобится 555й таймер и операционный усилитель, но зависимость этой вещи далека от линейной, может надоесть отлаживать.

Можно просто питать устройство от батареи инвертора, но судя по задаче, это невозможно.

SergAG
Offline
Зарегистрирован: 22.12.2012

Спасибо, будем исползовать внутреннее опорное

2std

У меня контроль питания отключен фьюзами, но дело не в этом. Для измерения напряжения нужно стабилизированное опорное напряжение. По умолчанию оно берется с VCC. Но когда питание ниже VCC, т.е. внутреннего стабилизатора 3,3В, стабилизации нет. Нет и измерения.

На мини нет входа AVCC.

SergAG
Offline
Зарегистрирован: 22.12.2012

Какие ограничения на выбор номиналов делителя при измерении напряжения? Мне бы суммарное получить 150-200кОм, чтобы ток поменьше. Что-нибудь 150к/50к, делитель 4:1. Как раз максимальное при зарядке 4,2В поделится до внутреннего опорного 1,1В

bwn
Offline
Зарегистрирован: 25.08.2014

SergAG пишет:

Какие ограничения на выбор номиналов делителя при измерении напряжения? Мне бы суммарное получить 150-200кОм, чтобы ток поменьше. Что-нибудь 150к/50к, делитель 4:1. Как раз максимальное при зарядке 4,2В поделится до внутреннего опорного 1,1В

Ну так попробуйте или даташит курите, какие там минимальные токи.

SergAG
Offline
Зарегистрирован: 22.12.2012

Покурил форум, ничего не нашел, кроме того, что входное сопротивление измеряется гигаОмами. Попробовал. Не получилось. Дает одинаковое значение при разных напряжениях питания. При напряжении 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();

bwn
Offline
Зарегистрирован: 25.08.2014

Самое простое, возьмите батарейку на 1,5в через потенциометр подключите на измерительный вход и посмотрите, что получается. 10мВ это весьма малая величина, чтобы понять работает или нет.

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

SergAG, у вас какой-то сумбур. Что, куда включено, зачем делитель? Тут всё просто, если вы хотите питать от лития, то вам вход raw вообще не нужен. Подключаете аккум прямо к Vcc. Напряжение питания Vcc можно измерить внутренней коммутацией, не используя внешние аналоговые входы. Вот рабочий счетч, с оверсемплингом до 14 бит:

void setup(){
Serial.begin(9600);
}

void loop() {   
float Vop= (float)(1.1*16368)/Vbg() ;
Serial.println (Vop,3); // 
}
 
 
int Vbg() {  
ADMUX = (1<<REFS0)|(0<<REFS1)|(1<<MUX3)|(1<<MUX2)|(1<<MUX1)|(0<<MUX0);
long buffersamp=0;
for (int n=0x0; n<=0xff; n++ ) {
ADCSRA |= (1<<ADSC)|(1<<ADEN);  //Starts a new conversion
while (bit_is_set(ADCSRA,ADSC));
buffersamp += ADC; }
buffersamp >>=4; //16368 full scale 14bit
ADCSRA &= ~(1 << ADEN);  // отключаем АЦП
return buffersamp;
 }

 

 

bwn
Offline
Зарегистрирован: 25.08.2014

Dimax, преклоняюсь. Это уже другой уровень.

SergAG
Offline
Зарегистрирован: 22.12.2012

bwn пишет:

10мВ это весьма малая величина, чтобы понять работает или нет.

Почему 10мВ? 3,79/4=0,95В

2dimax, очень интересно, буду разбираться, куда что можно воткнуть

 

 

SergAG
Offline
Зарегистрирован: 22.12.2012

Ругается на строчку

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'

jeka_tm
jeka_tm аватар
Offline
Зарегистрирован: 19.05.2013

у меня скомпилировалось и для нано и для меги8. хотя в меге 8 вроде надо будет поправить. там опорник вроде другой

а так да. это другой уровень. без поллитру не разберешься

хотя если каждый день хоть по 10-15мин перед сном читать даташит, через полгодика не хуже наверно можно будет написать

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

SergAG, думаю вы с ошибками скопировали. Обратите внимания в правом верхнем углу скетча есть вспывающая колонка с функциями работы (Код, скопировать, печать)

SergAG
Offline
Зарегистрирован: 22.12.2012

2dimax, аж мурашки по коже. Вы что, ясновидящий? Так действительно скомпилировалось, хотя визуально отличий я не увидел.

Правда, пока это достижение не привело к результату. На мониторе в месте, где напряжение, выводится другая цифирь, 4640, что это означает - не знаю, но главное - она не меняется от напряжения. Нагрузил аккум до 3,70В, на сериал выводится все те же 4640 (((

Попробую 3-вольтовую поставить. Посмотрю даташит, можно ли на ее VCC подавать 4,2В?

.... посомотрел, нигде не сказано, что можно на VCC подавать большее напряжение, чем 3,3 или 5, в зависимости от модификации. Не рискну, у меня и так уже шесть трупиков накопилось, уже впритык к потребности проекта. Остальные не паленые, а после манипуляций с оптибутом и дальнейшими прошивками перестали шиться.

Продолжаю пробы. Взял Ваш скетч совсем без правок (до этого в свой код вставлял) и прошил. На мониторе скорострельная колонка с отображением 3.880. Напряжение 3,78, и та же история: при просадке напряжения до 3,65 значение не меняется.

Поймал закономерность: значение меняется сразу после ресета, после чего стабильно вне зависимости от VCC. До следующего ресета. Где-то бага.

 

trembo
trembo аватар
Offline
Зарегистрирован: 08.04.2011

Всё уже украдено до вас......

Источник: http://provideyourown.com/2012/secret-arduino-voltmeter-measure-battery-voltage/

void setup(){
  Serial.begin (115200);
}
void loop() {
  Serial.println ( (float) readVcc()/1000, 3 );
  delay(500);
}
long readVcc() {
  // Read 1.1V reference against AVcc
  // set the reference to Vcc and the measurement to the internal 1.1V reference
#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
  ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
  ADMUX = _BV(MUX5) | _BV(MUX0);
#elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
  ADMUX = _BV(MUX3) | _BV(MUX2);
#else
  ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#endif  

  delay(2); // Wait for Vref to settle
  ADCSRA |= _BV(ADSC); // Start conversion
  while (bit_is_set(ADCSRA,ADSC)); // measuring

  uint8_t low  = ADCL; // must read ADCL first - it then locks ADCH  
  uint8_t high = ADCH; // unlocks both

  long result = (high<<8) | low;

  result = 1125300L / result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000
  return result; // Vcc in millivolts
}

 

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

SergAG, по поводу Vcc сказано на первой странице в даташите, можно подавать от 1,8 до 5,5 вольт, зависит только от рабочей тактовой частоты. Даже уточнено 0 - 10MHz@2.7 - 5.5.V, так что 8-Мгц ардуино уверенно в этом диапазоне будут работать.

Про баг -да, сорри, когда подчищал скетч от лишних данных смахнул включение ацп,  поменяйте 15 строчку на

ADCSRA |= (1<<ADSC)|(1<<ADEN);

SergAG
Offline
Зарегистрирован: 22.12.2012

dimax пишет:

поменяйте 15 строчку

Все в порядке, спасибо! У меня на разных входных напряжениях коэффициент 1,01-1,02 в сторону увеличения. Это погрешность тестеров?

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

SergAG, я не очень понял про что вы. Если про измерение опорного на выводе AREF -то я его измерял один лишь раз. У меня не плохой мультиметр, и он показал точно 1,100в при настройке analogReference(INTERNAL). Если вы измерите свой AREF, и там будет 1,1, но измеренное напряжение Vcc будет немного отличаться от того, что вычислило АЦП -это нормально.  У атмела есть целый мануал по калибровке АЦП. Но проще не заморачиваться, а поиграться с множителем  1,1v в формуле рассчёта, подогнав его так, что бы показания мультиметра и ардуино совпали.

SergAG
Offline
Зарегистрирован: 22.12.2012

dimax, я про разницу между данными, которые отдает Ардуина в сериал, измеряя VCC, и теми, что меряет тестер там же. Но эта разница в 1% в моем случае абсолютно не существенна.  Еще раз спасибо. Теперь буду добиваться, чтобы этот кусок заработал в моем скетче. Пока не очень.

bwn
Offline
Зарегистрирован: 25.08.2014

jeka_tm пишет:

у меня скомпилировалось и для нано и для меги8. хотя в меге 8 вроде надо будет поправить. там опорник вроде другой

а так да. это другой уровень. без поллитру не разберешься

хотя если каждый день хоть по 10-15мин перед сном читать даташит, через полгодика не хуже наверно можно будет написать

Жека, согласен на все сто. Если бы еще про это 30 лет назад знал, когда тетя учитель говорила - Учи балбес английский(((

SergAG
Offline
Зарегистрирован: 22.12.2012

Посмотрите, пожалуйста, не мешает ли эта функция замеру напряжения? После ресета первое значение правильное, потом 16368 до следующего ресета. Код не мой, автор сейчас помочь не может

void Sleep(int _sleep_time)
{  
  if(e){Serial.println("sleep. Number " + String(sleeps+1)); delay(10);};
  //radio_sleep();
  pin_uninit ();
  
  // disable ADC
  ADCSRA = 0;  

  // clear various "reset" flags
  MCUSR = 0;     
  // allow changes, disable reset
  WDTCSR = bit (WDCE) | bit (WDE);
  
  // set interrupt mode and an interval 
  /*
  if       (_sleep_time >= 0  && _sleep_time < 16)     {WDTCSR = bit (WDIE) | bit (WDP0);}                           // set WDIE, and 0.032 second delay  
  else if  (_sleep_time >= 16  && _sleep_time < 32)    {WDTCSR = bit (WDIE) | bit (WDP0);}                           // set WDIE, and 0.032 second delay
  else if  (_sleep_time >= 32 && _sleep_time < 64)     {WDTCSR = bit (WDIE) | bit (WDP0);}                           // set WDIE, and 0.032 second delay
  else if  (_sleep_time >= 64 && _sleep_time < 125)    {WDTCSR = bit (WDIE) | bit (WDP1);}                           // set WDIE, and 0.064 second delay
  else if  (_sleep_time >= 125 && _sleep_time < 250)   {WDTCSR = bit (WDIE) | bit (WDP1) | bit (WDP0);}              // set WDIE, and 0.125 second delay
  else if  (_sleep_time >= 250 && _sleep_time < 500)   {WDTCSR = bit (WDIE) | bit (WDP2);}                           // set WDIE, and 0.25 second delay
  else if  (_sleep_time >= 500 && _sleep_time < 1000)  {WDTCSR = bit (WDIE) | bit (WDP2) | bit (WDP0);}              // set WDIE, and 0.5 second delay
  else if  (_sleep_time >= 1000 && _sleep_time < 2000) {WDTCSR = bit (WDIE) | bit (WDP2) | bit (WDP1);}              // set WDIE, and 1.0 second delay
  else if  (_sleep_time >= 2000 && _sleep_time < 4000) {WDTCSR = bit (WDIE) | bit (WDP2) | bit (WDP1) | bit (WDP0);} // set WDIE, and 2.0 second delay
  else if  (_sleep_time >= 4000 && _sleep_time < 8000) {WDTCSR = bit (WDIE) | bit (WDP3);}                           // set WDIE, and 4.0 second delay 
  else if  (_sleep_time >= 8000)    
  {*/
  WDTCSR = bit (WDIE) | bit (WDP3) | bit (WDP0);
  //}              // set WDIE, and 8 second delay

  /*
  WDP3  WDP2  WDP1  WDP0       Number of WDT     Typical Time-out at  Oscillator Cycles     VCC = 5.0V

   0     0     0     0             2K (2048) cycles       16 ms
   0     0     0     1             4K (4096) cycles       32 ms
   0     0     1     0             8K (8192) cycles       64 ms
   0     0     1     1            16K (16384) cycles    0.125 s
   0     1     0     0            32K (32768) cycles    0.25 s
   0     1     0     1            64K (65536) cycles    0.5 s
   0     1     1     0            128K (131072) cycles 1.0 s
   0     1     1     1            256K (262144) cycles 2.0 s
   1     0     0     0            512K (524288) cycles 4.0 s
   1     0     0     1            1024K (1048576) cycles 8.0 s
  */

  wdt_reset();  // pat the dog
  
  set_sleep_mode (SLEEP_MODE_PWR_DOWN);  
  sleep_enable();
 
  // turn off brown-out enable in software
  MCUCR = bit (BODS) | bit (BODSE);
  MCUCR = bit (BODS); 

  sleep_cpu ();  
  
  // cancel sleep as a precaution
  sleep_disable();
  
  pin_init ();
  //radio_awake();
  radio_init ();
} // Sleep

 

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

SergAG, да, мешает. Он затирает весь регистр управления АЦП.  Поменяйте в моём скетче 15 строчку на ADCSRA = 0xc7;  должно помочь.

SergAG
Offline
Зарегистрирован: 22.12.2012

dimax пишет:

Поменяйте в моём скетче 15 строчку на ADCSRA = 0xc7;  должно помочь.

ПОМОГЛООООО!!!!!!! Огромное спасибо, у меня это была большая проблема. Завтра поеду на всех приводах менять скетчи. УФФ!

trembo
trembo аватар
Offline
Зарегистрирован: 08.04.2011
dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

Посмотрел сайтик 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, от этой замены совсем никто не страдает. И вполне удалось заставить , даже залил проверил -работает:

void setup(){
Serial.begin(9600);
}
void loop() {   
float Vop= (float)(1.1*1023)/analogRead(14) ;
Serial.println (Vop); // 
}

 

 

SergAG
Offline
Зарегистрирован: 22.12.2012

Мало ли кто не в курсе, или забыл специфику литиевых батарей. Полезно помнить при проектировании автономных устройств.

У лития чем меньше ток разряда, тем при более высоком напряжении начинается "свал" кривой. Я при расчете ресурса полагал, что аккумулятор будет разряжаться до 2,9-3,2В. ФигВам! Аккумуляторы начинают заваливаться уже при 3,71-3,72В. Причем все (зеленый просто меньшей емкости, красный исходно был плохо заряжен). Остальные тянут до сих пор, но напряжение уже 3,72. Еще пара соток, и начнется завал, после чего девайс живет 3-4 дня и вырубается.

В общем, за что боролся, что называется. Оптибутом прошился, в сон кладу... А аккумулятор при этом меньшую емкость выдает.

skyspirit
Offline
Зарегистрирован: 27.02.2015

А как реализовать индикацию заряда, мене нужно на устройстве вывод на экран заряда акума на 3.7в в процентах чтоб полный заряд оно воспринимало как 100 и дальше до убывания либо светодиод который будет загоратся при низком заряде.

Желательно схему чтоб напряжение акума считывало через аналоговый вход, так можна и другие акумуляторы проверять?

SunX
SunX аватар
Offline
Зарегистрирован: 04.10.2014

По поводу измеренеия напряжения я бы посоветовал почитать еще вот эту статью: http://tim4dev.com/arduino-secret-true-voltmeter/

Если вкратце, то опорное напряжение ни разу не 1.1В как принято считать, а индивидуально для каждого конкретного устройства и это стоит учитывать.

skyspirit
Offline
Зарегистрирован: 27.02.2015

вот я этот скеч доработал для моего экрана, но он показывает 1 не реагирует не на зарядку не на разрядку, это при том что стоит           float Vop= (float)(1.1*1023)/analogRead(14) я так понял это считывание внутрених параметров но когда ставлю float Vop= (float)(1.1*1023)/analogRead(0) то бегают какие то цифры а когда подключаешь напряжение на 0 аналоговый вход то опять 1 показывает ?

#include <LCD5110_Graph.h> // библиотека экрана
 LCD5110 myGLCD(2, 3, 4, 6, 5);

extern unsigned char MediumNumbers[];
void setup(){
    
  myGLCD.InitLCD(60);//контраст экрана

  myGLCD.setFont(MediumNumbers);
Serial.begin(9600);
}
void loop() {   
float Vop= (float)(1.1*1023)/analogRead(14) ;
Serial.println (Vop); //

myGLCD.clrScr(); // Очистка экрана
myGLCD.setFont(MediumNumbers); //вывод средних цыфр
myGLCD.printNumF(Vop,0, CENTER, 23);
  
myGLCD.update(); // Вывод вместимого буфера на дисплей
delay (100); // Задержка 0,1 с
 }

 

std
Offline
Зарегистрирован: 05.01.2012

Поднимаю.

ADMUX=_BV(REFS0)|_BV(MUX3)|_BV(MUX2)|_BV(MUX1);
ADCSRA|=_BV(ADSC);
while(bit_is_set(ADCSRA, ADSC));
uint8_t low=ADCL;
uint8_t high=ADCH;
float result=(high<<8)|low;
result=(1.21546*1023.0*1000)/result;

Итак, здесь мы имеем: 1.21546 - экспериментальный коэффициент погрешности, считаемый в калькуляторе, исходя их того, насколько отличается result и то что реально показывает вольтметр. И всё. Это надо подключить к A1. Через делитель 100k+10k. И уря! сакральный, непостижимый кусок скетча, которому надо поклоняться, работает! А большее мне непонятно. Ни. Хе. Ра.

- как перевесить это на другой порт АЦП, скажем A0? A3? A7?

- как приспособить для Atmega128? для Atmega32u4?

- как масштабировать для других номиналов делителя? для другого макс. напряжения?