Skip to content

Commit eecd3d6

Browse files
committed
Refactor to separate hardware-specific, send, and receive code.
1 parent 31d80ab commit eecd3d6

File tree

6 files changed

+797
-66
lines changed

6 files changed

+797
-66
lines changed

IRremote.cpp

+10-65
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
/*
22
* IRremote
3-
* Version 0.11 August, 2009
4-
* Copyright 2009 Ken Shirriff
3+
* Copyright 2009-2010 Ken Shirriff
54
* For details, see http://arcfn.com/2009/08/multi-protocol-infrared-remote-library.html
65
*
76
* Interrupt code based on NECIRrcv by Joe Knapp
@@ -11,9 +10,7 @@
1110

1211
#include "IRremote.h"
1312
#include "IRremoteInt.h"
14-
15-
// Provides ISR
16-
#include <avr/interrupt.h>
13+
#include "IRremoteHw.h"
1714

1815
volatile irparams_t irparams;
1916

@@ -169,79 +166,37 @@ void IRsend::sendRC6(unsigned long data, int nbits)
169166
void IRsend::mark(int time) {
170167
// Sends an IR mark for the specified number of microseconds.
171168
// The mark output is modulated at the PWM frequency.
172-
TCCR2A |= _BV(COM2B1); // Enable pin 3 PWM output
169+
IRremoteEnablePwm();
173170
delayMicroseconds(time);
174171
}
175172

176173
/* Leave pin off for time (given in microseconds) */
177174
void IRsend::space(int time) {
178175
// Sends an IR space for the specified number of microseconds.
179176
// A space is no output, so the PWM output is disabled.
180-
TCCR2A &= ~(_BV(COM2B1)); // Disable pin 3 PWM output
177+
IRremoteDisablePwm();
181178
delayMicroseconds(time);
182179
}
183180

184181
void IRsend::enableIROut(int khz) {
185-
// Enables IR output. The khz value controls the modulation frequency in kilohertz.
186-
// The IR output will be on pin 3 (OC2B).
187-
// This routine is designed for 36-40KHz; if you use it for other values, it's up to you
188-
// to make sure it gives reasonable results. (Watch out for overflow / underflow / rounding.)
189-
// TIMER2 is used in phase-correct PWM mode, with OCR2A controlling the frequency and OCR2B
190-
// controlling the duty cycle.
191-
// There is no prescaling, so the output frequency is 16MHz / (2 * OCR2A)
192-
// To turn the output on and off, we leave the PWM running, but connect and disconnect the output pin.
193-
// A few hours staring at the ATmega documentation and this will all make sense.
194-
// See my Secrets of Arduino PWM at http://arcfn.com/2009/07/secrets-of-arduino-pwm.html for details.
195-
196-
197-
// Disable the Timer2 Interrupt (which is used for receiving IR)
198-
TIMSK2 &= ~_BV(TOIE2); //Timer2 Overflow Interrupt
199-
200-
pinMode(3, OUTPUT);
201-
digitalWrite(3, LOW); // When not sending PWM, we want it low
202-
203-
// COM2A = 00: disconnect OC2A
204-
// COM2B = 00: disconnect OC2B; to send signal set to 10: OC2B non-inverted
205-
// WGM2 = 101: phase-correct PWM with OCRA as top
206-
// CS2 = 000: no prescaling
207-
TCCR2A = _BV(WGM20);
208-
TCCR2B = _BV(WGM22) | _BV(CS20);
209-
210-
// The top value for the timer. The modulation frequency will be SYSCLOCK / 2 / OCR2A.
211-
OCR2A = SYSCLOCK / 2 / khz / 1000;
212-
OCR2B = OCR2A / 3; // 33% duty cycle
182+
IRremoteEnableIRoutput(khz);
213183
}
214184

215185
IRrecv::IRrecv(int recvpin)
216186
{
217187
irparams.recvpin = recvpin;
218188
irparams.blinkflag = 0;
189+
IRremoteRegisterHandler(&interrupt_handler);
219190
}
220191

221192
// initialization
222193
void IRrecv::enableIRIn() {
223-
// setup pulse clock timer interrupt
224-
TCCR2A = 0; // normal mode
225-
226-
//Prescale /8 (16M/8 = 0.5 microseconds per tick)
227-
// Therefore, the timer interval can range from 0.5 to 128 microseconds
228-
// depending on the reset value (255 to 0)
229-
cbi(TCCR2B,CS22);
230-
sbi(TCCR2B,CS21);
231-
cbi(TCCR2B,CS20);
232-
233-
//Timer2 Overflow Interrupt Enable
234-
sbi(TIMSK2,TOIE2);
235-
236-
RESET_TIMER2;
237-
238-
sei(); // enable interrupts
194+
IRremoteEnableIRinput();
239195

240196
// initialize state machine variables
241197
irparams.rcvstate = STATE_IDLE;
242198
irparams.rawlen = 0;
243199

244-
245200
// set pin modes
246201
pinMode(irparams.recvpin, INPUT);
247202
}
@@ -254,17 +209,16 @@ void IRrecv::blink13(int blinkflag)
254209
pinMode(BLINKLED, OUTPUT);
255210
}
256211

257-
// TIMER2 interrupt code to collect raw data.
212+
213+
// interrupt code to collect raw data.
258214
// Widths of alternating SPACE, MARK are recorded in rawbuf.
259215
// Recorded in ticks of 50 microseconds.
260216
// rawlen counts the number of entries recorded so far.
261217
// First entry is the SPACE between transmissions.
262218
// As soon as a SPACE gets long, ready is set, state switches to IDLE, timing of SPACE continues.
263219
// As soon as first MARK arrives, gap width is recorded, ready is cleared, and new logging starts
264-
ISR(TIMER2_OVF_vect)
220+
static void interrupt_handler() {
265221
{
266-
RESET_TIMER2;
267-
268222
uint8_t irdata = (uint8_t)digitalRead(irparams.recvpin);
269223

270224
irparams.timer++; // One more 50us tick
@@ -317,15 +271,6 @@ ISR(TIMER2_OVF_vect)
317271
}
318272
break;
319273
}
320-
321-
if (irparams.blinkflag) {
322-
if (irdata == MARK) {
323-
PORTB |= B00100000; // turn pin 13 LED on
324-
}
325-
else {
326-
PORTB &= B11011111; // turn pin 13 LED off
327-
}
328-
}
329274
}
330275

331276
void IRrecv::resume() {

IRremote.h

-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ class IRrecv
4747
{
4848
public:
4949
IRrecv(int recvpin);
50-
void blink13(int blinkflag);
5150
int decode(decode_results *results);
5251
void enableIRIn();
5352
void resume();

IRremoteHw.cpp

+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/*
2+
* IRremoteHw - hardware-dependent code
3+
* Version 0.11 August, 2009
4+
* Copyright 2009 Ken Shirriff
5+
* For details, see http://arcfn.com/2009/08/multi-protocol-infrared-remote-library.html
6+
*
7+
* Interrupt code based on NECIRrcv by Joe Knapp
8+
* http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1210243556
9+
* Also influenced by http://zovirl.com/2008/11/12/building-a-universal-remote-with-an-arduino/
10+
*/
11+
12+
#include "IRremote.h"
13+
#include "IRremoteInt.h"
14+
// For ISR(...)
15+
#include "avr/interrupt.h"
16+
17+
#define CLKFUDGE 5 // fudge factor for clock interrupt overhead
18+
#define CLK 256 // max value for clock (timer 2)
19+
#define PRESCALE 8 // timer2 clock prescale
20+
#define SYSCLOCK 16000000 // main Arduino clock
21+
#define CLKSPERUSEC (SYSCLOCK/PRESCALE/1000000) // timer clocks per microsecond
22+
#define INIT_TIMER_COUNT2 (CLK - USECPERTICK*CLKSPERUSEC + CLKFUDGE)
23+
24+
void IRremoteEnablePwm() {
25+
// Sends an IR mark for the specified number of microseconds.
26+
// The mark output is modulated at the PWM frequency.
27+
TCCR2A |= _BV(COM2B1); // Enable pin 3 PWM output
28+
}
29+
30+
/* Leave pin off for time (given in microseconds) */
31+
void IRremoteDisablePwm() {
32+
// Sends an IR space for the specified number of microseconds.
33+
// A space is no output, so the PWM output is disabled.
34+
TCCR2A &= ~(_BV(COM2B1)); // Disable pin 3 PWM output
35+
}
36+
37+
void IRremoteEnableIRoutput(int khz) {
38+
// Enables IR output. The khz value controls the modulation frequency in kilohertz.
39+
// The IR output will be on pin 3 (OC2B).
40+
// This routine is designed for 36-40KHz; if you use it for other values, it's up to you
41+
// to make sure it gives reasonable results. (Watch out for overflow / underflow / rounding.)
42+
// TIMER2 is used in phase-correct PWM mode, with OCR2A controlling the frequency and OCR2B
43+
// controlling the duty cycle.
44+
// There is no prescaling, so the output frequency is 16MHz / (2 * OCR2A)
45+
// To turn the output on and off, we leave the PWM running, but connect and disconnect the output pin.
46+
// A few hours staring at the ATmega documentation and this will all make sense.
47+
// See my Secrets of Arduino PWM at http://arcfn.com/2009/07/secrets-of-arduino-pwm.html for details.
48+
49+
50+
// Disable the Timer2 Interrupt (which is used for receiving IR)
51+
TIMSK2 &= ~_BV(TOIE2); //Timer2 Overflow Interrupt
52+
53+
pinMode(3, OUTPUT);
54+
digitalWrite(3, LOW); // When not sending PWM, we want it low
55+
56+
// COM2A = 00: disconnect OC2A
57+
// COM2B = 00: disconnect OC2B; to send signal set to 10: OC2B non-inverted
58+
// WGM2 = 101: phase-correct PWM with OCRA as top
59+
// CS2 = 000: no prescaling
60+
TCCR2A = _BV(WGM20);
61+
TCCR2B = _BV(WGM22) | _BV(CS20);
62+
63+
// The top value for the timer. The modulation frequency will be SYSCLOCK / 2 / OCR2A.
64+
OCR2A = SYSCLOCK / 2 / khz / 1000;
65+
OCR2B = OCR2A / 3; // 33% duty cycle
66+
}
67+
68+
// initialization
69+
void IRremoteEnableIRinput() {
70+
// setup pulse clock timer interrupt
71+
TCCR2A = 0; // normal mode
72+
73+
//Prescale /8 (16M/8 = 0.5 microseconds per tick)
74+
// Therefore, the timer interval can range from 0.5 to 128 microseconds
75+
// depending on the reset value (255 to 0)
76+
cbi(TCCR2B,CS22);
77+
sbi(TCCR2B,CS21);
78+
cbi(TCCR2B,CS20);
79+
80+
//Timer2 Overflow Interrupt Enable
81+
sbi(TIMSK2,TOIE2);
82+
83+
TCNT2 = INIT_TIMER_COUNT2; // Reset TIMER2
84+
85+
sei(); // enable interrupts
86+
}
87+
88+
static void (*handler)() = 0;
89+
90+
void IRremoteRegisterHandler(void (*newhandler)()) {
91+
handler = newhandler;
92+
}
93+
94+
// Callback to the interrupt handler code.
95+
ISR(TIMER2_OVF_vect)
96+
{
97+
TCNT2 = INIT_TIMER_COUNT2; // Reset TIMER2
98+
99+
if (handler != 0) {
100+
(*handler)();
101+
}
102+
}

IRremoteInt.h

+12
Original file line numberDiff line numberDiff line change
@@ -125,5 +125,17 @@ extern volatile irparams_t irparams;
125125
#define MIN_RC5_SAMPLES 11
126126
#define MIN_RC6_SAMPLES 1
127127

128+
// Define the IRremoteHw.cpp "API"
129+
130+
void IRremoteEnablePwm();
131+
132+
void IRremoteDisablePwm();
133+
134+
void IRremoteEnableIRoutput(int khz);
135+
136+
void IRremoteEnableIRinput();
137+
138+
void IRremoteRegisterHandler(void (*newhandle)());
139+
128140
#endif
129141

0 commit comments

Comments
 (0)