Ваттметр 3-х канальный с перспективой.
- Войдите на сайт для отправки комментариев
В минимальном варианте это ваттметр трёхканальный (каналы независимые с гальванической изоляцией между собой). Но также платформа для надстройки чего только нужно или можно, что в дальнейшем и планируется. На данном этапе собрано на DUE от Iduino, самодельный шилд для ili9341, 2.4 дюйма ili9341 с тачскрином и SD. Датчики тока и напряжения это I2C оптоизолированые ina-226 купленные на https://strawberry-linux.com. Датчики могут измерять напряжение до 36 вольт и ток по каналам соответственно 3.2А , 20А, 100А. При проектировании прибора не брал за образец какой-либо готовый. Просто что вижу, то и пою.
Немного фото:




На фото выше видно 100 А шунт.

//Трех канальный ваттметр на датчиках INA 226 с дальнейшим расширением функционала
//Канал1 ток до 3.2А, Канал2 ток до 20.А, Канал3 ток до 100 А
String Ver="Ver. pm_5"; //версия прошивки
#include <Wire.h>
#include <SPI.h>
#include <ILI9341_due_config.h> //библиотека LCD ILI9341_due ( marekburiak-ILI9341_due )
#include <ILI9341_due.h>
#include <DueTimer.h>
#include "Tahoma_20.h" // шрифт 14x20
#include "Courier_20.h" // шрифт 14x20
#include "Courier_22.h" // шрифт 14x20
#include "Courier_26.h" // шрифт 14x26
#define TFT_CS 10 //SPI config
#define TFT_DC 9 //SPI config
#define TFT_RST 8 //SPI config
byte INA226_ADDR = B0000000; // переменная для адресации нескольких ina_226
const byte INA226_ADDR1 = B1000000; // 40h 64d Канал1
const byte INA226_ADDR2 = B1000001; // 41h 65d Канал2
const byte INA226_ADDR3 = B1000010; // 42h 66d Канал3
const byte INA226_CONFIG = 0x00; // наименование регистров ina 226
const byte INA226_SHUNTV = 0x01;
const byte INA226_BUSV = 0x02;
const byte INA226_POWER = 0x03;
const byte INA226_CURRENT = 0x04;
const byte INA226_CALIB = 0x05;
const byte INA226_MASK = 0x06;
const byte INA226_ALERTL = 0x07;
const byte INA226_DIE_ID = 0xff;
float Shunt = 0;
float Shunt1 = 25.35940; // mOhm Канал1
float Shunt2 = 2.02046; // mOhm Канал2
float Shunt3 = 0.74856; // mOhm Канал3
// 140uS 204uS 332uS 588uS 1100uS 2116uS 4156uS 8244uS
// AVERAGES 1 4007 404F 4097 40DF 4127 416F 41B7 41FF
// AVERAGES 4 4207 424F 4297 42DF 4327 436F 43B7 43FF
// AVERAGES 16 4407 444F 4497 44DF 4527 456F 45B7 45FF
// AVERAGES 64 4607 464F 4697 46DF 4727 476F 47B7 47FF
unsigned long allTimeSec = 0; // для расчета полного времени работы грубо
unsigned long lastFixAhTime = millis(); // для расчета накопительной Ah по каждому каналу
unsigned long lastFixAhTime1 = millis();
unsigned long lastFixAhTime2 = millis();
unsigned long lastFixAhTime3 = millis();
byte needClear=1; // для организации смены показаний 1-2 и 2-3 абсолют/проценты
float powerAh; // для расчета накопительной Ah по каждому каналу
float powerAh1;
float powerAh2;
float powerAh3;
//Описание массивов data и strdata с числоывми данными
//1=U1,2=U2,3=U3,4=I1,5=I2,6=I3,7=P1,8=P2,9=P3,10=Ah1,11=Ah2,12=Ah3,13=dU12,14=dU23,15=dI12,16=dI23,17=dP12,18=dP23,19=dAh12,20=dAh23
#define Myarray_size 21 //назначаем кол-во элементов 20+1 в массивах data[] с числоывми данными float и дублир. строками strdata[]
//Описание массивов datapr и strdatapr с процентными данными
//1=dU12,2=dU23,3=dI12,4=dI23,5=dP12,6=dP23,7=dAh12,8=dAh23
#define Myarraypr_size 9 //назначаем кол-во элементов 8+1 в массивах datapr[] с процентными данными int и дублир. строками strdatapr[]
//Описание массивов data и strdata с числоывми данными
//1=U1,2=U2,3=U3,4=I1,5=I2,6=I3,7=P1,8=P2,9=P3,10=Ah1,11=Ah2,12=Ah3,13=dU12,14=dU23,15=dI12,16=dI23,17=dP12,18=dP23,19=dAh12,20=dAh23;
float data[Myarray_size]; //массив всех числовых данных 21шт. в формате float
char strdata[Myarray_size][9]; //массив этих же данных в формате string 21шт. по 9 символов (формат 1234.6 + 2пробела +1);
//Описание массивов datapr и strdatapr с процентными данными
//1=dU12,2=dU23,3=dI12,4=dI23,5=dP12,6=dP23,7=dAh12,8=dAh23;
int datapr[Myarraypr_size]; //массив данных 9шт. в формате int для процентных величин для dU12 и т.д.
char strdatapr[Myarraypr_size][6]; //массив этих же данных 9шт. по 6 символов в формате string для процентных величин для dU12 и т.д. (формат 123_5 +1)
int str1 =45; // Y для строки канала №1 !!! данные для LCD 320х240 для построения изображения на LCD !!!
int str12 =70; // Y для строки канала 1/2 с процентами
int str2 =95; // Y для строки канала №2
int str23 =120; // Y для строки канала 2/3 с процентами
int str3 =145; // Y для строки канала №3
int slb1 =20; // X столбца №1 V
int slb2 =95; // X столбца №2 A
int slb3 =175; // X столбца №3 W
int slb4 =250; // X столбца №4 Ah
#include <avr/dtostrf.h>
ILI9341_due tft = ILI9341_due(TFT_CS, TFT_DC, TFT_RST);
//----------------------------------------------------------------------------------
void setup()
{
Timer3.attachInterrupt(everyOnesec).start(1000000);
Serial.begin(9600);
Wire.begin();
INA226_ADDR = INA226_ADDR1;
INA226_write(INA226_CONFIG, 0x41FF); // выбрать в таблице шестнадцитиричное значение ,идем процедуру запись конфига в ina 226
//INA226_write(INA226_CALIB, 2019); // 3.2А=2048 можно не калибровать если не работаем с регистрами тока и мощности
INA226_ADDR = INA226_ADDR2;
INA226_write(INA226_CONFIG, 0x41FF); // выбрать в таблице шестнадцитиричное значение ,идем процедуру запись конфига в ina 226
//INA226_write(INA226_CALIB, 3620); // 20А=3567 можно не калибровать если не работаем с регистрами тока и мощности
INA226_ADDR = INA226_ADDR3;
INA226_write(INA226_CONFIG, 0x41FF); // выбрать в таблице шестнадцитиричное значение ,идем процедуру запись конфига в ina 226
//INA226_write(INA226_CALIB, 1710); // 100А=1706 можно не калибровать если не работаем с регистрами тока и мощности
tft.begin(); // начинаем работу tft
tft.setRotation(iliRotation270);// поворот tft на 270 градусов
tft.fillScreen(ILI9341_BLACK); // залить tft черным
delay (200); // без паузы может не стартануть экран
tft.setFont(Courier_26); // печать версии прошивки (скетча) на старте
tft.setTextColor(ILI9341_YELLOW, ILI9341_BLACK);//желтым по черному
tft.printAt(Ver,100,100); // сначала X (0-320) затем Y(0-240)
delay (3000); // показали версию прошивки 3 сек
tft.fillScreen(ILI9341_BLACK); // затерли экран в черный
//tft.setFont(Courier_26); // печать даты часов пока нет для этого подпрограммы
//tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK);// белым по черному
//tft.printAt(" 19.06.2016 16:13:54",0,0);// типа число / время /
//tft.setFont(Tahoma_20); // печать названий столбцов на tft
tft.setFont(Courier_26);
tft.setTextColor(ILI9341_GREEN, ILI9341_BLACK);// зеленым по черному
tft.printAt(" V A W Ah",0,23);
tft.setFont(Courier_22); // печать номеров каналов 1,2,3
tft.setTextColor(ILI9341_GREEN, ILI9341_BLACK);//BLACK WHITE RED YELLOW GREEN BLUE DARKBLUE MAGENTA NAVY
tft.printAt("1",0,str1+2);
tft.printAt("2",0,str2+2);
tft.printAt("3",0,str3+2);
}
//----------------------------------------------------------------------------------------------------
void loop()
{
//long startmillis=millis();
//for(int i=1; i<100; i++) {
//allTimeSec = (millis()/1000); // счет секунд работы устройства в версии с таймером в процедуре everyOnesec
INA226_getData(); // идем на подпрограмму получить данные со всех каналов
floattostring(); // идем на перегон всех float data[] в string strdata[]
inttostring(); // идем на расчет процентных int datapr[] , перегон всех процентных int datapr[] в процентный string strdatapr[]
tolcd(); // идем на подпрограмму вывода данных на lcd
//}
//float time =((millis()-startmillis));
//Serial.print("time for 1 = ");
//Serial.print(time/1000/100);
//Serial.println(" sec");
}
//-----------------------------------------------------------------------------------------------
void everyOnesec(){
allTimeSec += 1; // счет секунд работы устройства в версс милисекунды по millis
}
//------------------------------------------------------------------------------------------------
void INA226_getData(){ // подпрограмма получения данных со всех каналов
INA226_ADDR = INA226_ADDR1;
Shunt = Shunt1;
lastFixAhTime = lastFixAhTime1;
INA226_readData(); // идем на подпрограмму чтения регистров канала 1
lastFixAhTime1 = millis(); // после чтения регистров запоминаем когда были сняты данные
powerAh1+= powerAh; // Ah за последнее измерение прибавим к сумме Ah
data[10]=powerAh1; // Ah1 3.2a
//Serial.print(powerAh1,5);
//Serial.println(" ");
INA226_ADDR = INA226_ADDR2;
Shunt = Shunt2;
lastFixAhTime = lastFixAhTime2;
INA226_readData();
lastFixAhTime2 = millis();
powerAh2+= powerAh; // Ah за последнее измерение прибавим к сумме Ah
data[11]=powerAh2; // Ah1 20.0a
//Serial.print(powerAh2,5);
//Serial.println(" ");
INA226_ADDR = INA226_ADDR3;
Shunt = Shunt3;
lastFixAhTime = lastFixAhTime3;
INA226_readData();
lastFixAhTime3 = millis();
powerAh3+= powerAh; // Ah за последнее измерение прибавим к сумме Ah
data[12]=powerAh3; // Ah1 100a
//Serial.print(powerAh3,5);
//Serial.println(" ");
//Serial.println(" ");
data[13]=data[2]-data[1]; //U1-U2 расчет разницы значенй между каналами если они нужны в абсолютном значении
data[14]=data[3]-data[2]; //U2-U3
data[15]=data[5]-data[4]; //I1-I2
data[16]=data[6]-data[5]; //I2-I3
data[17]=data[8]-data[7]; //P1-P2
data[18]=data[9]-data[8]; //P2-P3;
data[19]=data[11]-data[10]; //Ah1-Ah2
data[20]=data[12]-data[11]; //Ah2-Ah3;
//delay(1000);
}
//-----------------------------------------------------------------------------------------
void INA226_readData() // подпрограмма получения данных по напряжению, падению на шунте,
{ // для расчета тока, расчета мощности , расчета Ah
// !!! не забыть про вариант расчета напряжения и мощности до/после шунта !!!
float sv_, bv_, c_cal, p_cal; // shunt volt, bus volt, current calcul, power calcul
bv_ = INA226_read(INA226_BUSV); // идем процедуру чтение напряжения шины
sv_ = INA226_read(INA226_SHUNTV); // идем процедуру чтение падения на шунте
bv_ *= 1.25; // расчет по регистрам bus voltage in mV
sv_ *= 2.5; // расчет по регистрам shunt voltage in microV
c_cal = sv_/Shunt; // расчет тока in mA по полученому падению на шунте и известному сопрот. шунта
p_cal = (bv_-(sv_/1000))* c_cal /1000; // расчет мощности in mW с учетом падения на шунте
powerAh = millis() - lastFixAhTime; // время прошло после последней фиксации Ah
powerAh *= (sv_/Shunt); // ток в мА (sv_/Shunt)
powerAh /= 1000; // ток перевод в А
powerAh /= 1000; // мСек в сек
powerAh /= 3600; // сек в часы
if (INA226_ADDR == INA226_ADDR1) {;
data[1]=bv_/1000; //U1 3.2a
data[4]=c_cal/1000; //I1 3.2a
data[7]=p_cal/1000; //P1 3.2a
}
if (INA226_ADDR == INA226_ADDR2) {;
data[2]=bv_/1000; //U2 20a
data[5]=c_cal/1000; //I2 20a
data[8]=p_cal/1000; //P2 20a
}
if (INA226_ADDR == INA226_ADDR3) {;
data[3]=bv_/1000; //U3 100a
data[6]=c_cal/1000; //I3 100a
data[9]=p_cal/1000; //P3 100a
}
/*Serial.print("Ina_");
Serial.print(INA226_ADDR);
Serial.print(" bus_V=");
Serial.print(bv_/1000,3); // bus voltage in V по регистрам
Serial.print(" curr_cal_A=");
Serial.print(c_cal/1000,3); // ток в А
Serial.print(" Load_V=");
Serial.print((bv_-(sv_/1000))/1000,3); // с учетом падения U на шунте, расчет U на нагрузку
Serial.print(" Load_W=");
Serial.print( p_cal /1000 ,3 ); // с учетом падения U на шунте, расчет W на нагрузку
Serial.print(" allTimeSec=");
Serial.print( (millis()- allTime) ); // сколько времени работает программа всего
Serial.print(" Ah="); // заголовок для Ah остальное там откуда вывов подпрограммы
*/
}
//-----------------------------------------------------------------------------------------
void INA226_write(byte reg, unsigned short val) // процедура записи в регистры ina 226
{
Wire.beginTransmission(INA226_ADDR);
Wire.write(reg);
Wire.write(val >> 8);
Wire.write(val & 0xff);
Wire.endTransmission();
}
//-----------------------------------------------------------------------------------------
short INA226_read(byte reg) // процедура чтения из регистров ina 226
{
short ret = 0;
// request the registor
Wire.beginTransmission(INA226_ADDR);
Wire.write(reg);
Wire.endTransmission();
// read
Wire.requestFrom((int)INA226_ADDR, 2);
while(Wire.available()) {
ret = (ret << 8) | Wire.read();
}
return ret;
}
//---------------------------------------------------------------------------------------------------
void floattostring() // подпрограмма перегон всех числовых данных float data[] в string strdata[]
{
for (int x=1;x<Myarray_size;x++) { // цикл по всему массиву data[]
char buffer[10]; // резервируем буфер в 10 символов для перегона флоат в стринг
String stringVal = ""; // данные из буфера будут копироваться в стринг stringVal
dtostrf(data[x], 1, 4, buffer); // функция перегона числа data[x] в буфер символов buffer (1 -минимальная длина, 4-округлить знаков после запятой)
for(int i=0; i < 6; i++ ) // далее цикл длиной от нуля до 6 (5 цифр и запятая) это длина данных для измерителя мощности
{
stringVal += buffer[i]; // перегон по одному символу из буфера buffer добавляем к стрингу stringVal
}
stringVal+=" "; // 2пробела в конце для подтирания хвостов от цифр разной ширины
stringVal.toCharArray(strdata[x],9); // полученый stringVal копируем в элементы массива strdata[]
//Serial.print("strdata ");Serial.print(x);Serial.print(" : ");Serial.print(strdata[x]);Serial.println("_"); //вывод на экран элементов strdata[]
}
}
//---------------------------------------------------------------------------------------------------
void inttostring() //подпрограмма расчет процентных int datapr[] и перегон всех процентных данных int datapr[] в strintg strdatapr[]
{
//Описание массивов dataper и strdatapr с процентными данными
//1=dU12,2=dU23,3=dI12,4=dI23,5=dP12,6=dP23,7=dAh12,8=dAh23;
//Описание массивов data и strdata с числоывми данными
//1=U1,2=U2,3=U3,4=I1,5=I2,6=I3,7=P1,8=P2,9=P3,10=Ah1,11=Ah2,12=Ah3,13=dU12,14=dU23,15=dI12,16=dI23,17=dP12,18=dP23,19=dAh12,20=dAh23;
datapr[1]=abs(data[2]/(data[1]/100)); //U1-U2 1-2
datapr[2]=abs(data[3]/(data[2]/100)); //U2-U3 2-3
datapr[3]=abs(data[5]/(data[4]/100)); //I1-I2 4-5
datapr[4]=abs(data[6]/(data[5]/100)); //I2-I3 5-6
datapr[5]=abs(data[8]/(data[7]/100)); //P1-P2 7-8
datapr[6]=abs(data[9]/(data[8]/100)); //P2-P3 8-9
datapr[7]=abs(data[11]/(data[10]/100)); //Ah1-Ah2 10-11
datapr[8]=abs(data[12]/(data[11]/100)); //Ah2-Ah3 11-12
for (int x=1;x<Myarraypr_size;x++) { // цикл по всему массиву dataper[]
char buffer[10]; // резервируем буфер в 10 символов для перегона int в стринг
String stringVal = ""; // данные из буфера будут копироваться в стринг stringVal
itoa(datapr[x],buffer,10); // функция перегона числа int datapr[x] в буфер символов buffer
for(int i=0; i < strlen(buffer); i++ )// далее цикл длиной от нуля до длина буффера (кол-во цифр в буффере)
{
stringVal += buffer[i]; // перегон по одному символу из буфера buffer добавляем к стрингу stringVal
}
//if (datapr[x] < 100) stringVal +=" "; // если проценты < 100 то добавим пробел
//if (datapr[x] < 10) stringVal +=" "; // если проценты < 10 то добавим пробел
if (datapr[x] > 999 || datapr[x] < -999 ) stringVal="999"; // если проценты > 999 то нам не нужно показывать это
stringVal+=" %"; // добавляю отображение знака процентов
stringVal.toCharArray(strdatapr[x],6); // полученый stringVal копируем в элементы массива strdatapr[]
//Serial.print("strdatapr ");Serial.print(x);Serial.print(" : ");Serial.print(strdatapr[x]);Serial.println("_"); //вывод на экран элементов strdatapr[]
}
}
//--------------------------------------------------------------------------------------------------
void tolcd() { // подпрограмма вывода данных на tft
byte timer_sec = (allTimeSec)%60; // вывод таймера
byte timer_min = (allTimeSec/60)%60;
byte timer_hour = (allTimeSec/3600)%24;
String timerSTR = " ";
if (timer_hour < 10) timerSTR +="0";
timerSTR += String(timer_hour,DEC);
timerSTR +=":";
if (timer_min < 10) timerSTR +="0";
timerSTR += String(timer_min,DEC);
timerSTR +=":";
if (timer_sec < 10) timerSTR +="0";
timerSTR += String(timer_sec,DEC);
timerSTR +=" ";
tft.setFont(Courier_26); // печать даты часов счетчика пока нет для этого подпрограммы
tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK); // белым по черному
tft.printAt(timerSTR,230,0); // вывели таймер работы устройства
tft.setFont(Courier_26); //Начали вывод U,I,P,Ah данных на LCD
tft.setTextColor(ILI9341_YELLOW, ILI9341_BLACK); //BLACK WHITE RED YELLOW GREEN BLUE DARKBLUE MAGENTA NAVY
tft.printAt(strdata[1],slb1,str1); //1 U, во все стринги нужно добавить по пробелу
tft.printAt(strdata[4],slb2,str1); //1 A
tft.printAt(strdata[7],slb3,str1); //1 W
tft.printAt(strdata[10],slb4,str1); //1 Ah
tft.setFont(Courier_26);
tft.setTextColor(ILI9341_YELLOW, ILI9341_BLACK);
tft.printAt(strdata[2],slb1,str2); //2 U, во все стринги нужно добавить по пробелу
tft.printAt(strdata[5],slb2,str2); //2 A
tft.printAt(strdata[8],slb3,str2); //2 W
tft.printAt(strdata[11],slb4,str2); //2 Ah
tft.setFont(Courier_26);
tft.setTextColor(ILI9341_YELLOW, ILI9341_BLACK);
tft.printAt(strdata[3],slb1,str3); //3 U, во все стринги нужно добавить по пробелу
tft.printAt(strdata[6],slb2,str3); //3 A
tft.printAt(strdata[9],slb3,str3); //3 W
tft.printAt(strdata[12],slb4,str3); //3 Ah
byte whatSec = (allTimeSec)%10; // начинаем вывод разницы между каналами измерения абсолюные/процентные
if ( whatSec >= 3 && whatSec <= 6){ // между 3 и 6 секундами покажем проценты
tft.setFont(Courier_22);
tft.setTextColor(ILI9341_BLACK, ILI9341_BLACK);//не красивый способ стереть числовые данные для печати процентов
if (needClear == 1){
tft.printAt("0000000000000000000000000000",slb1,str12+2);
tft.printAt("0000000000000000000000000000",slb1,str23+2);
tft.setFont(Courier_22);
tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK); // и сам вывод процентов
tft.printAt(strdatapr[1],slb1+15,str12+2); //1/2 U +15 и +2 добавлены для выравнивания т.к. шрифт уменьшен
tft.printAt(strdatapr[3],slb2+15,str12+2); //1/2 A
tft.printAt(strdatapr[5],slb3+15,str12+2); //1/2 W
tft.printAt(strdatapr[7],slb4+15,str12+2); //1/2 Ah
tft.printAt(strdatapr[2],slb1+15,str23+2); //2/3 U во все стринги нужно добавить по пробелу
tft.printAt(strdatapr[4],slb2+15,str23+2); //2/3 A +15 и +2 добавлены для выравнивания т.к. шрифт уменьшен
tft.printAt(strdatapr[6],slb3+15,str23+2); //2/3 W
tft.printAt(strdatapr[8],slb4+15,str23+2); //2/3 Ah
}
needClear = 0;
}
else { // с 6 по 9 и далее с 0 по 3 сек покажем цифры
tft.setFont(Courier_22);
tft.setTextColor(ILI9341_BLUE, ILI9341_BLACK);
tft.printAt(strdata[13],slb1+3,str12+2); //1/2 U +3 и +2 добавлены для выравнивания т.к. шрифт уменьшен
tft.printAt(strdata[15],slb2+3,str12+2); //1/2 A
tft.printAt(strdata[17],slb3+3,str12+2); //1/2 W
tft.printAt(strdata[19],slb4+3,str12+2); //1/2 Ah
tft.printAt(strdata[14],slb1+3,str23+2); //2/3 U во все стринги нужно добавить по пробелу
tft.printAt(strdata[16],slb2+3,str23+2); //2/3 A +3 и +2 добавлены для выравнивания т.к. шрифт уменьшен
tft.printAt(strdata[18],slb3+3,str23+2); //2/3 W
tft.printAt(strdata[20],slb4+3,str23+2); //2/3 Ah
needClear = 1;
}
/*
tft.setFont(Courier_26); // balancer пока нет платы балансира поэтому имитация
tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
tft.printAt(" 4.200 3.200 1.200 3.888",0,172);
tft.printAt(" 4.200 3.200 1.200 2.777",0,194);
tft.setFont(Courier_22);
tft.setTextColor(ILI9341_MAGENTA, ILI9341_BLACK);
tft.printAt(" min 4.200 max 3.200 bal 0.777 ",0,218);
*/
}
//--------------------------------------------------------------------------------------------------
На экран одновременно выводится:
- часы и календарь (еще не реализовано)
- таймер
- напряжение, ток, мощность, ампер/часы всех трёх каналов
- показания разницы всех величин между каналами, с периодическим переключением показаний из абсолютных значений в процентное и обратно
Результаты проверки пока очень радуют.
Далее планы и железо есть, времени мало. По мере реализации напишу.
Короткое видео работы https://www.youtube.com/watch?v=cisTjr0wLas&feature=youtu.be
Да, навороченный получается ваттметр.. Сколько в долларах (с доставкой) обошелся один модуль INA226iso (с ADM3260)? Модуль конечно очень интересный.. Изолятор имеет изолированный источник питания 0,15Вт (30мА).. наверное можно еще прицепить какой нибудь МК типа тиньки к этому модулю, тогда не только мерить, но и управлять зарядом можно было бы - т.е. управлять балансировкой. (мысли вслух)
Ну и задумал не как ваттметр. Уже можно применять для нескольких задач. С балансировкой идет развитие. Дальше еще интересней и дороже, но пока не будем об этом. Модули на 3.2A http://strawberry-linux.com/catalog/items?code=10226 и на 20А http://strawberry-linux.com/catalog/items?code=11226 примерно по 1000 рублей, модуль на 100 А http://strawberry-linux.com/catalog/items?code=13226 с шунтом примерно 1700 рублей, комиссия посредника zenmarket http://zenmarket.jp/default.aspx 300 рублей, пересыл по Японии 200 рублей, пересыл с Японии ко мне около 1000 рублей. Итого немного меньше 6000 рублей за три модуля.
Спасибо за исходники! Цены в принципе адекватные, если задачи серьезные.. например я вижу применение при проектировании и анализе зарядных устройств, и как анализатор аккумуляторов, особенно в сборке.. правда из 4 аккумуляторов и выше, необходимо соответсвующее число модулей.
Посмотрел видео, хорошая точность.. даже на 100А отклонения не большие, получается до тысячной Ампера . А какая скорость считывания по каждому каналу получилась?
В Datasheet на инглише подробно описаны особенности конфигурирования. Про скорости считывания есть в коде.При конфигурировании можно выбрать время выборки (140uS 204uS 332uS 588uS 1100uS 2116uS 4156uS 8244uS). Чем дольше тем точнее, и количество выборок для усреднения от 1 до 64. Чем больше, тем точнее. Я пока выбрал усреднение 1 выборка 8244uS. Можно по каждому каналу брать через 140uS с усреднением 1. С регистрами расчета тока и мощности в самом INA226 я разобрался и все нормально работает, но решил ардуиной посчитаю, и на скорость в целом это влияет мало.
Ага.. то-то я смотрю по видео, похоже как нет усреднения, с другой стороны цифры не так уж пляшут.. По коду еще не разбирался, не было времени, спасибо за разъяснение. Значит у Вас получилось где-то около 120 выборок в секунду, при 8244uS. Мне бы минимум 300 выборок.. это чтобы можно смотреть модулированный сигнал заряда с несущей частотой до 40Гц.
А если взять к примеру 140uS и с усреднением = 64, точность на сколько будет хуже?
Я разницы между 140uS и 8244uS при усреднении 1 не заметил в своем применении. Выглядит одинаково, только еще быстрее работает при 140uS. Нужно в даташите посмотреть, не помню точно, кажется если взять 140uS и усреднение 64, то время до получения результата будет 8960uS (простое умножение). Точность при этом не думаю что будет плохая. Интересен фильтр при аналоговых измерениях типа: делаем n измерений, сортируем их в порядке возростания, берем из них центральных j значений, и по ним считаем среднее. Например 100 измерений залил в массив, отсортировал, взял с 45 до 55 номера и посчитал по ним среднее. Я пробовал довольно интересно. Но в случае с INA226 при конфигурировании можно пробовать добиться хороших измерений без проведения дополнительных расчетов.
ЮриБас, АЦП сигма-дельта физически очень медленные. В частности INA226 даёт 15- битное разрешение только с конвершн тайм 8.2 мс на семпл. Уменьшение времени в два раза отнимает один бит. Соответссно 140µS -это уже 8-битное разрешение. Короче не предназначены они для скоростных измерений в принципе.
Кстати на видео цифры пляшут капец как сильно, в последних разрядах просто мусор выводится.
Интересен фильтр при аналоговых измерениях типа: делаем n измерений, сортируем их в порядке возростания, берем из них центральных j значений, и по ним считаем среднее. Например 100 измерений залил в массив, отсортировал, взял с 45 до 55 номера и посчитал по ним среднее. Я пробовал довольно интересно. Но в случае с INA226 при конфигурировании можно пробовать добиться хороших измерений без проведения дополнительных расчетов.
Разработчики INA226 специально сделали аппаратное усреднение, зачем приделывать костыль?
Кстати, на видео импульсный блок питания и электронная нагрузка. Ток маленький конечно, но... Нужно записать то же , только сливать с акуумулятора на активную нагрузку типа лампы и посмотреть как это будет.Через пару дней сделаю.
tCT ADC conversion time
"CT" - это что, а "bit = 000" адрес? Или как-то на разрешение в битах говорит?
ЮриБас, 500кГц -частота модулирования, понятие из внутренней кухни дельта-сигма,конечным пользователям почти не о чём не говорящее. То, что время выборки влияет на разрешение -факт из принципа работы дельта-сигма. Для более старшего собрата -ina219 время/разрядность в табличке документировалось, но тут видимо решили не пугать пользователей. CT-биты -конвершн тайм биты. Соответссно по три бита на каждый конфиг. Да, по вашей последней фразе - всё неправильно поняли. Я ведь вам уже написал в #7 Конвершн тайм прямо влияет на разрядность. И кстати 16 -бит это дифф резолюшн, т.е. в обоих полярностях. А так оно 15 бит.
Спасибо dimax Да, с ina219 и с ADS1115 все понятно в даташите.. и да, действительно, увеличение скорости выборки в два раза уменьшает разрешение на один бит.
Таблица INA219
Странно, что у INA226 нет подобной таблицы.. Значит получается, что 16 бит в диф режиме может выдать на скорости выборки 8.244 -9.068 ms. т.е. 110 выборок в секунду ? Или может 16 бит выдает на 140мксек? Как определить?
Вот например у ADS1115 ясно написано, что 16 бит при 860 SPS (выборок в сек)..
С другой стороны, логично предположить, что 860 SPS будет на минимальном разрешении т.е. 8 бит .. Такое впечатление, что действительно, специально запутывают.. рекламный трюк.
Странно, что у INA226 нет подобной таблицы.. Значит получается, что 16 бит в диф режиме может выдать на скорости выборки 8.244 -9.068 ms. т.е. 110 выборок в секунду ? Или может 16 бит выдает на 140мксек? Как определить?
То ли вы читаете через строчку, то ли прочитанное тот час забываете. Перечитайте ещё раз сообщение #7
Мне просто было интересно, откуда Вы взяли эти цифры, т.е. каким образом вычитали из даташита.
Но вроде дошло.. хотя получается, что ошибался, наивно доверяясь таблицам, которые приводил выше.. особенно на счет ADS1115. А надо брать из даташита максимальную выборку и применять к минимальной битности.. которую еще надо выискивать, это как в рекламе, важное - мелкими буквами.
При измерениях с аккумулятора на активную нагрузку всё примерно так же. Третий разряд (милливольты и миллиамперы) нормально, а четвертый разряд бегает как мусор. Но это меня усстраивает. В первом канале дискретность должна быть 0.1мА, там еще посмотрим что можно сделать.
mikelari, что касается измерения напряжения -то ни при каких условиях там нет 4-го разряда. Девиация при идеальном источнике напряжения и максимальным конвершн тайм составляет 1LSB, то есть 1,25мв. Сответссно нет смысла выводить информацию с разрешением менее 2,5 мв -там будет мусор.
Мне просто было интересно, откуда Вы взяли эти цифры, т.е. каким образом вычитали из даташита.
Но вроде дошло.. хотя получается, что ошибался, наивно доверяясь таблицам, которые приводил выше.. особенно на счет ADS1115. А надо брать из даташита максимальную выборку и применять к минимальной битности.. которую еще надо выискивать, это как в рекламе, важное - мелкими буквами.
Ну да, есть какая то муть с ina226...
Предисловие:
В datasheet ads1115 сказано: 16bit , MAXIMUM 860 SPS. По дефолту настроено 128 SPS . Логично с фабрики иметь по дефолту конфиг с АЦП разрешением 16bit. И тут получается время конвертации примерно 8mS.
В datasheet ina219 сказано: Min Conversion Time 84uS при 9bit что (считаем) равно примерно 12000 SPS. По дефолту настроено Conversion Time 532uS при 12bit что (считаем) равно примерно 1880 SPS . Тоже логично что с фабрики идет по дефолту на 12 bit т.е. максимум его АЦП.
В datasheet ina226 сказано: Min Conversion Time 140uS при ??? bit что равно примерно 7140 SPS. По дефолту настроено Conversion Time 1.1mS при ??? bit что равно примерно 900 SPS .
Вот далее муть...
Какой смысл согласно datasheet ina226 иметь по дефолту настройку АЦП 12 bit если у него есть 16 bit т.е. менее его возможностей по разрядности? Так было бы вернее, если указаное по дефолту CONVERSION TIME = 1.1mS должно соответствовоть 16bit ( или там 15 без диффа) и это примерно 900 SPS ?
Мысли вслух...
mikelari, 12 бит уже достаточно для большинства задач, + часто имеется приоритет в область скорости. Например с максимальным разрешением режим усреднения становится слишким долгим, (до 8 секунд на 15 битах и 1024 семплах). А на 12 битах те же 1024 семпла отсчитает за 1 секунду. А режим усреднения -штука наиполезнейшая, например я с помощью ina219 измерял средний ток у мощного светодиода, работающего от шим-драйвера. Контрольное измерение tru-rms мультиметром показало ту же цифру что и усреднённый ток с ina.
Продолжение. Добавлен термоскан MLX90614. И… плата. Преобразователь уровней логики, далее I2C мультиплексор TCA9548A, далее восемь гальванически изолированных I2C Isolator на AMD3260 http://strawberry-linux.com/catalog/items?code=23260 , далее восемь ADC1115. Проще говоря восемь изолированных диф-каналов измерения 5 вольт. Проверил на измерении с балансных разъемов батарей. Работает хорошо. Далее железо обдумывается. Пилим софт. А тут работы края не видно. Нужно запустить лог на SD, вывод в LogView, продумать тач-меню с режимами: измерение мощности, измерение емкости батареи, измерения кпд преобразователей DC-DC, измерения внутреннего сопротивления банок и т.д. и т.п.. Короче, это надолго.
Софт пилим, функционал добавляется. Плохо читал про DUE в интернете, наступил на грабли. Подал 12В на разъем питания, и из за ошибки в разводе/схемотехнике DUE эти 12 вольт попали всюду на шину 5В. К счастью, все сделано на гальваноразвязке от ADM 3260, и перенапряжение не попало на датчики. Умер только I2C мультиплексор. Железо о котором писал выше наконец то собрано в кучу и работает довольно хорошо. Завораживает смотреть на кучу бегающих цветных циферек, которые к тому же не бесполезны. Снял небольшое видео https://youtu.be/yca17xm28f0 с добавленным функционалом, только на ваттетр и на балансное измерение подключены разные источники, поэтому напряжения не совпадают. Ну это сейчас и не важно, видно что и как работает. Работа продолжается.
Пока сделано всё, что запланировано. Выглядит так:
А также был сделан еще один ваттметр с автономным питанием на двух 18650, с 100А шунтом.
Симпатичненько, вот только входы-выходы для канала №3 смутили.)))
Подпишусь на тему
И как себя показали ADC1115? Нормально всё? Как у них с погрешностями, со временем/температурой/напряжением питания они не "уплывают"?