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.
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
typedefunion
6
{
7
uint8_tbyte;
8
struct
9
{
10
unsignedintbit0:1;
11
unsignedintbit1:1;
12
unsignedintbit2:1;
13
unsignedintbit3:1;
14
unsignedintbit4:1;
15
unsignedintbit5:1;
16
unsignedintbit6:1;
17
unsignedintbit7: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_treset1wire()
60
{
61
intcnt_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
returncnt_wait;
83
}
84
85
86
voidputc1wire(uint8_tc)
87
{
88
for(uint8_tiBit=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_tgetc1wire()
116
{
117
uint8_tret=0;
118
for(uint8_tiBit=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
returnret;
141
}
142
143
144
// liest ein Bit
145
uint8_tgetb1wire()
146
{
147
uint8_tret=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
returnret;
166
}
167
168
169
170
intmain(intnargs,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_tt_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_tdata1wire[9];
218
for(intiData=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
return0;
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