Besucher seit 10/07/2004

kostenlose counter
   

Im WWW Suchen

Benutzerdefinierte Suche
   

  Akkuwächter

Akkuwächter ProbeaufbauDiese kleine Schaltung, habe ich zur Überwachung  eines 12 Volt Bleiakku (Autobatterie) aufgebaut, der in Verbindung mit einem Solarpanel und Laderegler einen Wohnwagen mit Strom versorgt. Leider verfügt der Regler über keine Anzeige der auf den Ladezustand des Akkus schließen lässt. Hier soll dieses Miniprojekt Abhilfe schaffen.
Auf der Platine befinden sich 8 LEDs, die in einer Reihe angeordnet sind und als Bargraph Anzeige dienen. Je höher die Spannung am Akku ist, desto mehr LEDs leuchten. Dabei füllt sich der Bargraph von links nach rechts, wie man es z.B. von der Ladeanzeige eines Handys her kennt. Da bei einem 12 Volt Akku Hauptsächlich der Bereich von ca. 12 - 14 Volt Interessant ist,  leuchtet bei 12 Volt nur die erste (rote) LED. Alle weiteren LEDs leuchten dann in einstellbaren Schritten (z.B. 12,3 V 12,6 V usw.), bis dann  bei 13,8 Volt alle 8 LEDs aufleuchten und somit einen vollen Akku anzeigen. Der Akkuwächter ist also ein Voltmeter, welches den Bereich von 12 - 14 Volt (wenn auch nur mit geringer Auflösung)  anzeigt. Aber es kann noch mehr. Da ein Bleiakku sehr empfindlich auf eine Tiefentladung reagiert, zeigt die erste (rote) Led durch blinken an, wenn die Spannung am Akku,  unter einen einstellbaren Schwellwert gefallen ist. Ich habe hier 11,5 V eingetragen, da die LED ja schon vor der Tiefentladung (lt. Wiki 10,5 Volt) warnen soll.
Ab 14,2 Volt am Akku kann man annehmen, dass der Akku gerade aufgeladen wird. Die LEDs zeigen mit einem sich immer wieder füllenden Bargraph an, das sich das Akku im Lademodus befindet (auch hier lässt die Handyanzeige grüssen). Bei der Anzeige des Auflademodus, habe ich etwas gespielt. So kann man aus mehreren Lauflichteffekten wählen, welche den Lademodus anzeigen. Mehr dazu bei der Beschreibung der Firmware.

Wer sich etwas mit der Programmierung der AVR Controller auskennt könnte mit dieser Schaltung auch leicht andere Anwendungen programmieren. Z.B währe eine Pegelanzeige für Audio oder ein minimal Thermometer kein Problem. Fast alles eben, wo eine Bargraph Anzeige und ein Analogeingang ausreicht.

Die Schaltung


Eagle Schaltplan Akkuwächter

Die Schaltung wird vom Akku mit Strom versorgt und hat deshalb einen kleinen 78L05 Spannungsregler mit auf der Platine. Vor dem Spannungsregler ist der Eingang des Spannungsteiler (10k, 3k3) verbunden der die Akkuspannung passend für den internen  Analog - Digitalwandler des Tiny13  ADC2 (Referenzspannung=5Volt) ca. 4:1 herunterteilt. So sind Spannungen bis 20 Volt keine Gefahr für den Analogeingang des ATTiny13. In der Software stehen dann für den Interessanten Bereich von 10 - 15 Volt 256 Bit zur Verfügung, was für diese Anwendung mehr als genug ist. Die Toleranz der Widerstände ist nicht kritisch, da der Abgleich über die Software mit einem Multimeter erfolgt.

-Achtung- Ich habe in dieser Schaltung keinen Verpolungsschutz vorgesehen, deshalb aufpassen beim anschließen.

Charlie lässt grüssen

Den meisten werden wohl sofort die ungewöhnliche Anordnung der LEDs auffallen, welche notwendig ist um mit nur 4 Portpins 8 LEDs zu schalten. Diese Technik ist nicht neu. Da ein Mann namens Charlie auf diese Tricky Schaltung gekommen ist, nennt man diese Charlieplexing. Diese Art der LED-Ansteuerung macht sich zunutzen, das es in der digitalen Welt eben doch nicht nur high und low gibt, sondern noch einen 3. Zustand. Tristate eben. Um einen Pin des Tiny in den Tristate zu schalten, muss dieser als Eingang programmiert werden. Der Pullup Widerstand wird abgeschaltet, dann ist der Pin Hochohmig. Der Rest dürfte klar sein. Für die LED die leuchten soll, werden beide Portbits, die mit der LED verbunden sind auf Ausgang programmiert. Die Anode wird auf high geschaltet die Kathode auf low. Damit wird klar, das die Ansteuerung von der Softwareseite etwas komplexer wird. Anstatt wie bei der herkömmlichen Methode nur ein Bit zu setzen um eine LED zu schalten, muss man beim Charlieplexing 2 Bitmuster in 2 Register schreiben um nur eine LED leuchten zu lassen.
Übrigens sehe ich keinen Grund, bei meiner Schaltung nach jedem AVR-Pin einen Vorwiderstand mit halben Wert zu schalten, wie man es oft bei ähnlichen Schaltungen im WWW sieht.
Leider hat diese Technik auch ihre Grenzen (zumindest ohne speziellen Interfacebausteine) da, wenn alle LEDs gleich hell leuchten sollen,  immer nur eine LED gleichzeitig leuchten darf. Will man alle LEDs (wie in meiner Schaltung),  unabhängig von einander schalten, muss man multiplexen. Da beim muxen jede der 8 LEDs nur ein achtel ihrer Zeit leuchtet, muss man auch den 8-fachen Strom durch die LED treiben um auf die normale Helligkeit der Led zu kommen. So überschreitet man sehr schnell die im Datenblatt angegebenen Grenzen der LEDs und des Mikrocontrollers. Wollte man Treiber verwenden, müssten diese den Strom in beide Richtungen treiben können, das währe zwar möglich, aber dann könnte man auch gleich Schieberegister (evtl. sogar mit mit internen Treibern) verbauen und evtl. über eine normale Matrix multiplexen.
Die LEDs oben sind sogenannte Low Current LEDs die schon ab 2 mA genügend hell leuchten. Man könnte sogar zu den vorhandenen 8 LEDs noch 4 weitere LEDs anschließen (jeweils ein Pärchen zwischen PB0 und PB1 und zw. PB2 und PB3, hier währe ein weiterer Vorwiderstand nötig) und hätte dann insgesamt 12 LEDs an 4 Portpins , währe dann aber schon mit 24mA an einem Portpin im roten (oder rötlichem ) Bereich. Da 8 LEDs in dieser Schaltung durchaus genügen und diese bei einem 8Bit-µC auch einfacher zu handhaben sind, wollte ich hier nicht gierig sein. Natürlich könnte man auch einen grösseren Controller nehmen und die LEDs in Herkömmlicher Technik ansteuern, aber ich wollte Charlieplexing schon immer mal ausprobieren und diese Schaltung gab mir die Möglichkeit dazu. Interessant dürfte die Schaltung auf jeden Fall für den Modellbau sein, da hier oft auf engstem Raum viele LEDs geschaltet werden sollen.

Die Platine...

Eagle Layout Akkuwächter...ist einseitig und in THT Technik aufgebaut und deshalb auch hervorragend für Anfänger geeignet. Die 8 LEDs lassen sich auch abgewinkelt einbauen, wenn die Anschlussdrähte um 90° gebogen werden. Ich habe bei mir für LED1 die Farbe rot gewählt, dann 4 mal gelb und drei mal grün. Natürlich kann man die Farben auch anders als bei mir wählen, besonders dann, wenn man die Schwellwerte individuell an die Entladekurve seines Akkus anpassen möchte und in der Software andere Schwellwerte der Spannungen einträgt, was leicht möglich ist. Für den ATTiny13, habe ich eine 8polige Fassung vorgesehen. Diese ist notwendig, wenn der Tiny13 mit einem AVR-Dragon geflasht werden soll. Der Dragon weigert sich nämlich strikt den Tiny in der Schaltung (ISP) zu flashen, da die LEDs (hauptsächlich SCK) direkt mit den ISP Leitungen verbunden sind. in diesem Fall kann man sich auch gleich das bestücken des ISP Steckers JP1 sparen. Der Tiny kann natürlich außerhalb  der Schaltung (Nullkraftfassung oder Breadboard) auch mit einem Dragon geflasht werden. Da ich auch einen AVRISP2 besitze, habe ich diesen  ausprobiert und siehe da, das proggen des Tiny klappt mit diesem auch in der Schaltung per ISP.

Stückliste

R1, R5
10k
Alle Widerstände 1/4 Watt Metallfilm
R2
3,3K

R3, R5
220R

C1
10..47µF
Elko
C2...C4
100nF
Kerko
LED1...8
LED
3mm Low Current LED
IC1
78L05
Spannungsregler 5Volt 100mA
IC2
ATTiny13
µC Atmel
JP1
Pfostenleiste
6 polig (2*3), 90° abgewinkelt, RM 2,54

  Die Software

Das Programm ist wie bei allen meinen Projekten in Assembler geschrieben. Um eine brennbare Hex Datei zu erhalten, muss der Code mit dem (kostenlosen) AVR-Studio 4.19 assembliert werden. Eine kleine Einführung dazu gibt es hier. Man braucht in diesem Fall nur die Hex Datei brennen, die Fusebits können bleiben wie sie sind.
Da der 78L05 als Referenzspannungsquelle benutzt wird, der ADC und auch unsere Widerstände Toleranzen haben, wird der Abgleich über Software durchgeführt. So hängt die Genauigkeit fast nur noch vom verwendetem Multimeter ab, bei welchen auch schon preiwertere Modelle, recht gute Messergebnisse im Spannungsbereich liefern.

Abgleich

Dazu brauchen wir eine installierte und lauffähige Version von AVR-Studio 4.19, ein Netzteil mit einstellbarer Spannung und ein Multimeter. Natürlich ist ein Labornetzteil mit genauer Spannungsanzeige hierfür optimal. Nach dem einfügen des Quellcodes in das Studio, steht im oberen Teil unter Programdefs alles, was für den Abgleich nötig ist.

;***********************************************************************************
;Programmdefs
;***********************************************************************************
.set    messen  =   0           ;Auf 1 setzen, um den Binärwert der AD-Messung anzuzeigen
.set    player  =   4           ;Einen Player wählen (0-5)
.equ    delay   =   100         ;Je höher der Wert, desto langsamer das Lauflicht (1-255)
.equ    load    =   0b11100010  ;Der AD-Wert bei 14,2 Volt
.equ    lowbat  =   0b01011000  ;Der AD-Wert bei 11,5 Volt
;-----------------------------------------------------------------------------------
;Der AD-Wert (Schwellwert) bei dem der Bargraph bis zur LED X aufleuchten soll
;-----------------------------------------------------------------------------------
;LED1 (rot) leuchtet von 11,5V - 12.0V (Wert in lowbat). Unter 11,5V blinkt LED1
.equ    led2    =   0b01110010  ;gelb   12,0 V
.equ    led3    =   0b10000001  ;gelb   12,3 V
.equ    led4    =   0b10010001  ;gelb   12,6 V
.equ    led5    =   0b10100000  ;gelb   12,9 V
.equ    led6    =   0b10110000  ;grün   13,2 V
.equ    led7    =   0b10111100  ;grün   13,5 V
.equ    led8    =   0b11001100  ;grün   13,8 V

Zunächst wird .set    messen  =   0 in .set    messen  =   1 geändert und mit dieser Einstellung der ATTiny13 geflasht. Dies bewirkt, das auf den 8 LEDs der rohe Binärwert der Versorgungsspannung angezeigt wird, welcher der ADC liefert. Die Spannung muss sich hierbei zwischen etwa 10V und 15 V bewegen. Bei Spannungen unter 10 Volt sind die LEDs aus, über 15 Volt leuchten alle LEDs. Achtung, nicht über 20 Volt einstellen, da sonst der 78L05 und evtl. der Elko (Spannungsfestigkeit) Schaden nehmen könnten. Dreht man also am Poti der Spannugsquelle zwischen 10..15 Volt, ändert sich fortlaufend der Binärwert der 8 LEDs.
Nun stellt man das Netzteil auf den Spannungswert ab dem die Ladeanimation angezeigt werden soll (bei mir 14,2 Volt). Diesen Wert trägt man bei load ein, wobei man meine Messung Natürlich entfernen muss. LED1 kommt dabei in die Stelle ganz rechts, LED8 nach ganz links. Es ist also einfacher die Platine um 180° zu drehen so das die 8 LEDs oben sind. Das 0b muss vor jedem Bitmuster eingetragen werden, damit der Assembler weis, das es sich um eine Binärzahl handelt.
Der Wert bei lowbat (bei mir 11,5V) ist die untere Schwelle bei der LED1 blinkt. Wird diese Schwelle unterschritten, blinkt die LED, beim überschreiten leuchtet LED1 fortlaufend. Sind die beiden Sonderfälle erledigt, stellt man fortlaufend die Spannungen für LED2...LED8 ein, liest den Binärwert ab und trägt diesen gleich in den Code ein.
Nun kann der Eintrag messen wieder auf 0 geändert werden. Nach dem assemblieren und flashen prüft man die Anzeige durch ändern der  Spannung  zwischen 10 und 15 Volt auf plausible Werte, wobei sich die Bar zwischen 12 und 14 Volt von links nach rechts füllen sollte.

Sonstige Einstellungen

Der Wert delay enthält die Zeitdauer für das blinken der LED und den Player. Ein Tick dauert 1,7 mS. Man kann Werte zwischen 0 und 255 eintragen.

Der Player

Akkuwächter tiny13Es kann ein Player aus 6 möglichen augewählt werden (0..5).
Bei Werten von player  = 0..2 wird ein Lauflicht nach Knight Rider Vorbild gewählt, wobei bei 0 immer nur eine LED von rechts nach links wandert. Bei 1 sind es 2 wandernde LEDs und bei dem Wert 2 sind es dann letztendlich 3 LEDs.
player = 3 ist ein einfacher Binärzähler. player = 4 ist der voreingestellte Ladebalken nach Handymanier.
Bei player = 5 wird eine Tabelle als eine Art Daumenkino benutzt. Die Bitmuster werden von oben nach unten der Reihe nach angezeigt und fangen nach dem Ende wieder am Anfang an. Will man sich hier kreativ betätigen, muss im Quellcode folgender Programmteil gesucht werden.

        ;---------------------------------------------------------------------------
        ;Tabelle
        ;---------------------------------------------------------------------------
        .if player ==5
play:   ldi     temp3,(tabende-tab)*2;Anzahl der Tabelleneinträge
                                ;(Der Assembler kennt nur Words im Flash, deshalb *2)  
        ldi     zl,low(tab*2)   ;Zeiger auf Tab
        ldi     zh,high(tab*2)
tab1:   lpm     leds,z+         ;Tabelleneintrag nach LEDs holen
        rcall   ad              ;Zwischenduch messen
        cpi     temp1,load      ;Sind wir noch über 14,2 Volt?
        brcs    main            ;Wenn nein, Rücksprung
        rcall   wait            ;Zeit vertrödeln
        dec     temp3           ;Anzahl Tabeinträge - 1
        brne    tab1            ;Ist noch was da? Wenn nein nächsten Tabeintrag bearbeiten
        rjmp    play            ;Ansonsten, das ganze von vorn
        ;---------------------------------------------------------------------------
        ;Tab mit dem Daumenkino
        ;---------------------------------------------------------------------------
tab:    .db 0b00000000,0b00011000
        .db 0b00111100,0b01111110
        .db 0b11111111,0b01111110
        .db 0b00111100,0b00011000
tabende:
        .endif
        ;---------------------------------------------------------------------------

Die eigentliche Tabelle steht zwischen tab: und tabende:. Diese beiden Labels bitte stehen lassen. Es müssen immer eine gerade Anzahl von Bytes in einer Zeile stehen, ansonsten produziert der Assembler Warnings und fügt selbst nullen ein um auf Wortbreite zu kommen. Die Anzahl der Bytes ist nicht beschränkt. Man muss nur darauf achten, das das Bitmuster des letzten Bytes wieder zum ersten Byte in der Tabelle passt.

Programm

Wer mit dem Quellcode experimentieren, oder wissen will, wie Charlieplexing in meinem Assemblerprogramm funktioniert, findet hier ein paar Infos. Wer den Akkuwächter nur nachbauen möchte, braucht diesen Teil nicht lesen. Ich halte diesen Teil besonders für Programmierer Interessant, deshalb schildere ich kurz diese Funktion.
Das Charlieplexing selbst, wird komplett im Timerinterrupt erledigt. Im Hauptprogramm braucht nur das Register leds mit dem Bitmuster geladen werden. Dabei bedeutet eine 1 LED an und eine 0 LED aus, ganz wie man es vom beschreiben eines Ports gewohnt ist.

Multiplexing

Um alle LEDs unabhängig von einander ein und ausschalten zu können müssen diese gemultiplext werden. Sollen z.B. alle LEDs eingeschaltet werden, leuchtet eine LED in Wirklichkeit nur ein 1/8 ihrer Zeit, bevor die nächste LED ihr 1/8 bekommt. Sind alle LEDs durch, fängt das ganze wieder von vorne an. Macht man das nur schnell genug, entsteht der Eindruck, das jede LED andauernd leuchtet. In meinem Programm dauert das 1/8 der Zeit 1,7 Millisekunden (mS).
Da der ATTiny genau zu diesen Zweck einen Timer eingebaut hat, wird dieser dazu benutzt, alle 1,7 mS einen Interrupt zu erzeugen. Der Interrupt wird also ca. (1 / 1,7mS) 588 mal, pro Sekunde aufgerufen. Da sich die 8 LEDs die Aufrufe Teilen, wird jede einzelne LED (588/8) 73,5 mal pro Sekunde angesteuert sodass unser Auge zu unserem Gehirn sagt: Ja, die LED ist an, da flackert nix .
Für die Bitmuster, die in die Portregister geschrieben werden, habe ich eine Tabelle angelegt. Da für jede LED, das beschreiben von 2 Registern erforderlich ist, hat die Tabelle 16 Einträge. Die ersten beiden Einträge sind für LED1 bestimmt und enthalten den Wert für das Datenrichtungsregister ddrb (1.Wert) und PortB (2.Wert) . Danach folgen die beiden Einträge für LED2. So wird die Tabelle bis zum Ende fortgeführt.

Charlieplexing LED1

Im Schaltplanausschnitt oben, habe ich die Leitungen eingefärbt um zu verdeutlichen, wie PB0..3 anzusteuern sind, damit LED1 leuchtet. Rot = +5Volt, blau= GND, gelb = Tristate. Um den ersten Wert der Tabelle (ddrb) zu bestimmen, setzt man die beiden Portbits die Tristate geschaltet werden auf 0 (Eingang) und die restlichen beiden auf 1 (Ausgang) und erhält Binär 0b1010. Damit die LED leuchtet, setzt man im 2. Wert (portb) die Anode von LED1 auf 1 und erhält 0b0010, wobei Bit3 PB3 auf GND schaltet und Bit 0 und 2 die Pullups der Tristateausgänge abschalten.

Beim Programmstart wird diese Tabelle ins SRam kopiert, da AVRs nur mit Hilfe des Z-Register auf den Flash Speicher zugreifen können und dieses  auch im Hauptprogramm benötigt wird. Die ISR ist Geschwindigkeitsoptimiert. Man könnte auch eine Tabelle mit 8 Bytes erstellen, müsste dann aber die Nibbles swappen und ausmaskieren was zusätzliche Zeit in Anspruch nehmen würde.

Der Interrupt  macht folgendes:

Hier die ISR (Interrupt Service Routine), die alle 1,7 mS aufgerufen wird.

;***********************************************************************************
;Timerinterrupt ca. alle 1,7 mS. Ca. 73 Hz Muxfrequenz
;***********************************************************************************
        in      s,sreg              ;SREG wegspeichern
        dec     tick                ;Alle 1,7 mS (Variable für Unterprogramm wait)
        out     ddrb,null           ;Alle Leds aus (Tristate)
        out     portb,null          ;Pullups auch aus
        lsl     ileds               ;Nächste Led ins Carry
        brne    LEDtst              ;War LED8 schon durch?
        movw    y,arrayl:arrayh     ;Wenn ja, Tabellenzeiger auf LED1
        mov     ileds,leds          ;ileds Update
        sec                         ;Zeroflag wird nach dem 8. "lsl ileds" gesetzt
        rol     ileds               ;LED1 ins Carry
LEDtst: ld      itemp1,y+           ;Tristate und Portdaten der gerade aktiven LED
        ld      itemp2,y+           ;Immer laden damit Tabellenzeiger erhöht wird (Y+)
        brcc    LEDoff              ;Bei C=0, LED ausgeschaltet lassen
        out     ddrb,itemp1         ;in die µC Register schreiben
        out     portb,itemp2
LEDoff: out     sreg,s              ;SREG zurück
        reti  
;***********************************************************************************
;LED Tabelle
;***********************************************************************************
        ;---------------------------------------------------------------------------
        ;Zum LED Einschalten Tabwert in DDRB - PORTB schreiben
        ;Zum Ausschalten der LED in beide Register eine null (Tristate, Pullup aus)
        ;Die Tab wird um den Z-Pointer freizuhalten ins SRam kopiert
        ;---------------------------------------------------------------------------
LEDTab: .db     0b0101,0b0100       ;LED1 einschalten, 1. Wert in DDRB, 2. Wert PortB
        .db     0b0101,0b0001       ;LED2 ein usw.
        .db     0b1001,0b1000
        .db     0b1001,0b0001
        .db     0b0110,0b0100
        .db     0b0110,0b0010
        .db     0b1010,0b1000
        .db     0b1010,0b0010
;***********************************************************************************

Nach dem sichern vom Statusregister und dem dekrementieren einer Variablen (soll hier nicht Interessieren),  werden erst einmal alle Ausgänge auf Tristate gelegt und somit die LEDs abgeschaltet. In ileds befindet sich eine Kopie von  leds. Letzteres wird im Hauptprogramm beschrieben.
Bei jedem Interrupt, wird aus ileds ein Bit ins Carry geschoben mit welchem entschieden wird, ob die LED leuchten soll oder nicht. Gleichzeitig schiebt der lsl Befehl eine 0 in ileds, so das nach 8 Interrupts Ileds zu 0b00000000 wird und der bedingte Sprung brne LEDtest nicht stattfindet.  So wird fortlaufend alle 8 Interrupts der Y-Pointer auf den Tabellenanfang (Bitmuster für LED1) gesetzt, und ileds neu geladen. Beim Laden des Bitmusters wird mit dem nachfolgenden rol Befehl eine Zusätzliche 1 mit eingeschoben um sicherzugehen, das immer 8 Schiebebefehle ausgeführt werden, bevor ileds wieder 0 wird. Die eingeschobene 1 ist dann durch das komplette Byte gewandert und wieder im Carry angekommen (deshalb könnte man sogar noch den sec Befehl wegoptimieren , zum besseren Verständnis lasse ich den mal stehen).
Mit dieser Technik spart man sich ein Register das man als Interruptzähler Reservieren müsste und macht den Code noch etwas kürzer (und schneller), was in der ISR besonders wünschenswert ist. Auch aus Geschwindigkeitsgründen, wurde hier der Y-Pointer (Zeigt auf Tabelle LED1) beim Programmstart in die Register arrayl und arrayh kopiert, und kann so mit einem Assemblerbefehl in der ISR, als 16Bit-Wort gemoved werden. Die Tabellenwerte werden immer geladen, auch wenn die entsprechende LED nicht leuchten soll, da mit dem laden auch gleichzeitig der Tabellenzeiger erhöht wird. Das geht  deutlich schneller als vergleichen, verzweigen und anschließendem addieren zum Zeiger.  Hier werden die beiden out Befehle  einfach übersprungen, wenn die LED ausbleiben soll.

Anmerkung am Schluss:

Nach Fertigstellung meiner Platine, habe ich einen Denkfehler meinerseits, in der Software ausgeglichen. Der Bargraph bewegte sich von rechts nach links bei einer Spannungserhöhung . Da ich die gewohnte Richtung von links nach rechts bervorzuge, habe ich die Reihenfolge der Werte in der Tabelle so geändert, das man die Tabelle nun von unten nach oben lesen muss.

Download

Hier könnt ihr euch die Eagle Dateien und den Quellcode downloaden.

Viel Spaß beim Basteln,
Jürgen