From 1ffaf9422f498a3c965e3805971a322321f0bd22 Mon Sep 17 00:00:00 2001 From: ArminJo Date: Mon, 26 May 2025 18:09:58 +0200 Subject: [PATCH 1/3] Closes #1301 - Backward compatibility bug for printIRResultShort(3 params) --- src/IRReceive.hpp | 5 +++++ src/IRremoteInt.h | 2 ++ 2 files changed, 7 insertions(+) diff --git a/src/IRReceive.hpp b/src/IRReceive.hpp index 1c6ab4fb..bf13923c 100644 --- a/src/IRReceive.hpp +++ b/src/IRReceive.hpp @@ -1428,6 +1428,11 @@ void printActiveIRProtocols(Print *aSerial) { * which in turn may block the proper detection of repeats.* * @return true, if CheckForRecordGapsMicros() has printed a message, i.e. gap < 15ms (RECORD_GAP_MICROS_WARNING_THRESHOLD). */ +bool IRrecv::printIRResultShort(Print *aSerial, bool aPrintRepeatGap, bool aCheckForRecordGapsMicros) { + // DEPRECATED + (void) aPrintRepeatGap; + return printIRResultShort(aSerial, aCheckForRecordGapsMicros); +} bool IRrecv::printIRResultShort(Print *aSerial, bool aCheckForRecordGapsMicros) { // call no class function with same name ::printIRResultShort(aSerial, &decodedIRData); diff --git a/src/IRremoteInt.h b/src/IRremoteInt.h index 8e50048e..17ea1f66 100644 --- a/src/IRremoteInt.h +++ b/src/IRremoteInt.h @@ -261,6 +261,8 @@ class IRrecv { /* * Next 4 functions are also available as non member functions */ + bool printIRResultShort(Print *aSerial, bool aPrintRepeatGap, bool aCheckForRecordGapsMicros) + __attribute__ ((deprecated ("Remove second parameter, it is not supported any more! Third one was true by default."))); bool printIRResultShort(Print *aSerial, bool aCheckForRecordGapsMicros = true); void printDistanceWidthTimingInfo(Print *aSerial, DistanceWidthTimingInfoStruct *aDistanceWidthTimingInfo); void printIRSendUsage(Print *aSerial); From 5f347050f29a40f25b230d9ba58689ac410ae19d Mon Sep 17 00:00:00 2001 From: ArminJo Date: Tue, 27 May 2025 18:13:50 +0200 Subject: [PATCH 2/3] Added USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN --- .github/workflows/LibraryBuild.yml | 39 ++- README.md | 11 +- changelog.md | 2 + examples/AllProtocolsOnLCD/ADCUtils.hpp | 31 +- examples/SendDemo/SendDemo.ino | 4 +- examples/UnitTest/UnitTest.ino | 1 + src/IRSend.hpp | 44 ++- src/IRremote.hpp | 5 +- src/IRremoteInt.h | 2 +- src/ir_BangOlufsen.hpp | 4 +- src/private/IRTimer.hpp | 357 +++++++++++++++++++----- 11 files changed, 379 insertions(+), 121 deletions(-) diff --git a/.github/workflows/LibraryBuild.yml b/.github/workflows/LibraryBuild.yml index 93aee142..d15cf90a 100644 --- a/.github/workflows/LibraryBuild.yml +++ b/.github/workflows/LibraryBuild.yml @@ -51,6 +51,7 @@ jobs: - arduino:avr:uno|USE_NO_SEND_PWM - arduino:avr:uno|SEND_PWM_BY_TIMER - arduino:avr:uno|USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN + - arduino:avr:uno|USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN - arduino:avr:mega:cpu=atmega2560 - arduino:avr:leonardo - arduino:megaavr:nona4809:mode=off @@ -83,18 +84,20 @@ jobs: include: - arduino-boards-fqbn: arduino:avr:uno build-properties: # the flags were put in compiler.cpp.extra_flags + SimpleSender: -DUSE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN ReceiveDemo: -DIR_REMOTE_DISABLE_RECEIVE_COMPLETE_CALLBACK IRremoteExtensionTest: -DRAW_BUFFER_LENGTH=1200 -DIR_SEND_PIN=3 - arduino-boards-fqbn: arduino:avr:uno|DEBUG_TRACE build-properties: # the flags were put in compiler.cpp.extra_flags - IRremoteExtensionTest: -DRAW_BUFFER_LENGTH=750 -DIR_SEND_PIN=3 + IRremoteExtensionTest: -DIR_SEND_PIN=3 TinyReceiver: -DUSE_CALLBACK_FOR_TINY_RECEIVER All: -DEBUG -DTRACE - arduino-boards-fqbn: arduino:avr:uno|USE_NO_SEND_PWM build-properties: # the flags were put in compiler.cpp.extra_flags - IRremoteExtensionTest: -DRAW_BUFFER_LENGTH=750 -DIR_SEND_PIN=3 + SimpleSender: -DUSE_ACTIVE_HIGH_OUTPUT_FOR_NO_SEND_PWM + IRremoteExtensionTest: -DIR_SEND_PIN=3 TinyReceiver: -DUSE_FAST_PROTOCOL TinySender: -DUSE_FAST_PROTOCOL All: -DUSE_NO_SEND_PWM @@ -102,7 +105,8 @@ jobs: - arduino-boards-fqbn: arduino:avr:uno|SEND_PWM_BY_TIMER sketches-exclude: UnitTest,MultipleSendPins build-properties: # the flags were put in compiler.cpp.extra_flags - IRremoteExtensionTest: -DRAW_BUFFER_LENGTH=750 -DIR_SEND_PIN=3 -DSEND_PWM_BY_TIMER + SimpleSender: -DUSE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN + IRremoteExtensionTest: -DIR_SEND_PIN=3 ReceiveDemo: -DDECODE_ONKYO TinyReceiver: -DUSE_ONKYO_PROTOCOL TinySender: -DUSE_ONKYO_PROTOCOL @@ -110,22 +114,29 @@ jobs: - arduino-boards-fqbn: arduino:avr:uno|USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN build-properties: # the flags were put in compiler.cpp.extra_flags - IRremoteExtensionTest: -DRAW_BUFFER_LENGTH=750 -DIR_SEND_PIN=3 -DUSE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN + IRremoteExtensionTest: -DIR_SEND_PIN=3 -DUSE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN SimpleSender: -DSEND_PWM_BY_TIMER TinyReceiver: -DENABLE_NEC2_REPEATS TinySender: -DENABLE_NEC2_REPEATS All: -DUSE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN + - arduino-boards-fqbn: arduino:avr:uno|USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN + build-properties: # the flags were put in compiler.cpp.extra_flags + IRremoteExtensionTest: -DIR_SEND_PIN=3 -DUSE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN + SimpleSender: -DSEND_PWM_BY_TIMER + TinyReceiver: -DENABLE_NEC2_REPEATS + TinySender: -DENABLE_NEC2_REPEATS + All: -DUSE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN + - arduino-boards-fqbn: arduino:avr:mega:cpu=atmega2560 build-properties: # the flags were put in compiler.cpp.extra_flags - IRremoteExtensionTest: -DRAW_BUFFER_LENGTH=750 + IRremoteExtensionTest: -DRAW_BUFFER_LENGTH=1500 SimpleSender: -DSEND_PWM_BY_TIMER TinyReceiver: -DDISABLE_PARITY_CHECKS TinySender: -DDISABLE_PARITY_CHECKS - arduino-boards-fqbn: arduino:avr:leonardo build-properties: # the flags were put in compiler.cpp.extra_flags - IRremoteExtensionTest: -DRAW_BUFFER_LENGTH=750 TinyReceiver: -DNO_LED_FEEDBACK_CODE TinySender: -DNO_LED_FEEDBACK_CODE sketches-exclude: UnitTest # Sketch uses 28764 bytes (100%) of program storage space. Maximum is 28672 bytes @@ -133,37 +144,27 @@ jobs: - arduino-boards-fqbn: arduino:megaavr:nona4809:mode=off sketches-exclude: TinyReceiver,IRDispatcherDemo build-properties: # the flags were put in compiler.cpp.extra_flags - IRremoteExtensionTest: -DRAW_BUFFER_LENGTH=750 SimpleSender: -DSEND_PWM_BY_TIMER TinyReceiver: -DUSE_ONKYO_PROTOCOL -DENABLE_NEC2_REPEATS -DNO_LED_FEEDBACK_CODE -DUSE_CALLBACK_FOR_TINY_RECEIVER TinySender: -DUSE_ONKYO_PROTOCOL -DENABLE_NEC2_REPEATS -DNO_LED_FEEDBACK_CODE - arduino-boards-fqbn: arduino:samd:arduino_zero_native sketches-exclude: TinyReceiver,IRDispatcherDemo - build-properties: # the flags were put in compiler.cpp.extra_flags - IRremoteExtensionTest: -DRAW_BUFFER_LENGTH=750 - arduino-boards-fqbn: arduino:renesas_uno:unor4wifi sketches-exclude: TinyReceiver,IRDispatcherDemo - build-properties: # the flags were put in compiler.cpp.extra_flags - IRremoteExtensionTest: -DRAW_BUFFER_LENGTH=750 - arduino-boards-fqbn: adafruit:samd:adafruit_metro_m4:cache=on,speed=120,opt=small,maxqspi=50,usbstack=arduino,debug=off platform-url: https://adafruit.github.io/arduino-board-index/package_adafruit_index.json sketches-exclude: TinyReceiver,IRDispatcherDemo - build-properties: # the flags were put in compiler.cpp.extra_flags - IRremoteExtensionTest: -DRAW_BUFFER_LENGTH=750 - arduino-boards-fqbn: adafruit:samd:adafruit_itsybitsy_m4 platform-url: https://adafruit.github.io/arduino-board-index/package_adafruit_index.json sketches-exclude: TinyReceiver,IRDispatcherDemo - build-properties: # the flags were put in compiler.cpp.extra_flags - IRremoteExtensionTest: -DRAW_BUFFER_LENGTH=750 - arduino-boards-fqbn: arduino:mbed:nano33ble build-properties: # the flags were put in compiler.cpp.extra_flags - IRremoteExtensionTest: -DRAW_BUFFER_LENGTH=750 SimpleSender: -DSEND_PWM_BY_TIMER # @@ -171,7 +172,6 @@ jobs: # - arduino-boards-fqbn: arduino:mbed_rp2040:pico build-properties: # the flags were put in compiler.cpp.extra_flags - IRremoteExtensionTest: -DRAW_BUFFER_LENGTH=750 SimpleSender: -DSEND_PWM_BY_TIMER - arduino-boards-fqbn: rp2040:rp2040:arduino_nano_connect @@ -179,6 +179,7 @@ jobs: build-properties: # the flags were put in compiler.cpp.extra_flags IRremoteExtensionTest: -DSEND_PWM_BY_TIMER SimpleSender: -DSEND_PWM_BY_TIMER + SendDemo: -DSEND_PWM_BY_TIMER -DUSE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN All: -DRAW_BUFFER_LENGTH=750 # @@ -188,15 +189,11 @@ jobs: arduino-platform: megaTinyCore:megaavr platform-url: http://drazzy.com/package_drazzy.com_index.json sketches-exclude: AllProtocolsOnLCD,UnitTest # UnitTest region `text' overflowed by 997 bytes - build-properties: # the flags were put in compiler.cpp.extra_flags - IRremoteExtensionTest: -DRAW_BUFFER_LENGTH=360 - arduino-boards-fqbn: megaTinyCore:megaavr:atxy7:chip=3217,clock=16internal arduino-platform: megaTinyCore:megaavr platform-url: http://drazzy.com/package_drazzy.com_index.json sketches-exclude: AllProtocolsOnLCD - build-properties: # the flags were put in compiler.cpp.extra_flags - IRremoteExtensionTest: -DRAW_BUFFER_LENGTH=360 # # ATTinyCore diff --git a/README.md b/README.md index ab0b220b..f84ee20f 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,7 @@ Available as [Arduino library "IRremote"](https://www.arduinolibraries.info/libr - [Sending IR codes](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#sending-ir-codes) * [Sending IRDB IR codes](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#sending-irdb-ir-codes) * [Send pin](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#send-pin) + * [Polarity of send pin](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#polarity-of-send-pin) + [List of public IR code databases](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#list-of-public-ir-code-databases) - [Tiny NEC receiver and sender](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#tiny-nec-receiver-and-sender) - [The FAST protocol](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#the-fast-protocol) @@ -568,6 +569,11 @@ On **ESP32** ledc channel 0 is used for generating the IR PWM.
If `IR_SEND_PIN` is specified (as C macro), it reduces program size and improves send timing for AVR. If you want to use a variable to specify send pin e.g. with `setSendPin(uint8_t aSendPinNumber)`, you must disable this `IR_SEND_PIN` macro e.g. with `#undef IR_SEND_PIN`. Then you can change send pin at any time before sending an IR frame. See also [Compile options / macros for this library](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#compile-options--macros-for-this-library). +## Polarity of send pin +By default the polarity is HIGH for active and LOW for inactive or pause. +This corresponds to the connection schema: Pin -> Resistor-> IR-LED -> Ground.
+If you want to use the connection schema: VCC -> IR-LED -> Resistor -> Pin, you must specify `USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN`. + ### List of public IR code databases http://www.harctoolbox.org/IR-resources.html @@ -864,9 +870,10 @@ Modify them by enabling / disabling them, or change the values if applicable. | `IR_SEND_PIN` | disabled | If specified, it reduces program size and improves send timing for AVR. If you want to use a variable to specify send pin e.g. with `setSendPin(uint8_t aSendPinNumber)`, you must not use / disable this macro in your source. | | `SEND_PWM_BY_TIMER` | disabled | Disables carrier PWM generation in software and use hardware PWM (by timer). Has the **advantage of more exact PWM generation**, especially the duty cycle (which is not very relevant for most IR receiver circuits), and the **disadvantage of using a hardware timer**, which in turn is not available for other libraries and to fix the send pin (but not the receive pin) at the [dedicated timer output pin(s)](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#timer-and-pin-usage). Is enabled for ESP32 and RP2040 in all examples, since they support PWM gereration for each pin without using a shared resource (timer). | | `IR_SEND_DUTY_CYCLE_PERCENT` | 30 | Duty cycle of IR send signal. | -| `USE_NO_SEND_PWM` | disabled | Uses no carrier PWM, just simulate an **active low** receiver signal. Used for transferring signal by cable instead of IR. Overrides `SEND_PWM_BY_TIMER` definition. | +| `USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN` | disabled | Reverts the polarity at the send pin. Can be used to connect IR LED between VCC and the send pin. It is like open drain but with electrical active high in its logical inactive state. | | `USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN` | disabled | Uses or simulates open drain output mode at send pin. **Attention, active state of open drain is LOW**, so connect the send LED between positive supply and send pin! | -| `USE_ACTIVE_HIGH_OUTPUT_FOR_SEND_PIN` | disabled | Only if `USE_NO_SEND_PWM` is enabled. Simulate an **active high** receiver signal instead of an active low signal. | +| `USE_NO_SEND_PWM` | disabled | Uses no carrier PWM, just simulate an **active low** receiver signal. Used for transferring signal by cable instead of IR. Overrides `SEND_PWM_BY_TIMER` definition. | +| `USE_ACTIVE_HIGH_OUTPUT_FOR_NO_SEND_PWM` | disabled | Only if `USE_NO_SEND_PWM` is enabled. Simulate an **active high** receiver signal instead of an active low signal. | | `DISABLE_CODE_FOR_RECEIVER` | disabled | Disables static receiver code like receive timer ISR handler and static IRReceiver and irparams data. Saves 450 bytes program memory and 269 bytes RAM if receiving functions are not required. | | `FEEDBACK_LED_IS_ACTIVE_LOW` | disabled | Required on some boards (like my BluePill and my ESP8266 board), where the feedback LED is active low. | | `NO_LED_FEEDBACK_CODE` | disabled | Disables the LED feedback code for send and receive. Saves around 100 bytes program memory for receiving, around 500 bytes for sending and halving the receiver ISR (Interrupt Service Routine) processing time. | diff --git a/changelog.md b/changelog.md index 0ab56fa1..efe12b69 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,8 @@ # Changelog The latest version may not be released! See also the commit log at github: https://github.com/Arduino-IRremote/Arduino-IRremote/commits/master +# 4.4.3 +- Added USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN to make the software aware of reverse external connection. # 4.4.2 - Support for SAMD51 timer3 if timer 5 is not available (Adafruit ItsyBitsy M4). diff --git a/examples/AllProtocolsOnLCD/ADCUtils.hpp b/examples/AllProtocolsOnLCD/ADCUtils.hpp index 57a30a54..989a7e85 100644 --- a/examples/AllProtocolsOnLCD/ADCUtils.hpp +++ b/examples/AllProtocolsOnLCD/ADCUtils.hpp @@ -27,6 +27,7 @@ #include "ADCUtils.h" #if defined(ADC_UTILS_ARE_AVAILABLE) // set in ADCUtils.h, if supported architecture was detected +#define ADC_UTILS_ARE_INCLUDED #if !defined(STR) #define STR_HELPER(x) #x @@ -66,6 +67,11 @@ union WordUnionForADCUtils { #else //#define LOCAL_DEBUG // This enables debug output only for this file #endif +#if defined(INFO) +#define LOCAL_INFO +#else +//#define LOCAL_INFO // This enables debug output only for this file +#endif /* * Persistent storage for VCC value @@ -639,13 +645,13 @@ bool isVCCUSBPowered(Print *aSerial) { } /* + * It checks every 10 seconds for 6 times, and then returns true if the undervoltage condition ( <3.4V ) still applies. * @ return true only once, when VCC_UNDERVOLTAGE_CHECKS_BEFORE_STOP (6) times voltage too low -> shutdown */ bool isVCCUndervoltageMultipleTimes() { /* * Check VCC every VCC_CHECK_PERIOD_MILLIS (10) seconds */ - if (millis() - sLastVCCCheckMillis >= VCC_CHECK_PERIOD_MILLIS) { sLastVCCCheckMillis = millis(); @@ -655,30 +661,32 @@ bool isVCCUndervoltageMultipleTimes() { readVCCVoltageMillivolt(); # endif - if (sVCCTooLowCounter < VCC_UNDERVOLTAGE_CHECKS_BEFORE_STOP) { - /* - * Do not check again if shutdown has happened - */ + /* + * Do not check again if shutdown signaling (sVCCTooLowCounter >= 6) has happened + */ + if (sVCCTooLowCounter < VCC_UNDERVOLTAGE_CHECKS_BEFORE_STOP) { // VCC_UNDERVOLTAGE_CHECKS_BEFORE_STOP = 6 if (sVCCVoltageMillivolt > VCC_UNDERVOLTAGE_THRESHOLD_MILLIVOLT) { sVCCTooLowCounter = 0; // reset counter } else { /* - * Voltage too low, wait VCC_UNDERVOLTAGE_CHECKS_BEFORE_STOP (6) times and then shut down. + * Voltage too low, wait VCC_UNDERVOLTAGE_CHECKS_BEFORE_STOP (6) times and then signal shut down. */ if (sVCCVoltageMillivolt < VCC_EMERGENCY_UNDERVOLTAGE_THRESHOLD_MILLIVOLT) { // emergency shutdown sVCCTooLowCounter = VCC_UNDERVOLTAGE_CHECKS_BEFORE_STOP; -# if defined(INFO) +# if defined(LOCAL_INFO) Serial.println( F( "Voltage < " STR(VCC_EMERGENCY_UNDERVOLTAGE_THRESHOLD_MILLIVOLT) " mV detected -> emergency shutdown")); # endif } else { sVCCTooLowCounter++; -# if defined(INFO) - Serial.print(F("Voltage < " STR(VCC_UNDERVOLTAGE_THRESHOLD_MILLIVOLT) " mV detected: ")); +# if defined(LOCAL_INFO) + Serial.print(sVCCVoltageMillivolt); + Serial.print(F(" mV < " STR(VCC_UNDERVOLTAGE_THRESHOLD_MILLIVOLT) " mV detected: ")); + Serial.print(VCC_UNDERVOLTAGE_CHECKS_BEFORE_STOP - sVCCTooLowCounter); - Serial.println(F(" tries left")); + Serial.println(F(" attempts left")); # endif } if (sVCCTooLowCounter == VCC_UNDERVOLTAGE_CHECKS_BEFORE_STOP) { @@ -820,4 +828,7 @@ float getVCCVoltage() { #if defined(LOCAL_DEBUG) #undef LOCAL_DEBUG #endif +#if defined(LOCAL_INFO) +#undef LOCAL_INFO +#endif #endif // _ADC_UTILS_HPP diff --git a/examples/SendDemo/SendDemo.ino b/examples/SendDemo/SendDemo.ino index 79fd85e2..c04d3ae5 100644 --- a/examples/SendDemo/SendDemo.ino +++ b/examples/SendDemo/SendDemo.ino @@ -37,10 +37,12 @@ #if !defined(ARDUINO_ESP32C3_DEV) // This is due to a bug in RISC-V compiler, which requires unused function sections :-(. #define DISABLE_CODE_FOR_RECEIVER // Disables static receiver code like receive timer ISR handler and static IRReceiver and irparams data. Saves 450 bytes program memory and 269 bytes RAM if receiving functions are not required. #endif +//#define IR_USE_AVR_TIMER1 //#define EXCLUDE_EXOTIC_PROTOCOLS // Saves around 240 bytes program memory if IrSender.write is used +//#define USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN // Reverts the polarity at the send pin. //#define SEND_PWM_BY_TIMER // Disable carrier PWM generation in software and use (restricted) hardware PWM. //#define USE_NO_SEND_PWM // Use no carrier PWM, just simulate an active low receiver signal. Overrides SEND_PWM_BY_TIMER definition -//#define USE_ACTIVE_HIGH_OUTPUT_FOR_SEND_PIN // Simulate an active high receiver signal instead of an active low signal. +//#define USE_ACTIVE_HIGH_OUTPUT_FOR_NO_SEND_PWM // Simulate an active high receiver signal instead of an active low signal. //#define USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN // Use or simulate open drain output mode at send pin. Attention, active state of open drain is LOW, so connect the send LED between positive supply and send pin! #if FLASHEND <= 0x1FFF // For 8k flash or less like ATtiny85 #define NO_LED_FEEDBACK_CODE // Saves 344 bytes program memory diff --git a/examples/UnitTest/UnitTest.ino b/examples/UnitTest/UnitTest.ino index 07af7569..fc51b067 100644 --- a/examples/UnitTest/UnitTest.ino +++ b/examples/UnitTest/UnitTest.ino @@ -88,6 +88,7 @@ //#define ENABLE_BEO_WITHOUT_FRAME_GAP // !!!For successful unit testing we must see the warning at ir_BangOlufsen.hpp:100:2!!! #if defined(DECODE_BEO) #define RECORD_GAP_MICROS 16000 // Force to get the complete frame including the 3. space of 15 ms in the receive buffer +#define SUPPRESS_BEO_RECORD_GAP_MICROS_WARNING // We know, what we do here :-) #define BEO_KHZ 38 // We send and receive Bang&Olufsen with 38 kHz here (instead of 455 kHz). #endif diff --git a/src/IRSend.hpp b/src/IRSend.hpp index a2fafff1..756af9d9 100644 --- a/src/IRSend.hpp +++ b/src/IRSend.hpp @@ -1165,7 +1165,7 @@ void IRsend::mark(uint16_t aMarkMicros) { # if defined(USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN) && !defined(OUTPUT_OPEN_DRAIN) // Here we have no hardware supported Open Drain outputs, so we must mimicking it pinModeFast(sendPin, OUTPUT); // active state for mimicking open drain -# elif defined(USE_ACTIVE_HIGH_OUTPUT_FOR_SEND_PIN) +# elif defined(USE_ACTIVE_HIGH_OUTPUT_FOR_NO_SEND_PWM) || defined(USE_ACTIVE_HIGH_OUTPUT_FOR_SEND_PIN) // USE_ACTIVE_HIGH_OUTPUT_FOR_SEND_PIN is old and deprecated digitalWriteFast(sendPin, HIGH); // Set output to active high. # else digitalWriteFast(sendPin, LOW); // Set output to active low. @@ -1194,12 +1194,16 @@ void IRsend::mark(uint16_t aMarkMicros) { do { // digitalToggleFast(_IR_TIMING_TEST_PIN); /* - * Output the PWM pulse + * Output the PWM pulse - IR LED is active */ noInterrupts(); // do not let interrupts extend the short on period -# if defined(USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN) -# if defined(OUTPUT_OPEN_DRAIN) - digitalWriteFast(sendPin, LOW); // set output with pin mode OUTPUT_OPEN_DRAIN to active low +# if defined(USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN) || defined(USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN) +# if defined(USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN) || defined(OUTPUT_OPEN_DRAIN) + if (__builtin_constant_p(sendPin)) { + digitalWriteFast(sendPin, LOW); // set output to active low. Also applicable for pin with mode OUTPUT_OPEN_DRAIN :-) + } else { + digitalWrite(sendPin, LOW); + } # else pinModeFast(sendPin, OUTPUT); // active state for mimicking open drain # endif @@ -1216,11 +1220,15 @@ void IRsend::mark(uint16_t aMarkMicros) { delayMicroseconds (periodOnTimeMicros); // On time is 8 us for 30% duty cycle. This is normally implemented by a blocking wait. /* - * Output the PWM pause + * Output the PWM pause - IR LED is inactive */ -# if defined(USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN) && !defined(OUTPUT_OPEN_DRAIN) -# if defined(OUTPUT_OPEN_DRAIN) - digitalWriteFast(sendPin, HIGH); // Set output with pin mode OUTPUT_OPEN_DRAIN to inactive high. +# if defined(USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN) || defined(USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN) +# if defined(USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN) || defined(OUTPUT_OPEN_DRAIN) + if (__builtin_constant_p(sendPin)) { + digitalWriteFast(sendPin, HIGH); // Set output to inactive high. Also applicable for pin with mode OUTPUT_OPEN_DRAIN + } else { + digitalWrite(sendPin, HIGH); + } # else pinModeFast(sendPin, INPUT); // to mimic the open drain inactive state # endif @@ -1321,20 +1329,28 @@ void IRsend::IRLedOff() { # if defined(USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN) && !defined(OUTPUT_OPEN_DRAIN) digitalWriteFast(sendPin, LOW); // prepare for all next active states. pinModeFast(sendPin, INPUT);// inactive state for open drain -# elif defined(USE_ACTIVE_HIGH_OUTPUT_FOR_SEND_PIN) +# elif defined(USE_ACTIVE_HIGH_OUTPUT_FOR_NO_SEND_PWM) || defined(USE_ACTIVE_HIGH_OUTPUT_FOR_SEND_PIN) // USE_ACTIVE_HIGH_OUTPUT_FOR_SEND_PIN is old and deprecated digitalWriteFast(sendPin, LOW); // Set output to inactive low. # else digitalWriteFast(sendPin, HIGH); // Set output to inactive high. # endif #else -# if defined(USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN) -# if defined(OUTPUT_OPEN_DRAIN) - digitalWriteFast(sendPin, HIGH); // Set output to inactive high. +# if defined(USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN) || defined(USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN) +# if defined(USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN) || defined(OUTPUT_OPEN_DRAIN) + if (__builtin_constant_p(sendPin)) { + digitalWriteFast(sendPin, HIGH); // set output to inactive high. + } else { + digitalWrite(sendPin, HIGH); + } # else pinModeFast(sendPin, INPUT); // inactive state to mimic open drain # endif # else - digitalWriteFast(sendPin, LOW); + if (__builtin_constant_p(sendPin)) { + digitalWriteFast(sendPin, LOW); // set output to active low. + } else { + digitalWrite(sendPin, LOW); + } # endif #endif diff --git a/src/IRremote.hpp b/src/IRremote.hpp index 0a416a93..942e9aba 100644 --- a/src/IRremote.hpp +++ b/src/IRremote.hpp @@ -46,9 +46,11 @@ * * - RAW_BUFFER_LENGTH Buffer size of raw input buffer. Must be even! 100 is sufficient for *regular* protocols of up to 48 bits. * - IR_SEND_PIN If specified (as constant), reduces program size and improves send timing for AVR. + * - USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN Reverts the polarity at the send pin. + * - USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN Use or simulate open drain output mode at send pin. Attention, active state of open drain is LOW, so connect the send LED between positive supply and send pin! * - SEND_PWM_BY_TIMER Disable carrier PWM generation in software and use (restricted) hardware PWM. * - USE_NO_SEND_PWM Use no carrier PWM, just simulate an **active low** receiver signal. Overrides SEND_PWM_BY_TIMER definition. - * - USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN Use or simulate open drain output mode at send pin. Attention, active state of open drain is LOW, so connect the send LED between positive supply and send pin! + * - USE_ACTIVE_HIGH_OUTPUT_FOR_NO_SEND_PWM Simulate an **active high** receiver signal instead of an active low signal. * - EXCLUDE_EXOTIC_PROTOCOLS If activated, BANG_OLUFSEN, BOSEWAVE, WHYNTER, FAST and LEGO_PF are excluded in decode() and in sending with IrSender.write(). * - EXCLUDE_UNIVERSAL_PROTOCOLS If activated, the universal decoder for pulse distance protocols and decodeHash (special decoder for all protocols) are excluded in decode(). * - DECODE_* Selection of individual protocols to be decoded. See below. @@ -247,6 +249,7 @@ #define IR_SEND_DUTY_CYCLE_PERCENT 30 // 30 saves power and is compatible to the old existing code #endif + /** * microseconds per clock interrupt tick */ diff --git a/src/IRremoteInt.h b/src/IRremoteInt.h index 17ea1f66..76e2953c 100644 --- a/src/IRremoteInt.h +++ b/src/IRremoteInt.h @@ -262,7 +262,7 @@ class IRrecv { * Next 4 functions are also available as non member functions */ bool printIRResultShort(Print *aSerial, bool aPrintRepeatGap, bool aCheckForRecordGapsMicros) - __attribute__ ((deprecated ("Remove second parameter, it is not supported any more! Third one was true by default."))); + __attribute__ ((deprecated ("Remove second parameter, it is not supported any more."))); bool printIRResultShort(Print *aSerial, bool aCheckForRecordGapsMicros = true); void printDistanceWidthTimingInfo(Print *aSerial, DistanceWidthTimingInfoStruct *aDistanceWidthTimingInfo); void printIRSendUsage(Print *aSerial); diff --git a/src/ir_BangOlufsen.hpp b/src/ir_BangOlufsen.hpp index 628a5792..f399741f 100644 --- a/src/ir_BangOlufsen.hpp +++ b/src/ir_BangOlufsen.hpp @@ -96,11 +96,11 @@ //#define SUPPORT_BEO_DATALINK_TIMING_FOR_DECODE // This also supports headers up to 32 bit. Requires additional 150 bytes program memory. #if defined(DECODE_BEO) # if defined(ENABLE_BEO_WITHOUT_FRAME_GAP) -# if RECORD_GAP_MICROS > 15000 +# if RECORD_GAP_MICROS > 15000 && !defined(SUPPRESS_BEO_RECORD_GAP_MICROS_WARNING) #warning If defined ENABLE_BEO_WITHOUT_FRAME_GAP, RECORD_GAP_MICROS must be set to <= 15000 by "#define RECORD_GAP_MICROS 12750" # endif # else -# if RECORD_GAP_MICROS < 16000 +# if RECORD_GAP_MICROS < 16000 && !defined(SUPPRESS_BEO_RECORD_GAP_MICROS_WARNING) #error If not defined ENABLE_BEO_WITHOUT_FRAME_GAP, RECORD_GAP_MICROS must be set to a value >= 16000 by "#define RECORD_GAP_MICROS 16000" # endif # endif diff --git a/src/private/IRTimer.hpp b/src/private/IRTimer.hpp index 2e90423d..be3c50c4 100644 --- a/src/private/IRTimer.hpp +++ b/src/private/IRTimer.hpp @@ -63,6 +63,13 @@ void disableSendPWMByTimer(); // Switch off PWM generation #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 @@ -119,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(). @@ -339,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 @@ -379,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. @@ -395,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; @@ -416,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() @@ -435,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 @@ -473,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() { @@ -493,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 @@ -513,7 +555,7 @@ void timerConfigForReceive() { * 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 // defined(CORE_OC2B_PIN) +# endif // Set IR_SEND_PIN depending on CPU void enableSendPWMByTimer() { TCNT2 = 0; @@ -521,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) } /* @@ -540,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 } @@ -575,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 @@ -587,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; @@ -595,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) } /* @@ -602,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) @@ -636,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__) @@ -650,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) @@ -709,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 @@ -719,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 @@ -740,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) @@ -781,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) } /* @@ -797,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) @@ -840,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) } /* @@ -847,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) @@ -899,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) } /* @@ -912,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 @@ -995,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) } /* @@ -1011,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 } @@ -1064,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) } /* @@ -1084,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 @@ -1444,27 +1598,29 @@ void timerConfigForReceive() { # if !defined(ESP_ARDUINO_VERSION) #define ESP_ARDUINO_VERSION 0x010101 // Version 1.1.1 # endif -# if !defined(ESP_ARDUINO_VERSION_VAL) -#define ESP_ARDUINO_VERSION_VAL(major, minor, patch) ((major << 16) | (minor << 8) | (patch)) -# 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 = 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) #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 >= ESP_ARDUINO_VERSION_VAL(3, 0, 0) +# if ESP_ARDUINO_VERSION >= (3 << 16 + 0 >> 8 + 0) timerStart(s50usTimer); # else timerAlarmEnable(s50usTimer); # 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 */ @@ -1478,7 +1634,8 @@ void timerDisableReceiveInterrupt() { void timerDisableReceiveInterrupt() { if (s50usTimer != nullptr) { -# if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0) +//# 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); @@ -1497,9 +1654,9 @@ 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 == nullptr) { + if (s50usTimer == nullptr) { # if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0) - s50usTimer = timerBegin(1000000); // Only 1 parameter is required. 1000000 corresponds to 1 MHz / 1 uSec. After successful setup the timer will automatically start. + 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 @@ -1513,28 +1670,35 @@ void timerConfigForReceive() { } # 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) # if defined(IR_SEND_PIN) - ledcWrite(IR_SEND_PIN, (IR_SEND_DUTY_CYCLE_PERCENT * 256) / 100); // 3.x API + 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 * 256) / 100); // 3.x API + ledcWrite(IrSender.sendPin, (IR_SEND_DUTY_CYCLE_PERCENT_FOR_LEVEL_HIGH * _IRREMOTE_ESP32_LEDC_RESOLUTION_MAX_PWM_VALUE) / 100); // 3.x API # endif # else // ESP version < 3.0 - ledcWrite(SEND_LEDC_CHANNEL, (IR_SEND_DUTY_CYCLE_PERCENT * 256) / 100); // * 256 since we have 8 bit resolution + 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) # 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 // ESP version < 3.0 @@ -1550,19 +1714,19 @@ void timerConfigForSend(uint16_t aFrequencyKHz) { # if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0) # if defined(IR_SEND_PIN) if(sLastSendPin == 0){ - ledcAttach(IR_SEND_PIN, aFrequencyKHz * 1000, 8); // 3.x API + ledcAttach(IR_SEND_PIN, aFrequencyKHz * 1000, _IRREMOTE_ESP32_LEDC_RESOLUTION); // 3.x API sLastSendPin = IR_SEND_PIN; } # else if(sLastSendPin != 0 && sLastSendPin != IrSender.sendPin){ ledcDetach(IrSender.sendPin); // detach pin before new attaching see #1194 } - ledcAttach(IrSender.sendPin, aFrequencyKHz * 1000, 8); // 3.x API + ledcAttach(IrSender.sendPin, aFrequencyKHz * 1000, _IRREMOTE_ESP32_LEDC_RESOLUTION); // 3.x API sLastSendPin = IrSender.sendPin; # endif # else // ESP version < 3.0 - ledcSetup(SEND_LEDC_CHANNEL, aFrequencyKHz * 1000, 8); // 8 bit PWM resolution + ledcSetup(SEND_LEDC_CHANNEL, aFrequencyKHz * 1000, _IRREMOTE_ESP32_LEDC_RESOLUTION); // 8 bit PWM resolution # if defined(IR_SEND_PIN) ledcAttachPin(IR_SEND_PIN, SEND_LEDC_CHANNEL); // attach pin to channel # else @@ -1736,6 +1900,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); @@ -1744,7 +1909,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 } /* @@ -1752,8 +1921,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) @@ -1789,23 +1959,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 } /* @@ -1813,25 +2020,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) @@ -1995,14 +2214,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); From 4a80fc738508092ae9fa39aebf0b6c92ebfa7132 Mon Sep 17 00:00:00 2001 From: ArminJo Date: Fri, 30 May 2025 01:15:03 +0200 Subject: [PATCH 3/3] Fixed spelling errors inspired by PR 1304 --- Contributing.md | 2 +- README.md | 66 +++++++++++++------- changelog.md | 10 +-- examples/AllProtocolsOnLCD/LiquidCrystal.cpp | 26 +++++++- examples/SendDemo/SendDemo.ino | 4 +- examples/UnitTest/UnitTest.ino | 3 + 6 files changed, 76 insertions(+), 35 deletions(-) diff --git a/Contributing.md b/Contributing.md index 4424f809..7483472d 100644 --- a/Contributing.md +++ b/Contributing.md @@ -18,7 +18,7 @@ Refrain from using overly long or capitalized titles as they are usually annoyin We use the original [C Style by Kerninghan / Ritchie](https://en.wikipedia.org/wiki/Indentation_style#K&R_style) in [variant: 1TBS (OTBS)](https://en.wikipedia.org/wiki/Indentation_style#Variant:_1TBS_(OTBS)).
In short: 4 spaces indentation, no tabs, opening braces on the same line, braces are mandatory on all if/while/do, no hard line length limit.
To beautify your code, you may use the online formatter [here](https://www.freecodeformat.com/c-format.php). -#### Cover **all** occurences of the problem / addition you address with your PR +#### Cover **all** occurrences of the problem / addition you address with your PR Do not forget the documentation like it is done for existing code. Code changes without proper documentation will be rejected! ## Adding new protocols diff --git a/README.md b/README.md index f84ee20f..d10e9998 100644 --- a/README.md +++ b/README.md @@ -157,10 +157,11 @@ This library has been refactored, breaking backward compatibility with the old v ## Converting your 2.x program to the 4.x version Starting with the 3.1 version, **the generation of PWM for sending is done by software**, thus saving the hardware timer and **enabling arbitrary output pins for sending**.
-If you use an (old) Arduino core that does not use the `-flto` flag for compile, you can activate the line `#define SUPPRESS_ERROR_MESSAGE_FOR_BEGIN` in IRRemote.h, if you get false error messages regarding begin() during compilation. +If you use an (old) Arduino core that does not use the `-flto` flag for compile, you can activate the line `#define SUPPRESS_ERROR_MESSAGE_FOR_BEGIN` in IRRemote.h, +if you get false error messages regarding begin() during compilation. - **IRreceiver** and **IRsender** object have been added and can be used without defining them, like the well known Arduino **Serial** object. -- Just remove the line `IRrecv IrReceiver(IR_RECEIVE_PIN);` and/or `IRsend IrSender;` in your program, and replace all occurrences of `IRrecv.` or `irrecv.` with `IrReceiver` and replace all `IRsend` or `irsend` with `IrSender`. +- Just remove the line `IRrecv IrReceiver(IR_RECEIVE_PIN);` and/or `IRsend IrSender;` in your program and replace all occurrences of `IRrecv.` or `irrecv.` with `IrReceiver` and replace all `IRsend` or `irsend` with `IrSender`. - Since the decoded values are now in `IrReceiver.decodedIRData` and not in `results` any more, remove the line `decode_results results` or similar. - Like for the Serial object, call [`IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK)`](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/ReceiveDemo/ReceiveDemo.ino#L106) or `IrReceiver.begin(IR_RECEIVE_PIN, DISABLE_LED_FEEDBACK)` instead of the `IrReceiver.enableIRIn()` or `irrecv.enableIRIn()` in setup().
@@ -172,7 +173,9 @@ If IR_SEND_PIN is not defined (before the line `#include `) you mu - Overflow, Repeat and other flags are now in [`IrReceiver.receivedIRData.flags`](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/src/IRProtocol.h#L90-L101). - Seldom used: `results.rawbuf` and `results.rawlen` must be replaced by `IrReceiver.decodedIRData.rawDataPtr->rawbuf` and `IrReceiver.decodedIRData.rawDataPtr->rawlen`. -- The 5 protocols **NEC, Panasonic, Sony, Samsung and JVC** have been converted to LSB first. Send functions for sending old MSB data were renamed to `sendNECMSB`, `sendSamsungMSB()`, `sendSonyMSB()` and `sendJVCMSB()`. The old `sendSAMSUNG()` and `sendSony()` MSB functions are still available. The old MSB version of `sendPanasonic()` function was deleted, since it had bugs nobody recognized and therfore was assumed to be never used.
+- The 5 protocols **NEC, Panasonic, Sony, Samsung and JVC** have been converted to LSB first. Send functions for sending old MSB data were renamed to `sendNECMSB`, `sendSamsungMSB()`, `sendSonyMSB()` and `sendJVCMSB()`. +The old `sendSAMSUNG()` and `sendSony()` MSB functions are still available. +The old MSB version of `sendPanasonic()` function was deleted, since it had bugs nobody recognized and therefore was assumed to be never used.
For converting MSB codes to LSB see [below](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#how-to-convert-old-msb-first-32-bit-ir-data-codes-to-new-lsb-first-32-bit-ir-data-codes). ### Example @@ -263,7 +266,8 @@ Sending old MSB codes without conversion can be done by using `sendNECMSB()`, `s
## Errors when using the 4.x versions for old tutorials -If you suffer from errors with old tutorial code including `IRremote.h` instead of `IRremote.hpp`, just try to rollback to [Version 2.4.0](https://github.com/Arduino-IRremote/Arduino-IRremote/releases/tag/v2.4.0).
+If you suffer from errors with old tutorial code including `IRremote.h` instead of `IRremote.hpp`, +just try to rollback to [Version 2.4.0](https://github.com/Arduino-IRremote/Arduino-IRremote/releases/tag/v2.4.0).
Most likely your code will run and you will not miss the new features.
@@ -289,7 +293,8 @@ In the Arduino IDE the calls are executed when you click on *Verify* or *Upload* And now our problem with Arduino is:
**How to set [compile options](#compile-options--macros-for-this-library) for all *.cpp files, especially for libraries used?**
-IDE's like [Sloeber](https://github.com/ArminJo/ServoEasing#modifying-compile-options--macros-with-sloeber-ide) or [PlatformIO](https://github.com/ArminJo/ServoEasing#modifying-compile-options--macros-with-platformio) support this by allowing to specify a set of options per project. +IDE's like [Sloeber](https://github.com/ArminJo/ServoEasing#modifying-compile-options--macros-with-sloeber-ide) or +[PlatformIO](https://github.com/ArminJo/ServoEasing#modifying-compile-options--macros-with-platformio) support this by allowing to specify a set of options per project. They add these options at each compiler call e.g. `-DTRACE`. But Arduino lacks this feature. @@ -425,7 +430,8 @@ IrReceiver.printIRResultShort(&Serial); ```c++ IrReceiver.printIRResultRawFormatted(&Serial, true);` ``` -The raw data depends on the internal state of the Arduino timer in relation to the received signal and might therefore be slightly different each time. (resolution problem). The decoded values are the interpreted ones which are tolerant to such slight differences! +The raw data depends on the internal state of the Arduino timer in relation to the received signal and might therefore be slightly different each time. (resolution problem). +The decoded values are the interpreted ones which are tolerant to such slight differences! #### Print how to send the received data: ```c++ @@ -545,7 +551,8 @@ Send with: IrSender.sendLG(0x2, 0x3434, ); You will discover that **the address is a constant** and the commands sometimes are sensibly grouped.
If you are uncertain about the numbers of repeats to use for sending, **3** is a good starting point. If this works, you can check lower values afterwards. -If you have enabled `DECODE_DISTANCE_WIDTH`, the code printed by `printIRSendUsage()` **differs between 8 and 32 bit platforms**, so it is best to run the receiving program on the same platform as the sending program. +If you have enabled `DECODE_DISTANCE_WIDTH`, the code printed by `printIRSendUsage()` **differs between 8 and 32 bit platforms**, +so it is best to run the receiving program on the same platform as the sending program. **All sending functions support the sending of repeats** if sensible. Repeat frames are sent at a fixed period determined by the protocol. e.g. 110 ms from start to start for NEC.
@@ -559,18 +566,23 @@ The codes found in the [Flipper-IRDB database](https://github.com/Lucaslhm/Flipp Protocol matching is NECext -> NECext (or Onkyo), Samsung32 -> Samsung, SIRC20 -> Sony with 20 bits etc. The codes found in the [irdb database](https://github.com/probonopd/irdb/tree/master/codes) specify a **device**, a **subdevice** and a **function**. -Most of the times, *device* and *subdevice* can be taken as upper and lower byte of the **address parameter** and *function* is the **command parameter** for the **new structured functions** with address, command and repeat-count parameters like e.g. `IrSender.sendNEC((device << 8) | subdevice, 0x19, 2)`.
-An **exact mapping** can be found in the [IRP definition files for IR protocols](https://github.com/probonopd/MakeHex/tree/master/protocols). "D" and "S" denotes device and subdevice and "F" denotes the function. +Most of the times, *device* and *subdevice* can be taken as upper and lower byte of the **address parameter** and *function* +is the **command parameter** for the **new structured functions** with address, command and repeat-count parameters +like e.g. `IrSender.sendNEC((device << 8) | subdevice, 0x19, 2)`.
+An **exact mapping** can be found in the [IRP definition files for IR protocols](https://github.com/probonopd/MakeHex/tree/master/protocols). +"D" and "S" denotes device and subdevice and "F" denotes the function. ## Send pin Any pin can be chosen as send pin as long as `IR_SEND_PIN` is **not** defined. This is because the PWM signal is generated by default with software bit banging, since `SEND_PWM_BY_TIMER` is not active.
On **ESP32** ledc channel 0 is used for generating the IR PWM.
-If `IR_SEND_PIN` is specified (as C macro), it reduces program size and improves send timing for AVR. If you want to use a variable to specify send pin e.g. with `setSendPin(uint8_t aSendPinNumber)`, you must disable this `IR_SEND_PIN` macro e.g. with `#undef IR_SEND_PIN`. -Then you can change send pin at any time before sending an IR frame. See also [Compile options / macros for this library](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#compile-options--macros-for-this-library). +If `IR_SEND_PIN` is specified (as C macro), it reduces program size and improves send timing for AVR. +If you want to use a variable to specify send pin e.g. with `setSendPin(uint8_t aSendPinNumber)`, you must disable this `IR_SEND_PIN` macro e.g. with `#undef IR_SEND_PIN`. +Then you can change send pin at any time before sending an IR frame. +See also [Compile options / macros for this library](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#compile-options--macros-for-this-library). ## Polarity of send pin -By default the polarity is HIGH for active and LOW for inactive or pause. +By default the polarity is HIGH for active and LOW for inactive or pause. This corresponds to the connection schema: Pin -> Resistor-> IR-LED -> Ground.
If you want to use the connection schema: VCC -> IR-LED -> Resistor -> Pin, you must specify `USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN`. @@ -687,12 +699,12 @@ See [ReceiveDemo example](https://github.com/Arduino-IRremote/Arduino-IRremote/b ## Receiving sets overflow flag. The flag `IRDATA_FLAGS_WAS_OVERFLOW` is set, if `RAW_BUFFER_LENGTH` is too small for all the marks and spaces of the protocol. This can happen on long protocol frames like the ones from air conditioner. -It also can happen, if `RECORD_GAP_MICROS` is smaller than the real gap between a frame and thr repetition frame, thus interpreting both as one consecutive frame. +It also can happen, if `RECORD_GAP_MICROS` is smaller than the real gap between a frame and the repetition frame, thus interpreting both as one consecutive frame. Best is to dump the timing then, to see which reason holds. ## Problems with Neopixels, FastLed etc. IRremote will not work right when you use **Neopixels** (aka WS2811/WS2812/WS2812B) or other libraries blocking interrupts for a longer time (> 50 µs).
-Whether you use the Adafruit Neopixel lib, or FastLED, interrupts get disabled on many lower end CPUs like the basic Arduinos for longer than 50 µs. +Whether you use the Adafruit Neopixel library, or FastLED, interrupts get disabled on many lower end CPUs like the basic Arduinos for longer than 50 µs. In turn, this stops the IR interrupt handler from running when it needs to. See also this [video](https://www.youtube.com/watch?v=62-nEJtm070). One **workaround** is to wait for the IR receiver to be idle before you send the Neopixel data with `if (IrReceiver.isIdle()) { strip.show();}`.
@@ -725,7 +737,8 @@ On my Arduino Nanos, I always use a 100 Ω series resistor and one IR LED :gr ## Minimal CPU clock frequency For receiving, the **minimal CPU clock frequency is 4 MHz**, since the 50 µs timer ISR (Interrupt Service Routine) takes around 12 µs on a 16 MHz ATmega.
The TinyReceiver, which requires no polling, runs with 1 MHz.
-For sending, the **default software generated PWM has problems on AVR running with 8 MHz**. The PWM frequency is around 30 instead of 38 kHz and RC6 is not reliable. You can switch to timer PWM generation by `#define SEND_PWM_BY_TIMER`. +For sending, the **default software generated PWM has problems on AVR running with 8 MHz**. The PWM frequency is around 30 instead of 38 kHz and RC6 is not reliable. +You can switch to timer PWM generation by `#define SEND_PWM_BY_TIMER`. ## Bang & Olufsen protocol The Bang & Olufsen protocol decoder is not enabled by default, i.e if no protocol is enabled explicitly by #define `DECODE_`. It must always be enabled explicitly by `#define DECODE_BEO`. @@ -739,7 +752,8 @@ The examples are available at File > Examples > Examples from Custom Libraries / See also [DroneBot Workshop SimpleReceiver](https://dronebotworkshop.com/ir-remotes/#SimpleReceiver_Example_Code) and [SimpleSender](https://dronebotworkshop.com/ir-remotes/#SimpleSender_Example_Code). #### SimpleReceiver + SimpleSender -The **[SimpleReceiver](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/SimpleReceiver/SimpleReceiver.ino)** and **[SimpleSender](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/SimpleSender/SimpleSender.ino)** examples are a good starting point. +The **[SimpleReceiver](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/SimpleReceiver/SimpleReceiver.ino)** +and **[SimpleSender](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/SimpleSender/SimpleSender.ino)** examples are a good starting point. A simple example can be tested online with [WOKWI](https://wokwi.com/projects/338611596994544210). #### SimpleReceiverForHashCodes @@ -764,7 +778,8 @@ If the protocol is not NEC and code size matters, look at this [example](https:/ #### ReceiveDemo + AllProtocolsOnLCD [ReceiveDemo](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/ReceiveDemo/ReceiveDemo.ino) receives all protocols and **generates a beep with the Arduino tone() function** on each packet received.
Long press of one IR button (receiving of multiple repeats for one command) is detected.
-[AllProtocolsOnLCD](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/AllProtocolsOnLCD/AllProtocolsOnLCD.ino) additionally **displays the short result on a 1602 LCD**. The LCD can be connected parallel or serial (I2C).
+[AllProtocolsOnLCD](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/AllProtocolsOnLCD/AllProtocolsOnLCD.ino) additionally **displays the short result on a 1602 LCD**. +The LCD can be connected parallel or serial (I2C).
By connecting debug pin to ground, you can force printing of the raw values for each frame. The pin number of the debug pin is printed during setup, because it depends on board and LCD connection type.
This example also serves as an **example how to use IRremote and tone() together**. @@ -782,7 +797,8 @@ Demonstrates sending IR codes toggling between 2 **different send pins**. Demonstrates **receiving while sending**. #### ReceiveAndSend -Record and **play back last received IR signal** at button press. IR frames of known protocols are sent by the appropriate protocol encoder. `UNKNOWN` protocol frames are stored as raw data and sent with `sendRaw()`. +Record and **play back last received IR signal** at button press. IR frames of known protocols are sent by the appropriate protocol encoder. +`UNKNOWN` protocol frames are stored as raw data and sent with `sendRaw()`. #### ReceiveAndSendDistanceWidth Try to decode each IR frame with the *universal* **DistanceWidth decoder**, store the data and send it on button press with `sendPulseDistanceWidthFromArray()`.
@@ -868,9 +884,9 @@ Modify them by enabling / disabling them, or change the values if applicable. | `DISTANCE_WIDTH_DECODER_DURATION_ARRAY_SIZE` | 50 if RAM <= 2k, else 200 | A value of 200 allows to decode mark or space durations up to 10 ms. | | `IR_INPUT_IS_ACTIVE_HIGH` | disabled | Enable it if you use a RF receiver, which has an active HIGH output signal. | | `IR_SEND_PIN` | disabled | If specified, it reduces program size and improves send timing for AVR. If you want to use a variable to specify send pin e.g. with `setSendPin(uint8_t aSendPinNumber)`, you must not use / disable this macro in your source. | -| `SEND_PWM_BY_TIMER` | disabled | Disables carrier PWM generation in software and use hardware PWM (by timer). Has the **advantage of more exact PWM generation**, especially the duty cycle (which is not very relevant for most IR receiver circuits), and the **disadvantage of using a hardware timer**, which in turn is not available for other libraries and to fix the send pin (but not the receive pin) at the [dedicated timer output pin(s)](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#timer-and-pin-usage). Is enabled for ESP32 and RP2040 in all examples, since they support PWM gereration for each pin without using a shared resource (timer). | +| `SEND_PWM_BY_TIMER` | disabled | Disables carrier PWM generation in software and use hardware PWM (by timer). Has the **advantage of more exact PWM generation**, especially the duty cycle (which is not very relevant for most IR receiver circuits), and the **disadvantage of using a hardware timer**, which in turn is not available for other libraries and to fix the send pin (but not the receive pin) at the [dedicated timer output pin(s)](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#timer-and-pin-usage). Is enabled for ESP32 and RP2040 in all examples, since they support PWM generation for each pin without using a shared resource (timer). | | `IR_SEND_DUTY_CYCLE_PERCENT` | 30 | Duty cycle of IR send signal. | -| `USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN` | disabled | Reverts the polarity at the send pin. Can be used to connect IR LED between VCC and the send pin. It is like open drain but with electrical active high in its logical inactive state. | +| `USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN` | disabled | Reverses the polarity at the send pin. Can be used to connect IR LED between VCC and the send pin. It is like open drain but with electrical active high in its logical inactive state. | | `USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN` | disabled | Uses or simulates open drain output mode at send pin. **Attention, active state of open drain is LOW**, so connect the send LED between positive supply and send pin! | | `USE_NO_SEND_PWM` | disabled | Uses no carrier PWM, just simulate an **active low** receiver signal. Used for transferring signal by cable instead of IR. Overrides `SEND_PWM_BY_TIMER` definition. | | `USE_ACTIVE_HIGH_OUTPUT_FOR_NO_SEND_PWM` | disabled | Only if `USE_NO_SEND_PWM` is enabled. Simulate an **active high** receiver signal instead of an active low signal. | @@ -889,10 +905,10 @@ These next macros for **TinyIRReceiver** must be defined in your program before |-|-:|-| | `IR_RECEIVE_PIN` | 2 | The pin number for TinyIRReceiver IR input, which gets compiled in. Not used in IRremote. | | `IR_FEEDBACK_LED_PIN` | `LED_BUILTIN` | The pin number for TinyIRReceiver feedback LED, which gets compiled in. | -| `NO_LED_FEEDBACK_CODE` | disabled | Disables the feedback LED codefor send and receive. Saves 14 bytes program memory. | +| `NO_LED_FEEDBACK_CODE` | disabled | Disables the feedback LED code for send and receive. Saves 14 bytes program memory. | | `NO_LED_RECEIVE_FEEDBACK_CODE` | disabled | Disables the LED feedback code for receive. | | `NO_LED_SEND_FEEDBACK_CODE` | disabled | Disables the LED feedback code for send. | -| `DISABLE_PARITY_CHECKS` | disabled | Disables the addres and command parity checks. Saves 48 bytes program memory. | +| `DISABLE_PARITY_CHECKS` | disabled | Disables the address and command parity checks. Saves 48 bytes program memory. | | `USE_EXTENDED_NEC_PROTOCOL` | disabled | Like NEC, but take the 16 bit address as one 16 bit value and not as 8 bit normal and 8 bit inverted value. | | `USE_ONKYO_PROTOCOL` | disabled | Like NEC, but take the 16 bit address and command each as one 16 bit value and not as 8 bit normal and 8 bit inverted value. | | `USE_FAST_PROTOCOL` | disabled | Use FAST protocol (no address and 16 bit data, interpreted as 8 bit command and 8 bit inverted command) instead of NEC. | @@ -1055,7 +1071,7 @@ This works on AVR boards like Uno because each call to` tone()` completely initi If you define `SEND_PWM_BY_TIMER`, the send PWM signal is forced to be generated by a hardware timer on most platforms.
By default, the same timer as for the receiver is used.
Since each hardware timer has its dedicated output pin(s), you must change timer or timer sub-specifications to change PWM output pin. See [private/IRTimer.hpp](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/src/private/IRTimer.hpp)
-**Exeptions** are currently [ESP32, ARDUINO_ARCH_RP2040, PARTICLE and ARDUINO_ARCH_MBED](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/SimpleSender/PinDefinitionsAndMore.h#L334), where **PWM generation does not require a timer**. +**Exceptions** are currently [ESP32, ARDUINO_ARCH_RP2040, PARTICLE and ARDUINO_ARCH_MBED](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/SimpleSender/PinDefinitionsAndMore.h#L334),where **PWM generation does not require a timer**. ## Why do we use 30% duty cycle for sending We [do it](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/src/IRSend.hpp#L1192) according to the statement in the [Vishay datasheet](https://www.vishay.com/docs/80069/circuit.pdf): @@ -1070,7 +1086,9 @@ Due to automatic gain control and other bias effects, high intensity of the 38 k The IR signal is sampled at a **50 µs interval**. For a constant 525 µs pulse or pause we therefore get 10 or 11 samples, each with 50% probability.
And believe me, if you send a 525 µs signal, your receiver will output something between around 400 and 700 µs!
Therefore **we decode by default with a +/- 25% margin** using the formulas [here](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/src/IRremoteInt.h#L376-L399).
-E.g. for the NEC protocol with its 560 µs unit length, we have TICKS_LOW = 8.358 and TICKS_HIGH = 15.0. This means, we accept any value between 8 ticks / 400 µs and 15 ticks / 750 µs (inclusive) as a mark or as a zero space. For a one space we have TICKS_LOW = 25.07 and TICKS_HIGH = 45.0.
+E.g. for the NEC protocol with its 560 µs unit length, we have TICKS_LOW = 8.358 and TICKS_HIGH = 15.0. +This means, we accept any value between 8 ticks / 400 µs and 15 ticks / 750 µs (inclusive) as a mark or as a zero space. +For a one space we have TICKS_LOW = 25.07 and TICKS_HIGH = 45.0.
And since the receivers generated marks are longer or shorter than the spaces, we have introduced the [`MARK_EXCESS_MICROS`](https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#compile-options--macros-for-this-library) macro to compensate for this receiver (and signal strength as well as ambient light dependent :disappointed: ) specific deviation.
diff --git a/changelog.md b/changelog.md index efe12b69..bfed8d0e 100644 --- a/changelog.md +++ b/changelog.md @@ -69,14 +69,14 @@ See also the commit log at github: https://github.com/Arduino-IRremote/Arduino-I - Usage of ATTinyCore pin numbering scheme e.g. PIN_PB2. - Added ARDUINO_ARCH_NRF52 to support Seeed XIAO nRF52840 Sense. - First untested support of Uno R4. -- Extraced version macros to IRVersion.h. +- Extracted version macros to IRVersion.h. ## 4.1.2 - Workaround for ESP32 RTOS delay() timing bug influencing the mark() function. ## 4.1.1 - SAMD51 use timer3 if timer5 not available. -- Disabled #define LOCAL_DEBUG in IRReceive.hpp, which was accidently enabled at 4.1.0. +- Disabled #define LOCAL_DEBUG in IRReceive.hpp, which was accidentally enabled at 4.1.0. ## 4.1.0 - Fixed bug in printing durations > 64535 in printIRResultRawFormatted(). @@ -147,7 +147,7 @@ See also the commit log at github: https://github.com/Arduino-IRremote/Arduino-I - Improved pin mapping for TinyReceiver. ## 3.7.1 -- SendRaw now supports bufferlenght > 255. +- SendRaw now supports buffer length > 255. - Improved DistanceProtocol decoder output. - Fixed ESP32 send bug for 2.x ESP32 cores. @@ -382,7 +382,7 @@ Changes from #268 by adamlhumphreys - Fixed #110 Mess - Created Gitter Room - Added Gitter Badge -- Standardised Code Base +- Standardized Code Base - Clean Debug Output - Optimized Send Loops - Modularized Design @@ -410,4 +410,4 @@ Changes from #268 by adamlhumphreys - Broke Teensy 3 / 3.1 Support ### Not Working -- Teensy 3 / 3.1 Support is in Development +- Teensy 3 / 3.1 Support is in Development \ No newline at end of file diff --git a/examples/AllProtocolsOnLCD/LiquidCrystal.cpp b/examples/AllProtocolsOnLCD/LiquidCrystal.cpp index 5dcef083..8a6e7fa2 100644 --- a/examples/AllProtocolsOnLCD/LiquidCrystal.cpp +++ b/examples/AllProtocolsOnLCD/LiquidCrystal.cpp @@ -5,6 +5,20 @@ #include #include "Arduino.h" +#if defined(__AVR__) +/* + * The datasheet says: a command need > 37us to settle. + * Use a delay of 2 us instead of 100 us after each command, + * because the overhead of this library seems to be using the other 35 us. + * At least it works perfectly for all my LCD's connected to Uno, Nano etc. + * and it saves a lot of time in realtime applications using LCD as display, + * like https://github.com/ArminJo/Arduino-DTSU666H_PowerMeter + */ +# if !defined(DO_NOT_USE_FAST_LCD_TIMING) +#define USE_FAST_LCD_TIMING +# endif +#endif + // When the display powers up, it is configured as follows: // // 1. Display clear @@ -21,7 +35,7 @@ // S = 0; No shift // // Note, however, that resetting the Arduino doesn't reset the LCD, so we -// can't assume that its in that state when a sketch starts (and the +// can't assume that it is in that state when a sketch starts (and the // LiquidCrystal constructor is called). LiquidCrystal::LiquidCrystal(uint8_t rs, uint8_t rw, uint8_t enable, @@ -72,7 +86,7 @@ void LiquidCrystal::init(uint8_t fourbitmode, uint8_t rs, uint8_t rw, uint8_t en else _displayfunction = LCD_8BITMODE | LCD_1LINE | LCD_5x8DOTS; - begin(16, 1); + begin(16, 1); // initializing for a 1601 LCD, but the user must call begin() with the right values at startup. Outcommenting increases program size } void LiquidCrystal::begin(uint8_t cols, uint8_t lines, uint8_t dotsize) { @@ -262,12 +276,14 @@ void LiquidCrystal::noAutoscroll(void) { // Allows us to fill the first 8 CGRAM locations // with custom characters +// This also sets cursor to 0.0 void LiquidCrystal::createChar(uint8_t location, uint8_t charmap[]) { location &= 0x7; // we only have 8 locations 0-7 command(LCD_SETCGRAMADDR | (location << 3)); for (int i=0; i<8; i++) { write(charmap[i]); } + command(LCD_SETDDRAMADDR); // set cursor to 0.0, this avoids overwriting CGRAM by next write() command. } /*********** mid level commands, for sending data/cmds */ @@ -278,7 +294,7 @@ inline void LiquidCrystal::command(uint8_t value) { inline size_t LiquidCrystal::write(uint8_t value) { send(value, HIGH); - return 1; // assume sucess + return 1; // assume success } /************ low level data pushing commands **********/ @@ -306,7 +322,11 @@ void LiquidCrystal::pulseEnable(void) { digitalWrite(_enable_pin, HIGH); delayMicroseconds(1); // enable pulse must be >450ns digitalWrite(_enable_pin, LOW); +#if defined(USE_FAST_LCD_TIMING) + delayMicroseconds(2); // commands need > 37us to settle +#else delayMicroseconds(100); // commands need > 37us to settle +#endif } void LiquidCrystal::write4bits(uint8_t value) { diff --git a/examples/SendDemo/SendDemo.ino b/examples/SendDemo/SendDemo.ino index c04d3ae5..8aa9e0d6 100644 --- a/examples/SendDemo/SendDemo.ino +++ b/examples/SendDemo/SendDemo.ino @@ -39,11 +39,11 @@ #endif //#define IR_USE_AVR_TIMER1 //#define EXCLUDE_EXOTIC_PROTOCOLS // Saves around 240 bytes program memory if IrSender.write is used -//#define USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN // Reverts the polarity at the send pin. +//#define USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN // Reverses the polarity at the send pin. +//#define USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN // Use or simulate open drain output mode at send pin. Attention, active state of open drain is LOW, so connect the send LED between positive supply and send pin! //#define SEND_PWM_BY_TIMER // Disable carrier PWM generation in software and use (restricted) hardware PWM. //#define USE_NO_SEND_PWM // Use no carrier PWM, just simulate an active low receiver signal. Overrides SEND_PWM_BY_TIMER definition //#define USE_ACTIVE_HIGH_OUTPUT_FOR_NO_SEND_PWM // Simulate an active high receiver signal instead of an active low signal. -//#define USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN // Use or simulate open drain output mode at send pin. Attention, active state of open drain is LOW, so connect the send LED between positive supply and send pin! #if FLASHEND <= 0x1FFF // For 8k flash or less like ATtiny85 #define NO_LED_FEEDBACK_CODE // Saves 344 bytes program memory #endif diff --git a/examples/UnitTest/UnitTest.ino b/examples/UnitTest/UnitTest.ino index fc51b067..a6ca7fcf 100644 --- a/examples/UnitTest/UnitTest.ino +++ b/examples/UnitTest/UnitTest.ino @@ -46,8 +46,11 @@ //#define EXCLUDE_UNIVERSAL_PROTOCOLS // Saves up to 1000 bytes program memory. //#define EXCLUDE_EXOTIC_PROTOCOLS // Saves around 240 bytes program memory if IrSender.write is used +//#define USE_ACTIVE_LOW_OUTPUT_FOR_SEND_PIN // Reverses the polarity at the send pin. +//#define USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN // Use or simulate open drain output mode at send pin. Attention, active state of open drain is LOW, so connect the send LED between positive supply and send pin! //#define SEND_PWM_BY_TIMER // Disable carrier PWM generation in software and use (restricted) hardware PWM. //#define USE_NO_SEND_PWM // Use no carrier PWM, just simulate an active low receiver signal. Overrides SEND_PWM_BY_TIMER definition +//#define USE_ACTIVE_HIGH_OUTPUT_FOR_NO_SEND_PWM // Simulate an active high receiver signal instead of an active low signal. #if FLASHEND <= 0x7FFF // For 32k flash or less, like ATmega328 #define NO_LED_FEEDBACK_CODE // Saves 344 bytes program memory #endif