Forum: Mikrocontroller und Digitale Elektronik Berechnung abbrechen


von Luky (Gast)


Lesenswert?

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?

von Gast123 (Gast)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von Bergie B. (bergie)


Lesenswert?

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

von Luky (Gast)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von Luky (Gast)


Lesenswert?

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

von Bergie B. (bergie)


Lesenswert?

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 ?

von Karl H. (kbuchegg)


Lesenswert?

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.

von Luky (Gast)


Lesenswert?

> 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?

von Karl H. (kbuchegg)


Lesenswert?

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?

von Karl H. (kbuchegg)


Lesenswert?

Hmm
Da ist wohl mit den Postings was schief gelaufen.

von Luky (Gast)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von Luky (Gast)


Lesenswert?

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
Noch kein Account? Hier anmelden.