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
intds_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_ttemp1=ds_readbyte();
10
uint8_ttemp2=ds_readbyte();
11
intout=((temp2)*256)+temp1;
12
returnout;
13
}
14
intds_readbit(){
15
ds_out();
16
DS_PORT&=~(1<<DS_PIN);
17
ds_in();
18
_delay_us(1);
19
if(PINC&(1<<DS_PIN)){
20
return1;
21
}
22
return0;
23
}
24
intds_readbyte(){
25
inti;
26
inti_byte=0;
27
for(i=0;i<8;i++){
28
i_byte+=(ds_readbit()<<i);
29
}
30
returni_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.
Ist hier wohl kein Problem, aber generell zum Umgang mit dem I/O-Pin bei
separat versorgtem Sensor, also nicht parasitär über die Datenleitung:
Bit im Output-Register einmalig auf 0 setzten und dann dabei belassen.
Danach nur noch mit dem Bit in im Direction-Register spielen.
Dennis T. schrieb:> I/O Funktionen sehen so aus.
Ausser bei parasitärer Versorgung darf der Portpin nie auf High
gezogen werden. Das macht der externe Pullup-Widerstand. Der interne
Pullup ist zu hochohmig.
Mit einem LA-Shot ohne Zeitachse kann ich nichts anfangen. Und wenn der
Hersteller 15µs dranschreibt, dann verwendet man auch 15µs. Allerdings
insgesamt, d.h. die unbekannte Laufzeit vom Code ist mit im Spiel.
In dieser Phase ist übrigens ein Oszi besser als ein LA. Erst einmal
sollten Timing und Pegel stimmen. Daten kommen später.
Was liest du, wenn nach Powerup des Sensors keine Messung erfolgt? Da
müssen 85°C rauskommen.
Dennis T. schrieb:> das Auslesen klappt auch bei 1us, aber man sieht auch im Logicanalyzer,> dass der falsche Werte gibt.
Findest du nicht, dass der Satz sich selbst widerspricht?
Die Auslesezeit ist ja in erster Linie nicht wichtig, da ob ich auslese,
oder nicht der DS die Werte überträgt, nun habe ich den internen Pullup
weg gelassen, und der DS sendet keine ganzen Pakete mehr?
Dennis T. schrieb:> Screenshot_2021-08-27_194122.png> Rot sind die Timings des DS
Kannst du vielleicht mal eine ganz normale Zeitachse an das Zeitdiagramm
vom LA packen oder die Daten wenigsten in nutzbarer Form anhängen?
Wer soll sich denn durch den Pixelsalat durchfummeln?
Statt die Leute immer wieder mit unhandlichen und unnötigen Bildern zu
beglücken, könntest du einfach die Aufnahme des LA in einem der dafür
üblichen Formate speichern, und diese Datei mit den Daten hier anhängen.
Da sind dann alle Informationen drin, und jeder kann sie sich so
anschauen, wie er es braucht.
Jack V. schrieb:> Statt die Leute immer wieder mit unhandlichen und unnötigen Bildern zu> beglücken
Was hast du gegen die linke Seite? Etwas zu viel Photoshop vielleicht.
Dennis T. schrieb:> ich eigentlich nur die letzten 2 Zeilen in ds_readtemp geändert
Du hast also das Programm aus einer anderen Quelle geholt, und dort
funktioniert es (angeblich).
Dieselbe Taktfrequenz ?
Auf der einen Seite gibt es DS_PORT, auf der anderen PINC, ist DS_PORT
auch PORTC, oder geändert ? (scheiss Definitionen).
Dennis T. schrieb:> Leider kann ich nicht alle Zeiten anzeigen lassen, sorry bin noch> relativ neu in der Materie.
Und in der Pupertät hängen geblieben ;-)
Dennis T. schrieb:> ne, die Lib habe ich selber erstellt, habe da keine gefunden.
Ja klar, es hat ja auch noch niemals eine Onewire Bibliothek ins
Internet gestellt. OMG!
Beitrag "Re: Onewire + DS18x20 Library"
Profitipp: Wenn man die CRC-Prüfung der Daten macht, erkennt man
Übertragungsfehler.
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
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
voidds_command(uint8_tcommand){
2
inti;
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!
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 :)
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?
>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):
> 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.
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
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.
Vorn N. schrieb:> mask/=2
Wenn man Bits schieben will, sollte man das auch tun, statt per
Multiplikation oder Division um die Ecke zu denken. Zumal man dann je
nach Datentypen und Intelligenz des Compilers Überraschungen erleben
kann, nämlich wenn der Compiler mit Vorzeichen dividiert.
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.
Dennis T. schrieb:> for (i = 0; i < 8; i++){> i_byte += (ds_readbit() << i);> }
Hatten wir nicht gerade Shift Operationen mit variabler Schrittweite
besprochen?
Sehe ich als Verbesserungwürdig, mache ich gleich. Mal ne andere Frage
habe einen usbasp für 10€ nutze atmel Studio und Abtdude. Atmel Studio
läuft aber nur auf windoof, gibt es gute Alternativen?
Dennis T. schrieb:> for (i = 0; i < 8; i++){> i_byte &= ds_readbit();> i_byte = i_byte >> 1;> }
Ich würde mit i_byte=0 anfangen und dann |= anstelle von &= benutzen.
Dennis T. schrieb:> Hier sind meine Fuses E:07, H:D9, L:57
Ist auf 16MHZ gestellt
Stefan ⛄ F. schrieb:> Und nach links schieben! Nach rechts ist falsch.
Ja fiel mir auch nach dem Post ein, dass ich falschherum einlese.
Momentan klagt aber ein anderes Problem, der DS antwortet mit FF.
Fang erstmal ganz unten an. Deine ds_readbit() sind suboptimal. Sie
verstoßen gegen die "Time Slot" Spezifikation des DS18B20, welche
zwischen 60us und 120us liegen sollte. Du wartest nur bis zum
Rückgabewert und brichst dann ab. Im rar 15us, oben 30us. Lass die 15
und füge vor jedem return noch ein delay_us(70) ein.
Falk B. schrieb:> Das Ganze garniert mit dem ? Operator, den> man so eher sparsam nutzen sollte. Ein schnödes if() tut es auch und ist> besser lesbar.
da hast du Recht, aber da Monitore eher breit als hoch sind habe ich dem
? Einzeiler mehr sichtbaren Quellcode, deswegen nutze ich den gerne.
Stefan ⛄ F. schrieb:> if (command & 1) ds_writeone() else ds_writezero();
wetten da meckert der Compiler.
Stefan ⛄ F. schrieb:> Ich benutze den ternären Operator gerne, wenn er einen Wert zurück> liefert, wie in:puts( (httpStatus>=400) ? "error" : "success" );
me too
>> if (command & 1) ds_writeone() else ds_writezero();Joachim B. schrieb:> wetten da meckert der Compiler.
Sorry, da fehlt ein Semikolon hinter ds_writeone().
Stefan ⛄ F. schrieb:> Es sei denn, sein Program setzt das CLKPR Register passend.
Unwahrscheinlich ;-)
Dennis T. schrieb:> Studio_C.rar (75 KB)Dennis T. schrieb:> Das Timing passt nun auch, allerdings bei> #define F_CPU 2000000L obwohl ich ein 16mhz Quarz habe?
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
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_tds_readbyte(){
2
uint8_ti_byte=0;
3
4
for(inti=0;i<8;i++){
5
i_byte>>=1;// Platz schaffen für nächstes Bit ganz links
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.
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???
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
Stefan ⛄ F. schrieb:> Jetzt bin ich aber sehr gespannt, wie du die 8 Bits ohne 8> Shift-Operationen in ein Byte einliest.
Überhaupt kein Problem und oben schon gezeigt. Man muss nur die Shifts
durch Multiplikation und Division ersetzen. :-)
STK500-Besitzer schrieb:> Was macht der Compiler wohl aus "1<<i"?
Eine Konstante, wenn du ihn dazu kriegst, die Schleife zu unrollen. :-)
Wobei man das auch selber machen kann. 8x der gleiche Code statt
Schleife, und weg ist der Shift.
(prx) A. K. schrieb:> Wobei man das auch selber machen kann. 8x der gleiche Code statt> Schleife, und weg ist der Shift.
Ist wohl längerer Code, aber nicht zu viel und nach dem compilieren
wahrscheinlich sogar effektiver und kleiner, als mit Schleife und
variable?
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.
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.