You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

131 lines
5.6 KiB

/* IRrecvPCI.cpp
* Part of IRLib Library for Arduino receiving, decoding, and sending
* infrared signals. See COPYRIGHT.txt and LICENSE.txt for more information.
*
* This module implements the IRrecvPCI receiver class which uses pin change interrupts to
* poll the input pin. This class gives more accurate results than the 50us timer based
* IRrecv but it has other limitations. It uses the Arduino "attachInterrupt()" function
* which can conflict with other libraries. Also unless you use an external buffer and
* enable auto resume this receiver occasionally fails to receive the second of 2 quickly
* sent frames. If you do not have sufficient RAM for a second buffer you should consider
* using the other two available receiver classes.
*
* This receiver is based in part on Arduino firmware for use with AnalysIR IR signal analysis
* software for Windows PCs. Many thanks to the people at http://analysir.com for their
* assistance in developing this section of code.
*/
#include "IRLibRecvPCI.h"
#include "IRLibHardware.h" //needed for IRLib_didIROut
void IRrecvPCI_Handler();//prototype for interrupt handler
/* Note that the constructor is passed the interrupt number rather than the pin number.
* WARNING: These interrupt numbers which are passed to “attachInterrupt()” are not
* necessarily identical to the interrupt numbers in the datasheet of the processor chip
* you are using. Rather it is a numbering system to the “attachInterrupt()” Arduino
* function. For more information on attachInterrupt see
* http://arduino.cc/en/Reference/AttachInterrupt
*/
IRrecvPCI::IRrecvPCI(uint8_t pin) {
init();
intrNum=digitalPinToInterrupt(pin);
recvGlobal.recvPin=pin;
}
/* Initializes receiving and attaches the pin change interrupt handler. Call this to
* initialize it again to resume receiving after completing the decoding. Previous versions
* of IRLib had a "resume" method that you should use this in either initializing or resuming.
*/
void IRrecvPCI::enableIRIn(void) {
//See comments on IRrecv::enableIRIn in "IRLibRecv.cpp" for explanation of this logic
if(recvGlobal.newDataAvailable)
return;
recvGlobal.decoderWantsData=true;//otherwise he wouldn't have called
if( (recvGlobal.currentState==STATE_FINISHED) || IRLib_didIROut ) {
IRrecvBase::enableIRIn();
recvGlobal.timer=micros();
attachInterrupt(intrNum, IRrecvPCI_Handler, CHANGE);
}
};
/* Even when not sampling inputs and recording pulse timing, the ISR remains active
* continues to interrupt every 50us however it remains in a do-nothing state. If
* the user wants to truly shut down the ISR they can call this method.
*/
void IRrecvPCI::disableIRIn(void) {
detachInterrupt(intrNum);
IRrecvBase::disableIRIn();
}
/* Returns true if a frame of data is available in the buffer. Most of the
* handling is done by the base method. Because the ISR only is active when the pin
* is changing, it may not recognize the long gap at the end of the frame so we need to
* test to see if we had a long gap. Because the ISR is active, we need to ensure
* that it doesn't change any variables while we are in the middle of accessing them.
* Unfortunately the ATOMIC_BLOCK macro that we used to use is not supported for
* SAMD 21 platforms so we have to use "noInterrupts();" and "interrupts();"
* This is only necessary for multibyte variables.
*/
bool IRrecvPCI::getResults(void) {
if(recvGlobal.newDataAvailable) {
IRrecvBase::getResults();
return true;
}
//ISR hasn't detected end of frame so if running we will take a peek
if(recvGlobal.currentState==STATE_RUNNING) {
//Only check for timeout if it is a SPACE
if(digitalRead(recvGlobal.recvPin)) {//pin high means SPACE
uint32_t changeTime; //time of the last change
noInterrupts (); //Ensure atomic access of volatile variable
changeTime=recvGlobal.timer;
interrupts(); //restore interrupts
if( (micros()-changeTime) > recvGlobal.frameTimeout) {
IRLib_IRrecvComplete(3);
IRrecvBase::getResults();
return true;
}
}
}
return false;
};
/* This is the interrupt handler used by this class. It is called every time the input
* pin changes from high to low or from low to high. The initial state of the state machine
* is STATE_READY_TO_BEGIN. It waits until it sees a MARK before it switches to
* STATE_RUNNING. Each subsequent interrupt it computes the time since the previous
* interrupt. If it notices a SPACE is especially long then it calls IRLib_IRrecvComplete
* which sets the proper flags to say that we had received data and implements
* auto resume if enabled. The state STATE_FINISHED means that interrupts are continuing
* however the receiver isn't ready for more data.
*/
void IRrecvPCI_Handler(){
uint32_t volatile changeTime=micros();
uint32_t deltaTime=changeTime-recvGlobal.timer;
switch(recvGlobal.currentState) {
case STATE_FINISHED: return;
case STATE_RUNNING:
IRLib_doBlink();
if( !(recvGlobal.recvLength & 1) ){
if (deltaTime>recvGlobal.frameTimeout) {
IRLib_IRrecvComplete(1);//all finished, reset and possibly do auto resume
return;//don't record final space
}
}
break;
case STATE_READY_TO_BEGIN:
if(digitalRead(recvGlobal.recvPin)) {//pin high means SPACE
return;//don't start until we get a MARK
} else {
recvGlobal.currentState=STATE_RUNNING;
}
break;
};
recvGlobal.recvBuffer[recvGlobal.recvLength]=deltaTime;
recvGlobal.timer=changeTime;
if(++recvGlobal.recvLength>=RECV_BUF_LENGTH) {//buffer overflows so we quit
IRLib_IRrecvComplete(2);
}
}