Измерение Vcc

Leopoll
Offline
Зарегистрирован: 16.06.2016

Стоит задача мониторить состояние Vcc на Mega. В сети нашел код, код рабочий. Судя по описанию, в качестве опорного напряжения выбирается Vcc  и измеряется внутреннее напряжение 1.1В. Из этого и вычисляется само Vcc. 

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(75); // 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
}

Однако в проекте используется библиотекa Blynk. И похоже с ее таймером код конфликтует. По крайней мере, после команды timer.run(), код стабильно выдает -1.0
Насколько я знаю, таймер от Blynk аналогичен библиотеке SimpleTimer. Он меняет значение analogReference?

Как их подружить?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Насчёт конфликта Ваших библиотек ничего сказать не могу, а вот VCC Вы таким кодом измерите с точностью ±10% и не лучше. Почитайте статью Ника Гэммона о том, как это делать правильно.

Что же до библиотеки, не думаю, что она меняет refference. Скорее, проблема в чём-то другом. Но, поскольку код секретный, ничего сказать не могу.

renoshnik
Offline
Зарегистрирован: 11.04.2013

ЕвгенийП пишет:

Насчёт конфликта Ваших библиотек ничего сказать не могу, а вот VCC Вы таким кодом измерите с точностью ±10% и не лучше. Почитайте статью Ника Гэммона о том, как это делать правильно.

Что же до библиотеки, не думаю, что она меняет refference. Скорее, проблема в чём-то другом. Но, поскольку код секретный, ничего сказать не могу.

ссылку на статью дайте пожалуйста.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

renoshnik пишет:

ссылку на статью дайте пожалуйста.

Там поищите поиском заголовок "Detecting low voltage".

Leopoll
Offline
Зарегистрирован: 16.06.2016

Код брал отсюда.

10% меня устраивает. Плюс есть возможность калибровки для конкретной платы. Мне важно отследить понижение Vcc, так как есть подозрение что радиатор на LM7805 не справляется. Короче, ищу причины нерегулярных перезагрузок девайса иногда и через сутки работы. Код не секретный, но оочень длинный 2300 строк, соответственно не особо оформленный для демонстрации. Мне такое выставлять слегка неудобно, хотя и не жалко. Но и вам копаться в таком стогу вряд ли захочется. 

void loop() {
  Serial.println (readVcc());
  timer.run(); // Запускаем таймер
  Serial.println (readVcc());
  if (Blynk.connected()) {  // If connected run as normal
    Blynk.run();

На первом выводе результат нормальный, на втором -1.0

Библиотеки

#include <EEPROM.h>

#include <Adafruit_GFX.h> // Hardware-specific library
#include <UTFTGLUE.h>              // class methods are in here
#include <TouchScreen.h>

#include <OneWire.h>
#include <DallasTemperature.h>
#include <Ultrasonic.h>
#include <avr/wdt.h> // Ватчдог. Перезагрузка при зависании

/////////////////////////BLYNK//////////////////////////////////////////////////

#include <ESP8266_Lib.h>
#include <BlynkSimpleShieldEsp8266.h>
#include <WidgetRTC.h>

Подскажите хоть пути решения нужной задачи. 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Leopoll пишет:

Подскажите хоть пути решения нужной задачи. 

Ну, я уже попытался подсказать, но Вы же не хотите.

Путь тут один, взять Ваши 2300 строк и последовательно кусок за куском выбрасывать лишнее до тех пор пока проблема не исчезнет. Как только исчезнет, восстановить последнее выброшенное, убедиться, что проблема появилась снова и пытаться выбрасывать что-то другое. Этот процесс продолжать до тех пор, пока в коде не останется строк 20-30. Тогда проблема станет очевидной, а если не станет, то уже тот код (из 20-30 строк) выложить сюда, чтобы любой желающий мог воспроизвести проблему и попытаться понять её причины.

Я всегда так делаю (вернее наоборот - я никогда не пишу 2300 строк без того, чтобы по ходу дела проверять каждый новый кусок в пару десятков строк и добавлять его только тогда, когда в нём уверен). Других путей не знаю.

Leopoll
Offline
Зарегистрирован: 16.06.2016

Ок. Думал, что приведенный пример достаточно локализует проблему. Походу, для начала надо будет выковырять все таймеры, которые изобильно разбросаны по коду. Как сделаю, отпишусь.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Leopoll пишет:

Ок. Думал, что приведенный пример достаточно локализует проблему.

"Приведенный пример" не компилируется, а потому никак "локализовать проблему" не может.

Leopoll
Offline
Зарегистрирован: 16.06.2016

Ну вот, очевидная мысль, что после команды timer.run() начинают выполнятся мои таймеры, содержание которых и приводят к конфликту, меня сразу не посетила.

Ошибка возникает из-за подпрограммы, которая выполняется раз в 500мс, и в которой три команды analogRead(). Выполнение любой из них и приводит к ошибке.  Если убрать все три - результат верный. В Setup стоит analogRefenece(default). Код все еще нужен?

b707
Offline
Зарегистрирован: 26.05.2017

Leopoll пишет:

Код все еще нужен?

Коллеги, вам как? - мне не нужен

ТС, можете выкидывать...

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Leopoll пишет:
Код все еще нужен?
Кому? Мне - нет. А Вам?

Leopoll
Offline
Зарегистрирован: 16.06.2016

Ок. Считаю это за тонкий вежливый намек. 

#include <ESP8266_Lib.h>
#include <BlynkSimpleShieldEsp8266.h>
BlynkTimer timer;
void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  analogReference(DEFAULT);
  
   timer.setInterval (501, read_sensor); //
   timer.setInterval(2031L, showvaluse);
   Serial.println ( readVcc());          // Здесь верный результат
}
void read_sensor() {
  
 analogRead(A10);  // Проблема в этой строке
         
}
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(75); // 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
}
void showvaluse(){
  Serial.println ( readVcc()); // Здесь неверный результат -1.0
}
void loop() {
  timer.run();
}

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

И что?

Расскажите, что с этим кодом не так?

Что он, по-Вашему, должен делать? Что реально делает? Чем отличается одно от другого?

Leopoll
Offline
Зарегистрирован: 16.06.2016

Там в комментах вроде указано. Результат функции readVcc должен показывать соответственно Vcc. Что и показывает в строке 11.

Но после запуска таймера, результат -1 ( в строке 44 ). Причина - в выполняющейся команде analogRead() стр.15

b707
Offline
Зарегистрирован: 26.05.2017

Leopoll - это троллинг?

- Вы взяли низкоуровневый код для ADC чипов АВР, запускаете его на ЕСП и спрашиваете "Что не так?"

Leopoll
Offline
Зарегистрирован: 16.06.2016

Запускаю на Меге. Esp там отдельно болтается и примере не используется.  Поскольку таймер используется встроенный в Blynk, то и библиотеки в примере ихние. 

b707
Offline
Зарегистрирован: 26.05.2017

Leopoll пишет:

Запускаю на Меге

тогда я бы после строки 33 добавил маленькую задержку (2-5мс) и еще одно чтение из АDc - скорее всего , со второго раз должно выдаваться правильное значение

Leopoll
Offline
Зарегистрирован: 16.06.2016

В низкоуровневневом коде плохо понимаю. Чтение из ADC это и есть строка 33? Если да, то не помогло. 

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Библиотек Blynk в природе более, чем одна. Дайте ссылку на ту, которую Вы используете. И вообще, на всё, чтобы можно было у себя запустить.

b707
Offline
Зарегистрирован: 26.05.2017

Leopoll пишет:

Чтение из ADC это и есть строка 33?

нет, 31-33

и выложите полный код после исправления

Leopoll
Offline
Зарегистрирован: 16.06.2016

первую библиотеку убрал - не используется. 

Blynk - последней версии 0.6.1 

#include <BlynkSimpleShieldEsp8266.h>
BlynkTimer timer;
void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  analogReference(DEFAULT);
  
   timer.setInterval (501, read_sensor); //
   timer.setInterval(2031L, showvaluse);
   Serial.println ( readVcc());          // Здесь верный результат
}
void read_sensor() {
  
 analogRead(A10);
         
}
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(75); // Wait for Vref to settle
  ADCSRA |= _BV(ADSC); // Start conversion
  while (bit_is_set(ADCSRA,ADSC)); // measuring
  delay(75);
  ADCSRA |= _BV(ADSC);
  while (bit_is_set(ADCSRA,ADSC)); // measuring
  delay(5);
  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
}
void showvaluse(){
  Serial.println ( readVcc());
}
void loop() {
  timer.run();
}

 

b707
Offline
Зарегистрирован: 26.05.2017

и как - работает?

Leopoll
Offline
Зарегистрирован: 16.06.2016

Найн. Нихт. Результат тот же. 

gfx125
Offline
Зарегистрирован: 27.05.2017

А каким образом вы скрестили ежа с удавом? Как у вас мега передает данные в Blynk?

Leopoll
Offline
Зарегистрирован: 16.06.2016

В данном примере никак. В исходном коде через модуль Esp.

Bruzzer
Offline
Зарегистрирован: 17.03.2020

Возможно дело в том, что в мега 2560 имеется 16 аналоговых входов и для включения с ADC8 по ADC15 используется дополнительный бит в регистре ADCSRB. А поскольку на 1.1 v вы переключаете руками, то бит в регистре ADCSRB не сбрасывается, и вы читаете из несуществующего входа. Я бы написал, что уверен, что дело в этом, но проверить не могу.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

.

Leopoll
Offline
Зарегистрирован: 16.06.2016

Если напишите как его сбросить и в каком месте кода, я бы проверил.

В ваших рассуждениях не увидел на что влияет analogRead(), а ведь именно после этой команды код выдает неверный результат. 

Bruzzer
Offline
Зарегистрирован: 17.03.2020

Leopoll пишет:

Если напишите как его сбросить и в каком месте кода, я бы проверил.

В ваших рассуждениях не увидел на что влияет analogRead(), а ведь именно после этой команды код выдает неверный результат. 

Если бы вы написали в вопросе не  analogRead() а analogRead(A10) ответ бы был более очевиден А10 > A7 и для его включения используется бит MUX5 в регистре ADCSRB. Надо добавить сброс этого бита. Или измените  analogRead(A10) на analogRead(A1) и проблема должна уйти.

Bruzzer
Offline
Зарегистрирован: 17.03.2020

ОБНОВЛЕНО - написанное ниже неверно: analogRead(30) использовать нельзя.

Судя по описанию на мега 2560  analogRead(30) должно считывать напряжение источника 1.1. Можно попробовать вообще отказаться от readVcc() а дважды читать analogRead(30) с паузой между вызовами и брать результат второго вызова. Паузу между вызовами подобрать экспериментально.

ОБНОВЛЕНО - написанное выше неверно: analogRead(30) использовать нельзя.

Leopoll
Offline
Зарегистрирован: 16.06.2016

Bruzzer пишет:

 Или измените  analogRead(A10) на analogRead(A1) и проблема должна уйти.

Полностью подтвердилось.

Bruzzer пишет:

 дважды читать analogRead(30) с паузой между вызовами

Походу работает и с первого раза. Или все таки лучше сделать два?

Bruzzer
Offline
Зарегистрирован: 17.03.2020

ОБНОВЛЕНО - написанное ниже неверно: analogRead(30) и analogRead(31) использовать нельзя.

Попробуйте без цикла в тестовой программе сначала прочитать analogRead(31) - это чтение напряжения 0. Потом несколько раз analogRead(30) и на каком разе результат перестанет расти, или растет на устраивающую вас погрешность, там и остановитесь.

Для интереса можно сделать тоже самое но вместо analogRead(31) сделать analogRead(пин подключенный к 5В) тогда последовательные чтения  значения analogRead(30) должны уменьшаться. 

Я не использовал измерение 1.1 поэтому своего опыта у меня нет, но читал, что у источника 1.1 очень малая нагрузочная способность и с первого раза он может не зарядить целиком конденсатор АЦП.

ОБНОВЛЕНО - написанное выше неверно: analogRead(30) и analogRead(31) использовать нельзя.

Bruzzer
Offline
Зарегистрирован: 17.03.2020

Можно поправить readVcc() Добавить строчку сброса MUX5       

ADCSRB = (ADCSRB & ~(1 << MUX5));

перед

delay(75); // Wait for Vref to settle

 

Leopoll
Offline
Зарегистрирован: 16.06.2016

А вот это уже для меня за гранью. В тестовом примере  analogRead (30) выдавало правдоподобный результат. Абсолютно то же самое, перенесенное в основной скетч выдает результат примерно ( а может и точно ) в два раза больше. 

ADCSRB = (ADCSRB & ~(1 << MUX5));                   работает.

Bruzzer
Offline
Зарегистрирован: 17.03.2020

Я ошибся когда предлагал использовать analogRead(30) и analogRead(31)
Так делать нельзя.
Дело в том что в функции analogRead(uint8_t pin)
есть строка в которой "нижний" адрес канала ограничивается тремя битами.
ADMUX = (analog_reference << 6) | (pin & 0x07);

и MUX5 рассчитывается в этом случае тоже не верно.

Leopoll
Offline
Зарегистрирован: 16.06.2016

Спасибо. Тогда самое лучшее использовать все таки функцию readVcc? Теперь она работает. Ей задержки не нужны?