Forum: Mikrocontroller und Digitale Elektronik DS18B20 1-Wire Implementierung - Timing-Probleme


von Max M. (maxmicr)


Lesenswert?

Ich hab die letzten Stunden versucht, das 1-Wire Protokoll anhand des 
DS18B20-Datenblatts auf einem STM32F103C8 zu implementieren.

Dafür hab ich C mit IAR bzw. Keil µVision verwendet (Test-Versionen). 
Zum finden von Fehlern hab ich den Debugger mit einem ST-Link V2 sowie 
einem Logik-Analyzer verwendet.

Ich bin nun an einem recht frustrierten Punkt: Ich kann zwar den 
internen 64-bit ROM-Code auslesen (bzw. das was mir der Logic-Analyzer 
anzeigt sieht gut aus) - allerdings werden einzelne Bits falsch 
eingelesen (obwohl im Logic Analyzer alle 1er Bits bzw. 0er Bits 
identisch aussehen), deswegen stimmt auch die CRC-Summe am Ende des 
ROM-Codes nicht mit der im Programm berechneten überein (meine 
CRC-Implementierung ist nach einem Vergleich mit der AppNote von Maxim 
korrekt).

Ich hab für das Testen extra Compiler-Optimierungen ausgestellt da sowas 
natürlich gerne Timing-Probleme verursacht. Als es dann gar nicht 
klappen wollte, hab ich bei IAR die Optimierungen auf "Niedrig" 
eingestellt - dann wurde der ROM-Code korrekt ausgelesen.

Interessanterweise kommen Keil und IAR bei Optimierungsstufe "0" zu 
unterschiedlichen Werten beim ROM-Code. Mit Keil klappt das korrekte 
Auslesen gar nicht. Und meiner Ansicht nach bin ich innerhalb der 
Spezifikation und relativ weit vom kritischen Bereich entfernt (aus 
64-Bit werden ja 63 oder 62 korrekt gelesen).

Daher meine Frage: Macht es überhaupt Sinn, so etwas wie 1-Wire in C zu 
implementieren? Oder sollte man es in Assembler machen? Wobei ich auf 
ARM-Assembler nicht wirklich groß Lust habe.

Ich hatte beruflich schonmal mit 1-Wire zu tun und weiß nicht, wer sich 
dieses Teufelszeug hat einfallen lassen.

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Max M. schrieb:
> Daher meine Frage: Macht es überhaupt Sinn, so etwas wie 1-Wire in C zu
> implementieren?

Ja.

> Ich hatte beruflich schonmal mit 1-Wire zu tun und weiß nicht, wer sich
> dieses Teufelszeug hat einfallen lassen.

Ist doch harmlos?!

von Christian H. (netzwanze) Benutzerseite


Lesenswert?

Was implementierst Du da?
Host oder Client?

Beides ist möglich und funktioniert auch.

: Bearbeitet durch User
von Max M. (maxmicr)


Lesenswert?

Christian H. schrieb:
> Host oder Client?

Die Host-Seite. Vllt. ist auch meine Herangehensweise falsch. Macht man 
sowas lieber mit Timer-Interrupts?

Wobei es dann je nach Zustand unterschiedlich lange dauert bis man sich 
durch die State-Machine durchgehangelt hat.

Aber ich hätte vorher nicht gedacht, dass es vom Compiler abhängt, was 
man für ein Ergebnis bekommt?

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Max M. schrieb:
> Macht man sowas lieber mit Timer-Interrupts?

Innerhalb eines Bits nicht. Darüber hinaus kann man, muss man nicht.

> Wobei es dann je nach Zustand unterschiedlich lange dauert bis man sich
> durch die State-Machine durchgehangelt hat.

Es klingt ein wenig danach, als ob du dir das Leben schwerer machst als 
nötig. Das einzig wirklich interessante Timing ist das eines einzelnen 
Bits.

von Schlaumaier (Gast)


Lesenswert?

Auf einen Arduino-nano (mit 1-Wire-Lib) funktioniert das prima. Man muss 
für ein brauchbares Ergebnis nur den Mittelwert aus 10 Abfragen nehmen.

von (prx) A. K. (prx)


Lesenswert?

Schlaumaier schrieb:
> Auf einen Arduino-nano (mit 1-Wire-Lib) funktioniert das prima. Man muss
> für ein brauchbares Ergebnis nur den Mittelwert aus 10 Abfragen nehmen.

Wenn die Werte einzelner Messungen derart differieren, dass man mitteln 
muss, ist irgendwas falsch. Jene DS18B20, mit denen ich bisher zu tun 
hatte, differieren bei aufeinanderfolgenden Messungen praktisch nicht.

von Max M. (maxmicr)


Lesenswert?

A. K. schrieb:
> differieren bei aufeinanderfolgenden Messungen praktisch nicht.

Außer es gibt Übertragungsprobleme und man überprüft die CRC-Summe 
nicht: http://www.blog.pyoung.net/2015/01/28/ds18b20-crc-check-codes/

von (prx) A. K. (prx)


Lesenswert?

Max M. schrieb:
> A. K. schrieb:
>> differieren bei aufeinanderfolgenden Messungen praktisch nicht.
>
> Außer es gibt Übertragungsprobleme und man überprüft die CRC-Summe
> nicht: http://www.blog.pyoung.net/2015/01/28/ds18b20-crc-check-codes/

Dann ist, wie ich schrieb
> irgendwas falsch.
denn erst die CRC weglassen und dann deshalb mitteln ist eindeutig der 
falsche Weg. Wobei die Leitung nicht sonderlich anspruchvoll ist. Ich 
habe zig Meter quer durchs ganze Haus liegen, extrem simple 
Drillingsleitung, und CRC-Fehler sind trotzdem selten.

von Max M. (maxmicr)


Lesenswert?

Ist das eine gute Idee, das so zu machen?

Timer2-Initialisierung:
1
  /* Enable Clock for Timer 2 */
2
  RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
3
4
  /* Set Timer 2 Prescaler Value */
5
  TIM2->PSC = 0;
6
7
  /* Set Timer 2 Auto Reload Value */
8
  TIM2->ARR = 0xFFFF;
9
10
  /* Disable Update */
11
  TIM2->CR1 |= TIM_CR1_UDIS;

Bei 72MHz mit Prescaler '1' kann man sich dann die nötigen Delays 
ausrechnen.

Beim Starten der Kommunikation funktioniert das noch:
1
  /* Enable Timer 2 */
2
  TIM2->CR1 |= TIM_CR1_CEN;
3
4
  setWriteMode();
5
6
  /* Begin Master Tx Reset Pulse By Pulling 1-Wire Line Low */
7
  GPIOC->ODR &= ~GPIO_ODR_ODR14;
8
  
9
  /* Wait 480µs */
10
  TIM2->CNT = 0;
11
  while( TIM2->CNT < 34560 ) { ; }
12
  TIM2->CNT = 0;
13
  
14
  /* End Master Tx Reset Pulse By Releasing 1-Wire Line */
15
  GPIOC->ODR |= GPIO_ODR_ODR14;
16
17
  setReadMode();
18
19
  /* Let DS18B20 Delay Time Elapse */
20
  while( TIM2->CNT < 4320 ) { ; }
21
  TIM2->CNT = 0;
22
23
  /* 1-Wire Should Definitely Be Low Now */
24
  const bool is_low = !( ( GPIOC->IDR & GPIO_IDR_IDR14 ) >> 14 );
25
26
  /* Rx Pulse Of DS18B20 Needs To Be At Least 480µs long */
27
  while( TIM2->CNT < 30240 ) { ; }
28
  
29
  /* Disable Timer 2 */
30
  TIM2->CR1 &= ~TIM_CR1_CEN;

Beim Schreiben einer '0' bekomme ich anstatt des erwarteten 60µs Delays 
jedoch 150µs:
1
  /* Begin Write 0 Slot By Pulling 1-Wire Line Low */
2
  GPIOC->ODR &= ~GPIO_ODR_ODR14;
3
4
  /* Wait 60µs */
5
  TIM2->CNT = 0;
6
  while( TIM2->CNT < 4320 ) { ; }
7
8
  /* Release 1-Wire Line */
9
  GPIOC->ODR |= GPIO_ODR_ODR14;

Irgendwas über den Timer schein ich übersehen zu haben. Eventuell kann 
man das CNT-Register nicht einfach so on-the-fly setzen?

von Uwe K. (ukhl)


Lesenswert?

Versuche mal den Prescaler auf 72 zu setzen. Dann wird der Timer jede 
1µs aufgerufen. So bleiben wenigstens ein paar Takte für das Programm 
übrig.

Bei einem Prescaler von 1 bleiben für das Hauptprogramm keine Takte 
übrig.  Man muss Timerinterups auslassen. Das erklärt die starke 
Abweichung zum errechneten Wert. Fast jeder zweite Timerinterupt muss 
ausgelassen werden.

Ein paar Grundlagen in die Arbeitsweise von Mikroprozessoren würden dir 
sicher gut tun. Wozu so ein Takt wohl gut ist?  Kann es sein, dass ein 
Prozessor Befehl ein oder mehrere Takte benötigt um abgeschlossen zu 
werden? Und was mag wohl passieren, wenn ich zu jedem Takt ein Interrupt 
auslöse um das Hauptprogramm zu unterbrechen?

von Falk B. (falk)


Lesenswert?

Max M. schrieb:
> Daher meine Frage: Macht es überhaupt Sinn, so etwas wie 1-Wire in C zu
> implementieren?

Sicher, das geht sogar auf einem "kleinen" AVR.

Beitrag "Re: Onewire + DS18x20 Library"

Das kann man spielend auf fast jeden uC portieren, man muss nur den 
untersten Low Level Zugriff auf das IO-Pin und die Atomic-Geschichte 
anpassen.

von Peter D. (peda)


Lesenswert?

Max M. schrieb:
> Ich hab für das Testen extra Compiler-Optimierungen ausgestellt da sowas
> natürlich gerne Timing-Probleme verursacht.

Das Gegenteil ist der Fall, ohne Optimierung kann die Codelaufzeit 
deutliche Timingfehler verursachen.
Also bei kritischen Timings immer mit Speedoptimierung compilieren.
Ein Timer ist außerdem keine schlechte Idee. Bei kleinem CPU-Takt kann 
es trotzdem notwendig sein, eine Delaylib zu verwenden.
Hier mal ein Beispiel für den AVR:
Beitrag "DS18B20 mit Interrupt, AVR-GCC"

von Max M. (maxmicr)


Lesenswert?

Uwe K. schrieb:
> Bei einem Prescaler von 1 bleiben für das Hauptprogramm keine Takte
> übrig.

Warum? Der Timer läuft unabhängig von der CPU.

Uwe K. schrieb:
> Das erklärt die starke
> Abweichung zum errechneten Wert. Fast jeder zweite Timerinterupt muss
> ausgelassen werden.

Interrupts sind nicht aktiviert.

Uwe K. schrieb:
> Kann es sein, dass ein
> Prozessor Befehl ein oder mehrere Takte benötigt um abgeschlossen zu
> werden?

Ja, natürlich. Eine Abweichung von ~5µs hätte ich noch verstanden da die 
CPU ggf. den exakten Timerwert verpasst und danach natürlich noch den 
Pin up bzw. low schalten muss.

Uwe K. schrieb:
> und was mag wohl passieren, wenn ich zu jedem Takt ein Interrupt
> auslöse um das Hauptprogramm zu unterbrechen?

Mach ich nicht (oder ich hab grob was übersehen)?

von Peter D. (peda)


Lesenswert?

Max M. schrieb:
> /* Rx Pulse Of DS18B20 Needs To Be At Least 480µs long */
>   while( TIM2->CNT < 30240 ) { ; }

D.h. rückgerechnet läuft Dein Timer mit 63MHz?
Man sollte da keine magischen Nummern verwenden, sondern die Formeln und 
irgendwo die F_CPU definieren. Das macht es besser lesbar und änderbar.

von Max M. (maxmicr)


Lesenswert?

Peter D. schrieb:
> D.h. rückgerechnet läuft Dein Timer mit 63MHz?

30240 + 4320 = 34560, entspricht 480µs bei 72MHz.

Peter D. schrieb:
> Man sollte da keine magischen Nummern verwenden

Ja, das hätte ich gemacht nachdem ich validiert habe, dass es 
funktioniert.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Max M. schrieb:
> Daher meine Frage: Macht es überhaupt Sinn, so etwas wie 1-Wire in C zu
> implementieren?
OneWire ist eigentlich ziemlich simpel, weil der Start eines Bits vom µC 
festgelegt wird und dann nur noch die Dauer des nachfolgenden Low-Pegels 
gemessen werden muss.
Und C ist ziemlich deterministich: das Programm macht "eigentlich" bei 
jedem Durchlauf jedesmal das Selbe. So ist es auch beim Assembler (man 
kann sich ja ohne Probleme mal den Assemblercode des C-Compilers 
ansehen). Insofern macht er prinzipiell keinen Unterschied ob C oder 
Assembler.
Und wenn man einen grundlegenden Fehler im Design hat, dann hat der 
nichts mit der Programmiersprache zu tun...

Max M. schrieb:
> Mach ich nicht (oder ich hab grob was übersehen)?
Setzt mal direkt vor dem Einlesen des OneWire-Signals einen Portpin und 
gleich danach wieder zurück. Sieh dir diesen Einlesezeitpunkt im Bezug 
zum OneWire-Signal an. "Triffst" du beim Einlesen immer den richtigen 
Punkt?

von MaWin (Gast)


Lesenswert?

Max M. schrieb:
> Daher meine Frage: Macht es überhaupt Sinn, so etwas wie 1-Wire in C zu
> implementieren?

Eigentlich unproblematisch. Das haben nämlich schon Leute vor dir 
geschafft. Es gibt die STM32 tauglichen Arduino-Libs für OneWire und 
DS1820.
Du musst halt nur noch etwas programmieren lernen, dann kriegst du das 
auch hin. Optimierungen entfernen manchmal code, den man zu 
timing-Zwecken hingeschrieben hat, oder sortieren code um, wenn man die 
Reihenfolge nicht erzwingt.
Beim STM stört die lose Kopplung des Timings von I/O vs. CPU abe das 
versuchst du ja durch Timerabfragen zu kompensieren. Interessant wird 
OneWire, wenn Interrupts reinhauen und dein Timing zerstören oder gar 
die I/O manipulieren.
Andererseits hat man nicht immer die Zeit, die 3ms die Interrupts zu 
sperren, die das Auslesen des DS1820 dauert.

von Max M. (maxmicr)


Lesenswert?

Ist es normal, dass
1
  /* Begin Write 0 Slot By Pulling 1-Wire Line Low */
2
  GPIOC->ODR &= ~GPIO_ODR_ODR14;
3
4
  /* Release 1-Wire Line */
5
  GPIOC->ODR |= GPIO_ODR_ODR14;

5.5µs benötigt? Liegt vermutlich am Open-Drain Ausgang, aber dass das so 
lange dauert? Damit könnte man kein I²C mit 400kHz implementieren.

Beitrag #6361040 wurde vom Autor gelöscht.
von (prx) A. K. (prx)


Lesenswert?

Max M. schrieb:
> 5.5µs benötigt? Liegt vermutlich am Open-Drain Ausgang, aber dass das so
> lange dauert? Damit könnte man kein I²C mit 400kHz implementieren.

Man kann bei den STM32 den Pin direkt als Open Drain definieren. Dann 
ist keine solche Umschaltung nötig.

Pinsteuerung mit read-modify-write ist bei ARMs mitunter langsam, aber 
das ist bei leidlich erst zu nehmenden Takten trotzdem zu lang. Wobei 
auch der Peripherietakt mitmischt. Aber um das genauer unter die Lupe zu 
nehmen, müsste der Asm-Code inspiziert werden, Messmethode inklusive.

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

Max M. schrieb:
> Ist es normal, dass
>   /* Begin Write 0 Slot By Pulling 1-Wire Line Low */
>   GPIOC->ODR &= ~GPIO_ODR_ODR14;
>
>   /* Release 1-Wire Line */
>   GPIOC->ODR |= GPIO_ODR_ODR14;
>
> 5.5µs benötigt? Liegt vermutlich am Open-Drain Ausgang, aber dass das so
> lange dauert? Damit könnte man kein I²C mit 400kHz implementieren.

Man korrigiere mich wenn ich mich irre, aber haben nicht die meisten 
ARMs extra Set/Clear Register, damit man eben NICHT Read-Modifiy-Write 
für die IOs braucht? Die sollte man nutzen!

von (prx) A. K. (prx)


Lesenswert?

Falk B. schrieb:
> Man korrigiere mich wenn ich mich irre, aber haben nicht die meisten
> ARMs extra Set/Clear Register, damit man eben NICHT Read-Modifiy-Write
> für die IOs braucht? Die sollte man nutzen!

Nennt sich beim STM32 BSRR, mit Maske für Set und Reset der Bits.

von (prx) A. K. (prx)


Lesenswert?

A. K. schrieb:
> Man kann bei den STM32 den Pin direkt als Open Drain definieren. Dann
> ist keine solche Umschaltung nötig.

ODR mit was anderem verwechselt.

: Bearbeitet durch User
von Verzweifelter Minderleister (Gast)


Lesenswert?

Man benutzt dafür eine UART ohne Timer ....

von Verzweifelter Minderleister (Gast)


Lesenswert?


von Falk B. (falk)


Lesenswert?

Verzweifelter Minderleister schrieb:
> Man benutzt dafür eine UART ohne Timer ....

Kann man machen, hat in einigen Situationen auch Vorteile. ABER! Man 
sollte das auf einem ARM-Controlller auch rein in Software hinkriegen!

von Verzweifelter Minderleister (Gast)


Lesenswert?

Falk B. schrieb:
> Kann man machen, hat in einigen Situationen auch Vorteile. ABER! Man
> sollte das auf einem ARM-Controlller auch rein in Software hinkriegen!

Wozu? Um sich etwas zu beweisen? Blödsinn! Man nutzt die 
Hardwareschnittstelle, die einem soviel Arbeit wie möglich abnimmt. Und 
das ist in diesem Fall die UART!

von (prx) A. K. (prx)


Lesenswert?

Falk B. schrieb:
> ABER! Man
> sollte das auf einem ARM-Controlller auch rein in Software hinkriegen!

Das ging schon auf den ollen LPC2000. Man braucht allerdings brauchbaren 
Delay-Code. Ich hatte damals einen gebaut, der eine simple Schleife beim 
Start über einen Timer kalibriert.

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Verzweifelter Minderleister schrieb:
> Wozu? Um sich etwas zu beweisen?

Ich habe nur jenen bestehenden Code angepasst, den ich schon für AVR 
verwendet hatte. Der ich von Peter Danegger übernommen hatte. Wozu das 
Rad neu erfinden?

: Bearbeitet durch User
von Verzweifelter Minderleister (Gast)


Lesenswert?

A. K. schrieb:
> Wozu das Rad neu erfinden?

Manche fahren auch heute noch gern Trabi.

von (prx) A. K. (prx)


Lesenswert?

... und manche suchen Streit.

von Peter D. (peda)


Lesenswert?

Verzweifelter Minderleister schrieb:
> Wozu? Um sich etwas zu beweisen? Blödsinn! Man nutzt die
> Hardwareschnittstelle, die einem soviel Arbeit wie möglich abnimmt. Und
> das ist in diesem Fall die UART!

Selber Blödsinn.

Der einzige Vorteil ist, daß andere Interrupts das Timing nicht stören 
können.
Ansonsten ist das so ziemlich von hinten durch die Brust ins Auge.
Die Timings lassen sich nur sehr grob einstellen, da alles in 9 Bit 
passen muß. Auf den meisten 8Bittern kann man die Baudrate gar nicht so 
fein einstellen oder man kann nur bestimmte Quarzfrequenzen benutzen. 
Auch braucht man 2 Portpins und Zusatzbeschaltung für den open-Drain 
Ausgang.

Die meisten Implementierungen sind daher in SW oder mit dem DS2482.

von Max M. (maxmicr)


Angehängte Dateien:

Lesenswert?

Das Sampling des Pins stimmt ziemlich genau. Dafür hab ich einen zweiten 
Pin verwendet den ich toggle wenn das Sampling stattfindet (siehe 
Anhang).

Anstatt den gezeigten Nullern werden allerdings Einsen gelesen. Die 
ersten 4 Bytes des ROM-Codes stimmen jedoch (und da kommen auch Nuller 
vor, die korrekt gelesen werden).

Sehr seltsam. Vielleicht ist der Eingangspin defekt?

: Bearbeitet durch User
von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Max M. schrieb:
> Vielleicht ist der Eingangspin defekt?

Vielleicht ist auch die verwendete Software defekt? Poste bitte den 
Code, mit dem sich Dein Problem reproduzieren lässt.

von Max M. (maxmicr)


Lesenswert?

War nur eine Vermutung mit dem Pin. Mit einem anderen Pin funktionierts 
allerdings auch nicht.

Dann ist meine Software falsch und ich muss noch etwas debuggen

: Bearbeitet durch User
von Verzweifelter Minderleister (Gast)


Lesenswert?

Peter D. schrieb:
> Auf den meisten 8Bittern kann man die Baudrate gar nicht so
> fein einstellen oder man kann nur bestimmte Quarzfrequenzen benutzen.
> Auch braucht man 2 Portpins und Zusatzbeschaltung für den open-Drain
> Ausgang.

Ich suche keinen Streit, fakt ist aber dass es eine Steinzeitmthode ist 
OneWire mit Software zu machen und fehleranfällig, wie man hier sehen 
kann. Wir reden aber über STM32. Aber auch auf einem Atmega würde ich 
niemals in Software arbeiten. RX und TX Pins verbinden, einen Pullup 
nach VCC und beide Pins als Opendrain definieren.

Ich habe genau 3 Klassen:
a) USART: 38 Zeilen
b) OneWire: 149 Zeilen
c) DS18B20: 230 Zeilen

Eventuell schafft ihr eine OneWire auch komplett in 5 Zeilen, aber wo 
ist der Vorteil? Direkter Hardwarezugriff, gekapselt in C++ Klassen - 
das kann man auch auf AVR und macht das ganze super portierbar, wartbar 
und fehlerfreier, als sowas in Software zu frickeln.

Max M. schrieb:
> Sehr seltsam. Vielleicht ist der Eingangspin defekt?

Es ist so gut wie nie ein Hardwarefehler.

von Max M. (maxmicr)


Lesenswert?

Verzweifelter Minderleister schrieb:
> Es ist so gut wie nie ein Hardwarefehler.

Der Verdacht war nicht ganz unbegründet. Ich hab mir vor 2 Jahren 
schonmal einen DS18B20 für 1€ aus China kommen lassen. Ich hab mir den 
damals nicht genauer angeschaut da wieder etwas anderes wichtiger war. 
Gestern wollte ich meine Software dann damit testen. Allerdings ist das 
Teil innerhalb weniger Sekunden derart heiß geworden, dass es sich 
vermutlich um einen einfach BJT gehandelt hat.

Dann musste der Reichelt 10€ Express herhalten.

Verzweifelter Minderleister schrieb:
> Ich suche keinen Streit, fakt ist aber dass es eine Steinzeitmthode ist
> OneWire mit Software zu machen und fehleranfällig, wie man hier sehen
> kann.

Ich wäre bei dir mit dem UART wenn ich dafür jetzt nicht einen diskreten 
Open-Drain Eingang aufbauen bzw. mir einen zusätzlichen IC besorgen 
müsste.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Max M. schrieb:
> Anstatt den gezeigten Nullern werden allerdings Einsen gelesen.
Gib mal das, was du an dieser Stelle einliest gleich wieder an einem 
anderen Pin aus und leg das auch auf den LA. Also einfach mal dem 
Signalpfad folgen. Nicht, dass bei der nachfolgenden Bearbeitung der 
Bits und Bytes was schiefgeht.

Und miss mal mit einem Oszi das pyhsikalische Signal. Also ob da ein 
brauchbarer 0-Pegel ist, oder ob nur dein LA eine 0 sieht, der µC wegen 
einer Buskollision aber eine 1.

: Bearbeitet durch Moderator
von Verzweifelter Minderleister (Gast)


Lesenswert?

Max M. schrieb:
> Ich wäre bei dir mit dem UART wenn ich dafür jetzt nicht einen diskreten
> Open-Drain Eingang aufbauen bzw. mir einen zusätzlichen IC besorgen
> müsste.

Wieso solltest du das müssen? Du benutzt einenSTM32F103C8 ...

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Verzweifelter Minderleister schrieb:
> Ich habe genau 3 Klassen: a) USART: 38 Zeilen
> b) OneWire: 149 Zeilen
> c) DS18B20: 230 Zeilen

Wo kann man sich Deine SW herunterladen?

Berücksichtige bitte, dass es auch Projekte gibt, wo schon alle USARTs 
mit anderen Aufgaben belegt sind. Hatte ich zum Beispiel mit einem 
STM32F407VET6 - trotz 6 vorhandener USARTs.

von Verzweifelter Minderleister (Gast)


Lesenswert?

Frank M. schrieb:
> Wo kann man sich Deine SW herunterladen?
>
> Berücksichtige bitte, dass es auch Projekte gibt, wo schon alle USARTs
> mit anderen Aufgaben belegt sind. Hatte ich zum Beispiel mit einem
> STM32F407VET6 - trotz 6 vorhandener USARTs.

Müsste ich unter Projekte & Code posten. Wenn alle Usarts belegt sind 
und man dennoch eine mehr benötigt ist das ein grundsätzlicher 
Designfehler.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Verzweifelter Minderleister schrieb:
> Müsste ich unter Projekte & Code posten.

Sehr gern, wäre wirklich nett.

> Wenn alle Usarts belegt sind und man dennoch eine mehr benötigt ist das
> ein grundsätzlicher Designfehler.

Ich benötige aber gar keine weitere USARTs, von daher ist Deine Aussage 
über den Designfehler nichtig :-)

One-Wire geht auch mit Pin-Wackeln. Zwingend muss man dem STM32 also 
keinen weiteren USART aus der Nase popeln.

Die Verwendung eines USARTs für One-Wire ist nett, aber keine Religion.

: Bearbeitet durch Moderator
von Verzweifelter Minderleister (Gast)


Lesenswert?

Frank M. schrieb:
> Die Verwendung eines USARTs für One-Wire ist nett, aber keine Religion.

Wobei die Belegung von allen 6 verfügbaren U(S)ARTs natürlich die 
Ausnahme ist. Gaz eventuell würde ich da eventuell auch auf eine 
Software zurückgreifen - ist aber mehr als hässlich.

von c-hater (Gast)


Lesenswert?

Frank M. schrieb:

> Die Verwendung eines USARTs für One-Wire ist nett, aber keine Religion.

Sicher nicht. Man kann auch andere serielle Peripherie dafür 
"zweckentfremden".

Die mit Abstand dümmste (weil empfindlichste) Lösung auf 
Single-Core-Maschinen ist jedenfalls definitiv Bit-Banging mit Delays...

Bei Architekturen wie etwa dem Propeller hingegen ist das vollkommen OK.

von Max M. (maxmicr)


Lesenswert?

Verzweifelter Minderleister schrieb:
> Wieso solltest du das müssen? Du benutzt einenSTM32F103C8 ...

Steht in dem von dir verlinkten Dokument.

c-hater schrieb:
> Man kann auch andere serielle Peripherie dafür
> "zweckentfremden".

Macht halt imho keinen Sinn wenn damit der wesentliche Faktor von 1-Wire 
wegfällt - nämlich, dass man nur einen Daten-Pin benötigt. Dann lieber 
einen Sensor, der I2C oder SPI unterstützt.

von Verzweifelter Minderleister (Gast)


Lesenswert?

Max M. schrieb:
> Steht in dem von dir verlinkten Dokument.

Ne. Du verbindest den TX und RX Pin miteinander, definierst beide als 
Opendrain und verpasst denen noch einen externen Pullup. Also benötigst 
du genau einen Widerstand und du hast einen Hardware OneWire.

Eine diskreten Opendrain Eingang benötigst du nicht, denn der STM32 hat 
einen eingebaut.

von c-hater (Gast)


Lesenswert?

Max M. schrieb:

> Macht halt imho keinen Sinn wenn damit der wesentliche Faktor von 1-Wire
> wegfällt - nämlich, dass man nur einen Daten-Pin benötigt.

Nein, dass man nur einen Pin benötigt ist nicht der wesentliche Faktor. 
Wäre es so, würde das Protokoll OnePin heißen. Es heißt aber OneWire. 
DAS ist der wesentliche Faktor...

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

c-hater schrieb:
> Man kann auch andere serielle Peripherie dafür "zweckentfremden".

Sicher. Aber das Wort "zweckentfremden" sagt schon aus, dass die 
Verwendung nicht dem Zweck entspricht. Viel sinnvoller wäre ein 
One-Wire-Pin in HW am µC, der genau für diesen Zweck gedacht ist. Habe 
ich bisher aber noch nirgendwo gesehen. Allerdings muss ich zugeben, 
dass ich bisher auch nicht danach gesucht habe.

von Verzweifelter Minderleister (Gast)


Lesenswert?

Frank M. schrieb:
> Viel sinnvoller wäre ein
> One-Wire-Pin in HW am µC, der genau für diesen Zweck gedacht ist. Habe
> ich bisher aber noch nirgendwo gesehen.

Eine Usart (RX + TX verbunden mit gemeinsamen Pullup) kommt aber 
Geschwindigkeitstechnisch an die Definition von Onewire heran. Da muss 
man sich um kein Timinggewackel in Software mehr kümmern. Deutlich 
weniger fehleranfällig.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Verzweifelter Minderleister schrieb:
> Eine Usart (RX + TX verbunden mit gemeinsamen Pullup) kommt aber
> Geschwindigkeitstechnisch an die Definition von Onewire heran. Da muss
> man sich um kein Timinggewackel in Software mehr kümmern. Deutlich
> weniger fehleranfällig.

Dem kann ich nur zustimmen. Umso mehr blicke ich der Veröffentlichung 
Deines Codes unter P&C mit Freude entgegen.

von Verzweifelter Minderleister (Gast)


Lesenswert?

Frank M. schrieb:
> Dem kann ich nur zustimmen. Umso mehr blicke ich der Veröffentlichung
> Deines Codes unter P&C mit Freude entgegen.

Ich werde die mal eben für den STM32F103C8 aufbereiten und dann rel. 
Kommenarlos posten.

von Wolle G. (wolleg)


Lesenswert?

Mein Senf passt zwar nicht 100%ig, aber man kann es sich auch einfach 
machen.
Man nehme einen DS8482S-100 (1Kanal) oder DS2482S-800 (8Kanal) und 
schalte ihn zwischen DS18B20 (auch mehrere) und µC. Angesteuert wird mit 
I2C.
Da muss man sich keine Gedanken zu Zeiten usw. machen.

von Verzweifelter Minderleister (Gast)


Angehängte Dateien:

Lesenswert?

Da das nicht getestet ist, poste ich das mal eben hier statt in Projekte 
& Code.

Ohne Gewähr auf irgendwas. Da ich kein Profi und Minderleister bin, bin 
ich für Verbesserungsvorschläge offen. Ohne HAL.

So mache ich das.

von Max M. (maxmicr)


Lesenswert?

Weiß jemand, wie man den Prescaler eines laufenden Timers verändert?

Laut Datenblatt geschieht das entweder durch einen Überlauf / Unterlauf 
oder durch ein manuelles Update (TIM_EGR_UG-Bit).

Allerdings funktioniert das bei mir nicht.

So wird der Timer initialisiert:
1
  /* Set Timer 2 Prescaler Value */
2
  TIM2->PSC = 0;
3
  
4
  /* Set Timer 2 Auto Reload Value */
5
  TIM2->ARR = 0xFFFF;
6
  
7
  /* Enable Update Interrupts */
8
  TIM2->DIER |= TIM_DIER_UIE;
9
10
  /* Enable Timer 2 */
11
  TIM2->CR1 |= TIM_CR1_CEN;

Dann arbeite ich eine weile mit Prescaler = 1 und irgendwann setze ich:
1
    /* New Prescaler Value */
2
    TIM2->PSC = 4095;
3
    /* Trigger custom update */
4
    TIM2->EGR = TIM_EGR_UG;
5
    /* Wait until update interrupt occurs */
6
    while( TIM2->SR & TIM_SR_UIF == 0) { ; }
7
    /* Clear update interrupt flag */
8
    TIM2->SR &= ~TIM_SR_UIF;
9
    
10
    /* Reset counter to 0 */
11
    TIM2->CNT = 0;
12
    GPIOC->ODR &= ~GPIO_ODR_ODR15;
13
    while( TIM2->CNT < 17578) { ; }
14
    GPIOC->ODR |= GPIO_ODR_ODR15;

Mit einem CPU-Takt von 72MHz hätte ich jetzt erwartet, dass GPIOC15 1s 
low bleibt, allerdings bleibt er nur 0.243ms low. Warum?

: Bearbeitet durch User
von Verzweifelter Minderleister (Gast)


Lesenswert?

Max M. schrieb:
> while( TIM2->CNT < 17578) { ; }

Auf jeden Fall würde ich magical Numbers vermeiden.

von Max M. (maxmicr)


Lesenswert?

Hab ich oben schon geschrieben. Um Magic Numbers kümmere ich mich 
solange nicht bis es funktioniert.

von Verzweifelter Minderleister (Gast)


Lesenswert?

Max M. schrieb:
> Hab ich oben schon geschrieben. Um Magic Numbers kümmere ich mich
> solange nicht bis es funktioniert.

Die kann aber Teil des Problems sein.

von Max M. (maxmicr)


Lesenswert?

( 72 * 10^6 ) / 4096 = 17578

von Verzweifelter Minderleister (Gast)


Lesenswert?

Max M. schrieb:
> ( 72 * 10^6 ) / 4096 = 17578

Tip: Immer -1, weil die 0 mitzählt.
72000000UL / 4096 - 1

von Max M. (maxmicr)


Lesenswert?

Verzweifelter Minderleister schrieb:
> Tip: Immer -1, weil die 0 mitzählt.

Deswegen auch 4095 im PSC-Register.

Ich hab gerade gemerkt, selbst wenn ich ganz am Anfang bei der 
Initialisierung 4095 ins PSC-Register schreibe, ändert sich genau gar 
nichts am Verhalten. D.h. der Timer läuft immer mit Prescaler 0 (bzw. 1, 
je nachdem wie man es liest).

Also einen ärgerlicheren Tag hatte ich selten.

: Bearbeitet durch User
von Verzweifelter Minderleister (Gast)


Lesenswert?

Max M. schrieb:
> Ich hab gerade gemerkt, selbst wenn ich ganz am Anfang bei der
> Initialisierung 4095 ins PSC-Register schreibe, ändert sich genau gar
> nichts am Verhalten. D.h. der Timer läuft immer mit Prescaler 0 (bzw. 1,
> je nachdem wie man es liest).

Ich kann leider von hier nicht debuggen. Aber auch bei der 
Initialisierung musst du erst einen Update erzwingen, z.B. Durch 
TIM2->CNT = 0xffff;
Erst beim Überlauf wird PSC übernommen.

Und hast du generell mal deine Takte überprüft?

von Apollo M. (Firma: @home) (majortom)


Lesenswert?

Verzweifelter Minderleister schrieb:
> Man benutzt dafür eine UART ohne Timer ....

... so ist es, z.B. bei 1200 baud passt das OW Timing gut und die neuen 
AVR's haben auch alle im UART Modul einen One-Wire Mode, da wird RX und 
TX intern zusammen geschaltet ...

Es gibt auch eine HW/SW Variante mit Timers und Logic Block z.B. für 
PIC's und AVR1.

von Verzweifelter Minderleister (Gast)


Lesenswert?

Ich finde auch 1-wire hat einen eklatanten Designfehler, der das Ganze 
so gut wie unbrauchbar macht. Im Gegensatz zu i²c gibt es keine frei 
wählbaren Adressen. Wenn ich 10 Temperaturfühler einlöte, muss ich 
entweder dessen Adressen beim Löten bereits kennen oder die Software 
einlernen etc.

Wie bescheuert ist denn das? Man hätte doch auch Adresspins wie bei i²c 
verwenden können. Dann bräuchte man auch nicht so einen Unsinn wie 
MatchROM etc.

Wenn man nur ein Device pro Typ verwendet - ok. Ansonsten meiner Meinung 
nach unbrauchbar.

Oder gibts auch 1-wire Chips mit Adresspins?

von Peter D. (peda)


Lesenswert?

Verzweifelter Minderleister schrieb:
> Im Gegensatz zu i²c gibt es keine frei
> wählbaren Adressen.

Der DS18B20 hat 2 frei verfügbare Bytes im EEPROM.
Du kannst also einfach jeden Sensor vor dem Einbau konfigurieren und 
dort z.B. eine Adresse + Prüfbyte abspeichern.
Die Auswertesoftware liest dann einfach alle gefundenen Sensoren aus und 
speichert den Meßwert auf der Adresse im EEPROM. Sollten 2 Sensoren die 
gleiche Adresse haben, gibt man eine Fehlermeldung aus.

von Verzweifelter Minderleister (Gast)


Lesenswert?

Peter D. schrieb:
> Du kannst also einfach jeden Sensor vor dem Einbau konfigurieren und
> dort z.B. eine Adresse + Prüfbyte abspeichern.

Genau das meinte ich! Ich muss jeden in die Hand nehmen, vorher 
adressieren, identifizieren etc...

von Peter D. (peda)


Lesenswert?

Verzweifelter Minderleister schrieb:
> Ich muss jeden in die Hand nehmen, vorher
> adressieren, identifizieren etc...

Wie willst Du denn bei einem DS18B20 im Stahlrohr Adreßpins 
herausführen?

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Verzweifelter Minderleister schrieb:
> Ich muss jeden in die Hand nehmen, vorher adressieren

Naja, das musst Du bei I2C auch machen, nämlich die entsprechenden 
Lötbrücken bzw. Jumper setzen. Bei I2C machst Du das in HW, bei One-Wire 
mit SW.

von (prx) A. K. (prx)


Lesenswert?

Ich sehe hier 1-W in Vorteil gegenüber I2C, wenn man viele Sensoren an 
einem Gerät verwendet. Ein halbes Dutzend hatte ich schon mehrfach.

I2C Devices dieser Klasse haben nur sehr wenige hardwareseitig fest 
definierte Adressen zu Auswahl per Pin, beispielsweise 2. Damit ist die 
maximale Anzahl Devices pro Bus eng limitiert. 1-W Devices haben 
eindeutige Adressen, so dass man so viele davon an einen Bus hängen 
kann, wie man hardwareseitig dran kriegt.

Wenn man etliche Sensoren an einen Bus hängt, wird man die klugerweise 
einzeln trennbar machen. Schon um in Fehlerfall den Schmutzfuss zu 
finden. Damit ergibt sich auch eine einfache Möglichkeit, deren Adressen 
nacheinander ins Gerät einzukonfigurieren, oder alternativ mit einem 
separaten Gerät für den Feldeinsatz eine logische Adresse ins EEPROM vom 
Sensor zu schreiben.

Wen die Adressierung nervt, der kann einen 1-W Bus pro Device verwenden 
und auf die Adressierung verzichten. Das hat auch den Vorteil, dass ein 
defekter Sensor nur sich selbst lahm legt, nicht den ganzen Bus.

: Bearbeitet durch User
von Wolle G. (wolleg)


Lesenswert?

Meine DS18B20 haben alle eine vom Hersteller festgelegte 16stellige 
Hexadezimalseriennummer, die man auslesen kann.
Jetzt nur noch die Fühler kennzeichnen und die Seriennummer in einer 
Tabelle o. ä. zuordnen.
Damit lässt sich die Temperatur von jedem einzelnen Fühler separat 
auslesen.
fertig

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.