Forum: Compiler & IDEs DS18B20 Code funktioniert nicht


von Alibi (Gast)


Angehängte Dateien:

Lesenswert?

Hallo Leute,

ich habe nach einem One Wire Dokumentation/Tutorial diesen Code hier für 
einen ATMega portiert bzw. geschrieben. Allerdings funktioniert aus mir 
unerfindlichen Gründen das Auslesen nicht.
Ich erhalte beim Auslesen statt der richtigen Temperatur nur "lauter 
Einsen" (= Temperatur von 255).

Die "Bibliothek" ist eigentlich sehr minimal und von der Funktion habe 
ich sie auch soweit mit der von Herrn Danegger verglichen, wobei mir 
nichts die Funktion Beeinträchtigendes auffiel.

Deswegen bitte ich falls möglich um eine kurze Analyse des Code und 
vielleicht den Test bei euch, falls Hardware vorhanden.

Vielen Dank, es grüßt

von Mike (Gast)


Lesenswert?

Alibi schrieb:
> ... vielleicht den Test bei euch, falls Hardware vorhanden.

Dafür müßte man wissen, welche Hardware, insbesondere auf welchem 
Prozessor das laufen soll und mit welcher Taktfrequenz der µC betrieben 
wird (int/ext, Wert, Einstellungen der Fuses).

von Tibi (Gast)


Lesenswert?

Mike schrieb:
> Alibi schrieb:
>> ... vielleicht den Test bei euch, falls Hardware vorhanden.
>
> Dafür müßte man wissen, welche Hardware, insbesondere auf welchem
> Prozessor das laufen soll und mit welcher Taktfrequenz der µC betrieben
> wird (int/ext, Wert, Einstellungen der Fuses).

Hallo Mike,

ich probierte die Lib erfolglos auf verschiedenen Prozessoren, 
hauptsächlich aber dem ATMega8A, 16 MHz, Fuses bspw. hfuse = C9, lfuse = 
FF.

von (prx) A. K. (prx)


Lesenswert?

Dein delay_us ist sehr ungenau, sofern es nicht inlined wird. Die 
Schleife braucht für sich selbst mehrere Takte, was bei z.B. 16 Takten 
pro Mikrosekunde nicht vernachlässigbar ist.

Wird der Sensor parasitär versorgt? Wenn ja, dann verhungert er beim 
Messvorgang.

: Bearbeitet durch User
von Tibi (Gast)


Lesenswert?

A. K. schrieb:
> Dein delay_us ist sehr ungenau, sofern es nicht inlined wird. Die
> Schleife braucht für sich selbst mehrere Takte, was bei z.B. 16 Takten
> pro Mikrosekunde nicht vernachlässigbar ist.
>
> Wird der Sensor parasitär versorgt? Wenn ja, dann verhungert er beim
> Messvorgang.

Nein, der Sensor wird über eine eigene Leitung mit Spannung versorgt. 
Natürlich mit 4,7k Pullup.

von Tibi (Gast)


Lesenswert?

Ist das bei in dieser Weise konstruierten Delay Funktionen immer so?

von (prx) A. K. (prx)


Lesenswert?

Tibi schrieb:
> Ist das bei in dieser Weise konstruierten Delay Funktionen immer so?

Du musst den Aufwands der Schleife im Verhältnis zum festen Delay 
beachten. Bei _delay_us(10) und damit 160 Takten bei 16MHz fällt der 
Aufwand der Schleife kaum noch ins Gewicht. Bei _delay_us(1) und 16 
Takten kann es hingegen schon relevant sein, ob da noch einige Takte für 
die Schleife hinzu kommen.

Deine Methode ist eigentlich nur dann angebracht, wenn das Delay nicht 
konstant ist. Ist es hier aber durchgängig.

Ein paar Kleinigkeiten:

Ich würde empfehlen, ständig wiederholten Kram wie
  PORTB &= ~(1 << T_PIN);
in Makros wie beispielsweise
  OW_LOW()
zu verbuddeln. Liest sich besser.

Du hast zwar den Reset von Interrupts geschützt, der mit seiner halben 
Millisekunde nicht so empfindlich ist, nicht aber den dabei sehr 
empfindlichen Bitzyklus.

Spätestens wenn du die CRC kontrollieren willst, was ich nahelegen 
würde, wird das mit der 16-Bit Lesefunktion etwas unhandlich.

: Bearbeitet durch User
von Oliver (Gast)


Lesenswert?

Vor allem ist es völlig sinnlos, sich solche Schleifen für konstante 
delays selber zu basteln, Die Bibliotheksfunktionen aus delay.h können 
das sehr viel genauer und besser.

Die Schleifenkonstruktion braucht es nur bei nicht-konstanten delays.

Oliver

von Tibi (Gast)


Lesenswert?

Vielen Dank, werde die Vorschläge ergänzen.

Bzgl. des CRC: Zur Berechnung muss ich ja das gesamte Scratchpad 
auslesen, richtig? Sollte ich dafür einfach eine zweite längere Read 
Funktion implementieren? (Theoretisch kann ich ja bei jedem Auslesen das 
CRC berechnen!? D.h. nur eine Read Funktion)

von Karl H. (kbuchegg)


Lesenswert?

Tibi schrieb:
> Vielen Dank, werde die Vorschläge ergänzen.
>

Noch was.

Das hier
1
...
2
  unsigned int temp = ds18b20_read();
3
  if(temp<0x8000)  {    
4
    return temp;
5
  } else{
6
    temp = (~temp)+1;
7
    return temp;
8
  }

braucht es nicht.
Da dir der DS18B20 die 16 Bits schon im 2-er Komplement genau so 
schickt, wie du es im Programm benötigst, ist ein
1
int ds18b20_data() {
2
  ow_reset();
3
  ow_write(DS1820_SKIP_ROM);
4
  ow_write(DS1820_READ_SCRATCHPAD);  
5
  int temp = (int)ds18b20_read();
6
  return temp;
7
}
schon korrekt.
Dann klappts auch mit negativen Temperaturen.
In deinem Code würden negative Temperturen als positiv weiter 
verarbeitet werden.

> Bzgl. des CRC: Zur Berechnung muss ich ja das gesamte Scratchpad
> auslesen, richtig?

müsste man.
Kommt drauf an, wozu du das Ganze überhaupt brauchst.
Wenn es nur um einen Anzeige geht, kann man den CRC Teil auch weg 
lassen. Da es im Keller keine +85°C haben wird, weißt du durch den 
unsinnigen Wert auch so, dass das Ergebnis nicht korrekt sein kann.
Anders sieht es natürlich aus, wenn die Messung irgendeine Automatik mit 
Werten bedient.

von Felix P. (fixxl)


Lesenswert?

Nicht speziell auf diesen Fall bezogen sondern allgemein solltest du 
deine Funktionsdeklarationen in C präzisieren. Wenn in C eine Funktion 
keinen Übergabeparameter empfangen soll, sollte das bei der Deklaration 
in den Klammern hinter dem Funktionsnamen durch ein void klargestellt 
werden, z.B.
1
uint8_t funktion(void)

Die Variante
1
uint8_t funktion()
findet sich oft, kann aber eine Fehlerquelle sein, weil der Compiler 
nicht meckert, wenn man dieser Funktion, die eigentlich keine Parameter 
erhalten soll, auf einmal welche übergibt.

von (prx) A. K. (prx)


Lesenswert?

Tibi schrieb:
> Bzgl. des CRC: Zur Berechnung muss ich ja das gesamte Scratchpad
> auslesen, richtig?

Ja. Und die CRC-Berechnung funktioniert am besten byteweise. Weshalb die 
Lesefunktion üblicherweise byteweise arbeitet.

von Tibi (Gast)


Angehängte Dateien:

Lesenswert?

So, habe mal den Code etwas überarbeitet. Kann ihn leider erst nächstes 
WE in der Schaltung testen, ist er denn so theoretisch funktionsfähig? 
:D

von Karl H. (kbuchegg)


Lesenswert?

ernsthaft:

Lösch die beiden Funktionen
1
void delay_ms(int ms) {
2
  while(ms--) {
3
    _delay_ms(1);
4
  }
5
}
6
7
void delay_us(int us) 
8
...

aus dem Code raus.
Du brauchst sie nicht, weil du bei konstanten Werten auf die _delay_ms 
bzw. _delay_us Funktionen zurückgreifen kannst. Wenn du hingegen 
variable Delays in deinem Code hast, dann machst du grundsätzlich etwas 
falsch.
Derartige Delay Funktionen bringen nur Ärger. In deinem Fall wird das 
Timing, speziell in der delay_us bei weitem nicht stimmen.

Hier
1
unsigned char ow_reset(void)
2
{
3
  unsigned char error;
4
  cli();
5
  PORTB &= ~(1 << T_PIN);
6
  DDRB |= (1 << T_PIN);
7
  delay_us(480);
hast du nämlich vergessen, den Aufruf auszutauschen. Lösch die 
Funktionen raus, dann passiert dir sowas nicht.

: Bearbeitet durch User
von Tibi (Gast)


Lesenswert?

Karl Heinz schrieb:
> ernsthaft:
>
> Lösch die beiden Funktionenvoid delay_ms(int ms) {
>   while(ms--) {
>     _delay_ms(1);
>   }
> }
>
> void delay_us(int us)
> ...
>
> aus dem Code raus.

Werde ich machen, aber ist denn bei _delay_us nicht die maximale delay 
Zeit durch 768 / F_CPU (in MHz) begrenzt? Das heißt, ich würde mehrerer 
Aufrufe aneinanderhängen müssen. Oder geht das einfacher?

von Karl H. (kbuchegg)


Lesenswert?

Tibi schrieb:
> Karl Heinz schrieb:
>> ernsthaft:
>>
>> Lösch die beiden Funktionenvoid delay_ms(int ms) {
>>   while(ms--) {
>>     _delay_ms(1);
>>   }
>> }
>>
>> void delay_us(int us)
>> ...
>>
>> aus dem Code raus.
>
> Werde ich machen, aber ist denn bei _delay_us nicht die maximale delay
> Zeit durch 768 / F_CPU (in MHz) begrenzt?


Das war mal. Im letzten Jahrtausend. Kurz nachdem wir von den Bäumen 
runter gekommen sind.

von Tibi (Gast)


Lesenswert?

Karl Heinz schrieb:
> Tibi schrieb:
> Karl Heinz schrieb:
> ernsthaft:
>
> Lösch die beiden Funktionenvoid delay_ms(int ms) {
>   while(ms--) {
>     _delay_ms(1);
>   }
> }
>
> void delay_us(int us)
> ...
>
> aus dem Code raus.
>
> Werde ich machen, aber ist denn bei _delay_us nicht die maximale delay
> Zeit durch 768 / F_CPU (in MHz) begrenzt?
>
> Das war mal. Im letzten Jahrtausend. Kurz nachdem wir von den Bäumen
> runter gekommen sind.

OK.

Ich implementiere jetzt das byteweise Auslesen und CRC. Beim Verstehen 
habe ich aber noch ein kleines Problem.

Auf mehreren Seiten, Threads etc. findet sich als hex Zahl 0x8C für das 
Maxim Polynom 
(http://www.nongnu.org/avr-libc/user-manual/group__util__crc.html#ga37b2f691ebbd917e36e40b096f78d996) 
obwohl doch 2^8+2^5+2^4+2^0 = 304 = 0x131 ist. Warum?

von Felix P. (fixxl)


Lesenswert?

Das liegt an der Art und Weise, wie die Eingangsdaten bei der CRC von 
Maxim durch die Schieberegister geschleust werden. Die Maxim-CRC8 ist 
eine LSB-first-CRC, daher muss das Polynom gespiegelt werden und aus 
0x31 wird 0x8C.

Der Wert bei x^8 - also im neunten Bit - ist bei einer CRC8 zwingend 
immer 1, deshalb wird er bei der Angabe des Polynoms weggelassen.

Eine sehr anschauliche Beschreibung der Maxim-CRC8 gibt es in 
http://www.maximintegrated.com/app-notes/index.mvp/id/27, eine Funktion 
zur Berechnung namens _crc_ibutton_update (uint8_t __crc, uint8_t 
__data) ist bereits in AVR-GCC hinterlegt. Hierzu muss man 
<util/crc16.h> inkludieren.

von Tibi (Gast)


Angehängte Dateien:

Lesenswert?

Hallo Leute,

ich bins mal wieder. Habe jetzt meine Schaltung noch mal neu auf dem 
Steckbrett und danach auf Lochraster aufgebaut und es will einfach nicht 
funktionieren.

Der ATMega8A, den ich benutze, hat folgende Aufgabe:
- Temperatur vom DS18B20 holen
- Temperatur als Binärzahl auf dem USART ausgeben

Schaltung ist einfach nur Atmega an ISP Programmer (darüber auch VCC), 
16 MHz Quarz und 22pF Kondis, DS18B20 im 3 Pin Modus (nicht parasite 
powered), und ne LED an einem Port.

Das ganze habe ich jetzt mit einem Logic Analyzer überwacht und 
festgestellt, dass sowohl der Reset-Befehl als auch die SKIP_ROM, 
READ_SCRATCHPAD etc Befehle ausgegeben werden und der Temperatursensor 
darauf mit der Ausgabe des Scratchpads beginnt. Bilder habe ich mal 
angehängt.

Jedoch funktioniert das Ausgeben auf dem USART aus mir unerfindlichen 
Gründen immer noch nicht. Ausgabe geschieht ganz einfach durch itoa und 
dann handelsübliche USART Send Funktion. Einzelne Buchstaben sowie 
Stringketten funktionieren tadellos, also liegt es mMn nicht am USART, 
sondern wahrscheinlich irgendwie am Timing des Auslesens durch den µC.

Hat jemand ne Ahnung, was bei dem Atmega schief laufen könnte?

es grüßt
Tibi

von Tibi (Gast)


Lesenswert?

P.S.:
Ausgegeben wurde am USART beim hochgeladenen Scratchpad folgende 
Bitfolge:
1100001000000001
Also einfach Wirrwarr

von (prx) A. K. (prx)


Lesenswert?

Ganz so wirr finde ich den Wert eigentlich nicht. Wenn man bedenkt, dass 
du die Bytes falschrum zusammengesetzt hast. Es also 0x01C2 sind, 28°C 
entsprechend, nicht 0xC201.

: Bearbeitet durch User
von Tibi (Gast)


Lesenswert?

...
Funktioniert!
Danke ;)

Manchmal sieht man halt den Wald vor lauter ...

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
Noch kein Account? Hier anmelden.