PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Probleme mit RC5 Implementierung



thenoone
28.12.2006, 23:08
Hallo zusammen,

ich habe hier ein kleines Problem und zwar möchte ich einen Roboter über eine RC5-Fernbedienung steuern.
Hardware: AT90S2313 / 4MHz

Ich habe den Beispielcode aus dem Wiki verwendet und ein bischen angepasst an den 2313, allerdings funktioniert es nicht :-s



#define F_CPU 4000000

#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdint.h>
#include <stdlib.h> // itoa
#include <util/delay.h>

#define UART_BAUD_RATE 9600

#define RC5_INT0 0
#define RC5_INT1 1

#define RC5_ALL 0xff

typedef struct
{
uint8_t code;
uint8_t addr;
volatile signed char flip;
} rc5_t;

#ifndef RC5_INT
#define RC5_INT RC5_INT0
#endif /* RC5_INT */

#ifndef RC5_PRESCALE
#define RC5_PRESCALE 256 // default: 1024
#endif /* RC5_PRESCALE */

/* ************************************************** ****************************** */

rc5_t rc5;

/* ************************************************** ****************************** */

#ifndef F_CPU
#error Please define F_CPU
#endif /* !F_CPU */

/* µs for a whole bit of RC5 (first & second part) */
#define RC5_BIT_US (64*27)

#define RC5_TICKS \
((uint8_t) ((uint32_t) (F_CPU / 1000 * RC5_BIT_US / 1000 / RC5_PRESCALE)))

#define RC5_DELTA \
(RC5_TICKS / 6)

typedef union
{
uint16_t w;
uint8_t b[2];
} code_t;

static code_t code;
static uint8_t rc5_addr;

/* Number of Bits received so far */
/* Number of Interrupts occured so far */
static uint8_t nbits;
static uint8_t nint;

/* ************************************************** ****************************** */

void rc5_init (uint8_t addr)
{
nint = 0;
nbits = 0;
rc5.flip = -1;

rc5_addr = addr;

#if (RC5_PRESCALE==1024)
TCCR0 = (1 << CS02) | (1 << CS00);
#elif (RC5_PRESCALE==256)
TCCR0 = (1 << CS02);
#elif (RC5_PRESCALE==64)
TCCR0 = (1 << CS01) | (1 << CS00);
#else
#error This RC5_PRESCALE is not supported
#endif /* RC5_PRESCALE */

/* INTx on falling edge */
/* clear pending INTx */
/* enable INTx interrupt */
#if (RC5_INT == RC5_INT0)
MCUCR = (MCUCR | (1 << ISC01)) & ~ (1 << ISC00);
GIFR = (1 << INTF0);
/* ATMEGA: GICR |= (1 << INT0); */
GIMSK |= (1 << INT0);
#elif (RC5_INT == RC5_INT1)
MCUCR = (MCUCR | (1 << ISC11)) & ~ (1 << ISC10);
GIFR = (1 << INTF1);
GICR |= (1 << INT1);
#else
#error please define RC5_INT
#endif /* RC5_INT */
}

/* ************************************************** ****************************** */

//SIGNAL (SIG_OVERFLOW0)
ISR(TIMER0_OVF0_vect)
{
TIMSK &= ~(1 << TOIE0);

uint8_t _nbits = nbits;
code_t _code = code;

if (26 == _nbits)
{
_nbits++;
_code.w <<= 1;
}

if (27 == _nbits
&& _code.b[1] >= 0x30 /* AGC == 3 */
&& 0 > rc5.flip)
{
uint8_t _rc5_code;
uint8_t _rc5_addr;
/* we do the bit manipulation stuff by hand, because of code size */
_rc5_code = _code.b[0] & 0x3f; /* 0b00111111 : #0..#5 */
_code.w <<= 2;
_rc5_addr = _code.b[1] & 0x1f; /* 0b00011111 : #6..#10 */

if (rc5_addr & 0x80
|| rc5_addr == _rc5_addr)
{
rc5.code = _rc5_code;
rc5.addr = _rc5_addr;
signed char flip = 0;
if (_code.b[1] & 0x20) /* 0b00100000 : #11 */
flip = 1;
rc5.flip = flip;
}
}

nint = 0;
nbits = 0;

/* INTx on falling edge */
/* clear pending INTx */
/* enable INTx interrupt */
#if (RC5_INT == RC5_INT0)
MCUCR = (MCUCR | (1 << ISC01)) & ~ (1 << ISC00);
GIFR = (1 << INTF0);
/* ATMEGA: GICR |= (1 << INT0); */
GIMSK |= (1 << INT0);
#elif (RC5_INT == RC5_INT1)
MCUCR = (MCUCR | (1 << ISC11)) & ~ (1 << ISC10);
GIFR = (1 << INTF1);
GICR |= (1 << INT1);
#endif
}

/* ************************************************** ****************************** */

#if (RC5_INT == RC5_INT0)
//SIGNAL (SIG_INTERRUPT0)
ISR(INT0_vect)
#elif (RC5_INT == RC5_INT1)
SIGNAL (SIG_INTERRUPT1)
#endif /* RC5_INT */
{
code_t _code = code;
uint8_t _nint = nint;

uint8_t tcnt0 = TCNT0;
TCNT0 = 0;

if (0 == _nint)
{
/* INTx on both edges */
#if (RC5_INT == RC5_INT0)
MCUCR = (MCUCR | (1 << ISC00)) & ~ (1 << ISC01);
#elif (RC5_INT == RC5_INT1)
MCUCR = (MCUCR | (1 << ISC10)) & ~ (1 << ISC11);
#endif /* RC5_INT */

TIFR = (1 << TOV0);
TIMSK |= (1 << TOIE0);
_code.w = 0;
}
else
{
/* Number of bits of the just elapsed period */
uint8_t n = 1;

/* Bits received so far */
uint8_t _nbits = nbits;

/* is TCNT0 close to RC5_TICKS or RC5_TICKS/2 ? */
if (tcnt0 > RC5_TICKS + RC5_DELTA)
goto invalid;
else if (tcnt0 < RC5_TICKS/2 - RC5_DELTA)
goto invalid;
else if (tcnt0 > RC5_TICKS - RC5_DELTA)
n = 2;
else if (tcnt0 > RC5_TICKS/2 + RC5_DELTA)
goto invalid;

/* store the just received 1 or 2 bits */
do
{
_nbits++;
if (_nbits & 1)
{
_code.w <<= 1;
_code.b[0] |= _nint & 1;
}
}
while (--n);

if (0)
{
invalid:

/* disable INTx, run into Overflow0 */
#if (RC5_INT == RC5_INT0)
/* ATMEGA: GICR &= ~(1 << INT0); */
GIMSK &= ~(1 << INT0);
#elif (RC5_INT == RC5_INT1)
GICR &= ~(1 << INT1);
#endif /* RC5_INT */

_nbits = 0;
}

nbits = _nbits;
}

code = _code;
nint = 1+_nint;
}


// putc fuer AVR mit einem UART (z.B. AT90S8515)
int uart_putc(unsigned char c)
{
while(!(USR & (1 << UDRE))) /* warte, bis UDR bereit */
{
}

UDR = c; /* sende Zeichen */
return 0;
}

/* puts ist unabhaengig vom Controllertyp */
void uart_puts (char *s)
{
while (*s)
{ /* so lange *s != '\0' also ungleich dem "String-Endezeichen" */
uart_putc(*s);
s++;
}
}

int main(void) {

//char str[10] = "C:../A:..";
char str[5];

// Status Port
DDRB = 0xFF;
PORTB = 0x11;

// RC5 Initialisierung
DDRD &= ~(1<<PD2); /* INT0 muss Eingang sein! */
rc5_init(RC5_ALL); /* Initialisierung RC5 */

// UART
UCR |= (1<<TXEN);
UBRR = F_CPU / (UART_BAUD_RATE * 16L) - 1;

sei(); /* Interrupts zulassen */

uart_puts("RC5Test running.\r\n");

while (1) {
if (rc5.flip == -1) {
/* Mach irgendwas */
} else {
/* RC5 Code empfangen */
/* Ja, dann rc5.code merken und evtl. rc5.addr */
/* falls man die braucht und nicht sowieso schon kennt */
//uint8_t code = rc5.code;
//uint8_t addr = rc5.addr;
/* und auf naechstes Zeichen warten */
//rc5.flip = -1;

//sprintf(str,"CODE: %02x / ADR: %02x\r\n",rc5.code,rc5.addr);
uart_puts("CODE:");
itoa(rc5.code,str,16);
uart_puts(str);
uart_puts(" / ADR:");
itoa(rc5.addr,str,16);
uart_puts(str);
uart_puts("\r\n");


uart_puts(str);
rc5.flip = -1;
}
_delay_ms(50);
_delay_ms(50);
_delay_ms(50);
_delay_ms(50);
_delay_ms(50);
PORTB++;
}
}


Das Programm sendet jeden empfangenen Code inklusive Adresse über die serielle Schnittstelle an den PC... zumindest sollte es das.

Vielleicht kann mir jemand mit dem Quellcode helfen. Ich bin mir sicher, dass es nicht an der Hardware liegt. Habe den Aufbau mit der Application Note AVR410 und einem AT90S1200 erfolgreich getestet.

In dem Beispiel aus dem Wiki wird ein ATMEGA mit 16MHz verwendet. Der Prescaler wurde von mir von 1024 auf 256 reduziert => ich verwende ja schließlich nur 1/4 der Taktfrequenz.

Wenn das Programm geladen ist erscheint wird über die serielle Schnittstelle nur die Meldung, dass das Programm läuft, allerdings wird scheinbar keine Taste erkannt.

Bin für jede Hilfe dankbar.
Gruß
T.

SprinterSB
29.12.2006, 00:34
Du hast

#define RC5_INT1 1

scheinst aber Port D2 (INT0) verwenden zu wollen. Dann musst du

#define RC5_INT0

und RC5_INT1 *nicht* definieren, auch nicht zu 0! (Da wird nicht mit #if getestet, sondern mit #ifdef/#ifndef)

thenoone
29.12.2006, 02:00
Hallo SprinterSB,

danke für die Antwort.
Du hast schon recht, es wird INT0 verwendet, aber die #Define-Angaben sind so korrekt.
Die Konstante die bestimmt, ob INT0 oder INT1 verwendet wird ist RC5_INT und diese Konstante kann entweder den Wert von RC5_INT1 oder RC5_INT0 annehmen.

Weiter unten im Quellcode sind dann immer solche Abfragen vorhanden:
#if (RC5_INT == RC5_INT0)
...
#elif (RC5_INT == RC5_INT1)
...
#endif /* RC5_INT */

Über uart-Ausgaben konnte ich auch schon nachweisen, dass tatsächlich Interruptsausgelöst werden, wenn ich Tasten auf der Fernbedienung drücke.
Ich vermute das Problem liegt eher irgendwie beim Timing oder so...

SprinterSB
29.12.2006, 11:04
Ja stimmt, ist ne Zeit lang her daß ich das geschrieben habe... Ich wollte es immer schon mal umschreiben so daß es besser verständlich ist.

Bist du sicher, daß die Fernbedienung RC5 absetzt und nicht irgend nen RC5-Dialekt?

Mit 4MHz sollte der µC eigentlich schnell genug sein. Was mit den Timimngs nicht stimmt ist so trocken schwer zu sagen.

Um rauszufinden, was eine Fernbedienung so treibt, hab ich mal das da geschrieben. Aber Vorsicht, ist undokumentierter Hack aus der Anfängerzeit ;-)

nobody1900
05.01.2007, 00:15
Hi!

Ich habe genau das gleiche Problem. Nur habe ich einem Atmega16 mit einem 16MHz Quarz am laufen.

Zuerst ging mal gar nix, bis ich erstens alle Präprozessoranweisungen hinausgeschmissen habe und zweitens alle möglichen Fernbedienungen im Haus getestet hatte.

So ein LED habe ich auch in die ISR eingebaut, leuchtet auch schön brav auf, immer und egal wo ich bei welcher fernbedienung drücke.

nun kam ich auf die Idee mal alle knöpfe auf allen Fernbedienungen zu drücken, und siehe da, bei ein paar funktionierts glatt.

Nur leider nur bei ein paar Knöpfen.

Was mich noch stutzig macht, ist dass ich im Terminal verschiedene Adressen der gleichen Fernbedienung erhalte. (zb. 10 und 11)


Es wäre super, wenn auch mir jemand helfen könnte!!

PS. Hat sich da vieleicht in letzter Zeit der "Übertragungsstandart" geändert oder so??


mfg

philipp

SprinterSB
06.01.2007, 19:52
Ich bin grad ein neues Projekt am aufziehen und habe einen ATtiny bei F_CPU = 1MHz laufen. Funktionierte auf Anhieb der RC5-Code. Natürlich waren ein paar Anpassungen nötig, zB quick & dirty:

#define RC5_PRESCALE 64

#ifdef __AVR_ATtiny2313__
#define GICR GIMSK
#define GIFR EIFR
#endif // __AVR_ATtiny2313__

Sowie TCCR0 --> TCCR0B.

Hast du einen PullUp am RC5-Eingang? Evtl andere ISR-Routinen, die die IRQ-Latenz hochtreiben? Passen RC5-EMpfänger und Sender?

nobody1900
09.01.2007, 21:17
Hi SprinterSB!

Ich bin auch gerade beim Zusammenbauen einer RC5-Routine, nur will sie noch nicht do richtig.

Ich warte einfach mit dem Int0 bis eine erste fallende Flanke vom Sendor kommt. Dann starte ich damit einen Timer (T0 um genau zu sein), der nach berechneten 1328µsec das erste Viertel des nächaten Bits erreicht.
Nach dieser Zeit schalte ich mal alle ints aus, und lese den Sensor aus. ist er eins, rotiere ich eine eins in mein MSB und schalte den ext Int auf fallende Flanke. Genau das umgekehrte tue ich bei nullsignal.

Ich denke mal, dass da alle Lösungswege mehr oder weniger identisch sind.

Ich werde da mal ein wenig weiterprobieren, wird schon geht!

grüße
philipp

nobody1900
09.01.2007, 21:22
Achja, noch was. Deine RC5 Funktion, synchronisiert die sich bei jeder Flanke neu, oder nur auf das erste Startbit.

Habe eben deine noch mal probiert, bei einer Fernstuerung gingen semtliche Knöpfe, bei der anderen kein einziger!

Könnte das vielleicht auch auf eine zu ungenaue 36khz modulation des Senders zurückzuführen sein?

SprinterSB
10.01.2007, 10:42
Falls der Sender nicht auf 36kHz moduliert (sindern zB 56kHz), bekommst du natürlich nix mit.

Die Demodulation/Verstärkung etc erledigt der IR-EMpfänger wie TSOP17**, TSOP18**.

Ich hatte auch schon TSOP1738 für 36kHz im Einsatz, ging auch. So schmalbandig sind die nicht, evtl. geht die Empfindlichkeit etwas runter.

Meine RC5-Implementierung ist unabhängig von der 36kHz-Modulation. Sie geht davon aus, der der IR-Empfänger sich darum kümmert.

Synchronisation geschieht bei jeder Flanke.

nobody1900
10.01.2007, 12:03
Ah, ok. Werde heute Abend mal Messen, ob der Sensor bei der anderen Fernsteuerung überhaupt etwas mitkriegt.

Vielen Dank vorerst!

Grüße
philipp

SprinterSB
10.01.2007, 12:21
Bist du sicher, daß die RC5 absetzt?

nobody1900
10.01.2007, 15:06
hm, gute frage. sind beide nicht von sony philips oder so...

verwendet dein code auch das zweite startbit als Code?

mfg
philipp

SprinterSB
18.01.2007, 21:51
Nein, das wäre dann wohl RC6?

Mein Code geht davon aus, daß die beiden Startbits immer 1 sind. Das zu ändern wäre kein Aufwand.

Selbst Fernbedienungen von Phillips setzen nicht immer RC5 ab bzw es gibt da 1000 Dialekte, wo noch nicht mal mehr die Hersteller von Universal-Fernbedienungen dirchblicken...

nobody1900
18.01.2007, 23:49
Aha, also kein Wunder.

Ich habe vorgestern das Signal der anderen Fernsteuerung(die die nicht geht) gemessen, leider hatte ich nur so ein billiges usb oszi geliehen, und da konnte man zumindest eine Abweichung in der Signallänge (also Anzahl der Bits) die abgesetzt wurden erkennen.

Jedenfalls habe ich eine kleine saubillig fernsteuerung gefunden, bei der es perfekt klappt.

Vielen Dank nochmals!

Schöne Grüße

Philipp