Version 2 des 1-Wire Simulators

Durch die vielen Erfahrungen mit dem 1-Wire Simulator mit Atmel AVR-Mikrocontrollern ist die Idee für eine grundlegene neue Programierung gekommen. Wichtige Funktionen sollen dabei in einer Relativ statischen Bibliothek enthalten sein. Durch die Programmierung in Assembler könne diese Routinen, in Größe und Geschwindigkeit einmal optimiert, für alle 1-Wire Geräte anwendung finden.

Motivation für die Version 2

Schon lange plante ich den Quellcode für die 1-Wire Simulation mit Atmel AVR-Mikrocontrollern grundlegend zu überarbeiten. Folgendes sollte dabei besser werden:

  1. Trennung von grundlegenden 1-Wire Funktionen und gerätespezifischen Funktionen
  2. Einfaches ändern zwischen verschiedenen AVRs
  3. Implementation von Power Down Modi zur Energieeinsparung und damit eine Verringerung der Wärmeabstrahlung (Wichtig bei Temperaturmessung)
  4. Nach Möglichkeit: Lauffähig auch mit 4 MHz

Die Optimierung der grundlegenden 1-Wire Funktionalität und der ROM-Funktionen (die ja für alle 1-Wire Geräte gleich sind)  soll auch gleich bei allen bisher erstellten Gerätesimulationen gelten. Das bedeutet, dass es keine einzelne Datei mehr geben kann, sondern dass es eine Bibliothek gibt, in der alles Grundlegende gespeichert ist.

Da ich in letzter Zeit sehr viel mit dem AVR in Assembler programmiert habe, habe ich auch diese Grundbibliothek in Assembler geschrieben. Die spezifische Datei für des 1-Wire Gerät ist weiterhin in C. Dadurch lassen sich recht komplizierte Berechnungen wie z.B. der Luftdruck beim BMP085 / BMP280 recht schnell implementieren.

Dateien in der Bibliothek

Folgende Dateien sind in der Bibliothek:

DateiBeschreibung
OWPinInterrupt.s Interrupt für Pegeländerungen am 1-Wire Pin
OWTimerInterrupt.s Timer Interrupt für Zeitmessungen
OWConfig.s Allgemeine Konfiguration (Register und Speicher Belegung, Auswahl der Konfiguration für den speziellen AVR, Macros)
OWSet_ATTINYX4.s Spezielle Konfiguration für ATTINY24 - ATTINY84
OWSet_ATTINYX5.s Spezielle Konfiguration für ATTINY25 - ATTINY85
OWCRC8.s CRC8 Berechnung (Wird je nach Gerät ausgewählt)
OWCRC16.s CRC16 Berechnung (Wird je nach Gerät ausgewählt)
OWRomFunctions.s Alle ROM-Funktionen, sowie die Funktionalität zur Änderung der OW-Pin

Diese Dateien stehen im Unterverzeichnis "common". Für jedes 1-Wire Gerät wird ein weiteres Unterverzeichnis angelegt und dort ist mindestens jeweils eine .c Datei mit dem Hauptprogramm und eine .s Datei mit den spezifischen Assemblerroutinen für das Gerät.

Register und Variablen

Alle Registerbezeichnungen beginnen mit "r_", so dass sie nicht mit Variablen verwechselt werden können. Wenn es dazugehörige Variablen im SRAM gibt, dann stehen sie gleich dahinter.

BezeichnungNr.Hauptfunktion
 r_temp 16 temporäre Zwischenspeicherung
 r_rwbyte / rwbyte 17 aktuelles Byte welches gelesen oder geschrieben wird
 r_temp2 18 temporäres Zusatzregister, wenn eins nicht ausreicht
 r_bcount / bcount 19 Bit, welches gerade behandelt wird ist 1, Bit wird druchgeschoben. r_bcount=0 bedeutet: nächstes Byte muss behandelt werden
 r_mode / mode 20 Aktueller Zustand/Funktion, OW_SLEEP->Lehrlauf, Definition der ROM Commands in OWRomFunctions.s, gerätespezifische Konstanten in der .s Datei im Geräteverzeichnis. Nach dem Wert in mode werden über die Sprungtabelle (handle_stable) die entsprechenden Routinen ausgewählt.
 r_sendflag / sendflag 21  0 => Slave empfängt Daten vom Master, 1=> Slave sendet Daten zum Master
 r_bytep / bytep 22 Pointer auf das nächste (od. aktuelle) Byte
 r_crc 23 Hilfsvariable für die CRC-Berechnung
 srbyte   Aktuelles Byte für SEARCH_ROM
 alarmflag   Für das Suchen von Geräten im Alarmmodus (z.B. beim DS18B20)
 owid   Speicher für die 1-Wire-ID
 zl / zh 30/31 Speicherzugriff
 reset_indicator   Zeigt Hauptprogramm, dass ein Resetimpuls empfangen wurde
 gcontrol   Kommunikation mit dem Programmteilen die nicht in der Interruptroutine laufen

Einige Register werden in der SEARCH_ROM-Routine anders verwendet, um zusätzliche Push und Pop Befehle einzusparen.

Die vom Timing aus betrachtet kritischste Sitation ist wenn der Slave eine "0" an den Master sendet. Der Master zieht die Leitung auf Low und der Slave muss möglist in 6 µs die Leitung auch auf Low ziehen. Der DS9490 lässt die Leitung unter Umständen noch schneller wieder auf High (5V) springen. Nach der 1-Wire Spezifikation muss der Slave die Leitung nach 15 µs auf Low sein, denn dann liest der Master aus. Das ist kein Problem für den AVR. Es ist aber mein Ziel, dass zwischen dem Master-Low und dem Slave-Low keine Pause ist (die Leitung nicht auf High springt).
Es muss also in der Interruptroutine schnell entschieden werden ob eine "0" gesendet werden muss. Eine normal Variable in einem SRAM-Bereich müsste dazu erst in ein Register geladen werden. Das Register müsste vorher gesichert werden. Ein schnellerer Weg ist es, wenn ein ungenutztes Bit in einem I/O-Register des AVR für die Entscheidnugn genutzt wird.

Aus diesem Grund wurde das DDR Bit des Reset Pins für diese Entscheidung zweckentfremdet. Die Reset Leitung wird normalerweise nur zum Programmieren genutzt und da stört diese Anwendung nicht. Um das Reset-Pin als vollwertiges I/O-Pin zu nutzen muss erst eine FUSE gesetzt werden. Dann geht das Programmieren aber nur noch über die Hochvoltprogrammierung.

Es gibt also noch eine weiter Variable (ZEROMAKER), die nicht direkt im Quelltext auftaucht.

Folgende Abbildungen verdeutlicht diesen Zusammenhang nocheinmal:

Variable im SRAM  -> 9,3 µs bis Bus Low
Variable im SRAM -> 9,3 µs bis Bus Low

Variable im I/O Register -> Leitung nicht zwischendurch auf High
Variable im I/O Register -> Leitung nicht zwischendurch auf High

Die beiden Verläufe sind von einem Test mit einem MOSFET (2N7000) als Pegelwandler von 5V am 1-Wirebus und 3V am ATTINY84.

Programmablauf

Herzstück der Simulation ist die Interruptroutine die bei einer fallenden Flanke am 1-Wire Bus aufgerufen wird. In der neuen Software gibt es hier keinen unterschied zwischen den verschiedenen Zuständen wie Reset, Searchrom usw. Bei einem Reset wird ersteinmal ganz normal ein Bit gelesen. Da keine weitere fallende Flanke kommt, wird irgend wann der Timer Interrupt aufgrufen und Prüft ob die Leitung noch Low ist. Der Timer-Interrupt gibt dann entsprechend den Presents Impuls aus und schaltet auf OWM_READ_COMMAND.

Die Eigentlichen Funktionen werden bei handle_byte ausgeführt. Dort Springt das Programm anhand von einer Sprungtabelle und dem Wert in mode sehr effizient an die Stelle, wo der aktuelle Zustand bearbeitet wird.

Bei dem Searchrom-Algorithmus, wo jeweils zwei Bits gesendet (vom Slave gesndet) werden und ein Bit empfangen wird, bekommt der Bitzähler bcount nur entsprechend kleinere Werte und die handle_byte Funktion wird öfterer aufgerufen.

Beim Senden erfolgen die Berechnungen für das nächste Byte zwischen der fallenden Flanke vom Master bis zu der Zeit, in dem der Slave die Leitung wieder frei gibt, wenn denn eine 0 gesendet wird. Beim Empfangen erfolgen die Berechnungen nachdem die Leitung ausgelesen wurde bis maximal zum nächsten Low-Impuls vom Master (etwas eher sollte es schon sein).

Wichtig ist, dass nach einem handle_byte beim Empfangen das sendflag kontroliert wird. Wenn das nächste Byte gesendet wird muss der ZEROMARKER entsprechend gesetzt werden.

Im bei einem Timer Interupt wird geprüft, ob die Leitung Lange genug Low ist, damit der Impuls als Reset Impuls erkannt wird. Dazu gibt es noch die zweite Zeit OWT_RESET2. Die Zeiten wie auch OWT_READ und OWT_WRITE durch Polling eingehalten. Der Timer-Interrupt wird nur bei einem "Timeout" nach einer Fallenden Flanke ausgelöst.

Übersicht über die Zustände bei handle_byte

 In der Datei OWRomFunctions.s sind die grundlegenden Zustände für die 1-Wire Simulation definiert. Diese sind bei jedem Gerät gleich. Deshalb gibt es hier eine kurze Beschreibung dazu:

Zustand (mode)Beschreibung
OW_SLEEP Warten auf einen Resetimpuls. Alle fallenden Flanken auf dem Bus müssen dazu überprüft werden
OW_READ_ROM_COMMAND Nach erfolgreichen Reset wird ein Befehl zur Zugriffskontrolle gelesen.
OW_MATCHROM Empfangen einer ID und prüfen ob es die Eigene ist
OW_SEARCHROMS Search-Rom-Algorithmus: Senden eines Bits aus der ID und danach die Negation (Complement) des Bits
OW_SEARCHROMR Search-Rom-Algorithmus: Empfangen des Steuerbits vom Master
OW_READROM Wenn nur ein 1-Wire Gerät am Bus ist kann die ID ausgelesen werden.
OW_WRITE_NEWID1 Schreiben einer neuen ID in den ID-Zwischenspeicher
OW_READ_NEWID1 Kontrolieren der ID aus den Zwischenspeicher
OW_SET_NEWID1 Übernehmen der ID aus den Zwischenspeicher in den ID-Speicher (EEPROM)
OW_FIRST_COMMAND Kein Zustand, enthällt die Nummer des nächsten Zustandes für die gerätespezifischen Zustände (Befehle)

1 Nur wenn die Möglichkeit zur ID-Veränderung eingeschaltet ist 

Die Zustände ensprechen nicht unbedingt immer den 1-Wire Command-Codes die behandelt werden. Diese sind deshalb nocheinmal in der folgenden Tabelle angegeben:

CodeBeschreibung
0x55 MATCH ROM: selektieren eines Gerätes anhand einer ID
0xF0 SEARCH ROM: Suchalgorithmus für die angeschlossenen Geräte
0xCC SKIP ROM: Überspringen von MATCH ROM wenn nur ein Gerät am Bus
0x33 READ ROM: Lesen der ID wenn nur ein Gerät am Bus
0xEC ALARM SEARCH: SEARCH ROM für Geräte bei denen das Alarm-Flag gesetzt ist
0x75 WRITE NEWID: Schreiben einer neuen ID in den Zwischenspeicher
0xA7 READ NEWID: Lesen der ID aus dem Zwischenspeicher
0x79 SET NEWID: Übernehmen der neuen ID

Nach OW_READ_ROM_COMMAND bzw. nach OW_READ_COMMAND werden die Codes gebrüft. Dafür gibt es in der Datei OWRomFunctions.s zwei Makros. Wenn das Sprungziel im bereich von -63 und +64 Befehlen liegt kann das Makro cjmp verwendet werden. Alternativ, wenn der Linker fehler bringt, muss das aufwändigere Makro cljmp verwendt werden. Dabei wird mehr Speicher benötigt und die Ausführung dauert länger.

In der Regel wird zunächst zu einer Initialisierungsroutinge (hrc_set_[befehl]) gesprungen, die den neuen Zustand und alle anderen Parameter einstellt. In der Sprungtabelle (handle_stable) steht der Sprungbefehl zu der Routine, die den aktuellen Zustand behandlet (rjmp h_[Zustand]).

 

 

Kommentare  

#4 4MHz läuftTobias 2017-04-13 21:08
Hallo Markus,
danke fürs Checken. Schön, dass es auch andere Assemblerkundige gibt ;-). Ich habe die Änderungen eingepflegt. Beim nächsten Checkout werde sie online sein.
Mit „Zero-Polling“ wird es wahrscheinlich auch beim DS2490 oder DS2480 als Master keine High-Spitzen mehr zischen Master-Low und Slave-Low geben. Bei 4 MHz ist ja auch ein betrieb mit 1,8V möglich. Das spart nochmal Energie. Aber auch so ist der AttinyX4A einer der sparsamsten Mikrocontroller den ich kenne.
+1 #3 4MHz läuftMarkus 2017-04-13 19:23
(getestet DS2423 ohne zusätzliche Sensoren)
Hallo Tobias,
vielen Dank für die schnelle Anpassung, im Moment scheint es mit 4Mhz stabil zu laufen und ist unglaublich energiesparend :-) (ca. 60% von der 8MHz Variante)
Out-of-the-Box lief es leider nicht, ich glaube du hast bei den Timings einen Tippfehler gemacht: OWT_present habe ich auf 60 geändert, dann läuft es. Das Oszi sagt dann 90us present-pulse.
OWT_reset2 kommt mir zwar auch etwas kurz vor, aber da es läuft habe ich es lieber nicht verändert.
Außerdem ist mir aufgefallen, dass in der h_read_memory am Ende das temp2 Register (Hi-Addresse) geschrieben werden muss statt temp: sts pack+1,r_temp2

Das ist keine Kritik, bitte nicht falsch verstehen, der Code ist toll geschrieben, beeindruckend effektiv. Ich möchte nur meine Erfahrung zurückgeben, evtl. hilft es einem anderen Nutzer.

Grüße
+1 #2 4Mhz-ModusTobias 2017-04-07 07:43
Hallo Markus,
ich habe lange nichts mehr mit 4 Mhz gemacht. So habe ich gleich mal den Quelltext angepasst. Die Änderungen stehen jetzt unter "Sourcecode" im Git. Wenn du in der entsprechenden OW****.S Datei
#define __4MHZ__
einfügst, dann läuft die Simulation mit 4 Mhz.
(Es geht natürlich auch als Compiler-Option unter Projekteigenschaften -> Toolchain -> AVR/GNU C-Compiler -> Symbols)
Ich hoffe es hilft weiter.
Ich denke aber, sehr viele Devices lassen sich nicht parallel an einem Bus betreiben.
+1 #1 4Mhz-Modus?Markus 2017-04-05 18:40
Hallo,
danke, dass du uns deinen Code zur Verfügung stellst.
Er hat bei mir sofort funktioniert, als ich dann das zusätzliche Fuse Bit nachträglich gesetzt hatte.
Auch wenn die neue Version durch den Assembler Code schwieriger zu verstehen ist, freue ich mich, dass du ihn weiterentwickelt hast. :-)
Ich würde gerne den Controller Bus-powered verwenden. Ist es schon möglich mit 4Mhz zu arbeiten? Welche Änderungen muss ich im Code vornehmen, ich denke ich muss die Timingzeiten halbieren?
Grüße

You have no rights to post comments

Zum Seitenanfang