Ds18B20 Zeigt zu höhe Werte

#6802508
Lesenswert?

Ich habe einen mega328p mit einem DS18B20 Programmiert, die 
Temperaturausgabe geschieht über ein LCD Display. Anfangs habe ich es 
nicht hinbekommen, dass die Temperatur ausgegeben wird, der Sensor hat 
aber reale Werte geliefert, ca. 0x16B. Nun habe ich es hinbekommen, dass 
die Temperatur ausgegeben wird, aber nun gibt der Sensor mir einen Welt 
von 0x6D2 also über 100 Grad. Wo liegt der Fehler?
DS.c Datei
1
int ds_readtemp(){  
2
  ds_masterreset();
3
  ds_command(DS_SKIPROMCOMMAND);
4
  ds_command(DS_CONVERTTEMP);
5
  _delay_ms(DS_WAIT_MS);
6
  ds_masterreset();
7
  ds_command(DS_SKIPROMCOMMAND);
8
  ds_command(DS_READSCRATCH);
9
  uint8_t temp1 = ds_readbyte();
10
  uint8_t temp2 = ds_readbyte();
11
  int out = ((temp2) * 256) + temp1;
12
  return out;
13
}
14
int ds_readbit(){
15
  ds_out();
16
  DS_PORT &= ~(1 << DS_PIN);
17
  ds_in();
18
  _delay_us(1);
19
  if (PINC & (1 << DS_PIN)){
20
    return 1;
21
  }
22
  return 0;
23
} 
24
int ds_readbyte(){
25
  int i;
26
  int i_byte = 0;
27
  for (i = 0; i < 8; i++){
28
    i_byte += (ds_readbit() << i);
29
  }
30
  return i_byte;
31
}
hier habe ich eigentlich nur die letzten 2 Zeilen in ds_readtemp 
geändert, diese haben aber mit dem auslesen an sich eigentlich nichts 
zutun.
#6802941
Lesenswert?

Ich habe mal mein komplettes Projekt hier hoch geladen. es läuft Aktuell 
und zeigt die Temperatur an. Nicht über die F_CPU und die Delaywerte 
wundern, diese sind alle Kalibriert, klar wären Korrekte werte besser, 
habe aber irgendwelche Probleme mit den Fuses. Hier sind meine Fuses 
E:07, H:D9, L:57
Angehängte Dateien:
#6802957
Lesenswert?

Dennis T. schrieb:
> Ich habe mal mein komplettes Projekt hier hoch geladen. es läuft Aktuell
> und zeigt die Temperatur an. Nicht über die F_CPU und die Delaywerte
> wundern, diese sind alle Kalibriert, klar wären Korrekte werte besser,
> habe aber irgendwelche Probleme mit den Fuses. Hier sind meine Fuses
> E:07, H:D9, L:57

Naja, da gibt es "einiges" an Verbesserungspotential.
1
void ds_command(uint8_t command){
2
  int i;
3
  for (i = 0; i < 8; i++){
4
    ((command >> i) % 2) == 0 ? ds_writezero() : ds_writeone();
5
  }
6
}

Was soll das? Erstens ist eine variable Verschiebung  >> i relativ 
aufwändig und 2. sinnlos. Ein Modulo 2 ist es umso mehr. Akademischer 
und weltfremder geht es kaum. Das Ganze garniert mit dem ? Operator, den 
man so eher sparsam nutzen sollte. Ein schnödes if() tut es auch und ist 
besser lesbar.

Solche Sachen man man besser mit einer Maske und Bitmanipulation. 
Deutlich einfacher lesbar und deutlich performanter.

Deine Funktionen void ds_writezero(), void ds_writeone() ds_out() und 
ds_in() sind auch vollkommen akademisch aufgeblasen.

>Nicht über die F_CPU und die Delaywerte
>wundern, diese sind alle Kalibriert, klar wären Korrekte werte besser,
>habe aber irgendwelche Probleme mit den Fuses. Hier sind meine Fuses
>:07, H:D9, L:57

Dann löse zuerst diese Probleme, denn sonst wird dein Chaos noch größer!
Schreib ein Programm mit einem definierten Puls, z.B. 1ms, schau ihn dir 
mit dem Oszi oder Logikanaylsator an und stell deinen Fuses bzw. den 
RC-Oszillator so ein, daß er ausreichend genau 1ms erzeugt.
Hast du die Optimierung beim Compiler eingeschaltet? Ohne diese 
funktionieren diese Funktionen nicht!
#6802966
Lesenswert?

1
 for (i = 0; i < 8; i++){
2
    if((command >> i) % 2) == 0){
3
      ds_writezero();
4
    }else{
5
      ds_writeone();
6
    }
7
  }
und wie kann ich nun eine Maske und Bitmanipulation anwenden? habe nicht 
viel C Erfahrung, dazu kommt, dass die Digitaltechnik noch sehr neu ist. 
Es würde auch guter Lesestoff, oder Videos helfen. Dieses Thema schweift 
ja nun eigentlich sehr vom Topic ab :)

Danke euch übrigens sehr für eure Hilfe :)
Gast #6802977
Lesenswert?

Falk hat Recht. Zumindest bei AVR Mikrocontrollern sind Shift 
Operationen mit einer Variablen Anzahl von Schritten ziemlich 
ineffizient.

Vorher:
1
void ds_command(uint8_t command){
2
  int i;
3
  for (i = 0; i < 8; i++){
4
    ((command >> i) % 2) == 0 ? ds_writezero() : ds_writeone();
5
  }
6
}

Verbesserung:
1
void ds_command(uint8_t command){
2
  int i;
3
  for (i = 0; i < 8; i++){
4
    if (command & 1) {
5
      ds_writeone();
6
    }
7
    else {
8
      ds_writezero();
9
    }
10
    command = command >> 1;
11
  }
12
}
#6802979
Lesenswert?

Dennis T. schrieb:
> und wie kann ich nun eine Maske und Bitmanipulation anwenden? habe nicht
> viel C Erfahrung, dazu kommt, dass die Digitaltechnik noch sehr neu ist.
> Es würde auch guter Lesestoff,

Ja, siehe Bitmanipulation bzw. mein Projekt.

https://www.mikrocontroller.net/topic/goto_post/4890827

> oder Videos helfen.

Nö. Du bist ein Millenial, welcher glaubt, die Welt kann vor allem mit 
"Tutorials" und Youtube-Videos erklärt werden. Dem ist nicht so. In 
vielen Fällen sind einfache Texte, sei es auf Papier oder elektronisch 
einem Video überlegen.

> Danke euch übrigens sehr für eure Hilfe :)

Was hast du gelernt? Wo lag der Fehler? Wie hast du ihn erkannt? Wie 
hast du ihn behoben?
#6802981
Lesenswert?

>for (i = 0; i < 8; i++){
>    if((command >> i) % 2) == 0){
>      ds_writezero();
>    }else{
>      ds_writeone();
>    }
>  }
Das geht eleganter (mask ist zugleich Bitmaske und Zähler):
1
mask = 0x80;do{
2
  if(command & mask) ds_writeone(); else ds:writezero();}
3
  while(mask/=2);
4

5
oder gleich:
6
mask = 0x80; do ds_write(command & mask) while(mask/=2);

> Es würde auch guter Lesestoff, oder Videos helfen.
Das Internet enthält genug gute Beispiele, aus denen man viel lernen 
kann.
Die Kunst ist, die Qualität der Beiträge zu bestimmen.
#6802985
Lesenswert?

Vorn N. schrieb:
> Das geht eleganter (mask ist zugleich Bitmaske und Zähler):mask =
> 0x80;do{
>   if(command & mask) ds_writeone(); else ds:writezero();}
>   while(mask/=2);
> oder gleich:
> mask = 0x80; do ds_write(command & mask) while(mask/=2);

FAIL! OneWire sendet LSB first ;-)
Außerdem ist eine einfache UND-Verknüpfung und Auswertung kaum 
optimierbar, denn da kommt auf den meisten CPUs nur 1 ASM Befehl zum 
Bittesten raus.

"Ich bin dafür, die Dinge so weit wie möglich zu vereinfachen, aber 
nicht weiter."

Albert Einstein
Gast #6802986
Lesenswert?

Stefan ⛄ F. schrieb:
> if (command & 1)

Das ist die Maske, die Falk meint. Durch die UND-Verknüpfung wird nur 
das niedrigste Bit verwendet. Und wenn das 1 ist, dann wird der if-Block 
ausgeführt.

(Alle Ausdrücke die nicht 0 ergeben sind wahr)

> command = command >> 1;

Hier wird das Kommando bei jedem Schleifendurchlauf um eine Position 
nach rechts verschoben. Deswegen brauchen wir wir immer nur das gleiche 
Bit 0 mit if abzufragen. Die Verschiebung um 1 führt der Mikrocontroller 
besonders schnell aus, nämlich in nur einem einzigen Takt.
1
    if (command & 1) {
2
      ds_writeone();
3
    }
4
    else {
5
      ds_writezero();
6
    }

Alternative Schreibweise mit dem ternären Operator:
1
    (command & 1) ? ds_writeone() : ds_writezero();

Gefällt mir persönlich weniger gut, ist auch nicht effizienter.
#6803031
Lesenswert?

1
/*
2
 * DS18B20.c
3
 *
4
 * Created: 27.07.2021 21:12:53
5
 *  Author: User
6
 */ 
7

8
#include <avr/io.h>
9
#include "../header/DS18B20.h"
10
#include <util/delay.h>
11

12
void ds_out(){
13
  DS_DDR |= (1 << DS_PIN);
14
}
15
void ds_in(){
16
  DS_DDR &= ~(1 << DS_PIN);
17
}
18

19
void ds_masterreset(){
20
  ds_out();
21
  DS_PORT &= ~(1 << DS_PIN);
22
  _delay_us(DS_MASTERRESET_US);
23
  ds_in();
24
  _delay_us(DS_WAIT_US);
25
}
26
void ds_command(uint8_t command){
27
  int i;
28
  
29
  for (i = 0; i < 8; i++){
30
    if (command & 1) {
31
      ds_writeone();
32
    }
33
    else {
34
      ds_writezero();
35
    }
36
    command = command >> 1;
37
  }
38
}
39
void ds_writezero(){
40
  ds_out();
41
  DS_PORT &= ~(1 << DS_PIN);
42
  _delay_us(DS_WRITEZERO_US);
43
  ds_in();
44
}
45
void ds_writeone(){
46
  ds_out();
47
  DS_PORT &= ~(1 << DS_PIN);
48
  ds_in();  
49
  _delay_us(DS_WRITEONE_US);
50
}
51
int ds_check(){
52
  ds_masterreset();
53
  ds_command(DS_SKIPROMCOMMAND);
54
  ds_command(DS_READSCRATCH);
55
  uint8_t temp1 = ds_readbyte();
56
  uint8_t temp2 = ds_readbyte();
57
  int out = ((temp2) * 256) + temp1;
58
  return out;
59
}
60
int ds_readtemp(){  
61
  ds_masterreset();
62
  ds_command(DS_SKIPROMCOMMAND);
63
  ds_command(DS_CONVERTTEMP);
64
  _delay_ms(DS_WAIT_MS);
65
  ds_masterreset();
66
  ds_command(DS_SKIPROMCOMMAND);
67
  ds_command(DS_READSCRATCH);
68
  uint8_t temp1 = ds_readbyte();
69
  uint8_t temp2 = ds_readbyte();
70
  int out = ((temp2) * 256) + temp1;
71
  return out;
72
}
73
int ds_readbit(){
74
  ds_out();
75
  DS_PORT &= ~(1 << DS_PIN);
76
  ds_in();
77
  _delay_us(30);
78
  if (PINC & (1 << DS_PIN)){
79
    return 1;
80
  }
81
  return 0;
82
} 
83
int ds_readbyte(){
84
  int i;
85
  int i_byte = 0;
86
  for (i = 0; i < 8; i++){
87
    i_byte += (ds_readbit() << i);
88
  }
89
  return i_byte;
90
}
Ich hab nun ein wenig bearbeitet, setze den internen Pullup nicht mehr 
auf high, sondern stelle den Port wieder auf Eingang. Das Timing passt 
nun auch, allerdings bei
#define F_CPU 2000000L obwohl ich ein 16mhz Quarz habe?
Zu der Frage woran der Fehler lag kann ich nichts sagen, denn ich habe 
zu der Zeit nur Code auskommentiert, hochgeladen und dann wieder zurück, 
ging dann irgendwie obwohl nichts anders war als vorher auch.
Gast #6803177
Lesenswert?

1
(command & 1) ? ds_writeone() : ds_writezero();

Joachim B. schrieb:
> da Monitore eher breit als hoch sind habe ich dem
> ? Einzeiler mehr sichtbaren Quellcode, deswegen nutze ich den gerne.

Das:
1
if (command & 1) ds_writeone() else ds_writezero();

ist nicht verboten. Nur mal so als alternativen Vorschlag.

Ich benutze den ternären Operator gerne, wenn er einen Wert zurück 
liefert, wie in:
1
puts( (httpStatus>=400) ? "error" : "success" );
#6803441
Lesenswert?

Hm, der DS gibt ein Presentpulse, aber ich kann ihn nicht mehr auslesen.
1
void ds_readscratch(){
2
  ds_masterreset();
3
  ds_command(DS_SKIPROMCOMMAND);
4
  ds_command(DS_READSCRATCH);
5
  for(int i = 0; i < 9; i++){
6
    scratch[i] = ds_readbyte();
7
  }
8
}
9

10
void ds_converttemp(){  
11
  ds_masterreset();
12
  ds_command(DS_SKIPROMCOMMAND);
13
  ds_command(DS_CONVERTTEMP);
14
  _delay_ms(DS_WAIT_MS);
15
}
16

17
int ds_readbit(){
18
  ds_out();
19
  DS_PORT &= ~(1 << DS_PIN);
20
  ds_in();
21
  if (PINC & (1 << DS_PIN)){
22
    _delay_us(60);
23
    return 1;
24
  }
25
  _delay_us(60);
26
  return 0;
27
} 
28
uint8_t ds_readbyte(){
29
  uint8_t i_byte = 0;
30
  for (int i = 0; i <  8; i++){
31
    i_byte |= ds_readbit();
32
    i_byte =  i_byte << 1;
33
  }
34
  return i_byte;
35
}
#6803458
Lesenswert?

Dennis T. schrieb:
> Hm, der DS gibt ein Presentpulse, aber ich kann ihn nicht mehr auslesen.

Dein Quick & Dirty Hack ist Mist. Nicht rumstochern und "probieren", 
sondern ma nachdenken und auch MESSEN! Dein Lesen des Bits funktioniert 
so nicht. Man sollte schon die Zeiten einhalten. Und lass die Finger von 
dem PORT-Bit, das muss nur EINMAL auf 0 gesetzt werden, das macht 
eigentlich schon der Reset. Man muss nur die Richtung Eingang/Ausgang 
umschalten, denn es ist ein Open Drain Signal.

https://www.mikrocontroller.net/articles/Ausgangsstufen_Logik-ICs#Open_Collector
1
uint8_t ds_readbit(){
2
  uint8_t tmp=0;
3

4
  DS_DDR |= (1 << DS_PIN);   // LOW
5
  _delay_us(5);
6
  DS_DDR &= ~(1 << DS_PIN);  // Eingang/Tristate
7
  _delay_us(15);             // warten auf Reaktion
8
  if (PINC & (1 << DS_PIN)){ // lesen
9
    tmp = 1;
10
  }
11
  _delay_us(60);             // volle Bitzeit
12
  return tmp;
13
}
#6803462
Lesenswert?

Dennis T. schrieb:
> uint8_t ds_readbyte(){
>   uint8_t i_byte = 0;
>   for (int i = 0; i <  8; i++){
>     i_byte |= ds_readbit();
>     i_byte =  i_byte << 1;
>   }
>   return i_byte;
> }

Und du hast es immer noch nicht verstanden, daß man das sinnvollerweise 
anders macht? Außerdem schiebst du falsch! OneWire überträgt LSB first, 
du machst aber aus dem 1. Bit das MSB durch dein nach links schieben! 
Außerdem schiebst du NACH der letzten Bitspeicherung, womit die Daten 
erst recht im Eimer sind! Eher so!
1
uint8_t ds_readbyte(){
2
  uint8_t i_byte = 0;
3

4
  for (int i = 0; i <  8; i++) {
5
    i_byte >>= 1;   // Platz schaffen für nächstes Bit ganz links
6
    if (ds_readbit()) i_byte |= (1<<7);
7
  }
8
  return i_byte;
9
}
#6803528
Lesenswert?

Falk B. schrieb:
> Dein Lesen des Bits funktioniert
> so nicht. Man sollte schon die Zeiten einhalten. Und lass die Finger von
> dem PORT-Bit, das muss nur EINMAL auf 0 gesetzt werden, das macht
> eigentlich schon der Reset

Das mit den Zeiten habe ich auch schon geregelt, habe ins Datenblatt und 
im Logic geschaut, dann fiel mir ein dass ich falsches Timing habe, 
würde vorher von geredet, nur vergessen umzusetzen.
Das einlesen habe ich nun auch geregelt, danke für eure Geduld :D mein 
Problem ist wirklich dass ich mehrere Fehler habe welche ich behebe, 
dadurch kommt anderes unter, ich aber selber auch suche, dadurch lerne 
ich es aber auch.
Ihr seit mir aber auch eine große Hilfe bin euch da sehr Dankbar :)

Das Thema des Topics ist nun eigentlich erfüllt, es läuft wie es soll.
alles weitere ist somit eigentlich nur noch Optimierung
Folglich Poste ich noch einmal die momentan endgültige Version.

Dann geht es erstmal zum nächsten Sensor dem DHT22 mal sehen was hier 
auf mich wartet :)

Nochmals großen Dank für die große Hilfsbereitschaft.
Angehängte Dateien:
Beitrag #6803529 wurde von einem Moderator gelöscht.
Gast #6804001
Lesenswert?

Falk B. schrieb:
> Was soll das? Erstens ist eine variable Verschiebung  >> i relativ
> aufwändig und 2. sinnlos.

Falk B. schrieb:
> Deine Funktionen void ds_writezero(), void ds_writeone() ds_out() und
> ds_in() sind auch vollkommen akademisch aufgeblasen.

Falk B. schrieb:
> uint8_t ds_readbyte(){
>   uint8_t i_byte = 0;
>   for (int i = 0; i <  8; i++) {
>     i_byte >>= 1;   // Platz schaffen für nächstes Bit ganz links
>     if (ds_readbit()) i_byte |= (1<<7);
>   }
>   return i_byte;
> }

Da predigt er von ganz weit oben (wo übrigens ziemlich wenig Sauerstoff 
ist...) einen runter, erwähnt Effizienz, zitiert sogar Onkel Albert usw. 
und kommt dann mit einer Schleife um die Ecke, die in jedem Durchlauf 
7 unnötige Shifts macht???
Gast #6804018
Lesenswert?

Lutz schrieb:
> und kommt dann mit einer Schleife um die Ecke, die in jedem Durchlauf
> 7 unnötige Shifts macht???

Jetzt bin ich aber sehr gespannt, wie du die 8 Bits ohne 8 
Shift-Operationen in ein Byte einliest.

An alle: Achtung, ich bitte um Aufmerksamkeit. Der Lutz zeigt uns allen 
jetzt einen genialen Trick den jeder Programmierer drauf haben sollte. 
Trommelwirbel bitte: taraa taraa taratatatataa
Gast #6804087
Lesenswert?

Stefan ⛄ F. schrieb:
> Lutz schrieb:
>> und kommt dann mit einer Schleife um die Ecke, die in jedem Durchlauf
>> 7 unnötige Shifts macht???
>
> Jetzt bin ich aber sehr gespannt, wie du die 8 Bits ohne 8
> Shift-Operationen in ein Byte einliest.

Oh man, mal wieder der Beweis, daß es der Original-Stefan (nicht 
gehackter Account) ist...

Ich schrieb etwas von 7, du etwas von 8. Komm; das schaffst du im 
zweiten Versuch...
Ich muß jetzt leider mit dem Kochen anfangen und kann dich daher erstmal 
nicht weiter beaufsichtigen.
Gast #6804296
Lesenswert?

Lutz schrieb:
> Nun gut, Kommando zurück: 1<<7 ist ja eine Konstante zum verODERn.
> Das wird ja nicht zur Laufzeit geshiftet.

Na also, geht doch. Ich hoffe du schämst dich jetzt ein bisschen für 
deine persönlichen Angriffe gegen Falk und mich.

Lutz schrieb:
> Ich schrieb etwas von 7, du etwas von 8.

Aus gutem Grund, denn die Beispiele von Falk und mir nutzen 8 
Schiebe-Operationen (einer mehr als nötig, OK). Du hast geschrieben, 
dass davon 7 Shifts unnötig seien. Man hätte demnach also alle 8 Bits 
mit nur einer Shift Operation einlesen müssen.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren