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
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?!
Was implementierst Du da? Host oder Client? Beides ist möglich und funktioniert auch.
:
Bearbeitet durch User
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
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.
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.
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.
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/
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.
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?
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?
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.
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"
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)?
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.
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.
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?
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.
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.
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
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!
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.
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
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!
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!
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
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
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.
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
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.
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
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.
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.
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
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 ...
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.
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.
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
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.
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.
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.
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.
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...
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.
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.
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.
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.
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.
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.
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
Max M. schrieb: > while( TIM2->CNT < 17578) { ; } Auf jeden Fall würde ich magical Numbers vermeiden.
Hab ich oben schon geschrieben. Um Magic Numbers kümmere ich mich solange nicht bis es funktioniert.
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.
Max M. schrieb: > ( 72 * 10^6 ) / 4096 = 17578 Tip: Immer -1, weil die 0 mitzählt. 72000000UL / 4096 - 1
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
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?
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.
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?
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.
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...
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?
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.
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
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.