370 lines
10 KiB
C++
370 lines
10 KiB
C++
/*
|
|
* IRremote
|
|
* Version 0.11 August, 2009
|
|
* Copyright 2009 Ken Shirriff
|
|
* For details, see http://arcfn.com/2009/08/multi-protocol-infrared-remote-library.html
|
|
*
|
|
* Modified by Paul Stoffregen <paul@pjrc.com> to support other boards and timers
|
|
* Modified by Mitra Ardron <mitra@mitra.biz>
|
|
* Added Sanyo and Mitsubishi controllers
|
|
* Modified Sony to spot the repeat codes that some Sony's send
|
|
*
|
|
* Modifier by
|
|
* Interrupt code based on NECIRrcv by Joe Knapp
|
|
* http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1210243556
|
|
* Also influenced by http://zovirl.com/2008/11/12/building-a-universal-remote-with-an-arduino/
|
|
*
|
|
*/
|
|
|
|
#include "IRSendrev.h"
|
|
#include "IRsendRevInt.h"
|
|
|
|
// Provides ISR
|
|
#include <avr/interrupt.h>
|
|
|
|
volatile irparams_t irparams;
|
|
|
|
void IRSendRev::sendRaw(unsigned int buf[], int len, int hz)
|
|
{
|
|
enableIROut(hz);
|
|
|
|
for (int i = 0; i < len; i++) {
|
|
if (i & 1) {
|
|
space(buf[i]);
|
|
}
|
|
else {
|
|
mark(buf[i]);
|
|
}
|
|
}
|
|
space(0); // Just to be sure
|
|
}
|
|
|
|
void IRSendRev::mark(int time) {
|
|
// Sends an IR mark for the specified number of microseconds.
|
|
// The mark output is modulated at the PWM frequency.
|
|
TIMER_ENABLE_PWM; // Enable pin 3 PWM output
|
|
delayMicroseconds(time);
|
|
}
|
|
|
|
/* Leave pin off for time (given in microseconds) */
|
|
void IRSendRev::space(int time) {
|
|
// Sends an IR space for the specified number of microseconds.
|
|
// A space is no output, so the PWM output is disabled.
|
|
TIMER_DISABLE_PWM; // Disable pin 3 PWM output
|
|
delayMicroseconds(time);
|
|
}
|
|
|
|
void IRSendRev::enableIROut(int khz) {
|
|
// Enables IR output. The khz value controls the modulation frequency in kilohertz.
|
|
// The IR output will be on pin 3 (OC2B).
|
|
// This routine is designed for 36-40KHz; if you use it for other values, it's up to you
|
|
// to make sure it gives reasonable results. (Watch out for overflow / underflow / rounding.)
|
|
// TIMER2 is used in phase-correct PWM mode, with OCR2A controlling the frequency and OCR2B
|
|
// controlling the duty cycle.
|
|
// There is no prescaling, so the output frequency is 16MHz / (2 * OCR2A)
|
|
// To turn the output on and off, we leave the PWM running, but connect and disconnect the output pin.
|
|
// A few hours staring at the ATmega documentation and this will all make sense.
|
|
// See my Secrets of Arduino PWM at http://arcfn.com/2009/07/secrets-of-arduino-pwm.html for details.
|
|
|
|
// Disable the Timer2 Interrupt (which is used for receiving IR)
|
|
TIMER_DISABLE_INTR; //Timer2 Overflow Interrupt
|
|
|
|
pinMode(TIMER_PWM_PIN, OUTPUT);
|
|
digitalWrite(TIMER_PWM_PIN, LOW); // When not sending PWM, we want it low
|
|
|
|
TIMER_CONFIG_KHZ(khz);
|
|
TIMER_ENABLE_PWM;
|
|
|
|
}
|
|
|
|
void IRSendRev::Init(int revPin)
|
|
{
|
|
irparams.recvpin = revPin;
|
|
|
|
enableIRIn(); // Start the receiver
|
|
delay(20);
|
|
Clear();
|
|
}
|
|
|
|
void IRSendRev::Init()
|
|
{
|
|
delay(20);
|
|
Clear();
|
|
}
|
|
// initialization
|
|
void IRSendRev::enableIRIn() {
|
|
cli();
|
|
// setup pulse clock timer interrupt
|
|
//Prescale /8 (16M/8 = 0.5 microseconds per tick)
|
|
// Therefore, the timer interval can range from 0.5 to 128 microseconds
|
|
// depending on the reset value (255 to 0)
|
|
TIMER_CONFIG_NORMAL();
|
|
|
|
//Timer2 Overflow Interrupt Enable
|
|
TIMER_ENABLE_INTR;
|
|
|
|
TIMER_RESET;
|
|
|
|
sei(); // enable interrupts
|
|
|
|
// initialize state machine variables
|
|
irparams.rcvstate = STATE_IDLE;
|
|
irparams.rawlen = 0;
|
|
|
|
// set pin modes
|
|
pinMode(irparams.recvpin, INPUT);
|
|
}
|
|
|
|
// TIMER2 interrupt code to collect raw data.
|
|
// Widths of alternating SPACE, MARK are recorded in rawbuf.
|
|
// Recorded in ticks of 50 microseconds.
|
|
// rawlen counts the number of entries recorded so far.
|
|
// First entry is the SPACE between transmissions.
|
|
// As soon as a SPACE gets long, ready is set, state switches to IDLE, timing of SPACE continues.
|
|
// As soon as first MARK arrives, gap width is recorded, ready is cleared, and new logging starts
|
|
|
|
ISR(TIMER_INTR_NAME)
|
|
{
|
|
TIMER_RESET;
|
|
|
|
uint8_t irdata = (uint8_t)digitalRead(irparams.recvpin);
|
|
|
|
irparams.timer++; // One more 50us tick
|
|
if (irparams.rawlen >= RAWBUF) {
|
|
// Buffer overflow
|
|
irparams.rcvstate = STATE_STOP;
|
|
}
|
|
switch(irparams.rcvstate) {
|
|
case STATE_IDLE: // In the middle of a gap
|
|
if (irdata == MARK) {
|
|
if (irparams.timer < GAP_TICKS) {
|
|
// Not big enough to be a gap.
|
|
irparams.timer = 0;
|
|
}
|
|
else {
|
|
// gap just ended, record duration and start recording transmission
|
|
irparams.rawlen = 0;
|
|
irparams.rawbuf[irparams.rawlen++] = irparams.timer;
|
|
irparams.timer = 0;
|
|
irparams.rcvstate = STATE_MARK;
|
|
}
|
|
}
|
|
break;
|
|
case STATE_MARK: // timing MARK
|
|
if (irdata == SPACE) { // MARK ended, record time
|
|
irparams.rawbuf[irparams.rawlen++] = irparams.timer;
|
|
irparams.timer = 0;
|
|
irparams.rcvstate = STATE_SPACE;
|
|
}
|
|
break;
|
|
case STATE_SPACE: // timing SPACE
|
|
if (irdata == MARK) { // SPACE just ended, record it
|
|
irparams.rawbuf[irparams.rawlen++] = irparams.timer;
|
|
irparams.timer = 0;
|
|
irparams.rcvstate = STATE_MARK;
|
|
}
|
|
else { // SPACE
|
|
if (irparams.timer > GAP_TICKS) {
|
|
// big SPACE, indicates gap between codes
|
|
// Mark current code as ready for processing
|
|
// Switch to STOP
|
|
// Don't reset timer; keep counting space width
|
|
irparams.rcvstate = STATE_STOP;
|
|
}
|
|
}
|
|
break;
|
|
case STATE_STOP: // waiting, measuring gap
|
|
if (irdata == MARK) { // reset gap timer
|
|
irparams.timer = 0;
|
|
}
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
void IRSendRev::Clear() {
|
|
irparams.rcvstate = STATE_IDLE;
|
|
irparams.rawlen = 0;
|
|
}
|
|
|
|
// Decodes the received IR message
|
|
// Returns 0 if no data ready, 1 if data ready.
|
|
// Results of decoding are stored in results
|
|
int IRSendRev::decode(decode_results *results) {
|
|
results->rawbuf = irparams.rawbuf;
|
|
results->rawlen = irparams.rawlen;
|
|
if (irparams.rcvstate != STATE_STOP) {
|
|
return ERR;
|
|
}
|
|
// Throw away and start over
|
|
Clear();
|
|
return 1;
|
|
}
|
|
|
|
unsigned char IRSendRev::Recv(unsigned char *revData)
|
|
{
|
|
int count = results.rawlen;
|
|
int nshort = 0;
|
|
int nlong = 0;
|
|
int count_data = 0;
|
|
|
|
count_data = (count-4)/16;
|
|
|
|
for(int i = 0; i<10; i++) // count nshort
|
|
{
|
|
nshort += results.rawbuf[3+2*i];
|
|
}
|
|
nshort /= 10;
|
|
|
|
int i = 0;
|
|
int j = 0;
|
|
while(1) // count nlong
|
|
{
|
|
if(results.rawbuf[4+2*i] > (2*nshort))
|
|
{
|
|
nlong += results.rawbuf[4+2*i];
|
|
j++;
|
|
}
|
|
i++;
|
|
if(j==10)break;
|
|
if((4+2*i)>(count-10))break;
|
|
}
|
|
nlong /= j;
|
|
|
|
int doubleshort = 2*nshort;
|
|
for(i = 0; i<count_data; i++)
|
|
{
|
|
revData[i+D_DATA] = 0x00;
|
|
for(j = 0; j<8; j++)
|
|
{
|
|
if(results.rawbuf[4 + 16*i + j*2] > doubleshort) // 1
|
|
{
|
|
revData[i+D_DATA] |= 0x01<< (7-j);
|
|
}
|
|
else
|
|
{
|
|
revData[i+D_DATA] &= ~(0x01<<(7-j));
|
|
}
|
|
}
|
|
}
|
|
revData[D_LEN] = count_data+5;
|
|
revData[D_STARTH] = results.rawbuf[1];
|
|
revData[D_STARTL] = results.rawbuf[2];
|
|
revData[D_SHORT] = nshort;
|
|
revData[D_LONG] = nlong;
|
|
revData[D_DATALEN] = count_data;
|
|
|
|
#if __DEBUG
|
|
Serial.print("\r\n*************************************************************\r\n");
|
|
Serial.print("len\t = ");Serial.println(revData[D_LEN]);
|
|
Serial.print("start_h\t = ");Serial.println(revData[D_STARTH]);
|
|
Serial.print("start_l\t = ");Serial.println(revData[D_STARTL]);
|
|
Serial.print("short\t = ");Serial.println(revData[D_SHORT]);
|
|
Serial.print("long\t = ");Serial.println(revData[D_LONG]);
|
|
Serial.print("data_len = ");Serial.println(revData[D_DATALEN]);
|
|
for(int i = 0; i<revData[D_DATALEN]; i++)
|
|
{
|
|
Serial.print(revData[D_DATA+i]);Serial.print("\t");
|
|
}
|
|
Serial.print("\r\n*************************************************************\r\n");
|
|
#endif
|
|
|
|
Clear(); // Receive the next value
|
|
return revData[D_LEN]+1;
|
|
}
|
|
|
|
//if get some data from IR
|
|
unsigned char IRSendRev::IsDta()
|
|
{
|
|
|
|
if(decode(&results))
|
|
{
|
|
int count = results.rawlen;
|
|
if(count < 64 || (count -4)%8 != 0)
|
|
{
|
|
#if __DEBUG
|
|
Serial.print("IR GET BAD DATA!\r\n");
|
|
#endif
|
|
Clear(); // Receive the next value
|
|
return 0;
|
|
}
|
|
int count_data = (count-4) / 16;
|
|
#if __DEBUG
|
|
Serial.print("ir get data! count_data = ");
|
|
Serial.println(count_data);
|
|
#endif
|
|
return (unsigned char)(count_data+6);
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
}
|
|
|
|
void IRSendRev::Send(unsigned char *idata, unsigned char ifreq)
|
|
{
|
|
int len = idata[0];
|
|
unsigned char start_high = idata[1];
|
|
unsigned char start_low = idata[2];
|
|
unsigned char nshort = idata[3];
|
|
unsigned char nlong = idata[4];
|
|
unsigned char datalen = idata[5];
|
|
|
|
unsigned int *pSt = (unsigned int *)malloc((4+datalen*16)*sizeof(unsigned int));
|
|
|
|
if(NULL == pSt)
|
|
{
|
|
#if __DEBUG
|
|
Serial.println("not enough place!!\r\n");
|
|
#endif
|
|
exit(1);
|
|
}
|
|
|
|
#if __DEBUG
|
|
Serial.println("begin to send ir:\r\n");
|
|
Serial.print("ifreq = ");Serial.println(ifreq);
|
|
Serial.print("len = ");Serial.println(len);
|
|
Serial.print("start_high = ");Serial.println(start_high);
|
|
Serial.print("start_low = ");Serial.println(start_low);
|
|
Serial.print("nshort = ");Serial.println(nshort);
|
|
Serial.print("nlong = ");Serial.println(nlong);
|
|
Serial.print("datalen = ");Serial.println(datalen);
|
|
#endif
|
|
|
|
pSt[0] = start_high*50;
|
|
pSt[1] = start_low*50;
|
|
|
|
for(int i = 0; i<datalen; i++)
|
|
{
|
|
for(int j = 0; j<8; j++)
|
|
{
|
|
if(idata[6+i] & 0x01<<(7-j))
|
|
{
|
|
pSt[16*i + 2*j + 2] = nshort*50;
|
|
pSt[16*i + 2*j+3] = nlong*50;
|
|
}
|
|
else
|
|
{
|
|
pSt[16*i + 2*j+2] = nshort*50;
|
|
pSt[16*i + 2*j+3] = nshort*50;
|
|
}
|
|
}
|
|
}
|
|
|
|
pSt[2+datalen*16] = nshort*50;
|
|
pSt[2+datalen*16+1] = nshort*50;
|
|
|
|
#if __DEBUG
|
|
for(int i = 0; i<4+datalen*16; i++)
|
|
{
|
|
Serial.print(pSt[i]);Serial.print("\t");
|
|
}
|
|
Serial.println();
|
|
#endif
|
|
sendRaw(pSt, 4+datalen*16, ifreq);
|
|
free(pSt);
|
|
|
|
}
|
|
|
|
IRSendRev IR; |