Berechnung abbrechen

Gast #1598885
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?
Gast
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
Gast
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.
Gast
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.
Gast
> 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?
Gast
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.
Gast
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...

Antwort schreiben

Eine eigene Frage? Dieser Thread ist über 16 Jahre alt. Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.

Neuen Thread starten

Eine Lösung, Ergänzung oder Rückmeldung zu diesem Thread? Dann antworte hier.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.