RC5-avkodning på AVR

PIC, AVR, Arduino, Raspberry Pi, Basic Stamp, PLC mm.
Användarvisningsbild
$tiff
Inlägg: 4941
Blev medlem: 31 maj 2003, 19:47:52
Ort: Göteborg
Kontakt:

RC5-avkodning på AVR

Inlägg av $tiff »

Jag blir snart skogstokig.
Jag vill koda av en RC5-kodad IR-signal från en helt vanlig fjärrkontroll med en AVR. Det går inte så bra.
Jag skulle vilja ha en helt avbrottsstyrd avkodning med INT0 och en timer, som är skriven i C, eller åtminstone går att implementera i C. RC5-avkodningen ska vara en del i ett större program.
Frågan är: Är det någon som fått detta att fungera? Vill ni isåfall dela med er av er fungerande kod?

Så här har jag gjort:
Mottagare: Helt vanlig avstämd för 38 kHz. Datapinne kopplad direkt mot I/O på AVR.
µC: Mega32 (ej kritiskt val)
Labbmiljö: STK500
Signalkälla: Multifjärr

Jag har hittat några programexempel som gör avkodningen åt mig. Men för mig funkar de inte. Jag modifierar dem så att de skickar ut avkodad data via UARTen. UARTen i sig har jag inga problem med idag (wow!).
IR-Mottagaren fungerar och tar emot IR-signalerna, det bekräftar mitt oscilloskop, signalerna ser t.om. med ut att vara bi-phase-kodade. Jag dubbelkollar att jag matar signalen till rätt pinne på min AVR.
Det bästa resultatet jag fått hittills är att dataströmmen synkat ur.
Jag har dubbelkollat alla frekvenser och periodtider som laddas till timeravbrotten.

Program jag provat:
http://bray.velenje.cx/avr/rc5/rc5.html
http://markh.de/avr.html
http://www.roboternetz.de/wissen/index. ... BCr_ATMega
-en till som jag inte hittar länk till just nu

Program jag inte provat:
http://www.lancos.com/rc5.asm.html
http://www.avrfreaks.net/index.php?modu ... item_id=89
Användarvisningsbild
cykze
EF Sponsor
Inlägg: 1539
Blev medlem: 8 april 2004, 10:40:28
Ort: Uppsala

Inlägg av cykze »

Nej, det verkar inte vara någon som har hållt på med RC5 till AVR här...

Jag har bara hållt på med Sony:s fjärrkontroller, och där används ett annat protokoll. Principen borde även här vara att ha ett externt interrupt, precis som du skriver, som vid varje interrupt kollar hur långt en timer har tickat. Därefter nollställs timer och samma sak händer vid nästa externa interrupt. Med hjälp av timervärdet borde man sedan kunna räkna ut vad som har hänt mellan varje interrupt.

Att försöka få i gång någon kod man har hittat på internet brukar vara lönlöst. Det är alltid någon liten grej som inte stämmer med just den µC:n man själv använder osv. Då är det oftast bättre skriv allt själv, och det lär man sig ju mest på också.

Tänk också på att signalen från IR-mottagare brukar vara inverterad.
Användarvisningsbild
Xyzzy
Inlägg: 1260
Blev medlem: 30 januari 2004, 22:31:07
Ort: Uppsala, Sweden

Inlägg av Xyzzy »

Jag har faktiskt här i dagarna hållit på med just detta, men pga tidsbrist har jag inte möjlighet att fortsätta just nu.
Jag drev min IRmottagare från STK500 från början, till jag insåg att jag fick ut signaler hela tiden från mottagaren, testade filtrera och avkoppla, men hjälpte inte, så jag kopplade in mitt labbaggregat, och vips fungerade mottagaren som den skulle.
Men det verkar ju som du har rätt signaler ut från din, så det lär inte vara ditt problem.

Jag har inte heller fått någon kod att riktigt fungera, men som sagt, har inte haft så mycket tid att testa heller.
Meddela gärna om du lyckas få igång det!
Samot
Inlägg: 311
Blev medlem: 15 november 2004, 18:32:13

kanske

Inlägg av Samot »

Har gjort något liknade. Fick heller inte avkodningen att fungera. Problemet var
att för att man inte ska få utsignaler hela tiden( och för att få fina utsignaler) måste man koppla en kondensator parallellt med matningen till mottagaren. Sen funkade det klockrent :)
Användarvisningsbild
$tiff
Inlägg: 4941
Blev medlem: 31 maj 2003, 19:47:52
Ort: Göteborg
Kontakt:

Inlägg av $tiff »

Tack för svar.

Jag har avkopplat intill mottagaren för att slippa skumma signalfel, sånt lär man sig snabbt den "hårda vägen".

Jag vet inte vad problemet här är egentligen. RC5 är ett busenkelt protokoll att avkoda med en AVR. Jag var väl bara för lat just då och ville ha tag i något som funkade direkt. Jag har tyvärr inte haft tid att skriva det själv ännu.
Jag hoppas bara att min multifjärr verkligen använder RC5. Det svåra här verkar vara att lista ut det aktuella protokollet. Är väl kanske smartast att ta LIRC till hjälp där...
Användarvisningsbild
cykze
EF Sponsor
Inlägg: 1539
Blev medlem: 8 april 2004, 10:40:28
Ort: Uppsala

Inlägg av cykze »

Jag gjorde en liten logikanalysator i AVR:en som samplade (började sampla på fallande flank) var 32:e µs i ca 100 ms och sparade det till SRAM:et. När det var klart skickade AVR:en det till datorn via UART:en. Då kunde man se vilket protokoll den använde.
Användarvisningsbild
$tiff
Inlägg: 4941
Blev medlem: 31 maj 2003, 19:47:52
Ort: Göteborg
Kontakt:

Inlägg av $tiff »

>> cykze
Fräckt. Kompletterar man det med en fiffig algoritm och en sändare så har man ju byggt sig en fjärr som kan lära sig! :)
Användarvisningsbild
Xyzzy
Inlägg: 1260
Blev medlem: 30 januari 2004, 22:31:07
Ort: Uppsala, Sweden

Inlägg av Xyzzy »

Nu har jag haft lite tid att leka och jag har kommit fram till att det är det externa interruptet som aktiveras två gånger för mig, å' således ställer till det.

Jag har gjort något liknande det cykze gjort, med ext. interrupt samplar jag en stund på porten, och när den stunden "är över" skrivs samplingen ut på LCD, det fungerar fint OM jag ser till att den andra gången interruptet körs så sker ingen sampling.

Så, nu till min fråga, om jag är inne i interruptet (externt interrupt som reagerar på fallande flank), pollar AVRen om pinnen får fler flanker under tiden och kör interruptet igen, efteråt isf?

Jag kör 8515 för tillfället (hade den monterad på STK500 när jag började och orkade inte byta.)
(Displayrutinen är hemmasnickrad, därav de konstiga anropen till den, men den delen fungerar)

Det intressanta i koden (skrivet i codevision) (tror inte de övriga haxxen jag gjort är intressant...):

Kod: Markera allt


#include <90s8515.h>
#include <delay.h>
#include "xlcd_fancon4.c"

char test[12];	//för test

// External Interrupt 0 service routine
interrupt [EXT_INT0] void ext_int0_isr(void)
{
  char i, j;

  PORTD.3 = !PORTD.3;  //blinka LED vid varje interrupt


  if(PORTD.3 == 1)  //vid varannat interrupt sker sampling
    for(i=0;i<12;i++) //12 bytes samples
      for(j=0;j<8;j++)  //8bitar per bytes
      {
        test[i] = test[i] << 1;
        test[i] = test[i] + PIND.2; //Spara samplingen i LSB och shifta
        PORTD.4 = PIND.2; //LED indikerar sampelvärdet
        delay_ms(100);  //Högt värde för test med knapp istället för IRmottagare
      }
}



void main(void)
{
char i;

//(initiering av I/O bortklippt)

//(init av Timer/counter 0 och 1 bortklippt (anv. inte))

// External Interrupt(s) initialization
// INT0: On
// INT0 Mode: Falling Edge
// INT1: Off
GIMSK=0x40;
MCUCR=0x02;
GIFR=0x40;

// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x00;

//(Init av analog comp. bortklippt)

xlcdinit();  //Initiera LCD
clearlcd();
str2lcd("** LCD  Initiated **");
delay_ms(1000);
clearlcd();

// Global enable interrupts
#asm("sei")

while (1)
{
  if(i>10)
    i = 0;
  lcdcursor(1,1);
  int2lcd(i, 1, '0');
  data2lcd(':', 1);
  bin2lcd(test[i++]);
    
  int2lcd(i, 1, '0');
  data2lcd(':', 1);
  bin2lcd(test[i++]);
    
  lcdcursor(1,2);
  int2lcd(i, 1, '0');
  data2lcd(':', 1);
  bin2lcd(test[i++]);
    
  int2lcd(i, 1, '0');
  data2lcd(':', 1);
  bin2lcd(test[i++]);
  delay_ms(1000);
};
}
Och ja, Det är mycket fulhack i koden, det är ju inget seriöst program, bara en felsökningsalgoritm.

Frågan var alltså:
Ska ett extärnt interrupt (som reagerar på fallande flank) "buffras" och starta interruptet igen, om det redan är inne i det interruptet, eller har jag initierat interruptet felaktigt?
Användarvisningsbild
cykze
EF Sponsor
Inlägg: 1539
Blev medlem: 8 april 2004, 10:40:28
Ort: Uppsala

Inlägg av cykze »

Ja, du bör inaktivera externa interrupt innan du börjar inläsningen till 'test'. Inaktivera först med GIMSK=0x00 och nollställ interruptflaggan med GIFR=0x40, för säkerhets skull. Aktivera interruptsen igen om du vill göra en ny inläsning. Genom att man trycker på en knapp eller helt enkelt startar om AVR:en. Det är ju trots allt ett felsökningsprogram, så det behöver ju inte vara så snyggt.

När jag gjorde programmet för att sampla var 32:e µs (eller vad det var) så blev det ca 3000 bitar för 100 ms. Så det är nog bäst att du skriver ut det till UART:en istället för en LCD, om du inte nöjjer dig med färre bitar.
Användarvisningsbild
Xyzzy
Inlägg: 1260
Blev medlem: 30 januari 2004, 22:31:07
Ort: Uppsala, Sweden

Inlägg av Xyzzy »

Åhh, tack, jag ska genast testa det!

hehe, jaa jag vet att 96 samples (eller bitar) (12*8 bitar) inte är mycket, men det var mest för test, som sagt. Det ska ju gott och väl räcka, bara jag lyckas sampla vid rätt tidpunkt och i rätt längd (en RC5kod innehåller långt under 30 "bitar" ("nivåändringar")).

Kan meddela, det fungerar klocka!
Tack för hjälpen, nu ska jag "bara" fortsätta med resten av kodandet, så jag får ut datat också.

Edit: måste ju meddela vart felet låg också, det var GIFR=0x40, som jag inte tänkt på , GIMSK hade jag testat att sätta till noll under interruptet, men enbart det hjälpte inte)

Ännu en gång; Tack cykze!
Användarvisningsbild
$tiff
Inlägg: 4941
Blev medlem: 31 maj 2003, 19:47:52
Ort: Göteborg
Kontakt:

Inlägg av $tiff »

Jaha, så här gjorde jag samma sak :)

Kod: Markera allt

 /************************************************************
  *   Program:    Poll IR data stream and debug via UART
  *   Created:    06-01-03
  *   Author:     $tiff
  *   Comments:   Written for Mega32
  *		
  ************************************************************/
#include <avr/io.h>
#include <avr/signal.h>
#include <avr/interrupt.h>
#include <stdlib.h>
#include "UART1.c"

#define F_OSC 8000000			// System clock in Hertz
#define sample_freq 38000		// In Hertz
#define sample_time 50			// In milliseconds

#define nbr_of_bytes sample_freq*sample_time/8000
uint8_t data[nbr_of_bytes];		// Data vector
uint8_t bit_counter = 0;		
uint16_t byte_counter = 0;
uint8_t temp_sample = 0;		
uint8_t ready = 0;				// flag
uint8_t s[9];					// Temp varable to itoa();

void init_timer0(void){
	TCNT0 = 0;							// Reset counter register
	OCR0 = F_OSC/(8*sample_freq);		// Set interrupt period
	TIMSK |= (1<<OCIE0);				// Compare interrupt on
	TIFR = (1<<OCF0);					// Clear any old interrupt flags
	TCCR0 = (1<<WGM01)|(1<<CS01);		// CTC mode and Prescaler 8
}

void disable_timer0(void){
	TCCR0 = 0;				// No clock source
	TIMSK &= ~(1<<OCIE0);	// Compare interrupts off
}

void init_INT0(void){
	MCUCR = (1<<ISC01);		// Interrupt on falling edge
	GIFR = (1<<INTF0);			// Clear any old interrupts
	GICR = (1<<INT0);			// Interrupt enabled
}

void disable_INT0(void){
	GICR &= ~(1<<INT0);		// Disable INT0 interrupts
}

// INT0 ISR
SIGNAL(SIG_INTERRUPT0){
	// Falling flank detected, start data gathering
	disable_INT0();
	byte_counter = 0;	// Reset
	bit_counter = 0;	// Reset
	init_timer0();
}

// Timer0 Compare ISR
SIGNAL(SIG_OUTPUT_COMPARE0){
	// Put data into right slot

	if(PIND&(1<<2)){		// If input (IR data) pin high
		temp_sample |= 1;	// Set LSB high
	}						// Else leave low
	bit_counter++;			// Count bit
	
	if(bit_counter > 7){	// Got all bits in byte?
		data[byte_counter] = temp_sample; // Save byte
		temp_sample = 0;	// Reset
		bit_counter = 0;	// Reset
		byte_counter++;		// Count
	}
	temp_sample <<= 1; 		// Shift left
	
	if(byte_counter > nbr_of_bytes){	// Sampling done?
		disable_timer0();	// Disable sampling
		ready = 1;			// Flag ready
	}
}

void delay(unsigned int p)
{
  unsigned int i;
  unsigned char j;
  for(i=0;i<p;i++)
    for (j=0;j<222;j++);
} 


int main(void){
	DDRD  = 0;				// PORTD input
	PORTA = 0xFF;
	PORTB = 0xFF;			// Pullups
	PORTC = 0xFF;
	PORTD = 0xFF;
	
	UART_init(51);	// Set the baudrate to 19 200 bps using a 8 MHz intOSC and double speed

	while(1){
		TransmitString("\n\n\rNy:\n\r");
	
		ready = 0;		// Not ready
		init_INT0();	// Begin waiting for data
		sei();			// Global interrupts on
		while(!ready){
			delay(10000);
		}				// Wait until all data is gathered
	
		byte_counter = 0;	// Begin from start, FIFO.
		while(byte_counter <= nbr_of_bytes){
			TransmitString(itoa(data[byte_counter],s,2));
			byte_counter++;
		}
	}
	return 0;
}
Användarvisningsbild
cykze
EF Sponsor
Inlägg: 1539
Blev medlem: 8 april 2004, 10:40:28
Ort: Uppsala

Inlägg av cykze »

Jag gjorde så att jag skickade varje bit som en egen byte ('1' eller '0') via UART:en. Då var det jätteenkelt att se sekvensen direkt i terminalprogrammet. Hur gör du, $tiff, för att tolka utskriften på skärmen?
Användarvisningsbild
$tiff
Inlägg: 4941
Blev medlem: 31 maj 2003, 19:47:52
Ort: Göteborg
Kontakt:

Inlägg av $tiff »

Kod: Markera allt

while(byte_counter <= nbr_of_bytes){
         TransmitString(itoa(data[byte_counter],s,2));
         byte_counter++;
} 
itoa(data[byte_counter],s,2); gör om en databyte till åtta byte som representerar en bit var ur databyten. Alltså gör jag på samma sätt som du gör.

Med de inställningar jag skrivit ovan fylls ett terminalfönster lagom mycket och sekvensen som spelas in är längre än ett "datapaket". Det verkar stämma med RC5-protokollet iaf, där är varje datapaket 25 ms och jag spelar in 50 ms är det tänkt.
Användarvisningsbild
cykze
EF Sponsor
Inlägg: 1539
Blev medlem: 8 april 2004, 10:40:28
Ort: Uppsala

Inlägg av cykze »

Jag missade 2:an där...
gurrag
Inlägg: 15
Blev medlem: 1 november 2005, 15:23:48
Ort: Uppsala

Inlägg av gurrag »

Hallo!

Har slitt med samme problem som ni. Mitt fel var att det gikk for sakte att sende info via UART. En ny interrupt kom för info fra den forrige var sendt til PC. Gjorde som ni, sparte resultatet i en variabel och sendte det til PC etter att IR signalet var klart. Perfekt.

Har byggt en programerbar motager som fungerer sammen med tex Girder for att styre hele PC'en. Har ikke koden her nå, men kan sende den imorgen. Veldig enkel.

Skal bygge en med Tiny13 och mjukvara USB. Bara jag får tid!!!!
Skriv svar