// Standard NEC sends a special fixed repeat frame.
// NEC2: like NEC, but for repeat, the same full frame is sent after the 110 ms. I have a DVD remote with NEC2.
// NEC and NEC 2 only differ in the repeat frames, so the protocol can only be detected correctly after the first repeat.
@@ -115,14 +115,13 @@
#define APPLE_ADDRESS 0x87EE
-struct PulseDistanceWidthProtocolConstants NECProtocolConstants =
- { NEC, NEC_KHZ, NEC_HEADER_MARK, NEC_HEADER_SPACE, NEC_BIT_MARK,
- NEC_ONE_SPACE, NEC_BIT_MARK, NEC_ZERO_SPACE, PROTOCOL_IS_LSB_FIRST, (NEC_REPEAT_PERIOD / MICROS_IN_ONE_MILLI),
- &sendNECSpecialRepeat };
+struct PulseDistanceWidthProtocolConstants const NECProtocolConstants PROGMEM = {NEC, NEC_KHZ, NEC_HEADER_MARK, NEC_HEADER_SPACE, NEC_BIT_MARK,
+ NEC_ONE_SPACE, NEC_BIT_MARK, NEC_ZERO_SPACE, PROTOCOL_IS_LSB_FIRST, (NEC_REPEAT_PERIOD / MICROS_IN_ONE_MILLI),
+ &sendNECSpecialRepeat};
// Like NEC but repeats are full frames instead of special NEC repeats
-struct PulseDistanceWidthProtocolConstants NEC2ProtocolConstants = { NEC2, NEC_KHZ, NEC_HEADER_MARK, NEC_HEADER_SPACE, NEC_BIT_MARK,
-NEC_ONE_SPACE, NEC_BIT_MARK, NEC_ZERO_SPACE, PROTOCOL_IS_LSB_FIRST, (NEC_REPEAT_PERIOD / MICROS_IN_ONE_MILLI), NULL };
+struct PulseDistanceWidthProtocolConstants const NEC2ProtocolConstants PROGMEM = {NEC2, NEC_KHZ, NEC_HEADER_MARK, NEC_HEADER_SPACE, NEC_BIT_MARK,
+ NEC_ONE_SPACE, NEC_BIT_MARK, NEC_ZERO_SPACE, PROTOCOL_IS_LSB_FIRST, (NEC_REPEAT_PERIOD / MICROS_IN_ONE_MILLI), nullptr};
/************************************
* Start of send and decode functions
@@ -181,7 +180,7 @@ uint32_t IRsend::computeNECRawDataAndChecksum(uint16_t aAddress, uint16_t aComma
* @param aNumberOfRepeats If < 0 then only a special NEC repeat frame will be sent by calling NECProtocolConstants.SpecialSendRepeatFunction().
*/
void IRsend::sendNEC(uint16_t aAddress, uint16_t aCommand, int_fast8_t aNumberOfRepeats) {
- sendPulseDistanceWidth(&NECProtocolConstants, computeNECRawDataAndChecksum(aAddress, aCommand), NEC_BITS, aNumberOfRepeats);
+ sendPulseDistanceWidth_P(&NECProtocolConstants, computeNECRawDataAndChecksum(aAddress, aCommand), NEC_BITS, aNumberOfRepeats);
}
/**
@@ -190,7 +189,7 @@ void IRsend::sendNEC(uint16_t aAddress, uint16_t aCommand, int_fast8_t aNumberOf
* will be sent by calling NECProtocolConstants.SpecialSendRepeatFunction().
*/
void IRsend::sendOnkyo(uint16_t aAddress, uint16_t aCommand, int_fast8_t aNumberOfRepeats) {
- sendPulseDistanceWidth(&NECProtocolConstants, (uint32_t) aCommand << 16 | aAddress, NEC_BITS, aNumberOfRepeats);
+ sendPulseDistanceWidth_P(&NECProtocolConstants, (uint32_t) aCommand << 16 | aAddress, NEC_BITS, aNumberOfRepeats);
}
/**
@@ -199,7 +198,7 @@ void IRsend::sendOnkyo(uint16_t aAddress, uint16_t aCommand, int_fast8_t aNumber
* @param aNumberOfRepeats If < 0 then nothing is sent.
*/
void IRsend::sendNEC2(uint16_t aAddress, uint16_t aCommand, int_fast8_t aNumberOfRepeats) {
- sendPulseDistanceWidth(&NEC2ProtocolConstants, computeNECRawDataAndChecksum(aAddress, aCommand), NEC_BITS, aNumberOfRepeats);
+ sendPulseDistanceWidth_P(&NEC2ProtocolConstants, computeNECRawDataAndChecksum(aAddress, aCommand), NEC_BITS, aNumberOfRepeats);
}
/**
@@ -220,7 +219,7 @@ void IRsend::sendApple(uint8_t aDeviceId, uint8_t aCommand, int_fast8_t aNumberO
tRawData.UByte.MidHighByte = aCommand;
tRawData.UByte.HighByte = aDeviceId; // e.g. 0xD7
- sendPulseDistanceWidth(&NECProtocolConstants, tRawData.ULong, NEC_BITS, aNumberOfRepeats);
+ sendPulseDistanceWidth_P(&NECProtocolConstants, tRawData.ULong, NEC_BITS, aNumberOfRepeats);
}
/**
@@ -229,7 +228,7 @@ void IRsend::sendApple(uint8_t aDeviceId, uint8_t aCommand, int_fast8_t aNumberO
* will be sent by calling NECProtocolConstants.SpecialSendRepeatFunction().
*/
void IRsend::sendNECRaw(uint32_t aRawData, int_fast8_t aNumberOfRepeats) {
- sendPulseDistanceWidth(&NECProtocolConstants, aRawData, NEC_BITS, aNumberOfRepeats);
+ sendPulseDistanceWidth_P(&NECProtocolConstants, aRawData, NEC_BITS, aNumberOfRepeats);
}
/**
@@ -278,7 +277,7 @@ bool IRrecv::decodeNEC() {
}
// Try to decode as NEC protocol
- if (!decodePulseDistanceWidthData(&NECProtocolConstants, NEC_BITS)) {
+ if (!decodePulseDistanceWidthData_P(&NECProtocolConstants, NEC_BITS)) {
#if defined(LOCAL_DEBUG)
Serial.print(F("NEC: "));
Serial.println(F("Decode failed"));
diff --git a/src/ir_Others.hpp b/src/ir_Others.hpp
index cc5291b04..20e4cc62c 100644
--- a/src/ir_Others.hpp
+++ b/src/ir_Others.hpp
@@ -58,11 +58,11 @@
#define DISH_ZERO_SPACE 2800
#define DISH_REPEAT_SPACE 6200 // really?
-struct PulseDistanceWidthProtocolConstants DishProtocolConstants = { UNKNOWN, 56, DISH_HEADER_MARK, DISH_HEADER_SPACE,
-DISH_BIT_MARK, DISH_ONE_SPACE, DISH_BIT_MARK, DISH_ZERO_SPACE, PROTOCOL_IS_MSB_FIRST, 40, NULL };
+struct PulseDistanceWidthProtocolConstants const DishProtocolConstants PROGMEM = { UNKNOWN, 56, DISH_HEADER_MARK, DISH_HEADER_SPACE,
+DISH_BIT_MARK, DISH_ONE_SPACE, DISH_BIT_MARK, DISH_ZERO_SPACE, PROTOCOL_IS_MSB_FIRST, 40, nullptr };
void IRsend::sendDish(uint16_t aData) {
- sendPulseDistanceWidth(&DishProtocolConstants, aData, DISH_BITS, 4);
+ sendPulseDistanceWidth_P(&DishProtocolConstants, aData, DISH_BITS, 4);
}
//==============================================================================
@@ -84,11 +84,11 @@ void IRsend::sendDish(uint16_t aData) {
#define WHYNTER_ONE_SPACE 2150
#define WHYNTER_ZERO_SPACE 750
-struct PulseDistanceWidthProtocolConstants WhynterProtocolConstants = { WHYNTER, 38, WHYNTER_HEADER_MARK, WHYNTER_HEADER_SPACE,
-WHYNTER_BIT_MARK, WHYNTER_ONE_SPACE, WHYNTER_BIT_MARK, WHYNTER_ZERO_SPACE, PROTOCOL_IS_MSB_FIRST, 110, NULL };
+struct PulseDistanceWidthProtocolConstants const WhynterProtocolConstants PROGMEM = { WHYNTER, 38, WHYNTER_HEADER_MARK, WHYNTER_HEADER_SPACE,
+WHYNTER_BIT_MARK, WHYNTER_ONE_SPACE, WHYNTER_BIT_MARK, WHYNTER_ZERO_SPACE, PROTOCOL_IS_MSB_FIRST, 110, nullptr };
void IRsend::sendWhynter(uint32_t aData, uint8_t aNumberOfBitsToSend) {
- sendPulseDistanceWidth(&WhynterProtocolConstants, aData, NEC_BITS, aNumberOfBitsToSend);
+ sendPulseDistanceWidth_P(&WhynterProtocolConstants, aData, NEC_BITS, aNumberOfBitsToSend);
}
bool IRrecv::decodeWhynter() {
@@ -96,10 +96,10 @@ bool IRrecv::decodeWhynter() {
if (decodedIRData.rawlen != (2 * WHYNTER_BITS) + 4) {
return false;
}
- if (!checkHeader(&WhynterProtocolConstants)) {
+ if (!checkHeader_P(&WhynterProtocolConstants)) {
return false;
}
- if (!decodePulseDistanceWidthData(&WhynterProtocolConstants, WHYNTER_BITS)) {
+ if (!decodePulseDistanceWidthData_P(&WhynterProtocolConstants, WHYNTER_BITS)) {
return false;
}
// Success
diff --git a/src/ir_Pronto.hpp b/src/ir_Pronto.hpp
index 06bc15e70..e07c0b50a 100644
--- a/src/ir_Pronto.hpp
+++ b/src/ir_Pronto.hpp
@@ -2,6 +2,8 @@
* @file ir_Pronto.hpp
* @brief In this file, the functions IRrecv::compensateAndPrintPronto and IRsend::sendPronto are defined.
*
+ * Pronto is the standard for the professional audio and video hardware market.
+ *
* See http://www.harctoolbox.org/Glossary.html#ProntoSemantics
* Pronto database http://www.remotecentral.com/search.htm
*
@@ -10,7 +12,7 @@
************************************************************************************
* MIT License
*
- * Copyright (c) 2020 Bengt Martensson
+ * Copyright (c) 2020-2025 Bengt Martensson, 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
@@ -34,7 +36,7 @@
#ifndef _IR_PRONTO_HPP
#define _IR_PRONTO_HPP
-#if defined(DEBUG) && !defined(LOCAL_DEBUG)
+#if defined(DEBUG)
#define LOCAL_DEBUG
#else
//#define LOCAL_DEBUG // This enables debug output only for this file
@@ -99,7 +101,7 @@ void IRsend::sendPronto(const uint16_t *data, uint16_t length, int_fast8_t aNumb
uint16_t durations[intros + repeats];
for (uint16_t i = 0; i < intros + repeats; i++) {
uint32_t duration = ((uint32_t) data[i + numbersInPreamble]) * timebase;
- durations[i] = (uint16_t) ((duration <= UINT16_MAX) ? duration : UINT16_MAX);
+ durations[i] = (uint16_t)((duration <= UINT16_MAX) ? duration : UINT16_MAX);
}
/*
@@ -209,24 +211,25 @@ static uint16_t toFrequencyCode(uint16_t frequency) {
return referenceFrequency / effectiveFrequency(frequency);
}
-static char hexDigit(uint16_t x) {
+static char DigitToHex(uint8_t x) {
return (char) (x <= 9 ? ('0' + x) : ('A' + (x - 10)));
}
-static void dumpDigit(Print *aSerial, uint16_t number) {
- aSerial->print(hexDigit(number));
+static void dumpDigitHex(Print *aSerial, uint8_t number) {
+ aSerial->print(DigitToHex(number));
}
-static void dumpNumber(Print *aSerial, uint16_t number) {
+static void dumpNumberHex(Print *aSerial, uint16_t number) {
+ // Loop through all 4 nibbles
for (uint16_t i = 0; i < digitsInProntoNumber; i++) {
uint16_t shifts = bitsInHexadecimal * (digitsInProntoNumber - 1 - i);
- dumpDigit(aSerial, (number >> shifts) & hexMask);
+ dumpDigitHex(aSerial, (number >> shifts) & hexMask);
}
aSerial->print(' ');
}
-static void dumpDuration(Print *aSerial, uint32_t duration, uint16_t timebase) {
- dumpNumber(aSerial, (duration + timebase / 2) / timebase);
+static void dumpDurationHex(Print *aSerial, uint32_t duration, uint16_t timebase) {
+ dumpNumberHex(aSerial, (duration + timebase / 2) / timebase);
}
/*
@@ -241,29 +244,30 @@ static void compensateAndDumpSequence(Print *aSerial, const volatile IRRawbufTyp
} else {
tDuration += getMarkExcessMicros();
}
- dumpDuration(aSerial, tDuration, timebase);
+ dumpDurationHex(aSerial, tDuration, timebase);
}
// append a gap
- dumpDuration(aSerial, PRONTO_DEFAULT_GAP, timebase);
+ dumpDurationHex(aSerial, PRONTO_DEFAULT_GAP, timebase);
}
/**
* Print the result (second argument) as Pronto Hex on the Print supplied as argument.
* Used in the ReceiveDump example.
+ * Do not print repeat sequence data.
* @param aSerial The Print object on which to write, for Arduino you can use &Serial.
* @param aFrequencyHertz Modulation frequency in Hz. Often 38000Hz.
*/
void IRrecv::compensateAndPrintIRResultAsPronto(Print *aSerial, uint16_t aFrequencyHertz) {
- aSerial->println(F("Pronto Hex as string"));
+ aSerial->println(F("Pronto Hex as string without repeat sequence"));
aSerial->print(F("char prontoData[] = \""));
- dumpNumber(aSerial, aFrequencyHertz > 0 ? learnedToken : learnedNonModulatedToken);
- dumpNumber(aSerial, toFrequencyCode(aFrequencyHertz));
- dumpNumber(aSerial, (decodedIRData.rawlen + 1) / 2);
- dumpNumber(aSerial, 0);
+ dumpNumberHex(aSerial, aFrequencyHertz > 0 ? learnedToken : learnedNonModulatedToken);
+ dumpNumberHex(aSerial, toFrequencyCode(aFrequencyHertz));
+ dumpNumberHex(aSerial, (decodedIRData.rawlen + 1) / 2);
+ dumpNumberHex(aSerial, 0); // no repeat data
uint16_t timebase = toTimebase(aFrequencyHertz);
compensateAndDumpSequence(aSerial, &decodedIRData.rawDataPtr->rawbuf[1], decodedIRData.rawlen - 1, timebase); // skip leading space
- aSerial->println("\";");
+ aSerial->println(F("\";"));
}
/*
@@ -271,18 +275,18 @@ void IRrecv::compensateAndPrintIRResultAsPronto(Print *aSerial, uint16_t aFreque
* and can lead to resource problems especially on small processors like AVR's
*/
-static bool dumpDigit(String *aString, uint16_t number) {
- aString->concat(hexDigit(number));
+static bool dumpDigitHex(String *aString, uint8_t number) {
+ aString->concat(DigitToHex(number));
return number;
}
-static size_t dumpNumber(String *aString, uint16_t number) {
+static size_t dumpNumberHex(String *aString, uint16_t number) {
size_t size = 0;
for (uint16_t i = 0; i < digitsInProntoNumber; i++) {
uint16_t shifts = bitsInHexadecimal * (digitsInProntoNumber - 1 - i);
- size += dumpDigit(aString, (number >> shifts) & hexMask);
+ size += dumpDigitHex(aString, (number >> shifts) & hexMask);
}
aString->concat(' ');
size++;
@@ -293,8 +297,8 @@ static size_t dumpNumber(String *aString, uint16_t number) {
/*
* Compensate received values by MARK_EXCESS_MICROS, like it is done for decoding!
*/
-static size_t dumpDuration(String *aString, uint32_t duration, uint16_t timebase) {
- return dumpNumber(aString, (duration + timebase / 2) / timebase);
+static size_t dumpDurationHex(String *aString, uint32_t duration, uint16_t timebase) {
+ return dumpNumberHex(aString, (duration + timebase / 2) / timebase);
}
static size_t compensateAndDumpSequence(String *aString, const volatile IRRawbufType *data, size_t length, uint16_t timebase) {
@@ -309,11 +313,11 @@ static size_t compensateAndDumpSequence(String *aString, const volatile IRRawbuf
} else {
tDuration += getMarkExcessMicros();
}
- size += dumpDuration(aString, tDuration, timebase);
+ size += dumpDurationHex(aString, tDuration, timebase);
}
// append minimum gap
- size += dumpDuration(aString, PRONTO_DEFAULT_GAP, timebase);
+ size += dumpDurationHex(aString, PRONTO_DEFAULT_GAP, timebase);
return size;
}
@@ -327,12 +331,11 @@ size_t IRrecv::compensateAndStorePronto(String *aString, uint16_t frequency) {
size_t size = 0;
uint16_t timebase = toTimebase(frequency);
- size += dumpNumber(aString, frequency > 0 ? learnedToken : learnedNonModulatedToken);
- size += dumpNumber(aString, toFrequencyCode(frequency));
- size += dumpNumber(aString, (decodedIRData.rawlen + 1) / 2);
- size += dumpNumber(aString, 0);
- size += compensateAndDumpSequence(aString, &decodedIRData.rawDataPtr->rawbuf[1], decodedIRData.rawlen - 1,
- timebase); // skip leading space
+ size += dumpNumberHex(aString, frequency > 0 ? learnedToken : learnedNonModulatedToken);
+ size += dumpNumberHex(aString, toFrequencyCode(frequency));
+ size += dumpNumberHex(aString, (decodedIRData.rawlen + 1) / 2);
+ size += dumpNumberHex(aString, 0);
+ size += compensateAndDumpSequence(aString, &decodedIRData.rawDataPtr->rawbuf[1], decodedIRData.rawlen - 1, timebase); // skip leading space
return size;
}
diff --git a/src/ir_RC5_RC6.hpp b/src/ir_RC5_RC6.hpp
index 2cc8dd43f..d97f11b40 100644
--- a/src/ir_RC5_RC6.hpp
+++ b/src/ir_RC5_RC6.hpp
@@ -8,6 +8,8 @@
************************************************************************************
* MIT License
*
+ * Copyright (c) 2020-2025 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
@@ -30,7 +32,7 @@
#ifndef _IR_RC5_RC6_HPP
#define _IR_RC5_RC6_HPP
-#if defined(DEBUG) && !defined(LOCAL_DEBUG)
+#if defined(DEBUG)
#define LOCAL_DEBUG
#else
//#define LOCAL_DEBUG // This enables debug output only for this file
@@ -68,6 +70,7 @@ uint8_t sLastSendToggleValue = 1; // To start first command with toggle 0
//
// see: https://www.sbprojects.net/knowledge/ir/rc5.php
// https://en.wikipedia.org/wiki/Manchester_code
+// https://en.wikipedia.org/wiki/RC-5
// https://forum.arduino.cc/t/sending-rc-5-extended-code-using-irsender/1045841/10 - Protocol Maranz Extended
// mark->space => 0
// space->mark => 1
@@ -243,8 +246,8 @@ bool IRrecv::decodeRC5() {
+ 450
Sum: 23150
*/
-// Frame RC6: 1 start bit + 1 Bit "1" + 3 mode bits (000) + 1 toggle bit + 8 address + 8 command bits + 2666us pause
-// Frame RC6A: 1 start bit + 1 Bit "1" + 3 mode bits (110) + 1 toggle bit + "1" + 14 customer bits + 8 system bits + 8 command bits (=31bits) + 2666us pause
+// Frame RC6: 1 start bit + 1 Bit "1" + 3 mode bits (000) + 1 toggle bit + 8 address + 8 command bits + 2666us pause - 22 bits incl. start bit
+// Frame RC6A: 1 start bit + 1 Bit "1" + 3 mode bits (110) + 1 toggle bit + "1" + 14 customer bits + 8 system bits + 8 command bits + 2666us pause - 37 bits incl. start bit
// !!! toggle bit has another timing :-( !!!
// mark->space => 1
// space->mark => 0
@@ -261,8 +264,10 @@ bool IRrecv::decodeRC5() {
#define RC6_TOGGLE_BIT_INDEX RC6_MODE_BITS // fourth position, index = 3
#define RC6_ADDRESS_BITS 8
#define RC6_COMMAND_BITS 8
+#define RC6_CUSTOMER_BITS 14
#define RC6_BITS (RC6_LEADING_BIT + RC6_MODE_BITS + RC6_TOGGLE_BIT + RC6_ADDRESS_BITS + RC6_COMMAND_BITS) // 21
+#define RC6A_BITS (RC6_LEADING_BIT + RC6_MODE_BITS + RC6_TOGGLE_BIT + 1 + RC6_CUSTOMER_BITS + RC6_ADDRESS_BITS + RC6_COMMAND_BITS) // 36
#define RC6_UNIT 444 // 16 periods of 36 kHz (444.4444)
@@ -388,6 +393,57 @@ void IRsend::sendRC6(uint8_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRe
}
}
+/**
+ * Assemble raw data for RC6 from parameters and toggle state and send
+ * We do not wait for the minimal trailing space of 2666 us
+ * @param aEnableAutomaticToggle Send toggle bit according to the state of the static sLastSendToggleValue variable.
+ */
+void IRsend::sendRC6A(uint8_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats, uint16_t aCustomer,
+ bool aEnableAutomaticToggle) {
+
+ LongUnion tIRRawData;
+ tIRRawData.UByte.LowByte = aCommand;
+ tIRRawData.UByte.MidLowByte = aAddress;
+
+ tIRRawData.UWord.HighWord = aCustomer | 0x400; // bit 31 is always 1
+
+ if (aEnableAutomaticToggle) {
+ if (sLastSendToggleValue == 0) {
+ sLastSendToggleValue = 1;
+ // set toggled bit
+ IR_DEBUG_PRINT(F("Set Toggle "));
+ tIRRawData.UByte.HighByte |= 0x80; // toggle bit is bit 32
+ } else {
+ sLastSendToggleValue = 0;
+ }
+ }
+
+ // Set mode bits
+ uint64_t tRawData = tIRRawData.ULong + 0x0600000000;
+
+#if defined(LOCAL_DEBUG)
+ Serial.print(F("RC6A: "));
+ Serial.print(F("sLastSendToggleValue="));
+ Serial.print (sLastSendToggleValue);
+ Serial.print(F(" RawData="));
+ Serial.println(tIRRawData.ULong, HEX);
+#endif
+
+ uint_fast8_t tNumberOfCommands = aNumberOfRepeats + 1;
+ while (tNumberOfCommands > 0) {
+
+ // start and leading bits are sent by sendRC6
+ sendRC6Raw(tRawData, RC6A_BITS - 1); // -1 since the leading bit is additionally sent by sendRC6
+
+ tNumberOfCommands--;
+ // skip last delay!
+ if (tNumberOfCommands > 0) {
+ // send repeated command in a fixed raster
+ delay(RC6_REPEAT_DISTANCE / MICROS_IN_ONE_MILLI);
+ }
+ }
+}
+
/**
* Try to decode data as RC6 protocol
*/
@@ -482,7 +538,7 @@ bool IRrecv::decodeRC6() {
tValue.ULong = tDecodedRawData;
decodedIRData.decodedRawData = tDecodedRawData;
- if (tBitIndex < 36) {
+ if (tBitIndex < 35) {
// RC6 8 address bits, 8 command bits
decodedIRData.flags = IRDATA_FLAGS_IS_MSB_FIRST;
decodedIRData.command = tValue.UByte.LowByte;
@@ -494,21 +550,23 @@ bool IRrecv::decodeRC6() {
if (tBitIndex > 20) {
decodedIRData.flags |= IRDATA_FLAGS_EXTRA_INFO;
}
+ decodedIRData.protocol = RC6;
+
} else {
- // RC6A - 32 bits
- decodedIRData.flags = IRDATA_FLAGS_IS_MSB_FIRST;
- if ((tValue.UByte.MidLowByte & 0x80) != 0) {
- decodedIRData.flags = IRDATA_FLAGS_TOGGLE_BIT | IRDATA_FLAGS_IS_MSB_FIRST;
- }
- tValue.UByte.MidLowByte &= 0x87F; // mask toggle bit
+ // RC6A
+ decodedIRData.flags = IRDATA_FLAGS_IS_MSB_FIRST | IRDATA_FLAGS_EXTRA_INFO;
decodedIRData.command = tValue.UByte.LowByte;
decodedIRData.address = tValue.UByte.MidLowByte;
+ decodedIRData.extra = tValue.UWord.HighWord & 0x3FFF; // Mask to 14 bits, remove toggle and constant 1
+ if ((tValue.UByte.HighByte & 0x80) != 0) {
+ decodedIRData.flags |= IRDATA_FLAGS_TOGGLE_BIT;
+ }
+ decodedIRData.protocol = RC6A;
}
// check for repeat, do not check toggle bit yet
checkForRepeatSpaceTicksAndSetFlag(RC6_MAXIMUM_REPEAT_DISTANCE / MICROS_PER_TICK);
- decodedIRData.protocol = RC6;
return true;
}
diff --git a/src/ir_Samsung.hpp b/src/ir_Samsung.hpp
index eaa6e921b..58b285df6 100644
--- a/src/ir_Samsung.hpp
+++ b/src/ir_Samsung.hpp
@@ -8,7 +8,7 @@
************************************************************************************
* MIT License
*
- * Copyright (c) 2017-2024 Darryl Smith, Armin Joachimsmeyer
+ * Copyright (c) 2017-2025 Darryl Smith, 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
@@ -32,7 +32,7 @@
#ifndef _IR_SAMSUNG_HPP
#define _IR_SAMSUNG_HPP
-#if defined(DEBUG) && !defined(LOCAL_DEBUG)
+#if defined(DEBUG)
#define LOCAL_DEBUG
#else
//#define LOCAL_DEBUG // This enables debug output only for this file
@@ -102,13 +102,13 @@
#define SAMSUNG_MAXIMUM_REPEAT_DISTANCE (SAMSUNG_REPEAT_PERIOD + (SAMSUNG_REPEAT_PERIOD / 4)) // 137000 - Just a guess
// 19 byte RAM
-struct PulseDistanceWidthProtocolConstants SamsungProtocolConstants = { SAMSUNG, SAMSUNG_KHZ, SAMSUNG_HEADER_MARK,
-SAMSUNG_HEADER_SPACE, SAMSUNG_BIT_MARK, SAMSUNG_ONE_SPACE, SAMSUNG_BIT_MARK, SAMSUNG_ZERO_SPACE, PROTOCOL_IS_LSB_FIRST,
- (SAMSUNG_REPEAT_PERIOD / MICROS_IN_ONE_MILLI), NULL };
+struct PulseDistanceWidthProtocolConstants const SamsungProtocolConstants PROGMEM = {SAMSUNG, SAMSUNG_KHZ, SAMSUNG_HEADER_MARK,
+ SAMSUNG_HEADER_SPACE, SAMSUNG_BIT_MARK, SAMSUNG_ONE_SPACE, SAMSUNG_BIT_MARK, SAMSUNG_ZERO_SPACE, PROTOCOL_IS_LSB_FIRST,
+ (SAMSUNG_REPEAT_PERIOD / MICROS_IN_ONE_MILLI), nullptr};
-struct PulseDistanceWidthProtocolConstants SamsungLGProtocolConstants = { SAMSUNGLG, SAMSUNG_KHZ, SAMSUNG_HEADER_MARK,
-SAMSUNG_HEADER_SPACE, SAMSUNG_BIT_MARK, SAMSUNG_ONE_SPACE, SAMSUNG_BIT_MARK, SAMSUNG_ZERO_SPACE, PROTOCOL_IS_LSB_FIRST,
- (SAMSUNG_REPEAT_PERIOD / MICROS_IN_ONE_MILLI), &sendSamsungLGSpecialRepeat };
+struct PulseDistanceWidthProtocolConstants const SamsungLGProtocolConstants PROGMEM = {SAMSUNGLG, SAMSUNG_KHZ, SAMSUNG_HEADER_MARK,
+ SAMSUNG_HEADER_SPACE, SAMSUNG_BIT_MARK, SAMSUNG_ONE_SPACE, SAMSUNG_BIT_MARK, SAMSUNG_ZERO_SPACE, PROTOCOL_IS_LSB_FIRST,
+ (SAMSUNG_REPEAT_PERIOD / MICROS_IN_ONE_MILLI), &sendSamsungLGSpecialRepeat};
/************************************
* Start of send and decode functions
************************************/
@@ -143,6 +143,7 @@ void sendSamsungLGSpecialRepeat() {
/*
* Sent e.g. by an LG 6711R1P071A remote
+ * @param aAddress 16 bit address. If < 0x100, i.e. only 8 bit, then a (standard) 16 bit address <8_bit_address><8_bit_address> is generated.
* @param aNumberOfRepeats If < 0 then only a special repeat frame will be sent
*/
void IRsend::sendSamsungLG(uint16_t aAddress, uint16_t aCommand, int_fast8_t aNumberOfRepeats) {
@@ -154,10 +155,13 @@ void IRsend::sendSamsungLG(uint16_t aAddress, uint16_t aCommand, int_fast8_t aNu
// send 16 bit address and 8 command bits and then 8 inverted command bits LSB first
LongUnion tRawData;
tRawData.UWord.LowWord = aAddress;
+ if (aAddress < 0x100) { // This disables the sending of an (non standard?) 16 bit address with upper byte = 0 like 0x0014.
+ tRawData.UByte.MidLowByte = aAddress; // here we have 8 bit address which must be duplicated
+ }
tRawData.UByte.MidHighByte = aCommand;
tRawData.UByte.HighByte = ~aCommand;
- sendPulseDistanceWidth(&SamsungLGProtocolConstants, tRawData.ULong, SAMSUNG_BITS, aNumberOfRepeats);
+ sendPulseDistanceWidth_P(&SamsungLGProtocolConstants, tRawData.ULong, SAMSUNG_BITS, aNumberOfRepeats);
}
/**
@@ -191,7 +195,7 @@ void IRsend::sendSamsung(uint16_t aAddress, uint16_t aCommand, int_fast8_t aNumb
tSendValue.UWords[1] = aCommand;
}
- sendPulseDistanceWidth(&SamsungProtocolConstants, tSendValue.ULong, SAMSUNG_BITS, aNumberOfRepeats);
+ sendPulseDistanceWidth_P(&SamsungProtocolConstants, tSendValue.ULong, SAMSUNG_BITS, aNumberOfRepeats);
}
/**
@@ -208,7 +212,7 @@ void IRsend::sendSamsung16BitAddressAnd8BitCommand(uint16_t aAddress, uint8_t aC
tSendValue.UBytes[2] = aCommand;
tSendValue.UBytes[3] = ~aCommand;
- sendPulseDistanceWidth(&SamsungProtocolConstants, tSendValue.ULong, SAMSUNG_BITS, aNumberOfRepeats);
+ sendPulseDistanceWidth_P(&SamsungProtocolConstants, tSendValue.ULong, SAMSUNG_BITS, aNumberOfRepeats);
}
/**
@@ -224,7 +228,7 @@ void IRsend::sendSamsung16BitAddressAndCommand(uint16_t aAddress, uint16_t aComm
// Send 16 command bits
tSendValue.UWords[1] = aCommand;
- sendPulseDistanceWidth(&SamsungProtocolConstants, tSendValue.ULong, SAMSUNG_BITS, aNumberOfRepeats);
+ sendPulseDistanceWidth_P(&SamsungProtocolConstants, tSendValue.ULong, SAMSUNG_BITS, aNumberOfRepeats);
}
/**
* Here we send Samsung48
@@ -244,7 +248,7 @@ void IRsend::sendSamsung48(uint16_t aAddress, uint32_t aCommand, int_fast8_t aNu
tRawSamsungData[1] = tUpper8BitsOfCommand | (~tUpper8BitsOfCommand) << 8;
tRawSamsungData[0] = tSendValue.ULong;
- sendPulseDistanceWidthFromArray(&SamsungProtocolConstants, &tRawSamsungData[0], SAMSUNG48_BITS, aNumberOfRepeats);
+ sendPulseDistanceWidthFromArray_P(&SamsungProtocolConstants, &tRawSamsungData[0], SAMSUNG48_BITS, aNumberOfRepeats);
#else
LongLongUnion tSendValue;
tSendValue.UWords[0] = aAddress;
@@ -257,7 +261,7 @@ void IRsend::sendSamsung48(uint16_t aAddress, uint32_t aCommand, int_fast8_t aNu
} else {
tSendValue.ULongLong = aAddress | aCommand << 16;
}
- sendPulseDistanceWidth(&SamsungProtocolConstants, tSendValue.ULongLong, SAMSUNG48_BITS, aNumberOfRepeats);
+ sendPulseDistanceWidth_P(&SamsungProtocolConstants, tSendValue.ULongLong, SAMSUNG48_BITS, aNumberOfRepeats);
#endif
}
@@ -278,7 +282,7 @@ bool IRrecv::decodeSamsung() {
return false;
}
- if (!checkHeader(&SamsungProtocolConstants)) {
+ if (!checkHeader_P(&SamsungProtocolConstants)) {
return false;
}
@@ -294,7 +298,7 @@ bool IRrecv::decodeSamsung() {
/*
* Decode first 32 bits
*/
- if (!decodePulseDistanceWidthData(&SamsungProtocolConstants, SAMSUNG_BITS, 3)) {
+ if (!decodePulseDistanceWidthData_P(&SamsungProtocolConstants, SAMSUNG_BITS, 3)) {
#if defined(LOCAL_DEBUG)
Serial.print(F("Samsung: "));
Serial.println(F("Decode failed"));
@@ -310,7 +314,7 @@ bool IRrecv::decodeSamsung() {
* Samsung48
*/
// decode additional 16 bit
- if (!decodePulseDistanceWidthData(&SamsungProtocolConstants, (SAMSUNG_COMMAND32_BITS - SAMSUNG_COMMAND16_BITS),
+ if (!decodePulseDistanceWidthData_P(&SamsungProtocolConstants, (SAMSUNG_COMMAND32_BITS - SAMSUNG_COMMAND16_BITS),
3 + (2 * SAMSUNG_BITS))) {
#if defined(LOCAL_DEBUG)
Serial.print(F("Samsung: "));
@@ -349,11 +353,15 @@ bool IRrecv::decodeSamsung() {
if (tValue.UByte.MidHighByte == (uint8_t)(~tValue.UByte.HighByte)) {
// 8 bit command protocol -> assume 8 bit address
decodedIRData.command = tValue.UByte.MidHighByte; // first 8 bit
- decodedIRData.address = tValue.UByte.LowByte; // assume LowByte == MidLowByte
+ }
+
+ if (tValue.UByte.MidLowByte == tValue.UByte.LowByte) {
+ decodedIRData.address = tValue.UByte.LowByte; // assume LowByte == MidLowByte as seen for a LG HX906 A/V Receive E8172C2C
} else {
// 16 bit command protocol, address is filled above with the 16 bit value
decodedIRData.command = tValue.UWord.HighWord; // first 16 bit
}
+
decodedIRData.numberOfBits = SAMSUNG_BITS;
decodedIRData.protocol = SAMSUNG;
}
diff --git a/src/ir_Sony.hpp b/src/ir_Sony.hpp
index 59b7a8f74..bcb9eb9d2 100644
--- a/src/ir_Sony.hpp
+++ b/src/ir_Sony.hpp
@@ -30,7 +30,7 @@
#ifndef _IR_SONY_HPP
#define _IR_SONY_HPP
-#if defined(DEBUG) && !defined(LOCAL_DEBUG)
+#if defined(DEBUG)
#define LOCAL_DEBUG
#else
//#define LOCAL_DEBUG // This enables debug output only for this file
@@ -90,8 +90,8 @@
#define SONY_REPEAT_PERIOD 45000 // Commands are repeated every 45 ms (measured from start to start) for as long as the key on the remote control is held down.
#define SONY_MAXIMUM_REPEAT_DISTANCE (SONY_REPEAT_PERIOD - SONY_AVERAGE_DURATION_MIN) // 24 ms
-struct PulseDistanceWidthProtocolConstants SonyProtocolConstants = { SONY, SONY_KHZ, SONY_HEADER_MARK, SONY_SPACE, SONY_ONE_MARK,
-SONY_SPACE, SONY_ZERO_MARK, SONY_SPACE, PROTOCOL_IS_LSB_FIRST, (SONY_REPEAT_PERIOD / MICROS_IN_ONE_MILLI), NULL };
+struct PulseDistanceWidthProtocolConstants const SonyProtocolConstants PROGMEM = { SONY, SONY_KHZ, SONY_HEADER_MARK, SONY_SPACE, SONY_ONE_MARK,
+SONY_SPACE, SONY_ZERO_MARK, SONY_SPACE, PROTOCOL_IS_LSB_FIRST, (SONY_REPEAT_PERIOD / MICROS_IN_ONE_MILLI), nullptr };
/************************************
* Start of send and decode functions
@@ -103,12 +103,12 @@ SONY_SPACE, SONY_ZERO_MARK, SONY_SPACE, PROTOCOL_IS_LSB_FIRST, (SONY_REPEAT_PERI
void IRsend::sendSony(uint16_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats, uint8_t numberOfBits) {
uint32_t tData = (uint32_t) aAddress << 7 | (aCommand & 0x7F);
// send 5, 8, 13 address bits LSB first
- sendPulseDistanceWidth(&SonyProtocolConstants, tData, numberOfBits, aNumberOfRepeats);
+ sendPulseDistanceWidth_P(&SonyProtocolConstants, tData, numberOfBits, aNumberOfRepeats);
}
bool IRrecv::decodeSony() {
- if (!checkHeader(&SonyProtocolConstants)) {
+ if (!checkHeader_P(&SonyProtocolConstants)) {
return false;
}
@@ -122,7 +122,7 @@ bool IRrecv::decodeSony() {
return false;
}
- if (!decodePulseDistanceWidthData(&SonyProtocolConstants, (decodedIRData.rawlen - 1) / 2, 3)) {
+ if (!decodePulseDistanceWidthData_P(&SonyProtocolConstants, (decodedIRData.rawlen - 1) / 2, 3)) {
#if defined(LOCAL_DEBUG)
Serial.print(F("Sony: "));
Serial.println(F("Decode failed"));
diff --git a/src/ir_Template.hpp b/src/ir_Template.hpp
index 9855f51a3..ce1dab069 100644
--- a/src/ir_Template.hpp
+++ b/src/ir_Template.hpp
@@ -123,9 +123,9 @@
#define SHUZU_OTHER 1234 // Other things you may need to define
// use BOSEWAVE, we have no SHUZU code
-struct PulseDistanceWidthProtocolConstants ShuzuProtocolConstants = { BOSEWAVE, 38, SHUZU_HEADER_MARK, SHUZU_HEADER_SPACE,
-SHUZU_BIT_MARK, SHUZU_ONE_SPACE, SHUZU_BIT_MARK, SHUZU_ZERO_SPACE, PROTOCOL_IS_LSB_FIRST, (SHUZU_REPEAT_PERIOD
- / MICROS_IN_ONE_MILLI), NULL };
+struct PulseDistanceWidthProtocolConstants const ShuzuProtocolConstants PROGMEM = {BOSEWAVE, 38, SHUZU_HEADER_MARK, SHUZU_HEADER_SPACE,
+ SHUZU_BIT_MARK, SHUZU_ONE_SPACE, SHUZU_BIT_MARK, SHUZU_ZERO_SPACE, PROTOCOL_IS_LSB_FIRST, (SHUZU_REPEAT_PERIOD
+ / MICROS_IN_ONE_MILLI), nullptr};
/************************************
* Start of send and decode functions
@@ -133,7 +133,7 @@ SHUZU_BIT_MARK, SHUZU_ONE_SPACE, SHUZU_BIT_MARK, SHUZU_ZERO_SPACE, PROTOCOL_IS_L
void IRsend::sendShuzu(uint16_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats) {
- sendPulseDistanceWidth(&ShuzuProtocolConstants, (uint32_t) aCommand << 8 | aCommand, SHUZU_BITS, aNumberOfRepeats);
+ sendPulseDistanceWidth_P(&ShuzuProtocolConstants, (uint32_t) aCommand << 8 | aCommand, SHUZU_BITS, aNumberOfRepeats);
}
bool IRrecv::decodeShuzu() {
@@ -149,12 +149,12 @@ bool IRrecv::decodeShuzu() {
}
// Check header
- if (!checkHeader(&ShuzuProtocolConstants)) {
+ if (!checkHeader_P(&ShuzuProtocolConstants)) {
return false;
}
// Decode
- if (!decodePulseDistanceData(&ShuzuProtocolConstants, SHUZU_BITS)) {
+ if (!decodePulseDistanceData_P(&ShuzuProtocolConstants, SHUZU_BITS)) {
IR_DEBUG_PRINT(F("Shuzu: "));
IR_DEBUG_PRINTLN(F("Decode failed"));
return false;
diff --git a/src/private/IRTimer.hpp b/src/private/IRTimer.hpp
index 0b129eb56..ac893cc1d 100644
--- a/src/private/IRTimer.hpp
+++ b/src/private/IRTimer.hpp
@@ -11,7 +11,7 @@
*************************************************************************************
* MIT License
*
- * Copyright (c) 2021-2023 Armin Joachimsmeyer
+ * Copyright (c) 2021-2025 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
@@ -44,15 +44,17 @@
/*
* Functions declared here
*/
-void timerResetInterruptPending();
-void timerEnableReceiveInterrupt();
-void timerDisableReceiveInterrupt();
-void timerConfigForReceive();
-void enableSendPWMByTimer();
-void disableSendPWMByTimer();
-void timerConfigForSend(uint16_t aFrequencyKHz);
-
-// SEND_PWM_BY_TIMER is defined in IRremote.hpp line 195.
+void timerConfigForReceive(); // Initialization of 50 us timer, interrupts are still disabled
+void timerEnableReceiveInterrupt(); // Enable interrupts of an initialized timer
+void timerDisableReceiveInterrupt(); // Disable interrupts of an initialized timer
+void timerResetInterruptPending(); // ISR helper function for some architectures, which require a manual reset
+// of the pending interrupt (TIMER_REQUIRES_RESET_INTR_PENDING is defined). Otherwise empty.
+
+void timerConfigForSend(uint16_t aFrequencyKHz); // Initialization of timer hardware generated PWM, if defined(SEND_PWM_BY_TIMER)
+void enableSendPWMByTimer(); // Switch on PWM generation
+void disableSendPWMByTimer(); // Switch off PWM generation
+
+// SEND_PWM_BY_TIMER for different architectures is enabled / defined at IRremote.hpp line 195.
#if defined(SEND_PWM_BY_TIMER) && ( (defined(ESP32) || defined(ARDUINO_ARCH_RP2040) || defined(PARTICLE)) || defined(ARDUINO_ARCH_MBED) )
#define SEND_PWM_DOES_NOT_USE_RECEIVE_TIMER // Receive timer and send generation timer are independent here.
#endif
@@ -61,6 +63,13 @@ void timerConfigForSend(uint16_t aFrequencyKHz);
#undef IR_SEND_PIN // To avoid "warning: "IR_SEND_PIN" redefined". The user warning is done at IRremote.hpp line 202.
#endif
+#if defined(USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN)
+// Use the inverse value, so same code should work for active Low output
+#define IR_SEND_DUTY_CYCLE_PERCENT_FOR_LEVEL_HIGH (100 - IR_SEND_DUTY_CYCLE_PERCENT)
+#else
+#define IR_SEND_DUTY_CYCLE_PERCENT_FOR_LEVEL_HIGH IR_SEND_DUTY_CYCLE_PERCENT
+#endif
+
// Macros for enabling timers for development
//#define SEND_PWM_BY_TIMER
//#define IR_USE_AVR_TIMER1
@@ -117,7 +126,7 @@ void timerDisableReceiveInterrupt() {
/**
* IF PWM should be generated not by software, but by a timer, this function sets output pin mode,
- * configures the timer for generating a PWM with duty cycle of IR_SEND_DUTY_CYCLE_PERCENT
+ * configures the timer for generating a PWM with HIGH output level of duty cycle of IR_SEND_DUTY_CYCLE_PERCENT_FOR_LEVEL_HIGH
* and disables the receive interrupt if it uses the same resource.
* For most architectures, the pin number(s) which can be used for output is determined by the timer used!
* The output of the PWM signal is controlled by enableSendPWMByTimer() and disableSendPWMByTimer().
@@ -194,7 +203,7 @@ void disableSendPWMByTimer() {
// ATtiny84
#elif defined(__AVR_ATtiny84__) || defined(__AVR_ATtiny88__)
# if !defined(IR_USE_AVR_TIMER1)
-#define IR_USE_AVR_TIMER1 // send pin = pin 6
+#define IR_USE_AVR_TIMER1 // send pin = pin 6, no tone() available when using ATTinyCore
# endif
#elif defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__)
@@ -337,6 +346,7 @@ void timerConfigForReceive() {
}
# if defined(SEND_PWM_BY_TIMER)
+// Set IR_SEND_PIN depending on CPU
# if defined(CORE_OC1A_PIN)
#define IR_SEND_PIN CORE_OC1A_PIN // Teensy
@@ -377,9 +387,9 @@ void timerConfigForReceive() {
//#define IR_SEND_PIN PIN_PB6 // OC1AX / PB6 / Pin14 at ATTinyCore
# endif
-# else
+# else // defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__)
#define IR_SEND_PIN 9 // OC1A Arduino Duemilanove, Diecimila, LilyPad, Sparkfun Pro Micro, Leonardo, MH-ET Tiny88 etc.
-# endif // defined(CORE_OC1A_PIN)
+# endif // Set IR_SEND_PIN depending on CPU
# if defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__)
// Clear OC1A/OC1B on Compare Match when up-counting. Set OC1A/OC1B on Compare Match when down counting.
@@ -393,20 +403,31 @@ void enableSendPWMByTimer() {
//TCNT1 = 0; TCCR1A |= _BV(COM1B1); TCCR1D |= _BV(OC1BX); // + enable OC1BX as output
}
# else
-void disableSendPWMByTimer() {
+void enableSendPWMByTimer() {
TCNT1 = 0;
TCCR1A |= _BV(COM1A1);
- TCCR1D |= _BV(OC1AU); // + enable OC1BU as output
+ TCCR1D |= _BV(OC1AU); // + enable OC1AU as output
//TCNT1 = 0; TCCR1A |= _BV(COM1A1); TCCR1D |= _BV(OC1AV); // + enable OC1BV as output
//TCNT1 = 0; TCCR1A |= _BV(COM1A1); TCCR1D |= _BV(OC1AW); // + enable OC1BW as output
//TCNT1 = 0; TCCR1A |= _BV(COM1A1); TCCR1D |= _BV(OC1AX); // + enable OC1BX as output
}
+# endif // defined(USE_TIMER_CHANNEL_B)
-# endif
void disableSendPWMByTimer() {
TCCR1D = 0;
+# if defined(USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN)
+# if defined(IR_SEND_PIN)
+ digitalWriteFast(IR_SEND_PIN, HIGH);
+# else
+ if (__builtin_constant_p(sendPin)) {
+ digitalWriteFast(sendPin, HIGH);
+ } else {
+ digitalWrite(sendPin, HIGH);
+ }
+# endif // defined(IR_SEND_PIN)
+# endif // defined(USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN)
}
-# else
+# else // defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__)
# if defined(USE_TIMER_CHANNEL_B)
void enableSendPWMByTimer() {
TCNT1 = 0;
@@ -414,17 +435,40 @@ void enableSendPWMByTimer() {
}
void disableSendPWMByTimer() {
TCCR1A &= ~(_BV(COM1B1));
+# if defined(USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN)
+# if defined(IR_SEND_PIN)
+ digitalWriteFast(IR_SEND_PIN, HIGH);
+# else
+ if (__builtin_constant_p(sendPin)) {
+ digitalWriteFast(sendPin, HIGH);
+ } else {
+ digitalWrite(sendPin, HIGH);
+ }
+# endif // defined(IR_SEND_PIN)
+# endif // defined(USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN)
}
-# else
+# else // defined(USE_TIMER_CHANNEL_B)
void enableSendPWMByTimer() {
TCNT1 = 0;
TCCR1A |= _BV(COM1A1); // Clear OC1A/OC1B on Compare Match when up-counting. Set OC1A/OC1B on Compare Match when downcounting.
}
void disableSendPWMByTimer() {
TCCR1A &= ~(_BV(COM1A1));
+# if defined(USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN)
+# if defined(IR_SEND_PIN)
+ digitalWriteFast(IR_SEND_PIN, HIGH);
+# else
+ if (__builtin_constant_p(sendPin)) {
+ digitalWriteFast(sendPin, HIGH);
+ } else {
+ digitalWrite(sendPin, HIGH);
+ }
+# endif // defined(IR_SEND_PIN)
+# endif // defined(USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN)
}
-# endif
-# endif
+# endif // defined(USE_TIMER_CHANNEL_B)
+
+# endif // defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__)
/*
* timerConfigForSend() is used exclusively by IRsend::enableIROut()
@@ -433,31 +477,31 @@ void disableSendPWMByTimer() {
void timerConfigForSend(uint16_t aFrequencyKHz) {
timerDisableReceiveInterrupt();
-# if (((F_CPU / 2000) / 38) < 256)
+# if (((F_CPU / 2000) / 38) < 256)
const uint16_t tPWMWrapValue = (F_CPU / 2000) / (aFrequencyKHz); // 210,52 for 38 kHz @16 MHz clock, 2000 instead of 1000 because of Phase Correct PWM
TCCR1A = _BV(WGM11); // PWM, Phase Correct, Top is ICR1
TCCR1B = _BV(WGM13) | _BV(CS10); // CS10 -> no prescaling
ICR1 = tPWMWrapValue - 1;
-# if defined(USE_TIMER_CHANNEL_B)
- OCR1B = ((tPWMWrapValue * IR_SEND_DUTY_CYCLE_PERCENT) / 100) - 1;
-# else
- OCR1A = ((tPWMWrapValue * IR_SEND_DUTY_CYCLE_PERCENT) / 100) - 1;
-# endif
+# if defined(USE_TIMER_CHANNEL_B)
+ OCR1B = ((tPWMWrapValue * IR_SEND_DUTY_CYCLE_PERCENT_FOR_LEVEL_HIGH) / 100) - 1;
+# else
+ OCR1A = ((tPWMWrapValue * IR_SEND_DUTY_CYCLE_PERCENT_FOR_LEVEL_HIGH) / 100) - 1;
+# endif
TCNT1 = 0; // not really required, since we have an 8 bit counter, but makes the signal more reproducible
-# else
+# else
const uint16_t tPWMWrapValue = ((F_CPU / 8) / 2000) / (aFrequencyKHz); // 2000 instead of 1000 because of Phase Correct PWM
TCCR1A = _BV(WGM11);// PWM, Phase Correct, Top is ICR1
TCCR1B = _BV(WGM13) | _BV(CS11);// CS11 -> Prescaling by 8
ICR1 = tPWMWrapValue - 1;
-# if defined(USE_TIMER_CHANNEL_B)
- OCR1A = ((tPWMWrapValue * IR_SEND_DUTY_CYCLE_PERCENT) / 100) - 1;
-# else
- OCR1A = ((tPWMWrapValue * IR_SEND_DUTY_CYCLE_PERCENT) / 100) - 1;
-# endif
+# if defined(USE_TIMER_CHANNEL_B)
+ OCR1A = ((tPWMWrapValue * IR_SEND_DUTY_CYCLE_PERCENT_FOR_LEVEL_HIGH) / 100) - 1;
+# else
+ OCR1A = ((tPWMWrapValue * IR_SEND_DUTY_CYCLE_PERCENT_FOR_LEVEL_HIGH) / 100) - 1;
+# endif
TCNT1 = 0; // not really required, since we have an 8 bit counter, but makes the signal more reproducible
-# endif
+# endif // if (((F_CPU / 2000) / 38) < 256)
}
-# endif // defined(SEND_PWM_BY_TIMER)
+# endif // defined(SEND_PWM_BY_TIMER) - Timer1
/*
* AVR Timer2 (8 bits) // Tone timer on Uno
@@ -471,7 +515,6 @@ void timerDisableReceiveInterrupt() {
TIMSK2 = 0;
}
#define TIMER_INTR_NAME TIMER2_COMPB_vect // We use TIMER2_COMPB_vect to be compatible with tone() library
-
#define TIMER_COUNT_TOP (F_CPU * MICROS_PER_TICK / MICROS_IN_ONE_SECOND)
void timerConfigForReceive() {
@@ -491,6 +534,7 @@ void timerConfigForReceive() {
}
# if defined(SEND_PWM_BY_TIMER)
+// Set IR_SEND_PIN depending on CPU
# if defined(CORE_OC2B_PIN)
#define IR_SEND_PIN CORE_OC2B_PIN // Teensy
@@ -505,8 +549,13 @@ void timerConfigForReceive() {
#define IR_SEND_PIN 14 // MightyCore, MegaCore
# else
-#define IR_SEND_PIN 3 // Arduino Duemilanove, Diecimila, LilyPad, etc
-# endif // defined(CORE_OC2B_PIN)
+/*
+ * Using pin 11 / PB3 / OC2A for this purpose is NOT possible, since we need a PWM with a selectable frequency.
+ * This is only possible by using Phase Correct with Top as OCR2A.
+ * Thus the OCR2A register cannot be used for comparing for channel A and TOP with OCR2B is not supported by Hardware :-(.
+ */
+#define IR_SEND_PIN 3 // Arduino Uno Pin PD3, Duemilanove, Diecimila, LilyPad, etc
+# endif // Set IR_SEND_PIN depending on CPU
void enableSendPWMByTimer() {
TCNT2 = 0;
@@ -514,6 +563,17 @@ void enableSendPWMByTimer() {
}
void disableSendPWMByTimer() {
TCCR2A &= ~(_BV(COM2B1)); // Normal port operation, OC2B disconnected.
+# if defined(USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN)
+# if defined(IR_SEND_PIN)
+ digitalWriteFast(IR_SEND_PIN, HIGH);
+# else
+ if (__builtin_constant_p(sendPin)) {
+ digitalWriteFast(sendPin, HIGH);
+ } else {
+ digitalWrite(sendPin, HIGH);
+ }
+# endif // defined(IR_SEND_PIN)
+# endif // defined(USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN)
}
/*
@@ -533,14 +593,14 @@ void timerConfigForSend(uint16_t aFrequencyKHz) {
TCCR2A = _BV(WGM20); // PWM, Phase Correct, Top is OCR2A
TCCR2B = _BV(WGM22) | _BV(CS20); // CS20 -> no prescaling
OCR2A = tPWMWrapValue - 1; // The top value for the timer. The modulation frequency will be F_CPU / 2 / (OCR2A + 1).
- OCR2B = ((tPWMWrapValue * IR_SEND_DUTY_CYCLE_PERCENT) / 100) - 1;
+ OCR2B = ((tPWMWrapValue * IR_SEND_DUTY_CYCLE_PERCENT_FOR_LEVEL_HIGH) / 100) - 1;
TCNT2 = 0; // not really required, since we have an 8 bit counter, but makes the signal more reproducible
# else
const uint16_t tPWMWrapValue = ((F_CPU / 8) / 2000) / (aFrequencyKHz); // 2000 instead of 1000 because of Phase Correct PWM
TCCR2A = _BV(WGM20);// PWM, Phase Correct, Top is OCR2A
TCCR2B = _BV(WGM22) | _BV(CS21);// CS21 -> Prescaling by 8
OCR2A = tPWMWrapValue - 1;
- OCR2B = ((tPWMWrapValue * IR_SEND_DUTY_CYCLE_PERCENT) / 100) - 1;
+ OCR2B = ((tPWMWrapValue * IR_SEND_DUTY_CYCLE_PERCENT_FOR_LEVEL_HIGH) / 100) - 1;
TCNT2 = 0;// not really required, since we have an 8 bit counter, but makes the signal more reproducible
# endif
}
@@ -568,6 +628,7 @@ void timerConfigForReceive() {
}
# if defined(SEND_PWM_BY_TIMER)
+// Set IR_SEND_PIN depending on CPU
# if defined(CORE_OC3A_PIN)
#define IR_SEND_PIN CORE_OC3A_PIN // Teensy
@@ -580,7 +641,7 @@ void timerConfigForReceive() {
# else
#error Please add OC3A pin number here
-# endif
+# endif // Set IR_SEND_PIN depending on CPU
void enableSendPWMByTimer() {
TCNT3 = 0;
@@ -588,6 +649,17 @@ void enableSendPWMByTimer() {
}
void disableSendPWMByTimer() {
TCCR3A &= ~(_BV(COM3A1));
+# if defined(USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN)
+# if defined(IR_SEND_PIN)
+ digitalWriteFast(IR_SEND_PIN, HIGH);
+# else
+ if (__builtin_constant_p(sendPin)) {
+ digitalWriteFast(sendPin, HIGH);
+ } else {
+ digitalWrite(sendPin, HIGH);
+ }
+# endif // defined(IR_SEND_PIN)
+# endif // defined(USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN)
}
/*
@@ -595,16 +667,16 @@ void disableSendPWMByTimer() {
* Set output pin mode and disable receive interrupt if it uses the same resource
*/
void timerConfigForSend(uint16_t aFrequencyKHz) {
-#if F_CPU > 16000000
+# if F_CPU > 16000000
#error "Creating timer PWM with timer 3 is not supported for F_CPU > 16 MHz"
-#endif
+# endif
timerDisableReceiveInterrupt();
const uint16_t tPWMWrapValue = (F_CPU / 2000) / (aFrequencyKHz); // 210,52 for 38 kHz @16 MHz clock, 2000 instead of 1000 because of Phase Correct PWM
TCCR3A = _BV(WGM31);
TCCR3B = _BV(WGM33) | _BV(CS30); // PWM, Phase Correct, ICRn as TOP, complete period is double of tPWMWrapValue
ICR3 = tPWMWrapValue - 1;
- OCR3A = ((tPWMWrapValue * IR_SEND_DUTY_CYCLE_PERCENT) / 100) - 1;
+ OCR3A = ((tPWMWrapValue * IR_SEND_DUTY_CYCLE_PERCENT_FOR_LEVEL_HIGH) / 100) - 1;
TCNT3 = 0; // required, since we have an 16 bit counter
}
# endif // defined(SEND_PWM_BY_TIMER)
@@ -629,6 +701,7 @@ void timerConfigForReceive() {
}
# if defined(SEND_PWM_BY_TIMER)
+// Set IR_SEND_PIN depending on CPU
# if defined(CORE_OC4A_PIN)
#define IR_SEND_PIN CORE_OC4A_PIN
# elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
@@ -643,18 +716,29 @@ void enableSendPWMByTimer() {
}
void disableSendPWMByTimer() {
TCCR4A &= ~(_BV(COM4A1));
+# if defined(USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN)
+# if defined(IR_SEND_PIN)
+ digitalWriteFast(IR_SEND_PIN, HIGH);
+# else
+ if (__builtin_constant_p(sendPin)) {
+ digitalWriteFast(sendPin, HIGH);
+ } else {
+ digitalWrite(sendPin, HIGH);
+ }
+# endif // defined(IR_SEND_PIN)
+# endif // defined(USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN)
}
void timerConfigForSend(uint16_t aFrequencyKHz) {
-#if F_CPU > 16000000
+# if F_CPU > 16000000
#error "Creating timer PWM with timer 4 is not supported for F_CPU > 16 MHz"
-#endif
+# endif
timerDisableReceiveInterrupt();
const uint16_t tPWMWrapValue = (F_CPU / 2000) / (aFrequencyKHz); // 210,52 for 38 kHz @16 MHz clock, 2000 instead of 1000 because of Phase Correct PWM
TCCR4A = _BV(WGM41);
TCCR4B = _BV(WGM43) | _BV(CS40);
ICR4 = tPWMWrapValue - 1;
- OCR4A = ((tPWMWrapValue * IR_SEND_DUTY_CYCLE_PERCENT) / 100) - 1;
+ OCR4A = ((tPWMWrapValue * IR_SEND_DUTY_CYCLE_PERCENT_FOR_LEVEL_HIGH) / 100) - 1;
TCNT4 = 0; // required, since we have an 16 bit counter
}
# endif // defined(SEND_PWM_BY_TIMER)
@@ -702,6 +786,17 @@ void enableSendPWMByTimer() {
}
void disableSendPWMByTimer() {
TCCR4A &= ~(_BV(COM4A0)); // (Pro Micro does not map PC7 (32/ICP3/CLK0/OC4A)
+# if defined(USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN)
+# if defined(IR_SEND_PIN)
+ digitalWriteFast(IR_SEND_PIN, HIGH);
+# else
+ if (__builtin_constant_p(sendPin)) {
+ digitalWriteFast(sendPin, HIGH);
+ } else {
+ digitalWrite(sendPin, HIGH);
+ }
+# endif // defined(IR_SEND_PIN)
+# endif // defined(USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN)
}
// of ATmega32U4 )
# else
@@ -712,6 +807,17 @@ void enableSendPWMByTimer() {
}
void disableSendPWMByTimer() {
TCCR4A &= ~(_BV(COM4A1));
+# if defined(USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN)
+# if defined(IR_SEND_PIN)
+ digitalWriteFast(IR_SEND_PIN, HIGH);
+# else
+ if (__builtin_constant_p(sendPin)) {
+ digitalWriteFast(sendPin, HIGH);
+ } else {
+ digitalWrite(sendPin, HIGH);
+ }
+# endif // defined(IR_SEND_PIN)
+# endif // defined(USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN)
}
# endif
@@ -733,8 +839,8 @@ void timerConfigForSend(uint16_t aFrequencyKHz) {
TCCR4E = 0;
TC4H = tPWMWrapValue >> 8;
OCR4C = tPWMWrapValue;
- TC4H = (tPWMWrapValue * IR_SEND_DUTY_CYCLE_PERCENT / 100) >> 8;
- OCR4A = (tPWMWrapValue * IR_SEND_DUTY_CYCLE_PERCENT / 100) & 255;
+ TC4H = (tPWMWrapValue * IR_SEND_DUTY_CYCLE_PERCENT_FOR_LEVEL_HIGH / 100) >> 8;
+ OCR4A = (tPWMWrapValue * IR_SEND_DUTY_CYCLE_PERCENT_FOR_LEVEL_HIGH / 100) & 255;
TCNT4 = 0; // not really required, since we have an 8 bit counter, but makes the signal more reproducible
}
# endif // defined(SEND_PWM_BY_TIMER)
@@ -774,6 +880,17 @@ void enableSendPWMByTimer() {
}
void disableSendPWMByTimer() {
TCCR5A &= ~(_BV(COM5A1));
+# if defined(USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN)
+# if defined(IR_SEND_PIN)
+ digitalWriteFast(IR_SEND_PIN, HIGH);
+# else
+ if (__builtin_constant_p(sendPin)) {
+ digitalWriteFast(sendPin, HIGH);
+ } else {
+ digitalWrite(sendPin, HIGH);
+ }
+# endif // defined(IR_SEND_PIN)
+# endif // defined(USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN)
}
/*
@@ -790,7 +907,7 @@ void timerConfigForSend(uint16_t aFrequencyKHz) {
TCCR5A = _BV(WGM51);
TCCR5B = _BV(WGM53) | _BV(CS50);
ICR5 = tPWMWrapValue - 1;
- OCR5A = ((tPWMWrapValue * IR_SEND_DUTY_CYCLE_PERCENT) / 100) - 1;
+ OCR5A = ((tPWMWrapValue * IR_SEND_DUTY_CYCLE_PERCENT_FOR_LEVEL_HIGH) / 100) - 1;
TCNT5 = 0; // required, since we have an 16 bit counter
}
# endif // defined(SEND_PWM_BY_TIMER)
@@ -833,6 +950,17 @@ void enableSendPWMByTimer() {
}
void disableSendPWMByTimer() {
TCCR0A &= ~(_BV(COM0B1));
+# if defined(USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN)
+# if defined(IR_SEND_PIN)
+ digitalWriteFast(IR_SEND_PIN, HIGH);
+# else
+ if (__builtin_constant_p(sendPin)) {
+ digitalWriteFast(sendPin, HIGH);
+ } else {
+ digitalWrite(sendPin, HIGH);
+ }
+# endif // defined(IR_SEND_PIN)
+# endif // defined(USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN)
}
/*
@@ -840,16 +968,16 @@ void disableSendPWMByTimer() {
* Set output pin mode and disable receive interrupt if it uses the same resource
*/
void timerConfigForSend(uint16_t aFrequencyKHz) {
-#if F_CPU > 16000000
+# if F_CPU > 16000000
#error "Creating timer PWM with timer TINY0 is not supported for F_CPU > 16 MHz"
-#endif
+# endif
timerDisableReceiveInterrupt();
const uint16_t tPWMWrapValue = (F_CPU / 2000) / (aFrequencyKHz); // 210,52 for 38 kHz @16 MHz clock, 2000 instead of 1000 because of Phase Correct PWM
TCCR0A = _BV(WGM00); // PWM, Phase Correct, Top is OCR0A
TCCR0B = _BV(WGM02) | _BV(CS00); // CS00 -> no prescaling
OCR0A = tPWMWrapValue - 1;
- OCR0B = ((tPWMWrapValue * IR_SEND_DUTY_CYCLE_PERCENT) / 100) - 1;
+ OCR0B = ((tPWMWrapValue * IR_SEND_DUTY_CYCLE_PERCENT_FOR_LEVEL_HIGH) / 100) - 1;
TCNT0 = 0; // not really required, since we have an 8 bit counter, but makes the signal more reproducible
}
# endif // defined(SEND_PWM_BY_TIMER)
@@ -892,6 +1020,17 @@ void enableSendPWMByTimer() {
}
void disableSendPWMByTimer() {
GTCCR &= ~(_BV(PWM1B) | _BV(COM1B0));
+# if defined(USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN)
+# if defined(IR_SEND_PIN)
+ digitalWriteFast(IR_SEND_PIN, HIGH);
+# else
+ if (__builtin_constant_p(sendPin)) {
+ digitalWriteFast(sendPin, HIGH);
+ } else {
+ digitalWrite(sendPin, HIGH);
+ }
+# endif // defined(IR_SEND_PIN)
+# endif // defined(USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN)
}
/*
@@ -905,14 +1044,14 @@ void timerConfigForSend(uint16_t aFrequencyKHz) {
const uint16_t tPWMWrapValue = (F_CPU / 1000) / (aFrequencyKHz); // 421 @16 MHz, 26 @1 MHz and 38 kHz
TCCR1 = _BV(CTC1) | _BV(CS10);// CTC1 = 1: TOP value set to OCR1C, CS10 No Prescaling
OCR1C = tPWMWrapValue - 1;
- OCR1B = ((tPWMWrapValue * IR_SEND_DUTY_CYCLE_PERCENT) / 100) - 1;
+ OCR1B = ((tPWMWrapValue * IR_SEND_DUTY_CYCLE_PERCENT_FOR_LEVEL_HIGH) / 100) - 1;
TCNT1 = 0;// not really required, since we have an 8 bit counter, but makes the signal more reproducible
GTCCR = _BV(PWM1B) | _BV(COM1B0);// PWM1B = 1: Enable PWM for OCR1B, COM1B0 Clear on compare match
# else
const uint16_t tPWMWrapValue = ((F_CPU / 2) / 1000) / (aFrequencyKHz); // 210 for 16 MHz and 38 kHz
TCCR1 = _BV(CTC1) | _BV(CS11); // CTC1 = 1: TOP value set to OCR1C, CS11 Prescaling by 2
OCR1C = tPWMWrapValue - 1;
- OCR1B = ((tPWMWrapValue * IR_SEND_DUTY_CYCLE_PERCENT) / 100) - 1;
+ OCR1B = ((tPWMWrapValue * IR_SEND_DUTY_CYCLE_PERCENT_FOR_LEVEL_HIGH) / 100) - 1;
TCNT1 = 0; // not really required, since we have an 8 bit counter, but makes the signal more reproducible
GTCCR = _BV(PWM1B) | _BV(COM1B0); // PWM1B = 1: Enable PWM for OCR1B, COM1B0 Clear on compare match
# endif
@@ -988,6 +1127,17 @@ void enableSendPWMByTimer() {
}
void disableSendPWMByTimer() {
TCB0.CTRLB &= ~(TCB_CCMPEN_bm);
+# if defined(USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN)
+# if defined(IR_SEND_PIN)
+ digitalWriteFast(IR_SEND_PIN, HIGH);
+# else
+ if (__builtin_constant_p(sendPin)) {
+ digitalWriteFast(sendPin, HIGH);
+ } else {
+ digitalWrite(sendPin, HIGH);
+ }
+# endif // defined(IR_SEND_PIN)
+# endif // defined(USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN)
}
/*
@@ -1004,7 +1154,7 @@ void timerConfigForSend(uint16_t aFrequencyKHz) {
const uint16_t tPWMWrapValue = (F_CPU / 2000) / (aFrequencyKHz); // 210,52 for 38 kHz @16 MHz clock, 2000 instead of 1000 because of using CLK / 2
TCB0.CTRLB = TCB_CNTMODE_PWM8_gc; // 8 bit PWM mode
TCB0.CCMPL = tPWMWrapValue - 1; // Period of 8 bit PWM
- TCB0.CCMPH = ((tPWMWrapValue * IR_SEND_DUTY_CYCLE_PERCENT) / 100) - 1; // Duty cycle of waveform of 8 bit PWM
+ TCB0.CCMPH = ((tPWMWrapValue * IR_SEND_DUTY_CYCLE_PERCENT_FOR_LEVEL_HIGH) / 100) - 1; // Duty cycle of waveform of 8 bit PWM
TCB0.CTRLA = (TCB_CLKSEL_CLKDIV2_gc) | (TCB_ENABLE_bm); // use CLK / 2
TCB0.CNT = 0; // not really required, since we have an 8 bit counter, but makes the signal more reproducible
}
@@ -1057,6 +1207,17 @@ void enableSendPWMByTimer() {
}
void disableSendPWMByTimer() {
TCD0.CTRLA = 0; // do not disable output, disable complete timer
+# if defined(USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN)
+# if defined(IR_SEND_PIN)
+ digitalWriteFast(IR_SEND_PIN, HIGH);
+# else
+ if (__builtin_constant_p(sendPin)) {
+ digitalWriteFast(sendPin, HIGH);
+ } else {
+ digitalWrite(sendPin, HIGH);
+ }
+# endif // defined(IR_SEND_PIN)
+# endif // defined(USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN)
}
/*
@@ -1077,7 +1238,7 @@ void timerConfigForSend(uint16_t aFrequencyKHz) {
// Generate duty cycle signal for debugging etc.
TCD0.CMPASET = 0;
- TCD0.CMPACLR = (tPWMWrapValue * IR_SEND_DUTY_CYCLE_PERCENT / 100) - 1; // duty cycle for WOA
+ TCD0.CMPACLR = (tPWMWrapValue * IR_SEND_DUTY_CYCLE_PERCENT_FOR_LEVEL_HIGH / 100) - 1; // duty cycle for WOA
TCD0.INTFLAGS = TCD_OVF_bm; // reset interrupt flags
TCD0.INTCTRL = TCD_OVF_bm; // overflow interrupt
@@ -1434,39 +1595,51 @@ void timerConfigForReceive() {
* so it is recommended to always define SEND_PWM_BY_TIMER
**********************************************************/
#elif defined(ESP32)
-# if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
-#error This library does not work with ESP32 core 3.x. Please use ESP 2.0.17 core. You are kindly invited to port and document the code to 3.x, to fix this problem!
+# if !defined(ESP_ARDUINO_VERSION)
+#define ESP_ARDUINO_VERSION 0x010101 // Version 1.1.1
# endif
// Variables specific to the ESP32.
// the ledc functions behave like hardware timers for us :-), so we do not require our own soft PWM generation code.
-hw_timer_t *s50usTimer = NULL; // set by timerConfigForReceive()
+hw_timer_t *s50usTimer = nullptr; // set by timerConfigForReceive()
+#define _IRREMOTE_ESP32_LEDC_RESOLUTION 8
+#define _IRREMOTE_ESP32_LEDC_RESOLUTION_MAX_PWM_VALUE 255
-# if ESP_ARDUINO_VERSION < ESP_ARDUINO_VERSION_VAL(3, 0, 0) && !defined(SEND_LEDC_CHANNEL)
+//# if ESP_ARDUINO_VERSION < ESP_ARDUINO_VERSION_VAL(3, 0, 0) && !defined(SEND_LEDC_CHANNEL)
+# if ESP_ARDUINO_VERSION < (3 << 16 | 0 << 8 | 0) && !defined(SEND_LEDC_CHANNEL) // works also in case ESP_ARDUINO_VERSION_VAL is not defined
#define SEND_LEDC_CHANNEL 0 // The channel used for PWM 0 to 7 are high speed PWM channels
# endif
void timerEnableReceiveInterrupt() {
+//# if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
+# if ESP_ARDUINO_VERSION >= (3 << 16 | 0 << 8 | 0)
+ timerStart(s50usTimer);
+# else
timerAlarmEnable(s50usTimer);
+# endif
}
-# if !defined(ESP_ARDUINO_VERSION)
-#define ESP_ARDUINO_VERSION 0
-# endif
-# if !defined(ESP_ARDUINO_VERSION_VAL)
-#define ESP_ARDUINO_VERSION_VAL(major, minor, patch) 202
-# endif
-# if ESP_ARDUINO_VERSION < ESP_ARDUINO_VERSION_VAL(2, 0, 2)
+//# if ESP_ARDUINO_VERSION < ESP_ARDUINO_VERSION_VAL(2, 0, 2)
+# if ESP_ARDUINO_VERSION < (2 << 16 | 0 << 8 | 2)
+/*
+ * Special support for ESP core < 202
+ */
void timerDisableReceiveInterrupt() {
- if (s50usTimer != NULL) {
+ if (s50usTimer != nullptr) {
timerDetachInterrupt(s50usTimer);
timerEnd(s50usTimer);
}
}
# else
+
void timerDisableReceiveInterrupt() {
- if (s50usTimer != NULL) {
+ if (s50usTimer != nullptr) {
+//# if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
+# if ESP_ARDUINO_VERSION >= (3 << 16 | 0 << 8 | 0)
+ timerStop(s50usTimer);
+# else
timerAlarmDisable(s50usTimer);
+# endif
}
}
# endif
@@ -1476,37 +1649,63 @@ void timerDisableReceiveInterrupt() {
#undef ISR
# endif
-# if !defined(DISABLE_CODE_FOR_RECEIVER) // &IRReceiveTimerInterruptHandler is referenced, but not available
+# if !defined(DISABLE_CODE_FOR_RECEIVER) // Otherwise the &IRReceiveTimerInterruptHandler is referenced, but not available
void timerConfigForReceive() {
// ESP32 has a proper API to setup timers, no weird chip macros needed
// simply call the readable API versions :)
// 3 timers, choose #1, 80 divider for microsecond precision @80MHz clock, count_up = true
- if(s50usTimer == NULL) {
+ if (s50usTimer == nullptr) {
+//# if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
+# if ESP_ARDUINO_VERSION >= (3 << 16 | 0 << 8 | 0)
+ s50usTimer = timerBegin(1000000); // Only 1 parameter is required. 1000000 corresponds to 1 MHz / 1 uSec. After successful setup the timer will automatically start.
+ timerStop(s50usTimer); // Stop it here, to avoid "error E (3447) gptimer: gptimer_start(348): timer is not enabled yet" at timerEnableReceiveInterrupt()
+ timerAttachInterrupt(s50usTimer, &IRReceiveTimerInterruptHandler);
+ timerAlarm(s50usTimer, MICROS_PER_TICK, true, 0); // 0 in the last parameter is repeat forever
+# else
s50usTimer = timerBegin(1, 80, true);
timerAttachInterrupt(s50usTimer, &IRReceiveTimerInterruptHandler, false); // false -> level interrupt, true -> edge interrupt, but this is not supported :-(
timerAlarmWrite(s50usTimer, MICROS_PER_TICK, true);
+# endif
}
// every 50 us, autoreload = true
}
# endif
-# if !defined(IR_SEND_PIN)
-uint8_t sLastSendPin = 0; // To detach before attach, if already attached
-# endif
+uint8_t sLastSendPin = 0; // Avoid multiple attach() or if pin changes, detach before attach
# if defined(SEND_PWM_BY_TIMER)
void enableSendPWMByTimer() {
-# if ESP_ARDUINO_VERSION < ESP_ARDUINO_VERSION_VAL(3, 0, 0)
- ledcWrite(SEND_LEDC_CHANNEL, (IR_SEND_DUTY_CYCLE_PERCENT * 256) / 100); // * 256 since we have 8 bit resolution
+//# if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
+# if ESP_ARDUINO_VERSION >= (3 << 16 | 0 << 8 | 0)
+# if defined(IR_SEND_PIN)
+ ledcWrite(IR_SEND_PIN, (IR_SEND_DUTY_CYCLE_PERCENT_FOR_LEVEL_HIGH * _IRREMOTE_ESP32_LEDC_RESOLUTION_MAX_PWM_VALUE) / 100); // 3.x API
+# else
+ ledcWrite(IrSender.sendPin, (IR_SEND_DUTY_CYCLE_PERCENT_FOR_LEVEL_HIGH * _IRREMOTE_ESP32_LEDC_RESOLUTION_MAX_PWM_VALUE) / 100); // 3.x API
+# endif
# else
- ledcWrite(IrSender.sendPin, (IR_SEND_DUTY_CYCLE_PERCENT * 256) / 100); // New API
+ // ESP version < 3.0
+ ledcWrite(SEND_LEDC_CHANNEL, (IR_SEND_DUTY_CYCLE_PERCENT_FOR_LEVEL_HIGH * _IRREMOTE_ESP32_LEDC_RESOLUTION_MAX_PWM_VALUE) / 100); // * 256 since we have 8 bit resolution
# endif
}
void disableSendPWMByTimer() {
-# if ESP_ARDUINO_VERSION < ESP_ARDUINO_VERSION_VAL(3, 0, 0)
- ledcWrite(SEND_LEDC_CHANNEL, 0);
+//# if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
+# if ESP_ARDUINO_VERSION >= (3 << 16 | 0 << 8 | 0)
+# if defined(IR_SEND_PIN)
+# if defined(USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN)
+ ledcWrite(IR_SEND_PIN, _IRREMOTE_ESP32_LEDC_RESOLUTION_MAX_PWM_VALUE); // 3.x API
+# else
+ ledcWrite(IR_SEND_PIN, 0); // 3.x API
+# endif
+# else
+# if defined(USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN)
+ ledcWrite(IrSender.sendPin, _IRREMOTE_ESP32_LEDC_RESOLUTION_MAX_PWM_VALUE); // 3.x API
+# else
+ ledcWrite(IrSender.sendPin, 0); // 3.x API
+# endif
+# endif
# else
- ledcWrite(IrSender.sendPin, 0); // New API
+ // ESP version < 3.0
+ ledcWrite(SEND_LEDC_CHANNEL, 0);
# endif
}
@@ -1515,25 +1714,30 @@ void disableSendPWMByTimer() {
* ledcWrite since ESP 2.0.2 does not work if pin mode is set.
*/
void timerConfigForSend(uint16_t aFrequencyKHz) {
-# if ESP_ARDUINO_VERSION < ESP_ARDUINO_VERSION_VAL(3, 0, 0)
- ledcSetup(SEND_LEDC_CHANNEL, aFrequencyKHz * 1000, 8); // 8 bit PWM resolution
+//# if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
+# if ESP_ARDUINO_VERSION >= (3 << 16 | 0 << 8 | 0)
# if defined(IR_SEND_PIN)
- ledcAttachPin(IR_SEND_PIN, SEND_LEDC_CHANNEL); // attach pin to channel
+ if(sLastSendPin == 0){
+ ledcAttach(IR_SEND_PIN, aFrequencyKHz * 1000, _IRREMOTE_ESP32_LEDC_RESOLUTION); // 3.x API
+ sLastSendPin = IR_SEND_PIN;
+ }
# else
if(sLastSendPin != 0 && sLastSendPin != IrSender.sendPin){
- ledcDetachPin(IrSender.sendPin); // detach pin before new attaching see #1194
+ ledcDetach(IrSender.sendPin); // detach pin before new attaching see #1194
}
- ledcAttachPin(IrSender.sendPin, SEND_LEDC_CHANNEL); // attach pin to channel
+ ledcAttach(IrSender.sendPin, aFrequencyKHz * 1000, _IRREMOTE_ESP32_LEDC_RESOLUTION); // 3.x API
sLastSendPin = IrSender.sendPin;
# endif
-# else // New API here
+# else
+ // ESP version < 3.0
+ ledcSetup(SEND_LEDC_CHANNEL, aFrequencyKHz * 1000, _IRREMOTE_ESP32_LEDC_RESOLUTION); // 8 bit PWM resolution
# if defined(IR_SEND_PIN)
- ledcAttach(IR_SEND_PIN, aFrequencyKHz * 1000, 8); // New API
+ ledcAttachPin(IR_SEND_PIN, SEND_LEDC_CHANNEL); // attach pin to channel
# else
if(sLastSendPin != 0 && sLastSendPin != IrSender.sendPin){
- ledcDetach(IrSender.sendPin); // detach pin before new attaching see #1194
+ ledcDetachPin(IrSender.sendPin); // detach pin before new attaching see #1194
}
- ledcAttach(IrSender.sendPin, aFrequencyKHz * 1000, 8); // New API
+ ledcAttachPin(IrSender.sendPin, SEND_LEDC_CHANNEL); // attach pin to channel
sLastSendPin = IrSender.sendPin;
# endif
# endif
@@ -1550,8 +1754,13 @@ void timerConfigForSend(uint16_t aFrequencyKHz) {
# if !defined(IR_SAMD_TIMER)
# if defined(__SAMD51__)
+# if defined(TC5)
#define IR_SAMD_TIMER TC5
#define IR_SAMD_TIMER_IRQ TC5_IRQn
+# else
+#define IR_SAMD_TIMER TC3
+#define IR_SAMD_TIMER_IRQ TC3_IRQn
+# endif
# else
// SAMD21
#define IR_SAMD_TIMER TC3
@@ -1586,7 +1795,11 @@ void timerConfigForReceive() {
# if defined(__SAMD51__)
// Enable the TC5 clock, use generic clock generator 0 (F_CPU) for TC5
+# if defined(TC5_GCLK_ID)
GCLK->PCHCTRL[TC5_GCLK_ID].reg = GCLK_PCHCTRL_GEN_GCLK0_Val | (1 << GCLK_PCHCTRL_CHEN_Pos);
+# else
+ GCLK->PCHCTRL[TC3_GCLK_ID].reg = GCLK_PCHCTRL_GEN_GCLK0_Val | (1 << GCLK_PCHCTRL_CHEN_Pos);
+# endif
// The TC should be disabled before the TC is reset in order to avoid undefined behavior.
TC->CTRLA.reg &= ~TC_CTRLA_ENABLE; // Disable the Timer
@@ -1643,18 +1856,12 @@ void timerConfigForReceive() {
}
# if !defined(DISABLE_CODE_FOR_RECEIVER)
-# if defined(__SAMD51__)
-void TC5_Handler(void) {
- TcCount16 *TC = (TcCount16*) IR_SAMD_TIMER;
- // Check for right interrupt bit
- if (TC->INTFLAG.bit.MC0 == 1) {
- // reset bit for next turn
- TC->INTFLAG.bit.MC0 = 1;
- IRReceiveTimerInterruptHandler();
- }
-}
+# if defined(__SAMD51__) && defined(TC5)
+void TC5_Handler(void)
# else
-void TC3_Handler(void) {
+void TC3_Handler(void)
+# endif // defined(__SAMD51__)
+{
TcCount16 *TC = (TcCount16*) IR_SAMD_TIMER;
// Check for right interrupt bit
if (TC->INTFLAG.bit.MC0 == 1) {
@@ -1663,7 +1870,6 @@ void TC3_Handler(void) {
IRReceiveTimerInterruptHandler();
}
}
-# endif // defined(__SAMD51__)
# endif // !defined(DISABLE_CODE_FOR_RECEIVER)
/***************************************
@@ -1698,6 +1904,7 @@ mbed::PwmOut sPwmOutForSendPWM(digitalPinToPinName(IR_SEND_PIN));
mbed::PwmOut sPwmOutForSendPWM(digitalPinToPinName(IrSender.sendPin));
# endif
uint8_t sIROutPuseWidth;
+uint8_t sIROutPuseWidthForHigh; // for setting level to 1
void enableSendPWMByTimer() {
sPwmOutForSendPWM.pulsewidth_us(sIROutPuseWidth);
@@ -1706,7 +1913,11 @@ void enableSendPWMByTimer() {
//void disableSendPWMByTimer() { sPwmOutForSendPWM.suspend();} // this kills pulsewidth_us value and does not set output level to LOW
void disableSendPWMByTimer() {
+# if defined(USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN)
+ sPwmOutForSendPWM.pulsewidth_us(sIROutPuseWidthForHigh); // this also sets output level to HIGH :-)
+# else
sPwmOutForSendPWM.pulsewidth_us(0); // this also sets output level to LOW :-)
+# endif
}
/*
@@ -1714,8 +1925,9 @@ void disableSendPWMByTimer() {
* Set output pin mode and disable receive interrupt if it uses the same resource
*/
void timerConfigForSend(uint16_t aFrequencyKHz) {
- sPwmOutForSendPWM.period_us(1000 / aFrequencyKHz); // 26.315 for 38 kHz
- sIROutPuseWidth = (1000 * IR_SEND_DUTY_CYCLE_PERCENT) / (aFrequencyKHz * 100);
+ sIROutPuseWidthForHigh = 1000 / aFrequencyKHz;
+ sPwmOutForSendPWM.period_us(sIROutPuseWidthForHigh); // 26.315 for 38 kHz
+ sIROutPuseWidth = (1000 * IR_SEND_DUTY_CYCLE_PERCENT_FOR_LEVEL_HIGH) / (aFrequencyKHz * 100);
}
# endif // defined(SEND_PWM_BY_TIMER)
@@ -1742,7 +1954,7 @@ bool IRTimerInterruptHandlerHelper(repeating_timer_t*) {
}
void timerEnableReceiveInterrupt() {
- add_repeating_timer_us(-(MICROS_PER_TICK), IRTimerInterruptHandlerHelper, NULL, &s50usTimer);
+ add_repeating_timer_us(-(MICROS_PER_TICK), IRTimerInterruptHandlerHelper, nullptr, &s50usTimer);
}
void timerDisableReceiveInterrupt() {
cancel_repeating_timer(&s50usTimer);
@@ -1751,23 +1963,60 @@ void timerDisableReceiveInterrupt() {
void timerConfigForReceive() {
// no need for initializing timer at setup()
}
+#define SEND_PWM_BY_TIMER // Disable carrier PWM generation in software and use (restricted) hardware PWM.
# if defined(SEND_PWM_BY_TIMER)
#include "hardware/pwm.h"
+#define USE_RP2040_NATIVE_COMMANDS
+# if defined(USE_RP2040_NATIVE_COMMANDS)
uint sSliceNumberForSendPWM;
uint sChannelNumberForSendPWM;
uint sIROutPuseWidth;
+uint16_t sIROutPuseWidthForHigh; // for setting level to 1
+# else
+uint sFrequency;
+# endif
/*
* If we just disable the PWM, the counter stops and the output stays at the state is currently has
*/
void enableSendPWMByTimer() {
+# if defined(USE_RP2040_NATIVE_COMMANDS)
pwm_set_counter(sSliceNumberForSendPWM, 0);
pwm_set_chan_level(sSliceNumberForSendPWM, sChannelNumberForSendPWM, sIROutPuseWidth);
+# else
+ analogWriteFreq(sFrequency);
+# if defined(IR_SEND_PIN)
+ analogWrite(IR_SEND_PIN, (uint8_t) (IR_SEND_DUTY_CYCLE_PERCENT_FOR_LEVEL_HIGH * 255 / 100)); // Calculate duty as 0-255 value (analogWrite uses 8-bit resolution)
+# else
+ analogWrite(IrSender.sendPin, (uint8_t) (IR_SEND_DUTY_CYCLE_PERCENT_FOR_LEVEL_HIGH * 255 / 100)); // Calculate duty as 0-255 value (analogWrite uses 8-bit resolution)
+# endif
+# endif
}
+
void disableSendPWMByTimer() {
+# if defined(USE_RP2040_NATIVE_COMMANDS)
+# if defined(USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN)
+ pwm_set_chan_level(sSliceNumberForSendPWM, sChannelNumberForSendPWM, sIROutPuseWidthForHigh); // this sets output also to HIGH
+# else
pwm_set_chan_level(sSliceNumberForSendPWM, sChannelNumberForSendPWM, 0); // this sets output also to LOW
+# endif
+# else
+# if defined(IR_SEND_PIN)
+# if defined(USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN)
+ analogWrite(IR_SEND_PIN, 255); // analogWrite(0) disables PWM and sets pin HIGH
+# else
+ analogWrite(IR_SEND_PIN, 0); // analogWrite(0) disables PWM and sets pin LOW
+# endif
+# else
+# if defined(USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN)
+ analogWrite(IrSender.sendPin, 255); // analogWrite(0) disables PWM and sets pin HIGH
+# else
+ analogWrite(IrSender.sendPin, 0); // analogWrite(0) disables PWM and sets pin LOW
+# endif
+# endif
+# endif
}
/*
@@ -1775,25 +2024,37 @@ void disableSendPWMByTimer() {
* Set output pin mode and disable receive interrupt if it uses the same resource
*/
void timerConfigForSend(uint16_t aFrequencyKHz) {
-# if defined(IR_SEND_PIN)
+# if defined(USE_RP2040_NATIVE_COMMANDS)
+# if defined(IR_SEND_PIN)
gpio_set_function(IR_SEND_PIN, GPIO_FUNC_PWM);
// Find out which PWM slice is connected to IR_SEND_PIN
sSliceNumberForSendPWM = pwm_gpio_to_slice_num(IR_SEND_PIN);
sChannelNumberForSendPWM = pwm_gpio_to_channel(IR_SEND_PIN);
-# else
+# else
gpio_set_function(IrSender.sendPin, GPIO_FUNC_PWM);
// Find out which PWM slice is connected to IR_SEND_PIN
sSliceNumberForSendPWM = pwm_gpio_to_slice_num(IrSender.sendPin);
sChannelNumberForSendPWM = pwm_gpio_to_channel(IrSender.sendPin);
+# endif
+# else
+# if defined(IR_SEND_PIN)
+ pinMode(IR_SEND_PIN, OUTPUT); // Set the pin to output mode initially
+# else
+ pinMode(IrSender.sendPin, OUTPUT); // Set the pin to output mode initially
+# endif
# endif
+# if defined(USE_RP2040_NATIVE_COMMANDS)
uint16_t tPWMWrapValue = (clock_get_hz(clk_sys)) / (aFrequencyKHz * 1000); // 3289.473 for 38 kHz @125 MHz clock. We have a 16 bit counter and use system clock (125 MHz)
-
pwm_config tPWMConfig = pwm_get_default_config();
+ sIROutPuseWidthForHigh = tPWMWrapValue;
pwm_config_set_wrap(&tPWMConfig, tPWMWrapValue - 1);
pwm_init(sSliceNumberForSendPWM, &tPWMConfig, false); // we do not want to send now
- sIROutPuseWidth = ((tPWMWrapValue * IR_SEND_DUTY_CYCLE_PERCENT) / 100) - 1; // 985.84 for 38 kHz
+ sIROutPuseWidth = ((tPWMWrapValue * IR_SEND_DUTY_CYCLE_PERCENT_FOR_LEVEL_HIGH) / 100) - 1; // 985.84 for 38 kHz
pwm_set_chan_level(sSliceNumberForSendPWM, sChannelNumberForSendPWM, 0);
pwm_set_enabled(sSliceNumberForSendPWM, true);
+# else
+ sFrequency = aFrequencyKHz * 1000;
+# endif
}
# endif // defined(SEND_PWM_BY_TIMER)
@@ -1957,14 +2218,14 @@ void timerConfigForReceive() {
# if defined(SEND_PWM_BY_TIMER)
# if defined(IR_SEND_PIN)
void enableSendPWMByTimer() {
- analogWrite(IR_SEND_PIN, ((256L * 100) / IR_SEND_DUTY_CYCLE_PERCENT)), ir_out_kHz*1000);
+ analogWrite(IR_SEND_PIN, ((255L * 100) / IR_SEND_DUTY_CYCLE_PERCENT_FOR_LEVEL_HIGH)), ir_out_kHz*1000);
}
void disableSendPWMByTimer() {
analogWrite(IR_SEND_PIN, 0, ir_out_kHz*1000);
}
# else
void enableSendPWMByTimer() {
- analogWrite(IrSender.sendPin, ((256L * 100) / IR_SEND_DUTY_CYCLE_PERCENT), ir_out_kHz * 1000);
+ analogWrite(IrSender.sendPin, ((255L * 100) / IR_SEND_DUTY_CYCLE_PERCENT_FOR_LEVEL_HIGH), ir_out_kHz * 1000);
}
void disableSendPWMByTimer() {
analogWrite(IrSender.sendPin, 0, ir_out_kHz * 1000);
pFad - Phonifier reborn
Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies:
Alternative Proxy
pFad Proxy
pFad v3 Proxy
pFad v4 Proxy