PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : RC5 Empfang mit TSOP1736



Spurius
21.09.2006, 16:35
Hallo,
ich habe versucht ein Programm zu schreiben, mit dem ich RC5 Code empfangen und an die serielle Schnittstelle weitergeben kann. Mit einem Basic-Programm funktioniert dass, mit meinem C-Programm leider nicht.
Es wird zwar "Hallo Martin" ausgegeben, aber sonst nix.



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



struct rc5_parts
{
volatile char s_bit;
uint8_t addresse;
uint8_t code;
volatile char rdy;
} rc5;

volatile char start_rx = 0;
char mid;

void init_rc5()
{
MCUCR |= (1<<ISC11) | (1<<ISC10);
GICR |= (1<<INT1);
TCCR0 |= (1<<CS02);
TCNT0 = 145;
}

//************************************************** **USART******************************

void init_usart(void)
{
UBRRL |= 0b01100111;
UCSRB = (1<<TXEN) | (1<<RXEN);
UCSRC = (1<<URSEL) | (1<<UCSZ1) | (1<<UCSZ0);
}

void send_char(unsigned char s)
{
while (!(UCSRA & (1<<UDRE)));
UDR = s;
}

void send_string(char *s)
{
while(*s != '\0')
{
send_char(*s);
s++;
}
}

//************************************************** ********USART-ENDE********************

SIGNAL(SIG_OVERFLOW0)
{
uint8_t i,j;
i++;
TCNT0 = 145; //Timer soll nach 1.778ms Overflow haben
if(i == 12)
{
TIMSK &= ~(1<<TOIE0);
rc5.rdy = 1;
i = 0;
}

if(i<6) //Wenn i<6, werden die ausgelesenen Bits zu Addresse hinzugefügr
{
if(PORTD & (1<<4))
{
rc5.addresse |= (1<<(i-1));
}
else
{
rc5.addresse &= ~(1<<(i-1));
}
}

if((i>=6)&(i<12)) //6 - 11 werden zu rc5.code hinzugefügt
{
if(PORTD & (1<<4))
{
rc5.code |= (1<<(i-6));
}
else
{
rc5.code &= ~(1<<(i-6));
}
}
}

SIGNAL(SIG_INTERRUPT1)
{


if(rc5.s_bit<4)
{
rc5.s_bit++;
}
else
{
start_rx = 1;
TIMSK |= (1<<TOIE0);
TCNT0 |= 228;
rc5.s_bit = 0;
}
}

int main(void)
{
init_usart();
init_rc5();
char i = 1;
sei();
char result[10];
char hello[15] = "Hallo Martin";
send_string(hello);
for(;;)
{
if(rc5.rdy == 1)
{
itoa(0110,result,10);
send_string(result);
rc5.rdy = 0;
}
}
}




Seht ihr irgendwelche Fehler, die ich übersehen habe? Vom Prinzip her zählt das Programm mit jedem Interrupt an PD4 eine Variable hoch und sobald die größer 3 ist, springt der Timer an. Bei diesen 3 Interrupts sollen die 2 Startbits und das Togglebit erkannt werden.

Gruß
Martin

SprinterSB
21.09.2006, 17:00
TCNT0 |= 228; :-k

IMHO funktioniert das so nicht, weil du nicht an jedem Bit-Anfang einen IRQ bekommst

Bei [01][01] bekommst du IRQs (als *) bei 0*1*0*1
Bei [01][10] bekommst du IRQs (als *) bei 0*1.1*0, bei . gibt's keine IRQ

Wenn es dir nicht darauf ankommt, das alles selber zu machen, gibt's nen C-Code im Wiki.

Spurius
21.09.2006, 17:14
Hallo,
der Interupt wird halt dann ausgelöst, wenn das Togglebit 1 ist. Und sich das ja immer toggelt, müsste ich zumindest bei mehrmaligem Drücken ein Ergebnis sehen.

Deinen Artikel im Wiki habe ich mir schon angeschaut, er ist auch eine große Hilfe, aber ich möchte selbst eine Lösung finden.

SprinterSB
21.09.2006, 17:28
Was ist mit i und j in SIG_OVERFLOW0? Die sind nicht initialisiert! Eigentlich sollte der Compiler das warnen...

So wie du die verwendest sollten die static sein:


SIGNAL(...)
{
static uint8_t i=0, j=0;
i++;
...


Das nimmt die aber die möglichkeit, sie von "aussen" zu verändern, etwa resetten wenn die IR-Daten korrupt sind. Was du willst ist IMHO nicht lokal static, sondern static.

Spurius
21.09.2006, 17:53
Hallo SprintSB,

ich habe jetzt einige Sachen verändert, ausgebessert, etc., aber rc5.code ist immer 0. Hat noch jemand eine Idee, was ich da falsch mache?



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



struct rc5_parts
{
volatile char s_bit;
volatile uint8_t addresse;
volatile uint8_t code;
volatile char rdy;
} rc5;

char mid;
volatile uint8_t bitnummer = 0;

void init_rc5()
{
MCUCR |= (1<<ISC11) | (1<<ISC10);
GICR |= (1<<INT1);
TCCR0 |= (1<<CS02);
}

//************************************************** **USART******************************

void init_usart(void)
{
UBRRL |= 0b01100111;
UCSRB = (1<<TXEN) | (1<<RXEN);
UCSRC = (1<<URSEL) | (1<<UCSZ1) | (1<<UCSZ0);
}

void send_char(unsigned char s)
{
while (!(UCSRA & (1<<UDRE)));
UDR = s;
}

void send_string(char *s)
{
while(*s != '\0')
{
send_char(*s);
s++;
}
}

//************************************************** ********USART-ENDE********************

SIGNAL(SIG_OVERFLOW0)
{
bitnummer++;
TCNT0 = 145; //Timer soll nach 1.778ms Overflow haben

if(bitnummer == 12)
{
TIMSK &= ~(1<<TOIE0);
rc5.rdy = 1;
rc5.s_bit = 0;
bitnummer = 13;
}

if(bitnummer<6) //Wenn i<6, werden die ausgelesenen Bits zu Addresse hinzugefügr
{
if((PORTD & (1<<3)) == 1)
{
rc5.addresse |= (1<<(bitnummer-1));
}
else
{
rc5.addresse &= ~(1<<(bitnummer-1));
}
}

if((bitnummer>=6)&(bitnummer<=11)) //6 - 11 werden zu rc5.code hinzugefügt
{

if((PORTD & (1<<3)) == 1)
{
rc5.code = 0xff; //(1<<(bitnummer-6));
}
else
{
rc5.code = 0x00; //~(1<<(bitnummer-6));
char hello[15] = "111";
send_string(hello);
}
}
}

SIGNAL(SIG_INTERRUPT1)
{


if(rc5.s_bit<4)
{
rc5.s_bit++;
}
if(rc5.s_bit == 4)
{
TIMSK |= (1<<TOIE0); // Timer0 Interrupts enablen
TCNT0 |= 228; //Timer so vorladen, dass er zur Mitte des 2. Halbbits den ersten Timeroverflow hat

GICR &= ~(1<<INT1); //Int1 disablen
}
}

int main(void)
{
init_usart();
init_rc5();
char i = 1;
sei();
char result[10];
char hello[15] = "Hallo Martin";
send_string(hello);
for(;;)
{
if(rc5.rdy == 1)
{
itoa(rc5.code,result,10);
char hello2[15] = "RC5";
send_string(hello2);
send_string(result);
rc5.rdy = 0;
GICR |= (1<<INT1);
}
}
}

SprinterSB
22.09.2006, 11:35
-- Es sollte heissen: TCNT0 = 228;
-- Es sollte heissen: if (bitnummer>=6 && bitnummer<=11) ...
-- Es sollte heissen: if ((PORTD & (1<<3)) != 0)
-- bitnummer=11 wird 2x erhöht erhöht?
-- Was ist, wenn bitnummer=0 (AGC #2)?
-- Was ist, wenn bitnummer=1 (FLIP)?
-- Wo wird Timer0-IRQ deaktiviert und INT-IRQ aktiviert?
-- Wie kommst du auf diese init-Werte für TCNT0??? :-k
-- Hast du bedacht, daß der Ausgang eines TSOP1736 open collector ist?
-- Wie erkennst du ungültige Daten?

Spurius
22.09.2006, 14:17
Hallo,
habe deine Einwände zum Teil beseitig und Kommentare im Code ergänzt.
Die Werte für TCNT0 errechnen sich folgendermaßen:

1/(16000000/256) = 1.6exp-5
Das erste Intervall soll bis zur Mitte des low-Pegels vom Togglebit dauern, also 1.778ms/2->55.5 Timersteps
Danach sollen die Intervalle jeweils 1.778ms dauern, so dass immer die das 2. Halbbit erfasst wird.

Selbst wenn meine Timings nicht ganz stimmen würden, dürfte rc5.code doch nicht immer 0 sein??

Ich meine doch, ich habe hier den OpenCollector berücksichtigt, und wenn nicht, wäre das Signal halt invertiert.



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



struct rc5_parts
{
volatile char s_bit;
volatile int8_t addresse;
volatile int8_t code;
volatile char rdy;
} rc5;

char mid;
volatile uint8_t bitnummer = 0;

void init_rc5()
{
MCUCR |= (1<<ISC11) | (1<<ISC10);
GICR |= (1<<INT1);
TCCR0 |= (1<<CS02);
}

//************************************************** **USART******************************

void init_usart(void)
{
UBRRL |= 0b01100111;
UCSRB = (1<<TXEN) | (1<<RXEN);
UCSRC = (1<<URSEL) | (1<<UCSZ1) | (1<<UCSZ0);
}

void send_char(unsigned char s)
{
while (!(UCSRA & (1<<UDRE)));
UDR = s;
}

void send_string(char *s)
{
while(*s != '\0')
{
send_char(*s);
s++;
}
}

//************************************************** ********USART-ENDE********************

SIGNAL(SIG_OVERFLOW0)
{
bitnummer++;
TCNT0 = 145; //Timer soll nach 1.778ms Overflow haben

if(bitnummer == 12)
{
TIMSK &= ~(1<<TOIE0); //Wenn die 11 Bits davor erfasst wurden, TimerIRQ deaktivieren
rc5.rdy = 1;
rc5.s_bit = 0;
bitnummer = 13;
}

if(bitnummer<6) //Wenn i<6, werden die ausgelesenen Bits zu Addresse hinzugefügr
{
if((PORTD & (1<<3)) != 0)
{
rc5.addresse |= (1<<(-1)*(bitnummer-6));
}
else
{
rc5.addresse &= ~(1<<(-1)*(bitnummer-6));
}
}

if((bitnummer>=6 && bitnummer<=11)) //6 - 11 werden zu rc5.code hinzugefügt
{

if((PORTD & (1<<3)) != 0)
{
rc5.code |= (1<<(-1)*(bitnummer-7));
}
else
{
rc5.code &= ~(1<<(-1)*(bitnummer-7));
}
}
}

SIGNAL(SIG_INTERRUPT1)
{


if(rc5.s_bit<4)
{
rc5.s_bit++;
}
if(rc5.s_bit == 4)
{
TIMSK |= (1<<TOIE0); // Timer0 Interrupts enablen
TCNT0 = 200; //Timer so vorladen, dass er zur Mitte des 2. Halbbits den ersten Timeroverflow hat

GICR &= ~(1<<INT1); //Int1 disablen
}
}

int main(void)
{
init_usart();
init_rc5();
char i = 1;
sei();
char result[10];
char hello[15] = "Hallo Martin";
send_string(hello);
for(;;)
{
if(rc5.rdy == 1)
{
itoa(rc5.code,result,10);
char hello2[15] = "RC5-Code: ";
send_string(hello2);
send_string(result);
rc5.rdy = 0;
GICR |= (1<<INT1); // ext. Interrupt wieder aktivieren
}
}
}

SprinterSB
22.09.2006, 14:28
Von der ersten Flanke, die du siehst (1/2 AGC #1) bis zur Mitte der zweiten Hälfte von Flip (3/4 Flip) vergehen 2.25 * 1.778ms.

Die Bitzählerei stimmt IMHO immer noch nicht.

Was soll das (-1)* bei bitnummer???

Was zählt s_bit??? Wenn das AGC zählen soll, dann geht's nach Flanke No. 3 los (1/2 AGC #2). Von da bis 3/4 Flip sind's 1.25 * 1.778ms. --> TCNO0 = -138.88 ~ -139 = 117.

Spurius
22.09.2006, 17:31
s_bit wird bis 3 hochgezählt, d.h. die ersten 3 Bits waren 1, dann wird die
TimerISR ausgelöst.
Der erste Wert muss meiner Meinung anch 1.778ms/4 sein und nicht /2, wie ich vorhin schrieb. Somit ergibt sich ein erstes TCNT0 von 228.
Das (-1) soll bewirken, dass die Werte mit steigendem i vom MSB zum LSB in rc5.code geschrieben werden, stimmt aber nicht, dass muss ich mir noch anders überlegen...
Außerdem reagiere ich jetzt auf fallende Flanken am Anfang, ich hatte da die Invertierung des TSOP wohl doch nicht so ganz berücksichtigt.
Es scheint IMMER eine 1 anzuliegen, wenn rc5.code abgefragt wird, da ich jedesmal CODE-EINS per USART auf den Rechner kriege.



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



struct rc5_parts
{
volatile char s_bit;
volatile int8_t addresse;
volatile int8_t code;
volatile char rdy;
} volatile rc5;

char mid;
volatile uint8_t bitnummer = 0;

void init_rc5()
{
MCUCR |= (1<<ISC11);
GICR |= (1<<INT1);
TCCR0 |= (1<<CS02);
}

//************************************************** **USART******************************

void init_usart(void)
{
UBRRL |= 0b01100111;
UCSRB = (1<<TXEN) | (1<<RXEN);
UCSRC = (1<<URSEL) | (1<<UCSZ1) | (1<<UCSZ0);
}

void send_char(unsigned char s)
{
while (!(UCSRA & (1<<UDRE)));
UDR = s;
}

void send_string(char *s)
{
while(*s != '\0')
{
send_char(*s);
s++;
}
}

//************************************************** ********USART-ENDE********************

SIGNAL(SIG_OVERFLOW0)
{
bitnummer++;
TCNT0 = 145; //Timer soll nach 1.778ms Overflow haben

if(bitnummer == 12)
{
TIMSK &= ~(1<<TOIE0); //Wenn die 11 Bits davor erfasst wurden, TimerIRQ deaktivieren
rc5.rdy = 1;
rc5.s_bit = 0;
bitnummer = 13;
}

if(bitnummer<6) //Wenn i<6, werden die ausgelesenen Bits zu Addresse hinzugefügr
{
if((PORTD & (1<<3)) != 0)
{
rc5.addresse |= (1<<(-1)*(bitnummer-6));
}
else
{
rc5.addresse &= ~(1<<(-1)*(bitnummer-6));
}
}

if((bitnummer>=6 && bitnummer<=11)) //6 - 11 werden zu rc5.code hinzugefügt
{

if((PORTD & (1<<3)) != 1)
{
rc5.code |= (1<<5);
char hello[15] = "CODE-EINS";
send_string(hello);
}
else
{
rc5.code &= ~(1<<3);
char hello[15] = "CODE-NULL";
send_string(hello);
}
}
}

SIGNAL(SIG_INTERRUPT1)
{


if(rc5.s_bit<3)
{
rc5.s_bit++;
}
if(rc5.s_bit == 3)
{
TIMSK |= (1<<TOIE0); // Timer0 Interrupts enablen
TCNT0 = 228; //Timer so vorladen, dass er zur Mitte des 2. Halbbits den ersten Timeroverflow hat

GICR &= ~(1<<INT1); //Int1 disablen
}
}

int main(void)
{
init_usart();
init_rc5();
char i = 1;
sei();
char result[10];
char hello[15] = "Hallo Martin";
send_string(hello);
for(;;)
{
if(rc5.rdy == 1)
{
itoa(rc5.code,result,10);
char hello2[15] = "RC5-Code: ";
send_string(hello2);
send_string(result);
rc5.rdy = 0;
GICR |= (1<<INT1); // ext. Interrupt wieder aktivieren
bitnummer = 0; // bitnummer zurücksetzten
}
}
}

Spurius
23.09.2006, 14:51
So, mittlerweile bekomme ich zumindest sowohl 1 als auch 0 in rc5.code geschrieben, allerdings stimmen die Codes meistens nicht, oft kommt bei mehreren Tasten der selbe Code etc.
Vom Timing her müsste es stimmen, das hab ich mittlerweile oft nachgerechnet. Kann es sein, dass die Zeit, die bei der Abarbeitung des Codes verloren geht, meine Rechnung stört?


//************************************************** ********USART-ENDE********************

SIGNAL(SIG_OVERFLOW0)
{
bitnummer++;
TCNT0 = 145; //Timer soll nach 1.778ms Overflow haben

if(bitnummer == 12)
{
TIMSK &= ~(1<<TOIE0); //Wenn die 11 Bits davor erfasst wurden, TimerIRQ deaktivieren
rc5.rdy = 1;
bitnummer = 13;
}

if(bitnummer<6) //Wenn i<6, werden die ausgelesenen Bits zu Addresse hinzugefügr
{
if((PIND & (1<<3)) != 0)
{
rc5.addresse |= (1<<(5-bitnummer));
}
else
{
rc5.addresse &= ~(1<<(5-bitnummer));
}
}

if((bitnummer>=6 && bitnummer<=11)) //6 - 11 werden zu rc5.code hinzugefügt
{

if((PIND & (1<<3)) != 0)
{
rc5.code &= ~(1<<(11-bitnummer));

}
else
{
rc5.code |= (1<<(11-bitnummer));

}
}
}

SIGNAL(SIG_INTERRUPT1)
{


if(rc5.s_bit<3)
{
rc5.s_bit++;
}
if(rc5.s_bit == 3)
{
TIMSK |= (1<<TOIE0); // Timer0 Interrupts enablen
TCNT0 = 228; //Timer so vorladen, dass er zur Mitte des 2. Halbbits den ersten Timeroverflow hat

GICR &= ~(1<<INT1); //Int1 disablen
}
}

int main(void)
{
init_usart();
init_rc5();
char i = 1;
sei();
char result[10];
char hello[15] = "Hallo Martin";
send_string(hello);
for(;;)
{
if(rc5.rdy == 1)
{
itoa(rc5.code,result,10);
char hello2[15] = "RC5-Code: ";
send_string(hello2);
send_string(result);
rc5.rdy = 0;
rc5.s_bit = 0;
GICR |= (1<<INT1); // ext. Interrupt wieder aktivieren
bitnummer = 0; // bitnummer zurücksetzten
rc5.addresse = 0;
rc5.code = 0;
}
}
}

Spurius
24.09.2006, 15:34
Ich verzweifel da noch dran, hab jetzt Stunden mit ausprobieren verbracht aber ich komme auf keinen grünen Zweig...
Bei dem Code unten bekomme ich immer eine 17-stellige Zahl, dabei sollen einfach nur alle Bits in die Variable rc5.gesamt kopiert werden...



#include <avr/io.h>
#include <avr/interrupt.h>
#include <math.h>
#include <inttypes.h>
#include <compat/deprecated.h>




struct rc5_parts
{
volatile char s_bit;
volatile uint8_t addresse;
volatile uint8_t code;
volatile char rdy;
volatile uint16_t gesamt;
} volatile rc5;

volatile uint8_t bitnummer = 0;

void init_rc5()
{
MCUCR |= (1<<ISC11); //wait falling edge
GICR |= (1<<INT1); //enable INT1
TCCR0 |= (1<<CS02); //Prescaler 256
cbi(DDRD,PD3);
}

//************************************************** **USART******************************

void init_usart(void)
{
UBRRL |= 0b01100111;
UCSRB = (1<<TXEN) | (1<<RXEN);
UCSRC = (1<<URSEL) | (1<<UCSZ1) | (1<<UCSZ0);
}

void send_char(unsigned char s)
{
while (!(UCSRA & (1<<UDRE)));
UDR = s;
}

void send_string(char *s)
{
while(*s != '\0')
{
send_char(*s);
s++;
}
}

//************************************************** ********USART-ENDE********************

SIGNAL(SIG_OVERFLOW0)
{
bitnummer++;
if(rc5.s_bit == 1 && bitnummer < 14)
{
if((PIND & (1<<PD3)) != 0)
{
rc5.gesamt |= (1<<(14-bitnummer));
TCNT0 = 228;
}
else
{
rc5.gesamt &= ~(1<<(14-bitnummer));
TCNT0 = 228;
}
}
if(rc5.s_bit == 1 && bitnummer == 14)
{
cbi(TIMSK,TOIE0);
TCNT0 = 0;
rc5.rdy = 1;
}
}

SIGNAL(SIG_INTERRUPT1)
{


if(rc5.s_bit==0)
{
TCNT0 = 228;
rc5.s_bit = 1;
sbi(TIMSK,TOIE0);
cbi(GICR,INT1);
}
}

int main(void)
{
init_usart();
init_rc5();
sei();
char result[10];

for(;;)
{
if(rc5.rdy == 1)
{
itoa(rc5.gesamt,result,10);
char hello2[15] = "RC5-Code: ";
send_string(result);
rc5.rdy = 0;
rc5.gesamt = 0;
rc5.s_bit = 0;
bitnummer = 0;
sbi(GICR,INT1);
}
}
}

SprinterSB
24.09.2006, 16:14
Hast du nen Pullup an dem Port?

Spurius
24.09.2006, 17:11
Ja, gegen VCC. texttextextextextext

SprinterSB
24.09.2006, 18:34
Deine IRQs kommen doch mit -28, das ist 1/4 Bit. Für den ersten (ab INTx) ist das ok, aber danach sollten sie mit 1/2 Bit (-56) oder 1 Bit (-111) kommen. Du hast es auf 1 Bit ausgelegt (also ohne Konsistenzprüfung etc). Bist du sicher, daß du RC5 sendest?

Spurius
24.09.2006, 18:57
Genau, erst 1/4 Bit und dann immer ein ganzes Bit. Ich gehe davon aus, dass RC5 gesendet wird, da ich in Basic mit dem GetRC5()-Befehl die Commandos empfangen kann und es sich um eine Philipps-Fernbedienung handelt.

SprinterSB
24.09.2006, 19:16
Dann sollte doch in der Timer0-ISR Timer0 auf -111 preloadet werden, oder?

Ausserdem ist es ratsam, die IRQ-Flags vor Aktivierung einer IRQ zu resetten. Sonst bekommst du evtl eine INT1-IRQ, ohne daß es nach Aktivieren der IRQ ein INT1-Ereignis gab.

Spurius
24.09.2006, 20:10
Hi,
ich hatte das mit dem Preloaden so verstanden, dass der Timer bis 256 zählt und ich will, dass er nach 111 Schritten einen Overflow auslöst
->256-111 = 145
Wie soll ich dass denn sonst machen? Das mit dem Flag-resetten werde ich ausprobieren.

SprinterSB
24.09.2006, 20:44
Jo, -111 = 145. ich rechne immer mit den negativen, ist einfacher.