вольтметр переменного напряжения

DimaP.
Offline
Зарегистрирован: 21.04.2013

 

Решил поделится весьма скудными знаниями по созданию вольтметра переменного напряжения от 1 до 300 вольт. Заранее хочу выразить благодарность всем кто помог мне в этом нелегком деле!
материалы которые помогли разобратся привожу в виде ссылок:
1http://samou4ka.net/page/tajmer-schetchik-mikrokontrollerov-avr //тема о настройке таймеров
2http://samou4ka.net/page/analogo-cifrovoj-preobrazovatel-mk-atmega8 // настройка АЦП
3http://www.stepwood.com/avrsuite/2012/10/03/atmega-rezhimyi-rabotyi-taymerov/ еще немного о таймерах
4http://arv.radioliga.com/content/view/107/43/ про измерение напряжения теория
5http://cxem.net/izmer/izmer90.php полезная ссылочка
В этих статьях описанно как использовать Таймеры и АЦП управляя ими напрямую!

Итак сам вольтметр состоит из Аналоговой и цифровой части, В качестве АЦП применен встроенный преобразователь с разрешением в 10 бит микроконтроллера ATmega328 установленный на плату Arduino MIni. Я использовал на нем 2 аналоговых входа из восьми возможных!

Аналоговая часть состоит из2 ОУ схема достаточно простая и взята она от китайского стабилизатора, на 1 ОУ реализуется уменьшение 5 вольтового напряжения до уровня 2 вольта, второй через делитель напряжения получает входную величину и поднимает ее относительно 2 вольт. Очень важно выбрать хороший ОУ я применил MCP617 (По непонятной причине LM324 совершенно не подходит для этой схемы появляется двоение синусоиды) И желательно использовать сопративление ряда Е96 +-1%. Переменный резистор многооборотный 10кОм.

Можно было использовать схему приведенную ниже, но если захотите контролировать несколько фаз эта схема не пойдет и проблемы с ней обсуждались в этой теме.http://arduino.ru/forum/apparatnye-voprosy/kak-borotsya-s-dannym-yavleniem , некоторые люди пишут что применение диодов вносит погрешности при измерении чесно говоря я их не заметил, но это не говорит о том что их нет.

Цифровая часть написана в ArduinoIDE. Алгоритм измерения следеюший каждые 0.3 секунды запускается таймер с частотой прерывания 5000Гц(использован 8 битный таймер 2) следовательно если брать частоту 50 Гц то период равен 20мсек, за данный период в обработчике прерывания сохраняем 100 выборок напряжения в массив. после того как программа собрала 100 значений запрещаем прерывание и расчет переходит в основном цикле LOOP/
В цикле for происходит математические вычисления(вычитание подъема синусоиды на 2 вольта, возведение в квадрат и сложение этих значений) согласно вот этой теории :
по завершению цикла происходит вычисление реальной величины напряжения.
Переменная coef расчитывается с учетом напряжения делителя , я ее подбирал эксперементально, сравнивал измеренную величину и подаваемую источником переменного тока, и эту разницу выравнивал коэфициентом!
значение я выводил по сериал соединению!



* переменные работающие в обработчике прерывания */
volatile int Umass_A[101]; //масив переменных для хранения мгновенных напряжений фазы А


int Ucor = 0;

long Uism_A = 0; // переменная для хранения измеренного напряжения и квадрата фазы А
long Usumm_A = 0; // переменная для хранения сумм квадратов фазы А 



volatile byte counter = 0; // счетчик в обработчике прерывания


int ADC0 = 0; // аналоговый вход 0 для переменной Ucor!
int ADC1 = 1;


#define ADC1    1  



volatile byte flag = 0;
/* переменные для усреднения напряжений*/



float sqrtUsum_A = 0;


int real_U_A = 0;


float coff = 0.138;


unsigned long timeOut = 0;// переменная для хранения времени!!!

//массивы


// кнопки и светодиоды

void setup()  
{ 
    
    TIMSK2 = 0b00000000;         // запрещение прерывания по совпадению таймера/счетчика Т2  
    TCCR2A = 0b00000010;       // режим работы СТС
    TCCR2B = 0b00000011;     // предделитель на 32
    ASSR &= ~(1<<AS2);    // Выбор источника синхронизации таймера(от системного генератора
    OCR2A = 98;                                     
               // срабатывание таймера 16000000/32/100=5000 раз в секунду 100 раз за секунду
    
    ADMUX = (0<<REFS1)|(1<<REFS0)|(0<<ADLAR)|(0<<MUX3)|(0<<MUX2)|(0<<MUX1)|(0<<MUX0);
    ADCSRA = 0b10000111;
    Serial.begin(9600); 
     
  
}


void loop()
{

   
   if(millis()-timeOut > 400)
     {
       timeOut = millis();
       
         metod();
       
      }
   
   
   if(flag == 3)
   {
     for ( int i = 1; i<101; i++)
     {  
       Uism_A =  Umass_A [i];
       Uism_A -= Ucor;// убираем подьем синусоиды на 2 вольт
       
       Uism_A *= Uism_A;
       Usumm_A += Uism_A;
    
       
      }
      sqrtUsum_A = sqrt(Usumm_A); //вычисляем квадратный корень из суммы квадратов
      real_U_A = coff * sqrtUsum_A; //вычисляем реальное напряжение для фазы А
    
      
       Usumm_A = 0;
       counter = 0;
       flag = 0;
       
       Serial.println(real_U_A);

   }
}
   
  
   
  


 

void metod()
{
   Ucor = analogRead(ADC0);
   
   TIMSK2 |= (1<<OCIE2A); // разрешаем прерывание
   while(flag<3);
   TIMSK2 = 0b00000000; // останавливаем таймер     
}



word read_adc(byte adc_input)
{
         ADMUX = adc_input | (ADMUX & 0xF0);
//задержка для стабилизации входного напряжения
   
//начинаем преобразование (ADSC = 1)
         ADCSRA |= (1<<ADSC);
         while((ADCSRA & 0x10)==0); //ждем, пока АЦП закончит преобразование (ADIF = 0)
        ADCSRA|=0x10;//устанавливаем ADIF
        return ADCW;//ADCW - содержит ADCH и ADCL как нам нужно
}
	 
	//****************обработчик прерывания********************
	
ISR(TIMER2_COMPA_vect) 
	
{  
  counter++;
  if((counter<= 100)&&flag == 0)
  {
    Umass_A[counter] = read_adc(ADC1);  
    if(counter == 100)
    {
      flag = 3;
      counter = 1;
    }
  }

}           

вообщем получилось достаточно неплохо точность около 1%
Вот в принципе и все!

zhuki
Offline
Зарегистрирован: 12.10.2011

Интерес к измерениям переменного тока есть. Но,хотелось бы замерять значения не самим ацп Ардуино ,а внешним ацп . Например

в щитке ввода в дом ,а затем данные передавать в Ардуино. Тогда возникает вопрос как делать выборки.

И ещё ,почему для приведения к уровню вместо делителя  не использовать трансформатор с выпрямителем. Всё таки развязка. 

DimaP.
Offline
Зарегистрирован: 21.04.2013

zhuki пишет:

. Тогда возникает вопрос как делать выборки.

 

А что вам мешает в обработчике прерывания опрашивать не собственный АЦП, а внешний АЦП который будет подключен по SPI? Я думаю все вполне реализуемо.

А вот по поводу гальванической развязки это хороший вопрос!!!

http://www.microchip.su/showthread.php?t=2788&highlight=%C8%E7%EC%E5%F0%E5%ED%E8%E5+%EF%E5%F0%E5%EC%E5%ED%ED%EE%E3%EE

вот на этом форуме очень оживленно обсуждали данный вопрос, Один утверждал что смог сделать трансформатор из гайки М8 и первичную обмотку подключил через конденсатор! и это дало неплохие результаты, Но лично мои эксперементы с трансформатором дают странные рзультаты, изменяя напряжение с помощью ЛАТРа я заметил некоторую нелинейность т.е подаю с латра 100 вольт мой вольтметр показывает 110, увеличиваю до 200 показывает 197, и я таки не разобрался в чем было дело!

 

Michal
Michal аватар
Offline
Зарегистрирован: 26.04.2013

zhuki пишет:

И ещё ,почему для приведения к уровню вместо делителя  не использовать трансформатор с выпрямителем. Всё таки развязка. 

а чем вам плечо в 3МОм не развязка... да и трасформаторы это увеличение размера устройства. А в таком смд исполнении датчик напряжения на ОУ ~2-3см2 на плате.

DimaP.
Offline
Зарегистрирован: 21.04.2013

Michal пишет:

zhuki пишет:

И ещё ,почему для приведения к уровню вместо делителя  не использовать трансформатор с выпрямителем. Всё таки развязка. 

а чем вам плечо в 3МОм не развязка... да и трасформаторы это увеличение размера устройства. А в таком смд исполнении датчик напряжения на ОУ ~2-3см2 на плате.

У меня даже меньше получилоссь, если еще и оу взять смд!

Master67
Offline
Зарегистрирован: 09.01.2014

Хорошая статья о измерении переменного напряжения http://512volt.ru/s2.htm

DimaP.
Offline
Зарегистрирован: 21.04.2013

Да статья то хорошая но есть одно но, цена LTC1968IMS8 примерно 500 рублей, а если захотим измерять 3 фазы это уже 1500 не считая остальной мелочевки и контроллера!

a5021
Offline
Зарегистрирован: 07.07.2013

Лайнер обычно не дешев. Не полторы, конечно, тыщи (у китайцев за 850 руб можно пять штук взять и это включая стоимость доставки), но все равно ощутимо. Можно погуглить аналоги по сочетанию rms-to-dc -- там много чего в выдаче найти можно.

DimaP.
Offline
Зарегистрирован: 21.04.2013

a5021 пишет:
Лайнер обычно не дешев. Не полторы, конечно, тыщи (у китайцев за 850 руб можно пять штук взять и это включая стоимость доставки), но все равно ощутимо. Можно погуглить аналоги по сочетанию rms-to-dc -- там много чего в выдаче найти можно.

полторы тысячи это я посчитал для 3 фаз!! гуглил по теме самый дешевый лайнер без учета доставки стоит 8.9$ ну это по первой попавшейся ссылке.

Это в принципе не дорого!!

Arduino
Offline
Зарегистрирован: 12.05.2015

DimaP. пишет:
Решил поделится весьма скудными знаниями по созданию вольтметра переменного напряжения от 1 до 300 вольт.

DimaP, спасибо за труды. Однако, у меня схемка вроде как не рабочая. По крайней мере, кручу я подстроечный резистор на 10К - и на DSO138 (типа осцилограф сделай сам) не наблюдаю никаких изменений. Вообще никаких. Замер делал между ADC1 и ADC0, и между ADC1 и GND и между ADC0 и GND. Может еще что-то забыли указать? Применил тоже, как и увас MCP617 - я так понял это два ОУ в одном корпусе.

Arduino
Offline
Зарегистрирован: 12.05.2015

DimaP, забыл я просто подать питание на ОУ, вот же... Теперь все заработало, еще раз спасибо! Единственное не могу понять, как лучше переделать схему под 3 фазы. Ведь получается, что ноль то общий.

DimaP.
Offline
Зарегистрирован: 21.04.2013

возможно вам поможет, коментировал что мог по максимуму, измерение 3 фаз, + реакция при выходе из установленных пределов...

схема устройства не сохранилась, только микропрограмма. Наверное если сесть и вниматильно изуть код то можно понять как реализованна аппаратная часть, точно знаю что железная часть для считывание фаз напряженийреализованна с помощью картинки из поста №1 картинка 1...

#include <EEPROM.h>
#include <TimeHelpers.h> //библиотека для работы со временем
#include <LiquidCrystal_I2C.h> // Подключаем стандартную библиотеку LiquidCrystal
#include <Wire.h> // библиотека дря работы I2C
#include <DS1307.h> //Подключение библиотеки для DS1307
LiquidCrystal_I2C lcd(0x27,16,2);  // set the LCD address to 0x27 for a 16 chars and 2 line display

inline word read_adc(byte adc_input); //прототипы функций
int matimatics(long Usumm, double coff);
void EEPROMWriteInt(int p_address, word p_value);
word EEPROMReadInt(int p_address);

/* переменные работающие в обработчике прерывания */
volatile long Uism_L1; // измеренное напряжение для фазы 1
volatile long Usumm_L1; // суммированное напряжение для фазы 1
volatile long Uism_L2; // измеренное напряжение для фазы 2
volatile long Usumm_L2;// суммированное напряжение для фазы 2
volatile long Uism_L3; // измеренное напряжение для фазы 3
volatile long Usumm_L3;// суммированное напряжение для фазы 3
volatile byte flag = 0; // флаг для обрабтчика прерывания
volatile byte counter = 0; // счетчик в обработчике прерывания






//каналы для чтения функцией АЦП
#define ADC1    0  
#define ADC2    1
#define ADC3    2
#define ADC4    A3
#define ADC5    A6
#define ADC6    A7


//здесь задаютя значениявижержки времени для работы "TimeHelpers"
#define TIME_OUT_INTERVAL 350UL//интервал измерения фаз напряжений
#define TIME _SEC_       (1) //время равное 1 секунде
#define TIME_1            300UL // выдержка 300 мсек
#define TIME_SEVED        10000UL // выдержка 10 сек
#define BLINK_INTERVAL    500UL // для мигалки

int Ucor_L1 = 0; /*напряжения подъема синусоиды на 2 вольта*/
int Ucor_L2 = 0; /*напряжения подъема синусоиды на 2 вольта*/
int Ucor_L3 = 0; /*напряжения подъема синусоиды на 2 вольта*/
//переменные конечного значения напряжения по фазам 1. 2. 3
int real_U_L1 = 0;
int real_U_L2 = 0;
int real_U_L3 = 0;
//коэффициенты для расчетов среднеквадратического значения напряжения
double coff_L1 = 0;
double coff_L2 = 0;
double coff_L3 = 0;

// переменные для работы с eeprom
word ee_readL1 = 1400;
word ee_readL2 = 1400;
word ee_readL3 = 1400;


//массивы DATA сохраненных значений
byte mass_MIN[10];
byte mass_HR[10];
byte mass_DATE[10];
byte mass_MTH[10];
byte store_Fider_[10];
int store_U_L1[10];
int store_U_L2[10];
int store_U_L3[10];

//работа с lcd
char stroka[16];



// кнопки и светодиоды
const byte buttEnter = 13;// кнопка интер
const byte buttDown = 12; // кнопка для прокрутки вниз
const byte buttUp = 11;// кнопка для прокрутки вверх
const byte light = 10;//кнопка подсветки
const byte fider = 7;//индикация фидеров

#define ledRed  9 //светодиод наличия данных в памяти
#define ledGreen  8 // светодиод нормальной работы

/*разного рода переменные*/


//boolean waitTime = false; // выдержка времени для того чтобы не сыпалось много значений 
boolean flag_red_stop = false;// данный флаг нужен для запрета горящего класного светодиода когда идет выжержка времени
boolean downFlag = false; //flag кнопки интер
boolean delFlag = false; //flag кнопки вниз
boolean upFlag = false; //flag кнопки вверх
boolean off_light_flag = false;
boolean set_flag = false;
boolean ee_write_flag = false;
boolean flag_mig = false;
boolean tti = false;

byte value_delite = 0; //для выдержки времени при удалении
byte timeSet = 0;// время для выключения реле
byte off_light = 0;// выдержка на выключение подсветки LCD
byte massVal = 0; //  переменная для сохранения данных и определения индекса массивов
byte scroll = 1; //переменная для прокручивания результатов
byte value_switch = 0;//ДЛЯ ВЫДЕРЖКИ ВРЕМЕНИ НА ПОДАЧУ КОМАНДЫ ПЕРЕКЛЮЧИТЬ ФИДЕР
byte Timer123 = 0;
byte stateFider = 0;
byte inter_sel = 0;
byte inter_ON = 0;
byte set_regim = 0;// для настройки режимов

int enterRegim = 1; // переменная режима кнопки enter
int Uset = 0; //напряжение в режиме setings 

word set_coef = 1400;//переменная для настройки коэффициента

#define dell 10000UL //константа для деления напрядения и получения коэффициента
//значения превышенного и заниженного напряжения
#define Hi 242
#define Low 198


void setup()  
{ 
    pinMode(buttEnter, INPUT);
    pinMode(buttDown, INPUT);
    digitalWrite(buttDown, HIGH);
    pinMode(buttUp, INPUT);
    digitalWrite(buttUp, HIGH);
    pinMode(light, INPUT);
    digitalWrite(light, HIGH);
    pinMode(fider, INPUT);
    digitalWrite(fider, HIGH);
    
    pinMode(ledRed, OUTPUT);
    pinMode(ledGreen, OUTPUT);
   
    
    
    
    TIMSK2 = 0b00000000;         // запрещение прерывания по совпадению таймера/счетчика Т2  
    TCCR2A = 0b00000010;       // режим работы СТС
    TCCR2B = 0b00000011;     // предделитель на 32
    ASSR &= ~(1<<AS2);    // Выбор источника синхронизации таймера(от системного генератора
                                         
    OCR2A = 99;           // срабатывание таймера 16000000/32/100=5000 раз в секунду 100 раз за секунду
    
    ADMUX = (0<<REFS1)|(1<<REFS0)|(0<<ADLAR)|(0<<MUX3)|(0<<MUX2)|(0<<MUX1)|(0<<MUX0);
    ADCSRA = 0b10000111;
    lcd.init();                 
   
   
    ee_readL1 = EEPROMReadInt(6);//переменные читаемые занимают 2 байта
    coff_L1 = float(ee_readL1)/dell;// записываемс коеф для 1 фазы
    ee_readL2 = EEPROMReadInt(8);// поэтому здесь читаем с 2 байта
    coff_L2 = float(ee_readL2)/dell;// записываемс коеф для 2 фазы
    ee_readL3 = EEPROMReadInt(10); // читаем из еепрома коэффициент
    coff_L3 = float(ee_readL3)/dell;// записываемс коеф для 3 фазы
// Serial.begin(9600);
}

/*+++++++++++++++++++++++++++++++++++++++++*/
/*
  EEPROMWriteInt(0, 65535); //занимаються ячейки 0 и 1
  // 65535 максмальное значение которое можно записать в 2 байта

  Serial.print("Read the following int at the eeprom address 0: ");
  Serial.println(EEPROMReadInt(0)); */

// функция для записи/ чтения памяти EEPROM

//кушаем аж 2 байта EEPROM
void EEPROMWriteInt(int p_address, word p_value)
{
  byte lowByte = ((p_value >> 0) & 0xFF);
  byte highByte = ((p_value >> 8) & 0xFF);
  EEPROM.write(p_address, lowByte);
  EEPROM.write(p_address + 1, highByte);
  ee_write_flag = false;
}

// считаем нашы два байта
word EEPROMReadInt(int p_address)
{
  byte lowByte = EEPROM.read(p_address);
  byte highByte = EEPROM.read(p_address + 1);
  return ((lowByte << 0) & 0xFF) + ((highByte << 8) & 0xFF00);
}

void loop()
{

   DO_EVERY(TIME_OUT_INTERVAL,{
   Ucor_L1=analogRead(ADC4);
   Ucor_L2=analogRead(ADC5);
   Ucor_L3=analogRead(ADC6);  
   TIMSK2 |= (1<<OCIE2A);      }) // разрешаем прерывание через каждые 300 мСек и попутно читаем напряжение подъема синусоиды
/*--------------------------------------------------------------------*/ 



   if(flag == 3)// этоот флаг устанавливается в обработчике прерывания разрешая тем самым расчет реальныз величин U
   {
   
      real_U_L1 = matimatics(Usumm_L1, coff_L1);//расчет реальныз величин U1
      real_U_L2 = matimatics(Usumm_L2, coff_L2);//расчет реальныз величин U2
      real_U_L3 = matimatics(Usumm_L3, coff_L3);//расчет реальныз величин U3
      counter = 0;//сбрасываем счетчик
      flag = 0;//сбрасываем флаг
      /* сбрасываем суммы напряжений*/
      Usumm_L1 = 0;
      Usumm_L2 = 0;
      Usumm_L3 = 0;
   }
/*-----------------------------------------------------------------------*/    
//здесь мы прроверяем последовательно каждую фазу на предмет завышения или занижения напряжения
if((real_U_L1 >= Hi || real_U_L1 <= Low) || (real_U_L2>= Hi || real_U_L2 <= Low) || (real_U_L3 >= Hi || real_U_L3 <= Low ))
{
  digitalWrite(ledGreen, LOW);                                       
   if(tti == false)
   {
     flag_red_stop = true;
     massVal++;
     mass_safe();
     tti = true;
     if(massVal>10){massVal = 1;}
   }
}
/*--------------------------------------------------------------------*/
 if(massVal >= 1 && flag_red_stop == false)
{
 digitalWrite(ledRed, HIGH);
} 
else if (massVal == 0)
{
 digitalWrite(ledRed, LOW);
 digitalWrite(ledGreen, HIGH);
}

/*--------------------------------------------------------------------*/  

/*--------------------------------------------------------------------*/

  if (off_light_flag == true)
  {
   DO_EVERY(TIME, off_light++;)
  }
  if(off_light >= 20)
  {
    off_light_flag = false;
    lcd.noBacklight();
    off_light = 0;
   
  }
  
  
  if(flag_red_stop == true)
  {
    LED_mig();
  }
/*--------------------------------------------------------------------*/
button_prog();
ee_write_flag = false;
}

void button_prog(void)//Работаем с кнопками и режимами управления, а также включением и выключением подсветки LCD
{
  if(digitalRead(buttEnter) == LOW) //если нажата кнопка интер и не нажата кнопка прокрутки 
{
  DO_EVERY(TIME, inter_sel++;)
  DO_EVERY(TIME_1, inter_ON++;)
} 
if(digitalRead(buttEnter) == HIGH)
{
  inter_sel = 0;
  inter_ON = 0;
}
if(inter_ON >= 1){ee_write_flag = true; inter_ON =0; lcd.clear(); }
if(inter_sel >= 2)
{
   inter_sel = 0;  
   enterRegim++; //увеличиваем переменную  режимов на 1 
   lcd.clear();
   if(enterRegim > 2){ enterRegim = 1; }//если больше 2 режимов переходим к первому
}


if(digitalRead(buttDown) == LOW && digitalRead(buttUp) == LOW)
{
   DO_EVERY(TIME, timeSet++;)
 }
else {timeSet = 0;}
  if(timeSet >= 5)
  {
    timeSet = 0;
    enterRegim = 0;
  }
  

switch(enterRegim)
{
case -1: delite(); break;  
case 0: setings(); break;
case 1: standBy(); break;
case 2: data_save(); flag_red_stop = false; break;

}

if(digitalRead(light) == LOW)
{
  lcd.backlight();
  off_light_flag = true;
  DO_EVERY(TIME, value_delite++;)
  
}
else 
  {
   value_delite = 0;
  }
if(value_delite >=5)
  {
    enterRegim = -1;
     lcd.clear();
     value_delite = 0;
  }
  
  switch(digitalRead(fider)){
     case 0 : stateFider = 1;  break;
     case 1 : stateFider = 2;  break;
   }
 
   // выдержка времени для ожидания между сохранениями результатов
if(tti == true)
{
  DO_EVERY(TIME_SEVED, Timer123++;)
 //Serial.println(Timer123); 
}

if(Timer123 >= 6)
{
  tti = false;
  Timer123 = 0; 
}
 
} 
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-=-=-=-=*/
void delite (void)
{
  if(digitalRead(buttDown) == LOW && downFlag == false)//нажали кнопку вниз
  {
    downFlag = true;
    delFlag = ~delFlag;
    lcd.clear();
  }
  if(digitalRead(buttDown) == HIGH && downFlag == true){downFlag = false;}//отпустили кнопку вниз
  
  if(digitalRead(buttUp) == LOW && upFlag == false)//нажали кнопку вверх
  {
    upFlag = true;
    delFlag = ~delFlag;
    lcd.clear();
  }
  if(digitalRead(buttUp) == HIGH && upFlag == true){upFlag = false;}//отпустили кнопку вверх
  
   lcd.setCursor(0, 0);
   lcd.print("Delete DATA Yes");
   lcd.setCursor(0, 1);
   lcd.print("            No ");  
   
  if(delFlag==false)
 {
  blink_simbol(0);
   if(ee_write_flag == true)
  { 
    massVal = 0;
    value_delite = 0;
    scroll = 1;
    tti = false;
    enterRegim = 1;
    ee_write_flag = false;
    flag_red_stop = false;
  }
 }
  else{
   blink_simbol(1); 
   if(ee_write_flag == true)
  { 
    enterRegim = 1;
    ee_write_flag = false;
  }

 }
 
}

void blink_simbol(byte x)
{
  lcd.setCursor(15, x);
  DO_EVERY(BLINK_INTERVAL, flag_mig = ~flag_mig;)
  if(flag_mig == false){lcd.print("<");}
  else {                lcd.print(" ");} 
}
/*==============================================================================*/
void mass_safe(void)//сохраняем время и напряжение в масссив, пока сохраняем просто в RAM в дальнейшем будет во флеш
{
  mass_MIN[massVal] = RTC.get(DS1307_MIN,false);
  mass_HR[massVal] = RTC.get(DS1307_HR,true);
  mass_DATE[massVal] = RTC.get(DS1307_DATE,false);
  mass_MTH[massVal] = RTC.get(DS1307_MTH,false);
  store_Fider_[massVal] = stateFider;
  store_U_L1[massVal] = real_U_L1;
  store_U_L2[massVal] = real_U_L2;
  store_U_L3[massVal] = real_U_L3;
}

/*функция вычисления напряжения*/
int matimatics(long Usumm, double coff)
{ 
      int real_U_int;
      double real_U;
      double sqrtUsum;
      sqrtUsum = sqrt(Usumm); //вычисляем квадратный корень из суммы квадратов
      real_U = coff * sqrtUsum; //вычисляем реальное напряжение для фазы 
      real_U_int = int(real_U);
      return real_U_int;
}

/*==============================================================================*/
void LED_mig(void)
{
 DO_EVERY(BLINK_INTERVAL,digitalWrite(ledRed,!digitalRead(ledRed)));
}

/*==============================================================================*/  
void standBy(void)
{
  
 lcd.setCursor(0,0);
  sprintf (stroka, "%02d.%02d.%4d/%02d:%02d", RTC.get(DS1307_DATE,false), RTC.get(DS1307_MTH,false),
  RTC.get(DS1307_YR,false), RTC.get(DS1307_HR,true), RTC.get(DS1307_MIN,false));
  lcd.print(stroka);
  
  /*выводим состояния напряжений пофазно*/
  // печатаем вторую строку
   
  lcd.setCursor(0, 1);
  sprintf (stroka, "%3d|%3d|%3d|%1d%3s", real_U_L1, real_U_L2,  real_U_L3 ,
  stateFider, "Fid");
  lcd.print(stroka);
  
}

void data_save(void)
{
  if(digitalRead(buttDown) == LOW && downFlag == false)
  {
    downFlag = true;
    scroll++;
    lcd.clear();
    if(scroll > massVal) { scroll = 1; }
  }
  if(digitalRead(buttDown) == HIGH && downFlag == true)
  {
    downFlag = false;
  }
  if(digitalRead(buttUp) == LOW && upFlag == false)
  {
    upFlag = true;
    scroll--;
    lcd.clear();
    if(scroll < 1) { scroll = massVal; }
  }
  if(digitalRead(buttUp) == HIGH && upFlag == true)
  {
    upFlag = false;
  }
 if(massVal >= 1)
 {
   
   lcd.setCursor(0, 0);
   sprintf(stroka, "%02d. %02d.%02d %02d:%02d", scroll, mass_DATE[scroll], mass_MTH[scroll], 
   mass_HR[scroll], mass_MIN[scroll]);
   lcd.print(stroka);
  
  
   lcd.setCursor(0, 1);
   sprintf(stroka, "%3d|%3d|%3d|%1d%3s", store_U_L1[scroll], store_U_L2[scroll],
   store_U_L3[scroll], store_Fider_[scroll], "Fid");
   lcd.print(stroka);
   
}

 else
 {
  lcd.setCursor(0, 0);
  lcd.print("No DATA,to press");
  lcd.setCursor(0, 1);
  lcd.print( "the button ENTER");
 }

  
}

/*__________________________________________*/

void setings (void)
{
 
  
  if(digitalRead(buttDown) == LOW && downFlag == false)//нажали кнопку вниз
  {
    downFlag = true;
    set_coef--;
  }
  if(digitalRead(buttDown) == HIGH && downFlag == true)//отпустили кнопку вниз
  {
    downFlag = false;
  }
  if(digitalRead(buttUp) == LOW && upFlag == false)//нажали кнопку вверх
  {
    upFlag = true;
    set_coef++;
    
  }
  if(digitalRead(buttUp) == HIGH && upFlag == true)//отпустили кнопку вверх
  {
    upFlag = false;
  }
  
  if(digitalRead(light) == LOW&&set_flag==false)
{
  set_regim++;
  set_flag=true;
  lcd.clear();
  if(set_regim > 3)
{
  enterRegim = 1;
  set_regim = 0;
}
  
}
  if(digitalRead(light) == HIGH && set_flag == true){set_flag = false;}
  
  
  switch(set_regim)
  {
    case 1: 
    coff_L1 = float(set_coef)/dell;
    Uset = real_U_L1;
    if(ee_write_flag == true)
    {
      EEPROMWriteInt(6, set_coef); //занимаються ячейки 0 и 1
      // 65535 максмальное значение которое можно записать в 2 байта
    }
    break;
    case 2:
    if(ee_write_flag == true)
    {
      EEPROMWriteInt(8, set_coef); //занимаються ячейки 0 и 1
      // 65535 максмальное значение которое можно записать в 2 байта
    }
    coff_L2 = float(set_coef)/dell;
    Uset = real_U_L2;
    break;
    case 3: 
    if(ee_write_flag == true)
    {
      EEPROMWriteInt(10, set_coef); //занимаються ячейки 0 и 1
      // 65535 максмальное значение которое можно записать в 2 байта
    }
    coff_L3 = float(set_coef)/dell;
    Uset = real_U_L3;
    break;
  }
  
  lcd.setCursor(0, 0);
  lcd.print("     SETINGS    ");
  lcd.setCursor(0, 1);
  sprintf(stroka, "%1s%1d=%03d  %3s=%4d", "L", set_regim, Uset, "cof", set_coef);
  lcd.print( stroka);
  
}

inline word read_adc(byte adc_input)
{
   ADMUX = adc_input | (ADMUX & 0xF0);
   delayMicroseconds(10);//задержка для стабилизации входного напряжения
   ADCSRA |= (1<<ADSC);//начинаем преобразование (ADSC = 1)
   while((ADCSRA & 0x10)==0); //ждем, пока АЦП закончит преобразование (ADIF = 0)
   ADCSRA|=0x10;//устанавливаем ADIF
   return ADCW;//ADCW - содержит ADCH и ADCL как нам нужно
}
//****************обработчик прерывания********************
	
ISR(TIMER2_COMPA_vect) 
	
{  
  counter++;
  if((counter<= 100)&&flag == 0)
  {
       Uism_L1 = read_adc(ADC1); 
       Uism_L1 -= Ucor_L1;// убираем подьем синусоиды на 2 вольт
       if(Uism_L1 > -5 && Uism_L1 < 5){Uism_L1=0;}
       Uism_L1 *= Uism_L1;// возводим значение в квадрат
       Usumm_L1 += Uism_L1;// склдываем квадраты измерений
     
    if(counter == 100)
    {
      flag = 1;
      counter = 1;
    }
  }
 if((counter<=100)&&flag == 1)
 {
       Uism_L2 = read_adc(ADC2);
       Uism_L2 -= Ucor_L2; // убираем подьем синусоиды на 2 вольт
       if(Uism_L2 > -5 && Uism_L2 < 5){Uism_L2=0;}
       Uism_L2 *= Uism_L2;// возводим значение в квадрат
       Usumm_L2 += Uism_L2; // склдываем квадраты измерений
   if(counter == 100)
   {
     flag = 2;
     counter = 1;
   }
 }
 if((counter<=100)&&flag ==2)
 {
       Uism_L3 = read_adc(ADC3);
       Uism_L3 -= Ucor_L3; // убираем подьем синусоиды на 2 вольт
       if(Uism_L3 > -5 && Uism_L3 < 5){Uism_L3=0;}
       Uism_L3 *= Uism_L3;// возводим значение в квадрат
       Usumm_L3 += Uism_L3; // склдываем квадраты измерений 
   if(counter == 100)
   {
     flag = 3;
     
     TIMSK2 &= ~(1<<OCIE2A); // останавливаем таймер 
     
   }
 }
}           

Библиотеки использованные в коде, взяты с данного форума, с помощью поиска их легко найти.

DimaP.
Offline
Зарегистрирован: 21.04.2013

вот еще несколько картинок печатные платы ну и передняя панель прибора ))

DimaP.
Offline
Зарегистрирован: 21.04.2013

спустя 3 года XD

Arduino
Offline
Зарегистрирован: 12.05.2015

Спасибо, за схему и за код. Вижу что программа использует 6 аналоговых выходов для измерения (по 2 на фазу).  Хочу переделать на 4 выхода. 3 фазы и 1 ноль. Вот не могу понять, зачем сигнал 2В (с делителя напряжения) повторяется через ОУ? Чем вызвана такая необходимость. Я знаю, что сигнал повторяют через ОУ, если надо на него подцепить нагрузку. Если кто-то пояснит, толково и незаумно, был бы рад. 

diakin
diakin аватар
Offline
Зарегистрирован: 04.06.2016

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

mihaser
Offline
Зарегистрирован: 05.03.2016
А у меня есть разработка покруче! Снимаю ток с трансформатора тока (на предприятии). Передаю цифру по радио(433) в кабинет, пишу расход электроэнергии на флэшку + активная диаграмма в Excel! Всё мониторится на дисплейчике от 3310
Сергій Михайлов
Написати відповідь...
 
 
Олександр Теслюк
Олександр Теслюк И как на 433 работает? Не глючит?
Сергій Михайлов
Сергій Михайлов 60м полет нормальный. я же передаю цифру(допустим 8 или 12 или 40 или 4), а не 1 0. Кстати скечи тутhttps://drive.google.com/.../0B-5jtSzum-qbZU5OdW9ZOGNlUnM
 
DRIVE.GOOGLE.COM
 
Сергій Михайлов
Сергій Михайлов переменку мерял так (только вместо 3 МОм применял 300 кОм - зависит от трансформатора тока)http://arduino.ru/sites/default/files/u4737/snimok_10.png

 

Valerii_K
Valerii_K аватар
Offline
Зарегистрирован: 15.08.2016

Master67 пишет:

Хорошая статья о измерении переменного напряжения http://512volt.ru/s2.htm

Что за спам, конкретную ссылку дайте!!!

 

Hleb13
Offline
Зарегистрирован: 16.04.2015

Подскажите, а что за номинал резистора стоит в самой первой схеме слева, обозначенный W3 LB-O?

Hleb13
Offline
Зарегистрирован: 16.04.2015

Собрал первую предлагаему схему, залил код. Все работает отлично, кроме одного момента, связанного со скачками напряжения в пределах 2 вольт. То есть, напирмер, в сети 220В, а показания Arduino меняются от 219 до 221. Это нормальная работа или все же виной тому помехи, связанные с работой схемы?

neoblack2
Offline
Зарегистрирован: 15.04.2017

Добрый день, можна ли переделать код под таймер1 ? (просто уже таймер2 занят функцией вывода звука tone(). )

enjoyneering
enjoyneering аватар
Offline
Зарегистрирован: 05.09.2016

гальваническая развязка обсуждалась в жж сообществе "рожденный с паяльником". там предложили использовать китайский трасформатор тока (стоит копейки) включенный через через токограничиващий резистор в 220в так чтоб ток был 60-90% от максимума. дальше математика.

 

вот нашел схему с пояснениями - http://easyelectronics.ru/zamer-setevogo-napryazheniya.html