项目作者: wagiminator

项目描述 :
IR Remote Control
高级语言: C++
项目地址: git://github.com/wagiminator/ATtiny13-TinyRemote.git
创建时间: 2020-03-24T11:27:52Z
项目社区:https://github.com/wagiminator/ATtiny13-TinyRemote

开源协议:Other

下载


TinyRemote - IR Remote Control based on ATtiny13A

TinyRemote is a 5-button IR remote control based on an ATtiny13A powered by a CR2032 or LIR2032 coin cell battery.

pic1.jpg

For the 12-button version of the ATtiny13A-based IR remote control see TinyRemoteXL.

Hardware

The wiring is pretty simple:

Wiring.png

Due to the control with a high-frequency PWM, no series resistor is necessary for the LEDs. If you want to use only four buttons, you can leave KEY5 unsoldered and upload the 4-button version of the firmware. If you want to use all five buttons, you have to disable RESET on PB5 by burning the respective fuses after uploading the 5-button version of the firmware:

  1. avrdude -c usbasp -p t13 -U lfuse:w:0x2a:m -U hfuse:w:0xfe:m

Warning: You will need a high voltage fuse resetter to undo this change!

For a simple breadboard test you can directly connect an IR LED via a 220 Ohm resistor to PB1.

Software

There are a variety of communication protocols for infrared remote controls. Basically, most of them have in common that a carrier wave between 30kHz and 58kHz, depending on the protocol, is generated by means of PWM at the IR diode so that the receiver can distinguish the signal from the noise. The IR telegram is modulated onto the carrier wave using pulse code modulation (PCM) by simply switching the IR LED on and off in a defined pattern. The telegrams are composed of a start frame, the device address of the receiver and the key-dependent command. The three most widely used protocols are implemented for the TinyRemote:

Protocol Carrier Frequency Encoding Method Start Frame Address Command
NEC 38kHz Pulse Distance 9ms burst / 4.5ms space 8/16 bits 8 bits
RC-5 36kHz Manchester Start bits 5 bits 6/7 bits
Sony SIRC 40kHz Pulse Length 2.4ms burst / 0.6ms space 5/8/13 bits 7 bits

Since the software implementation for all protocols is very similar, only the NEC protocol is explained in more detail below.

Implementation of the NEC Protocol

Timer0 generates the 38kHz carrier frequency with a duty cycle of 25% on the output pin to the IR LED.

  1. // define values for 38kHz PWM frequency and 25% duty cycle
  2. #define TOP 31 // 1200kHz / 38kHz - 1 = 31
  3. #define DUTY 7 // 1200kHz / 38kHz / 4 - 1 = 7
  4. // set timer0 to toggle IR pin at 38 kHz
  5. TCCR0A = 0b00100011; // PWM on OC0B (PB1)
  6. TCCR0B = 0b00001001; // no prescaler
  7. OCR0A = TOP; // 38 kHz PWM frequency
  8. OCR0B = DUTY; // 25 % duty cycle

Here’s the result, captured with a logic analyzer:

PWM.png

The IR telegram is modulated by toggling the pin of the IR LED to input or output. Setting the pin to output enables the PWM on this pin and sends a burst of the carrier wave. Setting the pin to input turns off the LED completely. The NEC protocol uses pulse distance encoding, which means a data bit is defined by the time between the bursts. A “0” bit is a 562.5µs burst (LED on: 38kHz PWM) followed by a 562.5µs space (LED off), a “1” bit is a 562.5µs burst followed by a 1687.5µs space.

An IR telegram starts with a 9ms leading burst followed by a 4.5ms space. Afterwards 4 data bytes are transmitted, least significant bit first. A final 562.5µs burst signifies the end of the transmission. The four data bytes are in order:

  • the 8-bit address for the receiving device,
  • the 8-bit logical inverse of the address,
  • the 8-bit command and
  • the 8-bit logical inverse of the command.

NEC_transmission.png

  1. // macros to switch on/off IR LED
  2. #define IRon() DDRB |= 0b00000010 // PB1 as output = IR at OC0B (38 kHz)
  3. #define IRoff() DDRB &= 0b11111101 // PB1 as input = LED off
  4. // macros to modulate the signals according to NEC protocol with compensated timings
  5. #define startPulse() {IRon(); _delay_us(9000); IRoff(); _delay_us(4500);}
  6. #define normalPulse() {IRon(); _delay_us( 562); IRoff(); _delay_us( 557);}
  7. #define bit1Pause() _delay_us(1120) // 1687.5us - 562.5us = 1125us
  8. // send a single byte via IR
  9. void sendByte(uint8_t value) {
  10. for (uint8_t i=8; i; i--, value>>=1) { // send 8 bits, LSB first
  11. normalPulse(); // 562us burst, 562us pause
  12. if (value & 1) bit1Pause(); // extend pause if bit is 1
  13. }
  14. }
  15. // send complete telegram (start frame + address + command) via IR
  16. void sendCode(uint8_t cmd) {
  17. startPulse(); // signify start of transmission
  18. sendByte(ADDR); // send address byte
  19. sendByte(~ADDR); // send inverse of address byte
  20. sendByte(cmd); // send command byte
  21. sendByte(~cmd); // send inverse of command byte
  22. normalPulse(); // signify end of transmission
  23. }

The Extended NEC protocol uses 16-bit addresses. Instead of sending an 8-bit address and its logically inverse, first the low byte and then the high byte of the address is transmitted.

  1. // send complete telegram (start frame + address + command) via IR (Extended NEC)
  2. void sendCode(uint8_t cmd) {
  3. startPulse(); // signify start of transmission
  4. sendByte(ADDR & 0xFF); // send address low byte
  5. sendByte(ADDR >> 8); // send address high byte
  6. sendByte(cmd); // send command byte
  7. sendByte(~cmd); // send inverse of command byte
  8. normalPulse(); // signify end of transmission
  9. }

Here’s the result, captured with a logic analyzer:

NEC_protocol.png

If the key on the remote controller is kept depressed, a repeat code will be issued consisting of a 9ms leading burst, a 2.25ms pause and a 562.5µs burst to mark the end. The repeat code will continue to be sent out at 108ms intervals, until the key is finally released.

NEC_repeat.png

  1. // macros to modulate the signals according to NEC protocol with compensated timings
  2. #define repeatPulse() {IRon(); _delay_us(9000); IRoff(); _delay_us(2250);}
  3. #define repeatCode() {_delay_ms(40); repeatPulse(); normalPulse(); _delay_ms(56);}
  4. // send repeat command until button is released
  5. while (~PINB & 0b00111101) repeatCode();

The main loop of the implementation is pretty simple:

  1. // IR codes (use 16-bit address for extended NEC protocol)
  2. #define ADDR 0x04 // Address: LG TV
  3. #define KEY1 0x02 // Command: Volume+
  4. #define KEY2 0x00 // Command: Channel+
  5. #define KEY3 0x03 // Command: Volume-
  6. #define KEY4 0x01 // Command: Channel-
  7. #define KEY5 0x08 // Command: Power
  8. // main loop
  9. while(1) {
  10. sleep_mode(); // sleep until button is pressed
  11. _delay_ms(1); // debounce
  12. uint8_t buttons = ~PINB & 0b00111101; // read button pins
  13. switch (buttons) { // send corresponding IR code
  14. case 0b00000001: sendCode(KEY1); break;
  15. case 0b00000100: sendCode(KEY2); break;
  16. case 0b00001000: sendCode(KEY3); break;
  17. case 0b00010000: sendCode(KEY4); break;
  18. case 0b00100000: sendCode(KEY5); break;
  19. default: break;
  20. }
  21. }

Implementation of the Philips RC-5 Protocol

The Philips RC-5 protocol uses Manchester encoding on a carrier frequency of 36kHz. A “0” bit is an 889µs burst followed by an 889µs space, a “1” bit is an 889µs space followed by an 889µs burst. An IR telegram starts with two start bits. The first bit is always “1”, the second bit is “1” in the original protocol and the inverted 7th bit of the command in the extended RC-5 protocol. The third bit toggles after each button release. The next five bits represent the device address and the last six bits represent the command, all transmitted MSB first.

RC5_transmission.png

As long as a key remains down the telegram will be repeated every 114ms without changing the toggle bit.

  1. // define values for 36kHz PWM frequency and 25% duty cycle
  2. #define TOP 32 // 1200kHz / 36kHz - 1 = 32
  3. #define DUTY 7 // 1200kHz / 36kHz / 4 - 1 = 7
  4. // macros to switch on/off IR LED
  5. #define IRon() DDRB |= 0b00000010 // PB1 as output = IR at OC0B (36 kHz)
  6. #define IRoff() DDRB &= 0b11111101 // PB1 as input = LED off
  7. // macros to modulate the signals according to RC-5 protocol with compensated timings
  8. #define bit0Pulse() {IRon(); _delay_us(889); IRoff(); _delay_us(884);}
  9. #define bit1Pulse() {IRoff(); _delay_us(889); IRon(); _delay_us(884);}
  10. #define repeatDelay() _delay_ms(89) // 114ms - 14 * 2 * 889us
  11. // bitmasks
  12. #define startBit 0b0010000000000000
  13. #define cmdBit7 0b0001000000000000
  14. #define toggleBit 0b0000100000000000
  15. // toggle state variable
  16. uint8_t toggle = 0;
  17. // send complete telegram (startbits + togglebit + address + command) via IR
  18. void sendCode(uint8_t cmd) {
  19. // prepare the message
  20. uint16_t message = ADDR << 6; // shift address to the right position
  21. message |= (cmd & 0x3f); // add the low 6 bits of the command
  22. if (~cmd & 0x40) message |= cmdBit7; // add inverse of 7th command bit
  23. message |= startBit; // add start bit
  24. if (toggle) message |= toggleBit; // add toggle bit
  25. // send the message
  26. do {
  27. uint16_t bitmask = startBit; // set the bitmask to first bit to send
  28. for(uint8_t i=14; i; i--, bitmask>>=1) { // 14 bits, MSB first
  29. (message & bitmask) ? (bit1Pulse()) : (bit0Pulse()); // send the bit
  30. }
  31. IRoff(); // switch off IR LED
  32. repeatDelay(); // wait for next repeat
  33. } while(~PINB & 0b00111101); // repeat sending until button is released
  34. toggle ^= 1; // toggle the toggle bit
  35. }

Implementation of the Sony SIRC Protocol

The Sony SIRC protocol uses pulse length encoding on a carrier frequency of 40kHz. A “0” bit is a 600µs burst followed by a 600µs space, a “1” bit is a 1200µs burst followed by a 600µs space. An IR telegram starts with a 2400µs leading burst followed by a 600µs space. The command and address bits are then transmitted, LSB first. Depending on the protocol version, these are in detail:

  • 12-bit version: 7 command bits, 5 address bits
  • 15-bit version: 7 command bits, 8 address bits
  • 20-bit version: 7 command bits, 5 address bits, 8 extended bits

As long as a key remains down the telegram will be repeated every 45ms.

  1. // define values for 40kHz PWM frequency and 25% duty cycle
  2. #define TOP 29 // 1200kHz / 40kHz - 1 = 29
  3. #define DUTY 7 // 1200kHz / 40kHz / 4 - 1 = 7
  4. // macros to switch on/off IR LED
  5. #define IRon() DDRB |= 0b00000010 // PB1 as output = IR at OC0B (40kHz)
  6. #define IRoff() DDRB &= 0b11111101 // PB1 as input = LED off
  7. // macros to modulate the signals according to SONY protocol with compensated timings
  8. #define startPulse() {IRon(); _delay_us(2400); IRoff(); _delay_us( 595);}
  9. #define bit0Pulse() {IRon(); _delay_us( 600); IRoff(); _delay_us( 595);}
  10. #define bit1Pulse() {IRon(); _delay_us(1200); IRoff(); _delay_us( 595);}
  11. #define repeatPause() _delay_ms(27)
  12. // send "number" of bits of "value" via IR
  13. void sendByte(uint8_t value, uint8_t number) {
  14. do { // send number of bits, LSB first
  15. (value & 1) ? (bit1Pulse()) : (bit0Pulse()); // send bit
  16. value>>=1; // next bit
  17. } while(--number);
  18. }
  19. // send complete telegram (start frame + command + address) via IR
  20. void sendCode(uint8_t cmd) {
  21. do {
  22. startPulse(); // signify start of transmission
  23. sendByte(cmd, 7); // send 7 command bits
  24. #if BITS == 12 // if 12-bit version:
  25. sendByte(ADDR, 5); // send 5 address bits
  26. #elif BITS == 15 // if 15-bit version:
  27. sendByte(ADDR, 8); // send 8 address bits
  28. #elif BITS == 20 // if 20-bit version:
  29. sendByte(ADDR, 5); // send 5 address bits
  30. sendByte(EXTB, 8); // send 8 extended bits
  31. #endif
  32. repeatPause(); // wait until next repeat
  33. } while (~PINB & 0b00011101); // repeat sending until button is released
  34. }

Power Saving

The code shuts down unused peripherals and utilizes the sleep mode power down function. It wakes up on every button press by pin change interrupt.

  1. // setup pin change interrupt
  2. GIMSK = 0b00100000; // turn on pin change interrupts
  3. PCMSK = 0b00111101; // turn on interrupt on button pins
  4. SREG |= 0b10000000; // enable global interrupts
  5. // disable unused peripherals and set sleep mode to save power
  6. ADCSRA = 0b00000000; // disable ADC
  7. ACSR = 0b10000000; // disable analog comperator
  8. PRR = 0b00000001; // shut down ADC
  9. set_sleep_mode(SLEEP_MODE_PWR_DOWN); // set sleep mode to power down

As long as no button is pressed, the ATtiny remains in sleep mode power down and consumes a current of around 150nA at a voltage of 3V. The typical capacity of a CR2032 battery is 230mAh. This results in a theoretical battery life of 1.5 million hours or 179 years. In real life, of course, no battery will last that long due to its self-discharge. When the button is pressed, peaks of up to 30mA are consumed. The diagram below shows the course of the current consumption when a button is pressed and a NEC telegram is sent according to a measurement with the Power Profiler Kit II:

Current.png

When sending a NEC telegram, the current requirement increases to an average of around 2.75mA for 71ms. Theoretically, over 4 million telegrams could be sent with one battery. Note that the rechargeable LIR2032 batteries have a significantly lower capacity.

Timing Accuracy

The accuracy of the internal oscillator of the ATtiny13 is +/-10% with the factory calibration. Usually this is sufficient for an infrared remote control. Slight deviations in timing are tolerated by the receiver, since cheap remote controls are usually not more accurate. Nevertheless, it certainly doesn’t hurt to manually calibrate the internal oscillator and set the corresponding OSCCAL value at the beginning of the code.

  1. // oscillator calibration value (uncomment and set if necessary)
  2. #define OSCCAL_VAL 0x48

References, Links and Notes

  1. IR remote control explanations by San Bergmans
  2. IR remote control by Christoph Niessen (german)
  3. IR remote control detective by David Johnson-Davies
  4. Infrared communication concepts (altium.com)
  5. NEC decoder based on ATtiny13A
  6. TinyRemote XL
  7. TinyRemote RF
  8. OSC Calibrator
  9. ATtiny13A datasheet

pic2.jpg
pic3.jpg
pic4.jpg

License

license.png

This work is licensed under Creative Commons Attribution-ShareAlike 3.0 Unported License.
(http://creativecommons.org/licenses/by-sa/3.0/)