avatar_enek

Клонирование Watt's Up метра

Автор enek, 27 Дек. 2012 в 23:44

« назад - далее »

0 Пользователи и 1 гость просматривают эту тему.

acyd

#18
Вы молодец, продолжайте!
Модифицированная прошивка на ваттметр turnigy130 (он клон ватсапа), пролетала в теме про него, ею занимался tony16
https://electrotransport.ru/index.php/topic,6740.18.html
А ребята с намецкого сайта спидометр к нему примонстрячили :-D
А тиньку можно взять покруче c той же распиновкой? а то 2к памяти вообще мало. И кстати измеряемый вольтаж нужно минимум 70в а лучше 150, тогда прибор будет очень актуален.
Я обязательно повторю эту схему.
В самом простом варианте он интересен как показометр для зарядного устройства, только тут надо выносные калибровки по напряжению и току

enek

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

enek

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

А также с pedelecforum.de

Буду тестировать.
Что не убивает мой вел, то делает его сильнее...

acyd

Цитата: enek от 31 Дек. 2012 в 19:17
Получилось получить рабочие прошивки
Это просто подарок! Спасибо! :dance:
Надо будет прикинуть базовый функционал, и список деталей. А также инструкцию по прошивке.

enek

#22
в прошивке v2 максимальное напряжение 65В и макс ток 133А, в watt's up было 70.24V 143.92A
Что не убивает мой вел, то делает его сильнее...

acyd

Цитата: enek от 31 Дек. 2012 в 19:47
в прошивке v2 максимальное напряжение 65В и макс ток 133А, в watt's up было 70.24V 143.92A
Если прошивка компилируется, исходник можно и поправить. На сферах точно была прошивка до 80в.
Можно еще надыбать attiny 861, 8кв против 2-х с заделом на продвинутую версию.

enek

#24
attiny861 хорошая идея, мне сейчас 2кБ в тини26 не хватает. ATtiny167 будет еще лучше - 16кБ. Хочу сделать измерение температуры, точнее уже сделал на BASCOM-AVR, но памяти остается только на вольтметр с термометром. Датчик температуры ds18b20 работает по двум проводам с паразитным питанием, подключен к свободной 11 ноге с подтягивающим резистором 4.7к Ом к питанию. Вот рабочий код.
Спойлер

$regfile = "attiny26.dat"
$crystal = 8000000
Config Lcd = 16 * 2
Config Lcdpin = Pin , Db4 = Portb.0 , Db5 = Portb.1 , Db6 = Portb.2 , Db7 = Portb.3 , E = Portb.4 , Rs = Portb.6
Config Pinb.5 = Output                                     ' rw Ausgang auf 0
Portb.5 = 0

Initlcd
Cls
Waitms 50
Config Adc = Single , Prescaler = Auto , Reference = Internal
Start Adc
   'Declare Sub Readtemp
  Cursor Noblink

Dim Ds18b20_scratchpad(9) As Byte
Dim Ds18b20_integer_temp As Integer At Ds18b20_scratchpad Overlay
Dim Ds18b20_single_temp As Single
Dim I As Byte

Dim X As Word
Dim Xx As Single
Dim V As Single
' Dim A As Single
'Dim W As Single
'Dim Ah As Single
'Dim Wh As Single
' Dim N As Integer
    Config 1wire = Porta.7
1wreset
1wwrite &HCC
1wwrite &H4E
1wwrite &B00000000
1wwrite &B00000000
1wwrite &B00111111
1wreset

Do

1wreset
1wwrite &HCC
1wwrite &H44

Config Pina.7 = Output
Set Porta.7
Waitms 300
Config Pina.7 = Input                                      ' The pin used for the DQ line is set as input.
Reset Porta.7
1wreset                                                    ' Master issues reset pulse.
1wwrite &HCC                                               ' Master issues the Skip ROM command.
1wwrite &HBE                                               ' Master issues Read Scratchpad command.
For I = 1 To 9                                             'Master reads entire scratchpad including CRC.
    Ds18b20_scratchpad(i) = 1wread()                        ' Master reads entire scratchpad.
    Next I
Ds18b20_single_temp = Ds18b20_integer_temp * 0.0625

Locate 1 , 1
'Lcd "Temp :"
Lcd Ds18b20_single_temp




      X = Getadc(2 )                                        'Spannung V auslesen
     V = X * 0.0635                                         'Umrechnungsfaktor
   '   X = Getadc(0 , 11)                                    'Strom A auslesen
    '  If X >= 14 Then                                       'Offset korrigieren
   '      X = X - 14
   '  Else
    '     X = 0
   '   End If
    '  A = X * 0.134                                         'Umrechnungsfaktor
    '  'W = V * A                                             'Leistung W
      'Xx = A / 6000                                        'xx sind Ah in 500ms
                                                            'Amperestunden Ah
      'Ah = Ah + Xx                                          'Faktor erhohen vermindert Ah
      'Xx = W / 6120                                         'xx sind Wh in 500ms
                                                            'Wattstunden Wh
     ' Wh = Wh + Xx                                          'Faktor erhohen vermindert Wh
      Locate 2 , 1                                          'LCD Ausgabe
      Lcd Fusing(v , "#.##")
     ' Locate 1 , 5
      Lcd "V"

     ' Lcd Ah
     ' Locate 1 , 15
     ' Lcd "Ah"
     ' Locate 2 , 8
      'Lcd A
     ' Locate 2 , 16
     ' Lcd "A "
      'Lcd W
      'Locate 2 , 10
      'Lcd "W "
      'Lcd Wh
      'Locate 2 , 15
      'Lcd "Wh"

      'Waitms 300
Loop
End
сейчас на С пишу, урезаю библиотеку lcd, чтобы освободить память.

acyd, а какие желаемые требования к данному прибору? Напряжение до скольки В? Ток? На нужные диапазоны измерений сделать не проблема, вот только точность пострадает.
Я пока хотел бы выводить V, A, Ah и temp. Чтобы выводить больше данных то думаю удобнее использовать дисплей 20х4, собираюсь прикупить с ebay, или в элитане взять OLED.
Кстати, у меня давно уже лежит без дела датчик тока acs756, с диапазоном от -50 до +50А, хочется на него перейти. На ebay можно приобрести acs758, токи +-50А, +-100А, +-150А, +-200А. Внутреннее сопротивление шунта 0.1мОм. Тут отпадает необходимость подстройки нуля тока и имеем более эффективный "шунт" на основе эффекта холла с гальванической развязкой. у тини26л хороший АЦП (Differential ADC Channel Pairs with Programmable Gain 1x, 20x). вот думаю стоит пробовать на меге8 или нет? У нее АЦП простой (Eight Channels 10-bit Accuracy), есть несколько мег8 в запасе. Программ для микроконтроллеров до этого никогда не писал, но вроде что-то получается... :-)
Что не убивает мой вел, то делает его сильнее...

acyd

Думаю, надо сделать 1-ю версию дико простую и надежную как Калашников с функциями старого ватсапа, напряжением не менее 70в и ток 130а (главно чтобы шунт не самоотпаивался), суперточность не важна, но нужна возможность подстроить показания тока и напряжения.
Это позволит любому переделать или восстановить свой ваттметр до вменяемого состояния, используя от старого шунт и экран или быстро собрать новый из доступных компонентов.

Я бы даже регеном пока в одном устройстве не заморачивался а сделал бы два. Один считает потребление в широком диапазоне, преимущественно от 5 до 70а, а второй реген от 0 до 20а, т.е настроить их соответственно измеряемому диапазону.

А в версии 2 можно уже и другой проц, шунт и большой олед экран, но чем больше наворотов тем ниже надежность. К тому же экран перегружать не стоит, нужна эргономика.
Тут или у других разработчиков подсмотреть, или соцопрос устроить, или делать самому, поскольку всем не угодишь.




enek

Решил писать на Си с нуля, можно сэконимить памяти и местами использовать ассемблер. Алгоритм по исходникам BASCOM-AVR я уловил, но BASCOM-AVR  уж очень прожорлив.
За пару дней освоил инициализацию дисплеев, уже мозги закипают. Вот родной дисплей (контроллер HD44780) и олед (контроллер WS0010) работают вместе.

Начало есть...
Что не убивает мой вел, то делает его сильнее...

enek

потихоньку пишу код, пока на дисплей вывел ток и напряжение. я тут прикинул если АЦП у тиньки26 10 бит, т.е. 1024 значения, решил вольметр сделать до 102.3В. как раз точность будет ровно 0.1В, а не показ попугаев с округлением до сотых.
если сравнить по инициализации дисплея - мой код занимает 9.4%, а bascom-avr 17%. значит уже не зря старался и оптимизировал код. а вот с переменной типа float уже сложнее, если ее использую, то прошивка сразу становится размером 3500 байт, предел у тини26 - 2048.
поэтому переменную типа float не использовал, прошивка занимает 628 bytes 30.7% флеша и 12 bytes данных, должен уложиться.
вот код Си если кому интересно, пишу в AVR Studio 4, это моя первая программа, после мигания светодиодом конечно:  ;-)
Спойлер

#define F_CPU 8000000UL
#include <avr/io.h>
#include <util/delay.h>

   #define LCD_PORT    PORTB
   #define LCD_DDR       DDRB
//   #define DB4       0
//   #define DB5       1
//   #define DB6       2
//   #define DB7       3
   #define EN      4
//   #define RW      5
   #define RS      6
   #define ROW   2
   #define COL   16

#define lcd_e_delay()      __asm__ __volatile__( "rjmp 1f\n 1:" );
#define set_bit(bit)      LCD_PORT  |=  _BV(bit);
#define clear_bit(bit)      LCD_PORT  &= ~_BV(bit);

volatile unsigned int V,V_old,A,A_old,W,W_old;

static void toggle_e(void)
{
    set_bit(EN);
    lcd_e_delay();
    clear_bit(EN);
}

static inline void _delayFourCycles(unsigned int count)
{
    if ( count == 0 )   
        __asm__ __volatile__( "rjmp 1f\n 1:" );    // 2 cycles
    else
        __asm__ __volatile__ (
           "1: sbiw %0,1" "\n\t"                 
           "brne 1b"                              // 4 cycles/loop
           : "=w" (count)
           : "0" (count)
          );
}

#define delay(us)  _delayFourCycles(2*us)   //( ( ( 1*(XTAL/4000) )*us)/1000 )

void lcd (unsigned char data,unsigned char rs)
{
       if (rs) {   /* write data        (RS=1, RW=0) */
            set_bit(RS);
             } else {    /* write instruction (RS=0, RW=0) */
                      clear_bit(RS);
                   }
   unsigned char dataBits;

        /* передаем первый полубайт */
        dataBits = LCD_PORT & 0xF0;
        LCD_PORT = dataBits |((data>>4)&0x0F);
        toggle_e();

        /* передае второй полубайт */
        LCD_PORT = dataBits | (data&0x0F);
        toggle_e();

        /* all data pins high (inactive) */
     //   LCD_PORT = dataBits | 0x0F;
      delay(2000);
}

static void lcd_init()
{
lcd(0x33,0);
lcd(0x32,0);   //function set
lcd(0x28,0);   //function set - 2line 5x8dot 0x2a EN_RU 0x28 EN_JP
lcd(0x08,0);   //0x08 display off cursor off blink off
lcd(0x01,0);   //display clear;
lcd(0x02,0);   //return home
lcd(0x06,0);   //entry mode set
lcd(0x0c,0);   //0x0c display on cursor off blink off
}

unsigned int getADC(void) //Считывание АЦП
{ unsigned int a;
ADCSR|=(1<<ADSC);   //начать преобразование
while ((ADCSR&_BV(ADIF))==0x00); //ждем окончания преобразования
a=(ADCL|ADCH<<8 );
return a;
}

static void lcd_PrintNumber(unsigned int Number)
{
      unsigned int tmp_int;   char tmp_byte;

      tmp_int = Number;

/*
       if (Number >= 10000)
            {
            tmp_byte = tmp_int / 10000;
            lcd('0' + tmp_byte,1);

            while (tmp_byte > 0)
               {
               tmp_int = tmp_int - 10000;
               tmp_byte--;
               }   
            }   else lcd (' ',1);

       if (Number >= 1000)
            {
            tmp_byte = tmp_int / 1000;
            lcd('0' + tmp_byte,1);

            while (tmp_byte > 0)
               {
               tmp_int = tmp_int - 1000;
               tmp_byte--;
               }   
            }
         else lcd (' ',1);
*/
      if (Number >= 100)
      {
         tmp_byte = tmp_int / 100;
         lcd('0' + tmp_byte,1);

         while (tmp_byte > 0)
         {
            tmp_int = tmp_int - 100;
            tmp_byte--;
         }
      }
         else lcd (' ',1);

      if (Number >= 10)
      {
         tmp_byte = tmp_int / 10;
         lcd('0' + tmp_byte,1);
         while (tmp_byte > 0)
         {
            tmp_int = tmp_int - 10;
            tmp_byte--;
         }
      }
         else  {    lcd ('0',1);}

            lcd('.',1);

         lcd('0' + tmp_int,1);
}

int main()
{   
MCUSR=0x00;
DDRB=0x7f;
lcd_init();

//lcd (0x01.0);   //очистка дисплея
//lcd (0x02,0);   //в начало
//lcd (0x04,0);   //справа налево
//lcd (0x06,0);   //слева направо
//lcd (0x80,0); //первая строка
//lcd (0xc5,0); //вторая строка
while (1) {


ADCSR=(1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)|(0<<ADFR)|(1<<ADIE);
ADMUX=(1<<REFS1)|(0<<REFS0)|(1<<MUX0)|(1<<MUX1)|(0<<MUX2)|(1<<MUX3);   //A set to adc0+ adc1- 20x
A=getADC()*14/10;
if (A!=A_old) {
   A_old=A;
   lcd(0x80,0);
   lcd_PrintNumber(A_old);
   lcd ('A',1);   //lcd (' ',1);
            }
ADCSR=(0<<ADEN);


ADCSR=(1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)|(0<<ADFR)|(1<<ADIE);
ADMUX=(1<<REFS1)|(0<<REFS0)|(0<<MUX0)|(1<<MUX1)|(0<<MUX2)|(0<<MUX3);   //V set to adc2
V=getADC();
if (V!=V_old) {
   V_old=V;
   lcd(0x8b,0);
   lcd_PrintNumber(V_old);
   lcd ('V',1);   //lcd (' ',1);
            }
ADCSR=(0<<ADEN);
/*
W=A*V;
if (W!=W_old) {
   W_old=W;
   lcd (0xc0,0);
   lcd_PrintNumber(W_old);
   lcd (0xc5,0);
   lcd ('W',1);   //lcd (' ',1);
}
*/

_delay_ms(200);

}

}
Что не убивает мой вел, то делает его сильнее...

TRO

Чтобы два младших разряда не плавали, нужно вводить плавающее среднее (повышать точность измерений за счёт количества выборок). Ставить внешнюю высокостабильную опору, отдельный стабилизатор на притание АЦП, и перед измерениями вводить контроллер в спячку. Иначе точность падает до 8 бит. Я уже запарился на 128й меге пытаясь получит более стабильные показания с АЦП. А если использовать встроенный предусилитель, то всё ещё хуже.

Кстати, пару познавательных статеек.
http://chipenable.ru/index.php/programming-avr/item/142-avr121-oversampling-decimation.html
http://chipenable.ru/index.php/programming-avr/item/141-avr121-oversampling-decimation.html

Wahoo 2012 29er, +собран складной двухосис на раме"Land Rover" 69er с эл. мотором, и и МОНОКОЛЕСО

enek

спячку использую ADC Noise Canceler Function - в коде (1<<ADIE)
напряжение замеряет четко, ноль не колеблется. а вот ток АЦП замеряет с 20х усилением, ноль держит, но иногда проскакивает 0.1А.
немцы на бейсике решили проблему таким костылём
Спойлер

X = Getadc(0 , 11)                                    'Strom A auslesen
  If X >= 14 Then                                       'Offset korrigieren
   X = X - 14
  Else
   X = 0
  End If
   A = X * 0.134
TRO, спасибо за статейки, учтём. пока стараюсь не отклоняться от схемы ватсапа, а позже заняться своей версией.
Что не убивает мой вел, то делает его сильнее...

zap

Самое первое, что приходит в голову - сделать стопицот замеров и усреднить.
Это в любом случае идея неплохая. Например, сделав 4 замера вместо 1 мы повысим точность до 11 бит, 16 замеров дадут 12 бит и так далее.
Подробнее здесь: http://www.atmel.com/images/doc8003.pdf
Единственная засада - на протяжении этих стапицот замеров сигнал не должен изменяться, иначе мы получим не совсем ожидаемую точность. То есть, перед входом АЦП надо ставить ФНЧ с соответствующим срезом.

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

Чтобы это побороть, обязательно делайте фильтр с плавающим окном. Увеличит стабильность показаний.
Делается оно по стандартной схеме цифрового фильтра низких частот.

Обозначим MA - скользящее среднее.
X - очередное считанное с АЦП значение

Обновление скользящего среднего выглядит так:
MA = ((MA * (N - 1)) + X) / N
где N - ширина плавающего окна.
Чтобы избежать операции умножения и деления, выберем N значением, являющееся степенью двойки.
Если упростить выражение следующим образом:
MA = MA + (X - MA) / N
Тогда единственное деление на N легко заменяется сдвигом вправо.

Готовая функция для AVR:
void update_moving_average (int16_t x, int16_t *ma, uint8_t period)
{
    // MA = ((MA * (N - 1)) + X) / N
    // ==> MA = MA + (X - MA) / N
    // *ma = ((x - *ma) >> period) + *ma
    uint16_t tmp;
    __asm __volatile (
        "ld     %A0, Z\n"
        "ldd    %B0, Z+1\n"
        "sub    %A1, %A0\n"
        "sbc    %B1, %B0\n"
        "tst    %2\n"
        "breq   2f\n"
    "1:\n"
        "asr    %B1\n"
        "ror    %A1\n"
        "dec    %2\n"
        "brne   1b\n"
    "2:\n"
        "adc    %A0, %A1\n"
        "adc    %B0, %B1\n"
        "st     Z, %A0\n"
        "std    Z+1, %B0\n"
        : "=&r" (tmp), "+r" (x), "+r" (period)
        : "z" (ma)
        : "r0"
    );
}


Параметры:
x - очередное значение, поступающее на вход фильтра
ma - аккумулятор фильтра, оно же плавающее среднее значение
period - степень двойки, ширина фильтра в сэмплах. Например 4 задаёт окно шириной 16 сэмплов итд.

Использовать как-то так:

// глобальная переменная
uint16_t voltage;

...
uint16_t x = read_adc ();
update_moving_average (x, &voltage, 4);
// дальше выводим voltage на экран и т.п.


Надеюсь, кому-то пригодится.
С уважением,
Андрей

Поражаю масштабностью некопмпетентность (ц) из лички

i

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

enek

Последнее время начал увлекаться ассемблером. Стал много читать исходного кода перед сном...
Пробовал дизассемблировать прошивку от turnigy, IDA pro с этим хорошо справляется, ситуация немного стала проясняться.
На ES выкладывали прошивку с исходником, точнее после дизассемблирования.
Выкладываю рабочую версию прошивки с исходником, почти тоже самое что и у нового ватсапа.

Можно пробовать собрать с нуля, я выкладывал вторую версию платы, ее можно немного упростить, сделать так:

я добавил описание в исходник EEPROM, можно поиграться с переменными и попробовать откалибровать.
Спойлер
    .db   $B4      ; Калибровка осциллятора
   .db   $8B      ; настройка АЦП для I ADMUX = (1<<REFS1)|(0<<REFS0)|(1<<MUX3)|(0<<MUX2)|(1<<MUX1)|(1<<MUX0)
   .db     $10      ;хз
   .db   $15      ;A0
   .db   $51      ;хз
   .db   $04       ;хз
   .db   $00       ;хз
   .db   $7D       ;Akoef
   .db   $82       ;настройка АЦП для U ADMUX = (1<<REFS1)|(0<<REFS0)|(0<<MUX3)|(0<<MUX2)|(1<<MUX1)|(0<<MUX0)
   .db   $E9       ;хз
   .db   $07       ;V
   .db   $2F       ;A
   .db   $04       ;V
   .db   $09       ;хз
   .db   $F7      ;Vkoef
   .db   $FF       ;хз
по фьюзам ставил внутренний осциллятор 8 МГц.
Реанимировал свой девайс, теперь можно считать попугаев, но прошивкой не очень доволен, буду дальше ковырять, термометр хочу встроить.
Что не убивает мой вел, то делает его сильнее...

acyd

Цитата: enek от 02 Фев. 2013 в 19:39
Реанимировал свой девайс, теперь можно считать попугаев, но прошивкой не очень доволен, буду дальше ковырять, термометр хочу встроить.
в качестве датчика планируйте цифру или аналоговый? Просто здесь на форуме много людей, в тч я с Льеновким (Андрейм-а) кристалайтом со встроенным аналоговым термодатчиком.
http://www.nxp.com/documents/data_sheet/KTY81_SER.pdf
В Максконтроллере тоже аналоговый датчик используется.
Линейная зависимость 2КОм-25 градусов,  3.2КОм- 100. Думаю, надо посчитать падение напряжения. Можно на подстроечнике потренироваться.

Заказал себе парочку Attiny, а пока идет, сделаю плату и шнур для прошивки, сам я от программирования далек, но надеюсь воспроизвести девайс удастся.


enek

Планирую подключить ds18b20, на bascom разобрался, под си есть библиотека и есть кусок ассеблерного кода. Эти датчики можно сетью по двум проводам соединять. Я в мк на обмотку приклеил один, добавил через ось +1 провод, очень удобно.
Я десяток заказал, по 30р вышли.

Простейший программатор можно собрать по этой схеме, только нужен LPT порт.
Что не убивает мой вел, то делает его сильнее...

MadMNN

а вот простым пользователям можно вывести единственный нужный параметр ОСТАТОК ЗАРЯДА в левом верхнем углу?
с перемигиванием СКОЛЬКО ИЗРАСХОДОВАНО.
остальные параметры не столько важны.