Da mir der unter Raspberry Pi – Serielle Schnittstelle (RS232) nutzen vorgestellte Aufbau zu sperrig war, habe ich ein wenig gesucht, ob sich nicht ein Pegelwandler zusammen löten lässt, der direkt zwischen GPIO der Pi und RX / TX Pins des Mikrocontrollers hängt.
Gedacht ist die Übung als Vorbereitung für eine interne Systemuhr auf dem ATMega8, die von einem PC oder z.B. auch der Raspberry Pi gestellt und abgelesen werden kann.
Wie im Ablaufplan (Bild links) dargestellt, habe ich das Programm so strukturiert, dass
im Hauptprogramm die Variablen initialisiert werden und der Interrupt sobald ein Byte über RX/UART empfangen wird, aktiviert ist
Das Unterprogramm IncommingByte entscheidet, ob es sich um ein Aktives, oder Neues Unterprogramm handelt und ruft ein weiteres Unterprogramm FunctionCall auf, was an das aktive oder gewählte Unterprogramm weiterleitet.
Das Unterprogramm StelleUhr, wird durch das Senden eines Bytes mit dem Inhalt 1 aufgerufen und erwartet das danach 3 weitere Bytes als Inhalt für die Register Stunde, Minute und Sekunde via RX/UART übergeben werden. Diese werden in den Registern gespeichert.
Das Unterprogramm Sende Uhrzeit, wird mit einem Byte mit Inhalt 2 aufgerufen und sendet 3 Byte mit den Inhalten Stunde, Minute und Sekunde zurück an den PC.
Der hier abgebildete Assemblercode ist mit vielen Kommentaren versehen.
Quellcode des Assemblerprogramms:
; avrdude -p m8 -c stk500v2 -P /dev/ttyACM0 -U flash:w:Registerinhalte_via_UART_steuern_und_ausgeben.hex
; 13.9.2013 Das Programm soll Registerinhalte per serielle Datenübertragung manipulieren können und diese auch wieder ausgeben
.NOLIST
;.INCLUDE "/home/henry/Dokumente/Mikrocontroller/AVR-Assembler/AVR_Definitionsfiles/m8def.inc"
.INCLUDE "m8def.inc"
.LIST
;
.def Sekunde = R2
.def Minute = R3
.def Stunde = R4
;--------------------------------
.def temp = R16 ; Register 16 der Variable temp zuweisen
.def ByteNummer = R17 ; Enthält die Nummer des aktuell übertragenen Bytes innerhalb eine Unterprogramms
.def aktivesUnterprogramm = R19 ; Enthält die numerische ID des aktuell aktiven Unterprogamms
.def EmpfRXByte = R20 ; Enthält das zuletzt via. UART empfangene Byte
.equ F_CPU = 16000000 ; Systemtakt in Hz
.equ BAUD = 9600 ; Baudrate
; Berechnungen
.equ UBRR_VAL = ((F_CPU+BAUD*8)/(BAUD*16)-1) ; clever runden
.equ BAUD_REAL = (F_CPU/(16*(UBRR_VAL+1))) ; Reale Baudrate
.equ BAUD_ERROR = ((BAUD_REAL*1000)/BAUD-1000) ; Fehler in Promille
.if ((BAUD_ERROR>10) || (BAUD_ERROR<-10)) ; max. +/-10 Promille Fehler
.error "Systematischer Fehler der Baudrate grösser 1 Prozent und damit zu hoch!"
.endif
; Startvektoren
.org 0x00
rjmp main
.org URXCaddr ; Interruptvektor für UART-Empfang
rjmp IncommingByte
; --------------------------------Hauptprogramm--------------------------------------
main:
; Stackpointer initialisieren
ldi temp, HIGH(RAMEND)
out SPH, temp
ldi temp, LOW(RAMEND)
out SPL, temp
; Baudrate einstellen
ldi temp, HIGH(UBRR_VAL)
out UBRRH, temp
ldi temp, LOW(UBRR_VAL)
out UBRRL, temp
; Frame-Format: 8 Bit
ldi temp, (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0) out UCSRC, temp sbi UCSRB, RXCIE ; Interrupt bei Empfang sbi UCSRB, RXEN ; RX (Empfang) aktivieren sei ; Interrupts global aktivieren loop: rjmp loop ; Endlosschleife / idle mode ; ---------------------------ENDE Hauptprogramm-------------------------------------- ; ---------------------------IncommingByte------------------------------------------- ; Interruptroutine: wird ausgeführt sobald ein Byte über das UART empfangen wurde IncommingByte: in EmpfRXByte, UDR ; empfangenes Byte lesen, ; dadurch wird auch der Interrupt gelöscht cpi aktivesUnterprogramm, 1 ; vergleich aktivesUnterprogramm mit 1 brge UPAufruf ; ist aktivesUnterprogramm > 0, springe zu UPAufruf
mov aktivesUnterprogramm, EmpfRXByte ; kopiere EmpfRXByte in aktivesUnterprogramm
clr ByteNummer ; setze ByteNummer aud 0
UPAufruf:
rcall FunctionCall ; rufe Unterprogramm FunctionCall auf
EndIncommingByte:
reti ; Interrupt beenden
; ----------------------Ende-IncommingByte-------------------------------------------
; ---------------------------FunctionCall--------------------------------------------
; Abfrage aktivesUnterprogramm und Aufruf der entsprechenden Unterprogramme
FunctionCall:
cpi aktivesUnterprogramm, 1
breq Byte1 ; Wenn eine 1 übertragen wurde zu Byte1 springen
cpi aktivesUnterprogramm, 2
breq Byte2 ; Wenn eine 2 übertragen wurde zu Byte2 springen
rjmp EndFunctionCall ; zum Unterprogramm - Ende (Es wurde kein Wert der einem gültigem Unterprogarmm entspricht übertragen.)
Byte1:
rcall StelleUhr ; StelleUhr aufrufen
rjmp EndFunctionCall ; zum Unterprogramm - Ende
Byte2:
rcall SendeUhrzeit ; SendeUhrzeit aufrufen
rjmp EndFunctionCall ; zum Unterprogramm - Ende
EndFunctionCall:
ret ; Ende - zurück springen
; ----------------------Ende-FunctionCall--------------------------------------------
; ---------------------------StelleUhr-----------------------------------------------
StelleUhr:
cpi ByteNummer, 1 ; vergleich ByteNummer mit 1
breq StelleSekunde ; ist ByteNummer = 1, springe zu StelleSekunde
cpi ByteNummer, 2 ; vergleich ByteNummer mit 2
breq StelleMinute ; ist ByteNummer = 2, springe zu StelleMinute
cpi ByteNummer, 3 ; vergleich ByteNummer mit 3
breq StelleStunde ; ist ByteNummer = 3, springe zu StelleStunde
rjmp ErhoeheByteNummer ; springe zu ErhoeheByteNummer
StelleSekunde:
mov Sekunde, EmpfRXByte ; Kopiere EmpfRXByte in Sekunde
rjmp ErhoeheByteNummer ; spinge zu ErhoeheByteNummer
StelleMinute:
mov Minute, EmpfRXByte ; Kopiere EmpfRXByte in Minute
rjmp ErhoeheByteNummer ; spinge zu ErhoeheByteNummer
StelleStunde:
mov Stunde, EmpfRXByte ; Kopiere EmpfRXByte in Stunde
clr aktivesUnterprogramm ; setze aktivesUnterprogramm auf Null, wir sind fertig mit Uhr stellen :-)
rjmp EndStelleUhr ; spinge zu EndStelleUhr
ErhoeheByteNummer:
inc ByteNummer ; erhöhe ByteNummer um 1
EndStelleUhr:
ret ; Ende - zurück springen
; ----------------------Ende-StelleUhr-----------------------------------------------
; ---------------------------SendeUhrzeit--------------------------------------------
SendeUhrzeit:
cbi UCSRB, RXEN ; RX (Empfang) deaktivieren
sbi UCSRB, TXEN ; TX (Senden) aktivieren
Sekunde_wait:
sbis UCSRA,UDRE ; Warten bis UDR für das nächste Byte bereit ist
rjmp Sekunde_wait ; Springe zurück zu Sekunde_wait, solange UDR noch nicht bereit ist
out UDR, Sekunde ; sende das Byte Sekunde via UART
Minute_wait:
sbis UCSRA,UDRE ; Warten bis UDR für das nächste Byte bereit ist
rjmp Minute_wait ; Springe zurück zu Minute_wait, solange UDR noch nicht bereit ist
out UDR, Minute ; sende das Byte Minute via UART
Stunde_wait:
sbis UCSRA,UDRE ; Warten bis UDR für das nächste Byte bereit ist
rjmp Stunde_wait ; Springe zurück zu Stunde_wait, solange UDR noch nicht bereit ist
out UDR, Stunde ; sebnde das Byte Stunde via UART
cbi UCSRB, TXEN ; TX (Senden) deaktivieren
sbi UCSRB, RXEN ; RX (Empfang) aktivieren
clr aktivesUnterprogramm ; setze aktivesUnterprogramm auf Null, wir sind fertig mit Uhrzeit ausgeben :-)
EndSendeUhrzeit:
ret ; Ende - zurück springen
; ----------------------Ende-SendeUhrzeit--------------------------------------------
Das Prinzip der seriellen Schnittstelle (RS232), wie Sie früher an nahezu jedem PC zu finden war ist nach wie vor aktuell.
Für den PC sind RS232 zu USB Adapter verfügbar und auf der Mikrocontroller – Seite lässt sich z.B. ein Bluetooth zu Serial Adapter einsetzen, womit ganz auf die Kabel verzichtet werden kann und die Kommunikation auch jedes Smartphone übernehmen kann.
In diesem Beispiel soll ein Byte vom PC an das Evaluationboard gesendet werden.
Sobald Dieses über UART empfangen wird, beginnt die Prüfung des übertragenen Bytes.
Ist der Wert = 1, soll die LED1 eingeschaltet werden, bei 2 soll die LED1 ausgeschaltet werden.
Bei 3 soll die LED2 eingeschaltet werden und bei 4 soll die LED2 ausgeschaltet werden.
Werden andere Inhalte empfangen, sollen keine Aktionen ausgelöst werden.
Um Bytes über die serielle Schnittstelle zu übertragen, gibt es mehrere Möglichkeiten.
Unter Linux nutze ich Cutecom, unter Windows geht Putty.
Es gibt aber auch noch viele andere Programme. Auch ein entsprechendes Script ist für diesen Zweck schnell geschrieben.
Wie im Ablaufplan Links zu erkennen, ist dieses Beispiel ein wenig komplexer als die vorherigen beiden Posts zum Evaluationsboard (LED 1 und 2 über Taster T1 und T2 schalten und Hello World über serielle Schnittstelle senden).
Dennoch lässt sich der Code sehr gut in Assembler abbilden.
Kein Grund hier C einsetzen zu müssen oder gar ein Embedded Linux System o.ä.
Der hier abgebildete Assemblercode ist mit vielen Kommentaren versehen.
Quellcode des Assemblerprogramms:
; avrdude -p m8 -c stk500v2 -P /dev/ttyACM0 -U flash:w:LEDs_via_UART_schalten.hex
; 31.8.2013 Das Programm soll über UART empfangene Bytes auswerten und wenn ein Zahlenwert
; zwischen 1 und 4 empfangen wurde die LED1/2 folgendermßen schalten
; 1 - LED1 an
; 2 - LED1 aus
; 3 - LED2 an
; 4 - LED2 aus
; Der UART Receive Teil des Programms ist in Teilen aus
; http://www.mikrocontroller.net/articles/AVR-Tutorial:_UART#Empfangen_von_Zeichen_per_Interrupt
; übernommen. Dort gibt es noch viel weitere Infos und Tipps
.NOLIST
;.INCLUDE "/home/henry/Dokumente/Mikrocontroller/AVR-Assembler/AVR_Definitionsfiles/m8def.inc"
.INCLUDE "m8def.inc"
.LIST
;
.def temp = R16 ; Register 16 der Variable temp zuweisen
.def RXByte = R17 ; Register 17 der Variable RXByte zuweisen
.equ F_CPU = 16000000 ; Systemtakt in Hz
.equ BAUD = 9600 ; Baudrate
.equ LED1 = PD5 ; Pin LED1
.equ LED2 = PD6 ; Pin LED2
; Berechnungen
.equ UBRR_VAL = ((F_CPU+BAUD*8)/(BAUD*16)-1) ; clever runden
.equ BAUD_REAL = (F_CPU/(16*(UBRR_VAL+1))) ; Reale Baudrate
.equ BAUD_ERROR = ((BAUD_REAL*1000)/BAUD-1000) ; Fehler in Promille
.if ((BAUD_ERROR>10) || (BAUD_ERROR<-10)) ; max. +/-10 Promille Fehler
.error "Systematischer Fehler der Baudrate grösser 1 Prozent und damit zu hoch!"
.endif
; Startvektoren
.org 0x00
rjmp main
.org URXCaddr ; Interruptvektor für UART-Empfang
rjmp IncommingByte
; --------------------------------Hauptprogramm--------------------------------------
main:
; Stackpointer initialisieren
ldi temp, HIGH(RAMEND)
out SPH, temp
ldi temp, LOW(RAMEND)
out SPL, temp
; Baudrate einstellen
ldi temp, HIGH(UBRR_VAL)
out UBRRH, temp
ldi temp, LOW(UBRR_VAL)
out UBRRL, temp
ldi temp, (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0)
out UCSRC, temp
sbi UCSRB, RXCIE ; Interrupt bei Empfang
sbi UCSRB, RXEN ; RX (Empfang) aktivieren
; Port D, LED Pins auf Ausgang und Taster Pins auf Eingang schalten
ldi temp, (1<<LED1) | (1<<LED2)
out DDRD, temp ; LED1/2 Pollin Evaluationsboard ,, PD5/6 auf Ausgang schalten
sei ; Interrupts global aktivieren
loop:
rjmp loop ; Endlosschleife / idle mode
; ---------------------------ENDE Hauptprogramm--------------------------------------
; ---------------------------IncommingByte-------------------------------------------
; Interruptroutine: wird ausgeführt sobald ein Byte über das UART empfangen wurde
IncommingByte:
in RXByte, UDR ; empfangenes Byte lesen,
; dadurch wird auch der Interrupt gelöscht
; Abfrage des Byte Inhaltes und aufruf der entsprechenden Unterprogramme zum schalten der LED's
cpi RXByte, 1
breq Byte1 ; Wenn eine 1 übertragen wurde zu Byte1 springen
cpi RXByte, 2
breq Byte2 ; Wenn eine 2 übertragen wurde zu Byte2 springen
cpi RXByte, 3
breq Byte3 ; Wenn eine 3 übertragen wurde zu Byte3 springen
cpi RXByte, 4
breq Byte4 ; Wenn eine 4 übertragen wurde zu Byte4 springen
rjmp EndIncommingByte ; zum Unterprogramm - Ende (Es wurde kein Wert zwischen 1 und 4 übertragen.)
Byte1:
rcall LED1Ein ; LED1Ein aufrufen
rjmp EndIncommingByte ; zum Unterprogramm - Ende
Byte2:
rcall LED1Aus ; LED1Aus aufrufen
rjmp EndIncommingByte ; zum Unterprogramm - Ende
Byte3:
rcall LED2Ein ; LED2Ein aufrufen
rjmp EndIncommingByte ; zum Unterprogramm - Ende
Byte4:
rcall LED2Aus ; LED2Aus aufrufen
rjmp EndIncommingByte ; zum Unterprogramm - Ende
EndIncommingByte:
reti ; Interrupt beenden
; ----------------------Ende-IncommingByte-------------------------------------------
; ----------------------Die 4 Unterprogramme zum LED's schalten----------------------
LED1Ein:
sbi PORTD, LED1 ; Setze das LED1 Bit in PORTD
ret
LED1Aus:
cbi PORTD, LED1 ; Lösche das LED1 Bit in PORTD
ret
LED2Ein:
sbi PORTD, LED2 ; Setze das LED2 Bit in PORTD
ret
LED2Aus:
cbi PORTD, LED2 ; Lösche das LED2 Bit in PORTD
ret
; -----------------Ende-Die 4 Unterprogramme zum LED's schalten----------------------
Das Pollin Evaluations – Board ist mit einer seriellen RS232 Schnittstelle ausgestattet, was einen einfachen zum Datenaustausch mit einem PC ermöglicht.
Ziel ist es ein ‘Hello World’ an die serielle Schnittstelle zu senden, sobald der Taster T1 gedrückt wurde.
Die im Assemblerprogramm anzusteuernden Ports und Pins sind aus der Grafik auf der rechten Seite ersichtlich.
Auch dieses Beispiel habe ich so ähnlich aus dem AVR Tutorial von Mikrocontroller.net entnommen und auf die Gegebenheiten des Pollin Boards angepasst.
Der hier abgebildete Assemblercode ist mit vielen Kommentaren versehen.
Quellcode des Assemblerprogramms:
; avrdude -p m8 -c stk500v2 -P /dev/ttyACM0 -U flash:w:HelloWorld_via_UART.hex
; 5.8.2013 Das Programm soll die Worte 'Hello World' via. UART ausgeben, sobald der Taster1 gedrückt wurde
; Das Programm ist zum grossen Teil aus
; http://www.mikrocontroller.net/articles/AVR-Tutorial:_UART#Senden_von_Zeichen
; übernommen. Dort gibt es noch viel weitere Infos und Tipps
.NOLIST
;.INCLUDE "/home/henry/Dokumente/Mikrocontroller/AVR-Assembler/AVR_Definitionsfiles/m8def.inc"
.INCLUDE "m8def.inc"
.LIST
;
.def temp = R16
.equ F_CPU = 16000000 ; Systemtakt in Hz
.equ BAUD = 9600 ; Baudrate
.equ Taster1 = PD2
; Berechnungen
.equ UBRR_VAL = ((F_CPU+BAUD*8)/(BAUD*16)-1) ; clever runden
.equ BAUD_REAL = (F_CPU/(16*(UBRR_VAL+1))) ; Reale Baudrate
.equ BAUD_ERROR = ((BAUD_REAL*1000)/BAUD-1000) ; Fehler in Promille
.if ((BAUD_ERROR>10) || (BAUD_ERROR<-10)) ; max. +/-10 Promille Fehler
.error "Systematischer Fehler der Baudrate grösser 1 Prozent und damit zu hoch!"
.endif
.org 0x00
rjmp main
; Hauptprogramm
main:
; Stackpointer initialisieren
ldi temp, HIGH(RAMEND)
out SPH, temp
ldi temp, LOW(RAMEND)
out SPL, temp
; Baudrate einstellen
ldi temp, HIGH(UBRR_VAL)
out UBRRH, temp
ldi temp, LOW(UBRR_VAL)
out UBRRL, temp
; Frame-Format: 8 Bit
ldi temp, (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0)
out UCSRC, temp
sbi UCSRB,TXEN ; TX aktivieren
sei ; Interrupts global aktivieren Tastenabfrage:
sbic PIND, Taster1
rcall SendHelloWorld ; Wenn Taster1 auf High (gedrückt), dann rufe SendHelloWorld auf
rjmp Tastenabfrage ; Endlosschleife
SendHelloWorld:
T1entprellen:
sbic PIND, Taster1 ; loope solange der Taster1 nicht wieder losgelassen wurde
rjmp T1entprellen
ldi zl,low(my_HelloWorld*2) ; Z Pointer laden
ldi zh,high(my_HelloWorld*2)
rcall serout_string
ret
; Ausgabe eines Strings aus dem Flash serout_string:
lpm ; nächstes Byte aus dem Flash laden
and r0,r0 ; = Null?
breq serout_string_ende ; wenn ja, ->; Ende
serout_string_wait:
sbis UCSRA,UDRE ; Warten bis UDR für das nächste
; Byte bereit ist
rjmp serout_string_wait
out UDR, r0
adiw zl:zh,1 ; Zeiger erhöhen
rjmp serout_string ; nächstes Zeichen bearbeiten
serout_string_ende:
ret ; zurück zum Hauptprogramm
my_HelloWorld: .db "Hello World",10,0
Video, zur Demonstration
Im Video wird der serielle Port mittels eines Perl Scripts abgefragt.
Das geht natürlich auch mit Programmen wie Cutecom oder Putty.
Die meisten elektronischen Geräte / Schaltungen / Projekte müssen in irgendeiner Form auf Eingaben reagieren.
Sei es wie hier dargestellt auch ‘nur’ auf einen Taster.
Das Pollin Board bietet mehrere Taster, LED’s, einen Piepser und weitere Hardware zum ansteuern.
In diesem Beispiel wird die LED1 und 2 über Taster 1 und 2 angesteuert.
Wie in der Grafik zu erkennen, sind alle LED’s und Taster mit dem PortD verbunden.
Für dieses Beispiel werden keine weiteren Bauteile rund um das Evaluationsboard benötigt.
Der hier abgebildete Assemblercode ist mit vielen Kommentaren versehen.
Ich habe den Source auf Grundlage dieser Beispiele von Mikrocontroller.net aufgebaut.
Quellcode des Assemblerprogramms:
; avrdude -p m8 -c stk500v2 -P /dev/ttyACM0 -U flash:w:LED1_und_2_mit_Taster1_und_2.hex
; 9.8.2013 Das Programm soll sobald der Taster1 gedrückt wurde, die LED1 an bzw. ausschalten und
; sobald der Taster2 gedrückt wurde, die LED2 an bzw. ausschalten
; Das Programm ist zum grossen Teil aus
; http://www.mikrocontroller.net/articles/AVR-Tutorial:_IO-Grundlagen
; übernommen. Dort gibt es noch viel weitere Infos und Tipps
.NOLIST
;.INCLUDE "/home/henry/Dokumente/Mikrocontroller/AVR-Assembler/AVR_Definitionsfiles/m8def.inc"
.INCLUDE "m8def.inc"
.LIST
.def temp = R16
.equ Taster1 = PD2
.equ Taster2 = PD3
.equ LED1 = PD5
.equ LED2 = PD6
; Port D, LED Pins auf Ausgang und Taster Pins auf Eingang schalten
ldi temp, (1<<LED1) | (1<<LED2)
out DDRD, temp ; LED1/2 Pollin Evaluationsboard ,, PD5/6 auf Ausgang schalten
Tastenabfrage:
sbic PIND, Taster1
rjmp LED1Schalten ; Wenn Taster1 auf High (gedrückt), dann rufe LED1Schalten
sbic PIND, Taster2
rjmp LED2Schalten ; Wenn Taster2 auf High (gedrückt), dann rufe LED2Schalten
rjmp Tastenabfrage ; zurück zur Tastenabfrage
LED1Schalten:
T1entprellen:
sbic PIND, Taster1 ; loope solange der Taster1 nicht wieder losgelassen wurde
rjmp T1entprellen
sbic PORTD, LED1 ; überspringe die nächste Instruktion, wenn die LED1 ausgeschaltet ist
rjmp LED1ausschalten
LED1einschalten:
sbi PORTD, LED1 ; Setze das LED1 Bit in PORTD
rjmp EndeLED1schalten ; springe zu EndeLED1schalten
LED1ausschalten:
cbi PORTD, LED1 ; Lösche das LED1 Bit in PORTD
EndeLED1schalten:
rjmp Tastenabfrage ; zurück zum Hauptprogramm
LED2Schalten:
T2entprellen:
sbic PIND, Taster2 ; loope solange der Taster2 nicht wieder losgelassen wurde
rjmp T2entprellen
sbic PORTD, LED2 ; überspringe die nächste Instruktion, wenn die LED2 ausgeschaltet ist
rjmp LED2ausschalten
LED2einschalten:
sbi PORTD, LED2 ; Setze das LED2 Bit in PORTD
rjmp EndeLED2schalten ; springe zu EndeLED2schalten
LED2ausschalten:
cbi PORTD, LED2 ; Lösche das LED2 Bit in PORTD
EndeLED2schalten:
rjmp Tastenabfrage ; zurück zum Hauptprogramm
Die manuelle Messung hat sich als nicht praktikabel erwiesen. So ist es z.B. Voraussetzung vor Ort zu sein. Scheint die Sonne also stark wenn ich im Urlaub bin, schaltet der Laderegler ab und weg ist er, der Ertrag. Auch ein Grund, warum ich im sonnigen Halbjahr soviel verschenkt habe. Die bisher gemessene Leistung habe ich auf der Seite 10 Kilowattstunden in 2012 zusammengefasst.
Ein paar Bilder vom ‘Messgerät’ und dem Aufbau der Versuchsanlage:
Video:
Zielstellung:
Die Batterie soll immer einen gewissen Ladezustand halten, der für Aktivitäten wie Rasen trimmen oder andere diverse Elektrogeräte in der Garage benötigt wird.
Die Solarzellen sollen nicht leer laufen. Das heißt, dass alles was die Sonne bringt umgesetzt werden soll und wenn es auch erst einmal nur über die 26 Watt Glühlampe verbraucht wird.
Die Lampe soll dementsprechend ein- oder ausgeschaltet werden, um die überschüssige Energie zu verbrauchen
Die Anzahl der Minuten ( 10 Minutenintervalle), die die Lampe bereits eingeschaltet war soll via. Bluetooth auf dem Handy angezeigt werden.
Ablaufschema Der entsprechenden Logik:
Quellcode meines Assemblerprogramms:
;Pollin Board Stromverbrauchsgenerator und Messeinheit mit ATMega8
;Ausgabe via UART
.NOLIST
.INCLUDE "m8def.inc"
.LIST
;
; Henry Koch 5.8.2012
;
.def vierSekunden = r1
.def Ladewert = r2
.def IntervallCounter = r3
;----------------------------
.def temp = r16 ; Register für kleinere Arbeiten
.def zeichen = r17 ; in diesem Register wird das Zeichen an die
; Ausgabefunktion übergeben
.def count = r18
.def temp1 = r19
.def temp2 = r20
.def sync1 = r21
.def sync2 = r22
;
;.def messungen = r23
.equ Schwellwert = 212 ; 212 entspricht ca. 13,3 Volt bei meinem Spannungsteiler
.equ F_CPU = 4000000 ; Systemtakt in Hz
.equ BAUD = 9600 ; Baudrate
;
; Berechnungen
.equ UBRR_VAL = ((F_CPU+BAUD*8)/(BAUD*16)-1) ; clever runden
.equ BAUD_REAL = (F_CPU/(16*(UBRR_VAL+1))) ; Reale Baudrate
.equ BAUD_ERROR = ((BAUD_REAL*1000)/BAUD-1000) ; Fehler in Promille
;
.if ((BAUD_ERROR>10) || (BAUD_ERROR Ende
serout_string_wait:
sbis UCSRA,UDRE ; Warten bis UDR für das nächste
; Byte bereit ist
rjmp serout_string_wait
out UDR, r0
adiw zl:zh,1 ; Zeiger erhöhen
rjmp serout_string ; nächstes Zeichen bearbeiten
serout_string_ende:
ret ; zurück zum Hauptprogramm
;
; Umwandlung in Dezimalzahlen
;**********************************************************************
;
; Eine 8 Bit Zahl ohne Vorzeichen ausgeben
;
; Übergabe: Zahl im Register temp1
; veränderte Register: keine
;
lcd_number:
push temp1 ; die Funktion verändert temp1 und temp2,
push temp2 ; also sichern wir den Inhalt, um ihn am Ende
; wieder herstellen zu können
mov temp2, temp1 ; das Register temp1 frei machen
; abzählen wieviele Hunderter
; in der Zahl enthalten sind
;** Hunderter **
ldi temp1, '0'-1 ; temp1 mit ASCII '0'-1 vorladen
lcd_number_1:
inc temp1 ; ASCII erhöhen (somit ist nach dem ersten
; Durchlauf eine '0' in temp1)
subi temp2, 100 ; 100 abziehen
brcc lcd_number_1 ; ist dadurch kein Unterlauf entstanden?
; nein, dann zurück zu lcd_number_1
subi temp2, -100 ; 100 wieder dazuzählen, da die
; vorherhgehende Schleife 100 zuviel
; abgezogen hat
rcall lcd_data ; die Hunderterstelle ausgeben
;** Zehner **
ldi temp1, '0'-1 ; temp1 mit ASCII '0'-1 vorladen
lcd_number_2:
inc temp1 ; ASCII erhöhen (somit ist nach dem ersten
; Durchlauf eine '0' in temp1)
subi temp2, 10 ; 10 abziehen
brcc lcd_number_2 ; ist dadurch kein Unterlauf enstanden?
; nein, dann zurück zu lcd_number_2
subi temp2, -10 ; 10 wieder dazuzählen, da die
; vorherhgehende Schleife 10 zuviel
; abgezogen hat
rcall lcd_data ; die Zehnerstelle ausgeben
;** Einer **
ldi temp1, '0' ; die Zahl in temp2 ist jetzt im Bereich
add temp1, temp2 ; 0 bis 9. Einfach nur den ASCII Code für
rcall lcd_data ; '0' dazu addieren und wir erhalten dierekt
; den ASCII Code für die Ziffer
pop temp2 ; den gesicherten Inhalt von temp2 und temp1
pop temp1 ; wieder herstellen
ret ; und zurück
lcd_data:
mov zeichen, temp1
loop1:
rcall serout ; Unterprogramm aufrufen
rcall sync
brne loop1 ; solange die Null nicht erreicht ist springe zur loop
ret
my_Schwellwert: .db "ges. SchwellWert: ",0
my_Ladewert: .db "akt. Ladewert: ",0
my_Intervalle: .db "Anz. 10 Min. Int.: ",0
my_10MinIntervall: .db "**10 Min. Int. **",10,0
Schaltplan und technische Details:
Das Pollin Board ist an der Stelle des ATMega8 Prozessors mit einem 4 MHZ Quarz bestückt.
Das ist so, weil der Stromverbrauch des Prozessors so niedriger ist und die 16 MHZ für die Anwendung absolut nicht erforderlich sind.
Das hat den Haken, dass die Baud Rate für die Kommunikation via UART nicht riesig sein kann. Aber 9600 Baud sind vollkommen ok.
Die 5V Stromversorgung für das Pollin Board und die Relais Steuereinheit wurde mit einem handelsüblichen KFZ – USB Adapter realisiert.
Wünsche an eine verbesserte Version:
Stromzuführung für das Bluetooth Modul mit Taster des Pollin Boards steuern
UART Kommunikation in der Programmlogik nur aktiv betreiben, wenn das Bluetooth Modul angeschaltet ist
Es sollte ein Tageswechsel erkennbar sein, z.B. Start eines neuen Counters, mit jedem neuen Tag und Ausgabe einer Counterliste
Die 10 Minuten AUS Intervalle sind ungünstig. Möglicherweise ist der Ladezustand schon während der 10 Minuten wieder erreicht, bzw. geht so hoch, dass der Laderegler abschaltet. Habe ich am 9.8.2012 beobachtet!
normales Relais durch SSR tauschen und dementsprechend ansteuern
bessere Stabilisierung der Spannung, so dass die Messwerte nicht so sehr schwanken
den Spannungsteiler evtl. mit einer 10 Volt Zehnerdiode realisieren