Hallo zusammen, ich versuche nun schon mehrere Wochen einen DS1820 per 1-wire an einen TINY25 zu koppeln, schaffe es jedoch nie eine Temperatur auszulesen. Das ganze soll ein kleines Thermometer werden. Ich habe schon viel Erfahrung in der µC-Programmierung und einiges versucht. Das Problem sind wohl immer die Timings (wie eine Oszi-Messung bestätigt). Habe schon folgendes gemacht (CLK: 8Mhz, interner Quarz): - Timer verwendet (Vorteiler 16, Wartezeit/2, da nur 8-Bit-Timer) - Timer verwendet (Vorteiler 8, wenn Wartezeit <255 sonst: Vorteiler 16, Wartezeit/2) Beides mit und ohne Interrupt. Alles scheinbar zu ungenau. - _delay_us - _delay_loop - _delay_loop_2 (lt. Doku braucht sie 4 Takte, also Wartezeit*2 + while(us--)) - per _asm_ volatile ("nop"); mit verschiedenen nop-Anzahlen und while(us--) nop(); Hat alles bisher nicht geklappt! Hat evtl. jemand eine Idee oder sogar Code (wenn möglich in C), ohne dass ich noch weitere Bauteile "anschweißen" muss. Vielen lieben Dank! Gruß Erik
Wenn das Timing nicht stimmt, prüfe erstmal, ob es wirklich 8MHz sind. In der Codesammlung ist ein altes Beispiel, wo ich mir das Delay noch selber gebastelt hab. Ich würde aber dazu raten, die jetzigen Delay-Macros des AVR-GCC zu nehmen, die sind ausreichend genau. Natürlich muß man -Os benutzen. Was Du nicht benutzen darfst, sind long long Variablen (64 Bit), da verpulvert der AVR-GCC Unmengen an CPU-Zeit und Flash, sogar float ist schneller. Peter
Ich hatte bei einem Projekt ähnliche Probleme. Da lag es an den aktivierten Interrupts, die das Timing störten. Also vor jedem Zugriff auf die Sensoren erst die Interrupts abschalten. Ansonsten habe ich das einfach mit delays "sequentiell" gelöst und mit einem Logic Analyzer kontrolliert. Durch den niedrigen Takt bei meinem Projekt beeinflussten schon die Funktionsaurufe zum Schreiben der einzelnen Bytes das Timing, d.h. ich musste die delays kürzer machen. Ansonsten solltest du noch ein paar Infos geben. Nutzt du 3 Pins des Sensors (VCC, Data, GND) oder nur 2? Wenn du 2 Pins benutzt, musst du die Pins VCC und GND verbinden und auf GND legen. Deine Software muss entsprechend angepasst sein so dass sie den Sensor während der Messung mit Strom versorgt (für ich glaube 750ms). In dieser Zeit darf die Leitung nicht Low werden, sonst ließt du nur 85 Grad.
Kann es sein, daß Delays in diesem Fall ziemlicher Murks sind?
Klaus Wachtler schrieb: > Kann es sein, daß Delays in diesem Fall ziemlicher Murks sind? Nein. Peter
Klaus Wachtler schrieb: > Kann es sein, daß Delays in diesem Fall ziemlicher Murks sind? Eigentlich nicht. Peters Code hat bei mir auf Anhieb funktioniert. Allerdings hab ich auch Probleme, wenn Interrupts erlaubt sind -> cli / sei verwenden.
ok, hat hoffentlich niemand persönlich genommen. Aber es ist doch so, daß man ein genaues Timing mit _delay... nicht hinbekommt und gleichzeitig noch etwas anders machen kann als die Temperatur zu lesen; also kann man ausschließlich sequentiell arbeiten. Wenn das reicht, kann man es natürlich so machen.
Klaus Wachtler schrieb: > Aber es ist doch so, daß man ein genaues Timing mit _delay... > nicht hinbekommt und gleichzeitig noch etwas anders machen kann > als die Temperatur zu lesen; also kann man ausschließlich > sequentiell arbeiten. Grundsätzlich richtig. Ich bin da pragmatisch vorgegangen: Den DS1820 auslesen sind nur wenige Bytes hin und her Pro Byte sind Interrupts gesperrt. Das Auslesen passiert alle heiligen Zeiten mal (alle 8 Sekunden) Bei mir ist das schnell genug, dass ich einen anderen wichtigen Interrupt nicht versäume (INT0, 50Hz generiert aus 230V, für die Uhr) Der Rest ist nicht zeitkritisch. Und am wichtigsten: Der Code war schon da :-)
Klaus Wachtler schrieb: > Aber es ist doch so, daß man ein genaues Timing mit _delay... > nicht hinbekommt und gleichzeitig noch etwas anders machen kann > als die Temperatur zu lesen; also kann man ausschließlich > sequentiell arbeiten. So genau muß das ja auch nicht sein. Der Resetpuls kann 480 ... 960µs lang sein. Wenn also alle Interrupts zusammen <480µs dauern, braucht man dabei die Interrupts nicht zu sperren. Die Daten werden dann bitweise übertragen, da empfiehlt es sich die Interrupts für ein Bit (60µs) zu sperren. 60µs Interruptlatenz sollten in der Regel zu verkraften sein. Peter
So, Ende der Diskussion, ich nehme alles zurück. Ich hatte in meiner geistigen Umnachtung nicht den DS1820 vor Augen, sondern den TSic306. Da geht es wesentlich genauer zu. Hier müsste es auch entspannter klappen.
Um auch etwas positives beizutragen: Hier die relevanten Abschnitte aus einem erfolgreichen Versuch mit einem DS18S20:
1 | #include <avr/io.h> |
2 | #include <inttypes.h> |
3 | #include <util/delay.h> |
4 | |
5 | typedef union |
6 | {
|
7 | uint8_t byte; |
8 | struct
|
9 | {
|
10 | unsigned int bit0:1; |
11 | unsigned int bit1:1; |
12 | unsigned int bit2:1; |
13 | unsigned int bit3:1; |
14 | unsigned int bit4:1; |
15 | unsigned int bit5:1; |
16 | unsigned int bit6:1; |
17 | unsigned int bit7:1; |
18 | } s; |
19 | } byte8bits_t; |
20 | |
21 | |
22 | #ifdef DDRA
|
23 | #define ddra (*(volatile byte8bits_t*)(&DDRA))
|
24 | #define porta (*(volatile byte8bits_t*)(&PORTA))
|
25 | #define pina (*(volatile byte8bits_t*)(&PINA))
|
26 | #endif /* ifdef DDRA */ |
27 | |
28 | #ifdef DDRB
|
29 | #define ddrb (*(volatile byte8bits_t*)(&DDRB))
|
30 | #define portb (*(volatile byte8bits_t*)(&PORTB))
|
31 | #define pinb (*(volatile byte8bits_t*)(&PINB))
|
32 | #endif /* ifdef DDRB */ |
33 | |
34 | #ifdef DDRC
|
35 | #define ddrc (*(volatile byte8bits_t*)(&DDRC))
|
36 | #define portc (*(volatile byte8bits_t*)(&PORTC))
|
37 | #define pinc (*(volatile byte8bits_t*)(&PINC))
|
38 | #endif /* ifdef DDRC */ |
39 | |
40 | #ifdef DDRD
|
41 | #define ddrd (*(volatile byte8bits_t*)(&DDRD))
|
42 | #define portd (*(volatile byte8bits_t*)(&PORTD))
|
43 | #define pind (*(volatile byte8bits_t*)(&PIND))
|
44 | #endif /* ifdef DDRD */ |
45 | |
46 | |
47 | |
48 | #define DDR_DS18S20_VCC ddrd.s.bit1
|
49 | #define PORT_DS18S20_VCC portd.s.bit1
|
50 | #define PIN_DS18S20_VCC pind.s.bit1
|
51 | |
52 | #define DDR_DS18S20_DQ ddrd.s.bit2
|
53 | #define PORT_DS18S20_DQ portd.s.bit2
|
54 | #define PIN_DS18S20_DQ pind.s.bit2
|
55 | |
56 | |
57 | |
58 | |
59 | uint8_t reset1wire() |
60 | {
|
61 | int cnt_wait = 0; |
62 | PORT_DS18S20_DQ = 0; |
63 | _delay_us( 480 ); |
64 | PORT_DS18S20_DQ = 1; |
65 | // jetzt wartet der slave 15...60 usec und zieht dann die Leitung
|
66 | // für 60...240 sec auf low (presence pulse).
|
67 | // Also Pin als Eingang setzen, 60 usec warten und dann warten,
|
68 | // bis die Leitung wieder hoch geht:
|
69 | DDR_DS18S20_DQ = 0; |
70 | _delay_us( 60 ); |
71 | while( 1 ) |
72 | {
|
73 | if( PIN_DS18S20_DQ ) |
74 | {
|
75 | break; |
76 | }
|
77 | cnt_wait++; |
78 | _delay_us( 1 ); |
79 | }
|
80 | DDR_DS18S20_DQ = 1; // auf Ausgang setzen |
81 | PORT_DS18S20_DQ = 1; // high |
82 | return cnt_wait; |
83 | }
|
84 | |
85 | |
86 | void putc1wire( uint8_t c ) |
87 | {
|
88 | for( uint8_t iBit=0; iBit<8; ++iBit ) |
89 | {
|
90 | if( c&1 ) |
91 | {
|
92 | // 1 schreiben
|
93 | // Leitung auf low, gleich wieder (maximal in 15 usec, aber
|
94 | // warum nicht gleich?) wieder auf high und 60 usec warten:
|
95 | PORT_DS18S20_DQ = 0; |
96 | _delay_us( 1 ); |
97 | PORT_DS18S20_DQ = 1; |
98 | _delay_us( 60 ); |
99 | }
|
100 | else
|
101 | {
|
102 | // 0 schreiben
|
103 | // Leitung auf low, 60 usec warten, dann wieder auf high:
|
104 | PORT_DS18S20_DQ = 0; |
105 | _delay_us( 60 ); |
106 | PORT_DS18S20_DQ = 1; // high |
107 | }
|
108 | _delay_us( 1 ); // Sicherheitsabstand zwischen zwei Bits |
109 | c >>= 1; |
110 | }
|
111 | }
|
112 | |
113 | |
114 | // liest ein Byte
|
115 | uint8_t getc1wire() |
116 | {
|
117 | uint8_t ret = 0; |
118 | for( uint8_t iBit=0; iBit<8; ++iBit ) |
119 | {
|
120 | ret >>= 1; |
121 | |
122 | // Leitung auf low, gleich wieder (nach min. 1 usec) wieder auf
|
123 | // high und innerhalb der nächsten 15 usec lesen:
|
124 | PORT_DS18S20_DQ = 0; |
125 | _delay_us( 1 ); |
126 | PORT_DS18S20_DQ = 1; |
127 | DDR_DS18S20_DQ = 0; |
128 | _delay_us( 10 ); |
129 | if( PIN_DS18S20_DQ ) |
130 | {
|
131 | // 1 gelesen
|
132 | ret |= 0b10000000; |
133 | }
|
134 | // den Rest auf 60 usec warten:
|
135 | _delay_us( 60 ); |
136 | DDR_DS18S20_DQ = 1; // Ausgang |
137 | |
138 | _delay_us( 1 ); // Sicherheitsabstand zwischen zwei Bits |
139 | }
|
140 | return ret; |
141 | }
|
142 | |
143 | |
144 | // liest ein Bit
|
145 | uint8_t getb1wire() |
146 | {
|
147 | uint8_t ret = 0; |
148 | // Leitung auf low, gleich wieder (nach min. 1 usec) wieder auf
|
149 | // high und innerhalb der nächsten 15 usec lesen:
|
150 | PORT_DS18S20_DQ = 0; |
151 | _delay_us( 1 ); |
152 | PORT_DS18S20_DQ = 1; |
153 | DDR_DS18S20_DQ = 0; // Eingang |
154 | _delay_us( 10 ); |
155 | if( PIN_DS18S20_DQ ) |
156 | {
|
157 | // 1 gelesen
|
158 | ret |= 0b1; |
159 | }
|
160 | // den Rest auf 60 usec warten:
|
161 | _delay_us( 60 ); |
162 | DDR_DS18S20_DQ = 1; // Ausgang |
163 | |
164 | _delay_us( 1 ); // Sicherheitsabstand zwischen zwei Bits |
165 | return ret; |
166 | }
|
167 | |
168 | |
169 | |
170 | int main( int nargs, char **args ) |
171 | {
|
172 | |
173 | // Pin DQ erstmal als Ausgang und high setzen:
|
174 | DDR_DS18S20_DQ = 1; |
175 | PORT_DS18S20_DQ = 1; |
176 | |
177 | DDR_DS18S20_VCC = 1; // VCC für DS18S20 ausschalten |
178 | PORT_DS18S20_VCC = 0; |
179 | |
180 | reset1wire(); |
181 | |
182 | |
183 | while( 1 ) |
184 | {
|
185 | uint8_t t_ist; // IST-Temperatur [grad C] |
186 | |
187 | reset1wire(); |
188 | // Initialization:
|
189 | PORT_DS18S20_VCC = 1; // VCC einschalten |
190 | // 1. reset pulse (mind. 480 usec low)
|
191 | // presence pulse ist rum
|
192 | |
193 | // ROM Command
|
194 | // READ ROM (0x33) kann nur verwendet werden, wenn es nur einen
|
195 | // slave gibt; danach könnte man das ROM lesen.
|
196 | // SKIP ROM (0xCC) kann nur verwendet werden, wenn es nur einen
|
197 | // slave gibt.
|
198 | //putc1wire( 0x33 );
|
199 | putc1wire( 0xCC ); |
200 | |
201 | // Function Command
|
202 | // convert command
|
203 | putc1wire( 0x44 ); |
204 | |
205 | // warten, bis ein 1-Bit gelesen wird:
|
206 | while( !getb1wire() ) |
207 | {
|
208 | _delay_us( 1 ); |
209 | // TODO: notfalls abbrechen!
|
210 | }
|
211 | |
212 | // jetzt wieder Reset und Daten lesen:
|
213 | reset1wire(); |
214 | putc1wire( 0xCC ); // SKIP ROM |
215 | putc1wire( 0xBE ); // READ SCRATCHPAD |
216 | |
217 | uint8_t data1wire[9]; |
218 | for( int iData=0; iData<9; ++iData ) |
219 | {
|
220 | data1wire[iData] = getc1wire(); |
221 | }
|
222 | PORT_DS18S20_VCC = 0; // VCC ausschalten |
223 | |
224 | // Temperatur in Grad C
|
225 | t_ist = ( ( (int)data1wire[1] << 8 ) | data1wire[0] )/2; |
226 | _delay_ms( 1000 ); |
227 | }
|
228 | |
229 | return 0; |
230 | }
|
Das hat als C++ vor etwa einem halben Jahr auf einem Mega8 funktioniert und ist hier auf das DS18S20-Relevante eingedampft. Es ist so als C kompilierbar, getestet habe ich es aber nicht mehr weiter.
So, erstmal vielen Dank soweit! Werde mal schauen, ob der Code läuft. Der Witz ist, ich habe keine Interrupts mehr an und der DS1820 ist mit VCC und GND verbunden (also kein Parasiten-Power :-) ) Habe es auch schon mit "_delay_us( 480 );"... versucht, hat aber irgendwie nicht geklappt. Evtl. steckt der Teufel ja irgendwo im Detail. Werde es testen und ggf. nochmal SOS rufen! Gruß Erik
Erik schrieb: > Habe es auch schon mit "_delay_us( 480 );"... versucht, hat aber > irgendwie nicht geklappt. Dann stimmt Deine F_CPU Definition nicht mit der tatsächlichen AVR-Frequenz überein. Der AVR-GCC kann ja nicht riechen, welche Fuses und Prescaler Du gesetzt hast und welcher Quarz angeschlossen ist. Du mußt es ihm schon sagen. Du solltest auch die Compiler-Warnungen lesen. Peter
@Peter Dannegger Hey, hey, selbstverständlich stimmt das ;-) F_CPU=8000000, CLK: 8Mhz, intern, CKDIV ist nicht gesetzt (also keine Teilung) => alles richtig => keine Warnung Hatte ich nur vergessen zu schreiben, sorry. Gruß Erik
Also ich hab auch mal den DS1820 benutzt und der ist eigentlich wirklich recht tolerant bei den Zeiten. Bei mir lag es damals an was ganz anderem: ich hatte auch nur einen Sensor dran und dachte darum, ich müsse mich beim Auslesen auch um keine IDs kümmern, und so. Aber: dann muß man das dem Sensor auch sagen! Ich hab den Befehl nicht mehr im Kopf, aber man kann was senden, was dann den Abgleich mit der Sensor-ID überspringt. Als das drin war hat es super geklappt, lag also an meinem Protokoll... Sicher, daß das bei dir richtig ist? Gruß
Hallo nochmals...
Danke für die vielen Rückmeldungen!
> Bei mir lag es damals an was ganz anderem
Unglaublich aber wahr: Alle drei (!) Sensoren, die ich hatte, waren
defekt. Sie haben zwar das Kommando zur Temp.-Messung angenommen, dann
aber immer den Default von 85°C zurück gemeldet.
Fazit: Neuer Sensor angeschlossen und alles rennt!
Der Lerneffekt: Ich habe jetzt ca. 8 Arten griffbereit um Timings zu
erzeugen. ( Und es geht mit allen :-) )
...Murphy lässt grüßen...
Danke an alle & Gruß
Erik
Erik schrieb: > Unglaublich aber wahr: Alle drei (!) Sensoren, die ich hatte, waren > defekt. Sie haben zwar das Kommando zur Temp.-Messung angenommen, dann > aber immer den Default von 85°C zurück gemeldet. Die müssen aber nicht kaputt sein. Warscheinlich sinds nur DS1820-PAR. Da ist VCC nicht nach außen geführt, sondern intern mit GND verbunden. D.h. ohne parasite Power liefern die immer 85°C Parasite Power geht aber recht einfach: Direkt nach dem Start Conversion Kommando den Pin auf high Output setzen für ~1s. Guck mal, ob hinter der 20 nicht noch ein P steht. Peter
@Peter > ...sondern intern mit GND verbunden. Da ich aber VCC mit 5V versorgt habe, hätte es doch schön rauchen müssen. Der Stromfluss war aber nicht hoch. > Guck mal, ob hinter der 20 nicht noch ein P steht. Nein, kein "P". Also wohl doch kaputt. Denke jetzt aber darüber nach die neuen DS1820 mit parasite Power zu betreiben. So kann ich eine weitere Leitung einsparen. Danke & Gruß Erik
Was Delays angeht: Bei mir läuft Peters Code u.A. leicht variiert unter einem RTOS, bei dem nur die kurzen Delays bis zum unteren 2-stelligen µs-Bereich per Warteschleife mit abgeschalteten Interrupts abgesichert sind und der Rest über das RTOS-Scheduling mit dessen 10KHz Timer-Tick abgewickelt wird. Das einzige Timing-Problem ergab sich durch einen kleinen Fehler in der Originalversion, der bei höherer Taktfrequenz zu einer zu kurzen Erholzeit zwischen den Bits führte. Die nicht ausreichte, um eine lange Leitung passiv wieder sauber auf 1 zu ziehen (im zuständigen Thread dokumentiert). An dieser Stelle fehlte ein Delay von ein paar µs.
Erik schrieb: > Da ich aber VCC mit 5V versorgt habe, hätte es doch schön rauchen > müssen. Nö, beim -PAR ist der Pin ja unbeschaltet. Du kannst also nichtmal die Substratdiode messen. Peter
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.