Очень точный вольтметр на arduino

mikelari
Offline
Зарегистрирован: 14.05.2015

Похожих тем много, но у меня возникла необходимость измерять постоянное напряжение с высокой точностью. Имеющийся Fluke 17B меня не устраивал, нужный мне прибор стоит дорого. Поэтому покопал интернет и сделал свой. Корпус от китайца. Индикатор 16х2 I2C. Дуина Nano V3. И главная деталь "автомобиля" - 24 битный АЦП с хорошим источником опорного напряжения на борту, куплен с ebay. Название модуля LTC2400 24bit analog to digital converter ADC module temp sensor SPI AVR arduino. Стоимость тысяча рублей. Кому интересны весьма приличные характеристики этого модуля, поиском на ebay, и внизу в описании товара есть ссылка на документацию. Получился вольметр с одним диапазоном измерения от 0 до 17 вольт. При постройке прибора потребовался более менее хороший источник опорного напряжения желательно с невысокой ценой (1000 руб) , и он был найден и куплен там же на ebay. Имеет четыре переключаемых опорных напряжения. Купленый источник опорного напряжения и мой вольтметр были проверены на служебных Fluke с ценниками от самолетов. Я остался доволен. Пять знаков после запятой совпадают. Шестой знак я программно убрал, из за неверного  его измерения связанного с паразитной емкостью измерительного канала. Написание софта не представит сложности для начинающего ардуинщика.

Так выглядит вольтметр.

Вид вольтметра

Замер напряжения на Li-Pol аккуме.

Так выглядит АЦП 24 бит с опорником.

Это китайский источник опорного напряжения на 2.5  5.0  7.5 10.0 вольт. Удобен для настройки всякой всячины.

На тыльной стороне китайца напряжения измерянные на заводе изготовителе.

Ну и конечно же данные можно передавать в компьютер по USB.

Если кому будет полезен мой опыт, то удачных проектов!

 

 

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

если не секрет зачем такая высокая точность? что за прибор такой

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

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

HWman
HWman аватар
Offline
Зарегистрирован: 26.02.2013

Тоже уже давно вынашиваю идею подобного девайса, но пока что всё ещё балуюсь:

0..3.3000 В, но всё это несерьёзно, ибо программное растяжение разрядности.

Gippopotam
Gippopotam аватар
Offline
Зарегистрирован: 12.09.2014

HWman пишет:

... программное растяжение разрядности.

Поясните, пожалуйста, что это значит?

HWman
HWman аватар
Offline
Зарегистрирован: 26.02.2013

Gippopotam пишет:

HWman пишет:

... программное растяжение разрядности.

Поясните, пожалуйста, что это значит?

http://arduino.ru/forum/proekty/mnogokanalnyi-voltmetr-c-lcd-displeem-na...

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

К сожалению оверсэмплинг имеет два существенных недостатка:

1. Абсолютно беспомощен против систематической ошибки, уменьшая лишь случайную.

2. Случайную погрешность он тоже снижает гораздо в меньшей степени, чем, как правило, считают те, кто его применяет. Например, по указанной выше ссылке утверждается о "соответствии 14-битному АЦП", что сразу вызывает вопрос: А по какому именно параметру соответствует?

oleg_kazakof
Offline
Зарегистрирован: 24.04.2015

[quote=mik.

Так выглядит АЦП 24 бит с опорником.

Это китайский источник опорного напряжения на 2.5  5.0  7.5 10.0 вольт. Удобен для настройки всякой всячины.

На тыльной стороне китайца напряжения измерянные на заводе изготовителе.

.

Если кому будет полезен мой опыт, то удачных проектов!

 

 

[/quote]

Если возможно - адреса магазинов этих девайсов.

 

Dron_S
Offline
Зарегистрирован: 04.10.2016

Привет всем. Прикупил такойже ацп но не могу залить стандартный скетч для просмотра напряжения в мониторенге порта.

Подчеркивает строчку b0 = SPI_read(); // read 4 bytes adc raw data with SPI

И выдает ошибку Arduino: 1.6.10 (Windows 10), Плата:"Arduino Nano, ATmega328"

C:\Users\Ёжик\Desktop\LTC2400\Arduino sketch\LTC2400_ADC_Serial_out\LTC2400_ADC_Serial_out.ino: In function 'void loop()':

LTC2400_ADC_Serial_out:71: error: 'SPI_read' was not declared in this scope

LTC2400_ADC_Serial_out:98: error: 'printFloat' was not declared in this scope

exit status 1
'SPI_read' was not declared in this scope
Может кто сталкивался ?
 

sls
Offline
Зарегистрирован: 30.05.2016

Dron_S я бы русские буквы из путей убрал, для начала

 

 

slider
Offline
Зарегистрирован: 17.06.2014

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

// есть гораздо распространенные варианты точных АЦП , к примеру MCP3421  - 18бит , корпус  sot23-6  ,  3в мериет до 4го знака

стоимость на али за 10шт около 500руб. используют в дешевых точных волтметрах 0-3.0000 здесь 

по форуму много есть по поиску  MCP3421

 

 

Dron_S
Offline
Зарегистрирован: 04.10.2016

Привет всем удалось победить данную проблему установкой Arduino: 1.6.08, может еще винда 10-я забитая всякой хренью. Но возникла другая проблема. После заливки программы в мониторинге порта показывает 2,051343 Вольт, но никакой источник питания не подключен. Если подключить 1,5 вольтовою батарейку то показывает точно 1,5 и т.д. все как бы работает. Но вот от куда  2,051343 ?

vb
Offline
Зарегистрирован: 15.09.2016

Ребятки, может кому-то понадобится, тут есть подробная и очень простая схема вольтметра на Arduino

mikelari
Offline
Зарегистрирован: 14.05.2015

Dron_S, я начинал с кода

//LTC2400 24bit ADC Module 
//
//Application Demo: 7 digit voltmeter 
//24bit ADC IC: LTC2400
//4.096 precision reference: TI REF3040
//
//By coldtears electronics
//
//LTC2400 code is adapted from Martin Nawrath
//Kunsthochschule fuer Medien Koeln
//Academy of Media Arts Cologne
//


#include <Stdio.h>
#include<stdlib.h>


#ifndef cbi
#define cbi(sfr, bit)     (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit)     (_SFR_BYTE(sfr) |= _BV(bit))
#endif

#define LTC_CS     0       // LTC2400 Chip Select Pin  on Portb 2 *  CS mega 0 uno 2
#define LTC_MISO 3       // LTC2400 SDO Select Pin  on Portb 4 *  Miso mega 3  uno 4
#define LTC_SCK   1       // LTC2400 SCK Select Pin  on Portb 5 *  SCK mega 1  uno 5


void setup() {

 cbi(PORTB,LTC_SCK);      // LTC2400 SCK low
 sbi (DDRB,LTC_CS);       // LTC2400 CS HIGH

 cbi (DDRB,LTC_MISO);
 sbi (DDRB,LTC_SCK);

 Serial.begin(57600);
 
 // init SPI Hardware
 sbi(SPCR,MSTR) ; // SPI master mode
 sbi(SPCR,SPR0) ; // SPI speed
 sbi(SPCR,SPR1);  // SPI speed
 sbi(SPCR,SPE);   //SPI enable

 //Serial.println("LTC2400 ADC Test");

}
float volt;
float v_ref=4.094;          // Reference Voltage, 5.0 Volt for LT1021 or 3.0 for LP2950-3

long int ltw = 0;         // ADC Data ling int
int cnt;                  // counter
byte b0;                  //
byte sig;                 // sign bit flag
char st1[20];             // float voltage text

/********************************************************************/
void loop() {

 cbi(PORTB,LTC_CS);             // LTC2400 CS Low
 delayMicroseconds(1);
 if (!(PINB & (1 << 4))) {    // ADC Converter ready ?
   //    cli();
   ltw=0;
   sig=0;

   b0 = SPI_read();             // read 4 bytes adc raw data with SPI
   if ((b0 & 0x20) ==0) sig=1;  // is input negative ?
   b0 &=0x1F;                   // discard bit 25..31
   ltw |= b0;
   ltw <<= 8;
   b0 = SPI_read();
   ltw |= b0;
   ltw <<= 8;
   b0 = SPI_read();
   ltw |= b0;
   ltw <<= 8;
   b0 = SPI_read();
   ltw |= b0;

   delayMicroseconds(1);

   sbi(PORTB,LTC_CS);           // LTC2400 CS Low

   if (sig) ltw |= 0xf0000000;    // if input negative insert sign bit
   ltw=ltw/16;                    // scale result down , last 4 bits have no information
   volt = ltw * v_ref / 16777216; // max scale
   char tmp[10];
   dtostrf(volt,6,6,tmp);
   tmp[8]='V';
   tmp[9]='\n';
  Serial.print(cnt++);
  Serial.print(";  ");
  printFloat(volt,6);           // print voltage as floating number
  Serial.println("  ");

  
 }
 sbi(PORTB,LTC_CS); // LTC2400 CS hi
 delay(200);

}
/********************************************************************/
byte SPI_read()
{
 SPDR = 0;
 while (!(SPSR & (1 << SPIF))) ; /* Wait for SPI shift out done */
 return SPDR;
}
/********************************************************************/
//  printFloat from  tim / Arduino: Playground
// printFloat prints out the float 'value' rounded to 'places' places
//after the decimal point
void printFloat(float value, int places) {
 // this is used to cast digits
 int digit;
 float tens = 0.1;
 int tenscount = 0;
 int i;
 float tempfloat = value;

 // if value is negative, set tempfloat to the abs value

   // make sure we round properly. this could use pow from
 //<math.h>, but doesn't seem worth the import
 // if this rounding step isn't here, the value  54.321 prints as

 // calculate rounding term d:   0.5/pow(10,places)
 float d = 0.5;
 if (value < 0)
   d *= -1.0;
 // divide by ten for each decimal place
 for (i = 0; i < places; i++)
   d/= 10.0;
 // this small addition, combined with truncation will round our

 tempfloat +=  d;

 if (value < 0)
   tempfloat *= -1.0;
 while ((tens * 10.0) <= tempfloat) {
   tens *= 10.0;
   tenscount += 1;
 }

 // write out the negative if needed
 if (value < 0)
   Serial.print('-');

 if (tenscount == 0)
   Serial.print(0, DEC);

 for (i=0; i< tenscount; i++) {
   digit = (int) (tempfloat/tens);
   Serial.print(digit, DEC);
   tempfloat = tempfloat - ((float)digit * tens);
   tens /= 10.0;
 }

 // if no places after decimal, stop now and return
 if (places <= 0)
   return;

 // otherwise, write the point and continue on
 Serial.print(',');

 for (i = 0; i < places; i++) {
   tempfloat *= 10.0;
   digit = (int) tempfloat;
   Serial.print(digit,DEC);
   // once written, subtract off that digit
   tempfloat = tempfloat - (float) digit;
 }
}

Этот код нормально работал у меня.

Из за большого времени преобразования АЦП LTC2400 , я свой вольтметр переделал на ads1115 ( 16 bit I2C ). Сделал два входа, один дифференцияльный  +/- 5.2 вольт с индикацией 4 знака после точки , второй обычный single с делителем напряжения на 11 и диапазоном измерения до 55 вольт с индикацией 2 знака после точки. Питание от двух аккумуляторов 18650 . И добавил вывод на сериал порт  в формате ZeroFormat LogView. Этот вариант вольтметра мне нравиться намного больше.

Также сама ардуина через делитель измеряет напряжение аккумуляторов , и в процентах показывает заряд ,по уравнению полученному при контрольной разрядке аккумуляторов.

 

allabrt
Offline
Зарегистрирован: 02.01.2017

Интересный материал по машиностроению.

allabrt
Offline
Зарегистрирован: 02.01.2017

Интересный материал по машиностроению.

ЮриБас
Offline
Зарегистрирован: 13.01.2012

mikelari пишет:
.. я свой вольтметр переделал на ads1115 ( 16 bit I2C ). Сделал два входа, один дифференцияльный  +/- 5.2 вольт с индикацией 4 знака после точки , второй обычный single с делителем напряжения на 11 и диапазоном измерения до 55 вольт с индикацией 2 знака после точки. Питание от двух аккумуляторов 18650 . И добавил вывод на сериал порт  в формате ZeroFormat LogView. Этот вариант вольтметра мне нравиться намного больше. 

mikelari Если не трудно, не могли бы выложить скетч на ads1115 ?  

Тоже прикупил себе ads1115 (еще INA219),  есть подобная надобность сделать логгер с хорошим разрешением до 50В  на зарядное устройство (либо зарядное с внешним АЦП) ну и ток на шунте мерить, желательно в диф режиме, чтобы мерить в обе стороны (заряд/разряд).  Правда сразу возникли вопросы - как правильно реализовать в коде, чтобы разрешение было по максимуму..  к тому же важно не потерять скорость сэмплирования - 800 сэмплов..   и т.д.  

Хотелось еще спросить,  формат ZeroFormat LogView - это значит можно смотреть в стандартной программе для зарядников  LogView?

MitsuokaOroshi
Offline
Зарегистрирован: 03.01.2017

Поделитесь пожалуйста схемой включения Вашего вольтметра для АЦП LTC2400. И возможно ли сделать вольамперметр на его основе?

mikelari
Offline
Зарегистрирован: 14.05.2015

Вот http://www.dropbox.com/s/tlvosmpspz9b9nb/Adafruit_ADS1X15.zip?dl=0 исправленная библиотека для ADS1115 (860 samples per second) . На 100кГц I2C  у меня измерял 435 раз в секунду . На 400кГц I2C не пробовал. Примеры из библиотеки работают нормально.

Скетч-пожалуйста, но писал его давно, и в нем не самые красивые решения


  #include <Adafruit_ADS1015.h>  //
  #include <Wire.h>              // 
  #include "rgb_lcd.h"           //
  rgb_lcd lcd;                   //
  Adafruit_ADS1115 ads;          //

//*************************************************************************************************
// П Е Р Е М Е Н Н Ы Е

  int adcD[100];             // массив для записи Diff измерений в память ардуино 
  int adcS[100];             // массив для записи Singl измерений в память ардуино
  int nomb=100;              // сколько провести измерений для усреднения    
  float  voltsD = 0;         // переменная для Diff напряжения
  float  voltsS = 0;         // переменная для Singl напряжения
  long   time  = 0;
  long   volts = 0;
  
  String UstrS="" ;          //стринг для получения напряжения с точкой и со всеми значащими цифрами
  String UstrD="" ;          //стринг для получения напряжения с точкой и со всеми значащими цифрами
  
  long Ubat;                  //переменная для измерения напряжения батарей
  byte UbatProc;              //переменная для напряжен бат в процентах
  String UbatProcS = "" ;     //стринг для напряжения батарей в процентах  
//*************************************************************************************************
 void setup() {
  lcd.begin(16, 2);                   // set up the LCD's number of columns and rows
  lcd.setRGB(255, 255, 0);            // установка желтого цвета LCD

  lcd.setCursor(0, 0);                // начали вывод версии программы на LCD 
  lcd.print("firmware_v7     ");
  lcd.setCursor(0, 1);                // начали вывод на LCD Serial speed знать скорость для логгирования на компе
  lcd.print("Serial_9600     ");
  delay(1000);
  lcd.setCursor(0, 0);                // через 1 секунду чистим LCD
  lcd.print("                ");
  lcd.setCursor(0, 1); // 
  lcd.print("                ");

  Serial.begin(9600);
  ads.setGain(GAIN_TWOTHIRDS);        // 2/3x gain +/- 6.144V  1 bit = 3mV      0.1875mV (default)
  ads.begin();                        //начинаем работу АЦП
  }

//*****************************************************************************************************
 void loop() {
   Get_Ubat();                       // идем на подпрограмму измерения напряжения батарей вольтметра
   Find_UbatProcS();                 // идем на подпрограмму сборки стринга с напряжением батарей в процентах
   Get_ADC();                        // идем на подпрограмму получения данных из АЦП в переменную
   //Printscreen ();                   // подпрограмма вывода напряжений на экран
   
   Find_UstrS();                     // идем на подпрограмму получения стринга UstrS это напряжение с точкой и со всеми значащими цифрами
   Find_UstrD();                     // идем на подпрограмму получения стринга UstrD это напряжение с точкой и со всеми значащими цифрами
   LCDout ();                        // идем на подпрограмму вывода на LCD
   OFout();                          // идем на подпрограмму вывода в LOGVIEW
   
   
   
}                            
/*****************************************************************************************************************************/
  void Get_Ubat(){                                      // подпрограмма измеряет напряжение батарей через делитель на входе А1
   Ubat = analogRead(A1);  
   Ubat = ((Ubat * 1000 / 1023 * 5) * 2.019 + 5) / 10 ; // 2.019 коэф деления резисторного делителя
   UbatProc = map(Ubat, 660 , 840 , 0 , 99);            // находим напряжение в процентах ,напряжение от 6.6 до 8.40 вольт
   //if (UbatProc > 99) UbatProc = 0;
   //if (UbatProc <= 1) UbatProc = 1;
   //Serial.print("Ubat="); 
   //Serial.println(UbatProc);
   
  }

/*****************************************************************************************************************************/
  void Find_UbatProcS(){                            // подпрограмма собрать в стринг UbatProcS данные по заряду батарей в процентах
  char buffer [10];                                 // буфер для преобразований Long-Char
   int k=0;                                         // разные временные цифры
   int i=0;                                         // разные временные цифры
      
   ltoa(UbatProc, buffer, 10);                      // забросили в char буффер цифры из  UbatProc 
   k=(strlen(buffer));                              // k это кол-во цифр в буффере т.е. в числе UbatProc  
      
   UbatProcS = ""; 
   if  (UbatProc < 10 )   UbatProcS += " ";             //
   i=0;
     while (i <= (k-1)) {                           // из буфера берем K цифр и вставляем их в Ustr
     UbatProcS += buffer [i];                       // 
     i++;
   }
    UbatProcS +="%";
    if (UbatProc > 99) UbatProcS = "???";
    if (UbatProc < 1)  UbatProcS = "???";
    //Serial.println(UbatProcS);
  }  
/*****************************************************************************************************************************/
                                                 //подпрограмма для получения данных из АЦП в переменные  voltsD и voltS
  void Get_ADC(){
    long results01 = 0;                          // временная переменая для суммирования рез-ов Diff измерения ADC и вычисления ср. значения
    long results3  = 0;                          // временная переменая для суммирования рез-ов Singl измерения ADC и вычисления ср. значения
    float multiplierD = 187.531 ;                // множитель для Diff    187.528 188.346; 
    float multiplierS = 2076 ;                   // множитель для Singl 
    long tekmillis=millis();                     // переменная содержит время начала работы цикла
  
    for(int x = 0; x < nomb; x ++){              // цикл от 0 до nomb раз проводит измерения
    adcD[x] = ads.readADC_Differential_0_1();    // в массив заполняем результат измерения Diff
    adcS[x] = ads.readADC_SingleEnded(3);        // в массив заполняем результат измерения Singl
    }                                            // цикл закончен
    time = (millis()-tekmillis);                 // вычисляем время которое ушло на nomb раз измерения ADC и запись в память
    
    for(int x = 0; x < nomb; x ++){              // цикл от 0 до nomb раз берет данные из массива adcD и adcS
    results01 = results01 + adcD[x];             // суммирует их
    results3  = results3  + adcS[x];
    }
    voltsD = (results01/nomb * multiplierD/1000000); // из суммы вычисляем среднее значение измерений Diff
    voltsS = (results3 /nomb * multiplierS/1000000); // из суммы вычисляем среднее значение измерений Singl
    }  

/*****************************************************************************************************************************/
  void Printscreen (){
    Serial.print(time);                          // вывод потраченного времени
    Serial.print(" mS  :  Singl ");
    Serial.print  (voltsS,2);                    // Singl с двумя знаками
    Serial.print("  :  Diff ");
    Serial.println(voltsD,4);                    // Diff  с четырмя знаками
    Serial.println("");
    }  
/*****************************************************************************************************************************/

//подпрограмма для получения стринга UsrtS напряжения с точкой и со всеми значащими цифрами  
  void Find_UstrS(){

    
   char buffer [10];                                //буфер для преобразований Long-Char
   int k=0;                                         // разные временные цифры
   int i=0;                                         // разные временные цифры
      
   ltoa((abs(voltsS)+0.005) *100, buffer, 10);      // забросили в char буффер цифры из volts  
   k=(strlen(buffer));                              // k это кол-во цифр в буффере т.е. в числе volts  и в том числе знак минус
      
   UstrS = "";                                      //  в Ustr начинаем собирать напряжение с точкой !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
   
   if  (voltsS < 0)   UstrS += "-";       //минус
   if  (voltsS >= 0)  UstrS += " ";       //плюс
   
   
   if (k == 4) {                                   // если есть 4 значащих цифр
     i=0;
     while (i <= (k-1)) {                          // из буфера берем K цифр и вставляем их в Ustr
     if (i == 2) UstrS += ".";                     // номер i определяет где вставить точку т.е измерение 4 вольт или 17 вольт
     UstrS += buffer [i];                          // 
     i++;
   }
   }
   
   if (k == 3) {                                  // если есть 3 значащих цифр
     UstrS += " ";
     i=0;
     while (i <= (k-1)) {                        // из буфера берем K цифр и вставляем их в Ustr
     if (i == 1) UstrS += ".";                   // номер i определяет где вставить точку т.е измерение 4 вольт или 17 вольт
     UstrS += buffer [i];                        // 
     i++;
   }
   }
   
   if (k == 2) {                                 // если есть 2 значащих цифр
     UstrS += " 0";
     i=0;
     while (i <= (k-1)) {                        // из буфера берем K цифр и вставляем их в Ustr
     if (i == 0) UstrS += ".";                   // номер i определяет где вставить точку т.е измерение 4 вольт или 17 вольт
     UstrS += buffer [i];                        // 
     i++;
   }
   }
   
   if (k == 1) {                                 // если есть 1 значащих цифр
     UstrS += " 0.0";
     i=0;
     while (i <= (k-1)) {                        // из буфера берем K цифр и вставляем их в Ustr
     //if (i == 0) UstrS += ".";                 // номер i определяет где вставить точку т.е измерение 4 вольт или 17 вольт
     UstrS += buffer [i];                        // 
     i++;
   }
   }
      
   /*Serial.print("voltsS=");
   Serial.print(voltsS);
   Serial.print("   UstrS=");
   Serial.print(UstrS);
   Serial.print("  : k=");
   Serial.println(k);
   */
   
   
   //x++;
   //  }
   //  delay(10000);
   
}
/*****************************************************************************************************************************/
//подпрограмма для получения стринга UsrtD напряжения с точкой и со всеми значащими цифрами  
  void Find_UstrD(){
  
    
   char buffer [10];                                   //буфер для преобразований Long-Char
   int k=0;                                            // разные временные цифры
   int i=0;                                            // разные временные цифры
      
   ltoa((abs(voltsD)+0.00005) *10000, buffer, 10);     // забросили в char буффер цифры из volts  
   k=(strlen(buffer));                                 // k это кол-во цифр в буффере т.е. в числе volts  и в том числе знак минус
      
   UstrD = "";                                         //  в Ustr начинаем собирать напряжение с точкой !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
   
   if  (voltsD < 0)   UstrD += "-";       //минус
   if  (voltsD >= 0)  UstrD += " ";       //плюс
   UstrD += " ";                          //просто форматирование
   
   if (k == 5) {                                       // если есть 5 значащих цифр
     i=0;
     while (i <= (k-1)) {                              // из буфера берем K цифр и вставляем их в Ustr
     if (i == 1) UstrD += ".";                         // номер i определяет где вставить точку т.е измерение 4 вольт или 17 вольт
     UstrD += buffer [i];                              // 
     i++;
   }
   }
   
   if (k == 4) {                                       // если есть 4 значащих цифр
     UstrD += "0.";
     i=0;
     while (i <= (k-1)) {                             // из буфера берем K цифр и вставляем их в Ustr
     //if (i == 2) UstrD += ".";                      // номер i определяет где вставить точку т.е измерение 4 вольт или 17 вольт
     UstrD += buffer [i];                             // 
     i++;
   }
   }
   
   if (k == 3) {                                      // если есть 3 значащих цифр
     UstrD += "0.0";
     i=0;
     while (i <= (k-1)) {                             // из буфера берем K цифр и вставляем их в Ustr
     //if (i == 1) UstrD += ".";                      // номер i определяет где вставить точку т.е измерение 4 вольт или 17 вольт
     UstrD += buffer [i];                             // 
     i++;
   }
   }
   
   if (k == 2) {                                      // если есть 2 значащих цифр
     UstrD += "0.00";
     i=0;
     while (i <= (k-1)) {                             // из буфера берем K цифр и вставляем их в Ustr
     //if (i == 0) UstrD += ".";                      // номер i определяет где вставить точку т.е измерение 4 вольт или 17 вольт
     UstrD += buffer [i];                             // 
     i++;
   }
   }
   
   if (k == 1) {                                      // если есть 1 значащих цифр
     UstrD += "0.000";
     i=0;
     while (i <= (k-1)) {                             // из буфера берем K цифр и вставляем их в Ustr
     //if (i == 0) UstrD += ".";                      // номер i определяет где вставить точку т.е измерение 4 вольт или 17 вольт
     UstrD += buffer [i];                             // 
     i++;
   }
   } 
   
   /*Serial.print("voltsD=");
   Serial.print(voltsD,4);
   Serial.print("   UstrD=");
   Serial.print(UstrD);
   Serial.print("  : k=");
   Serial.println(k);
    
   x++;
     }
     */
  }
 /****************************************************************************************************************************/ 
 void LCDout(){
  lcd.setCursor(0, 0);        // начали вывод напряжения на LCD
  lcd.print("S "); 
  lcd.print(UstrS);
  lcd.print("   V bat");
  
  lcd.setCursor(0, 1);        // начали вывод во вторую строку LCD
  lcd.print("D "); 
  lcd.print(UstrD);
  lcd.print(" V ");
  lcd.print(UbatProcS);       //напряжение батарей в процентах
 }
 /*********************************************************************************************************************/
 void OFout(){               // подпрограмма вывода данных на USB порт для логирования в LOGVIEW
  Serial.print("$1;1;0;"); 
  Serial.print(voltsS,2);
  Serial.print(";");
  Serial.print(voltsD,4);
  Serial.print(";0"); 
  Serial.println(13,DEC); 
 }
 /*******************************************************************************************/

И да,  можно смотреть в стандартной программе для зарядников  LogView. Нужно только сделать ini файл для вольтметра. Нужно будет, скину свой ini файл.

При написании разных измерялок часто требуется применение различных фильтров. Мне понравился вариант описаный в статье https://geektimes.ru/post/269202/ но в этом измерятеле такой фильтр еще не реализован.

mikelari
Offline
Зарегистрирован: 14.05.2015

По  АЦП LTC2400 включен был по схеме 

Схема модуля http://www.dropbox.com/s/qtsn966we7rpmny/PCB%20schematic.pdf?dl=0

Vin1 измеряет до 4.096 В

Vin2 измеряет до 17 вольт (предварительно замкнуть перемычку JP2)

Можно сделать свой делитель, и измерить сколько нужно.

Вольтамперметр на его основе? Сделать можно всё что угодно, где АЦП можно впихнуть и если устраивает 24bit c Conversion Time 160 mS.

 

MitsuokaOroshi
Offline
Зарегистрирован: 03.01.2017

mikelari пишет:

По  АЦП LTC2400 включен был по схеме 

Схема модуля http://www.dropbox.com/s/qtsn966we7rpmny/PCB%20schematic.pdf?dl=0

Vin1 измеряет до 4.096 В

Vin2 измеряет до 17 вольт (предварительно замкнуть перемычку JP2)

Можно сделать свой делитель, и измерить сколько нужно.

Вольтамперметр на его основе? Сделать можно всё что угодно, где АЦП можно впихнуть и если устраивает 24bit c Conversion Time 160 mS.

 

Благодарю за ответ, Вы встречали такие решения? Хотелось бы точный вольтамперметр для диапазона 30V 10A. Если есть примеры такого применения этого АЦП - было бы здорово увидеть.

mikelari
Offline
Зарегистрирован: 14.05.2015
MitsuokaOroshi
Offline
Зарегистрирован: 03.01.2017

Да я находил этот модуль, но дело в том что уже готова плата будущего двухканального блока питания и трансформатор 2х24х15В 10А. Было опробовано много популярных ВАметров на PIC и Atmega8 и у всех в какой то части диапазона была приличная нелинейность. А по задуму хочется сделать ВАметр с режимом внешнего измерения посредством отключения входа с блока питания и переключения на внешний источник напряжения. Такой себе вольтметр+блок питания. Купил бы готовый модуль если б знал заранее что будет такая возня и плохие результаты ВАметров. Потому увидев возможности Вашего АЦП понадеялся что есть хотя бы фрагмент схемы на пике или атмеге, на той же ардуине нано. 

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

MitsuokaOroshi, вам эти 24 бита ни к чему. Совершенно.  Простой расчёт - точность вольтмера в составе БП должна быть чуть лучше, чем уровень помех БП. Если взять среднестатический импульсный БП , то примерный уровень шума у них 100мв. Т.е. нет смысла отображать напряжения после одной цифры за запятой, т.к. там заведомо мусор.  Соответссно минимальная разрядность АЦП должна быть 30 /0.1 = 300 градаций, или с запасом - 9 битное (512 градаций) АЦП. Если БП трансформаторный-линейный, то средний шум у них 10мв. Соответссно нужно не менее 3000 градаций, или 12-битный АЦП.

mikelari
Offline
Зарегистрирован: 14.05.2015

Так же много пробовал ( в том числе строить ваттметр на ads 1115 с нормальными шунтами и также были вопросы с  нелинейностью), поэтому  INA226 http://arduino.ru/forum/proekty/vattmetr-3-kh-kanalnyi-s-perspektivoi и далее смогу наращивать мясо на этот скелет, впрочем уже этим занимаюсь. Возможности/хотелки  ограничиваются деньгами и фантазией. 

MitsuokaOroshi
Offline
Зарегистрирован: 03.01.2017

Для трансформаторного блока питания - да, не нужен. Но я же писал что хочется применить его и как внешний вольтметр. Ну да ладно, благодарю за потраченное время. 

MitsuokaOroshi
Offline
Зарегистрирован: 03.01.2017

mikelari пишет:

Так же много пробовал ( в том числе строить ваттметр на ads 1115 с нормальными шунтами и также были вопросы с  нелинейностью), поэтому  INA226 http://arduino.ru/forum/proekty/vattmetr-3-kh-kanalnyi-s-perspektivoi и далее смогу наращивать мясо на этот скелет, впрочем уже этим занимаюсь. Возможности/хотелки  ограничиваются деньгами и фантазией. 

Интересное решение, буду подглядывать за ходом развития Вашей работы ))

mikelari
Offline
Зарегистрирован: 14.05.2015

Может кому пригодится черновик работа с ads1115 без библиотеки Adafruit. Только биты конфига сами выбираем по даташиту.


 #include <Wire.h>             
 #define  ADR_ASD1115  0x48   //адрес ads1115
 
 byte writeBuf[3];            //для конфигурирования АЦП три байта
 byte readBuf[3];             //для чтения данных из АЦП три байта 

 float myfloat;               
 const float VPS = 6.144 / 32768.0;  //вольт на шаг в зависимости от PGA  усиления АЦП тут для 6.144
 unsigned int val = 0;        
//-------------------------------------------------------------------------------
 void setup() {
 Serial.begin(9600); 
 Wire.begin();              // begin I2C
 }  
//--------------------------------------------------------------------------------

 void loop() {                
 config_ads115();            // Конфигурирование и запуск преобразования ads1115 
 get_config();               // Прочитать конфиг биты и вывести на экран
 get_voltage();              // Прочитать регистр преобразования, пересчитать в напряжение
 
}
//-------------------------------------------------------------------------------
 void config_ads115(){        // Конфигурирование и запуск преобразования ads1115
   
   writeBuf[0] = 1;           // Указатель регистра (Conversion register is 0b00000000; config register is 0b00000001,
                              // Lo_thresh register is 0b00000010;Hi_thresh register is 0b00000011) т.е. указали конфиг
                              
   writeBuf[1] = 0b10000000;  // биты с 15 по 8 конфигурации смотрим в даташите сами
                              

   writeBuf[2] = 0b10000011;   // биты с 7 по 0 конфигурации смотрим в даташите сами
                                
  Wire.beginTransmission(ADR_ASD1115);  //конфигурируем АЦП записью трех байт 
  Wire.write(writeBuf[0]);     // Указатель регистра
  Wire.write(writeBuf[1]);     // биты с 15 по 8 конфигурации
  Wire.write(writeBuf[2]);     // биты с 7 по 0 конфигурации
  Wire.endTransmission();  

  delay(500); 
 }  

//-------------------------------------------------------------------------------
 void get_voltage(){                   // Прочитать регистр преобразования, пересчитать в напряжение
  readBuf[0] = 0;                      // Указатель регистра (Conversion register is 0b00000000; config register is 0b00000001,
                                       // Lo_thresh register is 0b00000010;Hi_thresh register is 0b00000011)т.е. указали Conversion 
                                    
  Wire.beginTransmission(ADR_ASD1115); // Начали читать данные из регистра преобразования 
  Wire.write(readBuf[0]);              // pointer указал в readBuf[0] записан 0 т.е. регистр преобразования
  Wire.endTransmission();

  Wire.requestFrom(ADR_ASD1115, 2);    // читаем из АЦП два байта
  readBuf[1] = Wire.read();  // 
  readBuf[2] = Wire.read();  // 
  Wire.endTransmission();  

                                     
  val = readBuf[1] << 8 | readBuf[2];  // собираем два байта в кучу в переменную int val

  if (val > 32768) val = 0;
  
  myfloat = val * VPS;                 // пересчитываем в напряжение
  
  Serial.print("Sensor voltage = ");   // вывод на экран результата
  Serial.println(myfloat);
 
 } 
//--------------------------------------------------------------------------------
 void get_config(){                    // взять все биты конфига и вывести на экран
  readBuf[0] = 1;                      // Указатель регистра (Conversion register is 0b00000000; config register is 0b00000001,
                                       // Lo_thresh register is 0b00000010;Hi_thresh register is 0b00000011) т.е. указали config
                                    
  Wire.beginTransmission(ADR_ASD1115); // Начали читать данные из регистра конфига 
  Wire.write(readBuf[0]);              // pointer указал в readBuf[0] записан 1 т.е. регистр config
  Wire.endTransmission();

  Wire.requestFrom(ADR_ASD1115, 2);    // читаем из АЦП два байта
  readBuf[1] = Wire.read();  // 
  readBuf[2] = Wire.read();  // 
  Wire.endTransmission();  

  Serial.print("Config = ");           //выводим на экран все биты конфига
  Serial.print(readBuf[1],BIN);
  Serial.print("_");
  Serial.println(readBuf[2],BIN);
 
 } 

//-------------------------------------------------------------------------------

 

mikelari
Offline
Зарегистрирован: 14.05.2015

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

- отказался от библиотеки ads1115

- конфиг теперь 128 SPS

- запускается каждое измерение, а не цикличный режим работы АЦП

- пусто-гон по кругу ожидает окончание каждого измерения

- добавлен фильтр измерений типа как я описывал выше

- переменными Filter_read  и Filter_get   устанавливается настройки фильтра. У меня примерно около 480mS круг и выдача данных на свой дисплей и в порт для LogView

- прилагаю ini файл для LogView

http://www.dropbox.com/s/cgslaod0kjfb74h/LogView_ini.zip?dl=0

Код еще можно чистить и править, но работает нормально на nano


#include <Wire.h>              //
  #include "rgb_lcd.h"           //
  rgb_lcd lcd;                   //
  #define  ADR_ASD1115  0x48     //адрес ads1115
  
  #define Filter_read 24 //!!! только четное число сколько читать значений из АЦП для работы фильтра 
  #define Filter_get  4  //!!! только четное число сколько взять  значений  для расчета среднего фильтром


  float  float_voltsD = 0;      // переменная для Diff напряжения
  float  float_voltsS = 0;      // переменная для Singl напряжения
 
  
  String UstrS="" ;          //стринг для получения напряжения с точкой и со всеми значащими цифрами
  String UstrD="" ;          //стринг для получения напряжения с точкой и со всеми значащими цифрами
  
  long Ubat;                  //переменная для измерения напряжения батарей
  byte UbatProc;              //переменная для напряжен бат в процентах
  String UbatProcStr = "" ;   //стринг для напряжения батарей в процентах  
  byte writeBuf[3];           //для конфигурирования АЦП три байта
  byte readBuf[3];            //для чтения данных из АЦП три байта
  
  int  raw;                   //для получения одного значения ads raw из регистра преобразования АЦП после чтения
  long filter_raw;            //для получения множества значений raw из регистра преобразования АЦП после чтения по значениям Filter_read и Filter_get
  
  long time_start;
  long time_end;
//*************************************************************************************************
 void setup() {
  Wire.begin();                       // begin I2C
  Serial.begin(9600);
  lcd.begin(16, 2);                   // set up the LCD's number of columns and rows
  
  
  lcd.setRGB(255, 255, 0);            // установка желтого цвета LCD
  lcd.setCursor(0, 0);                // начали вывод версии программы на LCD 
  lcd.print("firmware_v9     ");
  lcd.setCursor(0, 1);                // начали вывод на LCD Serial speed знать скорость для логгирования на компе
  lcd.print("LogView_9600     ");
  delay(1000);
  lcd.setCursor(0, 0);                // через 1 секунду чистим LCD
  lcd.print("                ");
  lcd.setCursor(0, 1); // 
  lcd.print("                ");

  
  }

//*****************************************************************************************************
 void loop() {
   time_start = micros();
   
   Get_Ubat_328();                        // идем на подпрограмму измерения напряжения батарей питания вольтметра
   Find_UbatProcStr_328();                // идем на подпрограмму сборки стринга с напряжением батарей в процентах
 
   filter_raw_calc_D();                  // фильтр с дифф входа 0-1 прочитает множество измерений raw по  
   float_voltsD  = 187.5420001;                // значениям Filter_read и Filter_get и в перменную filter_raw поместит среднее 
   float_voltsD *= filter_raw / Filter_get;    // значение вычислений. Остается пересчитать это сырое значение АЦП в напряжение
   float_voltsD /= 1000000;                    // 187.5420001 это калибровка датчика опытным путем
   
   filter_raw_calc_S();                        // фильтр с несимм входа GND-3 прочитает множество измерений raw по  
   float_voltsS  = 2074.000001;                // значениям Filter_read и Filter_get и в перменную filter_raw поместит среднее 
   float_voltsS *= filter_raw / Filter_get;    // значение вычислений. Остается пересчитать это сырое значение АЦП в напряжение
   float_voltsS /= 1000000;                    // 2074.000001 это калибровка датчика с делителем напряжения опытным путем
    
   Find_UstrS();                     // идем на подпрограмму получения стринга UstrS это напряжение с точкой и со всеми значащими цифрами
   Find_UstrD();                     // идем на подпрограмму получения стринга UstrD это напряжение с точкой и со всеми значащими цифрами
   LCDout ();                        // идем на подпрограмму вывода на LCD
   OFout();    // идем на подпрограмму вывода в LOGVIEW
   
   time_end = micros();
   //Printscreen ();                   // подпрограмма вывода напряжений на экран
     
}                            
/*****************************************************************************************************************************/

  void Get_Ubat_328(){                                  // подпрограмма измеряет напряжение батарей через делитель на входе А1
   Ubat = analogRead(A1);  
   Ubat = ((Ubat * 1000 / 1023 * 5) * 2.019 + 5) / 10 ; // 2.019 коэф деления резисторного делителя
   UbatProc = map(Ubat, 660 , 840 , 0 , 99);            // находим напряжение в процентах ,напряжение от 6.6 до 8.40 вольт
   //if (UbatProc > 99) UbatProc = 0;
   //if (UbatProc <= 1) UbatProc = 1;
   //Serial.print("Ubat="); 
   //Serial.println(UbatProc);
   
  }

/*****************************************************************************************************************************/
  void Find_UbatProcStr_328(){                      // подпрограмма собоать в стринг UbatProcStr данные по заряду батарей в процентах
  char buffer [10];                                 // буфер для преобразований Long-Char
   int k=0;                                         // разные временные цифры
   int i=0;                                         // разные временные цифры
      
   ltoa(UbatProc, buffer, 10);                      // забросили в char буффер цифры из  UbatProc 
   k=(strlen(buffer));                              // k это кол-во цифр в буффере т.е. в числе UbatProc  
      
   UbatProcStr = ""; 
   if  (UbatProc < 10 )   UbatProcStr += " ";             
   i=0;
     while (i <= (k-1)) {                           // из буфера берем K цифр и вставляем их в Ustr
     UbatProcStr += buffer [i];                        
     i++;
   }
    UbatProcStr +="%";
    if (UbatProc > 99) UbatProcStr = "???";
    if (UbatProc < 1)  UbatProcStr = "???";
    //Serial.println(UbatProcStr);
  }  

//--------------------------------------------------------------------------------
 void read_config(){                    // Читаем конфиг и по 15 биту ожидаем окончание преобразования АПЦ (можно читать один 1-й байт readBuf[1] если не нужен весь конфиг)
  readBuf[0] = 1;                       // Указатель регистра (Conversion register is 0b00000000; config register is 0b00000001,
                                        // Lo_thresh register is 0b00000010;Hi_thresh register is 0b00000011) т.е. указали config
            
  do {
   Wire.beginTransmission(ADR_ASD1115); // Начали читать данные из регистра конфига 
   Wire.write(readBuf[0]);              // pointer указал в readBuf[0] записан 1 т.е. регистр config
   Wire.endTransmission();

   Wire.requestFrom(ADR_ASD1115, 2);   // читаем из АЦП два байта
   readBuf[1] = Wire.read();   
   readBuf[2] = Wire.read();   
   Wire.endTransmission(); 
   
  } while(bitRead(readBuf[1],7)==0);    //читаем байты пока 15-й бит не станет равен 1 что значит одно измерение закончено
  
/*Serial.print("Config = ");
  Serial.print(readBuf[1],BIN);
  Serial.print("_");
  Serial.print(readBuf[2],BIN);
  Serial.print("_");
  Serial.print(time_end-time_start);
*/
 } 

//-------------------------------------------------------------------------------
 void ads1115_config_Differential_0_1(){
   writeBuf[0] = 1;           // Указатель регистра (Conversion register is 0b00000000; config register is 0b00000001,
                              // Lo_thresh register is 0b00000010;Hi_thresh register is 0b00000011) т.е. указали конфиг
                              
   writeBuf[1] = 0b10000001;  // биты с 15 по 8 конфигурации 
   writeBuf[2] = 0b10000011;   // биты с 7 по 0 конфигурации 
                                
  Wire.beginTransmission(ADR_ASD1115);  //конфигурируем АЦП записью трех байт 
  Wire.write(writeBuf[0]);     // Указатель регистра
  Wire.write(writeBuf[1]);     // биты с 15 по 8 конфигурации
  Wire.write(writeBuf[2]);     // биты с 7 по 0 конфигурации
  Wire.endTransmission();  

  //delay(10);                    // 
 
 }

//-------------------------------------------------------------------------------
void ads1115_config_Single_GND_3(){
   writeBuf[0] = 1;           // Указатель регистра (Conversion register is 0b00000000; config register is 0b00000001,
                              // Lo_thresh register is 0b00000010;Hi_thresh register is 0b00000011) т.е. указали конфиг
                              
   writeBuf[1] = 0b11110001;  // биты с 15 по 8 конфигурации 
   writeBuf[2] = 0b10000011;   // биты с 7 по 0 конфигурации 
                                
  Wire.beginTransmission(ADR_ASD1115);  //конфигурируем АЦП записью трех байт 
  Wire.write(writeBuf[0]);     // Указатель регистра
  Wire.write(writeBuf[1]);     // биты с 15 по 8 конфигурации
  Wire.write(writeBuf[2]);     // биты с 7 по 0 конфигурации
  Wire.endTransmission();  

  //delay(10);                    // 
 
 }

//-----------------------------------------------------------------------------------------------------------------------
  void filter_raw_calc_D () {                   // подпрограмма считывания и фильтрации значений с дифф входа 0-1 
  
  int sortedValues[Filter_read];                //назначили  массив с sortedValues[Filter_read] 
  for (int i = 0; i < Filter_read; i++) {       //цикл от 0 до Filter_read
      //delay(25);                              //                                                                    
      ads1115_config_Differential_0_1();        //идем на конфигурирование и старт одного измерения дифф входа 0-1
      read_config();                            //читаем конфиг и при чтении его ждем окончания преобразования
      read_ads_raw();                           //идем на чтение результата одного измерения raw ads 
      int value = raw ;                         //value получает значение одного измерения raw ads  
      int j;                                    //переменная "j" куда вставлять значение при сортировке
      if (value < sortedValues[0] || i == 0) {  //если полученое значение valve < sortedValues[0] или это самое начало "i",то 
         j = 0;                                 //"j"= 0 это первая позиция 
      }                                         //конец  if
       else {                                   //иначе
          for (j = 1; j < i; j++) {             //цикл j от 1 до j < i искать "j"
           if (sortedValues[j - 1] <= value && sortedValues[j] >= value) { //ищем место"j" в sortedValues[]куда вставить value
             break;                                                        //"j" найдено,остановить выполнение цикла
           }                                    //конец if
          }                                     //конец цикла j
       }                                        //конец else
         
       for (int k = i; k > j; k--) {            //все значения от "i" до "j" (вниз к--) нужно поднять на одну позицию вверх
       sortedValues[k] = sortedValues[k - 1];   //чтобы освободить место "j" для valve
       }                                        //конец цикла
       sortedValues[j] = value;                 //вставить считаное valve в sortedValues[] в позицию "j"
  }                                             //конец цикла от 0 до Filter_read
  
  
  
 //расчет среднего знач из Filter_get. Эти значения взяты после отброса лишних значений
  filter_raw = 0;
  for (int i = Filter_read / 2 - Filter_get/2; i < (Filter_read / 2 + Filter_get/2); i++) {
    filter_raw += sortedValues[i];
  }
  
}

//-----------------------------------------------------------------------------
 void filter_raw_calc_S () {                   // подпрограмма считывания и фильтрации значений с дифф входа 0-1 
  
  int sortedValues[Filter_read];                //назначили  массив с sortedValues[Filter_read] 
  for (int i = 0; i < Filter_read; i++) {       //цикл от 0 до Filter_read
      //delay(25);                              //                                                                    
      ads1115_config_Single_GND_3();            //идем на конфигурирование и старт одного измерения несимметр входа GND-3
      read_config();                            //читаем конфиг и при чтении его ждем окончания преобразования
      read_ads_raw();                           //идем на чтение результата одного измерения raw ads 
      int value = raw ;                         //value получает значение одного измерения raw ads  
      int j;                                    //переменная "j" куда вставлять значение при сортировке
      if (value < sortedValues[0] || i == 0) {  //если полученое значение valve < sortedValues[0] или это самое начало "i",то 
         j = 0;                                 //"j"= 0 это первая позиция 
      }                                         //конец  if
       else {                                   //иначе
          for (j = 1; j < i; j++) {             //цикл j от 1 до j < i искать "j"
           if (sortedValues[j - 1] <= value && sortedValues[j] >= value) { //ищем место"j" в sortedValues[]куда вставить value
             break;                                                        //"j" найдено,остановить выполнение цикла
           }                                    //конец if
          }                                     //конец цикла j
       }                                        //конец else
         
       for (int k = i; k > j; k--) {            //все значения от "i" до "j" (вниз к--) нужно поднять на одну позицию вверх
       sortedValues[k] = sortedValues[k - 1];   //чтобы освободить место "j" для valve
       }                                        //конец цикла
       sortedValues[j] = value;                 //вставить считаное valve в sortedValues[] в позицию "j"
  }                                             //конец цикла от 0 до Filter_read
  
  
  
 //расчет среднего знач из Filter_get. Эти значения взяты после отброса лишних значений
  filter_raw = 0;
  for (int i = Filter_read / 2 - Filter_get/2; i < (Filter_read / 2 + Filter_get/2); i++) {
    filter_raw += sortedValues[i];
  }
  
}

//-----------------------------------------------------------------------------

 void read_ads_raw(){
  raw = 0;
  
  
  readBuf[0] = 0;                      // Указатель регистра (Conversion register is 0b00000000; config register is 0b00000001,
                                       // Lo_thresh register is 0b00000010;Hi_thresh register is 0b00000011)т.е. указали Conversion 
                                    
  Wire.beginTransmission(ADR_ASD1115); // Начали читать данные из регистра преобразования 
  Wire.write(readBuf[0]);              // pointer указал в readBuf[0] записан 0 т.е. регистр преобразования
  Wire.endTransmission();

  Wire.requestFrom(ADR_ASD1115, 2);    // читаем из АЦП два байта
  readBuf[1] = Wire.read();  // 
  readBuf[2] = Wire.read();  // 
  Wire.endTransmission();  
  
                                     
  raw = readBuf[1] << 8 | readBuf[2];  // собираем два байта в кучу в переменную int val

  if (raw > 32768) raw = 0;
  
 
 } 
/*****************************************************************************************************************************/
  void Printscreen (){
    Serial.print((time_end-time_start)/1000);    // вывод потраченного времени
    Serial.print(" mS  :  Singl ");
    Serial.print  (float_voltsS,5);                    // Singl с двумя знаками
    Serial.print("  :  Diff ");
    Serial.println(float_voltsD,5);                    // Diff  с четырмя знаками
    Serial.println("");
    }  
/*****************************************************************************************************************************/

//подпрограмма для получения стринга UsrtS напряжения с точкой и со всеми значащими цифрами  
  void Find_UstrS(){
    
    // float_voltsS = -10.999 ;  
    // int x=0;
    // while (x <= 10000) {           
    // float_voltsS = float_voltsS + 0.01;
     
    
   char buffer [10];                                //буфер для преобразований Long-Char
   int k=0;                                         // разные временные цифры
   int i=0;                                         // разные временные цифры
      
   ltoa((abs(float_voltsS)+0.005) *100, buffer, 10);// забросили в char буффер цифры из volts  
   k=(strlen(buffer));                              // k это кол-во цифр в буффере т.е. в числе volts  и в том числе знак минус
      
   UstrS = "";                                      //  в Ustr начинаем собирать напряжение с точкой !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
   
   if  (float_voltsS < 0)   UstrS += "-";       //минус
   if  (float_voltsS >= 0)  UstrS += " ";       //плюс
   
   
   if (k == 4) {                                   // если есть 4 значащих цифр
     i=0;
     while (i <= (k-1)) {                          // из буфера берем K цифр и вставляем их в Ustr
     if (i == 2) UstrS += ".";                     // номер i определяет где вставить точку т.е измерение 4 вольт или 17 вольт
     UstrS += buffer [i];                          // 
     i++;
   }
   }
   
   if (k == 3) {                                  // если есть 3 значащих цифр
     UstrS += " ";
     i=0;
     while (i <= (k-1)) {                        // из буфера берем K цифр и вставляем их в Ustr
     if (i == 1) UstrS += ".";                   // номер i определяет где вставить точку т.е измерение 4 вольт или 17 вольт
     UstrS += buffer [i];                        // 
     i++;
   }
   }
   
   if (k == 2) {                                 // если есть 2 значащих цифр
     UstrS += " 0";
     i=0;
     while (i <= (k-1)) {                        // из буфера берем K цифр и вставляем их в Ustr
     if (i == 0) UstrS += ".";                   // номер i определяет где вставить точку т.е измерение 4 вольт или 17 вольт
     UstrS += buffer [i];                        // 
     i++;
   }
   }
   
   if (k == 1) {                                 // если есть 1 значащих цифр
     UstrS += " 0.0";
     i=0;
     while (i <= (k-1)) {                        // из буфера берем K цифр и вставляем их в Ustr
     //if (i == 0) UstrS += ".";                 // номер i определяет где вставить точку т.е измерение 4 вольт или 17 вольт
     UstrS += buffer [i];                        // 
     i++;
   }
   }
      
   /*Serial.print("float_voltsS=");
   Serial.print(float_voltsS);
   Serial.print("   UstrS=");
   Serial.print(UstrS);
   Serial.print("  : k=");
   Serial.println(k);
   */
   
   
   //x++;
   //  }
   //  delay(10000);
   
}
/*****************************************************************************************************************************/
//подпрограмма для получения стринга UsrtD напряжения с точкой и со всеми значащими цифрами  
  void Find_UstrD(){
    //float_voltsD = -1.9999 ;  
    //int x=0;
     //while (x <= 10000) {           
     //float_voltsD = float_voltsD + 0.001;
     
    
   char buffer [10];                                   //буфер для преобразований Long-Char
   int k=0;                                            // разные временные цифры
   int i=0;                                            // разные временные цифры
      
   ltoa((abs(float_voltsD)+0.00001) *10000, buffer, 10);     // забросили в char буффер цифры из volts  
   k=(strlen(buffer));                                 // k это кол-во цифр в буффере т.е. в числе volts  и в том числе знак минус
      
   UstrD = "";                                         //  в Ustr начинаем собирать напряжение с точкой !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
   
   if  (float_voltsD < 0)   UstrD += "-";       //минус
   if  (float_voltsD >= 0)  UstrD += " ";       //плюс
   UstrD += " ";                          //просто форматирование
   
   if (k == 5) {                                       // если есть 5 значащих цифр
     i=0;
     while (i <= (k-1)) {                              // из буфера берем K цифр и вставляем их в Ustr
     if (i == 1) UstrD += ".";                         // номер i определяет где вставить точку т.е измерение 4 вольт или 17 вольт
     UstrD += buffer [i];                              // 
     i++;
   }
   }
   
   if (k == 4) {                                       // если есть 4 значащих цифр
     UstrD += "0.";
     i=0;
     while (i <= (k-1)) {                             // из буфера берем K цифр и вставляем их в Ustr
     //if (i == 2) UstrD += ".";                      // номер i определяет где вставить точку т.е измерение 4 вольт или 17 вольт
     UstrD += buffer [i];                             // 
     i++;
   }
   }
   
   if (k == 3) {                                      // если есть 3 значащих цифр
     UstrD += "0.0";
     i=0;
     while (i <= (k-1)) {                             // из буфера берем K цифр и вставляем их в Ustr
     //if (i == 1) UstrD += ".";                      // номер i определяет где вставить точку т.е измерение 4 вольт или 17 вольт
     UstrD += buffer [i];                             // 
     i++;
   }
   }
   
   if (k == 2) {                                      // если есть 2 значащих цифр
     UstrD += "0.00";
     i=0;
     while (i <= (k-1)) {                             // из буфера берем K цифр и вставляем их в Ustr
     //if (i == 0) UstrD += ".";                      // номер i определяет где вставить точку т.е измерение 4 вольт или 17 вольт
     UstrD += buffer [i];                             // 
     i++;
   }
   }
   
   if (k == 1) {                                      // если есть 1 значащих цифр
     UstrD += "0.000";
     i=0;
     while (i <= (k-1)) {                             // из буфера берем K цифр и вставляем их в Ustr
     //if (i == 0) UstrD += ".";                      // номер i определяет где вставить точку т.е измерение 4 вольт или 17 вольт
     UstrD += buffer [i];                             // 
     i++;
   }
   } 
   
   /*Serial.print("float_voltsD=");
   Serial.print(float_voltsD,4);
   Serial.print("   UstrD=");
   Serial.print(UstrD);
   Serial.print("  : k=");
   Serial.println(k);
    
   x++;
     }
     */
  }
 /****************************************************************************************************************************/ 
 void LCDout(){
  lcd.setCursor(0, 0);        // начали вывод напряжения на LCD
  lcd.print("S "); 
  lcd.print(UstrS);
  lcd.print("     bat");
  
  lcd.setCursor(0, 1);        // начали вывод во вторую строку LCD
  lcd.print("D "); 
  lcd.print(UstrD);
  lcd.print("   ");
  lcd.print(UbatProcStr);       //напряжение батарей в процентах
 }
 /*********************************************************************************************************************/
 void OFout(){               // подпрограмма вывода данных на USB порт для логирования в LOGVIEW
  Serial.print("$1;1;0;"); 
  Serial.print(float_voltsS,2);
  Serial.print(";");
  Serial.print(float_voltsD,4);
  Serial.print(";0"); 
  Serial.println(13,DEC); 
 }
 /*******************************************************************************************/
 
sls
Offline
Зарегистрирован: 30.05.2016

mikelari пишет:

Схема модуля http://www.dropbox.com/s/qtsn966we7rpmny/PCB%20schematic.pdf?dl=0

Vin1 измеряет до 4.096 В

Vin2 измеряет до 17 вольт (предварительно замкнуть перемычку JP2)

тоже брал такую платку. опора там никуда не годится, менял на даташитную lt1021 на проводках, затем стало проще переразвести плату.

на ltc2400 интересный проект милливольтметра есть https://www.youtube.com/watch?v=CiTPUmqE3Yg

mikelari
Offline
Зарегистрирован: 14.05.2015

Купил опору TI LM4040 A41IDBZR 4.096V 0.1%, (дорогие заразы) тоже хочу попробовать поменять на этом модуле, чтоб модуль использовать  для милливольтметра/миллиомметра.

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

да не так чтобы очень с учетом какая точность требуется

sls
Offline
Зарегистрирован: 30.05.2016

mikelari пишет:
Купил опору TI LM4040 A41IDBZR 4.096V 0.1%, (дорогие заразы) тоже хочу попробовать поменять на этом модуле, чтоб модуль использовать  для милливольтметра/миллиомметра.

по-моему lm4040 не очень сюда годится с ее 100ppm/C

это, конечно, лучше, чем то, что в платку поставили китайцы, но гораздо хуже, чем недорогие (ну, относительно) ref198/lt1021, не говоря уже о lt1027/max6341

dimax как-то Вы пессимистично смотрите на шум линейников.

у наиболее частого случая, древней 7815 Output Noise Voltage B =10Hz to 100KHz TJ = 25°C - max 40 μV/VO, это 600мкВ на 15В выходного напряжения, но никак не десятки миллиВольт.

 

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

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

sls
Offline
Зарегистрирован: 30.05.2016

dimax пишет:

sls, это собственный шум голой микрухи, А я имел ввиду средний суммарный уровень помех линейных бп.

в общем-то у линейников основной источник шума и есть ИОН, далее по общему вкладу идет делитель напряжения с него, остальные части схемы шумят мало

предлагаю помириться на том, что для нормально спроектированного и собранного линейного ЛБП в случае необходимости вполне можно использовать измеритель напряжения с тремя цифрами после запятой, но никак не одной :)

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

sls, да я не против, сам люблю попугаев за запятой. Как говорится:  "Ах, обмануть меня не трудно, я сам обманываться рад..."(С) Пушкин  :-)

sls
Offline
Зарегистрирован: 30.05.2016

dimax, я их тоже полюбляю, в особенности стабильные и достоверные :)

nicelight
Offline
Зарегистрирован: 20.09.2016

Добрый день, собрал измеритель напряжения.

Врет при увеличении напряжения, погрешность достигает 0,12 вольта..

На ареф замедены опорные 2,5 В с REF192FS ( пальцем ее обозначил). Подключил по datasheet, опорное напряжение стабильно во всем диапазоне измеряемых напряжений.

в скетче указал, что analogReference ( EXTERNAL )

Питается хозяйство от импульсного стабилизатора. напряжение снимается с делителя 1к  и 14  к ( тоже пальцами указал). До этого пробовал 430 к и 36 к , такой же результат. 

Рассчет происходит тупо умножением измеренного напряжения на экспериментально подобранный коэффициент

Что я могу делать не так?