Forum: Projekte & Code Source: RC5-Empfang, interruptbasiert, Manchester


von Haemi (Gast)


Lesenswert?

Salut,

da ich selbst bei der Suche nach einem wirklich guten
interruptbasierten, empfangssicheren RC5-Dekodierungsalgorithmus für
AVR in C nicht fündig geworden bin, hier nun mein Source. Auf daß
jemand da draußen ihn gebrauchen kann.
In der aktuellen Version ist er für INT1 und Timer0 konfiguriert.
Braucht evtl. etwas Anpassung bei den Ports. Die Timerkonfiguration
(Prescaler+Ladewerte) geschieht hingegen schon völlig automatisch über
den Präprozessor. Einfach XTAL eintragen und gut is. :))

Da die aktuelle Version noch nicht sonderlich störfest gegen EMV ist
(allerdings gegen IR-Fehlempfang!) und ihr somit auf Updates hoffen
könnt (in meinem Projekt setze ich schon die entprellte Version ein),
hier die URL. Besser als eine bald veraltete Version anzuhängen.
http://markh.de/rc5dec/

Schaut auf die Seite, um die aktuellen Features nachzulesen. Die
Distanz, aus der Signale empfangen werden können, ist ein Vielfaches
der Distanz aus der AVR410 AppNote. Bin selbst ehrlichgesagt sehr
beeindruckt, wieviel doch noch am TSOP ankommt, was von der AVR410
nicht mal ansatzweise erkannt wurde. Fehlerkannte Tasten hatte ich bis
jetzt noch nie. Manchester-Codierung wird voll ausgenutzt.

Viel Spaß damit!

Mark

von Haemi (Gast)


Lesenswert?

Salut nochmal,

ok, hab mir jetzt einfach die paar Minuten Zeit genommen und den neuen
Code extrahiert. Der filtert jetzt also auch EMV-Störungen aus und kann
daher z.B. prima ohne Hardware-Tiefpaß an einem Mehrkanal-Dimmer
genutzt werden. :) URL s. oben.

Have fun!
Mark

von Alexander Niessen (Gast)


Lesenswert?

Guten Morgen schlürfamkaffee
Der Code kam mir gerade recht.
Ich bin nämlich dabei eine (dimmbare) Deckenleuchte zu bauen, welche
drei Lichkreise beinhaltet.
Soviel zur Einführung ...

Ich habe deinen Code genommen und in AVR-Studio3.56 ein neues Projekt
angelegt.
Als MC benutze ich einen AT90S2313.

Nun zu meinem Problem:
Wenn ich make all mache bekomme ich folgenden Fehler:
'GICR' undeclared

Was kann ich bloss tun, um das Proggi fehlerfrei zu übersetzen ?

Mit freundlichen Gruessen,
Alex

von Haemi (Gast)


Lesenswert?

Salut Alex,

das GICR heißt beim 2313 noch GIMSK. Der Rest sollte passen. Falls es
sonst noch Probleme gibt, kannst Du mir gern bescheidgeben. Dann kann
ich den Source etwas portabler gestalten. Danke!

Viel Spaß damit.
Mark

von Alexander Niessen (Gast)


Lesenswert?

Nachdem ich GICR durch GIMSK ersetzt habe wurde das Programm fehlerfrei
compiliert.
Danke für den Hinweis.
Nach ein wenig Fummelei mit 'RC5RESAMPLE' (ist bei mir 37)
funktioniert nun alles prima.

Grüße,
Alex

von Haemi (Gast)


Lesenswert?

Salut,

das freut mich. :) Den resampling-Wert kannst Du zur Sicherheit auch
etwas höher ansetzen, aber damit verlängert sich natürlich die
Ausführung der ISR... Einen konkreten Wert gibt es nicht. Sind alles
Erfahrungswerte, die sich je nach Umgebung (EMV) einstellen.

Weiterhin viel Erfolg beim Basteln!
Mark

von Peter D. (peda)


Lesenswert?

@Mark,

"Da die aktuelle Version noch nicht sonderlich störfest gegen EMV
ist"

Ja, da rede ich mir immer schon den Mund fusselig, daß der externe
Interrupt eben äußerst ungünstig für alles lahmarschige oder
störverseuchte ist.

Und aus Sicht des AVR ist lahmarschig nicht nur Tastenentprellen oder
DCF77 sondern eben auch RC5.

Also Leute, macht euch das Leben einfach, vergeßt schnell den ganzen
externen Interrupt Scheiß und pollt sowas immmer mit nem Timer.


Anbei meine schon bekannte RC5 Routine aber jetzt von 8051 portiert
nach AVR.

Da der AVR ja keine Bitvariablen kennt, ist es etwas unübersichtlicher
geworden. Aber da der Mega8 ja riesigen Speicher hat, den kein Mensch
je im Leben braucht, habe ich mir den Luxus erlaubt für Bitvariablen
jeweils ein komplettes Byte zu verschwenden.

Etwas Augenmerk möchte ich noch auf diesen Code lenken:

cli();
i = rc5_data;
rc5_data = 0;
sei();

Da rc5_data eine 16-Bit Variable ist, benötigt der AVR zum Auslesen 2
Befehle. Da die Variable jedoch vom RC5-Interrrupt gesetzt wird, muß
man den Zugriff mit cli() und sei() kapseln. Sonst kann es passieren,
daß ein Byte 0 ist, wenn der Interrupt genau dazwischen haut.


Mark, es würde mich riesig freuen, wenn Du mal meine Routine testen
könntest in Bezug EMV und Fehlempfang. Denn nur Du weißt ja, wo bei Dir
die Probleme aufgetreten waren.


Peter

von Peter D. (peda)


Angehängte Dateien:

Lesenswert?

Irgendwie wurde das Posting mehrfach empfangen, aber der Anhang nicht
übertragen. Anbei nun der Anhang.


Peter

von Haemi (Gast)


Lesenswert?

Salut,

@peter:
werde Deinen Code bei Gelegenheit mal testen. Ich verwende den ext.
Int. auch nur zur Synchronisation. Das mit dem 16bit-Register stimmt
natürlich. Das sollte ich in dem Beispielcode nicht unterschlagen.

Grüße,
Mark

von Andreas Turban (Gast)


Lesenswert?

Hallo Alexander Niessen
Hey braucht man für den Code und einem at90s2313 noch weitere
Anpassungen?
Da bei mir der Code nicht läuft und ich weiß nicht genau warum?


Schon mal danke.

Viele Grüße
Andreas Turban

von Alexander Niessen (Gast)


Lesenswert?

Nachdem ich Haemis Rat befolgt hatte lief alles prima.
Aber das hab' ich oben ja schonmal geschrieben.

Was heisst denn: 'Da bei mir der Code nicht läuft' im Klartext ?
Wird er nichtmal fehlerfrei compiliert oder hast du den 2313 geflasht
und es funktioniert lediglich nicht so ganz nach deinen Vorstellungen
?
Schreib' das mal was präziser bitte.

Mit freundlichen Gruessen,
Alex

von Andreas Turban (Gast)


Lesenswert?

Hallo
Es geht alles wunderbar es erstellen, flashen, das Problem ist, dass
ich hald keine IR-Signal empfange. Also keinen Command bekomme, aber
der Interruppt löst aus und auch der Timer läuft, aber es geht hald
nicht ich weiß ja auch nicht warum.
Der Tsop funktoniert, und auch mein At90s2313.

von Alexander Niessen (Gast)


Angehängte Dateien:

Lesenswert?

Hier mal mein Testproggi.
Damit kannst du über eine TV-RC5-Fernbedienung mit
den Nummerntasten 1 & 2 eine LED ein- bzw ausschalten.
Mit Lautstärke lauter bzw leiser ist die LED dimmbar.
Die LED kommt an PB3 dran. TSOP wie gehabt.
Im Source sind einige '#defines' zum dimmen (z.B. Schrittweite).
Guck halt mal rein. Ich hoffe es hilft dir weiter.

Hast du mit dem Wert von 'RC5RESAMPLE' schon
herumexperimentiert ?

MfG,
Alex

von emax (Gast)


Angehängte Dateien:

Lesenswert?

Hallo Leute!

Bin neu bei den AVRs, sind tolle Dinger, alles dran. Komme aus der 8051
Ecke.

Hab mir in der letzten Woche mal so zusammengesucht was man so braucht,
und mir ein AVRCtrl-Board zusammengelötet.

Hier meine RC5-Version. Braucht keine Interrupts, ausser einen
500µSec-Timer (in dem man dann auch gleich alles mögliche andere
erledigen kann, dazu später mehr).

Läuft ganz gut, keine Resamples eingebaut, wäre aber einfach.

ACHTUNG: läuft z.Zt. nur ohne Optimierung ( -O0 ), warum weiss ich noch
nicht, bin halt neu auf dem avr-gcc.

Compiler: avr-gcc
SysClock: 8MHZ
CPU: at90s8535

Noch was: Dateianhang ist UNIX-Ascii, also für DOSen muss LF in CRLF
geändert werden.

Bis denne
emax.

von Andy (Gast)


Lesenswert?

"ACHTUNG: läuft z.Zt. nur ohne Optimierung ( -O0 ), warum weiss ich
noch
nicht, bin halt neu auf dem avr-gcc."

Du meinst wohl, Du bist neu mit "C"... ;)

"volatile" lässt grüssen... ;)

von emax (Gast)


Lesenswert?

Hm, eher nicht. Verdiene mein Geld seit 11 Jahren mit C ...

Allerdings auf Solaris, AIX, Linux....

Danke für den gutgemeinten Tip mit "volatile", aber so neu ist das
nun auch wieder nicht. Und als Antwort im Grunde auch etwas
undifferenziert.

Natürlich kann ich mit volatile alle Variablen-Optimierungen
kaltstellen. Aber genau das will ich ja nicht: wozu verwende ich einen
"optimizing" Compiler? Für "optimalen" Programmcode. Und wenn ich
den mit volatile zwar "sicher" mache, aber im Grunde nicht kapiert
habe, WELCHE Variablen mir hier Probleme machen, dann hab ich im Grunde
was gemacht, was ich nicht verstanden habe.

Insofern habe ich mit "weiss ich noch nicht" nur gemeint, dass ich
erst noch schauen möchte, WELCHE der Variablen ich der Speicherklasse
"volatile" zuordnen sollte. Erst dann kann ich sinnvoll kompilieren,
denn der Compiler SOLL JA DURCHAUS auch die Verwendung von Variablen
optimieren. Alles andere ist dümmlich; "hau mi'm Hammer drauf, wird
dann schon fest sein".

Und nochwas: "neu in C" ist ohnehin eine unlogische Schlussfolgerung.
Es ist durchaus Compilerabhängig, was ein Mikroprozessor mit einem
Programm so veranstaltet. Ich weiss nun nicht, ob Codevision oder die
anderen Compiler auch alle "volatile" für soche Probleme benötigen,
aber beim Keil C-Compiler für die 8051-Serie beispielsweise sieht das
i.d.R. ganz anders aus: dessen Optimierungen sind derart genial, dass
"volatile" kaum verwendet werden muss (ich kann mich genaugenommen
gar nicht erinnern, es jemals verwendet zu haben, trotz maximalster
Optimierungsstufe).

Wie auch immer. Vielleicht gibts ja zum Verfahren selbst mal nen
Kommentar. Bis jetzt habe ich jedefalls keine einfachere und klarere
Routine zur Dekodierung des RC5-Signals gesehen. Die wilde Hin- und
Herschalterei zwischen unterschiedlichen Flankentriggerungen ist mir
persönlich jedenfalls etwas suspekt. Ausserdem verballert man da so
gleich zwei Interrupts. Auch Laufzeitmässig UND Codegrössenmässig ist
meine Lösung eher anspruchslos. Was ich noch testen will, ist die
Prozessorbelastung.

bis denne
emax.

von Andy (Gast)


Lesenswert?

Naja, volatile sind alle Variablen, die parallel bearbeitet werden
können, also beispielsweise in Interrupts. Bei Dir sind das halt die
globalen RC5-Variablen. Da bekommst Du natürlich Probleme in der
main-Routine.
Was soll denn die Count-Variable?

von emax (Gast)


Lesenswert?

Die Count Variable kannste löschen, ist ein Überbleibsel.
Und wenn du den OCR2-Wert auf 124 änderst, hast Du eine exakte 500 µSec
Zeitbasis. Nur so als Nachtrag.

Und weil das mit der "parallel"-Bearbeitung ein bischen schwammig
ist, hier mal so zum Spass die präzise Definition für "volatile":

"A volatile qualified type indicates that agencies unknown to the
translator can access or alter the value stored in the data object. The
translator can assume that is has complete control of all data objects
that do not have volatile qualified types..."

Quelle: "ANSI and ISO Standard C, Programmer's Reference"

Soweit ist das klar.

Nur hab' ich mir darüber für dieses Programm einfach noch keine
Gedanken gemacht, thats all. Klar, die Rc5-Variablen sind betroffen.
Aber was ist mit der Mode-Variablen? Die müsste auch betroffen sein...

Hierzu ist es sicher sinnvoll, eine einfache, verbale Formel dafür zu
finden, welche Variablen grundsätzlich "volatile" definiert werden
sollten.

Ich denke, es sind alle diejenigen Variablen, die ausschliesslich in
Funktionen geändert bzw. benutzt werden, deren Aufrufe für den Compiler
nicht ersichtlich sind.

Allerdings hakt diese Definition. Denn genaugenommen müsste der
Compiler deshalb auch die gesamten SIGNAL-Routinen wegoptimieren: sie
werden nirgendwo explizit aufgerufen.

Ich suche also immer noch nach einer eindeutigen Definition für die
Frage: "welche Variablen müssen grundsätzlich volatile definiert
werden?"

Das wäre einen neuen Thread wert.

e.

von Johannes (Gast)


Lesenswert?

"Hm, eher nicht. Verdiene mein Geld seit 11 Jahren mit C ...
Allerdings auf Solaris, AIX, Linux...."

Microsoft verdient auch seit ewigen Jahren sein Geld erfolgreich mit
Betriebssystemen, was dabei rumkommt, sieht man gerade wieder an der
Wurmwelle... Ist aber nicht gegen Dich.

Was den Keil-C51 angeht, so kann ich mir ehrlich gesagt nicht
vorstellen, dass Du volatile nicht gebraucht hast. Seit wann kann denn
ein Compiler hellsehen? Insbesondere beim Zugriff auf externe Hardware
kommt man doch nicht ohne aus. Da werden sonst haufenweise Zugriffe
wegoptimiert. Das ist beim Keil nicht anders als beim Gnu-C.

Was die Signal-Routinen angeht, so ist das in der Tat eine
Implementationssache, da sowas ja bewusst aus dem C-Standard
herausgehalten wurde, da das ja eine Hardwaresache ist, die nichts mehr
mit der Sprache zu tun hat.
Der Compiler/Linker behandelt das deshalb gesondert.

von emax (Gast)


Lesenswert?

Das hat mit Hellseherei gar nix zu tun.
Ein Interrupthandler wird beim Keil z.B. so codiert:

void timer0() interrupt 1 using 2
{
   foo();
}

Hieran erkennt der Compiler natürlich genau, dass es sich um eine ISR
handelt. Und wenn man nun schlicht davon ausgeht, dass diese Routine
tatsächlich aufgerufen wird (interrupt 1 entspricht einem Call vom 1.
Interruptvektor aus), dann ist es gar nicht schwer, die entsprechenden
Variablen zu "verschonen". Das "using 2" übrigens teilt mit, welche
Registerbank verwendet (und damit gesichert) werden soll.

Zurück zum GCC: würde man per Konvention festlegen, dass alle
"SIGNAL" - Routinen zwangläufig aufgerufen werden (so wie das z.B.
der Keil mit den "interrupt"-gekennzeichneten Routinen auch tut), so
wäre ein "volatile" Schlüsselwort nur in wenigen Fällen nötig. Denn
alle Variablen, die innerhalb SIGNAL und deren "Kinder" verwendet
werden, wären genau so zu behandeln, als würde SIGNAL normal
aufgerufen.

Aber die GCC-Compilerbauer sind in dieser Hinsicht (Gott sei Dank)
Fundamentalisten. Sie durchbrechen ihr Konzept von Compiler und
Optimierern nicht. Denn würden sie das tun, wäre der GCC schon lange
eines qualvollen Todes gestorben.

Vielleicht hilft statt dessen aber auch ein Trick: man mache einen call
auf "SIGNAL", der nie ausgeführt wird, z. B:

if (Minute == 61)
   SIGNAL();

Da SIGNAL folgendermassen definiert ist:

#define SIGNAL(signame)          \
extern "C" void signame(void);        \
void signame (void) _attribute_ ((signal));    \
void signame (void)
#else
#define SIGNAL(signame)          \
void signame (void) _attribute_ ((signal));    \
void signame (void)
#endif

müsste der Aufruf dann z.B. so aussehen:

if (Minute == 61)
   SIG_OUTPUT_COMPARE2();

Man könnte dann an das Ende seiner main-Routine oder besser in einer
Initialisierungsroutine für jeden Interrupt einen solchen Dummy-Call
machen, und wäre alle Zweifel über volatile Variablen los. Ein
entsprechender Kommentar /* make compiler happy */ an der Stelle, und
gut wärs.

Ich denke, ich werd das mal probieren.

Gruesse
e.


PS: die Bemerkung mit MS find ich (als Linux-Mann) dann doch etwas
schmerzhaft. Zumal ich nur auf Andys Irrtum geantwortet hatte ...

von Peter D. (peda)


Lesenswert?

"Was den Keil-C51 angeht, so kann ich mir ehrlich gesagt nicht
vorstellen, dass Du volatile nicht gebraucht hast."

Stimmt aber. Der Keil nimmt automatisch alle I/Os und globale Variablen
als volatile an.

Der Unterschied ist auch nicht so groß, da man beim 8051 viele
Operation  auch direkt auf Speichervariablen machen kann, z.B. ++, --,
=, &=, ^= usw.


Peter

von emax (Gast)


Lesenswert?

Ähem, da muss ich aber widersprechen.

Wenn Du Dir die Memory-Maps und die Listings beim Keil mal genau
anschaust, dann wirst Du feststellen, dass er für nicht verwendete
Variablen auch tatsächlich keinen Platz reserviert. Würde er aber bei
"volatile" Variablen machen (müssen).

Ich denke, er wertet tatsächlich das "interrupt" Schlüsselwort aus.

Aber dieser Compiler ist schon eine Klasse für sich, ich komm da immer
wieder nicht aus dem Staunen, was der für kompakten Code hinlegt. Da
haben Assemblerleute schon schwer gegen anzustinken.

e.

PS: vielleicht möchte ja jemand einen neuen Thread aufmachen, das Thema
wär's wert: "volatile oder nicht volatile?"

von Andy (Gast)


Lesenswert?

@Peter:
Das mit dem Keil ist zumindest bis zur 6er Version nicht richtig (die
7er kenne ich nicht). Externe Hardware (memory mapped) muss man mit
volatile deklarieren, sonst verschwinden z.B. aufeinanderfolgende
Zugriffe. Steht aber auch in der Doku oder Knowledge Base von Keil.

von Peter D. (peda)


Lesenswert?

Ja, Ihr habt recht.

Warscheinlich ist in dem Keil eine KI drin :-)

Eine leere Delayloop wird nicht wegoptimiert. Warscheinlich denkt er,
wenn sich einer schon die Mühe macht, eine leere Loop hinzuschreiben,
dann wird das auch seinen Sinn haben.

Ich stolpere da auch regelmäßig über fehlende "volatile", wenn ich
C51 Code in den AVR übernehme.


"Aber dieser Compiler ist schon eine Klasse für sich, ich komm da
immer
wieder nicht aus dem Staunen, was der für kompakten Code hinlegt. Da
haben Assemblerleute schon schwer gegen anzustinken."

Geht mir absolut genau so.
Ich habe jahrelang assembliert und dachte, daß ich einigermaßen optimal
programmiere.
Aber mit dem Keil, passiert es oft, daß mein alter Assemblercode
langsamer und größer ist.
Der Keil hat also oftmals sogar einen "negativen" Overhead.


Beim WINAVR ist es dagegen viel leichter in Assembler gegenüber C
kleineren Kode zu erzeugen.
Einfach ein paar Register für Interrupts reservieren und schon spart
man massig PUSH und POP.
Dann noch die am häufigsten benutzten Variablen in Registern halten und
schon spart man ne Menge STS und LDS.
Und die switch-Ausdrücke durch einfache Byte-Vergleiche ersetzen.


Peter

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.