Arduino-IRremote/src/ir_Denon.hpp

314 lines
12 KiB
C++

/*
* ir_Denon.cpp
*
* Contains functions for receiving and sending Denon/Sharp IR Protocol
*
* This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote.
*
************************************************************************************
* MIT License
*
* Copyright (c) 2020-2023 Armin Joachimsmeyer
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is furnished
* to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
************************************************************************************
*/
#ifndef _IR_DENON_HPP
#define _IR_DENON_HPP
#if defined(DEBUG) && !defined(LOCAL_DEBUG)
#define LOCAL_DEBUG
#else
//#define LOCAL_DEBUG // This enables debug output only for this file
#endif
/** \addtogroup Decoder Decoders and encoders for different protocols
* @{
*/
//==============================================================================
// DDDD EEEEE N N OOO N N
// D D E NN N O O NN N
// D D EEE N N N O O N N N
// D D E N NN O O N NN
// DDDD EEEEE N N OOO N N
//==============================================================================
// SSSS H H AAA RRRR PPPP
// S H H A A R R P P
// SSS HHHHH AAAAA RRRR PPPP
// S H H A A R R P
// SSSS H H A A R R P
//==============================================================================
/*
Protocol=Denon Address=0x11 Command=0x76 Raw-Data=0xED1 15 bits LSB first
+ 200,-1800 + 300,- 750 + 300,- 800 + 200,- 800
+ 250,-1800 + 250,- 800 + 250,-1800 + 300,-1750
+ 300,- 750 + 300,-1800 + 250,-1800 + 250,-1850
+ 250,- 750 + 300,- 800 + 250,- 800 + 250
Sum: 23050
Denon/Sharp variant
Protocol=Denon Address=0x11 Command=0x76 Raw-Data=0x4ED1 15 bits LSB first
+ 200,-1800 + 300,- 750 + 250,- 800 + 250,- 750
+ 300,-1800 + 250,- 800 + 250,-1800 + 300,-1750
+ 300,- 750 + 300,-1800 + 250,-1800 + 250,-1800
+ 300,- 750 + 300,- 750 + 300,-1800 + 250
Sum: 23050
*/
/*
* https://www.mikrocontroller.net/articles/IRMP_-_english#DENON
* Denon published all their IR codes:
* http://assets.denon.com/documentmaster/us/denon%20master%20ir%20hex.xls
* Example:
* 0000 006D 0000 0020 000A 001E 000A 0046 000A 001E 000A 001E 000A 001E // 5 address bits
* 000A 001E 000A 001E 000A 0046 000A 0046 000A 0046 000A 001E 000A 0046 000A 0046 // 8 command bits
* 000A 001E 000A 001E 000A 0679 // 2 frame bits 0,0 + stop bit + space for AutoRepeat
* 000A 001E 000A 0046 000A 001E 000A 001E 000A 001E // 5 address bits
* 000A 0046 000A 0046 000A 001E 000A 001E 000A 001E 000A 0046 000A 001E 000A 001E // 8 inverted command bits
* 000A 0046 000A 0046 000A 0679 // 2 frame bits 1,1 + stop bit + space for Repeat
* From analyzing the codes for Tuner preset 1 to 8 in tab Main Zone ID#1 it is obvious, that the protocol is LSB first at least for command.
* All Denon codes with 32 as 3. value use the Kaseyikyo Denon variant.
*/
// LSB first, no start bit, 5 address + 8 command + 2 frame (0,0) + 1 stop bit - each frame 2 times
// Every frame is auto repeated with a space period of 45 ms and the command and frame inverted to (1,1) or (0,1) for SHARP.
//
#define DENON_ADDRESS_BITS 5
#define DENON_COMMAND_BITS 8
#define DENON_FRAME_BITS 2 // 00/10 for 1. frame Denon/Sharp, inverted for autorepeat frame
#define DENON_BITS (DENON_ADDRESS_BITS + DENON_COMMAND_BITS + DENON_FRAME_BITS) // 15 - The number of bits in the command
#define DENON_UNIT 260
#define DENON_BIT_MARK DENON_UNIT // The length of a Bit:Mark
#define DENON_ONE_SPACE (7 * DENON_UNIT) // 1820 // The length of a Bit:Space for 1's
#define DENON_ZERO_SPACE (3 * DENON_UNIT) // 780 // The length of a Bit:Space for 0's
#define DENON_AUTO_REPEAT_DISTANCE 45000 // Every frame is auto repeated with a space period of 45 ms and the command and frame inverted.
#define DENON_REPEAT_PERIOD 110000 // Commands are repeated every 110 ms (measured from start to start) for as long as the key on the remote control is held down.
// for old decoder
#define DENON_HEADER_MARK DENON_UNIT // The length of the Header:Mark
#define DENON_HEADER_SPACE (3 * DENON_UNIT) // 780 // The length of the Header:Space
struct PulseDistanceWidthProtocolConstants DenonProtocolConstants = { DENON, DENON_KHZ, DENON_HEADER_MARK, DENON_HEADER_SPACE,
DENON_BIT_MARK, DENON_ONE_SPACE, DENON_BIT_MARK, DENON_ZERO_SPACE, PROTOCOL_IS_LSB_FIRST,
(DENON_REPEAT_PERIOD / MICROS_IN_ONE_MILLI), NULL };
/************************************
* Start of send and decode functions
************************************/
void IRsend::sendSharp(uint8_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats) {
sendDenon(aAddress, aCommand, aNumberOfRepeats, true);
}
void IRsend::sendDenon(uint8_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats, bool aSendSharp) {
// Set IR carrier frequency
enableIROut (DENON_KHZ); // 38 kHz
// Add frame marker for sharp
uint16_t tCommand = aCommand;
if (aSendSharp) {
tCommand |= 0x0200; // the 2 upper bits are 00 for Denon and 10 for Sharp
}
uint16_t tData = aAddress | ((uint16_t) tCommand << DENON_ADDRESS_BITS);
uint16_t tInvertedData = (tData ^ 0x7FE0); // Command and frame (upper 10 bits) are inverted
uint_fast8_t tNumberOfCommands = aNumberOfRepeats + 1;
while (tNumberOfCommands > 0) {
// Data
sendPulseDistanceWidthData(&DenonProtocolConstants, tData, DENON_BITS);
// Inverted autorepeat frame
delay(DENON_AUTO_REPEAT_DISTANCE / MICROS_IN_ONE_MILLI);
sendPulseDistanceWidthData(&DenonProtocolConstants, tInvertedData, DENON_BITS);
tNumberOfCommands--;
// skip last delay!
if (tNumberOfCommands > 0) {
// send repeated command with a fixed space gap
delay( DENON_AUTO_REPEAT_DISTANCE / MICROS_IN_ONE_MILLI);
}
}
}
bool IRrecv::decodeSharp() {
return decodeDenon();
}
bool IRrecv::decodeDenon() {
// we have no start bit, so check for the exact amount of data bits
// Check we have the right amount of data (32). The + 2 is for initial gap + stop bit mark
if (decodedIRData.rawDataPtr->rawlen != (2 * DENON_BITS) + 2) {
IR_DEBUG_PRINT(F("Denon: "));
IR_DEBUG_PRINT(F("Data length="));
IR_DEBUG_PRINT(decodedIRData.rawDataPtr->rawlen);
IR_DEBUG_PRINTLN(F(" is not 32"));
return false;
}
// Try to decode as Denon protocol
if (!decodePulseDistanceWidthData(&DenonProtocolConstants, DENON_BITS, 1)) {
#if defined(LOCAL_DEBUG)
Serial.print(F("Denon: "));
Serial.println(F("Decode failed"));
#endif
return false;
}
// Check for stop mark
if (!matchMark(decodedIRData.rawDataPtr->rawbuf[(2 * DENON_BITS) + 1], DENON_HEADER_MARK)) {
#if defined(LOCAL_DEBUG)
Serial.print(F("Denon: "));
Serial.println(F("Stop bit mark length is wrong"));
#endif
return false;
}
// Success
decodedIRData.address = decodedIRData.decodedRawData & 0x1F;
decodedIRData.command = decodedIRData.decodedRawData >> DENON_ADDRESS_BITS;
uint8_t tFrameBits = (decodedIRData.command >> 8) & 0x03;
decodedIRData.command &= 0xFF;
// Check for (auto) repeat
if (decodedIRData.rawDataPtr->rawbuf[0] < ((DENON_AUTO_REPEAT_DISTANCE + (DENON_AUTO_REPEAT_DISTANCE / 4)) / MICROS_PER_TICK)) {
repeatCount++;
if (repeatCount > 1) { // skip first auto repeat
decodedIRData.flags = IRDATA_FLAGS_IS_REPEAT;
}
if (tFrameBits & 0x01) {
/*
* Here we are in the auto repeated frame with the inverted command
*/
#if defined(LOCAL_DEBUG)
Serial.print(F("Denon: "));
Serial.println(F("Autorepeat received="));
#endif
decodedIRData.flags |= IRDATA_FLAGS_IS_AUTO_REPEAT;
// Check parity of consecutive received commands. There is no parity in one data set.
if ((uint8_t) lastDecodedCommand != (uint8_t)(~decodedIRData.command)) {
decodedIRData.flags |= IRDATA_FLAGS_PARITY_FAILED;
#if defined(LOCAL_DEBUG)
Serial.print(F("Denon: "));
Serial.print(F("Parity check for repeat failed. Last command="));
Serial.print(lastDecodedCommand, HEX);
Serial.print(F(" current="));
Serial.println(~decodedIRData.command, HEX);
#endif
}
// always take non inverted command
decodedIRData.command = lastDecodedCommand;
}
// repeated command here
if (tFrameBits == 1 || tFrameBits == 2) {
decodedIRData.protocol = SHARP;
} else {
decodedIRData.protocol = DENON;
}
} else {
repeatCount = 0;
// first command here
if (tFrameBits == 2) {
decodedIRData.protocol = SHARP;
} else {
decodedIRData.protocol = DENON;
}
}
decodedIRData.numberOfBits = DENON_BITS;
return true;
}
/*********************************************************************************
* Old deprecated functions, kept for backward compatibility to old 2.0 tutorials
*********************************************************************************/
/*
* Only for backwards compatibility
*/
void IRsend::sendDenonRaw(uint16_t aRawData, int_fast8_t aNumberOfRepeats) {
sendDenon(aRawData >> (DENON_COMMAND_BITS + DENON_FRAME_BITS), (aRawData >> DENON_FRAME_BITS) & 0xFF, aNumberOfRepeats);
}
/*
* Old function with parameter data
*/
void IRsend::sendDenon(unsigned long data, int nbits) {
// Set IR carrier frequency
enableIROut (DENON_KHZ);
#if !(defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__))
// Serial.println(
// "The function sendDenon(data, nbits) is deprecated and may not work as expected! Use sendDenonRaw(data, NumberOfRepeats) or better sendDenon(Address, Command, NumberOfRepeats).");
#endif
// Header
mark(DENON_HEADER_MARK);
space(DENON_HEADER_SPACE);
// Data
sendPulseDistanceWidthData(DENON_BIT_MARK, DENON_ONE_SPACE, DENON_BIT_MARK, DENON_ZERO_SPACE, data, nbits,
PROTOCOL_IS_MSB_FIRST);
}
/*
* Old function without parameter aNumberOfRepeats
*/
void IRsend::sendSharp(uint16_t aAddress, uint16_t aCommand) {
sendDenon(aAddress, aCommand, true, 0);
}
bool IRrecv::decodeDenonOld(decode_results *aResults) {
// Check we have the right amount of data
if (decodedIRData.rawDataPtr->rawlen != 1 + 2 + (2 * DENON_BITS) + 1) {
return false;
}
// Check initial Mark+Space match
if (!matchMark(aResults->rawbuf[1], DENON_HEADER_MARK)) {
return false;
}
if (!matchSpace(aResults->rawbuf[2], DENON_HEADER_SPACE)) {
return false;
}
// Try to decode as Denon protocol
if (!decodePulseDistanceWidthData(DENON_BITS, 3, DENON_BIT_MARK, 0, DENON_ONE_SPACE, DENON_ZERO_SPACE, PROTOCOL_IS_MSB_FIRST)) {
return false;
}
// Success
aResults->value = decodedIRData.decodedRawData;
aResults->bits = DENON_BITS;
aResults->decode_type = DENON;
decodedIRData.protocol = DENON;
return true;
}
/** @}*/
#if defined(LOCAL_DEBUG)
#undef LOCAL_DEBUG
#endif
#endif // _IR_DENON_HPP