Ein Mikrocontroller (Atmega168) soll ein paar Spannungen einlesen. Dazu wird über einen längeren Zeitraum gemittelt. Auf Nachfrage werden die Werte gesendet und ein neuer Zyklus gestartet. Mein Problem ist, dass die Reaktionszeit auf die Sendeaufforderung sehr kurz und konstant sein muss. Also bleibt mir -glaube ich- nichts anderes übrig, als eine Interruptroutine für das Senden über USART zu verwenden. Das Problem liegt nun darin, dass ich nach dem senden wieder von einem definierten Zustand starten will. Das Programm soll also nicht mit der Messung dort fortfahren, wo der USART-Interrupt aufgetreten ist, sondern am Beginn der Messroutine. Kann ich nach dem Interrupt ohne einen vollständigen Softwarereset auszulösen (dann müsste ich zu viele Initialisierungen machen und das dauert zu lange) an eine bestimmte Stelle springen?
Hi, evtl. hilft es, in der ISR ein Flag oder Bit zu setzen in einer Variablen, die in jedem Messdurchlauf abgefragt wird. Ist das "lieber controller, starte mal bitte von vorn"-Flag gesetzt, erfolgt im Hauptprogramm oder an einer anderen sinnvollen Stelle der Sprung zum Anfang des Messablaufs.
Luky schrieb: > Mein Problem ist, dass die Reaktionszeit auf die Sendeaufforderung sehr > kurz und konstant sein muss. Wie kurz? Wie konstant? > Also bleibt mir -glaube ich- nichts anderes > übrig, als eine Interruptroutine für das Senden über USART zu verwenden. > Das Problem liegt nun darin, dass ich nach dem senden wieder von einem > definierten Zustand starten will. > Das Programm soll also nicht mit der Messung dort fortfahren, wo der > USART-Interrupt aufgetreten ist, sondern am Beginn der Messroutine. > Kann ich nach dem Interrupt ohne einen vollständigen Softwarereset > auszulösen (dann müsste ich zu viele Initialisierungen machen und das > dauert zu lange) an eine bestimmte Stelle springen? Dein ganzes 'Konzept' klingt danach, als ob es verkorkst ist. Vermutung ins Blaue: du verwendest _delay_ms und kommst jetzt in Schwierigkeiten, weil du kein Timing garantieren kannst. -> _delay_ms ist fast immer der falsche Weg um ein Timing zu erzeugen. Der saubere, richtige Weg führt praktisch immer über einen Timer, der einen Basistakt erzeugt.
hi! Meiner erste Idee dazu wäre das du beim starten deiner Messung ein Flag setzt. Dieses wird immer wieder abgefragt wärend der Messung. Sollte es nicht mehr gesetzt sein könntest du die Messung beenden, alle Werte verwerfen und eine neue Messung starten. Nun must du nur noch den Wert des Flag im IE zurücksetzten. Vieleicht hat ja noch jemand eine schönere Lösung. LG
Nein, ich habe kein _delay_ms. Ich messe ein paar Spannungen (auch mit externem, langsamen ADC) und mittle diese über die (unbekannt lange) Messperiode. Der Hauptcontroller fragt dann zu einem unbekannten Zeitpunkt die Messcontroller ab. Diese müssen also schnell reagieren, damit ich mehrere Slaves dranhängen und zuverlässig auf Timeouts prüfen kann. Die Einsprungzeit in die Interruptroutine kann ich kompensieren, eine Abfrage vor jeden Messbefehl wird aber etwas schwierig, vor allem da diese nicht atomar sind und ich verhindern möchte, mitten in einem Zyklus wieder zu beginnen. Und immer mit cli und sei die Interrupts zu deaktivieren / aktivieren baut teilweise ziemliche Verzögerungen ein. Ich möchte also nach dem Sendevorgang die alten Messungen alle verwerfen und sauber von neuem beginnen.
Luky schrieb: > Nein, ich habe kein _delay_ms. Dann: Code zeigen > Ich messe ein paar Spannungen (auch mit externem, langsamen ADC) und > mittle diese über die (unbekannt lange) Messperiode. OK. > Der Hauptcontroller fragt dann zu einem unbekannten Zeitpunkt die > Messcontroller ab. Diese müssen also schnell reagieren, damit ich > mehrere Slaves dranhängen und zuverlässig auf Timeouts prüfen kann. Was stellst du dir so als Zahlenwert für einen Timeout vor? > Und immer mit cli und sei die Interrupts zu deaktivieren / aktivieren > baut teilweise ziemliche Verzögerungen ein. Das ist mir noch nicht klar. Code? > Ich möchte also nach dem Sendevorgang die alten Messungen alle verwerfen > und sauber von neuem beginnen. Klingt für mich nach: Nach dem Senden werden die Summenvariablen, die du zum Berechnen des Mittelwerts benutzt, auf 0 zurückgesetzt.
1 | ISR(USART_RX_vect) { |
2 | (...)
|
3 | |
4 | USART_transmit( HIGHER(ADCVal) ); |
5 | USART_transmit( HIGH(ADCVal) ); |
6 | USART_transmit( LOW(ADCVal) ); |
7 | |
8 | ADCVal = 0; |
9 | |
10 | }
|
11 | |
12 | main
|
13 | |
14 | for (;;) { // Loop forever |
15 | |
16 | if ( ADCVal == 0 ) { //neuer Zyklus: Initialisieren |
17 | |
18 | readFromLTC2485(0, 4); //ADC lesen |
19 | ADCVal = ((uint32_t) (ucDataRead[0]&0x3F)<<24) + ((uint32_t) ucDataRead[1]<<16) + (ucDataRead[2]<<8) + ucDataRead[3]; |
20 | |
21 | (...)
|
22 | |
23 | }
|
24 | //normal: mittle Werte
|
25 | |
26 | readFromLTC2485(0, 4); //ADC lesen |
27 | ADCVal = ( ADCVal + ( (uint32_t) (ucDataRead[0]&0x3F)<<24) + ((uint32_t) ucDataRead[1]<<16) + (ucDataRead[2]<<8) + ucDataRead[3] ) / 2 ; |
28 | |
29 | (...)
|
30 | |
31 | } //forever |
Also ich bin mit nicht sicher ob ich das richtig verstehe, aber ich schreibe mal drauflos: 1) Du machst eine Messung.(Var: a) 2) Du Speicherwer den Wert (a) in 'X' ab. 3) Du machst eine Messung.(Var: a) 4) (a+X)/2 => X 5) 3 & 4 werden immer weiter wiederholt nach 10 Messungen hat der: - 10 Messwert 1/2 einfluss auf X - 9 Messwert 1/4 einfluss auf X - 8 Messwert 1/8 einfluss auf X - 7 Messwert 1/16 einfluss auf X - 6 Messwert 1/32 einfluss auf X - 5 Messwert 1/64 einfluss auf X - 4 Messwert 1/128 einfluss auf X - 3 Messwert 1/265 einfluss auf X - 2 Messwert 1/512 einfluss auf X - 1 Messwert 1/512 einfluss auf X ist das gewollt ? oder sehe ich das falsch ?
Luky schrieb: > if ( ADCVal == 0 ) { //neuer Zyklus: Initialisieren > > readFromLTC2485(0, 4); //ADC lesen Was macht readFromLTC2485? Du hast von einem externen ADC gesprochen. Die werden doch normalerweise so irgendwie angesprochen Starte Wandlung Warte aufs Fertigwerden Lies Ergebnis aus Das kann man auch oft umdrehen if( Ergebnis schon fertig ) { Lies Ergebnis und verarbeite Starte Wandlung } man hat dann so vom Starten der Wandlung bis zum Holen des Ergebnisses in der übergeordneten Endlosschleife alle Zeit der Welt, andere Dinge zu erledigen. Während der ADC buddelt, macht man andere Sachen und wenn die fertig sind, wird der ADC befragt ob er schon fertig ist und das Ergebnis geholt. Quasi in Multitasking auf 2 Chips. Der ADC fängt zu werkeln an, ohne dass ihm zunächst wer auf die Finger schaut.
> gesprungen wurde nicht verwenden zu müssen und "einfach" an den beginn > der Endlosschleiefe zu springen und nicht dorthin, wo der Interrupt > unterbrochen hat. Vergiss diese wilden Sprünge quer durch die Pampa gleich wieder. Das bringt dich nicht weiter. Wenn überhaupt, dann bringt dich dieser Gedankengang maximal nur in Schwierigkeiten. Was ist mit der ADC Abfrage? Lässt sich die nach obigem Schema umbauen?
Luky schrieb: > gesprungen wurde nicht verwenden zu müssen und "einfach" an den beginn > der Endlosschleiefe zu springen und nicht dorthin, wo der Interrupt > unterbrochen hat. Vergiss diese wilden Sprünge quer durch die Pampa gleich wieder. Das bringt dich nicht weiter. Wenn überhaupt, dann bringt dich dieser Gedankengang maximal nur in Schwierigkeiten. Was ist mit der ADC Abfrage? Lässt sich die nach obigem Schema umbauen?
Ja eigentlich wollte ich folgendes posten: Momentan handelt es sich hier noch um einen simplen Filter, der soll aber besser werden. Ja. das ist schon richtig so. Im Programm schalte ich danach noch den externen Multiplexer um und messe 7 andere Spannungen, der Teil ist aber nicht hier im Code. Die Frage ist, ob es eine Möglichkeit gibt die Deaktivierung der Interrupts vor jeder Messung und der Abfrage ob in die Interruptroutine gesprungen wurde nicht verwenden zu müssen und "einfach" an den beginn der Endlosschleiefe zu springen und nicht dorthin, wo der Interrupt unterbrochen hat.
Luky schrieb: > Im Programm schalte ich danach noch den externen Multiplexer um und > messe 7 andere Spannungen, der Teil ist aber nicht hier im Code. Hmm > readFromLTC2485(0, 4); //ADC lesen liest das 4 Spannungen vom ADC? Kein Wunder dass du in Schwierigkeiten kommst.
Das liest 4 Bytes vom ADC und speichert sie in ucDataRead[0]..[3] Funktioniert auch alles wunderbar. Aber wenn ich am Anfang die Interrupts deaktiviere und sie am ende wieder aktiviere gibt es halt zu viel Verzögerung und ich beginne in einem undefinierten Zustand wieder mit dem neuen Messzyklus. Mir würde die Idee mit dem "wilden Sprüngen" an genau eine Stelle deshalb sehr gut gefallen...
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.