Improving mark function

This commit is contained in:
Armin 2021-03-22 14:32:09 +01:00
parent 02ee78376f
commit 0cfbbcf4c4
12 changed files with 42 additions and 31 deletions

View File

@ -162,13 +162,11 @@ We are open to suggestions for adding support to new boards, however we highly r
# Timer and pin usage
The receiver sample interval is generated by a timer. On many boards this must be a hardware timer, on some a software timer is available and used.<br/>
Every pin can be used for receiving.<br/>
The send PWM signal is by default generated by software. **Therefore every pin can be used for sending**.
The send PWM signal is by default generated by software. **Therefore every pin can be used for sending**. The PWM pulse length is guaranteed to be constant by using `delayMicroseconds()`. Take care not to generate interrupts during sending with software generated PWM, otherwise you will get jitter in the generated PWM. E.g. wait for a former `Serial.print()` statement to be finished by `Serial.flush()`. Since the Arduino `micros()` function has a resolution of 4 us at 16 MHz, we always see a small jitter in the signal, which seems to be OK for the receivers.
| Software generated PWM showing discontinuities in the Arduino core micros() function | Zoom into the discontinuity. The space is once too short but compensated at the next one |
| Software generated PWM showing small jitter because of the limited resolution of 4 us of the Arduino core `micros()` function for an ATmega328 | Detail (ATmega328 generated) showing 33% Duty cycle |
|-|-|
| ![Software PWM](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/pictures/IR_PWM_by_software.png) | ![Software PWM detail](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/pictures/IR_PWM_by_software_zoom.png) |
| Software generated PWM (ATmega328) with 33% Duty cycle | |
| ![Software PWM detail](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/pictures/IR_PWM_by_software_detail.png) | |
| ![Software PWM](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/pictures/IR_PWM_by_software_jitter.png) | ![Software PWM detail](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/pictures/IR_PWM_by_software_detail.png) |
## Hardware-PWM signal generation for sending
If you define `SEND_PWM_BY_TIMER`, the send PWM signal is generated by a hardware timer. The same timer as for the receiver is used.

View File

@ -17,6 +17,7 @@ The latest version may not be released!
- Added ATtiny88 support.
- Use LED_BUILTIN instead of FEEDBACK_LED FeedbackLEDPin is 0.
- Use F_CPU instead of SYSCLOCK.
- Removed SENDPIN_ON and SENDPIN_OFF macros.
## 3.0.2
- Bug fix for USE_OLD_DECODE.

View File

@ -58,7 +58,7 @@ void setup() {
Serial.print(F("Ready to send IR signals at pin "));
Serial.println(IR_SEND_PIN);
#if defined(USE_SOFT_SEND_PWM) && !defined(ESP32) // for esp32 we use PWM generation by hw_timer_t for each pin
#if defined(USE_SOFT_SEND_PWM) && !defined(ESP32) // for esp32 we use PWM generation by hw_timer_t for each pin
/*
* Print internal signal generation info
*/
@ -100,10 +100,12 @@ void loop() {
Serial.flush();
Serial.println(F("Send NEC with 8 bit address"));
Serial.flush();
IrSender.sendNEC(sAddress & 0xFF, sCommand, sRepeats);
delay(DELAY_AFTER_SEND); // delay must be greater than 5 ms (RECORD_GAP_MICROS), otherwise the receiver sees it as one long signal
Serial.println(F("Send NEC with 16 bit address"));
Serial.flush();
IrSender.sendNEC(sAddress, sCommand, sRepeats);
delay(DELAY_AFTER_SEND);
@ -113,6 +115,7 @@ void loop() {
* Send constant values only once in this demo
*/
Serial.println(F("Sending NEC Pronto data with 8 bit address 0x80 and command 0x45 and no repeats"));
Serial.flush();
IrSender.sendPronto(F("0000 006D 0022 0000 015E 00AB " /* Pronto header + start bit */
"0017 0015 0017 0015 0017 0017 0015 0017 0017 0015 0017 0015 0017 0015 0017 003F " /* Lower address byte */
"0017 003F 0017 003E 0017 003F 0015 003F 0017 003E 0017 003F 0017 003E 0017 0015 " /* Upper address byte (inverted at 8 bit mode) */
@ -127,6 +130,7 @@ void loop() {
Serial.println(
F(
"Send NEC with 16 bit address 0x0102 and command 0x34 with NECRaw(0xCC340102) which results in a parity error, since 34 == ~CB and not C0"));
Serial.flush();
IrSender.sendNECRaw(0xC0340102, sRepeats);
delay(DELAY_AFTER_SEND);
@ -136,52 +140,64 @@ void loop() {
* Example:
* 0xCB340102 byte reverse -> 0x020134CB bit reverse-> 40802CD3
*/
Serial.flush();
Serial.println(F("Send NEC with 16 bit address 0x0102 and command 0x34 with old 32 bit format MSB first"));
IrSender.sendNECMSB(0x40802CD3, 32, false);
delay(DELAY_AFTER_SEND);
}
Serial.println(F("Send Apple"));
Serial.flush();
IrSender.sendApple(sAddress & 0xFF, sCommand, sRepeats);
delay(DELAY_AFTER_SEND);
Serial.println(F("Send Panasonic"));
Serial.flush();
IrSender.sendPanasonic(sAddress & 0xFFF, sCommand, sRepeats);
delay(DELAY_AFTER_SEND);
Serial.println(F("Send Kaseikyo with 0x4711 as Vendor ID"));
Serial.flush();
IrSender.sendKaseikyo(sAddress & 0xFFF, sCommand, sRepeats, 0x4711);
delay(DELAY_AFTER_SEND);
Serial.println(F("Send Denon"));
Serial.flush();
IrSender.sendDenon(sAddress & 0x1F, sCommand, sRepeats);
delay(DELAY_AFTER_SEND);
Serial.println(F("Send Denon/Sharp variant"));
Serial.flush();
IrSender.sendSharp(sAddress & 0x1F, sCommand, sRepeats);
delay(DELAY_AFTER_SEND);
Serial.println(F("Send Sony/SIRCS with 7 command and 5 address bits"));
Serial.flush();
IrSender.sendSony(sAddress & 0x1F, sCommand & 0x7F, sRepeats);
delay(DELAY_AFTER_SEND);
Serial.println(F("Send Sony/SIRCS with 7 command and 8 address bits"));
Serial.flush();
IrSender.sendSony(sAddress & 0xFF, sCommand, sRepeats, SIRCS_15_PROTOCOL);
delay(DELAY_AFTER_SEND);
Serial.println(F("Send Sony/SIRCS with 7 command and 13 address bits"));
Serial.flush();
IrSender.sendSony(sAddress & 0x1FFF, sCommand & 0x7F, sRepeats, SIRCS_20_PROTOCOL);
delay(DELAY_AFTER_SEND);
Serial.println(F("Send RC5"));
Serial.flush();
IrSender.sendRC5(sAddress & 0x1F, sCommand & 0x3F, sRepeats, true); // 5 address, 6 command bits
delay(DELAY_AFTER_SEND);
Serial.println(F("Send RC5X with 7.th MSB of command set"));
Serial.flush();
IrSender.sendRC5(sAddress & 0x1F, (sCommand & 0x3F) + 0x40, sRepeats, true); // 5 address, 7 command bits
delay(DELAY_AFTER_SEND);
Serial.println(F("Send RC6"));
Serial.flush();
IrSender.sendRC6(sAddress, sCommand, sRepeats, true);
delay(DELAY_AFTER_SEND);
@ -204,17 +220,20 @@ void loop() {
IRSendData.protocol = JVC; // switch protocol
Serial.print(F("Send "));
Serial.println(getProtocolString(IRSendData.protocol));
Serial.flush();
IrSender.write(&IRSendData, sRepeats);
delay(DELAY_AFTER_SEND);
IRSendData.protocol = LG;
Serial.print(F("Send "));
Serial.println(getProtocolString(IRSendData.protocol));
Serial.flush();
IrSender.write(&IRSendData, sRepeats);
delay(DELAY_AFTER_SEND);
IRSendData.protocol = BOSEWAVE;
Serial.println(F("Send Bosewave with no address and 8 command bits"));
Serial.flush();
IrSender.write(&IRSendData, sRepeats);
delay(DELAY_AFTER_SEND);
@ -222,6 +241,7 @@ void loop() {
* LEGO is difficult to receive because of its short marks and spaces
*/
Serial.println(F("Send Lego with 2 channel and with 4 command bits"));
Serial.flush();
IrSender.sendLegoPowerFunctions(sAddress, sCommand, LEGO_MODE_COMBO, true);
delay(DELAY_AFTER_SEND);
@ -248,6 +268,5 @@ void loop() {
sRepeats = 4;
}
delay(DELAY_AFTER_LOOP); // additional delay at the end of each loop
}

View File

@ -64,6 +64,7 @@ void ACSendCode(uint16_t aCommand) {
Serial.print(aCommand, HEX);
Serial.print(F(" | "));
Serial.println(aCommand, BIN);
Serial.flush();
IrSender.sendLG((uint8_t) 0x88, aCommand, 0);
}

View File

@ -91,6 +91,7 @@ void loop() {
* The values are NOT multiple of 50, but are taken from the NEC timing definitions
*/
Serial.println(F("Send NEC 8 bit address 0xFB04, 0x08 with exact timing (16 bit array format)"));
Serial.flush();
const uint16_t irSignal[] = { 9000, 4500, 560, 560, 560, 560, 560, 1690, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560,
1690, 560, 1690, 560, 560, 560, 1690, 560, 1690, 560, 1690, 560, 1690, 560, 1690, 560, 560, 560, 560, 560, 560, 560,
@ -106,11 +107,13 @@ void loop() {
* Note the approach used to automatically calculate the size of the array.
*/
Serial.println(F("Send NEC 16 bit address 0xFB0C, 0x18 with (50 us) tick resolution timing (8 bit array format) "));
Serial.flush();
IrSender.sendRaw_P(irSignalP, sizeof(irSignalP) / sizeof(irSignalP[0]), NEC_KHZ);
delay(1000); // delay must be greater than 5 ms (RECORD_GAP_MICROS), otherwise the receiver sees it as one long signal
Serial.println(F("Send NEC 16 bit address 0x0102, 8 bit data 0x34 with generated timing"));
Serial.flush();
IrSender.sendNEC(0x0102, 0x34, true, 0);
delay(3000);

View File

@ -64,6 +64,7 @@ void loop() {
Serial.println();
Serial.println(F("Send NEC with 16 bit address"));
Serial.flush();
// Results for the first loop to: Protocol=NEC Address=0x102 Command=0x34 Raw-Data=0xCB340102 (32 bits)
IrSender.sendNEC(sAddress, sCommand, sRepeats);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

View File

@ -31,6 +31,7 @@
*/
#include "IRremoteInt.h"
//#include "digitalWriteFast.h"
__attribute((error("Version > 3.0.1"))) void UsageError(const char *details);
@ -360,36 +361,27 @@ void IRsend::mark(unsigned int aMarkMicros) {
#if defined(USE_SOFT_SEND_PWM) && !defined(ESP32) // for esp32 we use PWM generation by hw_timer_t for each pin
unsigned long start = micros();
unsigned long nextPeriodEnding = start;
while (micros() - start < aMarkMicros) {
unsigned long tMicros;
do {
// digitalToggleFast(IR_TIMING_TEST_PIN);
// Output the PWM pulse
noInterrupts(); // do not let interrupts extend the short on period
# ifdef SENDPIN_ON
SENDPIN_ON(sendPin);
# else
digitalWrite(sendPin, HIGH);
# endif
digitalWrite(sendPin, HIGH); // 4.3 us from do{ to pin setting
delayMicroseconds(periodOnTimeMicros); // this is normally implemented by a blocking wait
// Output the PWM pause
# ifdef SENDPIN_OFF
SENDPIN_OFF(sendPin);
# else
digitalWrite(sendPin, LOW);
# endif
interrupts(); // Enable interrupts -to keep micros correct- for the longer off period 3.4 us until receive ISR is active (for 7 us + pop's)
nextPeriodEnding += periodTimeMicros;
while (micros() < nextPeriodEnding){
;
}
}
do {
tMicros = micros(); // we have only 4 us resolution for and AVR @16MHz
// digitalToggleFast(IR_TIMING_TEST_PIN); // 3.0 us per call @16MHz
} while (tMicros < nextPeriodEnding); // 3.4 us @16MHz
} while (tMicros - start < aMarkMicros);
#else
# if defined(USE_NO_SEND_PWM)
# ifdef SENDPIN_OFF
SENDPIN_OFF(sendPin);
# else
digitalWrite(sendPin, LOW); // Set output to active low.
# endif
# else
TIMER_ENABLE_SEND_PWM; // Enable pin 3 PWM output
@ -408,11 +400,7 @@ void IRsend::mark(unsigned int aMarkMicros) {
*/
void IRsend::ledOff() {
#if defined(USE_SOFT_SEND_PWM) && !defined(ESP32) // for esp32 we use PWM generation by hw_timer_t for each pin
# ifdef SENDPIN_OFF
SENDPIN_OFF(sendPin);
# else
digitalWrite(sendPin, LOW);
# endif
#elif defined(USE_NO_SEND_PWM)
digitalWrite(sendPin, HIGH); // Set output to inactive high.
#else

View File

@ -164,7 +164,7 @@
/**
* If USE_SOFT_SEND_PWM, this amount is subtracted from the on-time of the pulses.
* It should be the time used for SENDPIN_OFF(sendPin) and the call to delayMicros()
* It should be the time used for digitalWrite(sendPin, LOW) and the call to delayMicros()
* Measured value for Nano @16MHz is around 3000, for Bluepill @72MHz is around 700, for Zero 3600
*/
#ifndef PULSE_CORRECTION_NANOS

View File

@ -474,7 +474,7 @@ public:
uint8_t sendPin;
unsigned int periodTimeMicros;
unsigned int periodOnTimeMicros;
unsigned int periodOnTimeMicros; // compensated with PULSE_CORRECTION_NANOS for duration of digitalWrite.
void customDelayMicroseconds(unsigned long aMicroseconds);
};