超再生接收机价格低廉(2~3块一对),经济实惠,而且接收灵敏度高,但是缺点也很明显,那就是频率受温度漂移大,抗干扰能力差。 再超生模块可以用于315MHz、433MHz等频率。
[caption id="attachment_1285" align="alignnone" width="300"]
![超再生接收模块正面-S](https://static.oschina.net/uploads/img/201612/31204824_MBiR.jpg)
超再生接收模块正面-S[/caption]
[caption id="attachment_1286" align="alignnone" width="200"]
![超再生接收模块背面-S](https://static.oschina.net/uploads/img/201612/31204824_B98w.jpg)
超再生接收模块背面-S[/caption]
[caption id="attachment_1290" align="alignnone" width="150"]
![QQ图片20150329112657-S](https://static.oschina.net/uploads/img/201612/31204825_naoG.jpg)
QQ图片20150329112657-S[/caption]
[caption id="attachment_1291" align="alignnone" width="150"]
![QQ图片20150329112702-S](https://static.oschina.net/uploads/img/201612/31204825_TZES.jpg)
QQ图片20150329112702-S[/caption]
使用Arduino的话,主要使用通过
PWM来实现的。这里采用修改过的IRremote库来做实验。注意,接收模块接线时,从背面数data标志下第二个接口连接到Arduino的11上。发送模块的DATA接到Arduino的3上。 下面是发送端代码: [codesyntax lang="cpp"]
#include <IRremote315.h>
#include <IRremoteInt315.h>
IRsend irsend;
void setup(){
Serial.begin(9600);
pinMode(3, OUTPUT);
}
void loop(){
Serial.println("sendi");
irsend.sendNEC(0x765585, 32);
delay(1000);
}
[/codesyntax] 下面是接收端代码: [codesyntax lang="cpp"]
#include <IRremote315.h>
#include <IRremoteInt315.h>
int RECV_PIN = 11;
int LED1 = 2;
long on1 = 0x00FF906F;
int sta = 1;
IRrecv irrecv(RECV_PIN);
decode_results results;
void dump(decode_results *results){
int count = results->rawlen;
Serial.print("count : ");
Serial.println(count);
if(results->decode_type == UNKNOWN){
Serial.println("count not decode message");
}else{
if(results->decode_type == NEC){
Serial.print("decode nec:");
Serial.print(results->value, HEX);
Serial.print(" (");
Serial.print(results->bits, DEC);
Serial.println("bits)");
}
Serial.print("raw (");
Serial.print(count, DEC);
Serial.print(")");
for(int i = 0; i < count; i++){
if((i % 2) == 1){
Serial.print(results->rawbuf[i] * USECPERTICK, DEC);
}else{
Serial.print(-(int)results->rawbuf[i] * USECPERTICK, DEC);
}
Serial.print(" ");
}
Serial.println("");
}
}
void setup(){
Serial.begin(9600);
pinMode(RECV_PIN, INPUT);
pinMode(LED1, OUTPUT);
pinMode(13, OUTPUT);
irrecv.enableIRIn();
}
int on = 0;
unsigned long last = millis();
void loop(){
if(irrecv.decode(&results)){
if(millis() - last > 250){
on = !on;
digitalWrite(13, on ? HIGH : LOW);
dump(&results);
}
if(results.value = on1){
if(sta == 1){
digitalWrite(LED1, HIGH);
sta = 0;
}else{
digitalWrite(LED1, LOW);
sta = 1;
}
}
last = millis();
irrecv.resume();
}
}
[/codesyntax] 下面是库文件IRremote315/IRremote315.h: [codesyntax lang="cpp"]
class IRsend
{
public:
IRsend() {}
void sendNEC(unsigned long data, int nbits);
void sendRaw(unsigned int buf[], int len, int hz);
// private:
void enableIROut(int khz);
VIRTUAL void mark(int usec);
VIRTUAL void space(int usec);
}
;
// Some useful constants
#define USECPERTICK 50 // microseconds per clock interrupt tick
#define RAWBUF 76 // Length of raw duration buffer
// Marks tend to be 100us too long, and spaces 100us too short
// when received due to sensor lag.
#define MARK_EXCESS 50
#endif
[/codesyntax] 下面是库文件IRremote315/IRremote315.cpp: [codesyntax lang="cpp"]
* IRremote
* Version 0.11 August, 2009
* Copyright 2009 Ken Shirriff
* For details, see http://arcfn.com/2009/08/multi-protocol-infrared-remote-library.html
*
* 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 "IRremote315.h"
#include "IRremoteInt315.h"
// Provides ISR
#include <avr/interrupt.h>
volatile irparams_t irparams;
// These versions of MATCH, MATCH_MARK, and MATCH_SPACE are only for debugging.
// To use them, set DEBUG in IRremoteInt.h
// Normally macros are used for efficiency
#ifdef DEBUG
int MATCH(int measured, int desired) {
Serial.print("Testing: ");
Serial.print(TICKS_LOW(desired), DEC);
Serial.print(" <= ");
Serial.print(measured, DEC);
Serial.print(" <= ");
Serial.println(TICKS_HIGH(desired), DEC);
return measured >= TICKS_LOW(desired) && measured <= TICKS_HIGH(desired);
}
int MATCH_MARK(int measured_ticks, int desired_us) {
Serial.print("Testing mark ");
Serial.print(measured_ticks * USECPERTICK, DEC);
Serial.print(" vs ");
Serial.print(desired_us, DEC);
Serial.print(": ");
Serial.print(TICKS_LOW(desired_us), DEC);
Serial.print(" <= ");
Serial.print(measured_ticks, DEC);
Serial.print(" <= ");
Serial.println(TICKS_HIGH(desired_us), DEC);
return measured_ticks >= TICKS_LOW(desired_us) && measured_ticks <= TICKS_HIGH(desired_us);
}
int MATCH_SPACE(int measured_ticks, int desired_us) {
Serial.print("Testing space ");
Serial.print(measured_ticks * USECPERTICK, DEC);
Serial.print(" vs ");
Serial.print(desired_us, DEC);
Serial.print(": ");
Serial.print(TICKS_LOW(desired_us), DEC);
Serial.print(" <= ");
Serial.print(measured_ticks, DEC);
Serial.print(" <= ");
Serial.println(TICKS_HIGH(desired_us), DEC);
return measured_ticks >= TICKS_LOW(desired_us) && measured_ticks <= TICKS_HIGH(desired_us);
}
#endif
void IRsend::sendNEC(unsigned long data, int nbits)
{
enableIROut(38);
mark(NEC_HDR_MARK);
space(NEC_HDR_SPACE);
for (int i = 0; i < nbits; i++) {
if (data & TOPBIT) {
mark(NEC_BIT_MARK);
space(NEC_ONE_SPACE);
}
else {
mark(NEC_BIT_MARK);
space(NEC_ZERO_SPACE);
}
data <<= 1;
}
mark(NEC_BIT_MARK);
space(0);
}
void IRsend::mark(int time) {
// Sends an IR mark for the specified number of microseconds.
// The mark output is modulated at the PWM frequency.
TCCR2A |= _BV(COM2B1); // Enable pin 3 PWM output
delayMicroseconds(time);
}
/* Leave pin off for time (given in microseconds) */
void IRsend::space(int time) {
// Sends an IR space for the specified number of microseconds.
// A space is no output, so the PWM output is disabled.
TCCR2A &= ~(_BV(COM2B1)); // Disable pin 3 PWM output
delayMicroseconds(time);
}
void IRsend::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)
TIMSK2 &= ~_BV(TOIE2); //Timer2 Overflow Interrupt
pinMode(3, OUTPUT);
digitalWrite(3, LOW); // When not sending PWM, we want it low
// COM2A = 00: disconnect OC2A
// COM2B = 00: disconnect OC2B; to send signal set to 10: OC2B non-inverted
// WGM2 = 101: phase-correct PWM with OCRA as top
// CS2 = 000: no prescaling
TCCR2A = _BV(WGM20);
TCCR2B = _BV(WGM22) | _BV(CS20);
// The top value for the timer. The modulation frequency will be SYSCLOCK / 2 / OCR2A.
OCR2A = SYSCLOCK / 2 / khz / 1000;
OCR2B = OCR2A / 3; // 33% duty cycle
}
IRrecv::IRrecv(int recvpin)
{
irparams.recvpin = recvpin;
irparams.blinkflag = 0;
}
// initialization
void IRrecv::enableIRIn() {
// setup pulse clock timer interrupt
TCCR2A = 0; // normal mode
//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)
cbi(TCCR2B, CS22);
sbi(TCCR2B, CS21);
cbi(TCCR2B, CS20);
//Timer2 Overflow Interrupt Enable
sbi(TIMSK2, TOIE2);
RESET_TIMER2;
sei(); // enable interrupts
// initialize state machine variables
irparams.rcvstate = STATE_IDLE;
irparams.rawlen = 0;
// set pin modes
pinMode(irparams.recvpin, INPUT);
}
// enable/disable blinking of pin 13 on IR processing
void IRrecv::blink13(int blinkflag)
{
irparams.blinkflag = blinkflag;
if (blinkflag)
pinMode(BLINKLED, OUTPUT);
}
// 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(TIMER2_OVF_vect)
{
RESET_TIMER2;
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;
}
if (irparams.blinkflag) {
if (irdata == MARK) {
PORTB |= B00100000; // turn pin 13 LED on
}
else {
PORTB &= B11011111; // turn pin 13 LED off
}
}
}
void IRrecv::resume() {
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 IRrecv::decode(decode_results *results) {
results->rawbuf = irparams.rawbuf;
results->rawlen = irparams.rawlen;
if (irparams.rcvstate != STATE_STOP) {
return ERR;
}
#ifdef DEBUG
Serial.println("Attempting NEC decode");
#endif
if (decodeNEC(results)) {
return DECODED;
}
if (results->rawlen >= 6) {
// Only return raw buffer if at least 6 bits
results->decode_type = UNKNOWN;
results->bits = 0;
results->value = 0;
return DECODED;
}
// Throw away and start over
resume();
return ERR;
}
long IRrecv::decodeNEC(decode_results *results) {
long data = 0;
int offset = 0; // Skip first space
// Initial mark
if (!MATCH_MARK(results->rawbuf[offset], NEC_HDR_MARK)) {
return ERR;
}
offset++;
// Check for repeat
if (irparams.rawlen == 4 &&
MATCH_SPACE(results->rawbuf[offset], NEC_RPT_SPACE) &&
MATCH_MARK(results->rawbuf[offset + 1], NEC_BIT_MARK)) {
results->bits = 0;
results->value = REPEAT;
results->decode_type = NEC;
return DECODED;
}
if (irparams.rawlen < 2 * NEC_BITS + 4) {
return ERR;
}
// Initial space
if (!MATCH_SPACE(results->rawbuf[offset], NEC_HDR_SPACE)) {
return ERR;
}
offset++;
for (int i = 0; i < NEC_BITS; i++) {
if (!MATCH_MARK(results->rawbuf[offset], NEC_BIT_MARK)) {
return ERR;
}
offset++;
if (MATCH_SPACE(results->rawbuf[offset], NEC_ONE_SPACE)) {
data = (data << 1) | 1;
}
else if (MATCH_SPACE(results->rawbuf[offset], NEC_ZERO_SPACE)) {
data <<= 1;
}
else {
return ERR;
}
offset++;
}
// Success
results->bits = NEC_BITS;
results->value = data;
results->decode_type = NEC;
return DECODED;
}
// Gets one undecoded level at a time from the raw buffer.
// The RC5/6 decoding is easier if the data is broken into time intervals.
// E.g. if the buffer has MARK for 2 time intervals and SPACE for 1,
// successive calls to getRClevel will return MARK, MARK, SPACE.
// offset and used are updated to keep track of the current position.
// t1 is the time interval for a single bit in microseconds.
// Returns -1 for error (measured time interval is not a multiple of t1).
int IRrecv::getRClevel(decode_results *results, int *offset, int *used, int t1) {
if (*offset >= results->rawlen) {
// After end of recorded buffer, assume SPACE.
return SPACE;
}
int width = results->rawbuf[*offset];
int val = ((*offset) % 2) ? MARK : SPACE;
int correction = (val == MARK) ? MARK_EXCESS : -MARK_EXCESS;
int avail;
if (MATCH(width, t1 + correction)) {
avail = 1;
}
else if (MATCH(width, 2 * t1 + correction)) {
avail = 2;
}
else if (MATCH(width, 3 * t1 + correction)) {
avail = 3;
}
else {
return -1;
}
(*used)++;
if (*used >= avail) {
*used = 0;
(*offset)++;
}
#ifdef DEBUG
if (val == MARK) {
Serial.println("MARK");
}
else {
Serial.println("SPACE");
}
#endif
return val;
}
[/codesyntax] 下面是库文件IRremote315/IRremoteInt315.h: [codesyntax lang="cpp"]
/*
* IRremote
* Version 0.1 July, 2009
* Copyright 2009 Ken Shirriff
* For details, see http://arcfn.com/2009/08/multi-protocol-infrared-remote-library.html
*
* 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/
*/
#ifndef IRremoteint_h
#define IRremoteint_h
#include <Arduino.h>
#define CLKFUDGE 5 // fudge factor for clock interrupt overhead 时钟中断开销
#define CLK 256 // max value for clock (timer 2)
#define PRESCALE 8 // timer2 clock prescale
#define SYSCLOCK 16000000 // main Arduino clock
#define CLKSPERUSEC (SYSCLOCK/PRESCALE/1000000) // timer clocks per microsecond
#define ERR 0
#define DECODED 1
#define BLINKLED 13
// defines for setting and clearing register bits
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif
// clock timer reset value
#define INIT_TIMER_COUNT2 (CLK - USECPERTICK*CLKSPERUSEC + CLKFUDGE)//此处是为timer2计数器赋予初值
#define RESET_TIMER2 TCNT2 = INIT_TIMER_COUNT2 //TCNT是一个标记器,它标记了timer2的初值,此处的意义是让计数器从开始计算到值256的时间正好是50μs,
// pulse parameters in usec
#define NEC_HDR_MARK 9000
#define NEC_HDR_SPACE 4500
#define NEC_BIT_MARK 560
#define NEC_ONE_SPACE 1600
#define NEC_ZERO_SPACE 560
#define NEC_RPT_SPACE 2250
#define TOLERANCE 50 // percent tolerance in measurements <<---增加了容错率
#define LTOL (1.0 - TOLERANCE/100.)
#define UTOL (1.0 + TOLERANCE/100.)
#define _GAP 5000 // Minimum map between transmissions
#define GAP_TICKS (_GAP/USECPERTICK)
#define TICKS_LOW(us) (int) (((us)*LTOL/USECPERTICK))
#define TICKS_HIGH(us) (int) (((us)*UTOL/USECPERTICK + 1))
#ifndef DEBUG
#define MATCH(measured_ticks, desired_us) ((measured_ticks) >= TICKS_LOW(desired_us) && (measured_ticks) <= TICKS_HIGH(desired_us))
#define MATCH_MARK(measured_ticks, desired_us) MATCH(measured_ticks, (desired_us) + MARK_EXCESS)
#define MATCH_SPACE(measured_ticks, desired_us) MATCH((measured_ticks), (desired_us) - MARK_EXCESS)
// Debugging versions are in IRremote.cpp
#endif
// receiver states
#define STATE_IDLE 2
#define STATE_MARK 3
#define STATE_SPACE 4
#define STATE_STOP 5
// information for the interrupt handler
typedef struct {
uint8_t recvpin; // pin for IR data from detector
uint8_t rcvstate; // state machine
uint8_t blinkflag; // TRUE to enable blinking of pin 13 on IR processing
unsigned int timer; // state timer, counts 50uS ticks.
unsigned int rawbuf[RAWBUF]; // raw data
uint8_t rawlen; // counter of entries in rawbuf
}
irparams_t;
// Defined in IRremote.cpp
extern volatile irparams_t irparams;
// IR detector output is active low
#define MARK 0
#define SPACE 1
#define TOPBIT 0x80000000
#define NEC_BITS 32
#endif
[/codesyntax]
http://www.geek-workshop.com/thread-12589-1-1.html