Schon seit geraumer Zeit lagen im Sensorlager einige Exemplare DS1820 herum und warteten auf den Einsatz als Thermometer und Präzisionsthermostat. Allein, es fehlte an der Zeit, für die Einarbeitung in das One-Wire-Protokoll von Dallas, mit dem die Bausteine angesprochen werden. Den Stein ins Rollen brachte dann im Sommer 2016 ein Artikel in der ELEKTOR (Juni-Heft 2016 - Seite 37ff). Dort wurde ein Thermostat beschrieben, der mit einem ATTiny45 und zwei DS18B20 einen herkömmlichen Kleinlüfter ansteuert. Weder das herunter geladene c++-Programm noch die eigene Umsetzung in Assembler funktionierten korrekt. Also hieß es - tiefer in die Materie einsteigen.
Die Hardware
Das Know How
Kurze Produktinfo
Reset- und Device-Present-Puls
Master sendet Timeslots
Master empfängt Timeslots
Eine einfache Kommunikationskette
Das Hauptprogramm
Ein paar Sätze zu ausgewählten Routinen aus dem ModulZeitsteuerung
Die "Kernel"-Routinen
Die API
I/O-Routinen zur Kommunikation mit der Umgebung
Neben der Hardware, ich griff dazu auf ein bestehendes Projektergebnis mit dem ATTiny2313 zurück, da ich zum Testen die Ausgabe von Ergebnissen via RS232 haben musste. Die neuere erweiterte Platine enthält neben einigen LEDs und Tasten auch eine Schnittstelle zum Anschluss eines RS232-Erweiterungsmoduls.
Von links nach rechts:
drei Sensoren DS18X20, RS232-Modul, Programmer und AVR-Test- Platine, Busanschluss
Netzteil und BITSCOPE MICRO
Bereits beim ersten Einrichten Zusammenstellen der nötigen Bibiliteks-Module wurde klar, dass der 2313 zu wenig Flash-Speicher hatte. Der ATTiny2313 wurde daher durch den pin- und Funktionskompatiblen Typ ATTiny4313 mit 4KB Flash ersetzt.
Was mir am Artikel auch nicht gefiehl, war, dass die beiden DS1820 an zwei getrennten Anschlüssen des Tiny45 lagen. Ein Bus ist schließlich dazu da, mehrere Devices an einem Anschluss zu bedienen. Das Datenblatt zu Rate zu ziehen ist sicher keine schlechte Idee. Fürs Erste ganz gut, aber besser fand ich dann die Appnote 162 von MAXXIM, die schließlich auch den Durchbruch bei der Umsetzung der neuen Erfahrungen in ein Assemblermodul brachte. Einen Hinweis auf diese Appnote fand ich auch in dem hilfreichen Abstract von Gerard Marull Paretas auf den im ELEKTOR-Artikel verwiesen wurde.
Es gibt zwei Bausteine, die gleiche Funktionen aber unterschiedliche Auflösungen aufweisen. Der DS1820 löst grundsätzlich auf halbe Grad Celsius auf, während man die Auflösung des baugleichen DS18B20 durch beschreiben eines Registers im Baustein durch Software verändern kann. Dieser Unterschied betrifft nicht die Art und Weise, wie der Baustein angesprochen und gesteuert wird, wohl aber die Methode der Temperaturaufbereitung vom eingelesenen Rohwert bis zu auswertbaren Vergleichswerten oder einer Anzeige oder Ausgabe auf LCD/RS232. Dazu später mehr.
Die folgende Abbildung stellt die für das Projekt wesentlichen Teile dar. Standardmäßig wird der Prozessor mit einem 8MHz-Quarz und 5V betrieben. An den 1-Wirebus kann man (fast) beliebig viele serielle Devices anschließen. Begrenzt wird das Ganze hauptsächlich vom Platz im EEPROM-Speicher des AVR, weil dort die ROM-Code-Daten vorgehalten werden. Der vorliegende Ansatz geht von 8 DS18(B)20 aus, kann aber noch gut auf insgesamt 25 Bausteine erweitert werden.
Der Sensor liegt mit seiner Datenleitung an PD2, Eine LED mit Taster an PD5
und ein Ausgang zum Triggern des DSO an PD6
Nachdem, wie der Name schon sagt, alle Information auf einer Leitung ausgetauscht wird, ergeben sich daraus ein paar wichtige Grundsätze.
a) Es kann stets nur ein Device senden.
b) alle Devices hören gleichzeitig mit.
c) Es gibt keinen sychronisierenden Takt.
d) Die Übertragung auf Bitebene folgt einem zeitlichen Rahmen, der von
Dallas vorgegeben wird.
e) Die Kommunikation wird von einem Bus-Master gesteuert und initiiert, das
ist der Microcontroller.
f) Die Slavedevices können nicht mitenander kommunizieren.
Es werden nun im Einzelnen die wesentlichen atomaren Abläufe
des One-Wire-Protokolls dargestellt.
Nebenbei erwähnt: Natürlich braucht der DS1820 neben der Datenleitung
mindestens noch eine Masseleitung und wer außerdem noch eine Versorgungsspannungszuführung
spendiert, wird durch progammtechnisch einfacheres Handling belohnt. In diesem
Sinn ist es auch, die Datenleitung über einen 4k7-Widerstand auf +Vcc zu
ziehen, statt den im AVR eingebauten Pullup-Widerstand zu verwenden. Das sorgt
auf für saubere, eindeutige Signale auf der Datenleitung.
Jedes am Bus befindliche Gerät kann die Leitung auf Masse ziehen, was im Normalfall nur für ein einziges Device zulässig ist. Sind mehrere Slaves am Bus und zieht auch nur einer die Busleitung nach Masse, gehen die Highpegel, anderer Devices ins Leere (wired AND!). Lediglich bei der automatischen Suche des Masters nach Slaves hat das eine Bedeutung. In allen anderen Fällen findet Datenschredder statt, wenn mehrere Slaves gleichzeitig versuchen Daten zu senden. Das bedeutet, dass eben nur ein einziges Device am Bus ist oder dass der Master bekannt geben muss, mit welchem Baustein er Kontaktaufnehmen möchte. In letzterem Fall sendet der Master vor dem eigentlichen Funktionsaufruf via ROM-Kommando MATCHROM den zuvor aufgezeichneten ROM-Code des gewünschten Adressaten. Aber auch bei nur einem Baustein am Bus, muss der Master mit dem SKIPROM-Kommando mitteilen, dass als nächstes ein Memory-Kommando folgt. Näheres dazu im Kapitel Eine einfache Kommunikationskette
Ich stelle erst einmal die Info aus dem Datenblatt für die drei wesentlichen Vorgänge Reset- und Present-Puls, Master sendet Timeslots, Master empfängt Timeslots eigenen Messungen gegenüber, die das reale Verhalten zeigen. Gleichzeitig sieht man, dass die gemessenen Zeiten durchaus im theoretischen Rahmen des Datenblatts liegen.
Zeitschema der Initialisierung in Theorie und Praxis
Vor jedem Zugriff auf einen DS1820 muss ein Reset-Puls von 480 µs die Kommunikation einleiten. Dieser Puls kommt vom Busmaster, also dem AVR. Dieser lauscht danach am Bus, ob ein DS1820 seinerseits die Datenleitung gegen Masse zieht. Den zeitlichen Ablauf entnimmt man der obigen Darstellung.
Die Zeitfenster (Slots) für das Senden von Bits haben eine Länge von 60 bis 120 µs. Zwischen den Bitübertragungen muss der Leitungspegel Gelegenheit haben, sich von einem vorherigen Low-Pegel zu "erholen". Dallas sieht hierfür 1µs vor. Zeitkritisch ist lediglich der "1"-Slot, da in diesem Fall der Pegel nach spätestens 15µs durch den 4k7-Widerstand auf Vcc gezogen sein muss.
Zeitverhalten beim Senden von "0"- und "1"-Bits durch
den Master
Slavedevices senden nur, wenn der Master sie hierzu auffordert. Das geschieht in zweierlei Weise. Erstens muss bei mehreren Devices am Bus durch den Master sichergestellt werden, welcher Baustein sich angesprochen fühlen soll. Zur Bausteinauswahl folgen weiter unten genauere Hinweise. Zweitens muss selbst ein ausgewählter Baustein zum Senden jedes Bits aufgefordert werden. Das geschieht dadurch, dass der Master durch einen kurzen (1µs) Low-Puls den Slave dazu auffordert. Der Master liest daraufhin nach ca. 15µs den Leitungspegel ein.
Zeitablauf beim Datenempfang durch den Master
Das Zusammenspiel der einzelnen Elemente kann man an den folgenden Abbildungen studieren. Die schematische Darstellung zeigt den Ablauf der Übermittlung eines ROM-Kommandos, eines Speicherbefehls und die Antwort des, in diesem Fall, einzigen DS1820 auf dem Bus.Grundlage für diese Darstellung war ein Oszillogramm, das mit einem UNI-T 2202C aufgenommen wurde. Durch das zugrundeliegende AVR-Programm wurde an den interessanten Stellen der Triggerpegel von Low nach High gebracht (gelbes Signal).
Grafisch aufbereiteter Kommunikationsverlauf
Wie aus dden Abbildungen zu entnehmen ist, senden sowohl Master als auch Slave das LSBit als erstes und das MSBit zuletzt. Daher ergibt sich aus der Bitfolge 0-0-1-1-0-0-1-1 unmittelbar nach dem Presencepulse des DS1820 das Byte 0xCC und aus der Folge 0-0-1-0-1-1-0-1 von rechts nach links gelesen das Byte 0xB4. Das Byte 0xCC steht für den ROM-Befehl SKIPROM, der das angeschlossene Device anweist, nicht auf einen speziellen Auswahlbefehl zu warten. Das geht natürlich nur, wenn bloß ein einziger DS1820 am Bus ist. Der zweite Befehl 0xB4 ist ein Memorycommand und weist den DS1820 an, mitzuteilen, ob er mit einer normalen +5V-Leitung versorgt wird oder über die Datenleitung parasitär. Der angeschlossene DS1820 anwortet mit "1"-Slots und das heißt, er ist normal versorgt. Im anderen Fall würde er mit "0"-Slots antworten.
Die folgenden Headerdateien sowie deren Codeteile werden zur Hauptschleife hinzugefügt. Der Kernpunkt ist die neu zu definierende Bibiothek 1wire.asm
Nach dem üblichen Initialisierungsgerösel für RS232, LCD und Ports folgt auch schon der erste von drei Kernpunkten, das Einlesen der angeschlossenen Sensoren. Einer Erwähnung bedarf zunächst die Variable single_device. Sie wird der Flexibilität wegen neben der Konstanten singeSensorPerLine eingeführt, damit leichter auf Anpassungen reagiert werden kann. Die Konstante singeSensorPerLine wird in 1wire_h.asm mit true oder false vorbelegt. Den Initialierungsteil der Sensoren managet die Routine maintain_devices.
Es gibt insgesamt vier Startmodi der Software, die mit Hilfe des Tasters aufgerufen werden können. Die Nutzung des LED-Ports, der auch im 1wire-Modul definiert wird, und nur eines Tasters spart I/O-Ports ein und erlaubt den Betrieb des Modul auch auf kleinen AVRs. Die Betätigungsdauer der Taste beim Reset oder beim Einschalten entscheidet über den Modus mit dem die Schaltung startet. Grundsätzlich können mit der vorliegenden Software bis zu 8 DS18(B)20 an einem Pin des AVR betrieben werden. Über deren Anmeldungsweise entscheiden die Betriebsmodi 2 und 3.
Besondere Erwähnung benötigen die Punkte 2 und 3.
Im Modus 2 darf sich jeweils nur ein einziger Baustein auf dem Bus befinden. Die Anmelderoutine bedient sich des ROM-Kommandos READROM (siehe Datenblatt), wonach laut Datenblatt nur ein DS1820 am Bus sein darf. Mit jedem Reset kann ein weiterer DS1820 eingelesen werden. Das geht bis zu 8 mal. War das Einlesen erfolgreich, dann leuchtet die LED nach dem Loslassen der Taste dauerhaft, im Fehlerfall gar nicht. Modus 2 hat den Vorteil, dass man genau weiß, welcher Sensor welche Speichernummer hat.
Im Modus 3 müssen alle verwendeten Sensoren am Bus sein, wenn man die Taste loslässt. Das System liest dann automatisch alle Devices ein. Natürlich hat man keinen Überblick, welches Device welchen Speicherplatz für den ROM-Code belegt. Die einzige Möglichkeit, das zu erfahren, ist die Ausgabe des Konfigurationsspeichers über LCD oder RS232. Das vorliegende Programm gibt zunächst die Nummer des letzten belegten Speicherplatzes aus. Dann folgen in umgekehrter Reihenfolge die ROM-Codes der Bausteine. Sind die Codes im voraus bekannt, ist die Zuordnung der Sensoren zu Speicherplätzen möglich.
Der zweite und analoge 3. Teil des Hauptprogramms demonstriert das Vorgehen beim Ansprechen der Sensoren. Grundsätzlich muss im Register MASKE, das im Header 1wire_h.asm definiert wird, die Zugriffs-Bit-Maske abgelegt werden, welche es ermöglicht den Port für die Busleitung auf Ausgang zu schalten. Die Einstellung wird im Header 1wire_h.asm definiert.
Als nächstes muss die Variable single_device entsprechend der Busbelegung korrekt gesetzt sein. Das regelt in verschiedenen Unterprogrammroutinen die Art des Zugriffs auf die Devices.
Dann muss man sich für einen bestimmten Sensor entscheiden, der anzusprechen ist. Die zugehörige Speichernummer kommt in R17.
Jetzt muss der ROM-Code des Bausteins aus dem EEPROM in den SDRAM-Puffer gelesen werden. Das macht die Routine recall_ROM-code_From_EE.
Zur Auswahl des Bausteins wird das ROM-Kommando MATCHROM aufgerufen. Das erledigt die entsprechende Routine. Befindet sich nur ein DS1820 am Bus, entfällt das ROM-Kommando MATCHROM. In diesem Fall erkennt die Routine zum Auslesen der Temperatur an der mit true belegten Variable single_device, dass statt dessen das ROM-Kommando SKIPROM abzusetzen ist. In der Routine DS1820_read_tempo_raw geschieht diese Auswahl automatisch durch die korrekte Belegung der Variable single_device. Für andere noch nicht implementierte Memory-Funktionen des DS1820 muss das der Programmierer selbst umsetzen.
Der Baustein ist nun ausgewählt, kann mit einem Memory-Kommando eine Funktion abgerufen werden wie hier das Auslesen der Temperatur. Falls es sich um einen DS1820 gehandelt hat, wird der Roh-Wert der Temperatur (R14:R15) ins Anzeigeformat (R26:R27) umgewandelt und über V24 ausgegeben. Das Hauptprogramm endet schließlich wie üblich im Main-Loop, zu dem hier gesprungen wird, wenn der Devicetyp nicht DS1820 ist. anschließend wird die Temperatur von Sensor Nrummer 1 eingelesen.
Ähnlich funktioniert das Einlesen des gesamten Scratchpad eines Device. Weil alles bereits für Sensor 1 vorbereitet ist erfolgt die Ausführung für eben diesen Sensor lediglich mit dem Aufruf der Funktion read_scratchpad.
Im RS232-Terminal (Hyperterminal, COM3, 38200 Baud, no Parity, 1 Stopbit) sieht die Antwort nach jedem Neustart oder Reset so aus.
Begrüßungstext zur Kontrolle der RS232-Funktion
1 ist die Nummer der letzten belegten EEPROM-Speicherstruktur
Es folgen die beiden ROM-Codes der am Bus befindlichen Devices, beide DS1820
(Familycode 0x10); das obere Device wurde zuletzt eingelesen.
Die Temperatur von Device Nummer 0
Die Temperatur von Device Nummer 1
Der Inhalt von Scratchpad Nummer 1; 0x2A 0x00 (Lowbyte:Highbyte) ist der Temperaturcode
00 das Ergebnis des Prüfsummenaufbaus durch die Funktion crc_one_byte.
Decodieren des Temperaturcodes:
Das LSB des Highbytes ist das MSB der Temperaturauslesung (9. Bit) wir schieben das LSB rechts ins Carry -> C = 0
Das Lowbyte in Binärschreibweise: 0x2A = 0b0010 1010 enthält als LSB die 0,5°C-Stelle. Wir rotieren das Carry über das Lowbyte nach rechts:
C = 0 -> Lowbyte -> Carry: aus 0 0010 1010 wird 0001 0101 0
Die Null im LSB aus dem Lowbyte wandert ins Carry und im Lowbyte
steht die Ganzzahl (8 Bit) des Temperaturwerts. Wir addieren die Wertigkeiten:
16 + 4 + 1 = 21 und 0 halbe Grad aus dem Carry also 21,0°C.
Für den DS18B20 funktioniert das ähnlich, nur dass man die 4 Nachkommabits erst durch viermaliges Schieben und Rotieren von High- und Lowbyte erhält und in einem eigenen Register auffangen muss. In dem zuvor als Lowbyte bezeichneten Register steht auch jetzt wieder die Ganzzahl der Temperatur.
Ein DS18B20 wurde auf dem Bus ergänzt, der EEPROM-Speicher gelöscht und die Devices neu eingelesen. Nach dem nächsten normalen Start ergab sich die nachfolgende Ausgabe im Hyperterminal. Die obige Sequenz zeigt die Erweiterung auf drei Sensoren, wobei der dritte ein DS18B20 ist. Der vorher eingelesene ROM-Code wird in den Puffer gebracht, die Temperatur eingelesen und statt mit der dedizierten Rotutine DS18B20_temp_int_frac durch den Aufruf von convert_temp_int_frac ins Anzeigeformat umgewandelt. Das hat den Vorteil dass diese Routine den Typ des Bausteins anhand der Variablen ds_type aus dem Strukturpuffer im SDRAM selbst erkennt und die geeignete Umwandlungsmethode aufruft. Anschließend wird das Scratchpad dieses Bausteins eingelesen.
Der Datentransfer war ohne Fehler, s_CRC = 0.
Ähnlich wie oben gezeigt, kann man aus den ersten beiden Bytes den dargestellten Temperaturwert nach Rundung verifizieren.Allerdings haben wir hier vier Bits als Nachkommawert. Also:
0x01:0x53 = 0b0000 0001 : 0b0101 0011 -> 4 mal nach links schieben/rotieren
-> 0001 0101 : 0011 -> 16 + 4 + 1 = 21 : 0,125 + 0,0625 = 0,1875 gerundet
0,19 also 21,19°C
Im Anschluss an den Hauptteil folgen die Includes der Codedateien der Module
Die Headerdatei beginnt mit der Darstellung der enthaltenen Unterprogramme und deren Syntax. Nebenbei sei bemerkt, dass auch wieder einige der anderen Bibliotheksmodule eine Anpassung und Erweiterung erfahren haben. In diesem Zusammenhang ist es wichtig stets die Module zu verwenden, die im Paket selbst enthalten sind. Neuere Module sind meistens abwärtskompatibel aber eben nicht immer.
Folgt man dem Konzept, dass Routinen, die Funktionen von 1wire-Devices umsetzen und direkt benutzen, im Modul 1wire.asm anzusiedeln sind, dann kann man die Funktionen und Methoden des Moduls in eine lokale Gruppe und eine API trennen. Lokale Funktionen greifen direkt auf den Bus zu, um Bausteine anzusprechen. Sie bedienen sich lokaler Variablen und Konstanten. Die API-Routinen dienen dem Zugriff von außen auf Ergebnisse der lokalen Gruppe ohne sich aber um das WIE des Erhalts der Ergebnisse zu kümmern oder kümmern zu müssen.
Zu den lokalen Funktionen und Methoden gehören die Routinen für das Timing und für I/O-Operationen.
Hier der umfangreiche Satz an API-Funktionen nebst Parametertransfer.
maintain_devices
wird beim Booten der Schaltung aufgerufen und tut entweder nichts (= normaler
Start) oder liest 1-Wire-Bausteine ein oder löscht den Strukturspeicherbereich
im EEPROM.
DS1820_reset
wird stets aufgerufen, wenn ein Baustein angesprochen werden soll oder zum Abbruch
laufender Aktionen.
DS1820_init
Setzt die Auflösung eines DS18B20 und führt beim DS1820 lediglich
einen RESET aus.
DS18X20_READROM
liest die ROM-Codes des EINZIGEN! Bausteins am Bus aus und speichert sie im
SDRAM-Puffer ROM_NO ab.
MATCH_ROM
nutzt die Daten im SDRAM-Puffer um einen ganz bestimmten Baustein mit dem im
Puffer abgelegten ROM-Code zu adressieren.
Read_scratchpad
nutzt die Daten im SDRAM-Puffer um einen ganz bestimmten Baustein mit dem im
Puffer abgelegten ROM-Code zu adressieren oder verwendet bei nur einem Baustein
am Bus das ROM-Kommando SKIPROM. Nach erfolgreichem Durchlauf, der am rückgesetzten
error_flg zu erkennen ist, befindet sich der Inhalt des Scratchpads im SDRAM-Puffer.
Die Funktion ist in obiger Liste nicht aufgeführt, wohl aber bei den API-Funktionen
dargestellt.
Um mehrere Am Bus befindliche DS18(B)20 nacheinander zu indetifizieren, wird die Routine OWSEARCH (sieh unten) in zweifacher Weise genutzt.
OWFIRST
sucht den ersten Baustein am Bus bzw wird aufgerufen, um überhaupt mal
einen DS1820 zu finden. Im Erfolgsfall wird das Flag OWSEARCH_result auf 1,
also true, gesetzt, ROM_NO enthält dann den ROM-Code des Bausteins. Ferner
wird der Typ DS1820 oder DS18B20 festgestellt und geg.-falls die Auflösung
auf 12 Bit gesetzt (nur DS18B20). Den gesamten Puffer kann man darauf hin ins
EEPROM übertragen (siehe weiter unten)
OWNEXT
wird aufgerufen, falls die erste Suche LastDeviceFlag=0 zurück gibt. Dann
müssen sich weitere Devices am Bus befinden. Ist die Suche erfolgreich
abgeschlossen, stehen jetzt die ROM-Codes eines weiteren Bausteins im Puffer.
Die Suche wird ao lang wiederholt, bis das Flag LastDeviceFlag den Wert 1 aufweist.
OWSEARCH
ist die eigentliche Suchmaschine, die von OWFIRST und OWNEXT die jeweils zutreffenden
Rahmenparameter übergeben bekommt. Damit könnte man diese Routine
ebenso gut in den Bereich der lokalen Programmteile einordnen, weil sie von
außen nicht direkt aufgerufen werden kann, ohne dass man die Parameter
selbst setzt und auswertet. Als besonderes Gutti habe ich eine genaue Dokumentation
des ROM-Kommandos SEARCHROM in der nächsten Zeit geplant. Bis dahin verweise
ich auf die APP-Note 162 von MAXXIM/DALLAS, die mir auch sehr geholfen hat.
Die Verbindung aus Datenflussplan und Programmbeispiel sagt eingentlich schon
fast alles. Einen kleinen Rest muss man sich noch selbst erarbeiten. Die Routinen
zu diesem ROM-Kommando sind ausreichend kommentiert, sodass man das Vorgehen
sicher leicht nachvollziehen kann, wenn man die entsprechenden Seiten in den
Datenblättern und der APP-Note 162 gründlich verinnerlicht und den
Ablauf an den Beispielen studiert hat.
integrate_all_devices
steuert den Aufruf von OWFIRST und OWNEXT und bildet quasi den Deckel der gesamten
Suchmaschine. Die Routine wird einmal beim Programmstart aus maintain_devices
heraus aufgerufen, wenn die Taste zwischen 2 und 4 Sekunden lang gedrückt
wird.
read_last_store_number
Beim Abspeichern von ROM-Codes ins EEPROM wird die jeweilige Speicherplatznummer
auch dort mit abgelegt. Die Routine holt diesen Wert in die Variable LAST_DEV_NUM.
Wird benutzt, um mittels ROM_Code und MATCHROM ein ganz bestimmtes Device zu
adressieren.
write_last_store_number
schreibt die Nummer des zuletzt benutzten Speicherplatzes in EEPROM - wird von
integrate_all_devices benutzt
CRC-one_byte
berechnet die Prüfsumme, nachdem z.B. ein Byte bei der Suche nach Devices
oder beim Einlesen des Scratchpad eingelesen wurde. Der DS1820 bildet selbst
auch ein Prüfsummenbyte, welches stets als letztes übertragen wird.
Nachdem alle Bytes inclusive CRC-Byte übertragen und der Routine CRC_one_byte
übergeben wurden, muss diese den Wert 0x00 zurückgeben. ist das nicht
der Fall, war die Übertragung fehlerhaft.
DS1820_read_temp_raw
kombiniert die Übergabe des Befehls zur Einleitung einer Temperaturmessung
mit der nachfolgenden übertragung des ersten Teils vom Scratchpad, in dem
die gemessene Temperatur steht. die beiden eingelesenen Werte haben je nach
Baustein unterschiedlichen Aufbau, geben aber immer die Zweierkomplementdarstellung
der Temperatur wieder. Das heißt, bei positiven Temperaturen sind die
oberen, nicht genutzten Bits 0, bei negativen Temperaturen sind sie 1. Das erleichtert
die Aufbereitung dieser Rohtemperatur für Anzeige und Vergleichszwecke.
Die restlichen nun folgenden Routinen dienen der Aufbereitung der Rohtemperatur für Anzeige und Vergleichszwecke sowie dem dauerhaften Speichern im EEPROM
DS1820_tempx10
Die Temperaturwerte des DS1820 werden standardmäßig mit einer Genauigkeit
von 0,5°C erfasst. Für Vergleichszwecke wird der 10-fache Wert der
Temperatur in den Registern 26 und 27 zurückgegeben. Zum Vergleich mit
Temperaturkonstanten gibt man auch deren 10 fachen Wert an und führt einen
2-Byte-Vergleich durch.
DS18B20_tempx1000 und DS1820_tempx1000
bringen die 1000-fache Temperatur als Ganzzahl in den Registern des AccuB (R3:R5)
zurück und dienen auch dem Vergleich von Temperaturen. Weil der DS18B20
aber bis zu 4 Bit für die Nachkommastellen (1/2 bis 1/16) bereitstellt,
muss man, um eine Ganzzahl zu erreichen den Faktor 100 einführen. Dann
genügen hier die beiden Register R26:R27 nicht mehr und es muss zur Berechnung
außerdem auf das Arithmetikmodul zurückgegriffen werden. Deswegen
befindet sich das Ergebnis auch in dessen Accu B
save_ROM_code2EE
speichert den Inhalt des SDRAM-Puffers mit dem ROM-Code eines DS1820 im EEPROM
recall_ROM_code_from_EE
liest den Datensatz eines bestimmten Bausteins aus dem EEPROM in den SDRAM-Puffer.
Damit kann dann per MATCHROM dieser Baustein gezielt angesprochen werden.
convert_temp_int_frac
benutzt die beiden folgenden Routinen DS1820_temp_int_frac und DS18B20_temp_int_frac
um aus den Rohwerten in R14:R15 (tlow:thigh) den ganzzahligen und den Bruchanteil
jeweils im Dezimalformat in R26:R27 bereit zu stellen. Dieses Format nutzen
die Routinen der seriellen Schnittstelle und des LCD-Moduls, um die Temperatur
in normal lesbarer Form auszugeben.
Der SDRAM-Puffer für Bausteindaten enthält neben dem deviceeigenen ROM-Code zwei weitere Informationen, auf die die Routinen zugreifen, den Bausteintyp und ein Konfigurationsbyte.
Diese Struktur findet sich auch im EEPROM-Segment wieder.
Im Header werden unter anderem auch die Konstanten für die
DS-Kommandos definiert.
Eine kritische Stelle bei der Steuerung von 1-Wire-Bausteinen ist der zeitliche Ablauf. Zwar gibt es keinen direkten Übertragungstakt, alles passiert asynchron, und zwischen den einzelnen Bits können durchaus längere Pausen liegen.Aber während einer Bitübertragung verlangt das Protokoll die Einhaltung eines bestimmten zeitlichen Rahmens. Der beginnt mit der ersten fallenden Flanke, mit welcher der Master den Transfer einleitet. Siehe dazu das Datenblatt und auch das Kapitel "Das One-Wire-Protokoll". Die Impulslänge beträgt hier minimal 1 µs.
Die sehr kurzen einleitenden Pulse von 1µs werden direkt in den Routinen, in denen sie gebraucht werden, ohne Umweg über die Routine delay_µs eingebaut, weil durch den Aufruf und die Rückkehr ins aufrufende Programm zwei weitere µs vergehen. Bei längeren Intervallzeiten kann die Routine delay_µs unter Verwendung von evtl. verkürzten Laufzeiteingaben beim Aufruf eingesetzt werden.Meist kommt es aber nicht auf ein paar µs mehr an, weil auch für die anderen Zeiten durch Dallas lediglich Mindestwerte vorgegeben sind. Die Routine DS1820_read_bit zeigt, wie ein Bit durch den Master vom Bus gelesen wird. Der Lowimpuls von 1µs Dauer startet den Sendemechanismus des Slaves, er ist 8 Taktzyklen lang, was bei 8MHz Taktfrequenz genau 1µs entspricht. Messungen mit dem DSO habe gezeigt, dass selbst diese "exakten Impulse" mehr als 1µs entsprechen.. Ausch die DS1820 halten sich nicht so genau an diese Zeiten. Wie weiter oben schon gesagt, spielt das keine so wichtige Rolle, weil der Datentransfer nicht synchron verläuft sondern für jedes Bit eigens erneut angestoßen wird und Dallas relativ große Spielräume zulässt. Dennoch ist eine Aufzeichnung der Impulsformen zur Kontrolle nicht uninteressant.
Bemerkenswert ist, dass die Leseroutine für ein Bit dessen Wert nicht in einem Register sondern im Carrybit des Prozessors zurückgibt. Das erleichtert in der Routine, die ein Byte aus den empfangenen Bits aufbaut die Programmierung.
Ähnlich wie die Leseroutine sieht die Senderoutine des Masters aus. Für beide Routinen ist es für das Studium gut, sich das Zeitschema des Datenblatts nebenan bereit zu legen.
Der Einsatz der Routine DS1820_reset zeigt die Verwendung des Errorflags für den Fall, dass die Funktion kein Device auf dem Bus vorfindet.
Die Routinen zum Lesen und Schreiben eines Bytes vom 1-Wire-Bus benutzen das Register R20 als Parameterregister. Beim Lesen wird das Carrybit jeweils in das Reister R20 von links hineinrotiert (ror R20). Das erste reingeschobene Bit landet so nach 8 Schienevorgängeg im LSB von R20. Beim Schreiben wird durch die Routine ds1820_write_bit jeweils das LSB nach rechts herausgeschoben (lsr R20) und auf den Bus gelegt.
Die Routine DS1820_init demonstriert den Gebrauch der ROM-Kommandos DS1820_CMD_SKIPROM und MATCH_ROM sowie DS1820_CMD_WSCRATCHPAD. Im Fall eines DS1820 genügt für die Initialisierung ein einfacher Reset, weil es keine Konfiguration zu schreiben gibt. Liegt ein Baustein DS18B20 vor, dann kann mittels Scratchpad-Inhalt ein Konfigurationsbyte geschrieben werden. Dazu muss aber der Baustein im Falle mehrerer Devices am Bus zuerst mittels MATCHROM addressiert werden. Erst dann kann nach den beiden Alarmbytes das Konfigurationsbyte geschrieben werden.
Auch beim Einlesen der Rohtemperaturwerte muss zwischen einem und mehreren DS1820 auf dem Bus unterschieden werden. Bei mehreren Devices muss zunächst das Device mittels MATCHROM ausgewählt werden. Dann wird das Scratchpad eingelesen und die ersten beiden Bytes in die Register tlow und thigh übertragen und zurückgegeben.
Die Routine Read_scratchpad liest den gesamten Inhalt des Scratchpads in den SDRAM-Puffer. Bei nur einem Device am Bus (single_device=true) wird die schnellere Variante mit SKIPROM genutzt. Bei einem Multidevicesystem muss der ROM-Code des anzusprechenden Device im SDRAM-Puffer stehen. Er ist zuvor durch Angabe der Speicherplatznummer aus dem EEPROM dorthin zu bringen (recall_ROM_from_EE). Falls lauter 0xFF (=nicht das richtige Device present) oder ein Übertragungsfehler austritt, wird das Errorflag gesetzt. Ein Versuch zeigt das Verhalten.
Im Mainprogram wurde ein Byte des ROM-Puffercodes durch ein willkürlich gewähltes anderes vor dem Aufruf der Funktion Read_scratchpad verändert, sodass kein Device mit dem gewünschten ROM-Code sich am Bus befand. Das Ergebnis zeigt der Plot vom Hyperterminal.
Das Byte 0x9B wurdenach dem Auslesen der Temperatur aber vor dem Aufruf von Read_scratchpad mit 0x7E überschrieben. Der vorher adressierbare Sensor ist somit nicht mehr durch MATCHROM auffindbar. Weil aber noch zwei weitere Sensoren, nämlich die bislang registrierten, am Bus waren wurde vom Master ein Presencepulse registriert und das Errorflag somit nicht gesetzt.
Die Fehlersituation erkennt man an zwei Stellen. Es ist erstens unwahrscheinlich, dass lauter 0xFF-Bytes vom DS1820 übertragen wurden und zweitens ist die Prüfsumme dieser Übertragung 0x63 und nicht 0xFF wie das letzte Byte vorspiegelt.
Die Routine save_ROM_code2EE stellt zunächst fest, wie viel EEPROM-Speicher zur Verfügung stehen und richtet dann erst einmal das Registerpaar Z als Zeiger auf die Tabelle des Starts der Speicheradressen im EEPROM ein, die sich im Flashspeicher am Ende des Moduls befinden. Dabei muss je nach EEPROM-Umfang zwischen Byte- und Word-Adressen unterschieden werden. Die Startadresse für maximal 256 Byte EEPROMs kommt nach R17, die Adresse für größere EEPROMs kommt als Pointer ins Registerpaar Y. Registerpaar Z wird nun auf den Start des Puffers im SDRAM gesetzt und der Speichervorgang nach dem Laden des Durchgangszählers R18 gestartet. Anfangs war die Struktur nur 8 Bytes groß, gegenwärtig umfasst sie 10 Bytes. Die Größe ist leicht über die Konstante structuresize anpassbar.
Bemerkenswert in diesem Kapitel ist noch die Routine CRC_one_byte. Die relativ kurze Sequenz berechnet Byte für Byte die Prüfsumme und speichert den Zwischenwert in der Variable s_CRC im SDRAM ab. Nachdem das letzte Byte, die Prüfsumme vom DS1820 übergeben wurde, muss als Ergebnis 0x00 heraus kommen. Der Startwert in s_CRC ist ebenfalls 0x00. Eine C++-Routine habe ich in der APP-Note 162 von MAXXIM gefunden und nach einigem Probieren in Assembler transskribieren können. Das Progrämmchen benötigt deutlich weniger Speicherplatz wie die ebenfalls dort vorgestellte Variante mit einer 256-Byte-Tabelle. Den Einsatz dieser Routine kann man z.B. an der API-Methode OWSEARCH studieren.
Eine Reihe von Unterprogrammen beschäftigt sich mit der Umwandlung der Temperatur in ein Format, das problemlos von Menschen überschaubar ist und trotzdem vom AVR leicht zu Vergleichszwecken herangezogen werden kann. Beim DS1820 wird die ausgelesene Temperatur dzu einfach mit 10 multipiliziert und in 2 Bytes (R26:R27) untergebracht. Zum Vergleich mit einer Temperaturkonstante lässt man bei der Angabe schlicht das Komma weg. Aus 23,5°C wird 235, aus 57,0°C wird 570 und aus -12,5°C wird -125. Durch einen Zwei-Byte-Vergleich kann der AVR leicht eingelesene Temperaturen mit Konstanten vergleichen. So geschieht das z.B. in dem genannten Artikel von ELEKTOR.
Um die ganze Genauigkeit beim DS18B20 auszunutzen, bedarf es eines etwas mehr an Aufwand. Weil der Nachkommateil mindestens dreistellig berücksichtigt werden soll, ist hier der Faktor nicht 10 sondern 1000. Für die Berechnung wird jetzt nicht nur ein einfaches Temperatur*2+Temperatur*8 hergenommen sondern gleich das Arithmetikmodul bemüht. Damit sind Ganzzahlberechnungen mit 2, 3 und 4Byte kein Problem. Das Ergebnis Temperatur*1000 steht im AccuB und kann zum Speichern in eine Struktur von 3 Bytes im SDRAM (EEPROM) übertragen werden. Für Vergleiche ist ein 3-Byte-Vergleich nötig.
Zur Anzeige oder Ausgabe via RS232 benötigt man ein Format, das ganzzahligen Anteil und Nachkommastellen in korrekter Art und Weise wiedergeben kann. Der Ganzzahlige Anteil ist trivial, für die Nachkommastellen braucht es etwas mehr Aufwand. Die Funktionen ds1820_temp_int_frac erledigen das in Abhängigkeit vom Sensortyp. Hier die Lösung für den DS1820 mit einer Nachkommastelle.
Weil die Ausgabe einer Temperatur von einem DS18B20 anders verlaufen muss wie für einen DS1820, wurde die Routine convert_temp_int_frac eingeführt. Anstatt die dedizierten Routinen für die beiden Typen jeweils einzeln aufzurufen, sorgt dieses UP anhand der Variablen ds_type für die korrekte Umwandlung entweder über DS18B20_temp_int_frac oder DS1820_temp_int_frac. Eine entsprechende Ausgaberoutine wird erwogen, ist aber noch nicht realisiert.
Die nachfolgenden Routinen befinden sich nicht im Modul 1wire.asm sondern, weil sie der direkten Ausgabe auf RS232 dienen, im Modul RS232.asm. Sie werden in ein Projet über den Schalter v24_DS18X20_used, welcher sich in rs232_h.asm findet, mit eingebunden, indem man dem Schalter den Wert 1 zuweist.
Ähnliche Routinen für LDCs erhält man, indem man in den UPs v24 durch LCD ersetzt. Ein dem Schalter v24_DS18X20_used entsprechender im LCD-Modul ist sicher hilfreich, dann kann man die UPs ähnlich wie im RS232-Modul konditional in Projekte einbinden.
Hier wie immer alle Downloadangebote zum Projekt in der Übersicht.
Assembler-Pakete, HEX-File für den ATTiny4313, neueste Modulbibliothek(2016-10-23), Impulsprotokoll zur Fehlersuche von MATCHROM