TSic(TM) 206/306 Thermometer
Ich habe den
Tsic 206 schon bei meiner Tinyclock
eingesetzt. Nun habe ich mir den Sensor nochmal vorgenommen um meine
Kenntnisse zu erweitern. Während ich bei der Tinyclock
noch keine Auswertung des Paritätsbit und Sensorerkennung
programmiert habe, habe ich diese Funktionen beim TSic 206/306
Thermometer nachgeholt.
Das Thermometer zeigt Temperaturen von -50 °C bis 150 °C an. Der
verwendete Sensor hat eine Auflösung von 0,1 °C und eine Genauigkeit
von 0,3 °C (TSic306) im Temperaturbereich von 10° bis 90°.
Das beste ist jedoch das Handliche TO92 Gehäuse und das der
Tsic
schon kalibriert ist. Wer schon einmal bei einem Selbstbauthermometer
den Temperatursensor mit Eiswasser und Fieberthermometer abgeglichen
hat, weiss wovon ich rede.
Da das Display nur 3 stellig ist, werden nur im Bereich von -9.9° bis
+99.9°C Zehntel Grad angezeigt. Von -50° bis -10° und von
+100° bis 150° werden nur Ganzzahlige Temperaturwerte angezeigt. Bei
einem Übertragungsfehler wird im Display "P-E" für Parity-Error
angezeigt. Ist kein Sensor angeschlossen oder der TSic
antwortet aus anderen Gründen nicht, erscheint im Display "S-E" für
Sensor-Error.
Mehr zum Sensor in diesem
Datenblatt.
Als Hardware habe ich (wie schon öfter) mein Portview
Modul benutzt. Diese kleine Platine hat im Wesentlichen einen
AT Mega48 oder AT Mega8 auf der Unterseite und 3 Siebensegmentanzeigen
auf der Oberseite.
Anschluss

Der Tsic 206 oder 306 wird wie folgt am Portview Modul angeschlossen.
Pin1 | V+ | SV1 - 2 | rot |
Pin2 | Signal | SV1 - 1 | orange |
Pin3 | GND | SV1 - 9 | blau |
Zwischen Pin1 und 3 ist ein 100nF Kerko im SMD 0805 Gehäuse angelötet. Zusätzlich ist ein 220 Ohm Widerstand in die V+ Leitung einzulöten, den man am besten mit einem Stück Schrumpfschlauch einschließt. Der Sensor lässt sich sehr schön in ein Kugeschreiberdruckknopf einschliessen (siehe Tinyclock) wird dadurch aber etwas träger.
Da ich anfangs selbst auf der Suche nach Informationen über das Zacwire Protokoll war und diese im WWW recht spärlich ausfallen möchte ich hier meine Erfahrungen mit dem (wie ich finde) interessanten Sensor weitergeben, vielleicht hilft es ja jemanden. Wer also nur das Thermometer nachbauen möchte, kann die Technischen Details unten überspringen und einfach die kleine Portview Platine aufbauen den Sensor anschliessen und die Thermometer Firmware aufspielen. Die Fuse-Bits werden dabei so eingestellt, das der Mega8(48) mit den internen R-C Oszilator mit 8 MHz läuft.
Das Zacwire(TM) Protokoll
Der TSic 206/306 sendet die Temperaturdaten über das sogenannte Zacwire Protokoll. Sobald man die Versorgungsspannung an den Sensor anlegt sendet dieser nach 65 bis 85 mS seine 2 Datenbytes über die Datenleitung. Anders als bei anderen digitalen Sensoren mit z.B. I2C oder SPI Schnittstelle, sendet der TSic 206/306 seine Daten ungefragt. Bleibt die Spannung eingeschaltet "feuert" der TSic ca. alle 100 mS eine weiteres Datenwort ab. Die Datenleitung des TSic ist also zu jeder Zeit Ausgang, während der Dateneingang am Mikrocontroller zu jeder Zeit Eingang ist. Die Datenleitung ist im Ruhezustand auf Highpegel.
Möchte man nur eine Portleitung für den Temperatursensor opfern, müsste V+ des TSic fest mit +5V verbunden werden und das Programm vor dem auslesen des Sensors testen ob dieser gerade sendet.
Das könnte man einfach realisieren, indem man sicherstellt, das vor dem Auslesen die Datenleitung für mindestens 160 µS auf Highpegel ist. In meiner Schaltung habe ich den V+ des TSic Sensors an einen Ausgangspin des AVR angeschlossen und kann so den Sensor über Software ein und ausschalten. Das Datenblatt sieht bei dieser Anschlussart einen 220Ohm Widerstand in der V+ Leitung vor, der zusammen mit dem 100nF Kondensator einen Tiefpass bildet.
Die beiden Datenbytes...
...enthalten einen 11 Bit Rohwert der Temperatur, der später
mit einer Formel in °C (oder aber °F) umgerechnet wird und jeweils ein
Start- und ein Paritybit. Zuerst wird das Highbyte übertragen das in
den Bits 3..7 eine Null enthält und in Bit 0..2 die höherwertigen Bits
des 11 Bit Rohwertes. Danach folgt (nach einem ominösen halben
Stopbit) das Lowbyte, welches als Beispiel im Bild
unten zu sehen ist. Die Datenübertragung beginnt mit einer negativen
Flanke auf der Datenleitung.
Die Übertragung der beiden Datenbytes ist in 2,7 mS abgeschlossen.
Auf dem Bild unten ist der Verlauf von einem Datenbyte dargestellt.

Die Übertragungsdauer für ein Bit ist ca. 125µS. Der
Unterschied zwischen Logisch 0 und Logisch 1, wird über das Tastverhältniss
(Duty-Cycle) gesteuert. Bei Logisch 0 beträgt dieses 25% bei Logisch 1
75%.
Das Startbit ist im Datenblatt mit T-Strobe benannt und hat ein
Tastverhältnis von 50%. Danach folgen 8 Datenbits und zum Schluss das
Paritybit. Der Ausgangswert von T-Strobe ist im Datenblatt mit 62,5 µS
angegeben. Leider bleibt dieser Wert nicht mit der Temperatur konstant,
deshalb ist es ratsam die T-Strobezeit vor jedem übertragenen Byte
erneut zu messen. Im Bild oben habe ich für die zu messende T-Strobe
Zeit eine gelbe Strecke eingezeichnet.
Die gemessene T-Strobe Zeit, wartet man bei den folgenden Bits
ab (im Bild die eingezeichneten roten Strecken), bevor man die
Datenleitung abfragt. Der Timer wird bei jedem Bit, ab der fallenden
Flanke gestartet. Da die Übertragung jedes weiteren Bits auch mit einer
negativen Flanke beginnt, wird die Übertragung mit jedem Bit neu
synchronisiert.
Assembler Beispiel:
;T-Strobe Zeit (etwa 62,5 µS) messen.
;Hier für den Mega48 mit dem 8-Bit Timer/Counter0 (Compare A Match)
;---------------------------------------------------------------------
;Da sich T-Strobe mit der Temperatur ändert, bei jedem Byte neu messen
;---------------------------------------------------------------------
TStrobe_time:
out TCNT0,null ;8Bit Timer0, Zähler=0
ldi temp2,1<<CS01 ;Prescaler /8 vorbereiten
wdl: sbic PinC,tsic_dat ;Auf TSIC, neg. Flanke warten
rjmp wdl
out TCCR0B,temp2 ;Timer Los
wpc: sbis PinC,tsic_dat ;Warten, bis die Datenleitung wieder High
rjmp wpc
out TCCR0B,null ;Timer Stop
in temp2,TCNT0 ;Zählerstand holen...
out OCR0A,temp2 ;und in das Timer Vergleichsreg. A schreiben
ret
;-----------------------------------------------------------------
;Die oben gemessene Zeit abwarten
;-----------------------------------------------------------------
Wait_TStrobe:
sbi TIFR0,OCF0A ;Vergleichsflag A löschen
out TCNT0,null ;Zähler=0
ldi temp2,1<<CS01 ;Vorteiler /8 vorbereiten
wstr: sbic PinC,tsic_dat ;Auf neg. Flanke warten
rjmp wstr
out TCCR0B,temp2 ;Timer Los
wt0: sbis TIFR0,OCF0A ;Auf Match warten
rjmp wt0
out TCCR0B,null ;Timer Stop
ret
Im Beispiel oben sind die beiden Unterprogramme zum messen und abwarten der T-Strobe Zeit mit einem 8-Bit Timer dargestellt. Im kompletten Quellcode ist auch ein Beispiel in dem einfach Zyklen gezählt werden, für den Fall das der Timer für andere Zwecke gebraucht wird. Da mein Programm nur den Sensor ausliest und auf dem Display anzeigt, habe ich mich für das Auslesen über polling entschieden, vielleicht probier ich das irgendwann auch interruptgesteuert aus.
Das Parity Bit...
...dient der Erkennung von Übertragungsfehlern wie sie z.B. bei Verwendung von zu langen oder schlecht abgeschirmten Kabeln auftreten können. Bei den Tsic 206/306 Sensoren wird auf even (gerade) Parity geprüft. Dazu werden alle gesetzten Bits (die Einsen) im Datenbyte gezählt und das Paritybit addiert. Das Ergebnis muss eine gerade Zahl sein, ansonsten ist ein Übertragungsfehler aufgetreten. Im Bild oben wird die Binärzahl 0b11101010 übertragen. Die Quersumme ist 5 (dezimal), welche ja eine ungerade Zahl ist. Also ist in diesem Fall das Paritätsbit gesetzt.
Assembler Beispiel: Ein Byte lesen und Parity testen
;Datenbyte vom TSic lesen
;-----------------------------------------------------------------
TSIC_byte:
clr temp4 ;Parity
ldi temp3,1 ;Hier kommt das Datenbyte des TSIC rein
rcall TStrobe_time ;Wie lange ist PinC0 (Daten TSic) low ?
w_msb: lsl temp3 ;Eine 0 ins DatenByte schieben
rcall Wait_TStrobe ;TStrobe Zeit abwarten
sbic PinC,tsic_dat ;Wenn Datenpin auf 0, die 0(lsl oben)stehen lassen
inc temp3 ;Ansonsten Bit0 setzen
sbrc temp3,0 ;Wenn Bit0 auf 1...
inc temp4 ;...zum Paritybyte addieren
wh0: sbis pinc,tsic_dat ;Auf High warten
rjmp wh0
brcc w_msb ;Wenn die 1 (oben, ldi temp3,1)im Carry ist, sind wir fertig
;-----------------------------------------------------------------
;Paritätsbit holen und auf even prüfen
;Bei Even Parity, muss die Summe aller Bits + das Paritybit gerade sein
;-----------------------------------------------------------------
rcall Wait_TStrobe ;TStrobe Zeit abwarten
sbic pinc,tsic_dat
inc temp4 ;Wenn Paritybit=1, Temp4=Temp4+1
sbrc temp4,0 ;Zahl in Temp4 muss gerade (even Parity) sein
sbr flags,1<<par_err ;sonst Übertragungsfehler
wh1: sbis pinc,tsic_dat ;Auf High warten
rjmp wh1
ret
Um den 11 Bit Temperaturwert zu erhalten muss TSIC_byte 2 mal hintereinander aufgerufen werden. Das im Datenblatt erwähnte halbe Stopbit zwischen High- und Lowbyte, kann ignoriert werden, da dieses keine negative Flanke sendet sondern lediglich den Highpegel des Paritybit um etwa 62,5 µS verlängert.
Ein komplettes Datenpaket.

Im Beispiel oben wurde das Datenwort 0b01011011010 ($2DA) übertragen, welches nach Umrechnung 21,3°C ergibt.
Temperatur berechnen
Dafür steht
als Formel im Datenblatt T=
(Digital_signal/2047*(HT-LT)+LT) [°C]. Wobei HT
150 (das ist die Max. Temperatur) und LT -50 (Min.Temperatur) ist. Löst
man die Klammer (HT-LT) auf, erhält man 200. Das +LT ersetzt man durch
-50 und erhält T=
(Digital_signal/2047*200-50) [°C] wobei es in
Assemler besser ist, zuerst zu multiplizieren und dann zu dividieren.
Man kann auch schreiben T=
(Digital_signal*200/2047-50)
[°C].
Bei einer Division durch 2047 horcht der ASM-Programmierer auf.
Wen man statt 2047 als Divisor 2048 nehmen würde, hätte man
eine Potenz mit der Basis 2 und könnte die Division alleine
durch Rechtsschieben lösen und dadurch eine Menge Code und Laufzeit
sparen. Der Fehler währe eigentlich zu Vernachlässigen (siehe
Tabelle im Quelltext unten), trotzdem habe ich durch zufügen von
4 Befehlen das Digital_signal
so angepasst das es genau zu den Angaben (Tabelle) im
Datenblatt (Seite 3) passt.
Ein weiterer Vortei ist das man T=
(Digital_signal*200/2048-50)
/8 kürzen kann und erhält so T=
(Digital_signal*25/256-50).
Da mein Thermometer auch Zehntel Grad Anzeigen soll wird anstatt *25
mit 250 multipliziert und anstatt -50 wird 500 subtrahiert und erhält
so die Temperatur*10. Der Dezimalpunkt wird dann einfach vor die letzte
Stelle gesetzt und schon stimmt es wieder. Das ganze nennt
sich Festkommaarithmetik. Infos hierzu z.B. bei Mikrocontroller.net
oder Wikipedia
Meine Endgültige Formel für den TSic 206/306 lautet also T*10= (Digital_signal*250/256-500)
Assembler Beispiel:Temperaturwert berechnen
Im Beispiel wurde TSic_Byte: 2 mal aufgerufen und der TSic-Rohwert (Digital_signal) den Registern rawlow und rawhigh zugewiesen.
; 11Bit Rohwert des TSic in Temperatur umrechnen
;***************************************************************************************
calctemp:
;---------------------------------------------------------------------------------------
;Werte lt. Datenblatt
;$000=-50°, $199=-10°, $200=0°, $2ff=25°, $465=60°, $6fe=125°, $7ff=150°
;---------------------------------------------------------------------------------------
;Wert Formel-Datenbl. Meine Formel ... nach Anpassung Soll
;---------------------------------------------------------------------------------------
;$199 | -10,04 | -10,06 | -10,0 | -10,0
;$200 | 0,02 | 0,00 | 0,0 | 0,0
;$2ff | 24,94 | 24,90 | 25,0 | 25,0
;$465 | 59,92 | 59,86 | 60,0 | 60,0
;$6fe | 124,89 | 124,80 | 125,0 | 125,0
;$7ff | 150,00 | 149,90 | 150,1 | 150,0
;---------------------------------------------------------------------------------------
;Zum testen der Werte, die Auskommentierung unten entfernen
;---------------------------------------------------------------------------------------
/*
.equ test = $2ff
ldi rawlow,low(test)
ldi rawhigh,high(test)
*/
;----------------------------------------------------------
;Tsic Rohwert dem Soll (Tab. oben) lt. Datenblatt anpassen
;---------------------------------------------------------
;Dazu die beiden höchsten Bits (Bit10 und 9) des TSIC auswerten
;Bei TSIC Wert $000..$1ff keine Änderung
;Bei TSIC Wert $200..3ff TSIC Wert+1
;Bei TSIC Wert $400..5ff TSIC Wert+2
;Bei TSIC Wert $600..7ff TSIC Wert+3
;--------------------------------------------------------------
sbrc rawhigh,1
adiw rawlow:rawhigh,1 ;Wenn Bit9=1, Tsic+1
sbrc rawhigh,2
adiw rawlow:rawhigh,2 ;Wenn Bit10=1, Tsic+2
;---------------------------------------------------------------------------------------
; 11Bit Rohwert des TSic in Temperatur umrechnen
; Originalformel aus dem Datenblatt T= (Digital_signal/2047*(HT-LT)+LT) [°C]
; LT = -50, HT = 150 als Standardwert für die Temperatur-Berechnung
;
; Umgestellt für ASM
; t=TSic_Wert*200 /2048-50 (die 2047 um 1 erhöht um besser rechnen zu können)
; t=TSic_Wert*25/256-50 (gekürzt /8)
; Da eine Stelle hinter dem Komma angezeigt wird, rechne ich die Temperatur*10 und
; setze vor die letzte Stelle den Dezimalpunkt (Festkomma).
;
; Temperatur*10=(TSic_Wert*250)/256-500
;---------------------------------------------------------------------------------------
;temp3/2/1 enthält durch zufügen von Temp1 (0) den TSicwert * 256
clr temp1
mov temp2,rawlow
mov temp3,rawhigh
lsl rawlow ;*2
rol rawhigh
add rawlow,temp2 ;*3
adc rawhigh,temp3
lsl rawlow ;*6
rol rawhigh
sub temp1,rawlow ;*256 - *6 = *250
sbc temp2,rawhigh ;TSic_Wert*250 steht jetzt in temp1/temp2/temp3
sbci temp3,0 ;Durch verwerfen des LSB (temp1) wird durch 256 geteilt
subi temp2,low(500) ;500 (50.0) abziehen
sbci temp3,high(500)
;Nun steht die Temperatur(*10) in temp2 (low) und temp3 (high)
Die Temperatur*10 steht nun als 16 Bitzahl zur Verfügung und muss nur noch in den 7-Segmentcode gewandelt werden. Ausserdem ist bei Temperaturen unter 0° das N-Flag durch die Subtraktion schon gesetzt.
Interruptprobleme
Da
die Siebensegmentanzeigen in einem Timerinterrupt gemultiplext werden,
kann es vorkommen das die ISR genau dann aufgerufen wird wenn im
Hauptprogramm der Sensor ausgelesen wird und so das Timing
durcheinander bringt. Den Interrupt während dem Auslesen des Sensors
abzuschalten, hätte zur Folge das die Anzeige periodisch flackert. Das
ist also keine Lösung.
Am besten ist es die ISR so schnell zu durchlaufen, das die
Laufzeit noch in das Zeitfenster des Zacwire-Protokoll passt. Da jedes
Bit 125µS Dauert und das Tastverhältnis bei Logisch 0 bei 25% und bei
Logisch 1 bei 75% liegt, lässt sich der optimale Auslesezeitpunkt der
ja genau in der Mitte ( 50%) liegt nach rechts oder links verschieben
(Im Bild die Strecke +/-31µS).
Theoretisch währe 31,2 µS die maximale Zeit, die man den TSic früher oder später ohne Fehler auslesen könnte.
ISR
Die Ziffern hinter dem Semikolon sind die Taktzyklen für den entsprechenden Befehl. Die Maximale ISR-Dauer ist knapp 2,4µS, da währe noch jede Menge Luft nach oben.
;Timerinterrrupt alle 1mS
;Da die ISR auch währed dem auslesen des TSIC auftritt, ist sie Geschwindigkeitsoptimiert.
;----------------------------------------------------------------------------------------
;Die Maximale ISR-Dauer sind knapp 2,4µS. Der mittlere T-Strobe Wert = 62,5 µS (s. Zackwire Datenblatt)
;T-Strobe würde bei einem Interrupt max. 65µS dauern, ist also noch im grünen Bereich.
;****************************************************************************************
; ;4
ISR:in s,sreg ;1
dec tick ;1 Jede mS runterzählen
;--------------------------------------------------------------------------------------
;Display Multiplexen. Bei3 Anzeigen *1mS sind das 3mS für einen Durchlauf.
;Die Muxfreq. ist 1/3mS = 333.3 Hz
;--------------------------------------------------------------------------------------
sbic PortD,PD5 ;1|2 War zuletzt die 3. Anzeige eingeschaltet,... ; ...1. Anzeige einschalten
rjmp _mux1 ;2
out PortD,pnp1 ;1 PNP Stelle 1, Einschalten (PD7=0)
out PortB,seg0 ;1 Kathoden
out sreg,s ;1 Statusbits zurücklesen
reti ;4 in der Main weitermachen
;15 = 1,875µS
_mux1:
sbic PortD,PD7 ;1|2 War Anz.1 an, Anz.2 einschalten
rjmp _mux2 ;2
out PortD,pnp2 ;1 PNP Einschalten (PD6=0)
out PortB,seg1 ;1 Kathoden
out sreg,s ;1 Statusbits zurücklesen
reti ;4 in der Main weitermachen
;18 = 2,25µS
_mux2:
out PortD,pnp3 ;1 Kann nur noch Stelle 3 sein
out PortB,seg2 ;1
out sreg,s ;1 Statusbits zurücklesen
reti ;4 in der Main weitermachen
;19 = 2,375µS
;***************************************************************************************************************
; --- ENDE ISR ---
;***************************************************************************************************************
Die Register pnp1..3 werden am Programmanfang initialisiert und enthalten die fertigen Bitmasken um den entsprechenden PNP-Transistor in der ISR zu schalten. Diese Verschwendung ist Eigentlich nicht nötig, aber die Register werden bei diesem kleinen Programm eh nicht anderweitig benötigt. Die Register seg0..2 enthalten den Siebensegmentcode der Anzeige. Der Interrupt wird an 3 verschiedenen Stellen verlassen. Das spart den rjmp Befehl und somit etwas Zeit.
Download
Der Hoffentlich gut genug kommentierte Assembler Quellcode für AVR-Studio 4.xx
und die brennfertige .hex für den AT Mega48.
Beim brennen des Megas, die Fuses auf internen 8 MHz Takt stellen.