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
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);
|
|
}
|
|
}
|
|
|