Once I got a call from Rostelecom and offered to connect IP TV. Well, I decided, let the wife and son watch cartoons in the bedroom and agreed. And then they brought me the coveted box. Because I donβt have a separate TV for it, so I decided to connect it to an old monitor via an HDMI-VGA adapter. For sound, I had old computer speakers. Decided - done. Everything started up perfectly with one thing: from the remote control that came with the set-top box, it is impossible to adjust the sound volume. How is that? Honestly, I have never encountered such a thing. I didn't really understand the reasons, but it seems like the remote control from Rostelecom is registered on the TV, so the volume on the TV itself changes from the remote control, and not at the output of the set-top box. Conveniently? Of course, if you connect the set-top box to a modern TV.But getting out of bed and turning the turntable on the speakers every time you need to change the volume is inconvenient. We will deal with this issue. We will assemble a separate device that will adjust the volume on our speakers according to the signal from the remote control.
First, let's see what kind of signals the remote generates when pressing the "volume up", "volume down" and "mute" buttons. I used VS1838B as a receiver of signals from the remote control.
This is a handy receiver because it already demodulates the 38kHz infrared signal from the remote.
It turned out that the above buttons generate two types of signals alternately. First one option, next time you press another option. The figure shows one of the signal options when pressing the "mute" button. The signals were read using a logic analyzer.
100 . 24- . ( ) 900 , ( , ) 1800 . , , :
1. .
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
2 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 1 | 0 |
2 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 1 | 0 |
Mute 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 0 |
Mute 2 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 0 |
. , 50. .. , , β . "" . X9C103P. 10 . , , , 10 . .
VS1838B. . , , , . , , , . Attiny13A. : , . : 1 64 SRAM. (Atmega 328) 32 2 SRAM.
, , - . , :
const unsigned long UP1_DATA[] = {860, 900, 1750, 900, 860, 900, 860, 900, 860, 900, 860, 900, 860, 900, 860, 1750, 1750, 900, 860, 900, 860, 900, 860};
, attiny . . β , .. . : , 6- 15-, 19- 20- β . . β . . 8 ( 2).
2 β 1
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | |
---|---|---|---|---|---|---|---|---|
1 | 1 | 0 | 0 | 1 | 1 | 0 | 0 | 0 |
2 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 0 |
1 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 1 |
2 | 1 | 0 | 0 | 1 | 1 | 0 | 0 | 1 |
Mute 1 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 |
Mute 2 | 1 | 0 | 0 | 0 | 0 | 1 | 1 | 1 |
:
#define UP1_DATA 0b00011001
#define UP2_DATA 0b00011100
#define DOWN1_DATA 0b10011100
#define DOWN2_DATA 0b10011001
#define MUTE_ON_DATA 0b11100100
#define MUTE_OFF_DATA 0b11100001
. , ( ) . . , .
. _timer:
volatile unsigned long _timer = 0;
ISR(TIM0_OVF_vect)
{
_timer++;
}
Attiny13A 9,6 . 256 , 37500 . 900 33 , 1800 67 . , 9,6 , .
#define SHORT_TIME 33UL
#define LONG_TIME 67UL
uint8_t _counter = 0;
unsigned long getExpectedTime(uint8_t data)
{
uint8_t index;
if (_counter >= 2 && _counter <= 4)
{
index = _counter - 2;
}
else if (_counter >= 15 && _counter <= 17)
{
index = _counter - 12;
}
else if (_counter >= 20 && _counter <= 21)
{
index = _counter - 14;
}
else
{
return SHORT_TIME;
}
if (data & (1 << index)) return LONG_TIME;
return SHORT_TIME;
}
_counter β . data β 6 : UP1_DATA, UP2_DATA, DOWN1_DATA, DOWN2_DATA, MUTE_1_DATA, MUTE_2_DATA.
.. , , ( ) getExpectedTime . β .
volatile bool _hasPulse = false;
volatile unsigned long _RXPreviousTime = 0;
volatile unsigned long _pulseDuration = 0;
ISR(INT0_vect)
{
_pulseDuration = _timer - _RXPreviousTime;
_RXPreviousTime = _timer;
_hasPulse = true;
_rxPinStatus = !!(PINB & (1 << RX_PIN)); // digitalRead .
}
,
#define SIZE_OF_PATTERNS 6
#define PAUSE_TIME 375UL //10000
#define HAS_PATTERN_START 0b00111111
#define ERROR_VALUE 19UL
#define SIZE_OF_DATA 23
#define UP1_BT 0
#define UP2_BT 1
#define DOWN1_BT 2
#define DOWN2_BT 3
#define MUTE_ON_BT 4
#define MUTE_OFF_BT 5
// : UP1_BT, UP2_BT, DOWN1_DATA, DOWN2_DATA, MUTE_ON_BT, MUTE_OFF_BT
const uint8_t PATTERNS[] = {UP1_DATA, UP2_DATA, DOWN1_DATA, DOWN2_DATA, MUTE_ON_DATA, MUTE_OFF_DATA};
uint8_t _hasPattern = HAS_PATTERN_START;
// , PATTERNS.
uint8_t incrementCounter() // , PATTERNS.
{
if (_pulseDuration > PAUSE_TIME)
{
_counter = 0;
_hasPattern = HAS_PATTERN_START;
return 255;
}
if (_hasPattern)
{
unsigned long eTime;
for (uint8_t i = 0; i < SIZE_OF_PATTERNS; i++)
{
if (_hasPattern & (1 << i)) // .
{
eTime = getExpectedTime(PATTERNS[i]);
if (!((_rxPinStatus ^ !!(_counter % 2)) && _pulseDuration >= eTime - ERROR_VALUE && _pulseDuration <= eTime + ERROR_VALUE)) // .
{
_hasPattern &= ~(1 << i);
}
}
}
_counter++;
if (_counter == SIZE_OF_DATA)
{
if (_hasPattern) //-
{
switch (_hasPattern)
{
case 1: return UP1_BT;
case 2: return UP2_BT;
case 4: return DOWN1_BT;
case 8: return DOWN2_BT;
case 16: return MUTE_ON_BT;
case 32: return MUTE_OFF_BT;
default: return 255;
}
}
else
{
return 255;
}
}
else
{
return 255; //
}
}
else
{
return 255; //
}
}
_hasPattern β , . 1. -, , 0. , - . PATTERNS.
, X9C103P.
1020 1024 , , (-Os). GitHub. UNO, .. , Attiny13A. UNO , - . .
( ), .
9 , , 78L05.
8 : 3 , 3 . UTP , 8 . , .
:
, , , -. , GND , . . , smd 100 , .
, .
: