PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : RC-Kanal einlesen, Code in C



marius86
20.09.2008, 18:18
Hallo,
nach dem Vorlageprogramm in Bascom habe ich mal ein C-Programm geschrieben, um einen RC-Kanal einzulesen und je nach Signal einen Pin zu schalten (hier PA2).

Irgendwie tut sich jedoch nichts, vielleicht könnt ihr mal drüberschauen?

Den Timer habe ich mit Prescale 8 gestartet, sodass er im 2ms-Abtastzeitraum von 0-250 laufen sollte, was den 8bit-Bereich ja ziemlich gut ausnutzt.




#include <avr/io.h>
#include <avr/interrupt.h>

ISR(INT0_vect)
{
int reading;
int rc_value;

if(reading == 0)
{
TCNT0 = 0;
reading = 1;
}
else
{
rc_value = TCNT0;
TCNT0 = 0;
reading = 0;
if(rc_value > 128)
{
PORTA = (0<<PA2);
}
else
{
PORTA = (1<<PA2);
}
}
}

int main(void)
{
DDRA = 0xFF;
PORTA = 0xFF;
DDRB = 0x00;

int reading = 0;
int rc_value = 0;

TCCR0B = (1<<CS01);
MCUCR = (1<<ISC00);
sei();

while(1)
{
}
}

sternst
20.09.2008, 19:13
ISR(INT0_vect)
{
int reading;
int rc_value;

// An dieser Stelle hat reading immer einen eher zufälligen Inhalt.

if(reading == 0)
...

int reading;
->
static int reading;

Wozu sollen reading und rc_value in main gut sein?
Ich glaube, du hast bei Variablen noch ein Verständnisproblem global <-> lokal.

Außerdem bekommst du doch garantiert eine "is used uninitialized in this function"-Warnung. Warnungen sollte man nicht einfach ignorieren, und bei Fragen im Forum auch mitposten.

marius86
20.09.2008, 19:17
ah ok, das mit dem Static wusste ich nicht. Mich hat das schon ein bisschen verwirrt und ich hab mich die ganze Zeit gefragt, ob nicht gerade die doppelte Deklaration a) sinnlos ist und b) unvorhersehbare Zustände hervorruft.

Ich werds mal mit static int probieren, nur in der ISR und nicht in main.

Vielen Dank schonmal!

sternst
20.09.2008, 19:20
Ich werds mal mit static int probieren, nur in der ISR und nicht in main.
Wie ich bereits andeutete, die beiden Definitionen in der main sind einfach nur Nonsens.

marius86
20.09.2008, 19:28
Ok, leider tut sich immer noch nichts. Woran könnte es noch liegen?

Ich habe den INT0-Pin auf GND gelegt per 10k-Widerstand. Die RC-Signale sind soweit ich weiß positiv, von daher müsste das doch passen!?

sternst
20.09.2008, 19:37
Wie sieht der Code jetzt aus?
Welcher AVR eigentlich?

marius86
20.09.2008, 19:40
Ups, sorry. Ist ein Attiny24.

Hier der aktuelle Code:




#include <avr/io.h>
#include <avr/interrupt.h>

ISR(INT0_vect)
{
static int reading;
static int rc_value;

if(reading == 0)
{
TCNT0 = 0;
reading = 1;
}
else
{
rc_value = TCNT0;
TCNT0 = 0;
reading = 0;
if(rc_value > 128)
{
PORTA = (0<<PA2);
}
else
{
PORTA = (1<<PA2);
}
}
}

int main(void)
{
DDRA = 0xFF;
PORTA = 0xFF;
DDRB = 0x00;

TCCR0B = (1<<CS01);
MCUCR = (1<<ISC00);
sei();

while(1)
{
}
}

sternst
20.09.2008, 19:54
1) Wie soll an INT0 eine Flanke detektiert werden, wenn der Pin als Ausgang konfiguriert ist?

2)Du machst den Anfang der Messung nicht davon abhängig, was für eine Flanke das denn gerade war. Wenn das Programm während eines Impulses startet, misst du laufend nur die Pausen zwischen den Impulsen.

marius86
20.09.2008, 19:58
zu 1): Der Pin INT0 ist an Port B, welcher mit DDRB = 0x00; als Eingang angelegt ist.

zu 2): Da hast du Recht, ist mir nicht bewusst gewesen. Wie kann ich das korrigieren? Praktischerweise erst die Schaltung starten, dann den RC-Empfänger anschließen? Oder geht das auch per Software, die Flanke zu erkennen??

fhs
20.09.2008, 20:11
Hallo,

lies mal im Datenblatt Absatz 9.3.1 (zum Thema MCUCR) -- dann weißt Du, wie man auf steigende und fallende Flanken reagiert. Für ein positives Signal würde man also zunächst den INT0 so einstellen, dass er auf die steigende Flanke reagiert. Beim Interrupt dann den Timer starten und die INT0-Erkennung auf die fallende Flanke umstellen. Beim nächsten INT0 (fallende Flanke!) den Timer anhalten/auswerten. Da capo.

Es ist ratsam, zunächst ein solches Konzept zu erstellen (und im Kopf/auf Papier durchzuspielen), ehe man Code schreibt.

Gruß

Fred

sternst
20.09.2008, 20:14
zu 1): Der Pin INT0 ist an Port B
Ups, sorry, mit PCINT0 verwechselt.


Oder geht das auch per Software, die Flanke zu erkennen??
Klar, du musst im Interrupt doch nur nachschauen, ob der Pin gerade 0 oder 1 ist. Bei 1 war es eine steigende Flanke, bei 0 eine fallende. Dann kannst du dir auch gleich die Variable reading sparen.

PS: rc_value muss nicht static sein.

marius86
20.09.2008, 20:17
Vielen dank, ich werde mal schauen ob ich das jetzt hinbekomme. Was mich wundert ist, dass ich ja eigentlich nur das Beispielprogramm in C umgeschrieben habe, in Bascom hat es ja anscheinend so funktioniert...

marius86
20.09.2008, 20:41
Ok, der Code sieht jetzt wie folgt aus:



ISR(INT0_vect)
{
static int reading;
int rc_value;

if(reading == 0)
{
TCNT0 = 0;
MCUCR = (0<<ISC00) | (1<<ISC01);
reading = 1;
}
else
{
rc_value = TCNT0;
TCNT0 = 0;
MCUCR = (1<<ISC00) | (1<<ISC01);
reading = 0;
}

if(rc_value > 200)
{
PORTA = (1<<PA2);
}
else
{
PORTA = (0<<PA2);
}

}



Dennoch tut sich nach wie vor nichts. Die Schaltung ist komplett durchgecheckt, wenn ich PA2 manuell mit einem Testprogramm schalte, leuchtet die LED.

Irgendwie hängt es immer noch an der Signalabfrage... Noch eine Idee?

McJenso
20.09.2008, 20:57
Hallo,

schau dir mal das Register GIMSK an. Ich kann das in deinem Programm nicht finden.

Gruß

Jens

marius86
20.09.2008, 20:59
Hi,
reicht es nicht im Hauptprogramm mit sei() alle Interrupts zu aktivieren? Hatte jetzt nur noch die ISR gepostet, main war ja soweit anscheinend in Ordnung (siehe weiter oben).

McJenso
20.09.2008, 21:06
Hallo,

wenn das so währe, müssten dann nach sei() ja wirklich alle Interrupts aktiv sein. Auch die, die du nicht benutzt. Ohne ISR würde das unweigerlich zum Reset führen. Du musst schon jeden Interrupt mit seinem Register freischalten (hier in GIMSK) und du musst die Interrupts global erlauben (sei()).

Gruß

Jens

marius86
20.09.2008, 21:13
Ohhhh, du hast Recht. Hab bisher nur wenig mit Interrupts gearbeitet....

Also erst Bit setzen bzw Interrupt aktivieren und dann noch freigeben... Danke!

Und: ES GEHT!!!! Vielen vielen Dank an alle!

Einziges Problem jetzt: Die Led am Ausgang PA2 flackert ziemlich, auch wenn ich die Senderantenne ausfahre... Liegt das Flackern jetzt an meiner "Reagiergrenze" der Timer-if-Abfrage zum Schalten des Ausgangs? Eigentlich habe ich ja nur eine ja/nein-Abfrage drin, sodass es eigentlich nicht flackern sollte?

Wäre genial wenn ich das jetzt noch in den Griff bekomme!! :)

marius86
20.09.2008, 21:21
Ok, es liegt teilweise daran, dass ich zeitgleich noch andere Aktionen mache, z.b. blinkende LEDs und dass ich dort dann mit _delay_ms Verzögerungen habe, die das Programm dann wartet und wo der Interrupt anscheinend nicht springen kann? Geht ja eigentlich nur nach jeder einzelnen Anweisung. Vielleicht sollte ich die Verzögerungen auf mehrere kürzere Verzögerungen verteilen?


EDIT: Oder vielleicht einfach einen großen Elko, der kurze Aus-Zeiten abfängt? Bei LEDs vielleicht noch möglich, wenn man jetzt aber größere Verbraucher schalten will, bringts das nicht wirklich denke ich...

EDIT2: Beides zeigt keine Wirkung. Weder Verzögerungen auf mehrere Anweisungen verteilen (wird intern wahrscheinlich eh anders abgehandelt durch den Compiler) und Elko mir 330µF (der größte den ich gerade zur Hand habe).

EDIT3: Anscheinend wird auch die ganze Schaltung über den Impuls-Pin des Empfängers mit Strom versorgt, das ist natürlich nicht so erwünscht, wozu habe ich auch den extra Spannungsregler etc. Möchte den Empfänger auch nicht so belasten.. Müsste man irgendwie trennen, vielleicht beeinflusst das alles ungünstig...

Die gesamte Schaltung zieht im Moment 120mA, die Spannungseinbrüche sind mir schleierhaft. Liegt vermutlich wirklich am Code, dass die LEDs flackern...

McJenso
21.09.2008, 09:55
Hallo,

delays werden durch Interrupts unterbrochen. Im Normalfall werden nur die ISR nicht von Interruptsunterbrochen.
Solange du Spannungseinbrüche hast, brauchst du dir um den Code keine Gedanken zu machen. Was passiert, wenn du mit einem Testprogramm die Led im Sekundentakt blinken läst. Funktioniert das mit der aufgebauten Hardware?

Gruß

Jens

marius86
21.09.2008, 10:16
Hi,
Blinken im Sekundentakt geht, allerdings komischerweise nur dann, wenn ich den Sensorpin des Empfängers nicht mit der Schaltung verbinde!? Sobald ich das Empfängersignal in die Schaltung einleite, blitzt die Led nur kurz und auch nicht immer gleich hell.

Wie kann das sein??

Mein Testcode sollte doch eigentlich laufen, unabhängig von der ISR?




while(1)
{
PORTA = (1<<PA1) | (1<<PA0);
_delay_ms(1000);
PORTA = (0<<PA1) | (0<<PA0);
_delay_ms(1000);
}


2 blinkende LEDs an PA1 und PA0...

McJenso
21.09.2008, 10:23
Hallo,

der Controller resetet sich ständig. Es währe schön, wenn du immer das ganze Programm postest. Hast du sei() vor der main noch drinn. Das nimm bitte raus und teste NUR den Code oben, einmal mit Signal, einmal ohne. Damit grenzen wir ein, ob es ein Hardware- oder Softwareproblem ist.

Gruß

Jens

marius86
21.09.2008, 10:29
Hi,
danke, du hast Recht. Ich hatte allerdings nur im main den Part mit den Blitzen rausgenommen und durch Blinken ersetzt.

Hier mal der ganze Code komplett:




#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/wdt.h>

/*void blitz()
{
PORTA = (1<<PA1);
_delay_ms(1000);
PORTA = (0<<PA1);
}

void beacon()
{
PORTA = (1<<PA0);
_delay_ms(1000);
PORTA = (0<<PA0);
}
*/
ISR(INT0_vect)
{
static int reading;
static int rc_value;

if(reading == 0)
{
TCNT0 = 0;
MCUCR = (0<<ISC00) | (1<<ISC01);
reading = 1;
}
else
{
rc_value = TCNT0;
TCNT0 = 0;
MCUCR = (1<<ISC00) | (1<<ISC01);
reading = 0;
}

if(rc_value > 150)
{
PORTA = (1<<PA2);
}
else
{
PORTA = (0<<PA2);
}

}

int main(void)
{
DDRA = 0xFF;
PORTA = 0xFF;
DDRB = 0x00;

TCCR0B = (1<<CS01);
MCUCR = (1<<ISC00) | (1<<ISC01);
GIMSK = (1<<INT0);
//sei();

/* while(1)
{
blitz();
_delay_ms(150);
blitz();
_delay_ms(350);
beacon();
_delay_ms(500);

} */

while(1)
{
PORTA = (1<<PA1) | (1<<PA0);
_delay_ms(1000);
PORTA = (0<<PA1) | (0<<PA0);
_delay_ms(1000);
}
}


So wie er da steht, blinkt die LED, unabhängig vom verbundenen Signal-Pin. sei() ist auskommentiert. Sobald ich die Interrupts wieder erlaube, hat der Signalpin auch auf das Blinken Einfluss.

Ich brauche die Signalleitung bloß an der Isolation anfassen und es fängt wieder an zu blitzen, mal mehr mal weniger hell bzw kurz und lang. Ziemlich doof...

Besserwessi
21.09.2008, 12:33
Das sieht so aus, als würde die Signal leitung Störungen einfangen. Da sind dann wohl kurze Pulse die jedesmal die LED weder ausschalten. Man könnte sich etwas behelfen, indem man bei ganz kurzen pulsen (z.B. RC:value <10) die Messung nochmal startet und die LED nicht ausschaltet. So kurze pulse sollten bei einem RC signal eigentlich nicht vorkommen. Sonst hilft nur eine Hardwarelösung: die Störugen ganz vermeiden.

marius86
21.09.2008, 12:37
Hi,
wie vermeide ich die Störungen per Hardware?
Gruß
Marius

PS: Die Softwarelösung mit Abfrage <10 hilft nicht. Das Verhalten ist total chaotisch, mal leuchten alle Leds einfach dauerhaft, dann flackern sie oder blitzen nur ganz ganz kurz auf in unregelmäßigen Abständen...

Vielleicht wäre es am einfachsten für die Fernsteuerung einen kleinen extra-tiny zu programmieren...

Besserwessi
21.09.2008, 14:29
Zur vermeidung von Störungen sollte man eventuell abgeschrimte Leitungen nehmen und Auf die Masseverbindungen achten. Im einfachsten Fall treffen sich die Masseleitungen alle in einem Punkt.

marius86
21.09.2008, 14:35
Mich verwundert es auch ein wenig, dass die Schaltung selbst dann läuft, wenn ich den Strom am Pluspol trenne. Anscheinend läuft dann der Strom über den Signalpin in den Controller hinein und speist die Schaltung. Das ist doch sehr seltsam oder?

Und das Blinken sollte doch unabhängig vom Interrupt nebenher laufen. Vielleicht nicht 100%ig vom Timing, wenn oft in den Interrupt gesprungen wird, aber ein Blitzen sollte doch garnicht auftreten, da die delay-Anweisungen ja trotzdem abgearbeitet werden sollten!? Verstehs nicht...

McJenso
21.09.2008, 14:57
Hallo,

kannst du zum testen einen Pulldown von, sagen wir mal, 1k einbauen. Dann sag uns bitte deine Fusebits und deine Taktquelle. Wie hast du die Schaltung aufgebaut? Gibt es einen Schaltplan, ein Layout?

Gruß

Jens

marius86
21.09.2008, 15:22
Auf einen Pulldown hätte ich auch selber kommen können... Omann :D An den anderen Ausgängen ist überall einer, nur beim Interrupteingang hab ichs übersehen...

Der 1k ist drin, jetzt reagiert die Leitung nicht mehr auf "anfassen". Wenn mit dem Empfänger verbunden, schalten die anderen LEDs aber nicht im Sekundentakt sondern blitzen sekündlich nur schwach auf.

Einen Schaltplan habe ich leider nicht. Ich habe die AVR-Grundschaltung verwendet mit internem Takt. Davor einen 5V-Spannungsregler mit LM7805 (geht immerhin bis 1A, sollte doch reichen?).

Ansonsten ist nicht viel auf der Platine drauf, an Ausgängen PA0 und PA1 eben je eine Transistor CE-Schaltung (BC517) mit (anfängerhaften) 68-Ohm als Basiswiderstand. 2,2k wären auch gegangen habe ich schon gehört ;) Hab mir das mit meinem Halbwissen aus der E-Technik-Vorlesung bez. Arbeitspunkteinstellung etc ausgerechnet und bin auf 68 gekommen.

Vielleicht sollte ich die mal austauschen gegen die 2,2k?

Alle Ausgänge sind per Pulldown mit 10k auf GND gezogen. Ein 10µ-Elko sitzt am Hochspannungseingang (derzeit 11,5V Lipo-Akku). Sonst nur ein paar 100nF am Spannungsregler und einer direkt am Chip VCC-GND.

Fusebits sind: L: 62, H: DF, e: FF
Also alles standard + internem Takt. Es läuft ja auch alles eigentlich, bis auf die Störungen durch den Signalpin vom Empfänger.

Sollte ich vielleicht den Massenanschluss am Empfängerkanal auch noch mit dem Massenanschluss der Steuerschaltung verbinden? Hab bisher NUR den Signalpin abgegriffen.

Danke und Gruß.

McJenso
21.09.2008, 16:25
Hallo,


Alle Ausgänge sind per Pulldown mit 10k auf GND gezogen.

Wenn du die Ausgänge des Tinys meinst, brauchst du das nicht. Der Controller schaltet eh zwischen Vss und GND.



Fusebits sind: L: 62, H: DF, e: FF

Ich habe jetzt nicht nach gerechnet, hast du berücksichtigt, dass der Takt bei den Fusebit Einstellungen durch 8 geteilt wird?



Sollte ich vielleicht den Massenanschluss am Empfängerkanal auch noch mit dem Massenanschluss der Steuerschaltung verbinden? Hab bisher NUR den Signalpin abgegriffen.


JA, du brauchst unbedingt ein Bezugspotential, immer!



Gruß

Jens

marius86
21.09.2008, 16:30
Ich meine die Ausgänge für die Transistoren. Damit ein definierter Zustand vorliegt, nimmt man doch die Pulldowns? Da ich VCC schalten will, nehme ich einen Pulldown, der GND anlegt, wenn der Pin "aus" ist. So stehts in den Büchern, die ich für die Grundlagen mal durchgearbeitet habe...

Der Takt wird in der Tat geteilt, und zwar von internen 8Mhz auf 1Mhz dann. Die delay-Funktionen passen sich ja eh automatisch an und für die Timerskalierung habe ich auch mit 1Mhz gerechnet. Das Auslesen des RC-Wertes klappt auch wunderbar.

Zum Bezugspotential: Im Grunde sind ja Empfänger und Schaltung beide an der Akkus-Masse angeschlossen. Bloß geht der Empfänger vorher noch durch den Motorregler (BEC). Eine direkte GND-Leitung ist vielleicht effektiver, ich werde das direkt mal testen.

Gruß
Marius

marius86
21.09.2008, 16:50
Habe jetzt die Masseleitung gelegt. Hat sich nichts geändert.

Aufgefallen ist mir, dass die per RC geschalteten LEDs jede Sekunde kurz ausgehen (ansonsten beständig an) und die 1-Sek-Blink-Leds alle 2 Sek kurz aufblitzen.

Sobald ich den Signalpin aus dem Empfänger ziehe, blinken die Blink-Leds wie programmiert im 1sek-Rhythmus. Die RC-Leds sind aus (entsprechend der Programmierung).

Hier der aktuelle Code nochmal:




#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/wdt.h>

/*void blitz()
{
PORTA = (1<<PA1);
_delay_ms(1000);
PORTA = (0<<PA1);
}

void beacon()
{
PORTA = (1<<PA0);
_delay_ms(1000);
PORTA = (0<<PA0);
}
*/
ISR(INT0_vect)
{
static int reading;
static int rc_value;

if(reading == 0)
{
TCNT0 = 0;
MCUCR = (0<<ISC00) | (1<<ISC01);
reading = 1;
}
else
{
rc_value = TCNT0;
TCNT0 = 0;
MCUCR = (1<<ISC00) | (1<<ISC01);
reading = 0;
}

if(rc_value > 150)
{
PORTA = (1<<PA2);
}
else
{
PORTA = (0<<PA2);
}

}

int main(void)
{
DDRA = 0xFF;
PORTA = 0xFF;
DDRB = 0x00;

TCCR0B = (1<<CS01);
MCUCR = (1<<ISC00) | (1<<ISC01);
GIMSK = (1<<INT0);
sei();

/* while(1)
{
blitz();
_delay_ms(150);
blitz();
_delay_ms(350);
beacon();
_delay_ms(500);

}
*/

while(1)
{
PORTA = (1<<PA0) | (1<<PA1);
_delay_ms(1000);
PORTA = (0<<PA0) | (0<<PA1);
_delay_ms(1000);
}
}

McJenso
21.09.2008, 16:52
Hallo,

der Tiny schaltet nicht einfach die 5V am Ausgang ein und aus. Er schaltet zwischen 5V und GND hin und her. Wenn der Ausgang Logisch 0 ist, hat der Pin GND Potential und hängt nicht wie bei einem Schalter in der Luft.

Okay, ich hatte es jetzt so verstanden, dass du gar keine GND-Verbindung hast.

So, habe mir noch einmal deinen Code angeschaut. Da haben wir etwas fürchterlich übersehen. :-b

Wenn du
PORTA = (1<<PA1);
schreibst wird der komplette Port A geändert, auch PA2. Du könntest auch PORTA = 2; schreiben.

Bei
PORTA |= (1<<PA1);
wird nur PA1 geändert. Alle anderen Ausgänge an A bleiben unverändert.
Da gleiche gilt für
PORTA = (0<<PA2);
Hier könntest du auch gleich PORTA = 0; schreiben.
PORT &=~(1<<PA2);
Setzt wieder nur PA2 auf low.


Gruß

Jens

marius86
21.09.2008, 17:04
Ohhhhhh verdammt :D

Also:

PORTA |= (1<<PA1); um NUR PA1 auf 1 zu setzen, den Rest unberührt zu lassen.

PORTA &=~(1<<PA1); um NUR PA1 wieder auf 0 zu setzen.

Und das dann analog mit den anderen Pins.

Richtig? Werds direkt probieren... Wäre ja phänomenal :D

marius86
21.09.2008, 17:11
DER WAHNSINN!!!

Weiß garnicht wie ich dir danken soll.... Es funktioniert wunderbar!

Endlich :D Schwere Geburt... Was diese kleinen Fehlerchen alles ausmachen können...

Hab sonst immer direkt mit Hexwerten den ganzen Port beschrieben und bisher wenig mit direkten Bitoperationen gearbeitet.

DANKE!!!

marius86
21.09.2008, 17:49
der Tiny schaltet nicht einfach die 5V am Ausgang ein und aus. Er schaltet zwischen 5V und GND hin und her. Wenn der Ausgang Logisch 0 ist, hat der Pin GND Potential und hängt nicht wie bei einem Schalter in der Luft.



Das ist beim mega32 aber anders oder? Dort werden die Pulldowns/ups doch gebraucht!?

McJenso
21.09.2008, 18:01
Hallo,

nö. Wenn der Pin als Ausgang konfiguriert ist, kann er nur Vcc oder GND annehmen.
Wenn der Pin Eingang ist, kann er Tri-state (kein Potential) oder über zuschaltbaren internen Pullup Vcc annehmen. Wird ein Pulldown benötigt, muss dieser extern angeschlossen werden.

Das ist bei allen mir bekannten AtMegas und AtTinys der Fall. Du findest die Ports unter I/O Ports im entsprechenden Datenblatt beschrieben.

Gruß

Jens

marius86
21.09.2008, 18:04
Ok, also nur bei Eingängen... Macht irgendwo auch Sinn... Wieder was gelernt... :)

Danke!