Forum: Compiler & IDEs RC Empfänger auswerten, Signal modifizieren und wieder ausgeben


von Jürgen S. (jsachs)


Lesenswert?

Hallo,

ich versuche seit einer weile eine gute Lösung für mein Projekt zu 
finden.
Projektziel:
- Auswerten eines 6 Kanal RC Empfängers ohne Summensignal
- Interne Verarbeitung, Modifikation, etc
- Ausgabe von Mindestens 4 Kanälen, besser 4 Analog Servo Kanälen und 
zusätzliche Schaltausgänge für Licht, Blinker, etc
- Licht mit PWM um die Helligkeit einstellen zu können. Schön wäre noch 
eine Konstantstromquelle und ein integriertes Soundmodul, aber das geht 
aus Speicher gründen nicht.

Mein bisheriger Testaufbau mit einem ATmega16 sieht wie folgt aus:
- Timer CTC mode. Im Interrupt wird ein Port gesampelt und je nach 
Bitzustand dann die Bitlängen Zähler hochgezählt. Wurden alle gezählt 
(Austastlücke) wir ein Flag gesetzt und die mainline wertet die Daten 
aus.
- Im Timer werden auch die Servo Ausgänge gesetzt.

Problem, das kostet zu viel Rechenzeit, die Auflösung ist sehr gering 
und ich habe einen Jitter von +-1 auf dem Signal bei einem Signal von 
+-16.
Mein Gedanke war nun Externe Interrupts zu benutzen, aber hier finde ich 
nicht die richtige CPU. zudem bin ich mir nicht sicher ob es das ganze 
"schneller" macht.
Ich habe auch schon versucht im Interrupt alles schrittweise zu machen.
- Sampeln
- Warten bis Mainline fertig
- Ausgänge setzen
Allerdings ist die Ausgaberate dann sehr gering der Servosignale

Ich denke ich habe mich verrannt und hoffe jemand hat eine Creative 
Idee.
Das ganze soll und ist mit AVR und GCC gelösst werden.

Die bisherige Bastellösung sieht so aus (Sampelt nur einen Servo vom 
Empfänger):
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
4
#include "usart.h"
5
6
#define MAX_CHANNELS (6)
7
8
9
#define CH_IN1 PD3
10
#define CH_IN2 PD3
11
#define CH_IN3 PD3
12
#define CH_IN4 PD3
13
#define CH_IN5 PD3
14
#define CH_IN6 PD3
15
16
#define CH_OUT1 PD6
17
#define CH_OUT2 PD7
18
#define CH_OUT3 PD7
19
#define CH_OUT4 PD7
20
#define CH_OUT5 PD7
21
#define CH_OUT6 PD7
22
23
volatile uint8_t ch_data_in[MAX_CHANNELS];
24
volatile uint8_t ch_data_out[MAX_CHANNELS];
25
volatile uint8_t ch_ready=3;
26
27
volatile uint8_t reward=0;
28
volatile uint8_t rewardold=1;
29
30
void initTimer()
31
{
32
    // Timer 0 konfigurieren
33
    TCCR1B = _BV(WGM12); // CTC Modus
34
    TCCR1B |= _BV(CS10); // Prescaler 1
35
    // ((1000000/8)/1000) = 125
36
    OCR1A = 0xaF;
37
    
38
    // Compare Interrupt erlauben
39
    TIMSK |= _BV(OCIE1A);
40
    
41
    // Global Interrupts aktivieren
42
    sei();
43
    
44
}
45
46
int main()
47
{
48
    ch_ready = 0;
49
    DDRD=_BV(PD6)|_BV(PD5);
50
    usart_init(9600);
51
    initTimer();
52
    sei();
53
    
54
    while(1)
55
    {/*
56
       */ 
57
        
58
        // Serial output is control/test only
59
        //PORTD^=_BV(PD5);
60
        if (ch_ready==1)
61
        {
62
            // Normal control stuff
63
            if (ch_data_in[0] < 60)
64
            {
65
                if (reward != rewardold)
66
                {
67
                    reward = ~reward;
68
                    rewardold=reward;
69
                }
70
            }
71
            else if (ch_data_in[0]>60)
72
                rewardold=~reward;
73
            
74
            
75
            
76
            
77
            usart_write("data=");
78
            for (uint8_t i=0 ; i<MAX_CHANNELS ; i++)
79
            {
80
                usart_write("%i,",ch_data_in[i]);
81
                ch_data_out[i] = ch_data_in[i];
82
                ch_data_in[i]=0;
83
            }
84
            usart_write("\r\n");
85
            ch_ready=2;
86
            
87
            
88
            
89
            
90
        }
91
    }
92
}
93
94
ISR(TIMER1_COMPA_vect)
95
{
96
    uint8_t pin = PIND;
97
    uint8_t out = 0x00;
98
    uint8_t ch_cnt;
99
    
100
    volatile static uint8_t output_cnt;
101
    
102
    // sample if last value already read
103
    if (ch_ready==0)
104
    {
105
        ch_cnt = 0; // count how many channels we finished sampling
106
        
107
        if (pin&_BV(CH_IN1))
108
            ch_data_in[0]++;
109
        else
110
            if (ch_data_in[0]>0) ch_cnt++;
111
112
        if (pin&_BV(CH_IN2))
113
            ch_data_in[1]++;
114
        else
115
            if (ch_data_in[1]>0) ch_cnt++;
116
117
        if (pin&_BV(CH_IN3))
118
            ch_data_in[2]++;
119
        else
120
            if (ch_data_in[2]>0) ch_cnt++;
121
        
122
        if (pin&_BV(CH_IN4))
123
            ch_data_in[3]++;
124
        else
125
            if (ch_data_in[3]>0) ch_cnt++;
126
            
127
        if (pin&_BV(CH_IN5))
128
            ch_data_in[4]++;
129
        else
130
            if (ch_data_in[4]>0) ch_cnt++;
131
            
132
        if (pin&_BV(CH_IN6))
133
            ch_data_in[5]++;
134
        else
135
            if (ch_data_in[5]>0) ch_cnt++;
136
            
137
        // if all sampled, mark as ready
138
        if (ch_cnt==MAX_CHANNELS)
139
            ch_ready=1;
140
    }
141
    // make sure we do not start sample in the midle of a bit
142
    else if (ch_ready==3)
143
    {
144
        if ( (pin & (_BV(CH_IN1)|_BV(CH_IN2)|_BV(CH_IN3)|_BV(CH_IN4)|_BV(CH_IN5)|_BV(CH_IN6)) ) ==0 )
145
        {
146
            ch_ready=0;
147
        }
148
    }
149
    else if (ch_ready==2)
150
    {
151
        // control output ports
152
        if (output_cnt < ch_data_out[0])
153
            out|=_BV(CH_OUT1);
154
        
155
        if (output_cnt < ch_data_out[1])
156
            out|=_BV(CH_OUT2);
157
        
158
        if (output_cnt < ch_data_out[2])
159
            out|=_BV(CH_OUT3);
160
        
161
        if (output_cnt < ch_data_out[3])
162
            out|=_BV(CH_OUT4);
163
        
164
        if (output_cnt < ch_data_out[4])
165
            out|=_BV(CH_OUT5);
166
        
167
        if (output_cnt < ch_data_out[5])
168
            out|=_BV(CH_OUT6);
169
        
170
        if (reward)
171
            out|=_BV(PD5);
172
        
173
        PORTD=out;
174
        
175
        if (output_cnt >= 150)
176
        {
177
            output_cnt=0;
178
            ch_ready=3;
179
        }
180
        else
181
            output_cnt++;
182
    }
183
}

von Oliver (Gast)


Lesenswert?

Viel weniger Rechenzeit wirst du auch mit anderen Ansätzen nicht 
verbrauchen, denn viel mehr als die Eingänge abzufragen machst du ja in 
der ISR nicht. Optimierung eingeschaltet, 16MHz-Quarz, Assembler-Listing 
auf Auffälligkeiten geprüft?

Die neueren Megas bieten zwar die Möglichkeit, 6 Eingänge mit externem 
Interrupt abzufragen, aber auch da musst du dann erst noch den Pin 
abfragen, um rauszufinden, welcher jetzt den Interrupt ausgelöst hat.

Oliver

von Tobyas (Gast)


Lesenswert?

Doch Oliver, Jürgen kann mit anderen Ansätzen viel Rechenzeit sparen!
Und zwar wenn er versteht, dass es eine Welt außerhalb des 
Mikrocontrollers gibt. ;-)

Die einzelnen PLM codierten Kanäle sind (auch heute noch bei den meisten
digitalen RC Empfängern) Zeit-gemultiplext. Sprich die einzelnen Kanäle
kann man einfach per Logikschaltung zum Summensignal zusammensetzten.
Ein einfaches 6x ODER aus Dioden genügt da vollkommen.

Dann brauch nur noch das Summensignal ausgewertet werden. Das geht dann
recht effektiv mit dem Controller.

von Blackbird (Gast)


Lesenswert?

Geht eben nicht! Das "Summensignal" über ein OR-Gatter ist dann einfach 
nur über die Zeit aller Impulse High und während der Synchronpause Low!

Nur wenn die Flanken der einzelnen Impulse stark verschliffen sind, 
entsteht ein ganz kurzer Einbruch des High-Pegels.

Das echte Summensignal aus dem Empfäger (vor dem Decoder) hat jedoch ein 
Austast-Low-Signal von ca. 0,4ms. Dieses in jedem einzelnen Impuls. Das 
ist aber Bestandteil des Impulses!.

Der Decoder braucht diesen Austast-Impuls, um das Schieberegister 
weiterschalten zu können. Damit wird klar, das dieser Austast-Impuls 
Bestandteil des ganzen Impulses sein muß, egal wie lang der 
Austastimpuls auch war. Früher (vor der "Schmalband"-HF-Zeit) waren 
diese Austastimpulse 0,2ms lang. Dann paßten die Fernsteuerkanäle aber 
nicht mehr in das neue 10kHz-Raster. Deshalb die Vergrößerung auf 0,4ms.

Gilt alles nur für herkömmliche PPM-Anlagen, die etwas älter sind.

Blackbird.

von Oliver (Gast)


Lesenswert?

Blackbird schrieb:
> Geht eben nicht!

Geht schon, man braucht halt noch ein Monoflop dazu. Das funktioniert 
aber nur, wenn der Empfänger die einzelnen Servokanäle tatsächlich nach 
althergebrachter Art nacheinander im PPM-Summensignalzeitraster ausgibt. 
Das ist bei aktuellen 2.4Ghz-Systemen nicht immer so.

Oliver

von Blackbird (Gast)


Lesenswert?

das ist das (echte) Summensignal vom Empfängerausgang VOR dem Decoder
    --- ---- ---
----   -    -   -------------

das sind die einzelnen Servo-Impulse aus dem Schieberegister:
   ----
---    ----------------------
       -----
-------     -----------------
            ----
------------    -------------

das ist ein "ORed" Summensignal von den Schieberegisterausgängen:

   -------------
---             -------------

wie soll das ein Monoflop was triggern?


Blackbird

von Oliver (Gast)


Lesenswert?

Da habe ich mich ungenau ausgedrückt. Man braucht für jeden Kanal ein 
Monoflop.

Oliver

von Jürgen S. (jsachs)


Lesenswert?

Tobyas schrieb:
> Doch Oliver, Jürgen kann mit anderen Ansätzen viel Rechenzeit sparen!
> Und zwar wenn er versteht, dass es eine Welt außerhalb des
> Mikrocontrollers gibt. ;-)
>
> Die einzelnen PLM codierten Kanäle sind (auch heute noch bei den meisten
> digitalen RC Empfängern) Zeit-gemultiplext. Sprich die einzelnen Kanäle
> kann man einfach per Logikschaltung zum Summensignal zusammensetzten.
> Ein einfaches 6x ODER aus Dioden genügt da vollkommen.
>
> Dann brauch nur noch das Summensignal ausgewertet werden. Das geht dann
> recht effektiv mit dem Controller.
Das habe ich schon versucht. Wie später auch erwähnt wird, ist bei 
heutigen Epfängern das nicht einfach zu verknüpfen. Meiner hat auch kein 
Summensignal (Siehe anderen Post von mir...) Die Signal von einigen 
Kanälen kommen gleichzeitig.
Daher der Ansatz alle einzeln zu sampeln.

von Tobyas (Gast)


Lesenswert?

@Blackbird: Zum echten Summensignal kann ich leider keine Aussagen 
machen, da sich eh keiner der RC Empfänger Hersteller je an eine Spec 
gehalten hat...

Ich kann nur zu allen hier rumliegenden Empfängern (klassisch und 
2.4GHz) feststellen, dass sie entweder direkt das Summensignal liefern 
oder zeitgemultiplexte PLM Signale mit signifikanten Austastlücken 
liefern.

@Jürgen: Wie ich ja schrieb ist das nicht immer so...
Es ist auf jeden Fall immer Wert sich die Signale vorher mal 
anzuschauen. Entschuldige ich hatte dein anderes Posting nicht gesehen. 
Wenn dein Empfänger die Kanäle gleichzeitig bzw. überlappend liefert ist 
dein Ansatz schon der richtige.

Allerdings halten sich alte Konzepte ziemlich lange. Mein 2.4 GHz
RC Sender / Empfänger benutzt (intern) Telegramme, welche timing-genau
das Summensignal codieren. ;-)

von Jürgen S. (jsachs)


Lesenswert?

@Tobyas: Ich habe auf mein anderes Posting auch nicht hingewiesen, kein 
Problem :-)

Was für einen Empfänger hast Du den ? Ich kann zwar intern an diesem 
EMpfänger auch an eine SPI Schnittstelle, aber dann müsste ich dort die 
ganze Kommunikation mithören. Zwischen der "CPU" und dem Funkmodul. Das 
ist mir doch etwas zu heftig... :-)

Dann könnte ich gleich die CPU im Empfänger und im Sender ersetzen... 
Würde ich aber nur tun, wenn ich von den Funkmodulen noch ein paar über 
hätte, falls was kaputt geht.

von Oliver (Gast)


Lesenswert?

Um mal wieder aufs Thema zurückzukommen:

Wenn du den Servoimpuls in 256 Schritte auflöst (was eigentlich 
ausreicht), müsste deine ISR mit 256KHz aufgerufen werden. Bei 16MHz 
CPU-Takt bleiben dir da brutto 62 Zyklen, netto sind es dann einige 
weniger, da der ganze ISR-Aufruf-Overhead noch davon abgeht.

Da kann man sich die ISR auch ganz sparen, und einfach eine Schleife zur 
Datenerfassung in main laufen lassen. In dieser liest du den Port, auf 
dem die 6 Eingänge liegen, und den Wert des frei durchlaufenden Timer1. 
Wenn sich der Portzustand gegenüber dem vorherigen Schleifendurchlauf 
geändert hat, speicherst du den Portwert und den dazugehörigen 
Zählerstand in je einem Feld ab. Dazu noch eine Zeitüberwachung, die die 
Schleife nach 1.5ms beendet.

Danach hast du gut 18ms Zeit, die Daten auszuwerten, Ausgänge zu setzen, 
etc. Dann alle Daten in den Feldern löschen, und wieder von vorn.

Mit etwas Optimierung solllte die Schleife mit 62 Zyklen machbar sein. 
Wenn dir die Verzögerung von einem Frame bei den Kanälen, die du nur 
durchreichen willst, zu viel ist, kannst du die auch direkt in der 
Schleife ausgeben.

Oliver

von Tobyas (Gast)


Lesenswert?

Hallo Jürgen, der genannte 2.4 GHz Empfänger sollte ein AR6100E 
(Spectrum) sein. Habe zwar nachgeschaut kann aber meine Aufzeichnungen 
leider nicht finden. Ich hatte mir den Empfänger vor ca. 2 Jahre mal 
angeschaut und mich dann für das wired OR mit Dioden entschieden...

Dann halte dich mal an die Tips aus Olivers letztem Posting. Damit 
solltest du zum Ziel kommen.

Viel Erfolg!

von JSachs (Gast)


Lesenswert?

Ich bin im Moment am überlegen das Rad neu zu erfinden.
BTM-222 Bluetooth modul an beiden enden und mit rs232 vom Controller 
füttern. Dann muss ich eben meine Funke umbauen.
In etwa so wie die "Blauzahn" (Für Insider)

von Klaus (Gast)


Lesenswert?

Mir kommt bei dem Problem Input capture oder Counter mit Gate in den 
Sinn.

MfG Klaus

von JSachs (Gast)


Lesenswert?

Das Problem ist hierbei die Rechenzeit die man benötigt wenn man alle 6 
Eingänge gleichzeitig sampled. Da sind einfach nicht genug CPU Zyklen 
frei um noch mehr zu tun.

Wie gesagt überlege ich im moment das Problem (erneut) neu anzugehen mit 
eine Bluetooth Funkstrecke und BTM-222 Modul. Da sist eine Transparente 
RS232 mit ein paar AT Befehlen und gut ist...

Und ich habe volle Felxibilität was über die Leitung geht. Digital von A 
bis Z :-)

von OlliW (Gast)


Lesenswert?

Hallo Zusammen,

für das Problem gibt es durchaus sehr effektive Routinen auf PCINT Basis 
die auch auf nem ollen Atmega8 mit 16MHz mehr als genug Rechenzeit 
lassen um alles mögliche zu machen. Jürgen, sehe dich mal bei dmn 
Multicopter-Volk um. Als ein Beispiel, z.B. den MultiWii-Copter, da gibt 
es den C Code für Arduino frei verfügbar, und wenn du da reinsiehst 
findest du eine gute Lösung. (du kannst dir z.B. den zugehörigen Thread 
bei rcgroups ansehen, der Autor heisst alexinparis und der Thread ist 
immer oben und daher leicht zu finden, es gibt auch eine Wikiseite auf 
der du dir die ganzen Codes runterladen kannst).

Olli

PS: beim den "einfachereren" Spektrum Empfängern (wie dem AR61XXx) ist 
es tatsächlich so dass zwischen den PPM-Servo Impulsen eine Lücke von ca 
70us kommt, so dass das or-Ding geht, leider aber scheint's nicht bei 
Jürgen's Empfänger

von OlliW (Gast)


Lesenswert?

korrektur: ...auf nem ollen Atmega88 ... (der Atmega8 hat ja kein PCInt 
:-))

von Jürgen S. (jsachs)


Lesenswert?

Mit 6 ExtInt wäre ja auch mehr Rechenzeit frei.
Ich muss dann nicht alle 6 Eingänge Sampeln und "Zählen", sondern nur 
bei einer Änderung agieren, z.B. den Timer speichern.
Das war mein Ursprungsgedanke, allerdings tue ich mir noch schwer einen 
Atmel zu finden den ich bei "R" oder "C" bekomme... 100.000 Pins haben 
soll er dann auch ned. ATmega32 mit 6-8 Ext Int wäre Supper...

Humm.
Anderer Seits bekommt man das BTM-222 Modul für so um die 15€.
Ich muss da über Ostern mal etwas dran arbeiten.

Gruss
Jürgen

von Oliver (Gast)


Lesenswert?

Jürgen Sachs schrieb:
> allerdings tue ich mir noch schwer einen
> Atmel zu finden den ich bei "R" oder "C" bekomme...

Beim "C" kuft man keine Atmels.

Schau dich mal hier um:
www.csd-electronics.de

Oliver

von JSachs (Gast)


Lesenswert?

mache ich auch nicht. ist mir zu teuer.

Aber wegen ein oder zwei atmels wo bestellen. neues konto etc. ist mir 
dann doch zuviel aufwand ...

wie gesagt sehe ich mir noch mehr optionen an.

Juergen

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.