Jako, że od niedawna sam uczę się dopiero C, to proszę bardzo poniżej, najpierw kod do odbiornika RC5. Sprawdzony, krótki i odporny na zakłócenia oraz kody z innych pilotów - działa wyśmienicie i w ciekawy sposób wykorzystuje bit TOGGLE:
// zmienne do obsługi kodów IR w standardzie RC5
volatile uint8_t address; // adres RC5
volatile uint8_t command; // komenda RC5
volatile uint8_t toggle_bit; // bit TOGGLE
volatile uint8_t key_rep; // = 1 gdy klawisz wciśnięty dłużej, = 0 gdy tylko raz
volatile uint8_t key_time; // ilość powtórzeń ramki przy wciąż wciśniętym klawiszu - repeat
volatile uint8_t Ir_key_press_flag; // flaga = 1 - informuje, że odebrany został nowy kod z pilota
// po odczytaniu danych ze zmiennych należy wyzerować flagę aby
// zostały przyjęte kolejne kody z pilota
//
// przykład zastosowania:
/*
if (Ir_key_press_flag)
{
if (address == 0)
{
// np: (gdy polecenie = "jakaś wartość" - wykonaj jakąś funkcję)
if (command == something) .... make_something();
// lub: (gdy polecenie = "jakaś wartość" i tryb repeat....)
if (command == something && key_rep == 1) .... make_something();
// albo: (gdy polecenie = "jakaś wartość" i tryb repeat oraz nastąpiło 10 powtórzeń ramki....)
if (command == something && key_rep == 1 && key_time == 10) .... make_something();
}
// zawsze zerujemy na końcu flagę!!! aby odblokować bufor odbiorczy IR dla kolejnych danych
Ir_key_press_flag = 0;
}
*/
// stałe i zmienne potrzebne na wewnętrzne cele procedury obsługi RC5
// stała czasowa i tolerancja
#define TOLERANCE 200
#define MIN_HALF_BIT 889 - TOLERANCE
#define MAX_HALF_BIT 889 + TOLERANCE
#define MAX_BIT 889+889 + TOLERANCE
volatile uint8_t rc5cnt;
volatile uint8_t last_toggle;
void ir_init()
{
DDR(IR_PORT) RESET IR_IN; // pin jako wejście
PORT(IR_PORT) SET IR_IN; // podciągnięcie pinu do VCC
TCCR1B SET (1<<CS11); // Timer1 / 8
TCCR1B RESET (1<<ICES1); // Zbocze opadające na ICP
TIMSK SET (1<<TICIE1); // Przerwanie od ICP
rc5cnt = 0; // zerowanie licznika występująych zboczy
Ir_key_press_flag = 0; // zerowanie flagi otrzymania kodu z pilota
}
SIGNAL(SIG_INPUT_CAPTURE1)
{
#define FRAME_RESTART 0
#define FRAME_OK 1
#define FRAME_END 2
#define FRAME_ERROR 3
static uint16_t LastCapture;
uint16_t PulseWidth;
static uint8_t IrPulseCount;
static uint16_t IrData;
static uint8_t frame_status;
PulseWidth = ICR1 - LastCapture;
LastCapture = ICR1;
TCCR1B TOGGLE (1<<ICES1); // zmiana zbocza wyzwalającego na przeciwne
if (PulseWidth > MAX_BIT) rc5cnt = 0;
if (rc5cnt > 0) frame_status = FRAME_OK;
if (rc5cnt == 0)
{
IrData = 0;
IrPulseCount = 0;
TCCR1B SET (1<<ICES1);
rc5cnt++;
frame_status = FRAME_END;
}
if (frame_status == FRAME_OK)
{
if ( PulseWidth < MIN_HALF_BIT ) frame_status = FRAME_RESTART; // gdy zakłócenia (szpilki) - RESTART
if ( PulseWidth > MAX_BIT ) frame_status = FRAME_RESTART; // gdy błąd ramki danych (może inny standard niż RC5) RESTART
if (frame_status == FRAME_OK)
{
if (PulseWidth > MAX_HALF_BIT) rc5cnt++;
if (rc5cnt > 1)
if ( (rc5cnt % 2) == 0 )
{
IrData = IrData << 1;
if ( (TCCR1B & (1<<ICES1)) ) IrData |= 0x0001;
IrPulseCount++;
if (IrPulseCount > 12)
{
if (Ir_key_press_flag == 0)
{
command = IrData & 0b0000000000111111;
address = (IrData & 0b0000011111000000) >> 6;
toggle_bit = (IrData & 0b0000100000000000) >> 11;
if (toggle_bit == last_toggle) key_time++;
else key_time = 0;
if (key_time) key_rep = 1;
else key_rep = 0;
last_toggle = toggle_bit;
}
frame_status = FRAME_RESTART;
Ir_key_press_flag = 1;
}
}
rc5cnt++;
}
}
if (frame_status == FRAME_RESTART)
{
rc5cnt = 0;
TCCR1B RESET (1<<ICES1);
}
}
oczywiście słowo wyjaśnienia - (kod pisany dla ATmega8), czasy stałych i procedur dobrane są dla częstotliwości taktowania 8MHz tak więc jeśli więc ktoś chce inną lub inny procek AVR to musi sobie przerobić troszkę kodzik. Jeszcze nie ma "wszczepionego" uniwerslanego przeliczania w zależności od F_CPU. Poza tym program wykorzystuje jak widać w pełni Timer1 oraz wejście ICP mikrokontrolera - więc tylko do niego można podłączyć wejście z odbiornika podczerwieni lub radiowego.
a tu jeszcze niżej kod nadajnika RC5. Tu kod pisany był dla ATtiny2313 także przy częstotliwości taktowania 8MHz. Tutaj też z kolei korzystamy z jednej nóżki OC0A - wyjście PWM Timera0 i przez tranzystor PNP sterujemy diodą IR lub nadajnikiem radiowym
// gdzieś w main - inicjalizacja Timer0 oraz zmiennych
OCR0A = 110;
TCCR0A SET (1<<WGM01);
TCCR0B SET (1<<CS00);
uint8_t toggle_bit = 0;
uint8_t address = 255;
uint8_t command = 255;
void send_rc5_one()
{
czekaj_us(889);
TCCR0A SET (1<<COM0A0);
czekaj_us(889);
TCCR0A RESET (1<<COM0A0);
}
void send_rc5_zero()
{
TCCR0A SET (1<<COM0A0);
czekaj_us(889);
TCCR0A RESET (1<<COM0A0);
czekaj_us(889);
}
void send_rc5()
{
uint16_t data = 0;
// i musi być 16 bo transmisja zaczyna się od
// najstarszego bitu
uint8_t i = 16;
data |= ( (address<<11)|(command<<5) );
send_rc5_one();
#if IR_MODE == RC5
send_rc5_one();
#endif
#if IR_MODE == RC6
if (command & (1<<7)) send_rc5_one();
else send_rc5_zero();
#endif
if ( toggle_bit ) send_rc5_one();
else send_rc5_zero();
while (i>5)
{
i--;
if ( !(data & (1<<i)) ) send_rc5_zero();
else send_rc5_one();
}
}
przykład użycia nadawania RC5:
toggle_bit ^= (1<<0);
address = 0; // adres dla TV
command = 1; // klawisz nr.1 z klawiaturki numerycznej pilota
send_rc5;