Einfaches Volt-, Watt- und Amperemeter

Mikro23

Aktives Mitglied
2 Jan 2017
366
33
28
Großraum Hannover
Sprachen
ANSI C, Assembler
Eigentlich brauchte ich ja für ein Projekt noch einen weiteren INA219.
Leider hat R ihn vor ein paar Wochen aus dem Programm genommen.
Dafür gibt es dort jetzt allerdings den INA220. Der hat zwar zwei Pins mehr, dafür ist aber der Spannungseingang, der beim 219 intern mit IN+ verbunden ist, getrennt herausgeführt. Somit kann man damit auch Low-Side-Messungen machen. Also habe ich für das Projekt neue bestellt.

Nun habe ich einen INA219 übrig. Was mache ich denn jetzt damit?
Oh, da liegt ja noch ein altes zweizeiliges Display.
Zack! INA und Tiny13 auf die Rückseite geschnallt: fertig ist der Elektrolurch … äh, wollte sagen: das Volt-, Watt-, Amperemeter.
IMG_0235a.JPG
Das war eins der ersten LCDs von EA, das ich vor Jahren ausprobiert hatte. Dafür mußte ein schon mal benutzter Lochrasterrest herhalten, wie man deutlich sehen kann.
IMG_0236a.JPG
Den Spannungsregler habe ich gesockelt. Wenn man ihn durch eine Brücke ersetzt, kann man das Gerät auch direkt an 3,3 V betreiben.
 
  • Like
Wertungen: LotadaC

LotadaC

Sehr aktives Mitglied
22 Jan 2009
3.344
61
48
Marwitz
Sprachen
BascomAVR, Assembler
Schick und minimalistisch.
Das Display ist auch I²C?

Den Spannungsregler habe ich gesockelt.
Ebenso die (Shunt?)Widerstände, oder? Das sind eigentlich die Tüllen von Präzisionskontakt-Buchsen?

Niedlich auch die Do-it-yourself-SMD-Adapterplatine für den Tiny13 - @dino03 hätte den mit dem "Dino'schen Lochraster-Halbierungstrick" wahrscheinlich direkt mitaufgelötet...

Hier mal ein Link auf den INA220, falls wer weiterlesen will...

P.S.: Wie groß ist das Programm geworden (Soft-TWI und Display-Kram)? für den Tiny4/5/9/10 wär's 'ne Herrausforderung.
(Beim Tiny13 wirst Du außerdem B3 und B4 für I²C verwendet haben, die ISP-Beine komplett für ISP reserviert. Beim 4/5/9/10 müßte man einen der beiden I²C-Pins mitverwenden, also am ehesten TPIData=SDA - damit SCL beim Programming nicht zappelt.
Andererseits wird das Display möglicherweise mehr als den I²C benötigen - da wirds dann richtig kniffelig (vier I/Os, einer davon muß 12V-resistent gehalten werden wegen ISP, zwei andere würden dabei zappeln...)
 

Mikro23

Aktives Mitglied
2 Jan 2017
366
33
28
Großraum Hannover
Sprachen
ANSI C, Assembler

Mikro23

Aktives Mitglied
2 Jan 2017
366
33
28
Großraum Hannover
Sprachen
ANSI C, Assembler
Zweiter Teil:
Die INAs (219, 220 u.a.) haben zwei Spannungmeßbereiche: 16 und 32 V. Das deutet daraufhin, daß sie für die Strom- und Leistungsmessung an 12 bzw. 24 V Akkus gedacht sind.
Für meine Basteleien reicht der 16 V-Bereich. Für den Strom zwei 1 Ω Widerstände parallel ergibt im 320 mV Meßbereich ±640 mA. Die Widerstände sind auch gesockelt, so kann man den Meßbereich bei Bedarf auch mal ändern.
Elektrolurch.png
Da das DOGM162 nicht zuverlässig funktioniert, wenn man CS fest auf GND legt und ich den Reset-Pin des Tiny nicht umprogrammieren wollte, habe ich die SCL-Leitung vom Soft-I2C dafür zweckentfremdet. LCD und I2C werden abwechselnd benutzt und kommen sich nicht ins Gehege, weil SCL bei I2C erst nach dem Start, der durch SDA initiiert wird, eine Bedeutung hat. Und das LCD wird durch low auf CS nicht gestört, wenn alle anderen Leitungen sich währenddessen nicht ändern. (Falls man das Gerät mal in Serie bauen will und das Programm sich nicht mehr ändert, kann man dann natürlich auch den Reset-Pin dafür benutzen.)
 

LotadaC

Sehr aktives Mitglied
22 Jan 2009
3.344
61
48
Marwitz
Sprachen
BascomAVR, Assembler
/CS selektiert nicht nur den Display-Controller - die fallende Flanke setzt außerdem Schieberegister und Counter zurück. Möglicherweise reicht deswegen ein permanenter Low-Pegel nicht aus.

Der ST7036i würde übrigens auch I²C unterstützen - im Datenblatt des ST7036/i findet sich aber auch der explizite Hinweis, daß die I²C-Option in EA DOG Displays nicht verfügbar ist...

Aber kannst Du nicht trotzdem Takt- und Datenleitung für beide Geräte verwenden?
Das Display aktivierst Du ja über die /CS, sonst hält es sich raus.
Den INA über die Startcondition (und danach Adressen usw).
Für 'ne Startcondition muß Data fallen während Clock High ist (und anschließend auch die Clock fallen).

Beim hier verwendeten SPI wird Data durch 'ne steigende Clock-Flanke übernommen, also nur bei Clock=Low vorbereitet/geändert, d.h. hier wird nie 'ne SC generiert.

Damit hätte man wieder vier nötige I/Os.

Beim Tiny4/5/9/10 wüder ich dann folgendes versuchen:
B3=/RST als I/O-gefused auf /CS. Dazwischen 'ne Zenerdiode mit Strombegrenzungswiderstand, dann kann mit 12V der Reset fürs ISP ausgelöst werden, und das Display funkt nicht dazwischen.
B2 auf SDA/SI. Da B2 beim ISP nicht genutzt wird, ist es im Reset tristate, wird über den Pullup high gehalten, folglich kann keine SC generiert werden - der INA hält sich raus
Die restlichen beiden sollten eigentlich egal sein
B1=TPICLK auf SCL
B0=TPIDATA auf RS

(Ich will Dir mit dem Tiny13 nicht reinreden - mich selbst reizt bei solchen Sachen nur immer, ob es noch minimalistisher gehen würde, also nur von der Theorie her)
 

Mikro23

Aktives Mitglied
2 Jan 2017
366
33
28
Großraum Hannover
Sprachen
ANSI C, Assembler
Das Display lag halt rum und sollte endlich mal einer Verwendung zugeführt werden.
Aber kannst Du nicht trotzdem Takt- und Datenleitung für beide Geräte verwenden?
Damit hätte man wieder vier nötige I/Os.
Wäre vermutlich auch gegangen, aber dann wäre ja außer dem Resetpin noch einer ungenutzt geblieben ;)
(Ich will Dir mit dem Tiny13 nicht reinreden ...)
Die Auswahl beschränkte sich schon allein dadurch, daß ich nur den Tiny13 und den Tiny85 als kleinste hier rumzuliegen hatte und davon ausging, daß für diesen Zweck 1 kB Flash ausreichen müßte. Ich hätte gern noch Ladung und Arbeit berechnet und angezeigt, aber dafür fehlten dann doch über 300 Bytes Flash…
In Assembler würde man das wohl hinkriegen, aber in ein »Abfallprodukt« wollte ich jetzt nicht soviel Energie stecken.
 

Mikro23

Aktives Mitglied
2 Jan 2017
366
33
28
Großraum Hannover
Sprachen
ANSI C, Assembler
Dritter und (vorläufig) letzter Teil:
Das Hauptprogramm ist recht kurz

CodeBox C
#include "main.h"

ISR(WDT_vect)
{
    // dummy-ISR zum Aufwecken des Controllers
}

void ina_init(uint8_t p, uint8_t h, uint8_t l)
{
  
    i2c_init();   // enable I2C, wegen Doppelbenutzung des SCL-Pins für I2C und LCD
    i2c_start(INA219 + I2C_WRITE);
    i2c_write(p);            // address
    i2c_write(h);            // MSB
    i2c_write(l);            // LSB
    i2c_stop();
    PORTB |= SCL;            // enable CS für LCD
    DDRB |= SCL;
}

int16_t ina_read(uint8_t p)
{
    uint16_t temp;

    i2c_init();                // s.o.
    i2c_start(INA219 + I2C_WRITE);
    i2c_write(p);
    i2c_rep_start(INA219 + I2C_READ);
    temp = i2c_readAck() << 8;            // MSB
    temp |= i2c_readNak();                // LSB
    i2c_stop();
    PORTB |= SCL;            // s.o.
    DDRB |= SCL;
    return temp;
}

int main(void)
{
    DDRB = CLK | SI | RS | SCL;            // 0b00010111
    PORTB = SCL;                        // 0b00010000
    PRR = (1<<PRADC) | (1<<PRTIM0);
  
    lcd_init();
    xfunc_out = lcd_putc;                // Ausgabefunktion initialisieren
  
    ina_init(0, 0x1F, 0xFF);            // 16V, 320mV, 128 Samples
    ina_init(5, 0x03, 0x28);            // calibration bei 0,5 Ohm    Imax +/- 640 mA

    set_sleep_mode(1<<SM1);                // sleep mode power down
  
    wdt_enable(WDTO_500MS);                // watchdog auf 500 ms
    WDTCR |= (1<<WDTIE);                // wdt interrupt enable
   
    sei();
  
    FOREVER {
        sleep_mode();
        wdt_reset();
        WDTCR |= (1<<WDTIE);                // re-enable wdt-interrupt
        lcd_cursor(0, 8);
        xitoa((ina_read(2) >> 1) & 0xFFFE, 10, 5);    // Spannung
        xputs(PSTR(" mV"));
        lcd_cursor(1, 8);
        xitoa((int32_t) ((ina_read(4) + 5) / 10), -10, 5);    // Strom
        xputs(PSTR(" mA"));
        lcd_cursor(1, 0);
        xitoa(ina_read(3) << 1, 10, 5);        // Leistung
        xputs(PSTR(" mW"));
        if (MCUSR & (1<<WDRF)) {
            lcd_cursor(1, 0);
            xputs(PSTR("WDR"));                // anzeigen ob ein watchdog-reset aufgetreten ist
        }
    }
}
Die Spannung steht mit 13 Bit linksbündig im Bus Voltage Register. Das LSB entspricht 4 mV, muß also um ein Bit nach rechts verschoben werden, damit die Zahl als mV interpretiert werden kann. Das Conversion-Ready-Bit steht damit auf Bitposition null und muß noch ausgeblendet werden.

Mit dem gewählten Kalibrierungswert wird die Shunt-Spannung in Zehntel mA umgerechnet. Also muß der Wert im Current Register noch gerundet und durch 10 geteilt, und da der Strom auch negativ werden kann und die Ausgabefunktion 32-Bit-Werte erwartet, auf int32 gecastet werden.
Den Kalibrierungswert habe ich experimentell ermittelt durch Vergleich des Stromes mit einem Multimeter. Leider habe ich kein Meßgerät, mit dem ich die 1 Ω Widerstände auf eine für die Berechnung notwendige Genauigkeit hätte ausmessen können.

Und aus irgendeinem unerfindlichen Grund, muß der Wert im Power Register noch mit zwei multipliziert werden, um auf mW zu kommen.

Hier noch die Header-Datei

CodeBox C
#ifndef _MAIN_H
#define _MAIN_H

#define F_CPU        9600000UL / 8

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <util/delay.h>
#include <avr/wdt.h>
#include "Display.h"
#include "xitoa.h"
#include "i2cmaster.h"

#define FOREVER        for(;;)

#define INA219        0x80

#define SI            0x01    // 0b00000001 PB0
#define RS            0x02    // 0b00000010 PB1
#define CLK            0x04    // 0b00000100 PB2
#define SDA            0x08    // 0b00001000 PB3
#define SCL            0x10    // 0b00010000 PB4 auch als CS für LCD mißbraucht

#endif
und die Displayfunktionen

CodeBox C
#include "main.h"

void lcd_out(uint8_t byte)    // soft-SPI
{
    CLR_CS;
    for (uint8_t i = 0; i < 8; i++) {
        (byte & 0x80) ? (PORTB |= SI) : (PORTB &= ~SI);
        PORTB |= CLK;
        byte <<= 1;
        PORTB &= ~CLK;
    }
    SET_CS;
     _delay_us(30);
  
}
void lcd_putc(uint8_t byte)
{
    SET_RS;
    lcd_out(byte);
}
void lcd_cmd(uint8_t byte)
{
    CLR_RS;
    lcd_out(byte);
}
void lcd_init()        // DOGM 162
{
    CLR_CS;
    _delay_ms(40);
    lcd_cmd(0x38);    // 8 bit
    lcd_cmd(0x39);    // 8 bit, 2 line, normal font, instruction table 1
    lcd_cmd(0x14);    // bias 1/5, 2 line
    lcd_cmd(0x55);    // booster on, contrast high
    lcd_cmd(0x6E);    // follower on ,V generator
    lcd_cmd(0x72);    // contrast low byte
    _delay_ms(200);
    lcd_cmd(0x38);    // 8 bit, 2 line, normal font, instruction table 0
    lcd_cmd(0x0C);    // display on, cursor off, blink off
    lcd_cmd(0x01);    // clear display
    _delay_ms(2);
    lcd_cmd(0x06);    // increment, no display shift
    SET_CS;
}

void lcd_cursor(uint8_t row, uint8_t col)
{
    lcd_cmd(0x80 | (row << 6) | col);
}


CodeBox C
#ifndef _DISPLAY_H
#define _DISPLAY_H

#define CLR_CS    (PORTB &= ~SCL)        // 0b00010000
#define SET_CS    (PORTB |= SCL)
#define CLR_RS    (PORTB &= ~RS)        // 0b00000010
#define SET_RS    (PORTB |= RS)

void lcd_init();
void lcd_putc(uint8_t byte);
void lcd_cursor(uint8_t row, uint8_t col);

#endif

Die I2C-Funktionen sind bei Peter Fleury und die Ausgabe-Funktionen bei Elm-ChaN zu finden. (Die sind übrigens alle in Assembler geschrieben.) Man muß das Rad ja nicht jedesmal wieder neu erfinden… ;)
 
Zuletzt bearbeitet:

LotadaC

Sehr aktives Mitglied
22 Jan 2009
3.344
61
48
Marwitz
Sprachen
BascomAVR, Assembler
Wäre vermutlich auch gegangen, aber dann wäre ja außer dem Resetpin noch einer ungenutzt geblieben ;)
Stimmt natürlich auch wieder - wenn man schon 'n Chip mit xyz Beinen hat, haben die auch alle zu arbeiten (*Peitscheknall*)
Schlimm genug, daß AC, ADC und wahrscheinlich auch der Timer faulenzen:cheers:
 

Mikro23

Aktives Mitglied
2 Jan 2017
366
33
28
Großraum Hannover
Sprachen
ANSI C, Assembler
Schlimm genug, daß AC, ADC und wahrscheinlich auch der Timer faulenzen:cheers:
Der Timer ist auch abgeschaltet, bleibt alles am Watchdog hängen.

PS:
Wie groß ist das Programm geworden (Soft-TWI und Display-Kram)?
In der gegenwärtigen Version 940 Bytes Flash und 2 Bytes RAM. Wenn ich Texte und Konstanten ins EEPROM geschrieben hätte, wären es bestimmt noch über 20 Bytes weniger geworden. Aber das hätte für weitere Berechnungen immer noch nicht gereicht. Division braucht halt Platz, wenn man sie nicht (weg)optimieren kann...
IMG_0250a.JPG
So sehen übrigens die Präzisionsfassungen aus. Die sind über 35 Jahre alt. Keine Ahnung ob es die heute noch in der Form gibt. Jeder Pin ist einzeln auf Carriern in allen möglichen Größen.
 
  • Like
Wertungen: LotadaC

LotadaC

Sehr aktives Mitglied
22 Jan 2009
3.344
61
48
Marwitz
Sprachen
BascomAVR, Assembler
Jaja, Division ist echt böse - Multiplikation mit Zehn ist trivial (bissel schieben und addieren)...
Die Präzisionskontakte sind krass - ich verwende bei sowas (Quarze, ICs und ggf geeignete bedrahtete Bauteile) sonst entsprechende Buchsenleisten (die mit Kunsstoff drumrum). Deine scheinen für ganze DIL-ICs gedacht zu sein...
 

Mikro23

Aktives Mitglied
2 Jan 2017
366
33
28
Großraum Hannover
Sprachen
ANSI C, Assembler
Es gibt bei der Division ‘ne ganze Menge triviale Ausnahmen, z.B. schieben bei Zweierpotenzen, aber für 12 bzw. 36(00) ist mir noch nichts ausreichend vereinfachendes eingefallen.
C-Compiler haben da Tricks drauf, auf die wäre ich nie gekommen, aber da die AVRs keinen Barrel-Shifter haben, nützt auch die Reziproke Multiplikation nur begrenzt.
Deine scheinen für ganze DIL-ICs gedacht zu sein...
Ja, von 8 bis 40 Pins alles noch reichlich vorhanden, gab sogar Single-In-Line, die habe ich aber schon verbraucht. Dafür kommen jetzt auch gelegentlich Buchsenleisten mit Kunststoff zum Einsatz.
 

Mikro23

Aktives Mitglied
2 Jan 2017
366
33
28
Großraum Hannover
Sprachen
ANSI C, Assembler
für den Tiny4/5/9/10 wär's 'ne Herrausforderung
Nachdem ich mir das Datenblatt mal angesehen habe, würde ich sagen, daß es mit Tiny9/10 kein Problem sein sollte. Wenn man ein I2C-Display nehmen würde, wären sogar noch zwei Pins frei.
An einen könnte man einen Taster anschließen und damit über den anderen die LCD-Beleuchtung einschalten, die sich nach einer gewissen Zeit automatisch wieder abschaltet. ;)

Mit Tiny4/5 wird es sportlich. Um die 512 Bytes nicht zu überschreiten müßte man schon an jedem Byte knabbern. Zumal die kein EEPROM haben, in den man Text und Konstanten auslagern könnte. Und bei nur 16 Registern muß man für Berechnungen ggf. auch noch das RAM nutzen.
Wieviel Bytes brauchst Du denn für I2C? Die Funktionen von Peter Fleury brauchen an die 160 Bytes.

Schätze, ich würde eher dazu tendieren, die nächste Version eine Nummer größer zu bauen und noch Ladung und Arbeit mit anzuzeigen. In den Tiny85 würden dann sogar noch Funktionen für die Berechnung und Ausgabe von floating point-Werten mit reinpassen.
 

LotadaC

Sehr aktives Mitglied
22 Jan 2009
3.344
61
48
Marwitz
Sprachen
BascomAVR, Assembler
wären sogar noch zwei Pins frei.
Dann muß der Reset aber 12V-tolerant sein (Taster zB).

Bisher hab ich auf dem Tiny noch kein I²C gebraucht - Peters Funktionen sind C, oder?

Was mir bei dem Reset schon 'ne Weile durch den Kopf geht (dirty tricks) - meiner Meinung nach sollte sich bei vielen AVR, wo der Reset 'ne abschaltbare Alternativfunktion ist, grundsätzlich auch bei gefustem Reset der analoge Pfad des Pins nutzen lassen. Der Digital-Input-Override-Multiplexer sitzt hinter dem analogen Abzweig. Inwiefern man da über den AC was machen kann (ohne daß der Reset auslöst), hab ich bei den möglichen internen Referenzen noch nicht durchgespielt, aber zumindest den ADC sollte man nutzen können (also grob gesagt die obere Hälfte). Zu berücksichtigen ist dabei der aktivierte interne Pullup.
 

Mikro23

Aktives Mitglied
2 Jan 2017
366
33
28
Großraum Hannover
Sprachen
ANSI C, Assembler
Peters Funktionen sind C, oder?
Beides. Die C-Funktionen sind etwas leichter nachzuvollziehen, brauchen übersetzt aber etwas mehr Platz. Ich habe, wie oben schon erwähnt, die Assemblerversion genommen.

Da ich keine Entwicklung für die Serienproduktion mache, brauche ich nicht zu sparen. Wenn die Peripherie nicht reicht, nehme ich den nächstgrößeren Controller. So brauchte ich noch nie den Resetpin umzufunktionieren.
 

Über uns

  • Makerconnect ist ein Forum, welches wir ausschließlich für einen Gedankenaustausch und als Diskussionsplattform für Interessierte bereitstellen, welche sich privat, durch das Studium oder beruflich mit Mikrocontroller- und Kleinstrechnersystemen beschäftigen wollen oder müssen ;-)
  • Dirk
  • Du bist noch kein Mitglied in unserer freundlichen Community? Werde Teil von uns und registriere dich in unserem Forum.
  •  Registriere dich

User Menu

 Kaffeezeit

  • Wir arbeiten hart daran sicherzustellen, dass unser Forum permanent online und schnell erreichbar ist, unsere Forensoftware auf dem aktuellsten Stand ist und der Server regelmäßig gewartet wird. Auch die Themen Datensicherheit und Datenschutz sind uns wichtig und hier sind wir auch ständig aktiv. Alles in allem, sorgen wir uns darum, dass alles Drumherum stimmt :-)

    Dir gefällt das Forum und unsere Arbeit und du möchtest uns unterstützen? Unterstütze uns durch deine Premium-Mitgliedschaft!
    Wir freuen uns auch über eine Spende für unsere Kaffeekasse :-)
    Vielen Dank! :ciao:


     Spende uns! (Paypal)