Lernpaket Mikrocontroller Kap 7
7 Interfaces und Datenlogger
Die Verwendung der
Interface-Software wurde bereits in Kap. 2 vorgestellt. Hier soll das dahinter
liegende Programm genauer erläutert werden. Es ergeben sich dabei neue
Möglichkeiten und Varianten für spezielle Aufgaben. Der ATtiny85 besitzt 512
Byte RAM und 512 Byte EEPROM. Damit lassen sich erweiterte Interfaces und
Datenlogger realisieren.
7.1 Das universelle Interface
Das Interface-Programm des
Lernpakets wurde bereits in Kap. 2 ausführlich vorgestellt und erprobt. Es
basiert auf Byte-Kommandos, über die Ausgänge gesteuert oder Eingänge abgefragt
werden. Das Interface-Protokoll ist angelehnt an das SIOS-Interface von
AK-Modul-Bus und verwendet Gruppen von Byte-Kommandos, die sich leicht
erweitern lassen.
&H10: digitale Ausgänge
setzen
&H20: digitale Eingänge
lesen
&H30: analoge Eingänge
lesen
&H40: analoge Ausgänge
setzen
Das Interface-Programm
besteht im Kern aus einer Interpreterschleife, in der Kommandos vom PC
ausgewertet und ausgeführt werden. Am Anfang der Schleife wird ein Byte
empfangen und als Kommando in die Variable Kom kopiert. Gültige Kommandos sind
z.B. die Bytes 1, 16, 17, 18 und 32. Entsprechende Programmabschnitte führen
jeweils einen Vergleich mit einer Konstanten aus. Wenn ein Kommando erkannt
wurde, folgt eine zugehörige Ausgabe oder Eingabe. Im anderen Fall verzweigt
das Programm zum nächsten Vergleich. Die gesamte Kommunikation läuft mit 9600
Baud. Ein typisches Kommando mit zwei Bytes wird daher in etwa zwei
Millisekunden ausgeführt. Eine Interface-Abfrage mit dem Kommando 1 liefert die
Antwort 100 und dient nur zur Überprüfung der Bereitschaft.
Das SIOS-kompatible Kommando
16 (&H10) dient zur Ausgabe eines folgenden Bytes an den Port B. Mit dem
Kommando 17 wird die Datenrichtung festgelegt. In beiden Fällen werden Die
Leitungen RXD und TXD ausmaskiert, um die serielle Kommunikation nicht zu
stören. Das Kommando 18 initialisiert den PWM-Ausgang PWM0A.
Das Kommando 32 (&H20) fragt
den Zustand des Ports B ab. Mit der Abfrage 48 und 49 erhält man 8-Bit-Werte
des AD-Wandlers für die Kanäle 2 und 3. Mit 56 und 57 werden dieselben Kanäle
mit einer Auflösung von 10 Bit gemessen, wobei die Antwort dann jeweils aus
zwei Bytes besteht. Das Kommando 64 schließlich dient zur Ausgabe eines
8-Bit-PWM-Wertes an PWM0A.
'ATtiny85
Interface
$regfile
= "attiny85.dat"
$crystal
= 8000000
$hwstack
= 8
$swstack
=
4
'
16
$framesize
= 4
Dim
Kom As Byte
Dim
Dat As Byte
Dim
U As Word
Dim
Hi As Byte
Dim
Lo As Byte
Dim
N As Byte
Dim
Nn As Word
Dim
D(400) As Byte
Open
"comb.2:9600,8,n,1" For Input As #1
Open
"comb.1:9600,8,n,1" For Output As #2
Config
Adc = Single , Prescaler = 16
Start
Adc
Clkpr
= 128 '8 MHz
Clkpr
= 0
Clkpr
= 0
Ddrb
= &B00000011
Do
Get #1 , Kom
If Kom = 1 Then
Put #2 , 100
End If
If Kom = 16 Then
Get #1 , Dat
Dat = Dat And &B00011011
Dat = Dat Or &B00000010
Portb = Dat
End If
If Kom = 17 Then
Get #1 , Dat
Dat = Dat And
&B00011011
Dat = Dat Or &B00000010
Ddrb = Dat
End If
If Kom = 18 Then
Config Timer0 = Pwm , Prescale = 64 , Compare A
Pwm = Clear Up
Pwm0a = 0
End If
If Kom = 32 Then
Dat = Pinb
Put #2 , Dat
End If
If Kom = 48 Then
U = Getadc(2)
Shift U , Right , 2
Put #2 , U
End If
If Kom = 49 Then
U = Getadc(3)
Shift U , Right , 2
Put #2 , U
End If
If Kom = 56 Then
U = Getadc(2)
Hi = High(u)
Lo = Low(u)
Put #2 , Hi
Put #2 , Lo
End If
If Kom = 57 Then
U = Getadc(3)
Hi = High(u)
Lo = Low(u)
Put #2 , Hi
Put #2 , Lo
End If
If Kom = 64 Then
Get #1 , Dat
Pwm0a = Dat
End If
…
…
Loop
Listing 7.1:
I/O-Funktionen des Interface-Programms
7.2 Das Oszilloskop
Das Interface enthält eine
Oszilloskop-Funktion. Im Einkanal-Betrieb (Kommando 100) werden 361 Messwerte
im Byte-Format gespeichert. Sofort nach der eigentlichen Messung schickt das
Programm die Daten an den PC.
Config
Adc = Single , Prescaler = 16
…
If
Kom = 100 Then
For Nn = 1 To 362
U = Getadc(2)
Shift U , Right , 2
D(nn) = U
Next Nn
For Nn = 1 To 361
Dat = D(nn)
Put #2 , Dat
Next Nn
End If
If Kom = 101 Then
For N = 0 To 180
U = Getadc(2)
Shift U , Right , 2
Nn = N * 2
Nn = Nn + 1
D(nn) = U
U = Getadc(3)
Shift U , Right , 2
Nn = Nn + 1
D(nn ) = U
Next
N
For Nn = 1 To 362
Dat = D(nn)
Put #2 , Dat
Next Nn
End If
Listing 7.2: Die
Oszilloskop-Funktion
Die Zweikanal-Variante wird
mit dem Kommando 101 gestartet und liefert 181 ineinander geschachtelte Werte
für jeden Kanal. Die Geschwindigkeit hängt von der Einstellung des AD-Wandlers
ab. Für maximale Auflösung sollte der AD-Wandler mit einem Takt zwischen 50 kHz
und 200 kHz betrieben werden. Mit der Einstellung Prescaler = 16 hat man
bereits einen Takt von 500 kHz. Das ist jedoch problemlos, weil die Auflösung
auf 8 Bit reduziert wird. Jede Einzelmessung benötigt damit etwa 70 µs, sodass
man eine Abtastrate von ca. 14 kHz bekommt. Damit braucht die Serie mit 361
Einzelmessungen etwa 25 ms.
Der Timer 0 wird mit
Prescaler = 64 betreiben und liefert damit eine PWM-Frequenz von 244 Hz. Die
Frequenz wurde relativ niedrig gewählt, damit Experimente mit dem eigenen
Oszilloskop im Interface möglich sind. Wenn es aber auf eine einfache Glättung
der PWM-Ausgangsspannung ankommt, ist eine höhere Frequenz besser.
Die schnellere Variante des
Interface-Programms ist Interface2.bas. Der Timer0 wird mit Prescaler = 1,
betrieben, sodass die PWM-Frequenz auf 16 kHz steigt. Der AD-Wandler läuft nun
mit Prescaler = 4, was die Abtastrate auf 36 kHz erhöht. Das Oszilloskop stellt
nun einen Bereich von 10 ms dar. Bild 7.1 zeigt, dass trotz der hohen
Abtastrate noch eine gute Auflösung erreicht wird.
Abb. 7.1: Ein Signal mit 1
kHz
Interface2.bas erhält drei
zusätzliche Kommandos, mit denen man die Zeitachse des Oszilloskops zwischen 10
ms und 200 ms verändern kann. Die Byte-Kommandos 97, 98 und 99 können über das
Terminal gesendet werden.
If Kom = 97 Then
Config Adc = Single , Prescaler = 4 'Scope 10 ms
End If
If Kom = 98 Then
Config Adc = Single , Prescaler = 16 'Scope 25 ms
End If
If Kom = 99 Then
Config Adc = Single , Prescaler = 128 'Scope 200ms
End If
Listing 7.3: zusätzliche
Kommandos in Interface2.bas
7.3 Ein Transientenrecorder
Das Programm Logger1.bas
realisiert einen Datenlogger, mit dem schnelle Messdaten erfasst und dann
seriell im Textformat übertragen werden sollen. Die Funktion ähnelt einem
Oszilloskop, man kann jedoch das Zeitverhalten und die Auflösung in weiten
Grenzen anpassen. Die Daten lassen sich dann in eine Datei übertragen und
auswerten. Insgesamt sollen 200 Messdaten im RAM zwischengespeichert werden.
'Logger1.bas
Datenlogger B4
$regfile
= "attiny85.dat"
$crystal
= 8000000
$hwstack
= 8
$swstack
= 4
$framesize
= 4
Dim
Dat As Word
Dim
N As Byte
Dim
Dummy As Byte
Dim
Daten(200) As Word
Config
Portb.3 = Output
Config
Adc = Single , Prescaler = Auto
Open
"comb.1:9600,8,n,1" For Output As #1
Open
"comb.2:9600,8,n,1" For Input As #2
Do
Portb.3 = 1
For N = 1 To 200
Dat = Getadc(2)
Daten(n) = Dat
Next N
Portb.3 = 0
Get#2 , Dummy
For N = 1 To 200
Print #1 , N;
Print #1 , " , ";
Dat = Daten(n)
Print
#1 , Dat
Next N
Get#2 , Dummy
Loop
End
Listing 7.4: Lesen und
Schreiben im RAM
Ein Daten-Array lässt sich
mit Dim Daten(200) as Word erzeugen. Man hat dann Platz für 200 Daten im
Word-Format. Weil jedes Word zwei Byte belegt, werden insgesamt 400 Byte des
verfügbaren Speichers von 512 Byte belegt. Andere Variablen und die
Strack-Bereiche benötigen ebenfalls Speicher, sodass nicht mehr viel Platz
übrig bleibt. Einen Überblick vermittelt die beim Kompilieren erzeugte Datei
Logger1.rpt.
Report
: Logger1
Date
: 11-18-2015
Time
: 12:49:37
Compiler
: BASCOM-AVR LIBRARY V 2.0.7.8
Processor
: TINY85
SRAM
: 200 hex
EEPROM
: 200 hex
ROMSIZE
: 2000 hex
ROMIMAGE
: 2B2 hex -> Will fit into ROM
ROMIMAGE
: 690 dec
FLASH
USED : 8 %
BAUD
: 9600 Baud
XTAL
: 8000000 Hz
BAUD
error : 100.%
Stack
start : 25F hex
Stack
size : 8 hex
S-Stacksize
: 4 hex
S-Stackstart
: 258 hex
Framesize
: 4 hex
Framestart
: 250 hex
Space
left : 91 dec
Listing 7.5 Report zur
Speichernutzung
Die Messwerterfassung startet
nach jedem Reset, und damit auch mit einem neuen Öffnen des Bascom-Terminals.
Während der Messung wird B3 hochgesetzt. Eine hier angeschlossene LED zeigt die
Dauer der Messung an. Man sieht in diesem Fall nur einen kurzen Lichtblitz,
weil die gesamte Messung nach etwa 50 ms beendet ist.
Nach der Messung wartet das
Programm auf ein beliebiges Zeichen vom Terminal. Man kann z.B. ein Leerzeichen
eingeben. Dann werden alle 200 Daten übertragen. Zur Vereinfachung der späteren
Auswertung wird jeweils die Nummer des Messpunktes und Komma als Trennzeichen
mit ausgegeben.
Abb. 7.2 Messdaten im RAM
Mit Terminal/Open Log lassen
sich alle empfangenen Messdaten in eine Log-Datei speichern, deren Dateinamen
und Speicherort man angeben kann. Nach der Übertragung muss die Dateien mit
Close Log geschlossen werden. Die Log-Datei lässt sich dann z.B. mit Excel
auswerten und darstellen.
Abb. 7.3: Eine
300-Hz-Schwingung
7.4 Langzeit-Datenlogger
Im Gegensatz zum RAM bleiben
Daten im EEPROM auch nach dem Abschalten der Betriebsspannung erhalten. Das
EEPROM des Tiny85 hat eine Speichergröße von 512 Byte und kann sinnvoll zur
Speicherung von Messdaten verwendet werden. Hier werden 500 Byte-Daten zur
Helligkeit im Sekundentakt erfasst und gespeichert. Als Sensor dient der
Fototransistor. Der Auslesevorgang entspricht dem Datenlogger im letzten
Beispiel.
Abb. 7.4: Datenlogger mit
roter LED als „Startschlüssel“
Bei einer Langzeitmessung ist
es wichtig das Ende der Datenerfassung zu kennen. Außerdem braucht man eine
Möglichkeit, ein erneutes Überschreiben der Daten zu verhindern. Der
Mikrocontroller könnte z.B. mobile Messdaten aufnehmen, die dann später am PC
ausgelesen werden sollen. Zur Aktivierung der Messung wird in diesem Beispiel
eine rote LED eingesetzt. Am Port B3 wird beim Start der Pullup eingeschaltet.
Die rote LED leuchtet schwach. Aber der Controller erkennt einen Low-Zustand,
weil die LED-Spannung deutlich unter 2,5 V liegt.
'Logger2.bas
Datenlogger B3, 500 Bytes im EEPROM
$regfile
= "attiny85.dat"
$crystal
= 8000000
$hwstack
= 8
$swstack
= 8
$framesize
= 8
Dim
Dat As Word
Dim
Dbyte As Byte
Dim
N As Word
Dim
Dummy As Byte
Config
Adc = Single , Prescaler = Auto
Open
"comb.1:9600,8,n,1" For Output As #1
Open
"comb.2:9600,8,n,1" For Input As #2
Do
Portb.4 =
1
'Pullup
Waitms 10
If Pinb.4 = 0 Then
For N = 1 To 500
Dat = Getadc(3)
Shift Dat , Right , 2
Dbyte = Dat
Writeeeprom Dbyte , N
Print #1 , Dbyte
Waitms 1000
Next N
End If
Portb.4 = 0
Get #2 , Dummy
For N = 1 To 500
Print #1 , N;
Print #1 , " , ";
Readeeprom Dbyte , N
Print #1 , Dbyte
Next N
Get #2 , Dummy
Loop
End
Listing 7.6: Der Datenlogger
Die Beispielmessung in Abb.7.
5 zeigt den Helligkeitsverlauf bei Sonnenschein und teilweise bewölktem Himmel
Abb. 7.5: Langzeitmessung der
Helligkeit
7.5 Kennlinienschreiber
Eine Kennlinie beschreibt den
Zusammenhang von Strom und Spannung an einem Bauteil. Mit einer regelbaren
Spannungsquelle und zwei Messgeräten kann man Wertepaare erfassen, die dann in
einem I/U-Diagramm aufgetragen werden.
Um die Messung automatisch
durchzuführen, wird hier eine Spannungsquelle mit dem PWM-Ausgang und einem
Tiefpassfilter gebildet. Der Analogeingang ADC3 misst direkt die Spannung am
Messobjekt. Der Strom kann aus der Spannungsdifferenz zur Leerlauf-PWM-Spannung
und dem Widerstand im RC-Filter berechnet werden.
Abb. 7.6: Aufnehmen einer
LED-Kennlinie
Das Programm Kennlin1.bas
addiert jeweils fünf Messwerte pro Messpunkt. Damit wird einerseits eine
Glättung erreicht und anderseits ohne weitere Umrechnung ein Messbereich von
5115 mV, also näherungsweise 5 V. Pro AD-Stufe wird nämlich mit 5 mV gerechnet.
Der PWM-Ausgang hat nur eine Auflösung von 8 Bit und kommt entsprechend auf 20
mV pro Stufe, reicht also rechnerisch bis 20 mV * 255 = 5100 mV. Die Differenz
beider Spannungen liefert direkt den Strom in µA.
Eine Messung wird mit einem
beliebigen Zeichen gestartet. Im Terminal erscheinen dann Spannungen und
Ströme, getrennt durch ein Komma. In dieser Form lassen sich die Daten in eine
Tabellenkalkulation einlesen und grafisch auswerten.
2089 , 2871
2090 , 2890
Abb. 7.7 zeigt die typische
Diodenkennlinie einer grünen LED. In Abb. 7.8 sieht man eine andere LED mit
Vorwiderstand. Aus der Steigung lässt sich der Widerstand mit 1 kΩ
bestimmen.
'Kennlin1.bas
Kennlinie PWM, ADC3
$regfile
= "attiny85.dat"
$crystal
= 8000000
$hwstack
= 8
$swstack
= 4
$framesize
= 4
Dim
U_dat As Word
Dim
I_dat As Word
Dim
N As Byte
Dim
I As Byte
Dim
Dummy As Byte
Dim
Daten(200) As Word
Config
Portb.0 = Output
Config
Adc = Single , Prescaler = Auto
Open
"comb.1:9600,8,n,1" For Output As #1
Open
"comb.2:9600,8,n,1" For Input As #2
Config
Timer0 = Pwm , Prescale = 1 , Compare A Pwm = Clear Up
Pwm0a
= 0
Do
Get#2 , Dummy
For N = 1 To 255
Pwm0a = N
Waitms 10 0
U_dat = 0
For I = 1 To 5
U_dat = U_dat + Getadc(3)
Next I
I_dat = N * 20
I_dat = I_dat -
U_dat
Print #1 , U_dat;
Print
#1 , " , ";
Print #1 , I_dat
Next N
Pwm0a = 0
Loop
End
Listing 7.7: Messung von
Kennlinien
Abb. 7.7: Kennlinie einer
grünen LED
Abb. 7.8: Kennlinie LED mit
Vorwiderstand
7.6 Der MCS-Bootloader
Alle ATmega-Controller
besitzen einen Bootbereich im Flash, der über einen Boot-Vektor angesprungen
werden kann, wenn man ihn über die Fuses aktiviert. Bei jedem Neustart
verzweigt der Mikrocontroller dann zunächst in den Bootbereich. Das Bootprogramm
wartet auf eine Reaktion, die eine Neuprogrammierung einleiten soll. Bleibt sie
aus, verzweigt der Controller an die normale Reset-Adresse $0000. Wenn jedoch
ein Programm wie Bascom eine entsprechende Nachricht schickt, geht das
Bootprogramm in den Programmiermodus, um ein neues Programm in den untern
Flash-Bereich zu schreiben.
Es gibt zahlreiche Boot-Programme
für unterschiedliche Systeme. Bascom kennt den eigenen MSC Bootloader, der
selbst in Bascom geschrieben ist. Das Programm ist eigentlich nur für
ATmega-Controller vorgesehen, weil ein ATtiny keinen ausgewiesenen Bootsektor
und eine Boot-Fuses besitzt. Außerdem wird in der Vorlage der Hardware-UART
verwendet, der beim ATtiny85 fehlt.
Trotzdem konnte der
MCS-Bootloader an den ATtiny85 angepasst werden. Der Quelltext liegt dem
Lernpaket bei. Zum Kompilieren wird die Bascom-Vollversion benötigt, weil der
Code das obere Viertel des 8-k-Flash belegt. Die gesamte Kommunikation wurde
auf den Software-UART umgebaut.
'
(c) 1995-2009, MCS
'
Bootloader.bas
'
This sample demonstrates how you can write your own bootloader
'
in BASCOM BASIC
'
VERSION 2 of the BOOTLOADER. The waiting for the NAK is stretched
'
further a bug was resolved for the M64/M128 that have a big page size
'-----------------------------------------------------------------
'This
sample will be extended to support other chips with bootloader
'The
loader is supported from the IDE
'Bootloader
changed for ATtiny85 by Burkhard Kainka 2015
'Software
UART at 9600 Baud at 8 MHz
'$timeout
is not possible,special timeout before Testfor123
'Changes
first rjmp in loaded file to rjmp &0D00, Bootloader
'rjmp
to start address written to $F80
$regfile
= "attiny85.dat"
$crystal
= 8000000
$hwstack
= 8
$swstack
= 4
$framesize
= 4
$loader
=
$c00
Const
Maxwordbit = 5
Const
Maxword =(2 ^ Maxwordbit) * 2 '128
Const
Maxwordshift = Maxwordbit + 1
Const
Cdebug =
0
Listing 7.8
Programmkopf des Bootloaders
Der fehlende Bootsektor wird
durch eine Änderung der Sprungadressen ersetzt. Beim ersten Start ist der
untere Bereich mit FF gefüllt. Der Controller läuft dann automatisch in den
Bootbereich ab $0C00. Wenn dann ein Anwenderprogramm in den unteren Bereich
geladen werden soll, tauscht der modifizierte Bootloader die Sprungadressen
aus. An die Adresse $0000 kommt ein Sprung auf den Bootlaoder selbst bei $0C00,
damit beim nächsten Start wieder ein neues Programm übertragen werden kann.
Ursprünglich stand im Hexfile an der Adresse $0000 ein Sprungbefehl auf den
Anfang des Userprogramms. Dieser wird nun an die neue Adresse $0F80 gesetzt.
Zum Start des Userprogramms verzweigt der Bootloader zu dieser Adresse und von
dort zum Userprogramm.
Nach einem Hardware-Reset
startet also immer erst der Bootloader. Er wartet kurze Zeit, ob über die
serielle Schnittstelle ein Byte 123 empfangen wird, das als Start für einen
Bootvorgang vereinbart ist. Bleibt dieses Byte aus, wird das zuletzt geladene
Programm im unteren Programmbereich gestartet. Wenn man aber aus Bascom heraus
ein neues Programm übertragen möchte, generiert Bascom über die DTR-Leitung einen
Reset und stellt dann die Verbindung zum Bootloader her. Das neue Programm wird
übertragen und danach gestartet.
…
If
Bblock = 1 Then
Waddress = Buf(2)
- $c0
Waddress = Waddress * 256
Waddress = Waddress + Buf(1)
Waddress = Waddress + 1
Buf(1) =
$0ff
'rjmp $0D00 at 0000
Buf(2) = $0cb
End
If
…
…
Waddress = $1000 + Waddress 'rjmp ... in F80
Waddress = Waddress - $0f81
Buf(1) = Waddress And 255
Waddress = Waddress / 256
Buf(2) = Waddress + $c0
…
…
Do_start:
Goto
$0f80
Listing 7.9 Umkopieren von
Sprungadressen