Forum: Mikrocontroller und Digitale Elektronik Signalauswertung Empfänger (Modellbau)


von Stefan S. (novafly)


Lesenswert?

Hallo Gemeinde,
es geht leider schon wieder um das Thema der Impulslängenauswertung, 
hier im Beispiel bei einem Empfänger aus dem Modellbaubereich. Ich weiß, 
dass es dazu schon einige Beiträge gibt, welche ich auch gelesen habe 
aber mir bisher noch nicht den entscheidenden Hinweis zur Fehlerquelle 
in meinem Code gebracht haben.

Nachfolgend erstmal der Programmcode:
1
#include <inttypes.h>
2
#include <avr/interrupt.h>
3
#include <avr/io.h>
4
#include <util/delay.h>
5
6
7
volatile uint16_t signal_1st, signal_2nd;
8
volatile uint16_t impuls;
9
volatile uint8_t  kanal = 255;
10
volatile uint8_t setMode = 0;
11
volatile uint8_t signalEnd = 0;
12
13
uint16_t signalLength = 0;
14
15
16
ISR( TIMER1_CAPT_vect )
17
{
18
  
19
    switch(setMode)
20
  {
21
      case 0:
22
  
23
        signal_1st = ICR1;
24
  
25
          TCCR1B ^= (1<<ICES1);  // Capture-Flanke umschalten
26
          setMode = 1;      // Modus "fallende Flanke"
27
        break;
28
    case 1:
29
  
30
        signal_2nd = ICR1;
31
        TCCR1B ^= (1<<ICES1);  // Capture-Flanke umschalten
32
        setMode = 0;      // Modus "steigende Flanke"
33
        signalEnd = 1;      // Variable zum Beginn der Berechnung für die Impulslänge
34
        cli();
35
        break;
36
  }
37
}
38
39
40
int main(void)
41
{
42
  DDRB = 0x00;        // Port B als Input (inkl. PB0 = ICP1)
43
  DDRC = 0xff;        // Port C als Ausgang
44
  
45
  
46
  TIMSK |= (1<<TICIE1);           // Capture Interrupt enable
47
  TCCR1B |= (1<<CS12) | (1<<ICES1); // Prescaler 256 Timer 1, Capture bei steigender Flanke
48
49
  sei();                          // Globale Interruptfreigabe
50
51
52
    for(;;)
53
    {  
54
    
55
    if(signalEnd)
56
    {
57
      impuls = (signal_2nd - signal_1st);  // Differenzwert zwischen steigender und fallender Flanke
58
59
    
60
    
61
      if(impuls < 47)  // Vergleichswert für 1,5ms bei 8MHz und TimerPrescaler 256
62
      
63
        PORTC = ~0x00;
64
  
65
      else
66
    
67
        PORTC = ~0x01;
68
      
69
  
70
      signalEnd = 0;
71
      sei();
72
73
    }
74
75
    }
76
}

Zur Funktion:
Eigentlich ist die Geschichte recht simpel und wird bisher auf einem 
STK500 getestet. Die Impulslänge eines Empfängerkanals wird mittels 
Timer und "Input Capture" auf eine bestimmte Länge überprüft und damit 
einfach zwei Ausgänge geschalten.
In der ISR schalte ich die Flankenerkennung von zuerst "steigend" auf 
"fallend" um und berechne aus der Differenz der beiden Werte im ICR1 die 
vergangene Zeit.

Jetzt ist das Problem, dass ich praktisch immer (unabhängig von der 
Impulslänge) in die erste IF-Anweisung reinlaufe und ich vom errechneten 
Wert anscheinend immer unter den 47 bin. Die Impulslängenänderung beim 
Bewegen des Knüppels passt (lt. Oszi) und liegt auch am ICP1 an.

Ich sehe jetzt keinen Fehler und kann mir das auch nicht wirklich 
erklären. Wäre super, wenn jemand einmal über den Code sehen könnte, 
vielleicht bin ich ja einfach nur blind :) Bin für jeden Tipp dankbar.


Gruß
Stefan

von mmmh... (Gast)


Lesenswert?

schaut doch eigentlich ganz gut aus.

ich bin mir nur nicht so sicher, ob signalEnd nicht immer true ist.. 
(weil das ja sicherlich existiert)
- ich meine mein avr gcc hätte mir da mal was entsprechendes gesagt..
- kann aber auch sein das das in nem java code war...

geh doch auf nummer sicher und schreib
1
if(signalEnd!=0){
2
3
}

von Stefan S. (novafly)


Lesenswert?

hmm, "signalEnd" setze ich ja bei der Definition der Variable und in der 
IF-Anweisung auf Null. Von daher müsste es eigentlich stimmen.

Ich hab jetzt aus lauter Verzweiflung (weil ich keine Fehler gefunden 
habe) am Vergleichswert rumgespielt und siehe da... !

Wenn ich einen VGL-Wert von 6 einstelle, schaltet meine LED um, 
allerdings nicht von aus -> ein, sondern sie wird PWM-mäßig getaktet (je 
nach Knüppelposition am Sender) bis sie dann bei Vollauschlag dauerhaft 
leuchtet. Das dürfte aber doch bei einem Vergleich auf "größer" bzw. 
"kleiner" nicht der Fall sein, da ich eine eindeutige Grenze definiere.

Nun stellt sich mir noch die Frage, ob meine Überlegungen zur Berechnung 
des Vergleichswertes richtig waren. Bin hier folgendermaßen vorgegangen:

FCPU = 3.69MHZ
Prescaler Timer = 256 --> 1/(3.69MHZ/256) = ~0.07ms

Der Timer wird also alle 0.07ms angetaktet. Für einen Mittelpunktswert 
von 1,5ms sollten die beiden Werte im ICR1-Register eine Differenz von 
1,5ms/0.07ms = ~22 aufweisen, was meinen Vergleichwert bildet. Ist die 
Impulslänge kürzer oder länger bin ich jeweils unter bzw. über diesem 
Wert.

Jetzt irritiert mich der Wert von 6 aber, bei dem es funktioniert!? Wo 
ist mein Denkfehler? :/

Hier nochmal der Code bei dem es geht:
1
ISR( TIMER1_CAPT_vect )
2
{
3
  
4
    if(setMode == 0)
5
    {
6
    signal_1st = ICR1;
7
  
8
      TCCR1B ^= (1<<ICES1);  // Capture-Flanke umschalten
9
      setMode = 1;      // Modus "fallende Flanke"
10
    }
11
12
    else if (setMode == 1)
13
    {
14
    signal_2nd = ICR1;
15
    signalEnd = 1;
16
    }
17
18
}
19
20
int main(void)
21
{
22
23
24
  DDRB = 0x00;        // Port B als Input (inkl. PB0 = ICP1)
25
  DDRC = 0xff;        // Port C als Ausgang
26
  PORTC = 0xff;
27
  
28
  
29
  TIMSK |= (1<<TICIE1);           // Capture Interrupt enable
30
  TCCR1B |= (1<<CS12) | (1<<ICES1); // Prescaler 256 Timer 1, Capture bei steigender Flanke
31
32
  sei();                          // Globale Interruptfreigabe
33
34
35
    for(;;)
36
    {  
37
    
38
    
39
    if(signalEnd != 0)
40
    {
41
    signalLength = (signal_2nd - signal_1st);
42
43
      if(signalLength < 6)
44
      {    
45
        PORTC = 0xFE;
46
      }
47
      else
48
      {
49
        PORTC = 0xFF;
50
      }
51
      
52
        
53
      TCCR1B ^= (1<<ICES1);  // Capture-Flanke umschalten
54
        setMode = 0;      // Modus "steigende Flanke"
55
      signalEnd = 0;
56
57
58
    }
59
60
    }
61
}

Danke euch!
Gruß
Stefan

von Kurt H. (Firma: KHTronik) (kurtharders)


Lesenswert?

Hallo Stefan,
wenn ich die 8MHz aus dem Quellcode einsetze, erhalte 32µs Timer-Takt. 
Das sind dann für 1,5ms 47 Takte. Im zweiten Posting schreibst Du was 
von 3,69MHz? Die 6 als Vergleichswert würde bei 1MHz Takt passen. Fuses 
falsch?

Du setzt in Deinem Programm an keiner Stelle den Timer auf 0. Somit 
wirst Du hin und wieder statt einer sauberen Differenz der Zeitpunkte 
aus dem Capture einen negativen Wert erhalten. Ich würde nach der 
fallenden Flanke den Timer auf 0 setzen. Er zählt dann in den ca. 20ms 
Wiederholzeit bis ca. 625, also noch locker im Bereich für 16 Bit.

Welchen Sinn haben die cli in der ISR und der zweite sei im 
Hauptprogramm?

Grüße, Kurt

von Stefan S. (novafly)


Lesenswert?

Hallo Kurt,
das Wort "Fuses" löst gerade ein komisches Gefühl bei mir aus... Ich 
glaub, ich hab das total verpennt :/ Kaum umgstellt, geht das Ganze 
arg!

Anfangs habe ich einen 8MHz Quarz auf dem STK500 getestet, deswegen 
bezog sich der Wert auf 8 MHz. Standardmäßig werde ich aber mit den 
3,69MHz vom STK500 arbeiten, zumindestens bis alles läuft. Die Info hab 
ich euch verschwiegen :) Aber wenn natürlich in den Fuses keine 
Taktquelle eingstellt ist......

Mit cli() und sei() hatte ich vor die Interruptanforderungen während der 
Berechnung zu sperren, was aber völliger Blödsinn ist, da die Impulse in 
einem Abstand von ca. 20ms kommen und das dem Controller ja locker 
ausreicht die Berechnung durchzuführen.

Bezüglich der negativen Werte hatte ich vor das Overflow-Flag 
auszuwerten und dann entsprechend einen Offsetwert zu den beiden 
ICR1-Werten hinzuzurechnen. Den Timer einfach zu nullen wäre natürlich 
auch eine simple Idee. Ich werd mal sehen, wie ich das schlussendlich 
lösen werde.

Danke dir Kurt, manchmal denkt man an die simpelsten Sachen nicht mehr 
... :/

Viele Grüße
Stefan

von Hannes L. (hannes)


Lesenswert?

Stefan S. schrieb:
> Bezüglich der negativen Werte hatte ich vor das Overflow-Flag
> auszuwerten

Das überlass mal den ASM-Leuten... ^^

> und dann entsprechend einen Offsetwert zu den beiden
> ICR1-Werten hinzuzurechnen.

Außerdem gibt es da (zumindes in ASM) keine negativen Werte. Man 
subtrahiert den Zeitstempel des Impulsbeginns (der älter und daher 
kleiner ist) vom Zeitstempel des Impulsendes (der ja neuer ist) und 
erhält die Impulsbreite. Da beide 16-Bit-Werte als Ring (also kein 
Zahlenstrahl) zu verstehen sind, heben sich die Überträge auf.

> Den Timer einfach zu nullen wäre natürlich
> auch eine simple Idee.

Dachte ich zu Tiny12/15-Zeiten auch mal. Doch meist bietet es sich an, 
neben dem ICP-Interrupt noch eine oder beide Compare-Spuren zu nutzen, 
und da muss man den Timer frei durchlaufen lassen.

> Ich werd mal sehen, wie ich das schlussendlich
> lösen werde.

Mach' wie Du denkst, ich will Dir da nichts vorschreiben, lediglich 
Denkanstöße geben...

...

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.