Hallo,
ich habe letztes Jahr ja bereits begonnen mir Gedanken zur einer
Terrariensteuerung zu machen:
Beitrag "Erste Schaltung: Terrariensteuerung/DCF77/Dimmer/Schalter"
Das Grundgerüstist von der Mikrocontroller-Seite her ist jetzt
eigentlich fertig.*
Was mir zur Zeit Sorgen macht ist das Auslesen der Temperatur via 1-wire
Bus:
Bisher basiert der ganze Ablauf, wie das DCF77-Projekt das ich als
Grundlage genommen habe, darauf, dass jede Millisekunde ein Interrupt
ausgelöst wird, dann passiert der ganze interessante Kram in der
Interrupt-Routine und in der Hauptschleife setze ich dann einmal pro
Sekunde den Inhalt vom Display und die Ausgänge neu.
Wie kriege ich da jetzt die 1-wire-Kommunikation untergebracht?
Meine bisher "beste" Idee ist das auch in der Interrupt-Routine zu
machen, jede Millisekunde eine Aktion, z.B. ein Reset, ein Bit lesen
oder ein Bit schreiben.
Andererseits möchte ich die ISR nicht unnötig aufblähen.
main.c im Anhang. Bitte nicht erschrecken, ich bin immernoch Anfänger
;-)
Grüße, Andi M
*Nachts um 2 oder 3 (Sommer/Winterzeit, so gewählt dass ich immer nur 1
mal syncen muss) geht die Funkuhr an, wenn das Signal 3 mal in Folge
korrekte (Prüfbit) Werte geliefert hat die zueinander passen, wird die
Uhr synchronisiert.
Ein 4x16 Display zeigt die Infos an die ich haben möchte und ich kann
mit FETs Relais schalten.
Taktquelle ist ein 8 MHz Quarzoszillator.
Die einfachen Funktionen sind somit fertig.
Das Terrarium habe ich in der Zwischenzeit umgebaut, was einer der
Gründe dafür ist dass das alles so lang gedauert hat.
Vorteil: Für die Beleuchtung brauche ich erstmal keinen Dimmer mehr. Im
Winter würde ich gerne eine Heizung (Heizkabel oder -Strahler) per
Dimmer steuern, aber dafür brauche ich ja keine Mikrosekundengenaue
Steuerung.
Andi M. schrieb:> Wie kriege ich da jetzt die 1-wire-Kommunikation untergebracht?> Meine bisher "beste" Idee ist das auch in der Interrupt-Routine zu> machen, jede Millisekunde eine Aktion, z.B. ein Reset, ein Bit lesen> oder ein Bit schreiben.
Es gibt auch die Möglichkeit, das kritische 1-w Timing mit einem UART zu
erzeugen. Hier ist das erklärt:
http://www.maxim-ic.com/app-notes/index.mvp/id/214
Der Rest ist vom Timing her unkritisch und kann im Hauptprogramm gemacht
werden.
fchk
Ich habe bisher nichts mit dem UART gemacht aber eben schonmal etwas
dazu gelesen.
Sehe ich das richtg dass der U(S)ART "alleine" arbeitet?
Also einfach einstellen wie schnell er was ausgeben/einlesen soll und
den ganzen Timing-Kram macht der automatisch, ohne dass der sonstige
Programmablauf unterbrochen wird?
Frank K. schrieb:
[...]
> http://www.maxim-ic.com/app-notes/index.mvp/id/214
[...]
Ich habe eine Frage zu dem open-drain buffer circuit:
Die benutzen da 2 2n7002, das Schaltzeichen entspricht einem "normalen"
MOSFET mit einer parallelgeschalteten, in Sperrichtung gepolten Diode
zwischen Drain und Source, was auch immer die tun soll.
Ich hätte BS170 und 1N4148. Das sollte auch funktionieren, oder?
Andi M. schrieb:> Ich habe bisher nichts mit dem UART gemacht aber eben schonmal etwas> dazu gelesen.>> Sehe ich das richtg dass der U(S)ART "alleine" arbeitet?> Also einfach einstellen wie schnell er was ausgeben/einlesen soll und> den ganzen Timing-Kram macht der automatisch, ohne dass der sonstige> Programmablauf unterbrochen wird?
Genau. Für den Reset/Presence Detect Puls wird der USART auf 9600bps
eingestellt, für die Daten auf 115200bps. Das Teil empfängt bei Senden,
und so bekommst Du die Rückmeldung des Slaves mit.
Zeitkritisch ist nur der Teil beginnend von der fallenden Flanke des
Startpulses, den der Master vorgibt, bis zur Antwort des Slaves auf den
Reset bzw auf das jeweilige Bit. Das macht der USART. Die Zeit zwischen
zwei Bits bzw zwischen Reset/Presence Detect und dem ersten Bit ist
unkritisch, hier macht ein Interrupt nichts. Das nutzt man aus.
fchk
Andi M. schrieb:> Ich habe eine Frage zu dem open-drain buffer circuit:> Die benutzen da 2 2n7002, das Schaltzeichen entspricht einem "normalen"> MOSFET mit einer parallelgeschalteten, in Sperrichtung gepolten Diode> zwischen Drain und Source, was auch immer die tun soll.
Diese parasitäre Diode ist im MOSFET immer eingebaut und entsteht
während der Fertigung. Dagegen kann man sich nicht wehren.
> Ich hätte BS170 und 1N4148. Das sollte auch funktionieren, oder?
Streiche die Diode. Ein BC548 sollte es auch tun.
Siehe auch AppNote AVR318.
Ich nehme immer einen 74HC1G125. !OE an TXD, Y(Ausgang) an RXD,
A(Eingang) fest auf Ground. Wenn Du den nicht bekommst, ist ein ganzer
HC125 das nächst einfache, wobei Du da ja nur ein von 4 Gattern
brauchst. Die anderen kannst Du anderweitig verwenden.
fchk
Frank K. schrieb:> Diese parasitäre Diode ist im MOSFET immer eingebaut und entsteht> während der Fertigung. Dagegen kann man sich nicht wehren.
Okay, ich war mir nicht sicher ob das ein Sondertyp ist bei dem man
besonderen Wert auf diese Diode legt. Wenn die einfach den PN-Übergang
vom Substrat zur Drain-Schicht meinen ist ja alles gut.
>> Ich hätte BS170 und 1N4148. Das sollte auch funktionieren, oder?> Streiche die Diode. Ein BC548 sollte es auch tun.
mh, ich hätte BS 170, BS 250, BD 135, 2SK 170, 2SJ 74, ZTX 450 und ZVP
3310
Frank K. schrieb:> Genau. Für den Reset/Presence Detect Puls wird der USART auf 9600bps> eingestellt, für die Daten auf 115200bps. Das Teil empfängt bei Senden,> und so bekommst Du die Rückmeldung des Slaves mit.>> Zeitkritisch ist nur der Teil beginnend von der fallenden Flanke des> Startpulses, den der Master vorgibt, bis zur Antwort des Slaves auf den> Reset bzw auf das jeweilige Bit. Das macht der USART. Die Zeit zwischen> zwei Bits bzw zwischen Reset/Presence Detect und dem ersten Bit ist> unkritisch, hier macht ein Interrupt nichts. Das nutzt man aus.>> fchk
Ah, ja, sehr schön, dann habe ich Sonntag ja was zu tun :D
Vielen Dank
Andi M. schrieb:> Bisher basiert der ganze Ablauf, wie das DCF77-Projekt das ich als> Grundlage genommen habe, darauf, dass jede Millisekunde ein Interrupt> ausgelöst wird
Warum?
Was muß denn unbedingt alle 1ms gemacht werden?
Die kleinste Dauer bei DCF77 ist 100ms, Eine Abtastung alle 10 oder 20ms
ist daher dicke ausreichend.
Immer nur so schnell rechnen, wie nötig.
Peter
Hallo Peter,
das habe ich einfach so gelassen.
Grundlage des Programms ist (wenn ich das gerade nicht verwechsel) das
DCF-Projekt von hier: Beitrag "DCF-Uhr mit LCD in C"
Zu dem 1-wire über UART-Problem:
Ich habe heute erst mal das Display an einen andere Port verschieben
müssen, was länger dauerte als ich gedacht hätte und dann habe ich
angefangen Dinge für den UART zu basteln/programmieren, was bisher so
mehr oder weniger erfolgreich war:
Ich habe den open drain-Ausgang mit 2 BS170 aufgebaut (woran merke ich
dass das funktioniert?) und ein paar Funktionen geschrieben, von denen
ich hoffte dass sie den 1-wire-Baustein nach seiner ID fragen, kriege
aber eine nicht endende Folge von Zweien und Fünfen.
Demnächst werde ich wohl mal den Beispielquelltext ausprobieren den es
zur Application Note AVR318 gibt.
Mh, ich kriege das nicht dazu zu funktionieren.
Ich habe die zu dieser Appnote: http://www.atmel.com/Images/doc2579.pdf
gehörende Software: http://www.atmel.com/Images/AVR318.zip genommen und
folgende Anpassungen gemacht:
- aus #include <ioavr.h> wurde <avr/io.h>, aus <inavr.h> wurde
<avr/interrupt.h>
- \ in Pfadnamen durch / ersetzt
- ln -s OWIUARTBitFuntions.c OWIBitFunctions.c
- ./common_files/OWIDeviceSpecific.h: "#define __ATmega8__" eingefügt
- PORTB auf PORTC geändert (die Ausgabe soll auf LEDs landen, nicht auf
dem Display das ich an PORTB hängen habe)
- Versorgungsspannungspin des DS18S20 auf +5V gelegt, da im Quelltext
irgendwo in einem Kommentar steht dass diese Funtkion(en) nur dann
funktionieren würde(n).
Ich übersetze es mit dem "üblichen" Makefile mit:
In file included from OWIHighLevelFunctions.c:26:0:
2
OWIBitFunctions.h:38:1: warning: function declaration isn’t a prototype [-Wstrict-prototypes]
3
OWIBitFunctions.h:41:1: warning: function declaration isn’t a prototype [-Wstrict-prototypes]
4
OWIBitFunctions.h:44:1: warning: function declaration isn’t a prototype [-Wstrict-prototypes]
5
OWIBitFunctions.h:47:1: warning: function declaration isn’t a prototype [-Wstrict-prototypes]
6
OWIBitFunctions.h:50:1: warning: function declaration isn’t a prototype [-Wstrict-prototypes]
7
OWIHighLevelFunctions.c: In function ‘OWI_SearchRom’:
8
OWIHighLevelFunctions.c:199:13: warning: ‘return’ with no value, in function returning non-void [enabled by default]
Aber das Teil scheint einfach nichts zu machen.
Habe ich etwas wichtiges übersehen?
Was hat es mit dem Makro BUSES auf sich?
Wenn ich das richig sehe liefert das derzeit die beiden UART-Pins, ist
das so richtig?
Wie kriege ich raus ob der selbstgebaute open collector/drain-buffer
richtig funktioniert? Ich habe ja nur 2 BS170 und 2 Widerstände so
zusammengesteckt wie in der AppNote mit 2 BC547 gezeigt wird(das gab es
woanders genauso, aber mit FETs).
// Place the output value in the UART transmit buffer, and wait
4
// until it is received by the UART receiver.
5
OWI_UART_DATA_REGISTER=outValue;
6
lcd_setcursor(0,1);lcd_data('D');
7
while(!(OWI_UART_STATCTRL_REG_A&(1<<OWI_RXC)))
8
{
9
lcd_setcursor(0,1);lcd_data('F');
10
}
11
lcd_setcursor(0,1);lcd_data('G');
12
// Set the UART Baud Rate back to 115200kbps when finished.
13
OWI_UART_BAUD_RATE_REG_L=OWI_UBRR_115200;
14
returnOWI_UART_DATA_REGISTER;
15
lcd_setcursor(0,1);lcd_data('H');
16
}
Das Display zeigt "F"
OWI_UART_STATCTRL_REG_A wird aufgelöst zu UCSRA
OWI_RXC wird aufgelöst zu RXC
Das ist also alles so wie es sein soll,
Hier ist das nochmal mit Kommentar:
1
OWI_UART_WRITE1=0xff;
2
3
voidOWI_WriteBit1()
4
{
5
OWI_TouchBit(OWI_UART_WRITE1);
6
}
7
8
9
/*! \brief Write and read one bit to/from the 1-Wire bus. (Polled UART driver)
10
*
11
* Writes one bit to the bus and returns the value read from the bus.
12
*
13
* \param outValue The value to transmit on the bus.
14
*
15
* \return The value received by the UART from the bus.
16
*/
17
unsignedcharOWI_TouchBit(unsignedcharoutValue)
18
{
19
// Place the output value in the UART transmit buffer, and wait
20
// until it is received by the UART receiver.
21
OWI_UART_DATA_REGISTER=outValue;
22
while(!(OWI_UART_STATCTRL_REG_A&(1<<OWI_RXC)))
23
{
24
}
25
// Set the UART Baud Rate back to 115200kbps when finished.
26
OWI_UART_BAUD_RATE_REG_L=OWI_UBRR_115200;
27
returnOWI_UART_DATA_REGISTER;
28
}
Kann es sein dass das einfach konzeptionell falsch ist?
Wenn ich ein Bit schreibe soll er beim 1wire-bus doch einfach eine Zeit
lang warten, nicht bis er eine Rückmeldung bekommt, oder sehe ich das
hier völlig falsch?
Hallo Peter,
leider sehe ich noch nicht wie das mein Problem lösen würde.
Ich vermute du schlägst vor die Interrupts weiter auseinander zu ziehen
damit ich eine reine Softwarelösung für die 1wire-Kommunikation nehmen
kann, die nicht den UART benötigt sondern einfach irgendeinen I/O-Pin.
Aber 1 Bit dauert ungefähr 500 µs, dann passen in 20 ms doch maximal 40
Bit, dann kommt wieder ein Interrupt. So weit ich weiß reicht das nicht,
da ja schon die Geräteadressen 64Bit lang sind.
Die UART-Lösung finde ich im Prinzip ganz gut, ich könnte ja sogar
sicherstellen dass ich nur jede Millisekunde (oder alle x Millisekunden)
ein Bit schreibe/lese*, indem ich am Anfang der Schleife den µC in einen
sleepmode schicke, dann arbeitet er ja den Rest der Schleife erst ab
wenn die ISR durchgelaufen ist.
Grüße, Andi M
*auch die Bit-Übertragung vom Slave zum Master wird ja vom Master
initialisiert
Andi M. schrieb:> Aber 1 Bit dauert ungefähr 500 µs, dann passen in 20 ms doch maximal 40> Bit, dann kommt wieder ein Interrupt.
Nö.
Das Reset dauert 480µs, ein Bit aber nur 60µs.
Und man sperrt die Interrupts ja nicht für einen komplettes Datenpaket,
sondern nur für einen Puls, also max für 480µs.
Die High-Zeit zwischen den Pulsen ist nicht begrenzt, die kann auch
Stunden dauern. Daher stört dort ein Interrupt nicht.
Peter
>/*! \brief Write and read one bit to/from the 1-Wire bus. (Polled UART
10
> driver)
11
> *
12
> * Writes one bit to the bus and returns the value read from the bus.
13
> *
14
> * \param outValue The value to transmit on the bus.
15
> *
16
> * \return The value received by the UART from the bus.
17
> */
18
>unsignedcharOWI_TouchBit(unsignedcharoutValue)
19
>{
20
>// Place the output value in the UART transmit buffer, and wait
21
>// until it is received by the UART receiver.
22
>OWI_UART_DATA_REGISTER=outValue;
23
>while(!(OWI_UART_STATCTRL_REG_A&(1<<OWI_RXC)))
24
>{
25
>}
26
>// Set the UART Baud Rate back to 115200kbps when finished.
27
>OWI_UART_BAUD_RATE_REG_L=OWI_UBRR_115200;
28
>returnOWI_UART_DATA_REGISTER;
29
>}
>> Kann es sein dass das einfach konzeptionell falsch ist?> Wenn ich ein Bit schreibe soll er beim 1wire-bus doch einfach eine Zeit> lang warten, nicht bis er eine Rückmeldung bekommt, oder sehe ich das> hier völlig falsch?
Der Trick ist, dass TXD und RXD über den OC-Treiber (Deine beiden
Transistoren) und den Pullup auf dem 1W-Bus miteinander verbunden sind.
Wenn Du also etwas sendest, und es es nichts weiter am Bus
angeschlossen, dann solltest Du genau das gleiche wieder empfangen. Wenn
nicht, dann ist an der Hardware etwas faul.
Das musst Du dann debuggen mit den üblichen Elektronik-Messmitteln...
fchk
Oh, das hört sich sinnvoll an. Dann muss ich heute Nachmittag mal drüber
nachdenken wie sich der Ausgang verhalten soll und durchmessen.
Schnelltest: Temperatursensor abgezogen -> Funktion läuft durch.
Eine andere Sache: Ich habe ganz übersehen dass in der Application Note
sogar Hinweise stehen wie man die Software ans Laufen kriegt rotwerd
Leider weiss ich nicht bei allen Punkten was sie damit meinen, ich habe
das mal mit Fragezeichen an Stelle der Aufzählungszeichen markiert.
Achja: Ich nutze den AVR-GCC unter Linux.
To get started with the polled drivers, follow the steps below:
• Create a new project in IAR embedded workbench. Depending on the
version, this
might require that a workspace is already created.
• Add all *.c files from the “polled” and “common_files” directories.
• Select the project from the project browser. Right click on the
project and select
options to bring up the project options dialog.
? Under “General/Target”, make sure that the correct device and memory
model(?) is
selected.
? Under “General/Library configuration”, check the “Enable bit
definitions in I/O
include files” option. (#include <avr/io.h>?)
? Under “General/System”, set the Data stack (CSTACK) to 0x40 and the
Return
stack (RSTACK) to 0x10. This is required to run the memory-intensive
example
code. Smaller stack sizes may be sufficient for other application
utilizing this driver.
• If AVRStudio is used for debugging, the output file format must be
changed. Under
XLINK/Output, select Format/Other, and then select “ubrof 8 (forced)”
from the
“Output format” drop-down box.
• Open the file “OWIPolled.h” for editing and locate the section named
“User
defines”.
• Choose between software only or UART driver by uncommenting one of the
lines
as described in the file.
• Move down to the section corresponding to the selected driver.
• Adjust the defines in the section according to the hardware setup as
described in
the file.
• The project is now ready to be compiled.
Peter Dannegger schrieb:> Andi M. schrieb:>> Aber 1 Bit dauert ungefähr 500 µs, dann passen in 20 ms doch maximal 40>> Bit, dann kommt wieder ein Interrupt.>> Nö.> Das Reset dauert 480µs, ein Bit aber nur 60µs.>> Und man sperrt die Interrupts ja nicht für einen komplettes Datenpaket,> sondern nur für einen Puls, also max für 480µs.>> Die High-Zeit zwischen den Pulsen ist nicht begrenzt, die kann auch> Stunden dauern. Daher stört dort ein Interrupt nicht.>>> Peter
Diesen Post habe ich ganz übersehen da Frank direkt danach geantwortet
hat.
Mir ist eben eingefallen warum ich jede Millisekunde einen Interrupt
haben möchte:
Ich möchte die Temperatur auslesen um sie zu regeln, das Regeln soll per
Phasenanschnitt erfolgen. Da eine Halbwelle nur 50 ms dauert hätte ich
mit 20 ms etwas wenig Auflösung:
Ich dachte daran den Nulldurchgang zu detektieren, entweder indem ich
den Nulldurchgang zu einem Puls forme und als Interrupt annehme, sofern
das nicht Konflikte mit dem Timer-Interrupt gibt oder den Nulldurchgang
oder den Puls per Polling zu detektieren, dann einzustellen in welcher
Millisekunde der TRIAC zünden soll und ihn dann in der ISR zur
entsprechenden Millisekunde zu zünden.
Aber:
Wenn ich zwischen den Bits beliebig lang warten kann, kann ich ja
einfach jedes Bit am Ende der ISR lesen/schreiben.
Ich weiß nicht exakt wie lang die ISR läuft, aber eine LED die ich zu
Beginn der ISR anmache und am Ende wieder ausschalte leuchtet kaum, da
sollte also genug Zeit sein, auch mit 1 Interrupt pro Millisekunde.
Deinen 1wire-Quelltext habe ich bereits, wenn ich das mit dem OC-Buffer
nicht gefixt kriege probiere ich den nachher mal aus.
Kriegt man den DS18S20 kaputt indem man ihn falsch anschließt? Höhere
Spannungen als 5 Volt habe ich auf dem Steckbrett nicht...
Hallo.
Ich habe etwas weiter gebaut:
Ich habe Peters 1 wire-Code genommen und mit der Tempmeas.c aus diesem
Post: Beitrag "Re: DS1820, DS18B20 in C" (das ist die
Variante mit der Namenszuordnung) erweitert. Die Berechnung des
Temperaturwerts habe ich neu gebaut, die kann jetzt 0,1°C-Schritte und
negative Temperaturen.
Was noch nicht so recht klappen will ist die parallele Kommunikation per
1-Wire und Auswertung des DCF-Signals wenn die ISR jede Millisekunde
läuft.
Jetzt habe ich ein paar "Design"-Fragen:
- Sollte ich die ISR seltener laufenlassen? Peter hat das ja schon
mehrfach vorgeschlagen, aber das würde die Genauigkeit der geplanten
Temperaturregelung (über Triac) herabsetzen. Oder Kriegt man das "noch
nebenbei" mit Timern hin?
- Ich finde die Main-Schleife schon jetzt nicht hübsch.
Kann da mal jemand draufgucken und mir einen Vorschlag machen wie ich
das einfacher/übersichtlicher aufbauen könnte?