Исходная библиотека IRremote
This commit is contained in:
1 parent
3402beccdf
commit
c9055e36b3
122 files changed
+38600
No files matched your search
@@ -0,0 +1,347 @@
|
||||
/*
|
||||
* TinyIRSender.hpp
|
||||
*
|
||||
* Sends IR protocol data of NEC and FAST protocol using bit banging.
|
||||
* NEC is the protocol of most cheap remote controls for Arduino.
|
||||
*
|
||||
* The FAST protocol is a proprietary modified JVC protocol without address, with parity and with a shorter header.
|
||||
* FAST Protocol characteristics:
|
||||
* - Bit timing is like NEC or JVC
|
||||
* - The header is shorter, 3156 vs. 12500
|
||||
* - No address and 16 bit data, interpreted as 8 bit command and 8 bit inverted command,
|
||||
* leading to a fixed protocol length of (6 + (16 * 3) + 1) * 526 = 55 * 526 = 28930 microseconds or 29 ms.
|
||||
* - Repeats are sent as complete frames but in a 50 ms period / with a 21 ms distance.
|
||||
*
|
||||
*
|
||||
* This file is part of IRMP https://github.com/IRMP-org/IRMP.
|
||||
* This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote.
|
||||
*
|
||||
************************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2024 Armin Joachimsmeyer
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
||||
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
************************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef _TINY_IR_SENDER_HPP
|
||||
#define _TINY_IR_SENDER_HPP
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
//#define ENABLE_NEC2_REPEATS // Instead of sending / receiving the NEC special repeat code, send / receive the original frame for repeat.
|
||||
|
||||
#if defined(DEBUG) && !defined(LOCAL_DEBUG)
|
||||
#define LOCAL_DEBUG
|
||||
#else
|
||||
//#define LOCAL_DEBUG // This enables debug output only for this file
|
||||
#endif
|
||||
#include "TinyIR.h" // Defines protocol timings
|
||||
|
||||
#include "digitalWriteFast.h"
|
||||
/** \addtogroup TinySender Minimal sender for NEC and FAST protocol
|
||||
* @{
|
||||
*/
|
||||
|
||||
#if !defined(IR_SEND_PIN)
|
||||
#warning "IR_SEND_PIN is not defined, so it is set to 3"
|
||||
#define IR_SEND_PIN 3
|
||||
#endif
|
||||
/*
|
||||
* Generate 38 kHz IR signal by bit banging
|
||||
*/
|
||||
void sendMark(uint8_t aSendPin, unsigned int aMarkMicros) {
|
||||
unsigned long tStartMicros = micros();
|
||||
unsigned long tNextPeriodEnding = tStartMicros;
|
||||
unsigned long tMicros;
|
||||
do {
|
||||
/*
|
||||
* Generate pulse
|
||||
*/
|
||||
noInterrupts(); // do not let interrupts extend the short on period
|
||||
digitalWriteFast(aSendPin, HIGH);
|
||||
delayMicroseconds(8); // 8 us for a 30 % duty cycle for 38 kHz
|
||||
digitalWriteFast(aSendPin, LOW);
|
||||
interrupts(); // Enable interrupts - to keep micros correct- for the longer off period 3.4 us until receive ISR is active (for 7 us + pop's)
|
||||
|
||||
/*
|
||||
* PWM pause timing and end check
|
||||
* Minimal pause duration is 4.3 us
|
||||
*/
|
||||
tNextPeriodEnding += 26; // for 38 kHz
|
||||
do {
|
||||
tMicros = micros(); // we have only 4 us resolution for AVR @16MHz
|
||||
/*
|
||||
* Exit the forever loop if aMarkMicros has reached
|
||||
*/
|
||||
unsigned int tDeltaMicros = tMicros - tStartMicros;
|
||||
#if defined(__AVR__)
|
||||
// Just getting variables and check for end condition takes minimal 3.8 us
|
||||
if (tDeltaMicros >= aMarkMicros - (112 / (F_CPU / MICROS_IN_ONE_SECOND))) { // To compensate for call duration - 112 is an empirical value
|
||||
#else
|
||||
if (tDeltaMicros >= aMarkMicros) {
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
} while (tMicros < tNextPeriodEnding);
|
||||
} while (true);
|
||||
}
|
||||
|
||||
/*
|
||||
* Send NEC with 16 bit address and command, even if aCommand < 0x100 (I.E. ONKYO)
|
||||
* @param aAddress - The 16 bit address to send.
|
||||
* @param aCommand - The 16 bit command to send.
|
||||
* @param aNumberOfRepeats - Number of repeats send at a period of 110 ms.
|
||||
* @param aSendNEC2Repeats - Instead of sending the NEC special repeat code, send the original frame for repeat.
|
||||
*/
|
||||
void sendONKYO(uint8_t aSendPin, uint16_t aAddress, uint16_t aCommand, uint_fast8_t aNumberOfRepeats, bool aSendNEC2Repeats) {
|
||||
pinModeFast(aSendPin, OUTPUT);
|
||||
|
||||
uint_fast8_t tNumberOfCommands = aNumberOfRepeats + 1;
|
||||
while (tNumberOfCommands > 0) {
|
||||
unsigned long tStartOfFrameMillis = millis();
|
||||
|
||||
sendMark(aSendPin, NEC_HEADER_MARK);
|
||||
if ((!aSendNEC2Repeats) && (tNumberOfCommands < aNumberOfRepeats + 1)) {
|
||||
// send the NEC special repeat
|
||||
delayMicroseconds(NEC_REPEAT_HEADER_SPACE); // - 2250
|
||||
} else {
|
||||
// send header
|
||||
delayMicroseconds(NEC_HEADER_SPACE);
|
||||
LongUnion tData;
|
||||
tData.UWord.LowWord = aAddress;
|
||||
tData.UWord.HighWord = aCommand;
|
||||
// Send data
|
||||
for (uint_fast8_t i = 0; i < NEC_BITS; ++i) {
|
||||
sendMark(aSendPin, NEC_BIT_MARK); // constant mark length
|
||||
if (tData.ULong & 1) {
|
||||
delayMicroseconds(NEC_ONE_SPACE);
|
||||
} else {
|
||||
delayMicroseconds(NEC_ZERO_SPACE);
|
||||
}
|
||||
tData.ULong >>= 1; // shift command for next bit
|
||||
}
|
||||
} // send stop bit
|
||||
sendMark(aSendPin, NEC_BIT_MARK);
|
||||
|
||||
tNumberOfCommands--;
|
||||
// skip last delay!
|
||||
if (tNumberOfCommands > 0) {
|
||||
/*
|
||||
* Check and fallback for wrong RepeatPeriodMillis parameter. I.e the repeat period must be greater than each frame duration.
|
||||
*/
|
||||
auto tFrameDurationMillis = millis() - tStartOfFrameMillis;
|
||||
if (NEC_REPEAT_PERIOD / 1000 > tFrameDurationMillis) {
|
||||
delay(NEC_REPEAT_PERIOD / 1000 - tFrameDurationMillis);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Send NEC with 8 or 16 bit address or command depending on the values of aAddress and aCommand.
|
||||
* @param aAddress - If aAddress < 0x100 send 8 bit address and 8 bit inverted address, else send 16 bit address.
|
||||
* @param aCommand - If aCommand < 0x100 send 8 bit command and 8 bit inverted command, else send 16 bit command.
|
||||
* @param aNumberOfRepeats - Number of repeats send at a period of 110 ms.
|
||||
* @param aSendNEC2Repeats - Instead of sending the NEC special repeat code, send the original frame for repeat.
|
||||
*/
|
||||
void sendNECMinimal(uint8_t aSendPin, uint16_t aAddress, uint16_t aCommand, uint_fast8_t aNumberOfRepeats) {
|
||||
sendNEC(aSendPin, aAddress, aCommand, aNumberOfRepeats); // sendNECMinimal() is deprecated
|
||||
}
|
||||
void sendNEC(uint8_t aSendPin, uint16_t aAddress, uint16_t aCommand, uint_fast8_t aNumberOfRepeats, bool aSendNEC2Repeats) {
|
||||
pinModeFast(aSendPin, OUTPUT);
|
||||
|
||||
uint_fast8_t tNumberOfCommands = aNumberOfRepeats + 1;
|
||||
while (tNumberOfCommands > 0) {
|
||||
unsigned long tStartOfFrameMillis = millis();
|
||||
|
||||
sendMark(aSendPin, NEC_HEADER_MARK);
|
||||
if ((!aSendNEC2Repeats) && (tNumberOfCommands < aNumberOfRepeats + 1)) {
|
||||
// send the NEC special repeat
|
||||
delayMicroseconds(NEC_REPEAT_HEADER_SPACE); // - 2250
|
||||
} else {
|
||||
// send header
|
||||
delayMicroseconds(NEC_HEADER_SPACE);
|
||||
LongUnion tData;
|
||||
/*
|
||||
* The compiler is intelligent and removes the code for "(aAddress > 0xFF)" if we are called with an uint8_t address :-).
|
||||
* Using an uint16_t address requires additional 28 bytes program memory.
|
||||
*/
|
||||
if (aAddress > 0xFF) {
|
||||
tData.UWord.LowWord = aAddress;
|
||||
} else {
|
||||
tData.UByte.LowByte = aAddress; // LSB first
|
||||
tData.UByte.MidLowByte = ~aAddress;
|
||||
}
|
||||
if (aCommand > 0xFF) {
|
||||
tData.UWord.HighWord = aCommand;
|
||||
} else {
|
||||
tData.UByte.MidHighByte = aCommand;
|
||||
tData.UByte.HighByte = ~aCommand; // LSB first
|
||||
}
|
||||
// Send data
|
||||
for (uint_fast8_t i = 0; i < NEC_BITS; ++i) {
|
||||
sendMark(aSendPin, NEC_BIT_MARK); // constant mark length
|
||||
|
||||
if (tData.ULong & 1) {
|
||||
delayMicroseconds(NEC_ONE_SPACE);
|
||||
} else {
|
||||
delayMicroseconds(NEC_ZERO_SPACE);
|
||||
}
|
||||
tData.ULong >>= 1; // shift command for next bit
|
||||
}
|
||||
} // send stop bit
|
||||
sendMark(aSendPin, NEC_BIT_MARK);
|
||||
|
||||
tNumberOfCommands--;
|
||||
// skip last delay!
|
||||
if (tNumberOfCommands > 0) {
|
||||
/*
|
||||
* Check and fallback for wrong RepeatPeriodMillis parameter. I.e the repeat period must be greater than each frame duration.
|
||||
*/
|
||||
auto tFrameDurationMillis = millis() - tStartOfFrameMillis;
|
||||
if (NEC_REPEAT_PERIOD / 1000 > tFrameDurationMillis) {
|
||||
delay(NEC_REPEAT_PERIOD / 1000 - tFrameDurationMillis);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Send Extended NEC with a forced 16 bit address and 8 or 16 bit command depending on the value of aCommand.
|
||||
* @param aAddress - Send 16 bit address.
|
||||
* @param aCommand - If aCommand < 0x100 send 8 bit command and 8 bit inverted command, else send 16 bit command.
|
||||
* @param aNumberOfRepeats - Number of repeats send at a period of 110 ms.
|
||||
* @param aSendNEC2Repeats - Instead of sending the NEC special repeat code, send the original frame for repeat.
|
||||
*/
|
||||
void sendExtendedNEC(uint8_t aSendPin, uint16_t aAddress, uint16_t aCommand, uint_fast8_t aNumberOfRepeats, bool aSendNEC2Repeats) {
|
||||
pinModeFast(aSendPin, OUTPUT);
|
||||
|
||||
uint_fast8_t tNumberOfCommands = aNumberOfRepeats + 1;
|
||||
while (tNumberOfCommands > 0) {
|
||||
unsigned long tStartOfFrameMillis = millis();
|
||||
|
||||
sendMark(aSendPin, NEC_HEADER_MARK);
|
||||
if ((!aSendNEC2Repeats) && (tNumberOfCommands < aNumberOfRepeats + 1)) {
|
||||
// send the NEC special repeat
|
||||
delayMicroseconds(NEC_REPEAT_HEADER_SPACE); // - 2250
|
||||
} else {
|
||||
// send header
|
||||
delayMicroseconds(NEC_HEADER_SPACE);
|
||||
LongUnion tData;
|
||||
tData.UWord.LowWord = aAddress;
|
||||
if (aCommand > 0xFF) {
|
||||
tData.UWord.HighWord = aCommand;
|
||||
} else {
|
||||
tData.UByte.MidHighByte = aCommand;
|
||||
tData.UByte.HighByte = ~aCommand; // LSB first
|
||||
}
|
||||
// Send data
|
||||
for (uint_fast8_t i = 0; i < NEC_BITS; ++i) {
|
||||
sendMark(aSendPin, NEC_BIT_MARK); // constant mark length
|
||||
|
||||
if (tData.ULong & 1) {
|
||||
delayMicroseconds(NEC_ONE_SPACE);
|
||||
} else {
|
||||
delayMicroseconds(NEC_ZERO_SPACE);
|
||||
}
|
||||
tData.ULong >>= 1; // shift command for next bit
|
||||
}
|
||||
} // send stop bit
|
||||
sendMark(aSendPin, NEC_BIT_MARK);
|
||||
|
||||
tNumberOfCommands--;
|
||||
// skip last delay!
|
||||
if (tNumberOfCommands > 0) {
|
||||
/*
|
||||
* Check and fallback for wrong RepeatPeriodMillis parameter. I.e the repeat period must be greater than each frame duration.
|
||||
*/
|
||||
auto tFrameDurationMillis = millis() - tStartOfFrameMillis;
|
||||
if (NEC_REPEAT_PERIOD / 1000 > tFrameDurationMillis) {
|
||||
delay(NEC_REPEAT_PERIOD / 1000 - tFrameDurationMillis);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* LSB first, send header, command, inverted command and stop bit
|
||||
*/
|
||||
void sendFast8BitAndParity(uint8_t aSendPin, uint8_t aCommand, uint_fast8_t aNumberOfRepeats) {
|
||||
sendFAST(aSendPin, aCommand, aNumberOfRepeats);
|
||||
}
|
||||
|
||||
/*
|
||||
* LSB first, send header, 16 bit command or 8 bit command, inverted command and stop bit
|
||||
*/
|
||||
void sendFAST(uint8_t aSendPin, uint16_t aCommand, uint_fast8_t aNumberOfRepeats) {
|
||||
pinModeFast(aSendPin, OUTPUT);
|
||||
|
||||
uint_fast8_t tNumberOfCommands = aNumberOfRepeats + 1;
|
||||
while (tNumberOfCommands > 0) {
|
||||
unsigned long tStartOfFrameMillis = millis();
|
||||
|
||||
// send header
|
||||
sendMark(aSendPin, FAST_HEADER_MARK);
|
||||
delayMicroseconds(FAST_HEADER_SPACE);
|
||||
uint16_t tData;
|
||||
/*
|
||||
* The compiler is intelligent and removes the code for "(aCommand > 0xFF)" if we are called with an uint8_t command :-).
|
||||
* Using an uint16_t command requires additional 56 bytes program memory.
|
||||
*/
|
||||
if (aCommand > 0xFF) {
|
||||
tData = aCommand;
|
||||
} else {
|
||||
tData = aCommand | (((uint8_t) (~aCommand)) << 8); // LSB first
|
||||
}
|
||||
// Send data
|
||||
for (uint_fast8_t i = 0; i < FAST_BITS; ++i) {
|
||||
sendMark(aSendPin, FAST_BIT_MARK); // constant mark length
|
||||
|
||||
if (tData & 1) {
|
||||
delayMicroseconds(FAST_ONE_SPACE);
|
||||
} else {
|
||||
delayMicroseconds(FAST_ZERO_SPACE);
|
||||
}
|
||||
tData >>= 1; // shift command for next bit
|
||||
}
|
||||
// send stop bit
|
||||
sendMark(aSendPin, FAST_BIT_MARK);
|
||||
|
||||
tNumberOfCommands--;
|
||||
// skip last delay!
|
||||
if (tNumberOfCommands > 0) {
|
||||
/*
|
||||
* Check and fallback for wrong RepeatPeriodMillis parameter. I.e the repeat period must be greater than each frame duration.
|
||||
*/
|
||||
auto tFrameDurationMillis = millis() - tStartOfFrameMillis;
|
||||
if (FAST_REPEAT_PERIOD / 1000 > tFrameDurationMillis) {
|
||||
delay(FAST_REPEAT_PERIOD / 1000 - tFrameDurationMillis);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @}*/
|
||||
|
||||
#if defined(LOCAL_DEBUG)
|
||||
#undef LOCAL_DEBUG
|
||||
#endif
|
||||
#endif // _TINY_IR_SENDER_HPP
|
||||
Reference in new issue
Block a user