RC5 Sende Routine

Janiiix3

Aktives Mitglied
28. Sep. 2013
1.333
10
38
Hannover
Sprachen
  1. ANSI C
  2. C#
Hallo Leute,

Habe mich mal versucht an einer kleinen IR Fernbedienung. Mal wieder komme ich nicht weiter und bin auf eure Hilfe angewiesen.

Mein Problem ist jetzt einfach das auf Modulieren der 36kHz. Meine Zeitbasis für die "0" & "1" stehen bereits.
Wie baue ich jetzt den Träger mit ein? Mir fehlen die ansätze wie ich genau vorgehen muss. Habe es mit einem "delay" versucht (wollte ich so auch nicht lassen) aber das funktioniert auch nicht wirklich, ich denke mal das mein Ablauf nicht stimmt.



CodeBox C

/*
* Send_RC5.c
*
* Created: 24.06.2015 16:58:52
*  Author: Jan
*/


#define F_CPU 16000000

#define RC5_HALF_BIT_TIME 8 // 8 * 110µs = 880µs

#define RC5_SEND_ON     PORTD |=  (1<<PD1)
#define RC5_SEND_OFF   PORTD &= ~(1<<PD1)

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


void rc5_send_command(uint16_t command);
void rc5_send_one(void);
void rc5_send_zero(void);
void delay_880(void);
void rc5_modulation(void);

volatile static uint8_t  rc5_half_bit_cnt, rc5_state;
volatile uint16_t      modulation = 0x00;

uint16_t rc5_byte = 0x00;

int main(void)
{
   DDRB |= (1<<PB7);
   DDRD |= (1<<PD1);
 
   TCCR0A  = (1<<WGM01);  // CTC
   TCCR0B  = (1<<CS01);  // 8 ( ca. 110µs @ 16 MHz )
   TCCR1B  = (1<<CS10);  // 1 ( ca. 36 kHz @ 16 MHz )
   TCCR1B |= (1<<WGM12);  // CTC
   TIMSK0  = (1<<OCIE0A); // OutputCompare0A
   TIMSK1  = (1<<OCIE1A); // OutputCompare1A

   OCR0A  = (0xDB);
   OCR1A  = (0x00DD);

   sei();
 

     
  while(1)
  {
     rc5_send_command(0b11000000001100);
     _delay_ms(100);
   
     //rc5_send_command(0b011111111111110);
  }
}

/* send a 14 bit command to the IR - Receiver */
void rc5_send_command(uint16_t command)
{
   uint16_t send_byte = command;
 
   for (uint16_t x = 0 ; x < 14 ; x++)
   {
     if (send_byte & 0x8000)
     {
       rc5_send_one();
     }
     else
     {
       rc5_send_zero();
     }
     send_byte <<= 0x0001;
   }
}

/* send a logical one to the Receiver ( 1 --> 0 ) */
void rc5_send_one(void)
{ 
   rc5_state |= (0x80);
   RC5_SEND_ON;
   delay_880();
   rc5_state &= ~(0x80);
   RC5_SEND_OFF;
   delay_880();
}

/* send a logical zero to the  Receiver ( 0 --> 1 ) */
void rc5_send_zero(void)
{ 
   RC5_SEND_OFF; 
   delay_880();
   RC5_SEND_ON;
   delay_880();
}

void delay_880(void)
{
   while(1)
   {
     if ((rc5_state & 0x01) == 0x01)
     {
       rc5_half_bit_cnt = 0x00;
       rc5_state &= ~(0x01);
       break;
     }     
   }
}

void rc5_modulation(void)
{
   RC5_SEND_ON;
   _delay_us(3);
   RC5_SEND_OFF;
   _delay_us(3);
}

/* called every 110 µs */
ISR(TIMER0_COMPA_vect)
{
   rc5_half_bit_cnt++;
 
   if (rc5_half_bit_cnt >= RC5_HALF_BIT_TIME)
   {
     rc5_half_bit_cnt = 0x00;
     rc5_state |= 0x01;     
   }
}

/* called every 1,3 µs */
ISR(TIMER1_COMPA_vect)
{
 
}


Würde mich mal wieder über eure Hilfe freuen.
 
Zuletzt bearbeitet:
Hallo Janiiix,

konfiguriere einen Timer so, dass er 72.000 Mal pro Sekunde einen Interrupt erzeugt. In der Timer-ISR läßt du den Ausgang für die IR-LED toggeln, solange der RC5-Signalpegel = H ist. Wenn der RC5-Pegel von H auf L geht, schalte den Ausgang für die IR-LED so, dass die IR-LED aus ist (es könnte sein, dass die mit dem letzten toggeln eingeschaltet wurde).

Bei der Umsetzung in C kann ich dir allerdings nicht helfen.

Gruß
Pirx
 
...einen Timer so, dass er 72.000 Mal pro Sekunde einen Interrupt erzeugt. In der Timer-ISR läßt du den Ausgang für die IR-LED toggeln...
Das kann der Timer doch auch selbst, allerdings sinnigerweise mit PWM und einer Überlauffrequenz von 36kHz...
Für H und L-Pegel wäre dann lediglich der Output-Compare-Mode zu an-/abzuschalten.
Wenn ich das jetzt richtig gesehen habe, ist ein zu sendendes Bit genau 64 Carrier-Perioden lang, ein Halbbit entsprechend 32.

Ich hatte in einem anderen Thread ja mal auf den Tiny28L hingewiesen - der hatte nur einen Timer, konnte aber ein Bein mit bestimmten Frequenzen modullieren (quasi 'ne Ur-Form von PWM). Das besondere dabei war, daß man die Perioden dieses modullierens auch als Taktquelle auf den einzigen Timer legen konnte. Damit brauchte der nur noch bis 32 für die Halbbits zählen. Man hätte also nur für jedes Halbbit einen Interrupt.

Meiner Meinung nach kommt man um 2 Timer nur drumrum, wenn man die 36kHz-Interrupts in Kauf nimmt, und die 32 Perioden für die Halbbits selbst zählt.
Ansonsten könnte man die Sache wie beim Tiny28 nachbasteln, also einen Timer den 36kHz-Carrier erzeugen lassen, den er auf einem Kanal für die LED ausgibt, und auf dem 2ten Kanal den Takteingang des 2ten Timers ansteuert (der somit Counter ist). Dieser läuft nach 32 Takten (Carrier) mit IRQ über, in der ISR wird dann der erste Output Compare Mode des ersten Timers entsprechend der halben Bits festgelegt.

Oder eben einfach blind die Halbbits über den PWM-Carrier schalten.
 
Hallo Leute,

ich habe das jetzt mal mit dem Timer1 gemacht (ihn auf 72kHz konfiguriert) meine Trägerfrequenz steht!
Meine "1" senden passt denke ich auch mal (soweit ich das auf dem Oszi sehen konnte).

Anbei mal zwei Bilder von der Trägerfrequenz und einer Modulierten "1".
Die Modulation, führe ich nun mit nem Flag aus. Ist mein Flag "rc5_state == 0x80" weiß meine 72kHz ISR, dass sie die IR Diode togglen soll. Wird das Flag wieder gelöscht, so geht der Pin auf low.

Sehe ich das richtig, dass ich z.B meinen TV via dieser Sequenz ausschalten bzw einschalten kann?
Bin mir ziemlich unsicher ob ich die Bits richtig zusammen gebastelt habe...



CodeBox C
rc5_send_command(0b00110000000011); // switch tv on



hier noch einmal mein aktueller Quellcode...



CodeBox C
/*
* Send_RC5.c
*
* Created: 24.06.2015 16:58:52
*  Author: Jan
*/


#define F_CPU 16000000

#define RC5_HALF_BIT_TIME 8 // 8 * 110µs = 880µs

#define RC5_SEND_ON     PORTD |=  (1<<PD1) 
#define RC5_SEND_OFF   PORTD &= ~(1<<PD1)
#define RC5_SEND_TOGGLE   PORTD ^=  (1<<PD1)

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


void rc5_send_command(uint16_t command);
void rc5_send_one(void);
void rc5_send_zero(void);
void delay_880(void);
void rc5_modulation(void);

volatile static uint8_t  rc5_half_bit_cnt, rc5_state;
volatile uint16_t      modulation = 0x00;

uint16_t rc5_byte = 0x00;

int main(void)
{
   DDRB |= (1<<PB7);
   DDRD |= (1<<PD1);
 
   TCCR0A  = (1<<WGM01);  // CTC
   TCCR0B  = (1<<CS01);  // 8 ( ca. 110µs @ 16 MHz )
   TCCR1B  = (1<<CS10);  // 1 ( ca. 72 kHz @ 16 MHz )
   TCCR1B |= (1<<WGM12);  // CTC
   TIMSK0  = (1<<OCIE0A); // OutputCompare0A
   TIMSK1  = (1<<OCIE1A); // OutputCompare1A

   OCR0A  = (0xDB);
   OCR1A  = (0x006E);

   sei();
 

     
  while(1)
  {

     //rc5_send_command(0b00110000000011); // switch tv on
     rc5_send_one();
     //_delay_ms(200);
  }
}

/* send a 14 bit command to the IR - Receiver */
void rc5_send_command(uint16_t command)
{
   uint16_t send_byte = command;
 
   for (uint16_t x = 0 ; x < 14 ; x++)
   {
     if (send_byte & 0x8000)
     {
       rc5_send_one();
     }
     else
     {
       rc5_send_zero();
     }
     send_byte <<= 0x0001;
   }
}

/* send a logical one to the Receiver ( 1 --> 0 ) */
void rc5_send_one(void)
{ 
   rc5_state |= 0x80; // flag for modulation "1" @ 36kHz
   RC5_SEND_ON;
   delay_880();
   rc5_state &= ~(0x80);
   RC5_SEND_OFF;
   delay_880();
}

/* send a logical zero to the  Receiver ( 0 --> 1 ) */
void rc5_send_zero(void)
{ 
   rc5_state &= ~(0x80); // flag for modulation "1" @ 36kHz
   RC5_SEND_OFF; 
   delay_880();
   rc5_state |= (0x80);
   RC5_SEND_ON;
   delay_880();
}

void delay_880(void)
{
   while(1)
   {
     if ((rc5_state & 0x01) == 0x01)
     {
       rc5_half_bit_cnt = 0x00;
       rc5_state &= ~(0x01);
       break;
     }
   }
}

/* called every 110 µs */
ISR(TIMER0_COMPA_vect)
{
   rc5_half_bit_cnt++;
 
   if (rc5_half_bit_cnt >= RC5_HALF_BIT_TIME)
   {
     rc5_half_bit_cnt = 0x00;
     rc5_state |= 0x01;     
   }
}

/* 72kHz */
ISR(TIMER1_COMPA_vect)
{
   if ((rc5_state & 0x80) == 0x80)
   {
     RC5_SEND_TOGGLE;   
   }
   else
   {
     RC5_SEND_OFF;
   }
 
}
 

Anhänge

  • 20150628_115805.jpg
    20150628_115805.jpg
    549,3 KB · Aufrufe: 9
  • 20150628_120702.jpg
    20150628_120702.jpg
    664,2 KB · Aufrufe: 10
Hmm... Dein Controller läuft mit 16MHz, schaff also pro Sekunde 16.000.000 Ein-Takt-Instruktionen.
Jetzt läßt Du ihn Interrupts mit 72kHz generieren - das wären dann alle 222 Takte, korrekt?
Warum so kompliziert?

Ich würde einen Timer nur für den Carrier nehmen - allerdings mit PWM. Der soll einfach mit 36kHz überlaufen, und dazwischen bei 50% ein Compare-Match haben.
Der Compare-Pin ist im Datenrichtungsregister als Ausgang eingestellt, und im Port-Latch-Register so, daß die LED aus ist.

Ob die LED (gepulst) leuchten oder (dauerhaft) aus sein soll, kann man dann mit den Compare-Output-Mode-Bits des Beinchens festlegen:
COM1x1..0=0b00 -> Portbit gilt, LED ist aus
COM1x1..0=0b10 -> Timer gilt (nichtinvertierender PWM), LED flimmert. (oder 0b11 mit invertierendem PWM)
Es muß also lediglich eines der beiden COM-Bits gesetzt werden, um die LED flimmern zu lassen, sind beide gelöscht, ist die LED entsprechend des Zustandes im PORT-Bit.
Damit bleibt der 36kHz-Carrier (und das Flimmern) komplett im Hintergrund, Interrupts brauchst Du nur noch für das eventuelle Umschalten, also bei den Halbbits. Dafür dann den anderen Timer, der wesentlich seltener zuschlägt, mehr Zeit hat.

Mit welchem Code sich Dein Fernseher an/abschalten läßt, kann Dir hier sicher keiner sagen (am einfachsten wäre es, wenn Du Dir einfach einen Empfänger zusammensteckst, und das von der Fernbedienung aufzeichnest).
Wenn der überhaupt RC-5 ist...

RC-5 codiert einen 14-Bit-Frame biphasisch (Manchester -> unsere Halbbits), und modulliert das ganze auf einen 36kHz-Carrier (ggf auch andere Frequenzen) - soweit klar.
Die 14 Bits bestehen aus:
  • 2 Startbits (oder nur noch einem Startbit und einem Fieldbit, das den Kommandobereich verdoppelt) - Starrtbits sind immer "1"
  • einem Steuerbit, das bei jeder neuen Tastenbetätigung toggelt (toggelt sie es nicht, ist dieselbe Taste dauerhaft gedrückt (zB Lautstärke etc...))
  • 5 Adressbits (also 2^5=32 Geräte - Dein Fernseher hat da dann eine feste (ggf können das dann auch mehrere sein, zB bei Fernsehern mit integriertem DVD-Laufwerk etc...))
  • 6 Kommandobits, die damit 2^6=64 Kommandos ("Tasten") übertragen - mit dem Trick über das Field-Bit halt 128.
Wenn Du Kommandowert und Geräteadresse kennst, kannst Du das selbst leicht zusammensetzen...
 
Hallo lotadac,

ich weiß das meine Fernbedienung RC5 Code sendet. Mir geht es jetzt nur noch darum ob ich das Kommandobyte richtig zusammen gebastelt habe (für das Ausschalten)...
 
Hier habe ich mal einen Vergleich zwischen meiner Daten Sendung und der meiner Fernbeienung.
Kann man evtl. mit meinem Quellcode und den BIldern einen Fehler in meinem Code finden?
 

Anhänge

  • Fernbedienung.jpg
    Fernbedienung.jpg
    575,4 KB · Aufrufe: 7
  • Meine Sendung.jpg
    Meine Sendung.jpg
    618 KB · Aufrufe: 7
Das Signal von meiner Fernbedienung ist anscheinend auch invertiert oder spielt das keine Rolle?
 
Hallo Dirk,

habe ich schon getan. Hat was gebracht aber mein TV lässt sich immer noch nicht einschalten. Kannst du vill. mal einen Blick auf meinen Code werfen wenn du mal Zeit findest?
 
Eigentlich ist es ja nichts wildes. Ein Datenpaket beinhaltet 14 Bit...

Eine logische "1" fängt mit einer Modulierten (36kHz Carrier) "1" an... Diese Modulation geht bis ca. 889µs, dann kommt eine logische "0" diese geht ebenfalls über 889µs.

Bei einer logischen "0" alles anders herum,,, Das sind so die Fakten.
 
Hier ist ein Fehler drin:


CodeBox C
/* send a 14 bit command to the IR - Receiver */
void rc5_send_command(uint16_t command)
{
  uint16_t send_byte = command;
 
  for (uint16_t x = 0 ; x < 14 ; x++)
  {
     if (send_byte & 0x8000)
     {
        rc5_send_one();
     } else {
        rc5_send_zero();
     }
     send_byte <<= 0x0001;
  }
}


Dies wäre richtig ...
if (send_byte & 0x2000)
oder
if (send_byte & (1<<13))

da du 14 bit überträgst, nicht 16 bit.

Probiere mal ob es mit der Änderung funktioniert.

Dirk :ciao:
 
Wenn ich jetzt als Befehl : 0b01100000000011 eingebe... fehlen ja zwei Bits zu einem Uint16_t...
Diese werden doch folgendermaßen aufgefüllt : 0b0001100000000011.

Die beiden höchstwertigen Bits brauche ich in diesem Fall nicht. Müsste ich diese nicht aus maskieren?
 
Wenn ich jetzt als Befehl : 0b01100000000011 eingebe... fehlen ja zwei Bits zu einem Uint16_t...
Diese werden doch folgendermaßen aufgefüllt : 0b0001100000000011.

Die beiden höchstwertigen Bits brauche ich in diesem Fall nicht. Müsste ich diese nicht aus maskieren?


Das ist ja schon meine Maskierung, nicht wahr ^^ ?
 
Du musst doch gar keine Maske verwenden ... selbst wenn du uint64_t nutzen würdest nicht. Du musst auch nicht alle nicht genutzten hochwertigen Bits "auffüllen" damit alle Bits die der Datentyp fasst definiert sind.

Deine Funktion rc5_send_command untersucht einfach Bit13 bis Bit0 (in dieser Reihenfolge) und sendet dann entsprechend eine "1" oder "0".

Dadurch
if (send_byte & 0x8000)
überträgst du Bit15 bis Bit2 und nicht Bit13 bis Bit0.

Dirk :ciao:
 
Habe es jetzt umgebaut. Leider klappt das immer noch nicht :/
 
In deinem letzten geposteten Code ...
//rc5_send_command(0b00110000000011); // switch tv on
Wenn du nun damit getestet hast, funktioniert es nicht, wegen:
2 Startbits
1 Togglebit
5 Addressbits
6 Commandbits

Prüfe das nochmal.

Wenn es dann immer noch nicht geht:
Vereinfach und ersetze diese delay_880() durch _delay_us(889). Dadurch benötigst du nicht mehr den Timer0CompareA Interrupt, was eine mögliche Fehlerquelle weniger macht. Deaktiviere den Interrupt auch mal.
für _delay_us() vorher F_CPU definieren und danach <util/delay.h> einbinden.
 
So, nun mal den Code angepasst.
Leider empfange ich immer noch nichts (habe mir inzwischen einen IR Decoder gebastelt).
Was mir auch auffällt, wenn ich die Platine nen paar cm von meinem Empfänger weg halte ist der Empfang schon vorbei.
 

Über uns

  • Makerconnect ist ein Forum, welches wir ausschließlich für einen Gedankenaustausch und als Diskussionsplattform für Interessierte bereitstellen, welche sich privat, durch das Studium oder beruflich mit Mikrocontroller- und Kleinstrechnersystemen beschäftigen wollen oder müssen ;-)
  • Dirk
  • Du bist noch kein Mitglied in unserer freundlichen Community? Werde Teil von uns und registriere dich in unserem Forum.
  •  Registriere dich

User Menu

 Kaffeezeit

  • Wir arbeiten hart daran sicherzustellen, dass unser Forum permanent online und schnell erreichbar ist, unsere Forensoftware auf dem aktuellsten Stand ist und der Server regelmäßig gewartet wird. Auch die Themen Datensicherheit und Datenschutz sind uns wichtig und hier sind wir auch ständig aktiv. Alles in allem, sorgen wir uns darum, dass alles Drumherum stimmt :-)

    Dir gefällt das Forum und unsere Arbeit und du möchtest uns unterstützen? Unterstütze uns durch deine Premium-Mitgliedschaft!
    Wir freuen uns auch über eine Spende für unsere Kaffeekasse :-)
    Vielen Dank! :ciao:


     Spende uns! (Paypal)