Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions src/IRsend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
#endif
#include "IRtimer.h"

#if defined(ESP32) && !defined(UNIT_TEST)
static portMUX_TYPE timingMux = portMUX_INITIALIZER_UNLOCKED;
#endif

/// Constructor for an IRsend object.
/// @param[in] IRsendPin Which GPIO pin to use when sending an IR command.
/// @param[in] inverted Optional flag to invert the output. (default = false)
Expand Down Expand Up @@ -49,6 +53,19 @@ void IRsend::begin() {
ledOff(); // Ensure the LED is in a known safe state when we start.
}

void IRsend::beginCritical() {
#if defined(ESP32) && !defined(UNIT_TEST)
portENTER_CRITICAL(&timingMux);
#endif
}

void IRsend::endCritical() {
#if defined(ESP32) && !defined(UNIT_TEST)
portEXIT_CRITICAL(&timingMux);
vTaskDelay(1); // Yield to the OS.
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are the implications & practicalities of NOT calling vTaskDelay(1) here?

I'd much prefer to have the critical calls moved inside the mark() method so it works seemlessly for all protocols & uses.
For space()s, we might be able to use the critical calls only when we are not making a delay() call. i.e. very small spaces. Thus it should yield for longer gaps when the timing doesn't matter as much.

Has there been any analysis of where abouts in the message the interrupts are causing the message to be corrupted/not understood? My guess is in the bit-banged PWM mark() messages and short space(), so if we effectively yield only for very long space, we could move the critical calls deeper inside, yield the CPU earlier with less impact & cleaner implementation.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The vTaskDelay call is there to allow the OS task scheduler to assign CPU time for other tasks like the idle task. The delay does not implement a busy wait loop, but rather tells the scheduler to suspend the task for x milliseconds allowing other tasks to run. This is important for the idle loop that, among other things, handles the watchdog timer "kick" preventing the CPU from doing a hard reset. Not having it there could cause the task to enter a new critical section right away and possibly exhausting the scheduler. Critical section disables interrupts and prevent the OS task scheduler from doing its job and therefor should be used with care. I did try to limit the critical section to the senddata functions but it did not work. I am guessing the entire command section including preamp, header, data and footer is timing critical? The reason I kept the critical section within the repeat loop is to allow other tasks to run between each command, since delays between commands are tolerated. I will try to do some more testing and see if I can get the critical section closer to the core transmit logic. Hang on and I will get back with the results.....

#endif
}

/// Turn off the IR LED.
void IRsend::ledOff() {
#ifndef UNIT_TEST
Expand Down Expand Up @@ -362,6 +379,7 @@ void IRsend::sendGeneric(const uint16_t headermark, const uint32_t headerspace,

// We always send a message, even for repeat=0, hence '<= repeat'.
for (uint16_t r = 0; r <= repeat; r++) {
beginCritical();
usecs.reset();

// Header
Expand All @@ -379,6 +397,8 @@ void IRsend::sendGeneric(const uint16_t headermark, const uint32_t headerspace,
space(gap);
else
space(std::max(gap, mesgtime - elapsed));

endCritical();
}
}

Expand Down Expand Up @@ -419,6 +439,7 @@ void IRsend::sendGeneric(const uint16_t headermark, const uint32_t headerspace,
enableIROut(frequency, dutycycle);
// We always send a message, even for repeat=0, hence '<= repeat'.
for (uint16_t r = 0; r <= repeat; r++) {
beginCritical();
// Header
if (headermark) mark(headermark);
if (headerspace) space(headerspace);
Expand All @@ -431,6 +452,7 @@ void IRsend::sendGeneric(const uint16_t headermark, const uint32_t headerspace,
// Footer
if (footermark) mark(footermark);
space(gap);
endCritical();
}
}

Expand Down Expand Up @@ -518,6 +540,7 @@ void IRsend::sendManchester(const uint16_t headermark,

// We always send a message, even for repeat=0, hence '<= repeat'.
for (uint16_t r = 0; r <= repeat; r++) {
beginCritical();
// Header
if (headermark) mark(headermark);
if (headerspace) space(headerspace);
Expand All @@ -526,6 +549,7 @@ void IRsend::sendManchester(const uint16_t headermark,
// Footer
if (footermark) mark(footermark);
if (gap) space(gap);
endCritical();
}
}

Expand All @@ -542,6 +566,7 @@ void IRsend::sendRaw(const uint16_t buf[], const uint16_t len,
const uint16_t hz) {
// Set IR carrier frequency
enableIROut(hz);
beginCritical();
for (uint16_t i = 0; i < len; i++) {
if (i & 1) { // Odd bit.
space(buf[i]);
Expand All @@ -550,6 +575,7 @@ void IRsend::sendRaw(const uint16_t buf[], const uint16_t len,
}
}
ledOff(); // We potentially have ended with a mark(), so turn of the LED.
endCritical();
}
#endif // SEND_RAW

Expand Down
2 changes: 2 additions & 0 deletions src/IRsend.h
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,8 @@ class IRsend {
const uint8_t *dataptr, const uint16_t nbytes,
const uint16_t frequency, const bool MSBfirst,
const uint16_t repeat, const uint8_t dutycycle);
void beginCritical();
void endCritical();
static uint16_t minRepeats(const decode_type_t protocol);
static uint16_t defaultBits(const decode_type_t protocol);
bool send(const decode_type_t type, const uint64_t data,
Expand Down
2 changes: 2 additions & 0 deletions src/ir_Coolix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ void IRsend::sendCOOLIX(uint64_t data, uint16_t nbits, uint16_t repeat) {
enableIROut(38);

for (uint16_t r = 0; r <= repeat; r++) {
beginCritical();
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See other comment. It would be great to remove this stuff if we can. i.e. Make it hidden and self-applying.
Same for the other protocols changed etc.

// Header
mark(kCoolixHdrMark);
space(kCoolixHdrSpace);
Expand All @@ -75,6 +76,7 @@ void IRsend::sendCOOLIX(uint64_t data, uint16_t nbits, uint16_t repeat) {
// Footer
mark(kCoolixBitMark);
space(kCoolixMinGap); // Pause before repeating
endCritical();
}
space(kDefaultMessageGap);
}
Expand Down
2 changes: 2 additions & 0 deletions src/ir_Goodweather.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ void IRsend::sendGoodweather(const uint64_t data, const uint16_t nbits,
enableIROut(38);

for (uint16_t r = 0; r <= repeat; r++) {
beginCritical();
// Header
mark(kGoodweatherHdrMark);
space(kGoodweatherHdrSpace);
Expand All @@ -54,6 +55,7 @@ void IRsend::sendGoodweather(const uint64_t data, const uint16_t nbits,
space(kGoodweatherHdrSpace);
mark(kGoodweatherBitMark);
space(kDefaultMessageGap);
endCritical();
}
}
#endif // SEND_GOODWEATHER
Expand Down
2 changes: 2 additions & 0 deletions src/ir_Gree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ void IRsend::sendGree(const uint64_t data, const uint16_t nbits,
enableIROut(38);

for (uint16_t r = 0; r <= repeat; r++) {
beginCritical();
// Header
mark(kGreeHdrMark);
space(kGreeHdrSpace);
Expand All @@ -100,6 +101,7 @@ void IRsend::sendGree(const uint64_t data, const uint16_t nbits,
// Footer
mark(kGreeBitMark);
space(kGreeMsgSpace);
endCritical();
}
}
#endif // SEND_GREE
Expand Down
2 changes: 2 additions & 0 deletions src/ir_Midea.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ void IRsend::sendMidea(uint64_t data, uint16_t nbits, uint16_t repeat) {
enableIROut(38);

for (uint16_t r = 0; r <= repeat; r++) {
beginCritical();
// The protocol sends the message, then follows up with an entirely
// inverted payload.
for (size_t inner_loop = 0; inner_loop < 2; inner_loop++) {
Expand All @@ -83,6 +84,7 @@ void IRsend::sendMidea(uint64_t data, uint16_t nbits, uint16_t repeat) {
data = ~data;
}
space(kDefaultMessageGap);
endCritical();
}
}
#endif // SEND_MIDEA
Expand Down
2 changes: 2 additions & 0 deletions src/ir_Transcold.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ void IRsend::sendTranscold(uint64_t data, uint16_t nbits, uint16_t repeat) {
// Set IR carrier frequency
enableIROut(38);
for (uint16_t r = 0; r <= repeat; r++) {
beginCritical();
// Header
mark(kTranscoldHdrMark);
space(kTranscoldHdrSpace);
Expand All @@ -63,6 +64,7 @@ void IRsend::sendTranscold(uint64_t data, uint16_t nbits, uint16_t repeat) {
space(kTranscoldHdrSpace);
mark(kTranscoldBitMark);
space(kDefaultMessageGap);
endCritical();
}
}
#endif // SEND_TRANSCOLD
Expand Down
Loading