Простой код слишком медленный :-(

Barny
Offline
Зарегистрирован: 23.01.2015

Приветствую!

Вожусь с вольтметром. Сейчас использую код с корректировкой внешнего опорного напряжения по внутреннему. Код в принципе уже классический, но почему-то безмерно тормозит. Один полный проход получается около секунды. Это переделанный фрагмент под отладочную консоль:



// резисторы делителя напряжения
const float r1 = 99250;  // 100K
const float r2 =  9930;  // 10K

// эту константу (typVbg) необходимо откалибровать индивидуально
float typVbg = 1.069; // 1.0 -- 1.2

float Vcc = 0.0;
float MaxVoltage = 0.0;

#define A_PIN 1
#define COUNT 5

int i;
float curVoltage;

void setup() {
    Serial.begin(9600);
    Serial.println("---");
    delay(1000);
 
  // определение внутреннего опорного напряжения
  analogReference(DEFAULT);  // DEFAULT INTERNAL использовать Vcc как AREF
  delay(100);
  Vcc = readVcc();
  MaxVoltage = Vcc / (r2 / (r1 + r2));
  analogWrite(A_PIN, 0);

    Serial.print("Vcc = ");
    Serial.println(Vcc);
    Serial.print("Max V. = ");
    Serial.println( MaxVoltage );
    Serial.println("---");
}

void loop() 
{
  Vcc = readVcc();
  // считываем точное напряжение с A1, где находиться вольтметр с делителем напряжения
  curVoltage = 0.0;
  for (i = 0; i < COUNT; i++) 
  {
      curVoltage = curVoltage + analogRead(A_PIN);
      delay(10);
  }
  curVoltage = curVoltage / COUNT;
  float v  = (curVoltage * Vcc) / 1024.0;
  float v2 = v / (r2 / (r1 + r2));

    Serial.print("V = ");
    Serial.println(v2);
 
  analogWrite(A_PIN, 0);
  delay(500);
}



/****************************************************************************
 * Функции
 ****************************************************************************/

float readVcc()
{
  byte i;
  float result = 0.0;
  float tmp = 0.0;

  for (i = 0; i < 5; i++) {
         // works on an Arduino 168 or 328
        ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);//Регистр ADMUX (регистр управления мультиплексором)

    delay(3); // Wait for Vref to settle
    ADCSRA |= _BV(ADSC); // Запуск преобразования (в режиме однократного преобразования)
                         //0 – преобразование завершено
                         //1 – начать преобразование
    while (bit_is_set(ADCSRA,ADSC)); // ждем, пока не будет сброшен ADSC

    uint8_t low  = ADCL; // сперва читаем ADCL, затем ADCH
    uint8_t high = ADCH;

    tmp = (high<<8) | low;
    tmp = (typVbg * 1023.0) / tmp;
    result = result + tmp;
    delay(5);
  }

  result = result / 5;
  return result;
}

Где происходят эти дикие тормоза и как ускориться ?

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

arny, в принципе весь ваш скетч -полный хаос. Куча бесмысленных команд,  начиная прямо со 2 строки. -не оптимальное использование типа данных, самый трэш это 47-48 строка. такие вещи  в крайнем случае рассчитываются один раз в сетапе, а в идеале на калькуляторе, и оперируют в дальнейшем уже только одним множителем. Зачем то пять отсчётов беретё в loop, ещё 5 отчётов в readvcc. Смысл?  А 53 строка что делает? Ну про пол-секунды в 54 строке и так понятно. И конечно никаких корретировок тут и в помине нет.

Barny
Offline
Зарегистрирован: 23.01.2015

Т.е. вы не видите, где возникает задержка в 1 секунду?

Каким образом строку 47 можно рассчитать в setup'е, если измеряемое напряжение меняется?

Пол секунды к делу не относится, т.к. запускается всего один раз.

На счет вещественных переменных для резисторов это да, но думаю это не суть. Коэффициент делителя не прописан заранее, т.к. пока все в процессе. Прошу не судить строго, я новичек. Это мой 5-й скетч.

5 прогонов, т.к. один никогда не дает более-менее точного значения измеренного напряжения. В рабочем варианте вообще только усреднение 30 проходов дают достоверный результат.

Получаю усредненное значение фактического напряжения питания (опорное) и на его основе измеряю входное напряжение как среднее между несколькими измерениями.

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

Penni
Penni аватар
Offline
Зарегистрирован: 18.01.2015

А если с помощью millis посмотреть время выполнения куска кода с 38 по 48 строку, сколько покажет?

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Barny, сейчас к сожалению нет времени на подробности, обратите ж внимание, полсекунды ваши из 54 строки стоят в loop, это главный тормоз.

Barny
Offline
Зарегистрирован: 23.01.2015

Вот это и было в точку :-).

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

Указание коэффициента делителя вместо его рассчета вообще не повлияло на время выполнения.

Кстати, не могу понять как быть с такой сторокой 

float v = (curVoltage * Vcc) / 1023.0;

АЦП в Atmega328 10-ти разрядный, т.е. от 0 до 1023, но это 1024 значения... Совсем запутался, на сколько же тогда надо делить - на 1024 или на 1023?

gena
Offline
Зарегистрирован: 04.11.2012

  Программист я не очень, но: если попробовть вычислять 

  float v2 =  (curVoltage * Vcc) *const

где const  = (r1 + r2)/( 1024.0 * r2)  и этот расчёт сделать один раз в setup?

Barny
Offline
Зарегистрирован: 23.01.2015

Это уже перетянул в setup. До сих пор не понятно на что делить. На 1024 или на 1023. В сети оба варианта гуляют. Максимальное значение ЦПУ - 1023, а всего значений 1024....

Penni
Penni аватар
Offline
Зарегистрирован: 18.01.2015

Ну если аналогрид выдает при 5 вольтах (ну или при максимальном) значение 1023 получается обычная пропорция и соответственно делить надо на 1023 (как и в официальном скетче), а то что в инете много копи-паста это понятно.

gena
Offline
Зарегистрирован: 04.11.2012

 Не могу объяснить почему, но думаю что в стоках 47 и 83 должно стоять одинаковое число (или 1024, или 1023). Ещё. Если в   readVcc()  взять i=8? Ведь деление на 8 - это сдвиг вправо на  три разряда.  Возможно компилятор это и будет использовать.

Barny
Offline
Зарегистрирован: 23.01.2015

А как быть с float?

Если обьявить резисторы как int, ничего не работает. Надо как то временно их в float перегнать...

Barny
Offline
Зарегистрирован: 23.01.2015

Уже поставил везде 1023.

В readVcc() i - это только счетчик проходов измерения внешнего опорного напряжения, а сдвиг используется для получения результата сравнения. На сколько я понял из документации, процессор 8 разрядный, а АЦП 10-ти, поэтому результат сранится сразу в 2-х регистрах. В одном целиком, а во втором только часть.

Как разберемся с преобразованием типов "на лету" покажу рабочий скетч

Penni
Penni аватар
Offline
Зарегистрирован: 18.01.2015

А чем флот не устраивает? тем что две переменных займут на 4 байта больше? И что значит не работает когда в int переделываете, прям совсем ничего не работает? или как?

Barny
Offline
Зарегистрирован: 23.01.2015

Целочисленные почему-то отказываются делиться :-)

Barny
Offline
Зарегистрирован: 23.01.2015

Пдключил экранчик. Получился вот такой рабочий скетч вольтметра с самокорректировкой уровня входного напряжения питания. Внутреннее опорное напряжение подбирал под результат. Подключил на вход блок питания от ноутбука (измеренное 19.35В) и перебирая значения опорного в диаппазоне от 1 до 1.2В подобрал такое, при котором результат получился 19.35В Эту процедуру нужно делать для каждого чипа один раз.



#include "U8glib.h"
U8GLIB_SSD1306_ADAFRUIT_128X64 u8g(12, 11, 10, 9, 8);	// SW SPI Com: SCK = 13, MOSI = 11, CS = 10, A0 = 9, reset =8

// резисторы делителя напряжения
const float r1 = 99250;  // 100K
const float r2 =  9930;  // 10K

float typVbg = 1.069; // 1.0 -- 1.2 величина опорного напряжения, подбирается индивидуально.

float Vcc = 0.0;
float Dev = 0.0;

#define A_PIN 1
#define COUNT 15

int i;
float curVoltage;

void setup() 
{
  
    if ( u8g.getMode() == U8G_MODE_R3G3B2 ) {
    u8g.setColorIndex(255);     // white
  }
  else if ( u8g.getMode() == U8G_MODE_GRAY2BIT ) {
    u8g.setColorIndex(3);         // max intensity
  }
  else if ( u8g.getMode() == U8G_MODE_BW ) {
    u8g.setColorIndex(1);         // pixel on
  }
  // определение опорного напряжения
  analogReference(DEFAULT);  // DEFAULT INTERNAL использовать Vcc как AREF
  delay(100);
  Vcc = readVcc();
  Dev = r2 / (r1 + r2);
  }

void loop() 
{
  Vcc = readVcc();// юстировка опорного напряжения
  // считываем точное напряжение
  curVoltage = 0.0;
    for (i = 0; i < COUNT; i++) 
  {
      curVoltage = curVoltage + analogRead(A_PIN);
      delay(8);
  }
  curVoltage = curVoltage / COUNT;
  float v  = (curVoltage * Vcc) / 1023.0;
  float v2 = v / Dev;
 u8g.firstPage();  
  do {
    u8g.setFont(u8g_font_fub30n);
    u8g.setPrintPos(0, 31);
    u8g.print(v2,1);
   u8g.setFont(u8g_font_fur17);
    u8g.print("V ");
        u8g.setPrintPos(0, 56);
    u8g.print("Vcc ");
    u8g.print(Vcc);   
    
     } while( u8g.nextPage() );
}



/****************************************************************************
 * Функции
 ****************************************************************************/

float readVcc()
{
  float result = 0.0;
  float tmp = 0.0;

  for (i = 0; i < 8; i++) {
         // works on an Arduino 168 or 328
        ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);//Регистр ADMUX (регистр управления мультиплексором)

    delay(3); // Wait for Vref to settle
    ADCSRA |= _BV(ADSC); // Запуск преобразования (в режиме однократногопреобразования)
                         //0 – преобразование завершено
                         //1 – начать преобразование
    while (bit_is_set(ADCSRA,ADSC)); // ждем, пока не будет сброшен ADSC

    uint8_t low  = ADCL; // сперва читаем ADCL, затем ADCH
    uint8_t high = ADCH;

    tmp = (high<<8) | low;
    tmp = (typVbg * 1023.0) / tmp;
    result = result + tmp;
    delay(3);
  }

  result = result / 8;
  return result;
}



Ссылка на первоисточник.

Penni
Penni аватар
Offline
Зарегистрирован: 18.01.2015

Отказываются делиться? На пикет чтоли вышли с транспарантами и лозунгами"свободу float долой int" :)

Как минимум 99250 не влезет в инт только в лонг соответственно r1 делаете лонг а в коде пишите MaxVoltage = Vcc / ((float)r2/(r1+r2)); и всё будет делиться. Но сомневаюсь, что маломальски выгоду увидите

Barny
Offline
Зарегистрирован: 23.01.2015

Спасибо за совет, значит оставлю как есть :-). Просто не очень люблю деление на float. Не всегда 6/3=2 во флоате. Последний раз серьезно кодил 12 лет назад, почти все выветрилось из головы.

gena
Offline
Зарегистрирован: 04.11.2012

  А что за индикатор? Впрочем SSD1306.

Barny
Offline
Зарегистрирован: 23.01.2015

Графический OLED дистлей 128х64, Китайский. Без reset на этой библиотеке не заработал. На Adafruit пошло нормально.

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Barny, второй ваш скетч уже более-менее :)  5 и 6 строки всё же лучше поменять на #define r1  99250L  // 100K
#define r2   9930  // 10K     И  35-ю чуть подправить Dev = (float)r2 / (r1 + r2); А можно в ней и сразу умножить на 1023, что б не делать эту операцию в loop каждый раз. Dev = (float)(r2*1023) / (r1 + r2); Соотвесно в 49й строке сразу делить на Dev, float v  = (curVoltage * Vcc) / Dev;  а 50-ю ликвидировать

А корректировка множителя, рассчитанная через "секретный вольтметр"  -вещь весьма и весьма неоднозначная. С одной стороны вы измеряете фактическое опорное напряжение, а с другой стороны суммируете погрешность измерений референса и погрешность основного измерения , что вызовет только ещё большую болтанку результата счёта АЦП.  ЛУчше просто измерить мультиметром напряжение шины+5, и сделать его константой. -Это будет на порядок точнее измерения контроллером. А ещё лучше  перейти на reference internal, и не иметь лишних проблем. Впрочем если у вас на индикаторе одна цифра после запятой вам вообще все эти нюансы будут безразличны)

SU-27-16
SU-27-16 аватар
Offline
Зарегистрирован: 13.08.2012
Barny
Offline
Зарегистрирован: 23.01.2015

Dev = (float)(r2*1023) / (r1 + r2); - вроде как тут ошибка :-)

SU-27-16
SU-27-16 аватар
Offline
Зарегистрирован: 13.08.2012

лучшеееее давать весь код (свёрнутый ) со ссылкой на строку вас тревожащею :) так проще.....

Barny
Offline
Зарегистрирован: 23.01.2015

Немного еще ускорился и подчистил код.

Рабочий вариант выглядит так (с учетом комментария #29):

// Вольтметр до 50В
#include "U8glib.h"

U8GLIB_SSD1306_ADAFRUIT_128X64 u8g(12, 11, 10, 9, 8);	// SW SPI Com: SCK = 13, MOSI = 11, CS = 10, A0 = 9, reset=8

// резисторы делителя напряжения
#define r1  99250L // 100K
#define r2   9930  // 10K

float typVbg = 1.069; // 1.0 -- 1.2 величина опорного напряжения, подбирается индивидуально.

float Vcc = 0.0;
float Dev = 0.0;

#define A_PIN 1
#define COUNT 16

int i;
float curVoltage;

void setup() 
{
   if ( u8g.getMode() == U8G_MODE_R3G3B2 ) 
   {
    u8g.setColorIndex(255);     // white
   }
    else if ( u8g.getMode() == U8G_MODE_GRAY2BIT ) 
     {
      u8g.setColorIndex(3);         // max intensity
     }
     else if ( u8g.getMode() == U8G_MODE_BW ) 
     {
      u8g.setColorIndex(1);         // pixel on
     }
  // определение опорного напряжения
  Dev = (float)(r1 + r2)/(1023.0*r2);
}

void loop() 
{
 Vcc = readVcc();// юстировка опорного напряжения
  // считываем точное напряжение
  curVoltage = 0.0;
      for (i = 0; i < COUNT; i++) 
  {
      curVoltage = curVoltage + analogRead(A_PIN);
  }
  curVoltage = curVoltage / COUNT;
  float v  = (curVoltage * Vcc)*Dev;

 u8g.firstPage();  
  do {
    u8g.setFont(u8g_font_fub30n);
    u8g.setPrintPos(0, 31);
    u8g.print(v,1);
    u8g.setFont(u8g_font_fur17);
    u8g.print("V ");
    u8g.setPrintPos(0, 56);
    u8g.print("Vcc ");
    u8g.print(Vcc);   
      } while( u8g.nextPage() );
}



/****************************************************************************
 * Функции
 ****************************************************************************/

float readVcc()
{
  float result = 0.0;
  float tmp = 0.0;

  for (i = 0; i < 8; i++) {
         // works on an Arduino 168 or 328
        ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);//Регистр ADMUX (регистр управления мультиплексором)

    delay(3); // Wait for Vref to settle
    ADCSRA |= _BV(ADSC); // Запуск преобразования (в режиме однократного преобразования)
                         //0 – преобразование завершено
                         //1 – начать преобразование
    while (bit_is_set(ADCSRA,ADSC)); // ждем, пока не будет сброшен ADSC
    tmp = (typVbg * 1023.0) /ADC;
    result = result + tmp;
   }
  result = result / 8;
  return result;
}

 

 

SU-27-16
SU-27-16 аватар
Offline
Зарегистрирован: 13.08.2012

а типерь вопроса нету..... код устраивает ? какие вопросы ? :)

SU-27-16
SU-27-16 аватар
Offline
Зарегистрирован: 13.08.2012

улучшить ? нано брать не среднее из выборки, а среднеВзвешанное !

#include "Arduino.h"
//=================================================================
float AverageArray( float *Array , byte dimension )
{
  const float kStdnt = 2.33;
  float total , mo , disp , delta , lowLimit , highLimit;
  byte m , n;
  total = 0;
  for( n = 0; n < dimension; n++ )
    {
      total = total + Array[ n ];
    }
  mo = total / dimension; 
  total = 0;
  for ( n = 0; n < dimension; n++ )
    {
      total = total + pow( ( mo - Array[ n ] ), 2 );
    }
  disp = sqrt( total / dimension );
  delta = disp * kStdnt;
  lowLimit = mo - delta;
  highLimit = mo + delta;
  m = 0;
  total = 0;
  for ( n = 0; n < dimension; n++ )
    {
      if ( Array[ n ] >= lowLimit && Array[ n ] <= highLimit )
        {
          total = total + Array[ n ];
          m++;
        }
    }
  return total / m;
}
//=================================================================#include "Arduino.h"
//=================================================================
float AverageArray( float *Array , byte dimension )
{
  const float kStdnt = 2.33;
  float total , mo , disp , delta , lowLimit , highLimit;
  byte m , n;
  total = 0;
  for( n = 0; n < dimension; n++ )
    {
      total = total + Array[ n ];
    }
  mo = total / dimension; 
  total = 0;
  for ( n = 0; n < dimension; n++ )
    {
      total = total + pow( ( mo - Array[ n ] ), 2 );
    }
  disp = sqrt( total / dimension );
  delta = disp * kStdnt;
  lowLimit = mo - delta;
  highLimit = mo + delta;
  m = 0;
  total = 0;
  for ( n = 0; n < dimension; n++ )
    {
      if ( Array[ n ] >= lowLimit && Array[ n ] <= highLimit )
        {
          total = total + Array[ n ];
          m++;
        }
    }
  return total / m;
}
//=================================================================
#include "Arduino.h"
//=================================================================
float AverageArray( float *Array , byte dimension );
//=================================================================
//const float kStdnt = 0.861;                     // коэффициент Стьюдента для P=0,6   и N=20
//const float kStdnt = 1.328;                     // коэффициент Стьюдента для P=0,8   и N=20
//const float kStdnt = 2.093;                     // коэффициент Стьюдента для P=0,95  и N=20
//const float kStdnt = 2.861;                     // коэффициент Стьюдента для P=0,99  и N=20
//const float kStdnt = 3.883;                     // коэффициент Стьюдента для P=0,999 и N=20
//const float kStdnt = 1.38;                      // коэффициент Шовенэ для P=0,98 и N=3
//const float kStdnt = 1.54;                      // коэффициент Шовенэ для P=0,98 и N=4
//const float kStdnt = 1.65;                      // коэффициент Шовенэ для P=0,98 и N=5
//const float kStdnt = 1.73;                      // коэффициент Шовенэ для P=0,98 и N=6
//const float kStdnt = 1.80;                      // коэффициент Шовенэ для P=0,98 и N=7
//const float kStdnt = 1.87;                      // коэффициент Шовенэ для P=0,98 и N=8
//const float kStdnt = 1.91;                      // коэффициент Шовенэ для P=0,98 и N=9
//const float kStdnt = 1.96;                      // коэффициент Шовенэ для P=0,98 и N=10
//const float kStdnt = 2.13;                      // коэффициент Шовенэ для P=0,98 и N=15
//const float kStdnt = 2.24;                      // коэффициент Шовенэ для P=0,98 и N=20
//const float kStdnt = 2.33;                      // коэффициент Шовенэ для P=0,98 и N=25
//const float kStdnt = 2.57;                      // коэффициент Шовенэ для P=0,98 и N=50
//const float kStdnt = 2.81;                      // коэффициент Шовенэ для P=0,98 и N=100
//const float kStdnt = 3.14;                      // коэффициент Шовенэ для P=0,98 и N=300
//const float kStdnt = 3.29;                      // коэффициент Шовенэ для P=0,98 и N=500
//const float kStdnt = 3.48;                      // коэффициент Шовенэ для P=0,98 и N=1000
//=================================================================

 

Barny
Offline
Зарегистрирован: 23.01.2015

Ой... я совсем не хочу беспокоить именно вас :-)

SU-27-16
SU-27-16 аватар
Offline
Зарегистрирован: 13.08.2012

прикольно..... ответили - уже спалились :)

"Сынок, письмо в котором ты просишь денег - мы не получали " - :)

...тут и покруче ваших "ммммм...." слышал моя, "чявоужь там" - говори или спроси у папы Путина..... оне в ардуинах похлестчее нашего....

SU-27-16
SU-27-16 аватар
Offline
Зарегистрирован: 13.08.2012

Barny пишет:

Ой... я совсем не хочу беспокоить именно вас :-)

изменять сообщение с целью изменить мнение - плохой вариант, не "по чесноку" :)

.....забыли-проехали :)

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Barny, вы всё время вставляете чужие строки в скетч не разобрав их полностью. Вот например сокральный смысл строк 3..9 и 48-53 -скинуть пару младших бит, всю эту красивую большую конструкцию можно заменить на одну команду : ADCSRA&=0xFC; Это вообще то прескалер (делитель)  по-умолчанию тактовая частота проца делится на 128, а эта команда меняет делитель на 16.  Только это актуально для непрерывной работы, когда АЦП-шка строчит автоматом (поднятый бит ADATE), если читать через analogRead -это мёртвому припарка, скорость всё равно будет меньше самой маленькой. Т.е можете выкинуть эти строки вообще. К тому же (ещё повнимательнее посмотрел) вы этот дефайн даже и не запускаете, для запуска нужно было в шапке написать #define FASTADC 1

15 строка -число , которое занимает два байта -вы суёте в 4 байта. Конечно не жалко)) , но смысл? Из той же области 43-44 строка -конечно не вредят, но пользы никакой, Вы записываете значение, которое уже стоит по умолчанию в регистре. 103-106 строки можно выкинуть. Формально всё правильно, но можно написать  в конце 107 строки /ADC вместо /tmp;  будет  так же работать и ещё 2 байта экономии) И последнее -пауза в 109 строке -кого ждёт?)

Barny
Offline
Зарегистрирован: 23.01.2015

Спасибо за развернутый комментарий. Убрал лишние ускорители, т.к. скорость и точность очень даже устраивает. Теперь 30% проекта сделано :-).

Код выше подправил. Может кому пригодится.

Осталось собрать по такому же принципу остальные датчики.

Такой вопрос - в в чем физическая разница в использовании вн. и внешн. опорных напряжений и какое больше подходит для своих ситуаций ?

На ум приходят различные внешние электропомехи...

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Barny,  разница в помехах ,в температурной зависимости.  Использовать напряжение питания МК в качестве опорного -худший вариант для вольтметра, я вам об этом уже сказал.  Т.к.оно гуляет в зависимости от нагрузки и трясётся с тактовой частотой контроллера. Внутреннее опорное -подойдёт почти для всех применений, опорное от внешнего источника -имеет смысл, если использовать прецизионный ион, с параметрами лучшеми чем у внутреннего,  или преследовать иные цели. Вам есть смысл перейти на внутреннее опорное, а если нужно сохранить диапазон до 50 вольт -то потребуется поменять всёго лишь один резистор. Зато не нужно будет измерять реальное напряжение, т.к. оно будет одинаковым при любом напряжении питания.

Barny
Offline
Зарегистрирован: 23.01.2015

Надо попробовать. Получается под 1.1В безопасно лучше использовать R2=2кОм верно ?

Величина фактического внутреннего опорного известна.

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Barny, можно 2кОм, тогда при входном 55вольт на АЦП придёт 1,09 вольт. Можно ещё упростить расчёт итогового напряжения. Сделайте делитель , подайте на вход известное напряжение, (желательно от аккумулятора, что б не было помех) например взяли аккум, измерили напряжение мультиметром, пусть будет 12,78 вольт. Смотрите сколько выдаёт чистый analogRead, измеря это напряжение - пусть для примера  будет 217 единиц. делите на калькуляторе 217/12,78 =16,979. Это и будет множитель (кол-во попугаев на 1 вольт) . Перед выводом на дисплей делаете v= результ_ацп / 16.979 и всё. Это решение в совокупности с переходом на внутреннний ИОН позволит вам  существенно облегчить скетч :)

Barny
Offline
Зарегистрирован: 23.01.2015

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

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Barny, что-то вы неправильно включили. По идее должно быть с точностью до наоборот. Тестировать измерение тока желательно на малошумящем источнике тока, хотя бы батарейка с  резистором.

Barny
Offline
Зарегистрирован: 23.01.2015

Ок, может у меня где-то провал по пониманию процесса.

Использую датчик тока ACS712T. Все подключено к USB. "Ноль" на датчике - 2,46В. Ардуино соответственно 511-514 уровней АЦП. Подключаю к внешнему питанию, на датчике 2,62В. АЦП выдает 510 - 512. Если использовать внутренний опорный источник, то "ноль" сразу скаканет.

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Barny, Думаю объяснение тут может быть таким: если вы питаете acs712 от того -же, от чего и мк, то при измерении с reference=default опорное напряжение АЦП колеблется в точном соответствии с выходным напряжением acs712, а следовательно относительное измерение производится точно. А когда ацп считает от опорного 1,1в, то ловит все броски напряжения, которые пролезают через питание acs712.  Тут 2 варианта, -либо использовать reference=default, как естественную компенсацию помех, либо питать acs712 от отдельного малошумящего источника.

Barny
Offline
Зарегистрирован: 23.01.2015

Вот именно по этому я и максимально возможно калибраю значение внешнего источника питания. К сожалению очень стабильным его сделать нельзя, т.к. это всего лишь DC"шка бортового питания электровелосипеда у которой несколько потребилелей. Надеюсь в рамках этой ветки написать действующий скеч амперметра, базирующийся на слежении опорного напряжения.

Не могу понять, почему в регистре ADC хранится значение, считываемое с входа датчика тока ?

Я думал, там дискретное значение опорного напряжения...

Как же мне узнать цифроую величину опорного напряжения, что бы вывести на "ноль" показания датчика тока ?

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Barny пишет:
Не могу понять, почему в регистре ADC хранится значение, считываемое с входа датчика тока ?
Так опорное вообще нигде не хранится. Контроллеру в принципе без разницы что мы примем за 1023 попугая. Это важно нам, пользователям. Поэтому для пользователя опорное -это какое напряжение считать концом счёта ацп.

Barny пишет:
Как же мне узнать цифроую величину опорного напряжения, что бы вывести на "ноль" показания датчика тока ?

Тут я уже что-то перестаю понимать в чём проблема. Если референс=default, и acs712 питается от того же, что и ардуина -то середина напряжения  будет 512 попугаев ацп. Только умножать нужно будет на статический множитель, а не на вычисляемый Vcc. На референс =internal в силу специфики датчика тока нет смысла переходить, иначе придётся усложнять схему.

Barny
Offline
Зарегистрирован: 23.01.2015

Посмотрите функцию  = readVcc(). На сколько я понял, там берется ненагруженный пин и с него считывается величина Vcc. Вот только не понятно, какой это пин и как его назначить по своему усмотрению.

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Barny, это не совсем пин..  Вообще ацп в меге один. Что-бы обслуживать все свои входы имеется мультиплексор, он конфигурируется регистром ADMUX, его младшие пол-байта задают к чему собссно подключить ацп. Есть вариант подключиться к 8 реальным входам, к внутреннему ИОН, к датчику температуры или соединить с землёй.

Barny
Offline
Зарегистрирован: 23.01.2015

Про один АЦП и и мультиплексор я в курсе. В данной функции входные пины ни как не влияют на его работу?

И как можно получить величину, равную половине этого Vcc в попугаях?

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

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

Barny
Offline
Зарегистрирован: 23.01.2015

ОК, один вопрос остался.

Вот формулы для рассчета тока:

Это рассчет для 5 амперного датчика (чувствительность 185 мВ/А) с опорным 5В. У меня 30А датчик. Вроде как чувствительность 66 мВ/A. Очень смущает число 1024... Его точно не надо заменить на 2013 ?

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Barny, 1023 или 1024 это вопрос философский, из разряда вопросов что первичнее курица или яйцо? Какой год считать началом 21 века - 2000-й или 2001-й :)  Я лично предпочитаю 1023, некоторые наши уважаемые  форумчане предпочитают 1024 , в #20 посте вам давали ссылку на бурные дебаты на эту тему двух наших уважаемых товарищей :)