Halli hallo...
ich habe jetzt wieder mit der Implementierung meiner 1-Wire
Temperatursensoren DS18B20 weiter gemacht.
Das Protokoll habe ich eigentlich schon mehrfach durchgearbeitet und
auch unterschiedliche Programmschnipsel und dachte eigentlich ich hätte
es einigermaßen verstanden.
Nun zeigt der Test mit dem Logikanalysator leider, dass irgendwas schief
läuft.
So wie ich es verstanden habe, ist die Theorie doch wie folgt:
WRITE 0:
> pull bus low
> wait >= 60µs
> release bus
> wait 10µs
WRITE 1:
> pull bus low
> wait <= 15µs
> release bus
> wait 64µs
Ich habe hierfür in 8051er assembler folgende funktionen geschrieben:
; 0 = keine externe Zählersteuerung durch Torschaltung
59
; 0 = interner Zeitgeber
60
; 01 = 16 Bit-Zähler
61
; Timer 0
62
; 0000 = aus
63
clr tf1 ; overflow-flag zurücksetzen
64
setb tr1 ; Starten des Timers
65
ret
66
67
;sets TimerValue to FFFFFFFF - time in µSeconds
68
;inits and starts Timer
69
;and waits till Timeroverflow
70
delay:
71
call initTimer
72
jnb TF1,$ ; notify TimerOverflow
73
clr tr1 ; stop timer
74
ret
Das Timing wird hierbei über den Timer gesteuert. Ein Timer-Schritt
entspricht 1µs. Es muss daher nur die Anzahl an µs in die Register r5
und r6 geschrieben werden und der Timer angeschubst werden. Das
funktioniert auch eigentlich soweit.
Wie man allerdings in dem angehängten bildern sieht, sind die zeiten,
die der bus für eine 0 bzw. 1 auf low gezogen werden muss nicht richtig.
bei einer 0 sind es nur ca. 20µs die das signal auf low ist und bei
einer 1 ganze 74µs..
jetzt ist meine frage :
1. ist einfach mein grundverständnis vom timing und somit auch mein
programm falsch ?
2. oder liegt das problem nicht an meinem verständnis, sondern daran,
dass vlt doch ein fehler in der zeitsteuerung mit den timern vorliegt?
3. warum erkennt der protocol-analyzer die 0-en, obwohl die zeit auch
hier ja garnicht passt.
Im Anhang habe ich den kompletten Code angehängt, der einfach nur die
Adresse des Sensors auslesen und ausgeben soll.
Außerdem hab ich mal das Datenblatt und die Screenshots vom
Logikanalysator mit hochgeladen.
Wäre echt cool, wenn mir jemand helfen könnte :)
Gruß Anon1234
Ohne viel davon zu verstehen, aber folgende Stelle verstehe ich nicht:
1
;setsTimerValuetoFFFFFFFF-timeinµSeconds
2
mova,#0FFh
3
clrcy
4
subba,r5
5
movtl1,a
6
mova,#0FFh
7
subba,r6
8
movth1,a
offenbar ist dein timer 32 bit breit, und du setzt 2x16-bit values über
t11 und th1...?!
#0FFh ist aber nicht #FFFFh, was ich an dieser Stelle vermuten würde...
Wie breit sind denn die Register a, tl1, th1 in deinem System?
So wie es mir aussieht, setzt du den timer auf einen falschen Startwert
(sowas wie 0x00ff00ff - r6<<16 - r5)
Ausserdem kannst du dir das ganze Gedöhns mit #0 in r6 schreiben, um
dann später subb a,r6 zu machen komplett sparen, solange dein delay <
255 oder evtl. auch 65535us (falls die Register 16 bit breit sind)
bleibt...
Easylife schrieb:> Ohne viel davon zu verstehen, aber folgende Stelle verstehe ich> nicht: ; sets TimerValue to FFFFFFFF - time in µSeconds> mov a,#0FFh> clr cy> subb a,r5> mov tl1,a> mov a,#0FFh> subb a,r6> mov th1,a>> offenbar ist dein timer 32 bit breit, und du setzt 2x16-bit values über> t11 und th1...?!>> #0FFh ist aber nicht #FFFFh, was ich an dieser Stelle vermuten würde...> Wie breit sind denn die Register a, tl1, th1 in deinem System?>> So wie es mir aussieht, setzt du den timer auf einen falschen Startwert> (sowas wie 0x00ff00ff - r6<<16 - r5)
danke für deine Schnelle antwort.
der kommentar ist falsch. FFFFFFFF müsste eigentlich FFFF heißen. Die
beiden Register sind nämlich jeweils 8-Bit. Zusammen ergibt das dann
einen 8-Bit Timer.
die Null vor dem #0FFh steht da nur, damit der kompiler den Wert FF als
Wert erkennt. Muss man leider so schreiben, weil F ja ein buchstabe
ist..
Der ganze Schrunz in Deinem initTimer: kostet natürlich auch massig
Zeit.
Und ist außerdem falsch, Du mußt das 2-er Komplement bilden, also
-delay.
Du kannst aber besser das Rechnen zur Laufzeit ganz sparen, der
Assembler kann nämlich 16Bit Konstantenrechnung selber ausführen:
Anon Anon schrieb:> 1. ist einfach mein grundverständnis vom timing und somit auch mein> programm falsch ?
Du machst das viel zu kompliziert.
Bei 1-Wire ist:
Schreiben:
Log.1 = 10us Low, 60us Release.
Log.0 = 60us Low, 10us Release.
Lesen:
Log.1 = 10us Low, 60us Release.
Man hat also immer zuerst 10us Low, danach wird der Bus entweder
weiter auf Low gehalten oder freigegeben.
Die Antwort von Slave liest man nach weiteren 20us, auch wenn keine
vorgesehen ist. Nach weiteren 30us wird der Bus für 10us freigegeben.
Somit hast du nur 1 Stelle wo sich etwas ändert und das ist ganz
einfach (auch in Software mit Loop und NOPs) zu realisieren.
Also:
Bus Low
Delay 10us.
Bus entsprechend bitzustand setzen.
Delay 20us.
Bus lesen.
Delay 30us.
Bus freigeben.
Delay 10us.
Das Ganze 8 mal im Loop.
EDIT:
Beim lesen einfach 0xFF senden.
Danke für die ganzen schnellen Antworten :) :)
Peter Dannegger schrieb:> Du kannst aber besser das Rechnen zur Laufzeit ganz sparen, der> Assembler kann nämlich 16Bit Konstantenrechnung selber ausführen:> delay_60us equ 60>> mov tl0, #low( -delay_60us )> mov th0, #high( -delay_60us )
das ist natürlich ziemlich cool, das kannte ich noch garnicht. Danke für
den Tipp.
Peter Dannegger schrieb:> Und ist außerdem falsch, Du mußt das 2-er Komplement bilden, also> -delay.
Das ist mir gerade nicht klar, warum ich das machen soll. Verkompliziert
das nicht eher alles.
Momentan subtrahiere ich ja mit subb die benötigte zeit von FFFF. Sollte
doch eigentlich so funktionieren?
Marc Vesely schrieb:> Man hat also immer zuerst 10us Low, danach wird der Bus entweder> weiter auf Low gehalten oder freigegeben.> Die Antwort von Slave liest man nach weiteren 20us, auch wenn keine> vorgesehen ist. Nach weiteren 30us wird der Bus für 10us freigegeben.
Danke für das Beispiel :)
Insgesamt würde es dann so aussehen oder?
1
; write lsb of a to bus
2
; - pull bus LOW
3
; - wait 10µs
4
; - if HIGH
5
; > release bus
6
; > wait 60µs
7
; - if LOW
8
; > wait 50µs
9
; > release bus
10
; > wait 10µs
11
; - wait 20µs
12
; - read bus
13
; - save bus
14
; - wait 30µs
15
; - release bus
16
; - wait 10µs
Die Idee mit den Nops finde ich allerdings nicht so elegant. wie mit dem
Timer.. Ist halt die Frage, was hier zielführender ist.
Diese routine gibt den gelesenen Byte in a zurück.
Anon Anon schrieb:> Die Idee mit den Nops finde ich allerdings nicht so elegant. wie mit dem> Timer.. Ist halt die Frage, was hier zielführender ist.
Unwichtig.
Mach es halt so, wie es dir am Besten erscheint.
Anon Anon schrieb:> Das ist mir gerade nicht klar, warum ich das machen soll. Verkompliziert> das nicht eher alles.
Nein, wieso ?
Der Compiler rechnet das für dich, wenn das verkomplizieren ist...
Anon Anon schrieb:> Momentan subtrahiere ich ja mit subb die benötigte zeit von FFFF. Sollte> doch eigentlich so funktionieren?
Auch wieder nein.
Der Compiler hätte es schon richtig gemacht, du nicht.
Überlauf tritt erst beim FFFF -> 0 auf, nicht schon beim 0xFFFF.
-60 ist eben (0 - 60) ;-D
Das hat der PeDa gemeint.
Anon Anon schrieb:> Die Idee mit den Nops finde ich allerdings nicht so elegant. wie mit dem> Timer.. Ist halt die Frage, was hier zielführender ist.
Bei solch kurzen Wartepausen sind NOP-Delays nicht so falsch, vor allem,
wenn die beiden Timer bereits verwendet werden, z.B. für UART und Uhr.
Marc Vesely schrieb:> Eher so:
Aah okay.. also das mit der Schleife hätte ich schon noch gemacht :D
aber das mit dem überflüssigen waits für LOW hatte ich nicht so
geblickt. Jetzt wo ichs lese ist es aber klar.
Hallo nochmal...
danke für eure guten Antworten.
Es hat bei mir jetzt wieder mal ein bisschen gedauert, bis ich hier
weitergemacht hab, aber nach euren guten Tipps habe ich jetzt ein paar
Fortschritte.
Zuerst hab ich mein Glück noch einmal mit dem Timer versucht, musste
dann aber feststellen, (wie ihr ja schon geschrieben hattet), dass die
ganzen Befehle zum Einstellen und Starten des Timers schon viel zu viel
Zeit verbraucht haben und somit beim Timing (logischer weise) nichts
mehr gepasst hat.
Deswegen bin ich jetzt doch auf die Variante mit den Nops umgestiegen.
Und hab mich da ein bisschen an das gehalten, was peda in seinem code
mit den oben berechneten Konstanten gemacht hat.
Bei mir sieht der code jetzt wie folgt aus :
; writes lsb to bus and / or reads bus into msb of r7
21
22
; pull bus low
23
; wait 10µs
24
; if lsb = HIGH
25
; > release bus
26
; else
27
; > bus stays low
28
; wait 20µs
29
; read and save bus
30
; wait 30µs
31
; release bus
32
; wait 10µs
33
write_read:
34
clr p2.4 ; pull bus LOW
35
mov r1,#wait10 ;
36
wait10loop_1: ; wait 10µs
37
nop ;
38
djnz r1,wait10loop_1 ;
39
mov a,r7
40
jnb acc.0,read
41
setb p2.4 ; release bus if r7.0 is HIGH
42
read:
43
mov r1,#wait20 ;
44
wait20loop_1: ; wait 20µs
45
nop ;
46
djnz r1,wait20loop_1 ;
47
mov a,r7
48
rr a ; rotate a
49
jb p2.4,high_1
50
anl a,#01111111b ; set msb to 0
51
jmp afterRead_1
52
high_1:
53
anl a,#11111111b ; set msb to 1
54
afterRead_1:
55
mov r7,a
56
mov r1,#wait30 ;
57
wait30loop_1: ; wait 30µs
58
nop ;
59
djnz r1,wait30loop_1 ;
60
setb p2.4 ; release bus
61
mov r1,#wait10 ;
62
wait10loop_2: ; wait 10µs
63
nop ;
64
djnz r1,wait10loop_2 ;
65
ret
Hiermit hat es bei mir geklappt einen READROM Befehl zu schicken und die
Adresse des sensors zu lesen.
Die Adresse die ich hierbei mit dem Logikanalysator gelesen habe war die
folgende
1
00101000 28h Family Code
2
01000101 LSB Serial Code
3
01111111 ...
4
10101111 ...
5
00000101 ...
6
00000000 ...
7
00000000 MSB Serial Code
8
10000100 CRC
Da der sogenannte Family Code mit 28h richtig ist, bin ich jetzt
ersteinmal davon ausgegangen, dass die komplette Adresse richtig ist.
Im nächsten Schritt wollte ich jetzt Befehle wie MatchRom und dann
ConvertT und ReadScratchpad an meinen Sensor (DS18B20) schicken.
Viele Grüße
Anon1234