├── 3dprint ├── TinyRemoteIR.fcstd ├── TinyRemoteIR_case.stl └── TinyRemoteIR_lid.stl ├── LICENSE ├── README.md ├── documentation ├── TinyRemote_NEC.png ├── TinyRemote_PWM.png ├── TinyRemote_current.png ├── TinyRemote_pic1.jpg ├── TinyRemote_pic2.jpg ├── TinyRemote_pic3.jpg ├── TinyRemote_pic4.jpg ├── TinyRemote_pic5.jpg └── TinyRemote_wiring.png ├── hardware ├── TinyRemote_T13_BOM_v1.0.tsv ├── TinyRemote_T13_BOM_v1.1.tsv ├── TinyRemote_T13_gerber_v1.0.zip ├── TinyRemote_T13_gerber_v1.1.zip ├── TinyRemote_T13_schematic_v1.0.pdf └── TinyRemote_T13_schematic_v1.1.pdf └── software ├── MULTI_4_buttons ├── TinyRemote_t13_MULTI_4B.ino ├── main.c └── makefile ├── MULTI_5_buttons ├── TinyRemote_t13_MULTI_5B.ino ├── main.c └── makefile ├── NEC_4_buttons ├── TinyRemote_t13_NEC_4B.ino ├── main.c └── makefile ├── NEC_5_buttons ├── TinyRemote_t13_NEC_5B.ino ├── main.c └── makefile ├── RC5_4_buttons ├── TinyRemote_t13_RC5_4B.ino ├── main.c └── makefile ├── RC5_5_buttons ├── TinyRemote_t13_RC5_5B.ino ├── main.c └── makefile ├── SONY_4_buttons ├── TinyRemote_t13_SONY_4B.ino ├── main.c └── makefile └── SONY_5_buttons ├── TinyRemote_t13_SONY_5B.ino ├── main.c └── makefile /3dprint/TinyRemoteIR.fcstd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/ATtiny13-TinyRemote/9fef78df52df0114ab77d3ac22e2b230fedd6f06/3dprint/TinyRemoteIR.fcstd -------------------------------------------------------------------------------- /3dprint/TinyRemoteIR_case.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/ATtiny13-TinyRemote/9fef78df52df0114ab77d3ac22e2b230fedd6f06/3dprint/TinyRemoteIR_case.stl -------------------------------------------------------------------------------- /3dprint/TinyRemoteIR_lid.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/ATtiny13-TinyRemote/9fef78df52df0114ab77d3ac22e2b230fedd6f06/3dprint/TinyRemoteIR_lid.stl -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License. 2 | To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/3.0/ or send 3 | a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA. 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TinyRemote - IR Remote Control based on ATtiny13A 2 | TinyRemote is a 5-button IR remote control based on an ATtiny13A powered by a CR2032 or LIR2032 coin cell battery. 3 | 4 | - Project Video (YouTube): https://youtu.be/ad3eyNCov9c 5 | - Design Files (EasyEDA): https://easyeda.com/wagiminator/attiny13-tinyremote 6 | 7 | ![pic1.jpg](https://raw.githubusercontent.com/wagiminator/ATtiny13-TinyRemote/master/documentation/TinyRemote_pic1.jpg) 8 | 9 | For the 12-button version of the ATtiny13A-based IR remote control see [TinyRemoteXL](https://github.com/wagiminator/ATtiny13-TinyRemoteXL). 10 | 11 | # Hardware 12 | The wiring is pretty simple: 13 | 14 | ![Wiring.png](https://raw.githubusercontent.com/wagiminator/ATtiny13-TinyRemote/master/documentation/TinyRemote_wiring.png) 15 | 16 | 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: 17 | 18 | ``` 19 | avrdude -c usbasp -p t13 -U lfuse:w:0x2a:m -U hfuse:w:0xfe:m 20 | ``` 21 | 22 | Warning: You will need a high voltage fuse resetter to undo this change! 23 | 24 | For a simple breadboard test you can directly connect an IR LED via a 220 Ohm resistor to PB1. 25 | 26 | # Software 27 | 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: 28 | 29 | |Protocol|Carrier Frequency|Encoding Method|Start Frame|Address|Command| 30 | |:-|:-|:-|:-|:-|:-| 31 | |NEC|38kHz|Pulse Distance|9ms burst / 4.5ms space|8/16 bits|8 bits| 32 | |RC-5|36kHz|Manchester|Start bits|5 bits|6/7 bits| 33 | |Sony SIRC|40kHz|Pulse Length|2.4ms burst / 0.6ms space|5/8/13 bits|7 bits| 34 | 35 | Since the software implementation for all protocols is very similar, only the NEC protocol is explained in more detail below. 36 | 37 | ## Implementation of the NEC Protocol 38 | Timer0 generates the 38kHz carrier frequency with a duty cycle of 25% on the output pin to the IR LED. 39 | 40 | ```c 41 | // define values for 38kHz PWM frequency and 25% duty cycle 42 | #define TOP 31 // 1200kHz / 38kHz - 1 = 31 43 | #define DUTY 7 // 1200kHz / 38kHz / 4 - 1 = 7 44 | 45 | // set timer0 to toggle IR pin at 38 kHz 46 | TCCR0A = 0b00100011; // PWM on OC0B (PB1) 47 | TCCR0B = 0b00001001; // no prescaler 48 | OCR0A = TOP; // 38 kHz PWM frequency 49 | OCR0B = DUTY; // 25 % duty cycle 50 | ``` 51 | 52 | Here's the result, captured with a logic analyzer: 53 | 54 | ![PWM.png](https://raw.githubusercontent.com/wagiminator/ATtiny13-TinyRemote/master/documentation/TinyRemote_PWM.png) 55 | 56 | 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. 57 | 58 | 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: 59 | - the 8-bit address for the receiving device, 60 | - the 8-bit logical inverse of the address, 61 | - the 8-bit command and 62 | - the 8-bit logical inverse of the command. 63 | 64 | ![NEC_transmission.png](https://techdocs.altium.com/sites/default/files/wiki_attachments/296329/NECMessageFrame.png) 65 | 66 | ```c 67 | // macros to switch on/off IR LED 68 | #define IRon() DDRB |= 0b00000010 // PB1 as output = IR at OC0B (38 kHz) 69 | #define IRoff() DDRB &= 0b11111101 // PB1 as input = LED off 70 | 71 | // macros to modulate the signals according to NEC protocol with compensated timings 72 | #define startPulse() {IRon(); _delay_us(9000); IRoff(); _delay_us(4500);} 73 | #define normalPulse() {IRon(); _delay_us( 562); IRoff(); _delay_us( 557);} 74 | #define bit1Pause() _delay_us(1120) // 1687.5us - 562.5us = 1125us 75 | 76 | // send a single byte via IR 77 | void sendByte(uint8_t value) { 78 | for (uint8_t i=8; i; i--, value>>=1) { // send 8 bits, LSB first 79 | normalPulse(); // 562us burst, 562us pause 80 | if (value & 1) bit1Pause(); // extend pause if bit is 1 81 | } 82 | } 83 | 84 | // send complete telegram (start frame + address + command) via IR 85 | void sendCode(uint8_t cmd) { 86 | startPulse(); // signify start of transmission 87 | sendByte(ADDR); // send address byte 88 | sendByte(~ADDR); // send inverse of address byte 89 | sendByte(cmd); // send command byte 90 | sendByte(~cmd); // send inverse of command byte 91 | normalPulse(); // signify end of transmission 92 | } 93 | ``` 94 | 95 | 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. 96 | 97 | ```c 98 | // send complete telegram (start frame + address + command) via IR (Extended NEC) 99 | void sendCode(uint8_t cmd) { 100 | startPulse(); // signify start of transmission 101 | sendByte(ADDR & 0xFF); // send address low byte 102 | sendByte(ADDR >> 8); // send address high byte 103 | sendByte(cmd); // send command byte 104 | sendByte(~cmd); // send inverse of command byte 105 | normalPulse(); // signify end of transmission 106 | } 107 | ``` 108 | 109 | Here's the result, captured with a logic analyzer: 110 | 111 | ![NEC_protocol.png](https://raw.githubusercontent.com/wagiminator/ATtiny13-TinyRemote/master/documentation/TinyRemote_NEC.png) 112 | 113 | 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. 114 | 115 | ![NEC_repeat.png](https://techdocs.altium.com/sites/default/files/wiki_attachments/296329/NECRepeatCodes.png) 116 | 117 | ```c 118 | // macros to modulate the signals according to NEC protocol with compensated timings 119 | #define repeatPulse() {IRon(); _delay_us(9000); IRoff(); _delay_us(2250);} 120 | #define repeatCode() {_delay_ms(40); repeatPulse(); normalPulse(); _delay_ms(56);} 121 | 122 | // send repeat command until button is released 123 | while (~PINB & 0b00111101) repeatCode(); 124 | ``` 125 | 126 | The main loop of the implementation is pretty simple: 127 | 128 | ```c 129 | // IR codes (use 16-bit address for extended NEC protocol) 130 | #define ADDR 0x04 // Address: LG TV 131 | #define KEY1 0x02 // Command: Volume+ 132 | #define KEY2 0x00 // Command: Channel+ 133 | #define KEY3 0x03 // Command: Volume- 134 | #define KEY4 0x01 // Command: Channel- 135 | #define KEY5 0x08 // Command: Power 136 | 137 | // main loop 138 | while(1) { 139 | sleep_mode(); // sleep until button is pressed 140 | _delay_ms(1); // debounce 141 | uint8_t buttons = ~PINB & 0b00111101; // read button pins 142 | switch (buttons) { // send corresponding IR code 143 | case 0b00000001: sendCode(KEY1); break; 144 | case 0b00000100: sendCode(KEY2); break; 145 | case 0b00001000: sendCode(KEY3); break; 146 | case 0b00010000: sendCode(KEY4); break; 147 | case 0b00100000: sendCode(KEY5); break; 148 | default: break; 149 | } 150 | } 151 | ``` 152 | 153 | ## Implementation of the Philips RC-5 Protocol 154 | 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. 155 | 156 | ![RC5_transmission.png](https://techdocs.altium.com/sites/default/files/wiki_attachments/296330/RC5MessageFrame.png) 157 | 158 | As long as a key remains down the telegram will be repeated every 114ms without changing the toggle bit. 159 | 160 | ```c 161 | // define values for 36kHz PWM frequency and 25% duty cycle 162 | #define TOP 32 // 1200kHz / 36kHz - 1 = 32 163 | #define DUTY 7 // 1200kHz / 36kHz / 4 - 1 = 7 164 | 165 | // macros to switch on/off IR LED 166 | #define IRon() DDRB |= 0b00000010 // PB1 as output = IR at OC0B (36 kHz) 167 | #define IRoff() DDRB &= 0b11111101 // PB1 as input = LED off 168 | 169 | // macros to modulate the signals according to RC-5 protocol with compensated timings 170 | #define bit0Pulse() {IRon(); _delay_us(889); IRoff(); _delay_us(884);} 171 | #define bit1Pulse() {IRoff(); _delay_us(889); IRon(); _delay_us(884);} 172 | #define repeatDelay() _delay_ms(89) // 114ms - 14 * 2 * 889us 173 | 174 | // bitmasks 175 | #define startBit 0b0010000000000000 176 | #define cmdBit7 0b0001000000000000 177 | #define toggleBit 0b0000100000000000 178 | 179 | // toggle state variable 180 | uint8_t toggle = 0; 181 | 182 | // send complete telegram (startbits + togglebit + address + command) via IR 183 | void sendCode(uint8_t cmd) { 184 | // prepare the message 185 | uint16_t message = ADDR << 6; // shift address to the right position 186 | message |= (cmd & 0x3f); // add the low 6 bits of the command 187 | if (~cmd & 0x40) message |= cmdBit7; // add inverse of 7th command bit 188 | message |= startBit; // add start bit 189 | if (toggle) message |= toggleBit; // add toggle bit 190 | 191 | // send the message 192 | do { 193 | uint16_t bitmask = startBit; // set the bitmask to first bit to send 194 | for(uint8_t i=14; i; i--, bitmask>>=1) { // 14 bits, MSB first 195 | (message & bitmask) ? (bit1Pulse()) : (bit0Pulse()); // send the bit 196 | } 197 | IRoff(); // switch off IR LED 198 | repeatDelay(); // wait for next repeat 199 | } while(~PINB & 0b00111101); // repeat sending until button is released 200 | toggle ^= 1; // toggle the toggle bit 201 | } 202 | ``` 203 | 204 | ## Implementation of the Sony SIRC Protocol 205 | 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: 206 | - 12-bit version: 7 command bits, 5 address bits 207 | - 15-bit version: 7 command bits, 8 address bits 208 | - 20-bit version: 7 command bits, 5 address bits, 8 extended bits 209 | 210 | As long as a key remains down the telegram will be repeated every 45ms. 211 | 212 | ```c 213 | // define values for 40kHz PWM frequency and 25% duty cycle 214 | #define TOP 29 // 1200kHz / 40kHz - 1 = 29 215 | #define DUTY 7 // 1200kHz / 40kHz / 4 - 1 = 7 216 | 217 | // macros to switch on/off IR LED 218 | #define IRon() DDRB |= 0b00000010 // PB1 as output = IR at OC0B (40kHz) 219 | #define IRoff() DDRB &= 0b11111101 // PB1 as input = LED off 220 | 221 | // macros to modulate the signals according to SONY protocol with compensated timings 222 | #define startPulse() {IRon(); _delay_us(2400); IRoff(); _delay_us( 595);} 223 | #define bit0Pulse() {IRon(); _delay_us( 600); IRoff(); _delay_us( 595);} 224 | #define bit1Pulse() {IRon(); _delay_us(1200); IRoff(); _delay_us( 595);} 225 | #define repeatPause() _delay_ms(27) 226 | 227 | 228 | // send "number" of bits of "value" via IR 229 | void sendByte(uint8_t value, uint8_t number) { 230 | do { // send number of bits, LSB first 231 | (value & 1) ? (bit1Pulse()) : (bit0Pulse()); // send bit 232 | value>>=1; // next bit 233 | } while(--number); 234 | } 235 | 236 | // send complete telegram (start frame + command + address) via IR 237 | void sendCode(uint8_t cmd) { 238 | do { 239 | startPulse(); // signify start of transmission 240 | sendByte(cmd, 7); // send 7 command bits 241 | #if BITS == 12 // if 12-bit version: 242 | sendByte(ADDR, 5); // send 5 address bits 243 | #elif BITS == 15 // if 15-bit version: 244 | sendByte(ADDR, 8); // send 8 address bits 245 | #elif BITS == 20 // if 20-bit version: 246 | sendByte(ADDR, 5); // send 5 address bits 247 | sendByte(EXTB, 8); // send 8 extended bits 248 | #endif 249 | repeatPause(); // wait until next repeat 250 | } while (~PINB & 0b00011101); // repeat sending until button is released 251 | } 252 | ``` 253 | 254 | ## Power Saving 255 | 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. 256 | 257 | ```c 258 | // setup pin change interrupt 259 | GIMSK = 0b00100000; // turn on pin change interrupts 260 | PCMSK = 0b00111101; // turn on interrupt on button pins 261 | SREG |= 0b10000000; // enable global interrupts 262 | 263 | // disable unused peripherals and set sleep mode to save power 264 | ADCSRA = 0b00000000; // disable ADC 265 | ACSR = 0b10000000; // disable analog comperator 266 | PRR = 0b00000001; // shut down ADC 267 | set_sleep_mode(SLEEP_MODE_PWR_DOWN); // set sleep mode to power down 268 | ``` 269 | 270 | 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](https://www.nordicsemi.com/Products/Development-hardware/Power-Profiler-Kit-2): 271 | 272 | ![Current.png](https://raw.githubusercontent.com/wagiminator/ATtiny13-TinyRemote/master/documentation/TinyRemote_current.png) 273 | 274 | 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. 275 | 276 | ## Timing Accuracy 277 | 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](https://github.com/wagiminator/ATtiny84-TinyCalibrator) the internal oscillator and set the corresponding OSCCAL value at the beginning of the code. 278 | 279 | ```c 280 | // oscillator calibration value (uncomment and set if necessary) 281 | #define OSCCAL_VAL 0x48 282 | ``` 283 | 284 | # References, Links and Notes 285 | 1. [IR remote control explanations by San Bergmans](https://www.sbprojects.net/knowledge/ir/index.php) 286 | 2. [IR remote control by Christoph Niessen (german)](http://chris.cnie.de/avr/tcm231421.html) 287 | 3. [IR remote control detective by David Johnson-Davies](http://www.technoblogy.com/show?24A9) 288 | 4. [Infrared communication concepts (altium.com)](https://techdocs.altium.com/display/FPGA/Infrared+Communication+Concepts) 289 | 5. [NEC decoder based on ATtiny13A](https://github.com/wagiminator/ATtiny13-TinyDecoder) 290 | 6. [TinyRemote XL](https://github.com/wagiminator/ATtiny13-TinyRemoteXL) 291 | 7. [TinyRemote RF](https://github.com/wagiminator/ATtiny13-TinyRemoteRF) 292 | 8. [OSC Calibrator](https://github.com/wagiminator/ATtiny84-TinyCalibrator) 293 | 9. [ATtiny13A datasheet](http://ww1.microchip.com/downloads/en/DeviceDoc/doc8126.pdf) 294 | 295 | ![pic2.jpg](https://raw.githubusercontent.com/wagiminator/ATtiny13-TinyRemote/master/documentation/TinyRemote_pic2.jpg) 296 | ![pic3.jpg](https://raw.githubusercontent.com/wagiminator/ATtiny13-TinyRemote/master/documentation/TinyRemote_pic3.jpg) 297 | ![pic4.jpg](https://raw.githubusercontent.com/wagiminator/ATtiny13-TinyRemote/master/documentation/TinyRemote_pic4.jpg) 298 | 299 | # License 300 | ![license.png](https://i.creativecommons.org/l/by-sa/3.0/88x31.png) 301 | 302 | This work is licensed under Creative Commons Attribution-ShareAlike 3.0 Unported License. 303 | (http://creativecommons.org/licenses/by-sa/3.0/) 304 | -------------------------------------------------------------------------------- /documentation/TinyRemote_NEC.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/ATtiny13-TinyRemote/9fef78df52df0114ab77d3ac22e2b230fedd6f06/documentation/TinyRemote_NEC.png -------------------------------------------------------------------------------- /documentation/TinyRemote_PWM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/ATtiny13-TinyRemote/9fef78df52df0114ab77d3ac22e2b230fedd6f06/documentation/TinyRemote_PWM.png -------------------------------------------------------------------------------- /documentation/TinyRemote_current.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/ATtiny13-TinyRemote/9fef78df52df0114ab77d3ac22e2b230fedd6f06/documentation/TinyRemote_current.png -------------------------------------------------------------------------------- /documentation/TinyRemote_pic1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/ATtiny13-TinyRemote/9fef78df52df0114ab77d3ac22e2b230fedd6f06/documentation/TinyRemote_pic1.jpg -------------------------------------------------------------------------------- /documentation/TinyRemote_pic2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/ATtiny13-TinyRemote/9fef78df52df0114ab77d3ac22e2b230fedd6f06/documentation/TinyRemote_pic2.jpg -------------------------------------------------------------------------------- /documentation/TinyRemote_pic3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/ATtiny13-TinyRemote/9fef78df52df0114ab77d3ac22e2b230fedd6f06/documentation/TinyRemote_pic3.jpg -------------------------------------------------------------------------------- /documentation/TinyRemote_pic4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/ATtiny13-TinyRemote/9fef78df52df0114ab77d3ac22e2b230fedd6f06/documentation/TinyRemote_pic4.jpg -------------------------------------------------------------------------------- /documentation/TinyRemote_pic5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/ATtiny13-TinyRemote/9fef78df52df0114ab77d3ac22e2b230fedd6f06/documentation/TinyRemote_pic5.jpg -------------------------------------------------------------------------------- /documentation/TinyRemote_wiring.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/ATtiny13-TinyRemote/9fef78df52df0114ab77d3ac22e2b230fedd6f06/documentation/TinyRemote_wiring.png -------------------------------------------------------------------------------- /hardware/TinyRemote_T13_BOM_v1.0.tsv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/ATtiny13-TinyRemote/9fef78df52df0114ab77d3ac22e2b230fedd6f06/hardware/TinyRemote_T13_BOM_v1.0.tsv -------------------------------------------------------------------------------- /hardware/TinyRemote_T13_BOM_v1.1.tsv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/ATtiny13-TinyRemote/9fef78df52df0114ab77d3ac22e2b230fedd6f06/hardware/TinyRemote_T13_BOM_v1.1.tsv -------------------------------------------------------------------------------- /hardware/TinyRemote_T13_gerber_v1.0.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/ATtiny13-TinyRemote/9fef78df52df0114ab77d3ac22e2b230fedd6f06/hardware/TinyRemote_T13_gerber_v1.0.zip -------------------------------------------------------------------------------- /hardware/TinyRemote_T13_gerber_v1.1.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/ATtiny13-TinyRemote/9fef78df52df0114ab77d3ac22e2b230fedd6f06/hardware/TinyRemote_T13_gerber_v1.1.zip -------------------------------------------------------------------------------- /hardware/TinyRemote_T13_schematic_v1.0.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/ATtiny13-TinyRemote/9fef78df52df0114ab77d3ac22e2b230fedd6f06/hardware/TinyRemote_T13_schematic_v1.0.pdf -------------------------------------------------------------------------------- /hardware/TinyRemote_T13_schematic_v1.1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/ATtiny13-TinyRemote/9fef78df52df0114ab77d3ac22e2b230fedd6f06/hardware/TinyRemote_T13_schematic_v1.1.pdf -------------------------------------------------------------------------------- /software/MULTI_4_buttons/TinyRemote_t13_MULTI_4B.ino: -------------------------------------------------------------------------------- 1 | // tinyIRremote for ATtiny13A - Multi Protocol, 4 Buttons 2 | // 3 | // IR remote control using an ATtiny 13A. Timer0 generates a carrier 4 | // frequency with a duty cycle of 25% on the output pin to the 5 | // IR LED. The signal is modulated by toggling the pin to input/output. 6 | // 7 | // The code utilizes the sleep mode power down function. The device will 8 | // work several months on a CR2032 battery. 9 | // 10 | // +-\/-+ 11 | // KEY5 --- A0 (D5) PB5 1| |8 Vcc 12 | // KEY3 --- A3 (D3) PB3 2| |7 PB2 (D2) A1 --- KEY2 13 | // KEY4 --- A2 (D4) PB4 3| |6 PB1 (D1) ------ IR LED 14 | // GND 4| |5 PB0 (D0) ------ KEY1 15 | // +----+ 16 | // 17 | // Controller: ATtiny13 18 | // Core: MicroCore (https://github.com/MCUdude/MicroCore) 19 | // Clockspeed: 1.2 MHz internal 20 | // BOD: BOD disabled (energy saving) 21 | // Timing: Micros disabled (Timer0 is in use) 22 | // 23 | // Note: The internal oscillator may need to be calibrated for the device 24 | // to function properly. 25 | // 26 | // 2019 by Stefan Wagner 27 | // Project Files (EasyEDA): https://easyeda.com/wagiminator 28 | // Project Files (Github): https://github.com/wagiminator 29 | // License: http://creativecommons.org/licenses/by-sa/3.0/ 30 | // 31 | // Based on the work of: 32 | // - San Bergmans (https://www.sbprojects.net/knowledge/ir/index.php), 33 | // - Christoph Niessen (http://chris.cnie.de/avr/tcm231421.html), 34 | // - David Johnson-Davies (http://www.technoblogy.com/show?UVE). 35 | 36 | 37 | // oscillator calibration value (uncomment and set if necessary) 38 | //#define OSCCAL_VAL 0x48 39 | 40 | // libraries 41 | #include 42 | #include 43 | #include 44 | #include 45 | 46 | // assign IR commands to the keys 47 | #define KEY1 NEC_sendCode(0x04,0x08) // LG TV Power: addr 0x04, cmd 0x08 48 | #define KEY2 RC5_sendCode(0x00,0x0B) // Philips TV Power: addr 0x00, cmd 0x0B 49 | #define KEY3 SON_sendCode(0x01,0x15,12) // Sony TV Power: addr 0x01, cmd 0x15, 12-bit version 50 | #define KEY4 SAM_sendCode(0x07,0x02) // Samsung TV Power: addr: 07, cmd: 02 51 | #define KEY5 _delay_ms(10) // nothing 52 | 53 | // macros to switch on/off IR LED 54 | #define IRon() DDRB |= 0b00000010 // PB1 as output = IR at OC0B (38 kHz) 55 | #define IRoff() DDRB &= 0b11111101 // PB1 as input = LED off 56 | 57 | // button mask 58 | #define BT_MASK 0b00011101 // port mask for button pins 59 | 60 | // ----------------------------------------------------------------------------------- 61 | // NEC Protocol Implementation 62 | // ----------------------------------------------------------------------------------- 63 | // 64 | // The NEC protocol uses pulse distance modulation. 65 | // 66 | // +---------+ +-+ +-+ +-+ +-+ +- ON 67 | // | | | | | | | | | | | bit0: 562.5us 68 | // | 9ms |4.5ms| |0| | 1 | | 1 | |0| ... 69 | // | | | | | | | | | | | bit1: 1687.5us 70 | // ------+ +-----+ +-+ +---+ +---+ +-+ OFF 71 | // 72 | // IR telegram starts with a 9ms leading burst followed by a 4.5ms pause. 73 | // Afterwards 4 data bytes are transmitted, least significant bit first. 74 | // A "0" bit is a 562.5us burst followed by a 562.5us pause, a "1" bit is 75 | // a 562.5us burst followed by a 1687.5us pause. A final 562.5us burst 76 | // signifies the end of the transmission. The four data bytes are in order: 77 | // - the 8-bit address for the receiving device, 78 | // - the 8-bit logical inverse of the address, 79 | // - the 8-bit command and 80 | // - the 8-bit logical inverse of the command. 81 | // The Extended NEC protocol uses 16-bit addresses. Instead of sending an 82 | // 8-bit address and its logically inverse, first the low byte and then the 83 | // high byte of the address is transmitted. 84 | // 85 | // If the key on the remote controller is kept depressed, a repeat code 86 | // will be issued consisting of a 9ms leading burst, a 2.25ms pause and 87 | // a 562.5us burst to mark the end. The repeat code will continue to be 88 | // sent out at 108ms intervals, until the key is finally released. 89 | 90 | // define values for 38kHz PWM frequency and 25% duty cycle 91 | #define NEC_TOP 31 // 1200kHz / 38kHz - 1 = 31 92 | #define NEC_DUTY 7 // 1200kHz / 38kHz / 4 - 1 = 7 93 | 94 | // macros to modulate the signals according to NEC protocol with compensated timings 95 | #define NEC_startPulse() {IRon(); _delay_us(9000); IRoff(); _delay_us(4500);} 96 | #define NEC_repeatPulse() {IRon(); _delay_us(9000); IRoff(); _delay_us(2250);} 97 | #define NEC_normalPulse() {IRon(); _delay_us( 562); IRoff(); _delay_us( 557);} 98 | #define NEC_bit1Pause() _delay_us(1120) // 1687.5us - 562.5us = 1125us 99 | #define NEC_repeatCode() {_delay_ms(40); NEC_repeatPulse(); NEC_normalPulse(); _delay_ms(56);} 100 | 101 | // send a single byte via IR 102 | void NEC_sendByte(uint8_t value) { 103 | for (uint8_t i=8; i; i--, value>>=1) { // send 8 bits, LSB first 104 | NEC_normalPulse(); // 562us burst, 562us pause 105 | if (value & 1) NEC_bit1Pause(); // extend pause if bit is 1 106 | } 107 | } 108 | 109 | // send complete telegram (start frame + address + command) via IR 110 | void NEC_sendCode(uint16_t addr, uint8_t cmd) { 111 | // prepare carrier wave 112 | OCR0A = NEC_TOP; // set PWM frequency 113 | OCR0B = NEC_DUTY; // set duty cycle 114 | 115 | // send telegram 116 | NEC_startPulse(); // 9ms burst + 4.5ms pause to signify start of transmission 117 | if (addr > 0xFF) { // if extended NEC protocol (16-bit address): 118 | NEC_sendByte(addr); // send address low byte 119 | NEC_sendByte(addr >> 8); // send address high byte 120 | } else { // if standard NEC protocol (8-bit address): 121 | NEC_sendByte(addr); // send address byte 122 | NEC_sendByte(~addr); // send inverse of address byte 123 | } 124 | NEC_sendByte(cmd); // send command byte 125 | NEC_sendByte(~cmd); // send inverse of command byte 126 | NEC_normalPulse(); // 562us burst to signify end of transmission 127 | while(~PINB & BT_MASK) NEC_repeatCode(); // send repeat command until button is released 128 | } 129 | 130 | // ----------------------------------------------------------------------------------- 131 | // SAMSUNG Protocol Implementation 132 | // ----------------------------------------------------------------------------------- 133 | // 134 | // The SAMSUNG protocol corresponds to the NEC protocol, except that the start pulse is 135 | // 4.5ms long and the address byte is sent twice. The telegram is repeated every 108ms 136 | // as long as the button is pressed. 137 | 138 | #define SAM_startPulse() {IRon(); _delay_us(4500); IRoff(); _delay_us(4500);} 139 | #define SAM_repeatPause() _delay_ms(44) 140 | 141 | // send complete telegram (start frame + address + command) via IR 142 | void SAM_sendCode(uint8_t addr, uint8_t cmd) { 143 | // prepare carrier wave 144 | OCR0A = NEC_TOP; // set PWM frequency 145 | OCR0B = NEC_DUTY; // set duty cycle 146 | 147 | // send telegram 148 | do { 149 | SAM_startPulse(); // 9ms burst + 4.5ms pause to signify start of transmission 150 | NEC_sendByte(addr); // send address byte 151 | NEC_sendByte(addr); // send address byte again 152 | NEC_sendByte(cmd); // send command byte 153 | NEC_sendByte(~cmd); // send inverse of command byte 154 | NEC_normalPulse(); // 562us burst to signify end of transmission 155 | SAM_repeatPause(); // wait for next repeat 156 | } while(~PINB & BT_MASK); // repeat sending until button is released 157 | } 158 | 159 | // ----------------------------------------------------------------------------------- 160 | // RC-5 Protocol Implementation 161 | // ----------------------------------------------------------------------------------- 162 | // 163 | // The RC-5 protocol uses bi-phase modulation (Manchester coding). 164 | // 165 | // +-------+ +-------+ ON 166 | // | | 167 | // 889us | 889us 889us | 889us 168 | // | | 169 | // +-------+ +-------+ OFF 170 | // 171 | // |<-- Bit "0" -->| |<-- Bit "1" -->| 172 | // 173 | // IR telegram starts with two start bits. The first bit is always "1", 174 | // the second bit is "1" in the original protocol and inverted 7th bit 175 | // of the command in the extended RC-5 protocol. The third bit toggles 176 | // after each button release. The next five bits represent the device 177 | // address, MSB first and the last six bits represent the command, MSB 178 | // first. 179 | // 180 | // As long as a key remains down the telegram will be repeated every 181 | // 114ms without changing the toggle bit. 182 | 183 | // define values for 36kHz PWM frequency and 25% duty cycle 184 | #define RC5_TOP 32 // 1200kHz / 36kHz - 1 = 32 185 | #define RC5_DUTY 7 // 1200kHz / 36kHz / 4 - 1 = 7 186 | 187 | // macros to modulate the signals according to RC-5 protocol with compensated timings 188 | #define RC5_bit0Pulse() {IRon(); _delay_us(889); IRoff(); _delay_us(884);} 189 | #define RC5_bit1Pulse() {IRoff(); _delay_us(889); IRon(); _delay_us(884);} 190 | #define RC5_repeatPause() _delay_ms(89) // 114ms - 14 * 2 * 889us 191 | 192 | // bitmasks 193 | #define RC5_startBit 0b0010000000000000 194 | #define RC5_cmdBit7 0b0001000000000000 195 | #define RC5_toggleBit 0b0000100000000000 196 | 197 | // toggle variable 198 | uint8_t RC5_toggle = 0; 199 | 200 | // send complete telegram (startbits + togglebit + address + command) via IR 201 | void RC5_sendCode(uint8_t addr, uint8_t cmd) { 202 | // prepare carrier wave 203 | OCR0A = RC5_TOP; // set PWM frequency 204 | OCR0B = RC5_DUTY; // set duty cycle 205 | 206 | // prepare the message 207 | uint16_t message = addr << 6; // shift address to the right position 208 | message |= (cmd & 0x3f); // add the low 6 bits of the command 209 | if (~cmd & 0x40) message |= RC5_cmdBit7; // add inverse of 7th command bit 210 | message |= RC5_startBit; // add start bit 211 | if (RC5_toggle) message |= RC5_toggleBit; // add toggle bit 212 | 213 | // send the message 214 | do { 215 | uint16_t bitmask = RC5_startBit; // set the bitmask to first bit to send 216 | for(uint8_t i=14; i; i--, bitmask>>=1) { // 14 bits, MSB first 217 | (message & bitmask) ? (RC5_bit1Pulse()) : (RC5_bit0Pulse()); // send the bit 218 | } 219 | IRoff(); // switch off IR LED 220 | RC5_repeatPause(); // wait for next repeat 221 | } while(~PINB & BT_MASK); // repeat sending until button is released 222 | RC5_toggle ^= 1; // toggle the toggle bit 223 | } 224 | 225 | // ----------------------------------------------------------------------------------- 226 | // SONY SIRC Protocol Implementation 227 | // ----------------------------------------------------------------------------------- 228 | // 229 | // The SONY SIRC protocol uses pulse length modulation. 230 | // 231 | // +--------------------+ +-----+ +----------+ +-- ON 232 | // | | | | | | | 233 | // | 2400us |600us|600us|600us| 1200us |600us| ... 234 | // | | | | | | | 235 | // ------+ +-----+ +-----+ +-----+ OFF 236 | // 237 | // |<------ Start Frame ----->|<- Bit=0 ->|<--- Bit=1 ---->| 238 | // 239 | // A "0" bit is a 600us burst followed by a 600us space, a "1" bit is a 240 | // 1200us burst followed by a 600us space. An IR telegram starts with a 241 | // 2400us leading burst followed by a 600us space. The command and 242 | // address bits are then transmitted, LSB first. Depending on the 243 | // protocol version, these are in detail: 244 | // - 12-bit version: 7 command bits, 5 address bits 245 | // - 15-bit version: 7 command bits, 8 address bits 246 | // - 20-bit version: 7 command bits, 5 address bits, 8 extended bits 247 | // 248 | // As long as a key remains down the message will be repeated every 45ms. 249 | 250 | // define values for 40kHz PWM frequency and 25% duty cycle 251 | #define SON_TOP 29 // 1200kHz / 40kHz - 1 = 29 252 | #define SON_DUTY 7 // 1200kHz / 40kHz / 4 - 1 = 7 253 | 254 | // macros to modulate the signals according to SONY protocol with compensated timings 255 | #define SON_startPulse() {IRon(); _delay_us(2400); IRoff(); _delay_us( 595);} 256 | #define SON_bit0Pulse() {IRon(); _delay_us( 600); IRoff(); _delay_us( 595);} 257 | #define SON_bit1Pulse() {IRon(); _delay_us(1200); IRoff(); _delay_us( 595);} 258 | #define SON_repeatPause() _delay_ms(27) 259 | 260 | // send "number" of bits of "value" via IR 261 | void SON_sendByte(uint8_t value, uint8_t number) { 262 | do { // send number of bits, LSB first 263 | (value & 1) ? (SON_bit1Pulse()) : (SON_bit0Pulse()); // send bit 264 | value>>=1; // next bit 265 | } while(--number); 266 | } 267 | 268 | // send complete telegram (start frame + command + address) via IR 269 | void SON_sendCode(uint16_t addr, uint8_t cmd, uint8_t bits) { 270 | // prepare carrier wave 271 | OCR0A = SON_TOP; // set PWM frequency 272 | OCR0B = SON_DUTY; // set duty cycle 273 | 274 | // send telegram 275 | do { 276 | SON_startPulse(); // signify start of transmission 277 | SON_sendByte(cmd, 7); // send 7 command bits 278 | switch (bits) { 279 | case 12: SON_sendByte(addr, 5); break; // 12-bit version: send 5 address bits 280 | case 15: SON_sendByte(addr, 8); break; // 15-bit version: send 8 address bits 281 | case 20: SON_sendByte(addr, 8); SON_sendByte(addr>>8, 5); break; // 20-bit: 13 bits 282 | default: break; 283 | } 284 | SON_repeatPause(); // wait until next repeat 285 | } while (~PINB & BT_MASK); // repeat sending until button is released 286 | } 287 | 288 | // ----------------------------------------------------------------------------------- 289 | // Main Function 290 | // ----------------------------------------------------------------------------------- 291 | 292 | // main function 293 | int main(void) { 294 | // set oscillator calibration value 295 | #ifdef OSCCAL_VAL 296 | OSCCAL = OSCCAL_VAL; // set the value if defined above 297 | #endif 298 | 299 | // setup pins 300 | DDRB = 0b00000000; // all pins are input pins by now 301 | PORTB = BT_MASK; // pull-up for button pins 302 | 303 | // set timer0 to toggle IR pin at 38 kHz 304 | TCCR0A = 0b00100011; // PWM on OC0B (PB1) 305 | TCCR0B = 0b00001001; // no prescaler 306 | 307 | // setup pin change interrupt 308 | GIMSK = 0b00100000; // turn on pin change interrupts 309 | PCMSK = BT_MASK; // turn on interrupt on button pins 310 | SREG |= 0b10000000; // enable global interrupts 311 | 312 | // disable unused peripherals and set sleep mode to save power 313 | ADCSRA = 0b00000000; // disable ADC 314 | ACSR = 0b10000000; // disable analog comperator 315 | PRR = 0b00000001; // shut down ADC 316 | set_sleep_mode(SLEEP_MODE_PWR_DOWN); // set sleep mode to power down 317 | 318 | // main loop 319 | while(1) { 320 | sleep_mode(); // sleep until button is pressed 321 | _delay_ms(1); // debounce 322 | uint8_t buttons = ~PINB & BT_MASK; // read button pins 323 | switch (buttons) { // send corresponding IR code 324 | case 0b00000001: KEY1; break; 325 | case 0b00000100: KEY2; break; 326 | case 0b00001000: KEY3; break; 327 | case 0b00010000: KEY4; break; 328 | case 0b00100000: KEY5; break; 329 | default: break; 330 | } 331 | } 332 | } 333 | 334 | // pin change interrupt service routine 335 | EMPTY_INTERRUPT (PCINT0_vect); // nothing to be done here, just wake up from sleep 336 | -------------------------------------------------------------------------------- /software/MULTI_4_buttons/main.c: -------------------------------------------------------------------------------- 1 | // tinyIRremote for ATtiny13A - Multi Protocol, 4 Buttons 2 | // 3 | // IR remote control using an ATtiny 13A. Timer0 generates a carrier 4 | // frequency with a duty cycle of 25% on the output pin to the 5 | // IR LED. The signal is modulated by toggling the pin to input/output. 6 | // 7 | // The code utilizes the sleep mode power down function. The device will 8 | // work several months on a CR2032 battery. 9 | // 10 | // +-\/-+ 11 | // KEY5 --- A0 (D5) PB5 1| |8 Vcc 12 | // KEY3 --- A3 (D3) PB3 2| |7 PB2 (D2) A1 --- KEY2 13 | // KEY4 --- A2 (D4) PB4 3| |6 PB1 (D1) ------ IR LED 14 | // GND 4| |5 PB0 (D0) ------ KEY1 15 | // +----+ 16 | // 17 | // Controller: ATtiny13A 18 | // Clockspeed: 1.2 MHz internal 19 | // 20 | // Note: The internal oscillator may need to be calibrated for the device 21 | // to function properly. 22 | // 23 | // 2019 by Stefan Wagner 24 | // Project Files (EasyEDA): https://easyeda.com/wagiminator 25 | // Project Files (Github): https://github.com/wagiminator 26 | // License: http://creativecommons.org/licenses/by-sa/3.0/ 27 | // 28 | // Based on the work of: 29 | // - San Bergmans (https://www.sbprojects.net/knowledge/ir/index.php), 30 | // - Christoph Niessen (http://chris.cnie.de/avr/tcm231421.html), 31 | // - David Johnson-Davies (http://www.technoblogy.com/show?UVE). 32 | 33 | 34 | // oscillator calibration value (uncomment and set if necessary) 35 | //#define OSCCAL_VAL 0x48 36 | 37 | // libraries 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | // assign IR commands to the keys 44 | #define KEY1 NEC_sendCode(0x04,0x08) // LG TV Power: addr 0x04, cmd 0x08 45 | #define KEY2 RC5_sendCode(0x00,0x0B) // Philips TV Power: addr 0x00, cmd 0x0B 46 | #define KEY3 SON_sendCode(0x01,0x15,12) // Sony TV Power: addr 0x01, cmd 0x15, 12-bit version 47 | #define KEY4 SAM_sendCode(0x07,0x02) // Samsung TV Power: addr: 07, cmd: 02 48 | #define KEY5 _delay_ms(10) // nothing 49 | 50 | // macros to switch on/off IR LED 51 | #define IRon() DDRB |= 0b00000010 // PB1 as output = IR at OC0B (38 kHz) 52 | #define IRoff() DDRB &= 0b11111101 // PB1 as input = LED off 53 | 54 | // button mask 55 | #define BT_MASK 0b00011101 // port mask for button pins 56 | 57 | // ----------------------------------------------------------------------------------- 58 | // NEC Protocol Implementation 59 | // ----------------------------------------------------------------------------------- 60 | // 61 | // The NEC protocol uses pulse distance modulation. 62 | // 63 | // +---------+ +-+ +-+ +-+ +-+ +- ON 64 | // | | | | | | | | | | | bit0: 562.5us 65 | // | 9ms |4.5ms| |0| | 1 | | 1 | |0| ... 66 | // | | | | | | | | | | | bit1: 1687.5us 67 | // ------+ +-----+ +-+ +---+ +---+ +-+ OFF 68 | // 69 | // IR telegram starts with a 9ms leading burst followed by a 4.5ms pause. 70 | // Afterwards 4 data bytes are transmitted, least significant bit first. 71 | // A "0" bit is a 562.5us burst followed by a 562.5us pause, a "1" bit is 72 | // a 562.5us burst followed by a 1687.5us pause. A final 562.5us burst 73 | // signifies the end of the transmission. The four data bytes are in order: 74 | // - the 8-bit address for the receiving device, 75 | // - the 8-bit logical inverse of the address, 76 | // - the 8-bit command and 77 | // - the 8-bit logical inverse of the command. 78 | // The Extended NEC protocol uses 16-bit addresses. Instead of sending an 79 | // 8-bit address and its logically inverse, first the low byte and then the 80 | // high byte of the address is transmitted. 81 | // 82 | // If the key on the remote controller is kept depressed, a repeat code 83 | // will be issued consisting of a 9ms leading burst, a 2.25ms pause and 84 | // a 562.5us burst to mark the end. The repeat code will continue to be 85 | // sent out at 108ms intervals, until the key is finally released. 86 | 87 | // define values for 38kHz PWM frequency and 25% duty cycle 88 | #define NEC_TOP 31 // 1200kHz / 38kHz - 1 = 31 89 | #define NEC_DUTY 7 // 1200kHz / 38kHz / 4 - 1 = 7 90 | 91 | // macros to modulate the signals according to NEC protocol with compensated timings 92 | #define NEC_startPulse() {IRon(); _delay_us(9000); IRoff(); _delay_us(4500);} 93 | #define NEC_repeatPulse() {IRon(); _delay_us(9000); IRoff(); _delay_us(2250);} 94 | #define NEC_normalPulse() {IRon(); _delay_us( 562); IRoff(); _delay_us( 557);} 95 | #define NEC_bit1Pause() _delay_us(1120) // 1687.5us - 562.5us = 1125us 96 | #define NEC_repeatCode() {_delay_ms(40); NEC_repeatPulse(); NEC_normalPulse(); _delay_ms(56);} 97 | 98 | // send a single byte via IR 99 | void NEC_sendByte(uint8_t value) { 100 | for (uint8_t i=8; i; i--, value>>=1) { // send 8 bits, LSB first 101 | NEC_normalPulse(); // 562us burst, 562us pause 102 | if (value & 1) NEC_bit1Pause(); // extend pause if bit is 1 103 | } 104 | } 105 | 106 | // send complete telegram (start frame + address + command) via IR 107 | void NEC_sendCode(uint16_t addr, uint8_t cmd) { 108 | // prepare carrier wave 109 | OCR0A = NEC_TOP; // set PWM frequency 110 | OCR0B = NEC_DUTY; // set duty cycle 111 | 112 | // send telegram 113 | NEC_startPulse(); // 9ms burst + 4.5ms pause to signify start of transmission 114 | if (addr > 0xFF) { // if extended NEC protocol (16-bit address): 115 | NEC_sendByte(addr); // send address low byte 116 | NEC_sendByte(addr >> 8); // send address high byte 117 | } else { // if standard NEC protocol (8-bit address): 118 | NEC_sendByte(addr); // send address byte 119 | NEC_sendByte(~addr); // send inverse of address byte 120 | } 121 | NEC_sendByte(cmd); // send command byte 122 | NEC_sendByte(~cmd); // send inverse of command byte 123 | NEC_normalPulse(); // 562us burst to signify end of transmission 124 | while(~PINB & BT_MASK) NEC_repeatCode(); // send repeat command until button is released 125 | } 126 | 127 | // ----------------------------------------------------------------------------------- 128 | // SAMSUNG Protocol Implementation 129 | // ----------------------------------------------------------------------------------- 130 | // 131 | // The SAMSUNG protocol corresponds to the NEC protocol, except that the start pulse is 132 | // 4.5ms long and the address byte is sent twice. The telegram is repeated every 108ms 133 | // as long as the button is pressed. 134 | 135 | #define SAM_startPulse() {IRon(); _delay_us(4500); IRoff(); _delay_us(4500);} 136 | #define SAM_repeatPause() _delay_ms(44) 137 | 138 | // send complete telegram (start frame + address + command) via IR 139 | void SAM_sendCode(uint8_t addr, uint8_t cmd) { 140 | // prepare carrier wave 141 | OCR0A = NEC_TOP; // set PWM frequency 142 | OCR0B = NEC_DUTY; // set duty cycle 143 | 144 | // send telegram 145 | do { 146 | SAM_startPulse(); // 9ms burst + 4.5ms pause to signify start of transmission 147 | NEC_sendByte(addr); // send address byte 148 | NEC_sendByte(addr); // send address byte again 149 | NEC_sendByte(cmd); // send command byte 150 | NEC_sendByte(~cmd); // send inverse of command byte 151 | NEC_normalPulse(); // 562us burst to signify end of transmission 152 | SAM_repeatPause(); // wait for next repeat 153 | } while(~PINB & BT_MASK); // repeat sending until button is released 154 | } 155 | 156 | // ----------------------------------------------------------------------------------- 157 | // RC-5 Protocol Implementation 158 | // ----------------------------------------------------------------------------------- 159 | // 160 | // The RC-5 protocol uses bi-phase modulation (Manchester coding). 161 | // 162 | // +-------+ +-------+ ON 163 | // | | 164 | // 889us | 889us 889us | 889us 165 | // | | 166 | // +-------+ +-------+ OFF 167 | // 168 | // |<-- Bit "0" -->| |<-- Bit "1" -->| 169 | // 170 | // IR telegram starts with two start bits. The first bit is always "1", 171 | // the second bit is "1" in the original protocol and inverted 7th bit 172 | // of the command in the extended RC-5 protocol. The third bit toggles 173 | // after each button release. The next five bits represent the device 174 | // address, MSB first and the last six bits represent the command, MSB 175 | // first. 176 | // 177 | // As long as a key remains down the telegram will be repeated every 178 | // 114ms without changing the toggle bit. 179 | 180 | // define values for 36kHz PWM frequency and 25% duty cycle 181 | #define RC5_TOP 32 // 1200kHz / 36kHz - 1 = 32 182 | #define RC5_DUTY 7 // 1200kHz / 36kHz / 4 - 1 = 7 183 | 184 | // macros to modulate the signals according to RC-5 protocol with compensated timings 185 | #define RC5_bit0Pulse() {IRon(); _delay_us(889); IRoff(); _delay_us(884);} 186 | #define RC5_bit1Pulse() {IRoff(); _delay_us(889); IRon(); _delay_us(884);} 187 | #define RC5_repeatPause() _delay_ms(89) // 114ms - 14 * 2 * 889us 188 | 189 | // bitmasks 190 | #define RC5_startBit 0b0010000000000000 191 | #define RC5_cmdBit7 0b0001000000000000 192 | #define RC5_toggleBit 0b0000100000000000 193 | 194 | // toggle variable 195 | uint8_t RC5_toggle = 0; 196 | 197 | // send complete telegram (startbits + togglebit + address + command) via IR 198 | void RC5_sendCode(uint8_t addr, uint8_t cmd) { 199 | // prepare carrier wave 200 | OCR0A = RC5_TOP; // set PWM frequency 201 | OCR0B = RC5_DUTY; // set duty cycle 202 | 203 | // prepare the message 204 | uint16_t message = addr << 6; // shift address to the right position 205 | message |= (cmd & 0x3f); // add the low 6 bits of the command 206 | if (~cmd & 0x40) message |= RC5_cmdBit7; // add inverse of 7th command bit 207 | message |= RC5_startBit; // add start bit 208 | if (RC5_toggle) message |= RC5_toggleBit; // add toggle bit 209 | 210 | // send the message 211 | do { 212 | uint16_t bitmask = RC5_startBit; // set the bitmask to first bit to send 213 | for(uint8_t i=14; i; i--, bitmask>>=1) { // 14 bits, MSB first 214 | (message & bitmask) ? (RC5_bit1Pulse()) : (RC5_bit0Pulse()); // send the bit 215 | } 216 | IRoff(); // switch off IR LED 217 | RC5_repeatPause(); // wait for next repeat 218 | } while(~PINB & BT_MASK); // repeat sending until button is released 219 | RC5_toggle ^= 1; // toggle the toggle bit 220 | } 221 | 222 | // ----------------------------------------------------------------------------------- 223 | // SONY SIRC Protocol Implementation 224 | // ----------------------------------------------------------------------------------- 225 | // 226 | // The SONY SIRC protocol uses pulse length modulation. 227 | // 228 | // +--------------------+ +-----+ +----------+ +-- ON 229 | // | | | | | | | 230 | // | 2400us |600us|600us|600us| 1200us |600us| ... 231 | // | | | | | | | 232 | // ------+ +-----+ +-----+ +-----+ OFF 233 | // 234 | // |<------ Start Frame ----->|<- Bit=0 ->|<--- Bit=1 ---->| 235 | // 236 | // A "0" bit is a 600us burst followed by a 600us space, a "1" bit is a 237 | // 1200us burst followed by a 600us space. An IR telegram starts with a 238 | // 2400us leading burst followed by a 600us space. The command and 239 | // address bits are then transmitted, LSB first. Depending on the 240 | // protocol version, these are in detail: 241 | // - 12-bit version: 7 command bits, 5 address bits 242 | // - 15-bit version: 7 command bits, 8 address bits 243 | // - 20-bit version: 7 command bits, 5 address bits, 8 extended bits 244 | // 245 | // As long as a key remains down the message will be repeated every 45ms. 246 | 247 | // define values for 40kHz PWM frequency and 25% duty cycle 248 | #define SON_TOP 29 // 1200kHz / 40kHz - 1 = 29 249 | #define SON_DUTY 7 // 1200kHz / 40kHz / 4 - 1 = 7 250 | 251 | // macros to modulate the signals according to SONY protocol with compensated timings 252 | #define SON_startPulse() {IRon(); _delay_us(2400); IRoff(); _delay_us( 595);} 253 | #define SON_bit0Pulse() {IRon(); _delay_us( 600); IRoff(); _delay_us( 595);} 254 | #define SON_bit1Pulse() {IRon(); _delay_us(1200); IRoff(); _delay_us( 595);} 255 | #define SON_repeatPause() _delay_ms(27) 256 | 257 | // send "number" of bits of "value" via IR 258 | void SON_sendByte(uint8_t value, uint8_t number) { 259 | do { // send number of bits, LSB first 260 | (value & 1) ? (SON_bit1Pulse()) : (SON_bit0Pulse()); // send bit 261 | value>>=1; // next bit 262 | } while(--number); 263 | } 264 | 265 | // send complete telegram (start frame + command + address) via IR 266 | void SON_sendCode(uint16_t addr, uint8_t cmd, uint8_t bits) { 267 | // prepare carrier wave 268 | OCR0A = SON_TOP; // set PWM frequency 269 | OCR0B = SON_DUTY; // set duty cycle 270 | 271 | // send telegram 272 | do { 273 | SON_startPulse(); // signify start of transmission 274 | SON_sendByte(cmd, 7); // send 7 command bits 275 | switch (bits) { 276 | case 12: SON_sendByte(addr, 5); break; // 12-bit version: send 5 address bits 277 | case 15: SON_sendByte(addr, 8); break; // 15-bit version: send 8 address bits 278 | case 20: SON_sendByte(addr, 8); SON_sendByte(addr>>8, 5); break; // 20-bit: 13 bits 279 | default: break; 280 | } 281 | SON_repeatPause(); // wait until next repeat 282 | } while (~PINB & BT_MASK); // repeat sending until button is released 283 | } 284 | 285 | // ----------------------------------------------------------------------------------- 286 | // Main Function 287 | // ----------------------------------------------------------------------------------- 288 | 289 | // main function 290 | int main(void) { 291 | // set oscillator calibration value 292 | #ifdef OSCCAL_VAL 293 | OSCCAL = OSCCAL_VAL; // set the value if defined above 294 | #endif 295 | 296 | // setup pins 297 | DDRB = 0b00000000; // all pins are input pins by now 298 | PORTB = BT_MASK; // pull-up for button pins 299 | 300 | // set timer0 to toggle IR pin at 38 kHz 301 | TCCR0A = 0b00100011; // PWM on OC0B (PB1) 302 | TCCR0B = 0b00001001; // no prescaler 303 | 304 | // setup pin change interrupt 305 | GIMSK = 0b00100000; // turn on pin change interrupts 306 | PCMSK = BT_MASK; // turn on interrupt on button pins 307 | SREG |= 0b10000000; // enable global interrupts 308 | 309 | // disable unused peripherals and set sleep mode to save power 310 | ADCSRA = 0b00000000; // disable ADC 311 | ACSR = 0b10000000; // disable analog comperator 312 | PRR = 0b00000001; // shut down ADC 313 | set_sleep_mode(SLEEP_MODE_PWR_DOWN); // set sleep mode to power down 314 | 315 | // main loop 316 | while(1) { 317 | sleep_mode(); // sleep until button is pressed 318 | _delay_ms(1); // debounce 319 | uint8_t buttons = ~PINB & BT_MASK; // read button pins 320 | switch (buttons) { // send corresponding IR code 321 | case 0b00000001: KEY1; break; 322 | case 0b00000100: KEY2; break; 323 | case 0b00001000: KEY3; break; 324 | case 0b00010000: KEY4; break; 325 | case 0b00100000: KEY5; break; 326 | default: break; 327 | } 328 | } 329 | } 330 | 331 | // pin change interrupt service routine 332 | EMPTY_INTERRUPT (PCINT0_vect); // nothing to be done here, just wake up from sleep 333 | -------------------------------------------------------------------------------- /software/MULTI_4_buttons/makefile: -------------------------------------------------------------------------------- 1 | # Project: tinyRemote - 4 buttons 2 | # Author: Stefan Wagner 3 | # Year: 2019 4 | # URL: https://easyeda.com/wagiminator 5 | # https://github.com/wagiminator 6 | # 7 | # Type "make help" in the command line. 8 | 9 | # Compiler Options 10 | DEVICE = attiny13a 11 | CLOCK = 1200000 12 | 13 | # Programmer Options 14 | PROGRMR = usbasp 15 | TGTDEV = attiny13 16 | LFUSE = 0x2a 17 | HFUSE = 0xff 18 | 19 | # Objects 20 | OBJECTS = main.o 21 | 22 | # Commands 23 | AVRDUDE = avrdude -c $(PROGRMR) -p $(TGTDEV) 24 | COMPILE = avr-gcc -Wall -Os -flto -mmcu=$(DEVICE) -DF_CPU=$(CLOCK) -DDEBUG_LEVEL=0 25 | CLEAN = rm -f main.lst main.obj main.cof main.list main.map main.eep.hex *.o main.s 26 | 27 | # Symbolic Targets 28 | all: help 29 | 30 | help: 31 | @echo "Use the following commands:" 32 | @echo "make hex compile and build main.hex for $(DEVICE)" 33 | @echo "make asm compile and disassemble to main.asm for $(DEVICE)" 34 | @echo "make bin compile and build main.bin for $(DEVICE)" 35 | @echo "make upload compile and upload to $(DEVICE) using $(PROGRMR)" 36 | @echo "make fuses burn fuses of $(DEVICE) using $(PROGRMR) programmer" 37 | @echo "make install compile, upload and burn fuses for $(DEVICE)" 38 | @echo "make clean remove all build files" 39 | 40 | bin: $(OBJECTS) 41 | @echo "Building main.bin ..." 42 | @$(COMPILE) -o main.bin $(OBJECTS) 43 | @echo "Removing temporary files ..." 44 | @$(CLEAN) 45 | 46 | hex: bin 47 | @echo "Building main.hex ..." 48 | @avr-objcopy -j .text -j .data -O ihex main.bin main.hex 49 | @echo "FLASH: $(shell avr-size -d main.bin | awk '/[0-9]/ {print $$1 + $$2}') bytes" 50 | @echo "SRAM: $(shell avr-size -d main.bin | awk '/[0-9]/ {print $$2 + $$3}') bytes" 51 | @echo "Removing main.bin ..." 52 | @rm -f main.bin 53 | 54 | asm: bin 55 | @echo "Disassembling to main.asm ..." 56 | @avr-objdump -d main.bin > main.asm 57 | @echo "Removing main.bin ..." 58 | @rm -f main.bin 59 | 60 | upload: hex 61 | @echo "Uploading to $(TGTDEV) ..." 62 | @$(AVRDUDE) -U flash:w:main.hex:i 63 | 64 | fuses: 65 | @echo "Burning fuses of $(TGTDEV) ..." 66 | @$(AVRDUDE) -U hfuse:w:$(HFUSE):m -U lfuse:w:$(LFUSE):m 67 | 68 | install: upload fuses 69 | 70 | clean: 71 | @echo "Cleaning all up ..." 72 | @$(CLEAN) 73 | @rm -f main.bin main.hex main.asm 74 | 75 | .c.o: 76 | @echo "Compiling $< for $(DEVICE) @ $(CLOCK)Hz ..." 77 | @$(COMPILE) -c $< -o $@ 78 | 79 | .S.o: 80 | @echo "Compiling $< for $(DEVICE) @ $(CLOCK)Hz ..." 81 | @$(COMPILE) -x assembler-with-cpp -c $< -o $@ 82 | 83 | .c.s: 84 | @echo "Compiling $< for $(DEVICE) @ $(CLOCK)Hz ..." 85 | @$(COMPILE) -S $< -o $@ 86 | -------------------------------------------------------------------------------- /software/MULTI_5_buttons/TinyRemote_t13_MULTI_5B.ino: -------------------------------------------------------------------------------- 1 | // tinyIRremote for ATtiny13A - Multi Protocol, 5 Buttons 2 | // 3 | // IR remote control using an ATtiny 13A. Timer0 generates a carrier 4 | // frequency with a duty cycle of 25% on the output pin to the 5 | // IR LED. The signal is modulated by toggling the pin to input/output. 6 | // 7 | // The code utilizes the sleep mode power down function. The device will 8 | // work several months on a CR2032 battery. 9 | // 10 | // +-\/-+ 11 | // KEY5 --- A0 (D5) PB5 1| |8 Vcc 12 | // KEY3 --- A3 (D3) PB3 2| |7 PB2 (D2) A1 --- KEY2 13 | // KEY4 --- A2 (D4) PB4 3| |6 PB1 (D1) ------ IR LED 14 | // GND 4| |5 PB0 (D0) ------ KEY1 15 | // +----+ 16 | // 17 | // Controller: ATtiny13 18 | // Core: MicroCore (https://github.com/MCUdude/MicroCore) 19 | // Clockspeed: 1.2 MHz internal 20 | // BOD: BOD disabled (energy saving) 21 | // Timing: Micros disabled (Timer0 is in use) 22 | // 23 | // Reset pin must be disabled by writing respective fuse after uploading the code: 24 | // avrdude -p attiny13 -c usbasp -U lfuse:w:0x2a:m -U hfuse:w:0xfe:m 25 | // Warning: You will need a high voltage fuse resetter to undo this change! 26 | // 27 | // Note: The internal oscillator may need to be calibrated for the device 28 | // to function properly. 29 | // 30 | // 2019 by Stefan Wagner 31 | // Project Files (EasyEDA): https://easyeda.com/wagiminator 32 | // Project Files (Github): https://github.com/wagiminator 33 | // License: http://creativecommons.org/licenses/by-sa/3.0/ 34 | // 35 | // Based on the work of: 36 | // - San Bergmans (https://www.sbprojects.net/knowledge/ir/index.php), 37 | // - Christoph Niessen (http://chris.cnie.de/avr/tcm231421.html), 38 | // - David Johnson-Davies (http://www.technoblogy.com/show?UVE). 39 | 40 | 41 | // oscillator calibration value (uncomment and set if necessary) 42 | //#define OSCCAL_VAL 0x48 43 | 44 | // libraries 45 | #include 46 | #include 47 | #include 48 | #include 49 | 50 | // assign IR commands to the keys 51 | #define KEY1 NEC_sendCode(0x04,0x08) // LG TV Power: addr 0x04, cmd 0x08 52 | #define KEY2 RC5_sendCode(0x00,0x0B) // Philips TV Power: addr 0x00, cmd 0x0B 53 | #define KEY3 SON_sendCode(0x01,0x15,12) // Sony TV Power: addr 0x01, cmd 0x15, 12-bit version 54 | #define KEY4 SAM_sendCode(0x07,0x02) // Samsung TV Power: addr: 07, cmd: 02 55 | #define KEY5 _delay_ms(10) // nothing 56 | 57 | // macros to switch on/off IR LED 58 | #define IRon() DDRB |= 0b00000010 // PB1 as output = IR at OC0B (38 kHz) 59 | #define IRoff() DDRB &= 0b11111101 // PB1 as input = LED off 60 | 61 | // button mask 62 | #define BT_MASK 0b00111101 // port mask for button pins 63 | 64 | // ----------------------------------------------------------------------------------- 65 | // NEC Protocol Implementation 66 | // ----------------------------------------------------------------------------------- 67 | // 68 | // The NEC protocol uses pulse distance modulation. 69 | // 70 | // +---------+ +-+ +-+ +-+ +-+ +- ON 71 | // | | | | | | | | | | | bit0: 562.5us 72 | // | 9ms |4.5ms| |0| | 1 | | 1 | |0| ... 73 | // | | | | | | | | | | | bit1: 1687.5us 74 | // ------+ +-----+ +-+ +---+ +---+ +-+ OFF 75 | // 76 | // IR telegram starts with a 9ms leading burst followed by a 4.5ms pause. 77 | // Afterwards 4 data bytes are transmitted, least significant bit first. 78 | // A "0" bit is a 562.5us burst followed by a 562.5us pause, a "1" bit is 79 | // a 562.5us burst followed by a 1687.5us pause. A final 562.5us burst 80 | // signifies the end of the transmission. The four data bytes are in order: 81 | // - the 8-bit address for the receiving device, 82 | // - the 8-bit logical inverse of the address, 83 | // - the 8-bit command and 84 | // - the 8-bit logical inverse of the command. 85 | // The Extended NEC protocol uses 16-bit addresses. Instead of sending an 86 | // 8-bit address and its logically inverse, first the low byte and then the 87 | // high byte of the address is transmitted. 88 | // 89 | // If the key on the remote controller is kept depressed, a repeat code 90 | // will be issued consisting of a 9ms leading burst, a 2.25ms pause and 91 | // a 562.5us burst to mark the end. The repeat code will continue to be 92 | // sent out at 108ms intervals, until the key is finally released. 93 | 94 | // define values for 38kHz PWM frequency and 25% duty cycle 95 | #define NEC_TOP 31 // 1200kHz / 38kHz - 1 = 31 96 | #define NEC_DUTY 7 // 1200kHz / 38kHz / 4 - 1 = 7 97 | 98 | // macros to modulate the signals according to NEC protocol with compensated timings 99 | #define NEC_startPulse() {IRon(); _delay_us(9000); IRoff(); _delay_us(4500);} 100 | #define NEC_repeatPulse() {IRon(); _delay_us(9000); IRoff(); _delay_us(2250);} 101 | #define NEC_normalPulse() {IRon(); _delay_us( 562); IRoff(); _delay_us( 557);} 102 | #define NEC_bit1Pause() _delay_us(1120) // 1687.5us - 562.5us = 1125us 103 | #define NEC_repeatCode() {_delay_ms(40); NEC_repeatPulse(); NEC_normalPulse(); _delay_ms(56);} 104 | 105 | // send a single byte via IR 106 | void NEC_sendByte(uint8_t value) { 107 | for (uint8_t i=8; i; i--, value>>=1) { // send 8 bits, LSB first 108 | NEC_normalPulse(); // 562us burst, 562us pause 109 | if (value & 1) NEC_bit1Pause(); // extend pause if bit is 1 110 | } 111 | } 112 | 113 | // send complete telegram (start frame + address + command) via IR 114 | void NEC_sendCode(uint16_t addr, uint8_t cmd) { 115 | // prepare carrier wave 116 | OCR0A = NEC_TOP; // set PWM frequency 117 | OCR0B = NEC_DUTY; // set duty cycle 118 | 119 | // send telegram 120 | NEC_startPulse(); // 9ms burst + 4.5ms pause to signify start of transmission 121 | if (addr > 0xFF) { // if extended NEC protocol (16-bit address): 122 | NEC_sendByte(addr); // send address low byte 123 | NEC_sendByte(addr >> 8); // send address high byte 124 | } else { // if standard NEC protocol (8-bit address): 125 | NEC_sendByte(addr); // send address byte 126 | NEC_sendByte(~addr); // send inverse of address byte 127 | } 128 | NEC_sendByte(cmd); // send command byte 129 | NEC_sendByte(~cmd); // send inverse of command byte 130 | NEC_normalPulse(); // 562us burst to signify end of transmission 131 | while(~PINB & BT_MASK) NEC_repeatCode(); // send repeat command until button is released 132 | } 133 | 134 | // ----------------------------------------------------------------------------------- 135 | // SAMSUNG Protocol Implementation 136 | // ----------------------------------------------------------------------------------- 137 | // 138 | // The SAMSUNG protocol corresponds to the NEC protocol, except that the start pulse is 139 | // 4.5ms long and the address byte is sent twice. The telegram is repeated every 108ms 140 | // as long as the button is pressed. 141 | 142 | #define SAM_startPulse() {IRon(); _delay_us(4500); IRoff(); _delay_us(4500);} 143 | #define SAM_repeatPause() _delay_ms(44) 144 | 145 | // send complete telegram (start frame + address + command) via IR 146 | void SAM_sendCode(uint8_t addr, uint8_t cmd) { 147 | // prepare carrier wave 148 | OCR0A = NEC_TOP; // set PWM frequency 149 | OCR0B = NEC_DUTY; // set duty cycle 150 | 151 | // send telegram 152 | do { 153 | SAM_startPulse(); // 9ms burst + 4.5ms pause to signify start of transmission 154 | NEC_sendByte(addr); // send address byte 155 | NEC_sendByte(addr); // send address byte again 156 | NEC_sendByte(cmd); // send command byte 157 | NEC_sendByte(~cmd); // send inverse of command byte 158 | NEC_normalPulse(); // 562us burst to signify end of transmission 159 | SAM_repeatPause(); // wait for next repeat 160 | } while(~PINB & BT_MASK); // repeat sending until button is released 161 | } 162 | 163 | // ----------------------------------------------------------------------------------- 164 | // RC-5 Protocol Implementation 165 | // ----------------------------------------------------------------------------------- 166 | // 167 | // The RC-5 protocol uses bi-phase modulation (Manchester coding). 168 | // 169 | // +-------+ +-------+ ON 170 | // | | 171 | // 889us | 889us 889us | 889us 172 | // | | 173 | // +-------+ +-------+ OFF 174 | // 175 | // |<-- Bit "0" -->| |<-- Bit "1" -->| 176 | // 177 | // IR telegram starts with two start bits. The first bit is always "1", 178 | // the second bit is "1" in the original protocol and inverted 7th bit 179 | // of the command in the extended RC-5 protocol. The third bit toggles 180 | // after each button release. The next five bits represent the device 181 | // address, MSB first and the last six bits represent the command, MSB 182 | // first. 183 | // 184 | // As long as a key remains down the telegram will be repeated every 185 | // 114ms without changing the toggle bit. 186 | 187 | // define values for 36kHz PWM frequency and 25% duty cycle 188 | #define RC5_TOP 32 // 1200kHz / 36kHz - 1 = 32 189 | #define RC5_DUTY 7 // 1200kHz / 36kHz / 4 - 1 = 7 190 | 191 | // macros to modulate the signals according to RC-5 protocol with compensated timings 192 | #define RC5_bit0Pulse() {IRon(); _delay_us(889); IRoff(); _delay_us(884);} 193 | #define RC5_bit1Pulse() {IRoff(); _delay_us(889); IRon(); _delay_us(884);} 194 | #define RC5_repeatPause() _delay_ms(89) // 114ms - 14 * 2 * 889us 195 | 196 | // bitmasks 197 | #define RC5_startBit 0b0010000000000000 198 | #define RC5_cmdBit7 0b0001000000000000 199 | #define RC5_toggleBit 0b0000100000000000 200 | 201 | // toggle variable 202 | uint8_t RC5_toggle = 0; 203 | 204 | // send complete telegram (startbits + togglebit + address + command) via IR 205 | void RC5_sendCode(uint8_t addr, uint8_t cmd) { 206 | // prepare carrier wave 207 | OCR0A = RC5_TOP; // set PWM frequency 208 | OCR0B = RC5_DUTY; // set duty cycle 209 | 210 | // prepare the message 211 | uint16_t message = addr << 6; // shift address to the right position 212 | message |= (cmd & 0x3f); // add the low 6 bits of the command 213 | if (~cmd & 0x40) message |= RC5_cmdBit7; // add inverse of 7th command bit 214 | message |= RC5_startBit; // add start bit 215 | if (RC5_toggle) message |= RC5_toggleBit; // add toggle bit 216 | 217 | // send the message 218 | do { 219 | uint16_t bitmask = RC5_startBit; // set the bitmask to first bit to send 220 | for(uint8_t i=14; i; i--, bitmask>>=1) { // 14 bits, MSB first 221 | (message & bitmask) ? (RC5_bit1Pulse()) : (RC5_bit0Pulse()); // send the bit 222 | } 223 | IRoff(); // switch off IR LED 224 | RC5_repeatPause(); // wait for next repeat 225 | } while(~PINB & BT_MASK); // repeat sending until button is released 226 | RC5_toggle ^= 1; // toggle the toggle bit 227 | } 228 | 229 | // ----------------------------------------------------------------------------------- 230 | // SONY SIRC Protocol Implementation 231 | // ----------------------------------------------------------------------------------- 232 | // 233 | // The SONY SIRC protocol uses pulse length modulation. 234 | // 235 | // +--------------------+ +-----+ +----------+ +-- ON 236 | // | | | | | | | 237 | // | 2400us |600us|600us|600us| 1200us |600us| ... 238 | // | | | | | | | 239 | // ------+ +-----+ +-----+ +-----+ OFF 240 | // 241 | // |<------ Start Frame ----->|<- Bit=0 ->|<--- Bit=1 ---->| 242 | // 243 | // A "0" bit is a 600us burst followed by a 600us space, a "1" bit is a 244 | // 1200us burst followed by a 600us space. An IR telegram starts with a 245 | // 2400us leading burst followed by a 600us space. The command and 246 | // address bits are then transmitted, LSB first. Depending on the 247 | // protocol version, these are in detail: 248 | // - 12-bit version: 7 command bits, 5 address bits 249 | // - 15-bit version: 7 command bits, 8 address bits 250 | // - 20-bit version: 7 command bits, 5 address bits, 8 extended bits 251 | // 252 | // As long as a key remains down the message will be repeated every 45ms. 253 | 254 | // define values for 40kHz PWM frequency and 25% duty cycle 255 | #define SON_TOP 29 // 1200kHz / 40kHz - 1 = 29 256 | #define SON_DUTY 7 // 1200kHz / 40kHz / 4 - 1 = 7 257 | 258 | // macros to modulate the signals according to SONY protocol with compensated timings 259 | #define SON_startPulse() {IRon(); _delay_us(2400); IRoff(); _delay_us( 595);} 260 | #define SON_bit0Pulse() {IRon(); _delay_us( 600); IRoff(); _delay_us( 595);} 261 | #define SON_bit1Pulse() {IRon(); _delay_us(1200); IRoff(); _delay_us( 595);} 262 | #define SON_repeatPause() _delay_ms(27) 263 | 264 | // send "number" of bits of "value" via IR 265 | void SON_sendByte(uint8_t value, uint8_t number) { 266 | do { // send number of bits, LSB first 267 | (value & 1) ? (SON_bit1Pulse()) : (SON_bit0Pulse()); // send bit 268 | value>>=1; // next bit 269 | } while(--number); 270 | } 271 | 272 | // send complete telegram (start frame + command + address) via IR 273 | void SON_sendCode(uint16_t addr, uint8_t cmd, uint8_t bits) { 274 | // prepare carrier wave 275 | OCR0A = SON_TOP; // set PWM frequency 276 | OCR0B = SON_DUTY; // set duty cycle 277 | 278 | // send telegram 279 | do { 280 | SON_startPulse(); // signify start of transmission 281 | SON_sendByte(cmd, 7); // send 7 command bits 282 | switch (bits) { 283 | case 12: SON_sendByte(addr, 5); break; // 12-bit version: send 5 address bits 284 | case 15: SON_sendByte(addr, 8); break; // 15-bit version: send 8 address bits 285 | case 20: SON_sendByte(addr, 8); SON_sendByte(addr>>8, 5); break; // 20-bit: 13 bits 286 | default: break; 287 | } 288 | SON_repeatPause(); // wait until next repeat 289 | } while (~PINB & BT_MASK); // repeat sending until button is released 290 | } 291 | 292 | // ----------------------------------------------------------------------------------- 293 | // Main Function 294 | // ----------------------------------------------------------------------------------- 295 | 296 | // main function 297 | int main(void) { 298 | // set oscillator calibration value 299 | #ifdef OSCCAL_VAL 300 | OSCCAL = OSCCAL_VAL; // set the value if defined above 301 | #endif 302 | 303 | // setup pins 304 | DDRB = 0b00000000; // all pins are input pins by now 305 | PORTB = BT_MASK; // pull-up for button pins 306 | 307 | // set timer0 to toggle IR pin at 38 kHz 308 | TCCR0A = 0b00100011; // PWM on OC0B (PB1) 309 | TCCR0B = 0b00001001; // no prescaler 310 | 311 | // setup pin change interrupt 312 | GIMSK = 0b00100000; // turn on pin change interrupts 313 | PCMSK = BT_MASK; // turn on interrupt on button pins 314 | SREG |= 0b10000000; // enable global interrupts 315 | 316 | // disable unused peripherals and set sleep mode to save power 317 | ADCSRA = 0b00000000; // disable ADC 318 | ACSR = 0b10000000; // disable analog comperator 319 | PRR = 0b00000001; // shut down ADC 320 | set_sleep_mode(SLEEP_MODE_PWR_DOWN); // set sleep mode to power down 321 | 322 | // main loop 323 | while(1) { 324 | sleep_mode(); // sleep until button is pressed 325 | _delay_ms(1); // debounce 326 | uint8_t buttons = ~PINB & BT_MASK; // read button pins 327 | switch (buttons) { // send corresponding IR code 328 | case 0b00000001: KEY1; break; 329 | case 0b00000100: KEY2; break; 330 | case 0b00001000: KEY3; break; 331 | case 0b00010000: KEY4; break; 332 | case 0b00100000: KEY5; break; 333 | default: break; 334 | } 335 | } 336 | } 337 | 338 | // pin change interrupt service routine 339 | EMPTY_INTERRUPT (PCINT0_vect); // nothing to be done here, just wake up from sleep 340 | -------------------------------------------------------------------------------- /software/MULTI_5_buttons/main.c: -------------------------------------------------------------------------------- 1 | // tinyIRremote for ATtiny13A - Multi Protocol, 5 Buttons 2 | // 3 | // IR remote control using an ATtiny 13A. Timer0 generates a carrier 4 | // frequency with a duty cycle of 25% on the output pin to the 5 | // IR LED. The signal is modulated by toggling the pin to input/output. 6 | // 7 | // The code utilizes the sleep mode power down function. The device will 8 | // work several months on a CR2032 battery. 9 | // 10 | // +-\/-+ 11 | // KEY5 --- A0 (D5) PB5 1| |8 Vcc 12 | // KEY3 --- A3 (D3) PB3 2| |7 PB2 (D2) A1 --- KEY2 13 | // KEY4 --- A2 (D4) PB4 3| |6 PB1 (D1) ------ IR LED 14 | // GND 4| |5 PB0 (D0) ------ KEY1 15 | // +----+ 16 | // 17 | // Controller: ATtiny13A 18 | // Clockspeed: 1.2 MHz internal 19 | // 20 | // Reset pin must be disabled by writing respective fuse after uploading the code: 21 | // avrdude -p attiny13 -c usbasp -U lfuse:w:0x2a:m -U hfuse:w:0xfe:m 22 | // Warning: You will need a high voltage fuse resetter to undo this change! 23 | // 24 | // Note: The internal oscillator may need to be calibrated for the device 25 | // to function properly. 26 | // 27 | // 2019 by Stefan Wagner 28 | // Project Files (EasyEDA): https://easyeda.com/wagiminator 29 | // Project Files (Github): https://github.com/wagiminator 30 | // License: http://creativecommons.org/licenses/by-sa/3.0/ 31 | // 32 | // Based on the work of: 33 | // - San Bergmans (https://www.sbprojects.net/knowledge/ir/index.php), 34 | // - Christoph Niessen (http://chris.cnie.de/avr/tcm231421.html), 35 | // - David Johnson-Davies (http://www.technoblogy.com/show?UVE). 36 | 37 | 38 | // oscillator calibration value (uncomment and set if necessary) 39 | //#define OSCCAL_VAL 0x48 40 | 41 | // libraries 42 | #include 43 | #include 44 | #include 45 | #include 46 | 47 | // assign IR commands to the keys 48 | #define KEY1 NEC_sendCode(0x04,0x08) // LG TV Power: addr 0x04, cmd 0x08 49 | #define KEY2 RC5_sendCode(0x00,0x0B) // Philips TV Power: addr 0x00, cmd 0x0B 50 | #define KEY3 SON_sendCode(0x01,0x15,12) // Sony TV Power: addr 0x01, cmd 0x15, 12-bit version 51 | #define KEY4 SAM_sendCode(0x07,0x02) // Samsung TV Power: addr: 07, cmd: 02 52 | #define KEY5 _delay_ms(10) // nothing 53 | 54 | // macros to switch on/off IR LED 55 | #define IRon() DDRB |= 0b00000010 // PB1 as output = IR at OC0B (38 kHz) 56 | #define IRoff() DDRB &= 0b11111101 // PB1 as input = LED off 57 | 58 | // button mask 59 | #define BT_MASK 0b00111101 // port mask for button pins 60 | 61 | // ----------------------------------------------------------------------------------- 62 | // NEC Protocol Implementation 63 | // ----------------------------------------------------------------------------------- 64 | // 65 | // The NEC protocol uses pulse distance modulation. 66 | // 67 | // +---------+ +-+ +-+ +-+ +-+ +- ON 68 | // | | | | | | | | | | | bit0: 562.5us 69 | // | 9ms |4.5ms| |0| | 1 | | 1 | |0| ... 70 | // | | | | | | | | | | | bit1: 1687.5us 71 | // ------+ +-----+ +-+ +---+ +---+ +-+ OFF 72 | // 73 | // IR telegram starts with a 9ms leading burst followed by a 4.5ms pause. 74 | // Afterwards 4 data bytes are transmitted, least significant bit first. 75 | // A "0" bit is a 562.5us burst followed by a 562.5us pause, a "1" bit is 76 | // a 562.5us burst followed by a 1687.5us pause. A final 562.5us burst 77 | // signifies the end of the transmission. The four data bytes are in order: 78 | // - the 8-bit address for the receiving device, 79 | // - the 8-bit logical inverse of the address, 80 | // - the 8-bit command and 81 | // - the 8-bit logical inverse of the command. 82 | // The Extended NEC protocol uses 16-bit addresses. Instead of sending an 83 | // 8-bit address and its logically inverse, first the low byte and then the 84 | // high byte of the address is transmitted. 85 | // 86 | // If the key on the remote controller is kept depressed, a repeat code 87 | // will be issued consisting of a 9ms leading burst, a 2.25ms pause and 88 | // a 562.5us burst to mark the end. The repeat code will continue to be 89 | // sent out at 108ms intervals, until the key is finally released. 90 | 91 | // define values for 38kHz PWM frequency and 25% duty cycle 92 | #define NEC_TOP 31 // 1200kHz / 38kHz - 1 = 31 93 | #define NEC_DUTY 7 // 1200kHz / 38kHz / 4 - 1 = 7 94 | 95 | // macros to modulate the signals according to NEC protocol with compensated timings 96 | #define NEC_startPulse() {IRon(); _delay_us(9000); IRoff(); _delay_us(4500);} 97 | #define NEC_repeatPulse() {IRon(); _delay_us(9000); IRoff(); _delay_us(2250);} 98 | #define NEC_normalPulse() {IRon(); _delay_us( 562); IRoff(); _delay_us( 557);} 99 | #define NEC_bit1Pause() _delay_us(1120) // 1687.5us - 562.5us = 1125us 100 | #define NEC_repeatCode() {_delay_ms(40); NEC_repeatPulse(); NEC_normalPulse(); _delay_ms(56);} 101 | 102 | // send a single byte via IR 103 | void NEC_sendByte(uint8_t value) { 104 | for (uint8_t i=8; i; i--, value>>=1) { // send 8 bits, LSB first 105 | NEC_normalPulse(); // 562us burst, 562us pause 106 | if (value & 1) NEC_bit1Pause(); // extend pause if bit is 1 107 | } 108 | } 109 | 110 | // send complete telegram (start frame + address + command) via IR 111 | void NEC_sendCode(uint16_t addr, uint8_t cmd) { 112 | // prepare carrier wave 113 | OCR0A = NEC_TOP; // set PWM frequency 114 | OCR0B = NEC_DUTY; // set duty cycle 115 | 116 | // send telegram 117 | NEC_startPulse(); // 9ms burst + 4.5ms pause to signify start of transmission 118 | if (addr > 0xFF) { // if extended NEC protocol (16-bit address): 119 | NEC_sendByte(addr); // send address low byte 120 | NEC_sendByte(addr >> 8); // send address high byte 121 | } else { // if standard NEC protocol (8-bit address): 122 | NEC_sendByte(addr); // send address byte 123 | NEC_sendByte(~addr); // send inverse of address byte 124 | } 125 | NEC_sendByte(cmd); // send command byte 126 | NEC_sendByte(~cmd); // send inverse of command byte 127 | NEC_normalPulse(); // 562us burst to signify end of transmission 128 | while(~PINB & BT_MASK) NEC_repeatCode(); // send repeat command until button is released 129 | } 130 | 131 | // ----------------------------------------------------------------------------------- 132 | // SAMSUNG Protocol Implementation 133 | // ----------------------------------------------------------------------------------- 134 | // 135 | // The SAMSUNG protocol corresponds to the NEC protocol, except that the start pulse is 136 | // 4.5ms long and the address byte is sent twice. The telegram is repeated every 108ms 137 | // as long as the button is pressed. 138 | 139 | #define SAM_startPulse() {IRon(); _delay_us(4500); IRoff(); _delay_us(4500);} 140 | #define SAM_repeatPause() _delay_ms(44) 141 | 142 | // send complete telegram (start frame + address + command) via IR 143 | void SAM_sendCode(uint8_t addr, uint8_t cmd) { 144 | // prepare carrier wave 145 | OCR0A = NEC_TOP; // set PWM frequency 146 | OCR0B = NEC_DUTY; // set duty cycle 147 | 148 | // send telegram 149 | do { 150 | SAM_startPulse(); // 9ms burst + 4.5ms pause to signify start of transmission 151 | NEC_sendByte(addr); // send address byte 152 | NEC_sendByte(addr); // send address byte again 153 | NEC_sendByte(cmd); // send command byte 154 | NEC_sendByte(~cmd); // send inverse of command byte 155 | NEC_normalPulse(); // 562us burst to signify end of transmission 156 | SAM_repeatPause(); // wait for next repeat 157 | } while(~PINB & BT_MASK); // repeat sending until button is released 158 | } 159 | 160 | // ----------------------------------------------------------------------------------- 161 | // RC-5 Protocol Implementation 162 | // ----------------------------------------------------------------------------------- 163 | // 164 | // The RC-5 protocol uses bi-phase modulation (Manchester coding). 165 | // 166 | // +-------+ +-------+ ON 167 | // | | 168 | // 889us | 889us 889us | 889us 169 | // | | 170 | // +-------+ +-------+ OFF 171 | // 172 | // |<-- Bit "0" -->| |<-- Bit "1" -->| 173 | // 174 | // IR telegram starts with two start bits. The first bit is always "1", 175 | // the second bit is "1" in the original protocol and inverted 7th bit 176 | // of the command in the extended RC-5 protocol. The third bit toggles 177 | // after each button release. The next five bits represent the device 178 | // address, MSB first and the last six bits represent the command, MSB 179 | // first. 180 | // 181 | // As long as a key remains down the telegram will be repeated every 182 | // 114ms without changing the toggle bit. 183 | 184 | // define values for 36kHz PWM frequency and 25% duty cycle 185 | #define RC5_TOP 32 // 1200kHz / 36kHz - 1 = 32 186 | #define RC5_DUTY 7 // 1200kHz / 36kHz / 4 - 1 = 7 187 | 188 | // macros to modulate the signals according to RC-5 protocol with compensated timings 189 | #define RC5_bit0Pulse() {IRon(); _delay_us(889); IRoff(); _delay_us(884);} 190 | #define RC5_bit1Pulse() {IRoff(); _delay_us(889); IRon(); _delay_us(884);} 191 | #define RC5_repeatPause() _delay_ms(89) // 114ms - 14 * 2 * 889us 192 | 193 | // bitmasks 194 | #define RC5_startBit 0b0010000000000000 195 | #define RC5_cmdBit7 0b0001000000000000 196 | #define RC5_toggleBit 0b0000100000000000 197 | 198 | // toggle variable 199 | uint8_t RC5_toggle = 0; 200 | 201 | // send complete telegram (startbits + togglebit + address + command) via IR 202 | void RC5_sendCode(uint8_t addr, uint8_t cmd) { 203 | // prepare carrier wave 204 | OCR0A = RC5_TOP; // set PWM frequency 205 | OCR0B = RC5_DUTY; // set duty cycle 206 | 207 | // prepare the message 208 | uint16_t message = addr << 6; // shift address to the right position 209 | message |= (cmd & 0x3f); // add the low 6 bits of the command 210 | if (~cmd & 0x40) message |= RC5_cmdBit7; // add inverse of 7th command bit 211 | message |= RC5_startBit; // add start bit 212 | if (RC5_toggle) message |= RC5_toggleBit; // add toggle bit 213 | 214 | // send the message 215 | do { 216 | uint16_t bitmask = RC5_startBit; // set the bitmask to first bit to send 217 | for(uint8_t i=14; i; i--, bitmask>>=1) { // 14 bits, MSB first 218 | (message & bitmask) ? (RC5_bit1Pulse()) : (RC5_bit0Pulse()); // send the bit 219 | } 220 | IRoff(); // switch off IR LED 221 | RC5_repeatPause(); // wait for next repeat 222 | } while(~PINB & BT_MASK); // repeat sending until button is released 223 | RC5_toggle ^= 1; // toggle the toggle bit 224 | } 225 | 226 | // ----------------------------------------------------------------------------------- 227 | // SONY SIRC Protocol Implementation 228 | // ----------------------------------------------------------------------------------- 229 | // 230 | // The SONY SIRC protocol uses pulse length modulation. 231 | // 232 | // +--------------------+ +-----+ +----------+ +-- ON 233 | // | | | | | | | 234 | // | 2400us |600us|600us|600us| 1200us |600us| ... 235 | // | | | | | | | 236 | // ------+ +-----+ +-----+ +-----+ OFF 237 | // 238 | // |<------ Start Frame ----->|<- Bit=0 ->|<--- Bit=1 ---->| 239 | // 240 | // A "0" bit is a 600us burst followed by a 600us space, a "1" bit is a 241 | // 1200us burst followed by a 600us space. An IR telegram starts with a 242 | // 2400us leading burst followed by a 600us space. The command and 243 | // address bits are then transmitted, LSB first. Depending on the 244 | // protocol version, these are in detail: 245 | // - 12-bit version: 7 command bits, 5 address bits 246 | // - 15-bit version: 7 command bits, 8 address bits 247 | // - 20-bit version: 7 command bits, 5 address bits, 8 extended bits 248 | // 249 | // As long as a key remains down the message will be repeated every 45ms. 250 | 251 | // define values for 40kHz PWM frequency and 25% duty cycle 252 | #define SON_TOP 29 // 1200kHz / 40kHz - 1 = 29 253 | #define SON_DUTY 7 // 1200kHz / 40kHz / 4 - 1 = 7 254 | 255 | // macros to modulate the signals according to SONY protocol with compensated timings 256 | #define SON_startPulse() {IRon(); _delay_us(2400); IRoff(); _delay_us( 595);} 257 | #define SON_bit0Pulse() {IRon(); _delay_us( 600); IRoff(); _delay_us( 595);} 258 | #define SON_bit1Pulse() {IRon(); _delay_us(1200); IRoff(); _delay_us( 595);} 259 | #define SON_repeatPause() _delay_ms(27) 260 | 261 | // send "number" of bits of "value" via IR 262 | void SON_sendByte(uint8_t value, uint8_t number) { 263 | do { // send number of bits, LSB first 264 | (value & 1) ? (SON_bit1Pulse()) : (SON_bit0Pulse()); // send bit 265 | value>>=1; // next bit 266 | } while(--number); 267 | } 268 | 269 | // send complete telegram (start frame + command + address) via IR 270 | void SON_sendCode(uint16_t addr, uint8_t cmd, uint8_t bits) { 271 | // prepare carrier wave 272 | OCR0A = SON_TOP; // set PWM frequency 273 | OCR0B = SON_DUTY; // set duty cycle 274 | 275 | // send telegram 276 | do { 277 | SON_startPulse(); // signify start of transmission 278 | SON_sendByte(cmd, 7); // send 7 command bits 279 | switch (bits) { 280 | case 12: SON_sendByte(addr, 5); break; // 12-bit version: send 5 address bits 281 | case 15: SON_sendByte(addr, 8); break; // 15-bit version: send 8 address bits 282 | case 20: SON_sendByte(addr, 8); SON_sendByte(addr>>8, 5); break; // 20-bit: 13 bits 283 | default: break; 284 | } 285 | SON_repeatPause(); // wait until next repeat 286 | } while (~PINB & BT_MASK); // repeat sending until button is released 287 | } 288 | 289 | // ----------------------------------------------------------------------------------- 290 | // Main Function 291 | // ----------------------------------------------------------------------------------- 292 | 293 | // main function 294 | int main(void) { 295 | // set oscillator calibration value 296 | #ifdef OSCCAL_VAL 297 | OSCCAL = OSCCAL_VAL; // set the value if defined above 298 | #endif 299 | 300 | // setup pins 301 | DDRB = 0b00000000; // all pins are input pins by now 302 | PORTB = BT_MASK; // pull-up for button pins 303 | 304 | // set timer0 to toggle IR pin at 38 kHz 305 | TCCR0A = 0b00100011; // PWM on OC0B (PB1) 306 | TCCR0B = 0b00001001; // no prescaler 307 | 308 | // setup pin change interrupt 309 | GIMSK = 0b00100000; // turn on pin change interrupts 310 | PCMSK = BT_MASK; // turn on interrupt on button pins 311 | SREG |= 0b10000000; // enable global interrupts 312 | 313 | // disable unused peripherals and set sleep mode to save power 314 | ADCSRA = 0b00000000; // disable ADC 315 | ACSR = 0b10000000; // disable analog comperator 316 | PRR = 0b00000001; // shut down ADC 317 | set_sleep_mode(SLEEP_MODE_PWR_DOWN); // set sleep mode to power down 318 | 319 | // main loop 320 | while(1) { 321 | sleep_mode(); // sleep until button is pressed 322 | _delay_ms(1); // debounce 323 | uint8_t buttons = ~PINB & BT_MASK; // read button pins 324 | switch (buttons) { // send corresponding IR code 325 | case 0b00000001: KEY1; break; 326 | case 0b00000100: KEY2; break; 327 | case 0b00001000: KEY3; break; 328 | case 0b00010000: KEY4; break; 329 | case 0b00100000: KEY5; break; 330 | default: break; 331 | } 332 | } 333 | } 334 | 335 | // pin change interrupt service routine 336 | EMPTY_INTERRUPT (PCINT0_vect); // nothing to be done here, just wake up from sleep 337 | -------------------------------------------------------------------------------- /software/MULTI_5_buttons/makefile: -------------------------------------------------------------------------------- 1 | # Project: tinyRemote - 5 buttons 2 | # Author: Stefan Wagner 3 | # Year: 2019 4 | # URL: https://easyeda.com/wagiminator 5 | # https://github.com/wagiminator 6 | # 7 | # Type "make help" in the command line. 8 | 9 | # Compiler Options 10 | DEVICE = attiny13a 11 | CLOCK = 1200000 12 | 13 | # Programmer Options 14 | PROGRMR = usbasp 15 | TGTDEV = attiny13 16 | LFUSE = 0x2a 17 | HFUSE = 0xfe 18 | 19 | # Objects 20 | OBJECTS = main.o 21 | 22 | # Commands 23 | AVRDUDE = avrdude -c $(PROGRMR) -p $(TGTDEV) 24 | COMPILE = avr-gcc -Wall -Os -flto -mmcu=$(DEVICE) -DF_CPU=$(CLOCK) -DDEBUG_LEVEL=0 25 | CLEAN = rm -f main.lst main.obj main.cof main.list main.map main.eep.hex *.o main.s 26 | 27 | # Symbolic Targets 28 | all: help 29 | 30 | help: 31 | @echo "Use the following commands:" 32 | @echo "make hex compile and build main.hex for $(DEVICE)" 33 | @echo "make asm compile and disassemble to main.asm for $(DEVICE)" 34 | @echo "make bin compile and build main.bin for $(DEVICE)" 35 | @echo "make upload compile and upload to $(DEVICE) using $(PROGRMR)" 36 | @echo "make fuses burn fuses of $(DEVICE) using $(PROGRMR) programmer" 37 | @echo "make install compile, upload and burn fuses for $(DEVICE)" 38 | @echo "make clean remove all build files" 39 | 40 | bin: $(OBJECTS) 41 | @echo "Building main.bin ..." 42 | @$(COMPILE) -o main.bin $(OBJECTS) 43 | @echo "Removing temporary files ..." 44 | @$(CLEAN) 45 | 46 | hex: bin 47 | @echo "Building main.hex ..." 48 | @avr-objcopy -j .text -j .data -O ihex main.bin main.hex 49 | @echo "FLASH: $(shell avr-size -d main.bin | awk '/[0-9]/ {print $$1 + $$2}') bytes" 50 | @echo "SRAM: $(shell avr-size -d main.bin | awk '/[0-9]/ {print $$2 + $$3}') bytes" 51 | @echo "Removing main.bin ..." 52 | @rm -f main.bin 53 | 54 | asm: bin 55 | @echo "Disassembling to main.asm ..." 56 | @avr-objdump -d main.bin > main.asm 57 | @echo "Removing main.bin ..." 58 | @rm -f main.bin 59 | 60 | upload: hex 61 | @echo "Uploading to $(TGTDEV) ..." 62 | @$(AVRDUDE) -U flash:w:main.hex:i 63 | 64 | fuses: 65 | @echo "Burning fuses of $(TGTDEV) ..." 66 | @echo "Warning: RESET pin will be disabled !" 67 | @$(AVRDUDE) -U hfuse:w:$(HFUSE):m -U lfuse:w:$(LFUSE):m 68 | 69 | install: upload fuses 70 | 71 | clean: 72 | @echo "Cleaning all up ..." 73 | @$(CLEAN) 74 | @rm -f main.bin main.hex main.asm 75 | 76 | .c.o: 77 | @echo "Compiling $< for $(DEVICE) @ $(CLOCK)Hz ..." 78 | @$(COMPILE) -c $< -o $@ 79 | 80 | .S.o: 81 | @echo "Compiling $< for $(DEVICE) @ $(CLOCK)Hz ..." 82 | @$(COMPILE) -x assembler-with-cpp -c $< -o $@ 83 | 84 | .c.s: 85 | @echo "Compiling $< for $(DEVICE) @ $(CLOCK)Hz ..." 86 | @$(COMPILE) -S $< -o $@ 87 | -------------------------------------------------------------------------------- /software/NEC_4_buttons/TinyRemote_t13_NEC_4B.ino: -------------------------------------------------------------------------------- 1 | // tinyIRremote for ATtiny13A - NEC, 4 Buttons 2 | // 3 | // IR remote control using an ATtiny 13A. Timer0 generates a 38kHz 4 | // carrier frequency with a duty cycle of 25% on the output pin to the 5 | // IR LED. The signal (NEC protocol) is modulated by toggling the pin 6 | // to input/output. The protocol uses pulse distance modulation. 7 | // 8 | // +---------+ +-+ +-+ +-+ +-+ +- ON 9 | // | | | | | | | | | | | bit0: 562.5us 10 | // | 9ms |4.5ms| |0| | 1 | | 1 | |0| ... 11 | // | | | | | | | | | | | bit1: 1687.5us 12 | // ------+ +-----+ +-+ +---+ +---+ +-+ OFF 13 | // 14 | // IR telegram starts with a 9ms leading burst followed by a 4.5ms pause. 15 | // Afterwards 4 data bytes are transmitted, least significant bit first. 16 | // A "0" bit is a 562.5us burst followed by a 562.5us pause, a "1" bit is 17 | // a 562.5us burst followed by a 1687.5us pause. A final 562.5us burst 18 | // signifies the end of the transmission. The four data bytes are in order: 19 | // - the 8-bit address for the receiving device, 20 | // - the 8-bit logical inverse of the address, 21 | // - the 8-bit command and 22 | // - the 8-bit logical inverse of the command. 23 | // The Extended NEC protocol uses 16-bit addresses. Instead of sending an 24 | // 8-bit address and its logically inverse, first the low byte and then the 25 | // high byte of the address is transmitted. 26 | // 27 | // If the key on the remote controller is kept depressed, a repeat code 28 | // will be issued consisting of a 9ms leading burst, a 2.25ms pause and 29 | // a 562.5us burst to mark the end. The repeat code will continue to be 30 | // sent out at 108ms intervals, until the key is finally released. 31 | // 32 | // The code utilizes the sleep mode power down function. The device will 33 | // work several months on a CR2032 battery. 34 | // 35 | // +-\/-+ 36 | // KEY5 --- A0 (D5) PB5 1| |8 Vcc 37 | // KEY3 --- A3 (D3) PB3 2| |7 PB2 (D2) A1 --- KEY2 38 | // KEY4 --- A2 (D4) PB4 3| |6 PB1 (D1) ------ IR LED 39 | // GND 4| |5 PB0 (D0) ------ KEY1 40 | // +----+ 41 | // 42 | // Controller: ATtiny13 43 | // Core: MicroCore (https://github.com/MCUdude/MicroCore) 44 | // Clockspeed: 1.2 MHz internal 45 | // BOD: BOD disabled (energy saving) 46 | // Timing: Micros disabled (Timer0 is in use) 47 | // 48 | // Note: The internal oscillator may need to be calibrated for the device 49 | // to function properly. 50 | // 51 | // 2019 by Stefan Wagner 52 | // Project Files (EasyEDA): https://easyeda.com/wagiminator 53 | // Project Files (Github): https://github.com/wagiminator 54 | // License: http://creativecommons.org/licenses/by-sa/3.0/ 55 | // 56 | // Based on the work of: 57 | // - San Bergmans (https://www.sbprojects.net/knowledge/ir/index.php), 58 | // - Christoph Niessen (http://chris.cnie.de/avr/tcm231421.html), 59 | // - David Johnson-Davies (http://www.technoblogy.com/show?UVE). 60 | 61 | 62 | // oscillator calibration value (uncomment and set if necessary) 63 | //#define OSCCAL_VAL 48 64 | 65 | // libraries 66 | #include 67 | #include 68 | #include 69 | #include 70 | 71 | // IR codes (use 16-bit address for extended NEC protocol) 72 | #define ADDR 0x04 // Address: LG TV 73 | #define KEY1 0x02 // Command: Volume+ 74 | #define KEY2 0x00 // Command: Channel+ 75 | #define KEY3 0x03 // Command: Volume- 76 | #define KEY4 0x01 // Command: Channel- 77 | 78 | // define values for 38kHz PWM frequency and 25% duty cycle 79 | #define TOP 31 // 1200kHz / 38kHz - 1 = 31 80 | #define DUTY 7 // 1200kHz / 38kHz / 4 - 1 = 7 81 | 82 | // macros to switch on/off IR LED 83 | #define IRon() DDRB |= 0b00000010 // PB1 as output = IR at OC0B (38 kHz) 84 | #define IRoff() DDRB &= 0b11111101 // PB1 as input = LED off 85 | 86 | // macros to modulate the signals according to NEC protocol with compensated timings 87 | #define startPulse() {IRon(); _delay_us(9000); IRoff(); _delay_us(4500);} 88 | #define repeatPulse() {IRon(); _delay_us(9000); IRoff(); _delay_us(2250);} 89 | #define normalPulse() {IRon(); _delay_us( 562); IRoff(); _delay_us( 557);} 90 | #define bit1Pause() _delay_us(1120) // 1687.5us - 562.5us = 1125us 91 | #define repeatCode() {_delay_ms(40); repeatPulse(); normalPulse(); _delay_ms(56);} 92 | 93 | 94 | // send a single byte via IR 95 | void sendByte(uint8_t value) { 96 | for (uint8_t i=8; i; i--, value>>=1) { // send 8 bits, LSB first 97 | normalPulse(); // 562us burst, 562us pause 98 | if (value & 1) bit1Pause(); // extend pause if bit is 1 99 | } 100 | } 101 | 102 | // send complete telegram (start frame + address + command) via IR 103 | void sendCode(uint8_t cmd) { 104 | startPulse(); // 9ms burst + 4.5ms pause to signify start of transmission 105 | #if ADDR > 0xFF // if extended NEC protocol (16-bit address): 106 | sendByte(ADDR & 0xFF); // send address low byte 107 | sendByte(ADDR >> 8); // send address high byte 108 | #else // if standard NEC protocol (8-bit address): 109 | sendByte(ADDR); // send address byte 110 | sendByte(~ADDR); // send inverse of address byte 111 | #endif 112 | sendByte(cmd); // send command byte 113 | sendByte(~cmd); // send inverse of command byte 114 | normalPulse(); // 562us burst to signify end of transmission 115 | } 116 | 117 | // main function 118 | int main(void) { 119 | // set oscillator calibration value 120 | #ifdef OSCCAL_VAL 121 | OSCCAL = OSCCAL_VAL; // set the value if defined above 122 | #endif 123 | 124 | // setup pins 125 | DDRB = 0b00000000; // all pins are input pins by now 126 | PORTB = 0b00011101; // pull-up for button pins 127 | 128 | // set timer0 to toggle IR pin at 38 kHz 129 | TCCR0A = 0b00100011; // PWM on OC0B (PB1) 130 | TCCR0B = 0b00001001; // no prescaler 131 | OCR0A = TOP; // 38 kHz PWM frequency 132 | OCR0B = DUTY; // 25 % duty cycle 133 | 134 | // setup pin change interrupt 135 | GIMSK = 0b00100000; // turn on pin change interrupts 136 | PCMSK = 0b00011101; // turn on interrupt on button pins 137 | SREG |= 0b10000000; // enable global interrupts 138 | 139 | // disable unused peripherals and set sleep mode to save power 140 | ADCSRA = 0b00000000; // disable ADC 141 | ACSR = 0b10000000; // disable analog comperator 142 | PRR = 0b00000001; // shut down ADC 143 | set_sleep_mode(SLEEP_MODE_PWR_DOWN); // set sleep mode to power down 144 | 145 | // main loop 146 | while(1) { 147 | sleep_mode(); // sleep until button is pressed 148 | _delay_ms(1); // debounce 149 | uint8_t buttons = ~PINB & 0b00011101; // read button pins 150 | switch (buttons) { // send corresponding IR code 151 | case 0b00000001: sendCode(KEY1); break; 152 | case 0b00000100: sendCode(KEY2); break; 153 | case 0b00001000: sendCode(KEY3); break; 154 | case 0b00010000: sendCode(KEY4); break; 155 | default: break; 156 | } 157 | while (~PINB & 0b00011101) repeatCode(); // send repeat command until button is released 158 | } 159 | } 160 | 161 | // pin change interrupt service routine 162 | EMPTY_INTERRUPT (PCINT0_vect); // nothing to be done here, just wake up from sleep 163 | -------------------------------------------------------------------------------- /software/NEC_4_buttons/main.c: -------------------------------------------------------------------------------- 1 | // tinyIRremote for ATtiny13A - NEC, 4 Buttons 2 | // 3 | // IR remote control using an ATtiny 13A. Timer0 generates a 38kHz 4 | // carrier frequency with a duty cycle of 25% on the output pin to the 5 | // IR LED. The signal (NEC protocol) is modulated by toggling the pin 6 | // to input/output. The protocol uses pulse distance modulation. 7 | // 8 | // +---------+ +-+ +-+ +-+ +-+ +- ON 9 | // | | | | | | | | | | | bit0: 562.5us 10 | // | 9ms |4.5ms| |0| | 1 | | 1 | |0| ... 11 | // | | | | | | | | | | | bit1: 1687.5us 12 | // ------+ +-----+ +-+ +---+ +---+ +-+ OFF 13 | // 14 | // IR telegram starts with a 9ms leading burst followed by a 4.5ms pause. 15 | // Afterwards 4 data bytes are transmitted, least significant bit first. 16 | // A "0" bit is a 562.5us burst followed by a 562.5us pause, a "1" bit is 17 | // a 562.5us burst followed by a 1687.5us pause. A final 562.5us burst 18 | // signifies the end of the transmission. The four data bytes are in order: 19 | // - the 8-bit address for the receiving device, 20 | // - the 8-bit logical inverse of the address, 21 | // - the 8-bit command and 22 | // - the 8-bit logical inverse of the command. 23 | // The Extended NEC protocol uses 16-bit addresses. Instead of sending an 24 | // 8-bit address and its logically inverse, first the low byte and then the 25 | // high byte of the address is transmitted. 26 | // 27 | // If the key on the remote controller is kept depressed, a repeat code 28 | // will be issued consisting of a 9ms leading burst, a 2.25ms pause and 29 | // a 562.5us burst to mark the end. The repeat code will continue to be 30 | // sent out at 108ms intervals, until the key is finally released. 31 | // 32 | // The code utilizes the sleep mode power down function. The device will 33 | // work several months on a CR2032 battery. 34 | // 35 | // +-\/-+ 36 | // KEY5 --- A0 (D5) PB5 1| |8 Vcc 37 | // KEY3 --- A3 (D3) PB3 2| |7 PB2 (D2) A1 --- KEY2 38 | // KEY4 --- A2 (D4) PB4 3| |6 PB1 (D1) ------ IR LED 39 | // GND 4| |5 PB0 (D0) ------ KEY1 40 | // +----+ 41 | // 42 | // Controller: ATtiny13A 43 | // Clockspeed: 1.2 MHz internal 44 | // 45 | // Note: The internal oscillator may need to be calibrated for the device 46 | // to function properly. 47 | // 48 | // 2019 by Stefan Wagner 49 | // Project Files (EasyEDA): https://easyeda.com/wagiminator 50 | // Project Files (Github): https://github.com/wagiminator 51 | // License: http://creativecommons.org/licenses/by-sa/3.0/ 52 | // 53 | // Based on the work of: 54 | // - San Bergmans (https://www.sbprojects.net/knowledge/ir/index.php), 55 | // - Christoph Niessen (http://chris.cnie.de/avr/tcm231421.html), 56 | // - David Johnson-Davies (http://www.technoblogy.com/show?UVE). 57 | 58 | 59 | // oscillator calibration value (uncomment and set if necessary) 60 | //#define OSCCAL_VAL 48 61 | 62 | // libraries 63 | #include 64 | #include 65 | #include 66 | #include 67 | 68 | // IR codes (use 16-bit address for extended NEC protocol) 69 | #define ADDR 0x04 // Address: LG TV 70 | #define KEY1 0x02 // Command: Volume+ 71 | #define KEY2 0x00 // Command: Channel+ 72 | #define KEY3 0x03 // Command: Volume- 73 | #define KEY4 0x01 // Command: Channel- 74 | 75 | // define values for 38kHz PWM frequency and 25% duty cycle 76 | #define TOP 31 // 1200kHz / 38kHz - 1 = 31 77 | #define DUTY 7 // 1200kHz / 38kHz / 4 - 1 = 7 78 | 79 | // macros to switch on/off IR LED 80 | #define IRon() DDRB |= 0b00000010 // PB1 as output = IR at OC0B (38 kHz) 81 | #define IRoff() DDRB &= 0b11111101 // PB1 as input = LED off 82 | 83 | // macros to modulate the signals according to NEC protocol with compensated timings 84 | #define startPulse() {IRon(); _delay_us(9000); IRoff(); _delay_us(4500);} 85 | #define repeatPulse() {IRon(); _delay_us(9000); IRoff(); _delay_us(2250);} 86 | #define normalPulse() {IRon(); _delay_us( 562); IRoff(); _delay_us( 557);} 87 | #define bit1Pause() _delay_us(1120) // 1687.5us - 562.5us = 1125us 88 | #define repeatCode() {_delay_ms(40); repeatPulse(); normalPulse(); _delay_ms(56);} 89 | 90 | 91 | // send a single byte via IR 92 | void sendByte(uint8_t value) { 93 | for (uint8_t i=8; i; i--, value>>=1) { // send 8 bits, LSB first 94 | normalPulse(); // 562us burst, 562us pause 95 | if (value & 1) bit1Pause(); // extend pause if bit is 1 96 | } 97 | } 98 | 99 | // send complete telegram (start frame + address + command) via IR 100 | void sendCode(uint8_t cmd) { 101 | startPulse(); // 9ms burst + 4.5ms pause to signify start of transmission 102 | #if ADDR > 0xFF // if extended NEC protocol (16-bit address): 103 | sendByte(ADDR & 0xFF); // send address low byte 104 | sendByte(ADDR >> 8); // send address high byte 105 | #else // if standard NEC protocol (8-bit address): 106 | sendByte(ADDR); // send address byte 107 | sendByte(~ADDR); // send inverse of address byte 108 | #endif 109 | sendByte(cmd); // send command byte 110 | sendByte(~cmd); // send inverse of command byte 111 | normalPulse(); // 562us burst to signify end of transmission 112 | } 113 | 114 | // main function 115 | int main(void) { 116 | // set oscillator calibration value 117 | #ifdef OSCCAL_VAL 118 | OSCCAL = OSCCAL_VAL; // set the value if defined above 119 | #endif 120 | 121 | // setup pins 122 | DDRB = 0b00000000; // all pins are input pins by now 123 | PORTB = 0b00011101; // pull-up for button pins 124 | 125 | // set timer0 to toggle IR pin at 38 kHz 126 | TCCR0A = 0b00100011; // PWM on OC0B (PB1) 127 | TCCR0B = 0b00001001; // no prescaler 128 | OCR0A = TOP; // 38 kHz PWM frequency 129 | OCR0B = DUTY; // 25 % duty cycle 130 | 131 | // setup pin change interrupt 132 | GIMSK = 0b00100000; // turn on pin change interrupts 133 | PCMSK = 0b00011101; // turn on interrupt on button pins 134 | SREG |= 0b10000000; // enable global interrupts 135 | 136 | // disable unused peripherals and set sleep mode to save power 137 | ADCSRA = 0b00000000; // disable ADC 138 | ACSR = 0b10000000; // disable analog comperator 139 | PRR = 0b00000001; // shut down ADC 140 | set_sleep_mode(SLEEP_MODE_PWR_DOWN); // set sleep mode to power down 141 | 142 | // main loop 143 | while(1) { 144 | sleep_mode(); // sleep until button is pressed 145 | _delay_ms(1); // debounce 146 | uint8_t buttons = ~PINB & 0b00011101; // read button pins 147 | switch (buttons) { // send corresponding IR code 148 | case 0b00000001: sendCode(KEY1); break; 149 | case 0b00000100: sendCode(KEY2); break; 150 | case 0b00001000: sendCode(KEY3); break; 151 | case 0b00010000: sendCode(KEY4); break; 152 | default: break; 153 | } 154 | while (~PINB & 0b00011101) repeatCode(); // send repeat command until button is released 155 | } 156 | } 157 | 158 | // pin change interrupt service routine 159 | EMPTY_INTERRUPT (PCINT0_vect); // nothing to be done here, just wake up from sleep 160 | -------------------------------------------------------------------------------- /software/NEC_4_buttons/makefile: -------------------------------------------------------------------------------- 1 | # Project: tinyRemote - 4 buttons 2 | # Author: Stefan Wagner 3 | # Year: 2019 4 | # URL: https://easyeda.com/wagiminator 5 | # https://github.com/wagiminator 6 | # 7 | # Type "make help" in the command line. 8 | 9 | # Compiler Options 10 | DEVICE = attiny13a 11 | CLOCK = 1200000 12 | 13 | # Programmer Options 14 | PROGRMR = usbasp 15 | TGTDEV = attiny13 16 | LFUSE = 0x2a 17 | HFUSE = 0xff 18 | 19 | # Objects 20 | OBJECTS = main.o 21 | 22 | # Commands 23 | AVRDUDE = avrdude -c $(PROGRMR) -p $(TGTDEV) 24 | COMPILE = avr-gcc -Wall -Os -flto -mmcu=$(DEVICE) -DF_CPU=$(CLOCK) -DDEBUG_LEVEL=0 25 | CLEAN = rm -f main.lst main.obj main.cof main.list main.map main.eep.hex *.o main.s 26 | 27 | # Symbolic Targets 28 | all: help 29 | 30 | help: 31 | @echo "Use the following commands:" 32 | @echo "make hex compile and build main.hex for $(DEVICE)" 33 | @echo "make asm compile and disassemble to main.asm for $(DEVICE)" 34 | @echo "make bin compile and build main.bin for $(DEVICE)" 35 | @echo "make upload compile and upload to $(DEVICE) using $(PROGRMR)" 36 | @echo "make fuses burn fuses of $(DEVICE) using $(PROGRMR) programmer" 37 | @echo "make install compile, upload and burn fuses for $(DEVICE)" 38 | @echo "make clean remove all build files" 39 | 40 | bin: $(OBJECTS) 41 | @echo "Building main.bin ..." 42 | @$(COMPILE) -o main.bin $(OBJECTS) 43 | @echo "Removing temporary files ..." 44 | @$(CLEAN) 45 | 46 | hex: bin 47 | @echo "Building main.hex ..." 48 | @avr-objcopy -j .text -j .data -O ihex main.bin main.hex 49 | @echo "FLASH: $(shell avr-size -d main.bin | awk '/[0-9]/ {print $$1 + $$2}') bytes" 50 | @echo "SRAM: $(shell avr-size -d main.bin | awk '/[0-9]/ {print $$2 + $$3}') bytes" 51 | @echo "Removing main.bin ..." 52 | @rm -f main.bin 53 | 54 | asm: bin 55 | @echo "Disassembling to main.asm ..." 56 | @avr-objdump -d main.bin > main.asm 57 | @echo "Removing main.bin ..." 58 | @rm -f main.bin 59 | 60 | upload: hex 61 | @echo "Uploading to $(TGTDEV) ..." 62 | @$(AVRDUDE) -U flash:w:main.hex:i 63 | 64 | fuses: 65 | @echo "Burning fuses of $(TGTDEV) ..." 66 | @$(AVRDUDE) -U hfuse:w:$(HFUSE):m -U lfuse:w:$(LFUSE):m 67 | 68 | install: upload fuses 69 | 70 | clean: 71 | @echo "Cleaning all up ..." 72 | @$(CLEAN) 73 | @rm -f main.bin main.hex main.asm 74 | 75 | .c.o: 76 | @echo "Compiling $< for $(DEVICE) @ $(CLOCK)Hz ..." 77 | @$(COMPILE) -c $< -o $@ 78 | 79 | .S.o: 80 | @echo "Compiling $< for $(DEVICE) @ $(CLOCK)Hz ..." 81 | @$(COMPILE) -x assembler-with-cpp -c $< -o $@ 82 | 83 | .c.s: 84 | @echo "Compiling $< for $(DEVICE) @ $(CLOCK)Hz ..." 85 | @$(COMPILE) -S $< -o $@ 86 | -------------------------------------------------------------------------------- /software/NEC_5_buttons/TinyRemote_t13_NEC_5B.ino: -------------------------------------------------------------------------------- 1 | // tinyIRremote for ATtiny13A - NEC, 5 Buttons 2 | // 3 | // IR remote control using an ATtiny 13A. Timer0 generates a 38kHz 4 | // carrier frequency with a duty cycle of 25% on the output pin to the 5 | // IR LED. The signal (NEC protocol) is modulated by toggling the pin 6 | // to input/output. The protocol uses pulse distance modulation. 7 | // 8 | // +---------+ +-+ +-+ +-+ +-+ +- ON 9 | // | | | | | | | | | | | bit0: 562.5us 10 | // | 9ms |4.5ms| |0| | 1 | | 1 | |0| ... 11 | // | | | | | | | | | | | bit1: 1687.5us 12 | // ------+ +-----+ +-+ +---+ +---+ +-+ OFF 13 | // 14 | // IR telegram starts with a 9ms leading burst followed by a 4.5ms pause. 15 | // Afterwards 4 data bytes are transmitted, least significant bit first. 16 | // A "0" bit is a 562.5us burst followed by a 562.5us pause, a "1" bit is 17 | // a 562.5us burst followed by a 1687.5us pause. A final 562.5us burst 18 | // signifies the end of the transmission. The four data bytes are in order: 19 | // - the 8-bit address for the receiving device, 20 | // - the 8-bit logical inverse of the address, 21 | // - the 8-bit command and 22 | // - the 8-bit logical inverse of the command. 23 | // The Extended NEC protocol uses 16-bit addresses. Instead of sending an 24 | // 8-bit address and its logically inverse, first the low byte and then the 25 | // high byte of the address is transmitted. 26 | // 27 | // If the key on the remote controller is kept depressed, a repeat code 28 | // will be issued consisting of a 9ms leading burst, a 2.25ms pause and 29 | // a 562.5us burst to mark the end. The repeat code will continue to be 30 | // sent out at 108ms intervals, until the key is finally released. 31 | // 32 | // The code utilizes the sleep mode power down function. The device will 33 | // work several months on a CR2032 battery. 34 | // 35 | // +-\/-+ 36 | // KEY5 --- A0 (D5) PB5 1| |8 Vcc 37 | // KEY3 --- A3 (D3) PB3 2| |7 PB2 (D2) A1 --- KEY2 38 | // KEY4 --- A2 (D4) PB4 3| |6 PB1 (D1) ------ IR LED 39 | // GND 4| |5 PB0 (D0) ------ KEY1 40 | // +----+ 41 | // 42 | // Controller: ATtiny13 43 | // Core: MicroCore (https://github.com/MCUdude/MicroCore) 44 | // Clockspeed: 1.2 MHz internal 45 | // BOD: BOD disabled (energy saving) 46 | // Timing: Micros disabled (Timer0 is in use) 47 | // 48 | // Reset pin must be disabled by writing respective fuse after uploading the code: 49 | // avrdude -p attiny13 -c usbasp -U lfuse:w:0x2a:m -U hfuse:w:0xfe:m 50 | // Warning: You will need a high voltage fuse resetter to undo this change! 51 | // 52 | // Note: The internal oscillator may need to be calibrated for the device 53 | // to function properly. 54 | // 55 | // 2019 by Stefan Wagner 56 | // Project Files (EasyEDA): https://easyeda.com/wagiminator 57 | // Project Files (Github): https://github.com/wagiminator 58 | // License: http://creativecommons.org/licenses/by-sa/3.0/ 59 | // 60 | // Based on the work of: 61 | // - San Bergmans (https://www.sbprojects.net/knowledge/ir/index.php), 62 | // - Christoph Niessen (http://chris.cnie.de/avr/tcm231421.html), 63 | // - David Johnson-Davies (http://www.technoblogy.com/show?UVE). 64 | 65 | 66 | // oscillator calibration value (uncomment and set if necessary) 67 | //#define OSCCAL_VAL 48 68 | 69 | // libraries 70 | #include 71 | #include 72 | #include 73 | #include 74 | 75 | // IR codes (use 16-bit address for extended NEC protocol) 76 | #define ADDR 0x04 // Address: LG TV 77 | #define KEY1 0x02 // Command: Volume+ 78 | #define KEY2 0x00 // Command: Channel+ 79 | #define KEY3 0x03 // Command: Volume- 80 | #define KEY4 0x01 // Command: Channel- 81 | #define KEY5 0x08 // Command: Power 82 | 83 | // define values for 38kHz PWM frequency and 25% duty cycle 84 | #define TOP 31 // 1200kHz / 38kHz - 1 = 31 85 | #define DUTY 7 // 1200kHz / 38kHz / 4 - 1 = 7 86 | 87 | // macros to switch on/off IR LED 88 | #define IRon() DDRB |= 0b00000010 // PB1 as output = IR at OC0B (38 kHz) 89 | #define IRoff() DDRB &= 0b11111101 // PB1 as input = LED off 90 | 91 | // macros to modulate the signals according to NEC protocol with compensated timings 92 | #define startPulse() {IRon(); _delay_us(9000); IRoff(); _delay_us(4500);} 93 | #define repeatPulse() {IRon(); _delay_us(9000); IRoff(); _delay_us(2250);} 94 | #define normalPulse() {IRon(); _delay_us( 562); IRoff(); _delay_us( 557);} 95 | #define bit1Pause() _delay_us(1120) // 1687.5us - 562.5us = 1125us 96 | #define repeatCode() {_delay_ms(40); repeatPulse(); normalPulse(); _delay_ms(56);} 97 | 98 | 99 | // send a single byte via IR 100 | void sendByte(uint8_t value) { 101 | for (uint8_t i=8; i; i--, value>>=1) { // send 8 bits, LSB first 102 | normalPulse(); // 562us burst, 562us pause 103 | if (value & 1) bit1Pause(); // extend pause if bit is 1 104 | } 105 | } 106 | 107 | // send complete telegram (start frame + address + command) via IR 108 | void sendCode(uint8_t cmd) { 109 | startPulse(); // 9ms burst + 4.5ms pause to signify start of transmission 110 | #if ADDR > 0xFF // if extended NEC protocol (16-bit address): 111 | sendByte(ADDR & 0xFF); // send address low byte 112 | sendByte(ADDR >> 8); // send address high byte 113 | #else // if standard NEC protocol (8-bit address): 114 | sendByte(ADDR); // send address byte 115 | sendByte(~ADDR); // send inverse of address byte 116 | #endif 117 | sendByte(cmd); // send command byte 118 | sendByte(~cmd); // send inverse of command byte 119 | normalPulse(); // 562us burst to signify end of transmission 120 | } 121 | 122 | // main function 123 | int main(void) { 124 | // set oscillator calibration value 125 | #ifdef OSCCAL_VAL 126 | OSCCAL = OSCCAL_VAL; // set the value if defined above 127 | #endif 128 | 129 | // setup pins 130 | DDRB = 0b00000000; // all pins are input pins by now 131 | PORTB = 0b00111101; // pull-up for button pins 132 | 133 | // set timer0 to toggle IR pin at 38 kHz 134 | TCCR0A = 0b00100011; // PWM on OC0B (PB1) 135 | TCCR0B = 0b00001001; // no prescaler 136 | OCR0A = TOP; // 38 kHz PWM frequency 137 | OCR0B = DUTY; // 25 % duty cycle 138 | 139 | // setup pin change interrupt 140 | GIMSK = 0b00100000; // turn on pin change interrupts 141 | PCMSK = 0b00111101; // turn on interrupt on button pins 142 | SREG |= 0b10000000; // enable global interrupts 143 | 144 | // disable unused peripherals and set sleep mode to save power 145 | ADCSRA = 0b00000000; // disable ADC 146 | ACSR = 0b10000000; // disable analog comperator 147 | PRR = 0b00000001; // shut down ADC 148 | set_sleep_mode(SLEEP_MODE_PWR_DOWN); // set sleep mode to power down 149 | 150 | // main loop 151 | while(1) { 152 | sleep_mode(); // sleep until button is pressed 153 | _delay_ms(1); // debounce 154 | uint8_t buttons = ~PINB & 0b00111101; // read button pins 155 | switch (buttons) { // send corresponding IR code 156 | case 0b00000001: sendCode(KEY1); break; 157 | case 0b00000100: sendCode(KEY2); break; 158 | case 0b00001000: sendCode(KEY3); break; 159 | case 0b00010000: sendCode(KEY4); break; 160 | case 0b00100000: sendCode(KEY5); break; 161 | default: break; 162 | } 163 | while (~PINB & 0b00111101) repeatCode(); // send repeat command until button is released 164 | } 165 | } 166 | 167 | // pin change interrupt service routine 168 | EMPTY_INTERRUPT (PCINT0_vect); // nothing to be done here, just wake up from sleep 169 | -------------------------------------------------------------------------------- /software/NEC_5_buttons/main.c: -------------------------------------------------------------------------------- 1 | // tinyIRremote for ATtiny13A - NEC, 5 Buttons 2 | // 3 | // IR remote control using an ATtiny 13A. Timer0 generates a 38kHz 4 | // carrier frequency with a duty cycle of 25% on the output pin to the 5 | // IR LED. The signal (NEC protocol) is modulated by toggling the pin 6 | // to input/output. The protocol uses pulse distance modulation. 7 | // 8 | // +---------+ +-+ +-+ +-+ +-+ +- ON 9 | // | | | | | | | | | | | bit0: 562.5us 10 | // | 9ms |4.5ms| |0| | 1 | | 1 | |0| ... 11 | // | | | | | | | | | | | bit1: 1687.5us 12 | // ------+ +-----+ +-+ +---+ +---+ +-+ OFF 13 | // 14 | // IR telegram starts with a 9ms leading burst followed by a 4.5ms pause. 15 | // Afterwards 4 data bytes are transmitted, least significant bit first. 16 | // A "0" bit is a 562.5us burst followed by a 562.5us pause, a "1" bit is 17 | // a 562.5us burst followed by a 1687.5us pause. A final 562.5us burst 18 | // signifies the end of the transmission. The four data bytes are in order: 19 | // - the 8-bit address for the receiving device, 20 | // - the 8-bit logical inverse of the address, 21 | // - the 8-bit command and 22 | // - the 8-bit logical inverse of the command. 23 | // The Extended NEC protocol uses 16-bit addresses. Instead of sending an 24 | // 8-bit address and its logically inverse, first the low byte and then the 25 | // high byte of the address is transmitted. 26 | // 27 | // If the key on the remote controller is kept depressed, a repeat code 28 | // will be issued consisting of a 9ms leading burst, a 2.25ms pause and 29 | // a 562.5us burst to mark the end. The repeat code will continue to be 30 | // sent out at 108ms intervals, until the key is finally released. 31 | // 32 | // The code utilizes the sleep mode power down function. The device will 33 | // work several months on a CR2032 battery. 34 | // 35 | // +-\/-+ 36 | // KEY5 --- A0 (D5) PB5 1| |8 Vcc 37 | // KEY3 --- A3 (D3) PB3 2| |7 PB2 (D2) A1 --- KEY2 38 | // KEY4 --- A2 (D4) PB4 3| |6 PB1 (D1) ------ IR LED 39 | // GND 4| |5 PB0 (D0) ------ KEY1 40 | // +----+ 41 | // 42 | // Controller: ATtiny13A 43 | // Clockspeed: 1.2 MHz internal 44 | // 45 | // Reset pin must be disabled by writing respective fuse after uploading the code: 46 | // avrdude -p attiny13 -c usbasp -U lfuse:w:0x2a:m -U hfuse:w:0xfe:m 47 | // Warning: You will need a high voltage fuse resetter to undo this change! 48 | // 49 | // Note: The internal oscillator may need to be calibrated for the device 50 | // to function properly. 51 | // 52 | // 2019 by Stefan Wagner 53 | // Project Files (EasyEDA): https://easyeda.com/wagiminator 54 | // Project Files (Github): https://github.com/wagiminator 55 | // License: http://creativecommons.org/licenses/by-sa/3.0/ 56 | // 57 | // Based on the work of: 58 | // - San Bergmans (https://www.sbprojects.net/knowledge/ir/index.php), 59 | // - Christoph Niessen (http://chris.cnie.de/avr/tcm231421.html), 60 | // - David Johnson-Davies (http://www.technoblogy.com/show?UVE). 61 | 62 | 63 | // oscillator calibration value (uncomment and set if necessary) 64 | //#define OSCCAL_VAL 48 65 | 66 | // libraries 67 | #include 68 | #include 69 | #include 70 | #include 71 | 72 | // IR codes (use 16-bit address for extended NEC protocol) 73 | #define ADDR 0x04 // Address: LG TV 74 | #define KEY1 0x02 // Command: Volume+ 75 | #define KEY2 0x00 // Command: Channel+ 76 | #define KEY3 0x03 // Command: Volume- 77 | #define KEY4 0x01 // Command: Channel- 78 | #define KEY5 0x08 // Command: Power 79 | 80 | // define values for 38kHz PWM frequency and 25% duty cycle 81 | #define TOP 31 // 1200kHz / 38kHz - 1 = 31 82 | #define DUTY 7 // 1200kHz / 38kHz / 4 - 1 = 7 83 | 84 | // macros to switch on/off IR LED 85 | #define IRon() DDRB |= 0b00000010 // PB1 as output = IR at OC0B (38 kHz) 86 | #define IRoff() DDRB &= 0b11111101 // PB1 as input = LED off 87 | 88 | // macros to modulate the signals according to NEC protocol with compensated timings 89 | #define startPulse() {IRon(); _delay_us(9000); IRoff(); _delay_us(4500);} 90 | #define repeatPulse() {IRon(); _delay_us(9000); IRoff(); _delay_us(2250);} 91 | #define normalPulse() {IRon(); _delay_us( 562); IRoff(); _delay_us( 557);} 92 | #define bit1Pause() _delay_us(1120) // 1687.5us - 562.5us = 1125us 93 | #define repeatCode() {_delay_ms(40); repeatPulse(); normalPulse(); _delay_ms(56);} 94 | 95 | 96 | // send a single byte via IR 97 | void sendByte(uint8_t value) { 98 | for (uint8_t i=8; i; i--, value>>=1) { // send 8 bits, LSB first 99 | normalPulse(); // 562us burst, 562us pause 100 | if (value & 1) bit1Pause(); // extend pause if bit is 1 101 | } 102 | } 103 | 104 | // send complete telegram (start frame + address + command) via IR 105 | void sendCode(uint8_t cmd) { 106 | startPulse(); // 9ms burst + 4.5ms pause to signify start of transmission 107 | #if ADDR > 0xFF // if extended NEC protocol (16-bit address): 108 | sendByte(ADDR & 0xFF); // send address low byte 109 | sendByte(ADDR >> 8); // send address high byte 110 | #else // if standard NEC protocol (8-bit address): 111 | sendByte(ADDR); // send address byte 112 | sendByte(~ADDR); // send inverse of address byte 113 | #endif 114 | sendByte(cmd); // send command byte 115 | sendByte(~cmd); // send inverse of command byte 116 | normalPulse(); // 562us burst to signify end of transmission 117 | } 118 | 119 | // main function 120 | int main(void) { 121 | // set oscillator calibration value 122 | #ifdef OSCCAL_VAL 123 | OSCCAL = OSCCAL_VAL; // set the value if defined above 124 | #endif 125 | 126 | // setup pins 127 | DDRB = 0b00000000; // all pins are input pins by now 128 | PORTB = 0b00111101; // pull-up for button pins 129 | 130 | // set timer0 to toggle IR pin at 38 kHz 131 | TCCR0A = 0b00100011; // PWM on OC0B (PB1) 132 | TCCR0B = 0b00001001; // no prescaler 133 | OCR0A = TOP; // 38 kHz PWM frequency 134 | OCR0B = DUTY; // 25 % duty cycle 135 | 136 | // setup pin change interrupt 137 | GIMSK = 0b00100000; // turn on pin change interrupts 138 | PCMSK = 0b00111101; // turn on interrupt on button pins 139 | SREG |= 0b10000000; // enable global interrupts 140 | 141 | // disable unused peripherals and set sleep mode to save power 142 | ADCSRA = 0b00000000; // disable ADC 143 | ACSR = 0b10000000; // disable analog comperator 144 | PRR = 0b00000001; // shut down ADC 145 | set_sleep_mode(SLEEP_MODE_PWR_DOWN); // set sleep mode to power down 146 | 147 | // main loop 148 | while(1) { 149 | sleep_mode(); // sleep until button is pressed 150 | _delay_ms(1); // debounce 151 | uint8_t buttons = ~PINB & 0b00111101; // read button pins 152 | switch (buttons) { // send corresponding IR code 153 | case 0b00000001: sendCode(KEY1); break; 154 | case 0b00000100: sendCode(KEY2); break; 155 | case 0b00001000: sendCode(KEY3); break; 156 | case 0b00010000: sendCode(KEY4); break; 157 | case 0b00100000: sendCode(KEY5); break; 158 | default: break; 159 | } 160 | while (~PINB & 0b00111101) repeatCode(); // send repeat command until button is released 161 | } 162 | } 163 | 164 | // pin change interrupt service routine 165 | EMPTY_INTERRUPT (PCINT0_vect); // nothing to be done here, just wake up from sleep 166 | -------------------------------------------------------------------------------- /software/NEC_5_buttons/makefile: -------------------------------------------------------------------------------- 1 | # Project: tinyRemote - 5 buttons 2 | # Author: Stefan Wagner 3 | # Year: 2019 4 | # URL: https://easyeda.com/wagiminator 5 | # https://github.com/wagiminator 6 | # 7 | # Type "make help" in the command line. 8 | 9 | # Compiler Options 10 | DEVICE = attiny13a 11 | CLOCK = 1200000 12 | 13 | # Programmer Options 14 | PROGRMR = usbasp 15 | TGTDEV = attiny13 16 | LFUSE = 0x2a 17 | HFUSE = 0xfe 18 | 19 | # Objects 20 | OBJECTS = main.o 21 | 22 | # Commands 23 | AVRDUDE = avrdude -c $(PROGRMR) -p $(TGTDEV) 24 | COMPILE = avr-gcc -Wall -Os -flto -mmcu=$(DEVICE) -DF_CPU=$(CLOCK) -DDEBUG_LEVEL=0 25 | CLEAN = rm -f main.lst main.obj main.cof main.list main.map main.eep.hex *.o main.s 26 | 27 | # Symbolic Targets 28 | all: help 29 | 30 | help: 31 | @echo "Use the following commands:" 32 | @echo "make hex compile and build main.hex for $(DEVICE)" 33 | @echo "make asm compile and disassemble to main.asm for $(DEVICE)" 34 | @echo "make bin compile and build main.bin for $(DEVICE)" 35 | @echo "make upload compile and upload to $(DEVICE) using $(PROGRMR)" 36 | @echo "make fuses burn fuses of $(DEVICE) using $(PROGRMR) programmer" 37 | @echo "make install compile, upload and burn fuses for $(DEVICE)" 38 | @echo "make clean remove all build files" 39 | 40 | bin: $(OBJECTS) 41 | @echo "Building main.bin ..." 42 | @$(COMPILE) -o main.bin $(OBJECTS) 43 | @echo "Removing temporary files ..." 44 | @$(CLEAN) 45 | 46 | hex: bin 47 | @echo "Building main.hex ..." 48 | @avr-objcopy -j .text -j .data -O ihex main.bin main.hex 49 | @echo "FLASH: $(shell avr-size -d main.bin | awk '/[0-9]/ {print $$1 + $$2}') bytes" 50 | @echo "SRAM: $(shell avr-size -d main.bin | awk '/[0-9]/ {print $$2 + $$3}') bytes" 51 | @echo "Removing main.bin ..." 52 | @rm -f main.bin 53 | 54 | asm: bin 55 | @echo "Disassembling to main.asm ..." 56 | @avr-objdump -d main.bin > main.asm 57 | @echo "Removing main.bin ..." 58 | @rm -f main.bin 59 | 60 | upload: hex 61 | @echo "Uploading to $(TGTDEV) ..." 62 | @$(AVRDUDE) -U flash:w:main.hex:i 63 | 64 | fuses: 65 | @echo "Burning fuses of $(TGTDEV) ..." 66 | @echo "Warning: RESET pin will be disabled !" 67 | @$(AVRDUDE) -U hfuse:w:$(HFUSE):m -U lfuse:w:$(LFUSE):m 68 | 69 | install: upload fuses 70 | 71 | clean: 72 | @echo "Cleaning all up ..." 73 | @$(CLEAN) 74 | @rm -f main.bin main.hex main.asm 75 | 76 | .c.o: 77 | @echo "Compiling $< for $(DEVICE) @ $(CLOCK)Hz ..." 78 | @$(COMPILE) -c $< -o $@ 79 | 80 | .S.o: 81 | @echo "Compiling $< for $(DEVICE) @ $(CLOCK)Hz ..." 82 | @$(COMPILE) -x assembler-with-cpp -c $< -o $@ 83 | 84 | .c.s: 85 | @echo "Compiling $< for $(DEVICE) @ $(CLOCK)Hz ..." 86 | @$(COMPILE) -S $< -o $@ 87 | -------------------------------------------------------------------------------- /software/RC5_4_buttons/TinyRemote_t13_RC5_4B.ino: -------------------------------------------------------------------------------- 1 | // tinyIRremote for ATtiny13A - RC-5, 4 Buttons 2 | // 3 | // IR remote control using an ATtiny 13A. Timer0 generates a 36kHz 4 | // carrier frequency with a duty cycle of 25% on the output pin to the 5 | // IR LED. The signal (RC-5 protocol) is modulated by toggling the pin 6 | // to input/output. The protocol uses bi-phase modulation (Manchester 7 | // coding). 8 | // 9 | // +-------+ +-------+ ON 10 | // | | 11 | // 889us | 889us 889us | 889us 12 | // | | 13 | // +-------+ +-------+ OFF 14 | // 15 | // |<-- Bit "0" -->| |<-- Bit "1" -->| 16 | // 17 | // IR telegram starts with two start bits. The first bit is always "1", 18 | // the second bit is "1" in the original protocol and inverted 7th bit 19 | // of the command in the extended RC-5 protocol. The third bit toggles 20 | // after each button release. The next five bits represent the device 21 | // address, MSB first and the last six bits represent the command, MSB 22 | // first. 23 | // 24 | // As long as a key remains down the telegram will be repeated every 25 | // 114ms without changing the toggle bit. 26 | // 27 | // The code utilizes the sleep mode power down function. The device will 28 | // work several months on a CR2032 battery. 29 | // 30 | // +-\/-+ 31 | // KEY5 --- A0 (D5) PB5 1| |8 Vcc 32 | // KEY3 --- A3 (D3) PB3 2| |7 PB2 (D2) A1 --- KEY2 33 | // KEY4 --- A2 (D4) PB4 3| |6 PB1 (D1) ------ IR LED 34 | // GND 4| |5 PB0 (D0) ------ KEY1 35 | // +----+ 36 | // 37 | // Controller: ATtiny13 38 | // Core: MicroCore (https://github.com/MCUdude/MicroCore) 39 | // Clockspeed: 1.2 MHz internal 40 | // BOD: BOD disabled (energy saving) 41 | // Timing: Micros disabled (Timer0 is in use) 42 | // 43 | // Note: The internal oscillator may need to be calibrated for the device 44 | // to function properly. 45 | // 46 | // 2019 by Stefan Wagner 47 | // Project Files (EasyEDA): https://easyeda.com/wagiminator 48 | // Project Files (Github): https://github.com/wagiminator 49 | // License: http://creativecommons.org/licenses/by-sa/3.0/ 50 | // 51 | // Based on the work of: 52 | // - San Bergmans (https://www.sbprojects.net/knowledge/ir/index.php), 53 | // - Christoph Niessen (http://chris.cnie.de/avr/tcm231421.html), 54 | // - David Johnson-Davies (http://www.technoblogy.com/show?UVE). 55 | 56 | 57 | // oscillator calibration value (uncomment and set if necessary) 58 | //#define OSCCAL_VAL 48 59 | 60 | // libraries 61 | #include 62 | #include 63 | #include 64 | #include 65 | 66 | // IR codes 67 | #define ADDR 0x00 // Address: Philips TV 68 | #define KEY1 0x0D // Command: Volume+ 69 | #define KEY2 0x11 // Command: Channel+ 70 | #define KEY3 0x0E // Command: Volume- 71 | #define KEY4 0x12 // Command: Channel- 72 | 73 | // define values for 36kHz PWM frequency and 25% duty cycle 74 | #define TOP 32 // 1200kHz / 36kHz - 1 = 32 75 | #define DUTY 7 // 1200kHz / 36kHz / 4 - 1 = 7 76 | 77 | // macros to switch on/off IR LED 78 | #define IRon() DDRB |= 0b00000010 // PB1 as output = IR at OC0B (36 kHz) 79 | #define IRoff() DDRB &= 0b11111101 // PB1 as input = LED off 80 | 81 | // macros to modulate the signals according to RC-5 protocol with compensated timings 82 | #define bit0Pulse() {IRon(); _delay_us(889); IRoff(); _delay_us(884);} 83 | #define bit1Pulse() {IRoff(); _delay_us(889); IRon(); _delay_us(884);} 84 | #define repeatDelay() _delay_ms(89) // 114ms - 14 * 2 * 889us 85 | 86 | // bitmasks 87 | #define startBit 0b0010000000000000 88 | #define cmdBit7 0b0001000000000000 89 | #define toggleBit 0b0000100000000000 90 | 91 | // toggle variable 92 | uint8_t toggle = 0; 93 | 94 | // send complete telegram (startbits + togglebit + address + command) via IR 95 | void sendCode(uint8_t cmd) { 96 | // prepare the message 97 | uint16_t message = ADDR << 6; // shift address to the right position 98 | message |= (cmd & 0x3f); // add the low 6 bits of the command 99 | if (~cmd & 0x40) message |= cmdBit7; // add inverse of 7th command bit 100 | message |= startBit; // add start bit 101 | if (toggle) message |= toggleBit; // add toggle bit 102 | 103 | // send the message 104 | do { 105 | uint16_t bitmask = startBit; // set the bitmask to first bit to send 106 | for(uint8_t i=14; i; i--, bitmask>>=1) { // 14 bits, MSB first 107 | (message & bitmask) ? (bit1Pulse()) : (bit0Pulse()); // send the bit 108 | } 109 | IRoff(); // switch off IR LED 110 | repeatDelay(); // wait for next repeat 111 | } while(~PINB & 0b00011101); // repeat sending until button is released 112 | toggle ^= 1; // toggle the toggle bit 113 | } 114 | 115 | // main function 116 | int main(void) { 117 | // set oscillator calibration value 118 | #ifdef OSCCAL_VAL 119 | OSCCAL = OSCCAL_VAL; // set the value if defined above 120 | #endif 121 | 122 | // setup pins 123 | DDRB = 0b00000000; // all pins are input pins by now 124 | PORTB = 0b00011101; // pull-up for button pins 125 | 126 | // set timer0 to toggle IR pin at 38 kHz 127 | TCCR0A = 0b00100011; // PWM on OC0B (PB1) 128 | TCCR0B = 0b00001001; // no prescaler 129 | OCR0A = TOP; // 38 kHz PWM frequency 130 | OCR0B = DUTY; // 25 % duty cycle 131 | 132 | // setup pin change interrupt 133 | GIMSK = 0b00100000; // turn on pin change interrupts 134 | PCMSK = 0b00011101; // turn on interrupt on button pins 135 | SREG |= 0b10000000; // enable global interrupts 136 | 137 | // disable unused peripherals and set sleep mode to save power 138 | ADCSRA = 0b00000000; // disable ADC 139 | ACSR = 0b10000000; // disable analog comperator 140 | PRR = 0b00000001; // shut down ADC 141 | set_sleep_mode(SLEEP_MODE_PWR_DOWN); // set sleep mode to power down 142 | 143 | // main loop 144 | while(1) { 145 | sleep_mode(); // sleep until button is pressed 146 | _delay_ms(1); // debounce 147 | uint8_t buttons = ~PINB & 0b00011101; // read button pins 148 | switch (buttons) { // send corresponding IR code 149 | case 0b00000001: sendCode(KEY1); break; 150 | case 0b00000100: sendCode(KEY2); break; 151 | case 0b00001000: sendCode(KEY3); break; 152 | case 0b00010000: sendCode(KEY4); break; 153 | default: break; 154 | } 155 | } 156 | } 157 | 158 | // pin change interrupt service routine 159 | EMPTY_INTERRUPT (PCINT0_vect); // nothing to be done here, just wake up from sleep 160 | -------------------------------------------------------------------------------- /software/RC5_4_buttons/main.c: -------------------------------------------------------------------------------- 1 | // tinyIRremote for ATtiny13A - RC-5, 4 Buttons 2 | // 3 | // IR remote control using an ATtiny 13A. Timer0 generates a 36kHz 4 | // carrier frequency with a duty cycle of 25% on the output pin to the 5 | // IR LED. The signal (RC-5 protocol) is modulated by toggling the pin 6 | // to input/output. The protocol uses bi-phase modulation (Manchester 7 | // coding). 8 | // 9 | // +-------+ +-------+ ON 10 | // | | 11 | // 889us | 889us 889us | 889us 12 | // | | 13 | // +-------+ +-------+ OFF 14 | // 15 | // |<-- Bit "0" -->| |<-- Bit "1" -->| 16 | // 17 | // IR telegram starts with two start bits. The first bit is always "1", 18 | // the second bit is "1" in the original protocol and inverted 7th bit 19 | // of the command in the extended RC-5 protocol. The third bit toggles 20 | // after each button release. The next five bits represent the device 21 | // address, MSB first and the last six bits represent the command, MSB 22 | // first. 23 | // 24 | // As long as a key remains down the telegram will be repeated every 25 | // 114ms without changing the toggle bit. 26 | // 27 | // The code utilizes the sleep mode power down function. The device will 28 | // work several months on a CR2032 battery. 29 | // 30 | // +-\/-+ 31 | // KEY5 --- A0 (D5) PB5 1| |8 Vcc 32 | // KEY3 --- A3 (D3) PB3 2| |7 PB2 (D2) A1 --- KEY2 33 | // KEY4 --- A2 (D4) PB4 3| |6 PB1 (D1) ------ IR LED 34 | // GND 4| |5 PB0 (D0) ------ KEY1 35 | // +----+ 36 | // 37 | // Controller: ATtiny13A 38 | // Clockspeed: 1.2 MHz internal 39 | // 40 | // Note: The internal oscillator may need to be calibrated for the device 41 | // to function properly. 42 | // 43 | // 2020 by Stefan Wagner 44 | // Project Files (EasyEDA): https://easyeda.com/wagiminator 45 | // Project Files (Github): https://github.com/wagiminator 46 | // License: http://creativecommons.org/licenses/by-sa/3.0/ 47 | // 48 | // Based on the work of: 49 | // - San Bergmans (https://www.sbprojects.net/knowledge/ir/index.php), 50 | // - Christoph Niessen (http://chris.cnie.de/avr/tcm231421.html), 51 | // - David Johnson-Davies (http://www.technoblogy.com/show?UVE). 52 | 53 | 54 | // oscillator calibration value (uncomment and set if necessary) 55 | //#define OSCCAL_VAL 48 56 | 57 | // libraries 58 | #include 59 | #include 60 | #include 61 | #include 62 | 63 | // IR codes 64 | #define ADDR 0x00 // Address: Philips TV 65 | #define KEY1 0x0D // Command: Volume+ 66 | #define KEY2 0x11 // Command: Channel+ 67 | #define KEY3 0x0E // Command: Volume- 68 | #define KEY4 0x12 // Command: Channel- 69 | 70 | // define values for 36kHz PWM frequency and 25% duty cycle 71 | #define TOP 32 // 1200kHz / 36kHz - 1 = 32 72 | #define DUTY 7 // 1200kHz / 36kHz / 4 - 1 = 7 73 | 74 | // macros to switch on/off IR LED 75 | #define IRon() DDRB |= 0b00000010 // PB1 as output = IR at OC0B (36 kHz) 76 | #define IRoff() DDRB &= 0b11111101 // PB1 as input = LED off 77 | 78 | // macros to modulate the signals according to RC-5 protocol with compensated timings 79 | #define bit0Pulse() {IRon(); _delay_us(889); IRoff(); _delay_us(884);} 80 | #define bit1Pulse() {IRoff(); _delay_us(889); IRon(); _delay_us(884);} 81 | #define repeatDelay() _delay_ms(89) // 114ms - 14 * 2 * 889us 82 | 83 | // bitmasks 84 | #define startBit 0b0010000000000000 85 | #define cmdBit7 0b0001000000000000 86 | #define toggleBit 0b0000100000000000 87 | 88 | // toggle variable 89 | uint8_t toggle = 0; 90 | 91 | // send complete telegram (startbits + togglebit + address + command) via IR 92 | void sendCode(uint8_t cmd) { 93 | // prepare the message 94 | uint16_t message = ADDR << 6; // shift address to the right position 95 | message |= (cmd & 0x3f); // add the low 6 bits of the command 96 | if (~cmd & 0x40) message |= cmdBit7; // add inverse of 7th command bit 97 | message |= startBit; // add start bit 98 | if (toggle) message |= toggleBit; // add toggle bit 99 | 100 | // send the message 101 | do { 102 | uint16_t bitmask = startBit; // set the bitmask to first bit to send 103 | for(uint8_t i=14; i; i--, bitmask>>=1) { // 14 bits, MSB first 104 | (message & bitmask) ? (bit1Pulse()) : (bit0Pulse()); // send the bit 105 | } 106 | IRoff(); // switch off IR LED 107 | repeatDelay(); // wait for next repeat 108 | } while(~PINB & 0b00011101); // repeat sending until button is released 109 | toggle ^= 1; // toggle the toggle bit 110 | } 111 | 112 | // main function 113 | int main(void) { 114 | // set oscillator calibration value 115 | #ifdef OSCCAL_VAL 116 | OSCCAL = OSCCAL_VAL; // set the value if defined above 117 | #endif 118 | 119 | // setup pins 120 | DDRB = 0b00000000; // all pins are input pins by now 121 | PORTB = 0b00011101; // pull-up for button pins 122 | 123 | // set timer0 to toggle IR pin at 38 kHz 124 | TCCR0A = 0b00100011; // PWM on OC0B (PB1) 125 | TCCR0B = 0b00001001; // no prescaler 126 | OCR0A = TOP; // 38 kHz PWM frequency 127 | OCR0B = DUTY; // 25 % duty cycle 128 | 129 | // setup pin change interrupt 130 | GIMSK = 0b00100000; // turn on pin change interrupts 131 | PCMSK = 0b00011101; // turn on interrupt on button pins 132 | SREG |= 0b10000000; // enable global interrupts 133 | 134 | // disable unused peripherals and set sleep mode to save power 135 | ADCSRA = 0b00000000; // disable ADC 136 | ACSR = 0b10000000; // disable analog comperator 137 | PRR = 0b00000001; // shut down ADC 138 | set_sleep_mode(SLEEP_MODE_PWR_DOWN); // set sleep mode to power down 139 | 140 | // main loop 141 | while(1) { 142 | sleep_mode(); // sleep until button is pressed 143 | _delay_ms(1); // debounce 144 | uint8_t buttons = ~PINB & 0b00011101; // read button pins 145 | switch (buttons) { // send corresponding IR code 146 | case 0b00000001: sendCode(KEY1); break; 147 | case 0b00000100: sendCode(KEY2); break; 148 | case 0b00001000: sendCode(KEY3); break; 149 | case 0b00010000: sendCode(KEY4); break; 150 | default: break; 151 | } 152 | } 153 | } 154 | 155 | // pin change interrupt service routine 156 | EMPTY_INTERRUPT (PCINT0_vect); // nothing to be done here, just wake up from sleep 157 | -------------------------------------------------------------------------------- /software/RC5_4_buttons/makefile: -------------------------------------------------------------------------------- 1 | # Project: tinyRemote - 4 buttons 2 | # Author: Stefan Wagner 3 | # Year: 2019 4 | # URL: https://easyeda.com/wagiminator 5 | # https://github.com/wagiminator 6 | # 7 | # Type "make help" in the command line. 8 | 9 | # Compiler Options 10 | DEVICE = attiny13a 11 | CLOCK = 1200000 12 | 13 | # Programmer Options 14 | PROGRMR = usbasp 15 | TGTDEV = attiny13 16 | LFUSE = 0x2a 17 | HFUSE = 0xff 18 | 19 | # Objects 20 | OBJECTS = main.o 21 | 22 | # Commands 23 | AVRDUDE = avrdude -c $(PROGRMR) -p $(TGTDEV) 24 | COMPILE = avr-gcc -Wall -Os -flto -mmcu=$(DEVICE) -DF_CPU=$(CLOCK) -DDEBUG_LEVEL=0 25 | CLEAN = rm -f main.lst main.obj main.cof main.list main.map main.eep.hex *.o main.s 26 | 27 | # Symbolic Targets 28 | all: help 29 | 30 | help: 31 | @echo "Use the following commands:" 32 | @echo "make hex compile and build main.hex for $(DEVICE)" 33 | @echo "make asm compile and disassemble to main.asm for $(DEVICE)" 34 | @echo "make bin compile and build main.bin for $(DEVICE)" 35 | @echo "make upload compile and upload to $(DEVICE) using $(PROGRMR)" 36 | @echo "make fuses burn fuses of $(DEVICE) using $(PROGRMR) programmer" 37 | @echo "make install compile, upload and burn fuses for $(DEVICE)" 38 | @echo "make clean remove all build files" 39 | 40 | bin: $(OBJECTS) 41 | @echo "Building main.bin ..." 42 | @$(COMPILE) -o main.bin $(OBJECTS) 43 | @echo "Removing temporary files ..." 44 | @$(CLEAN) 45 | 46 | hex: bin 47 | @echo "Building main.hex ..." 48 | @avr-objcopy -j .text -j .data -O ihex main.bin main.hex 49 | @echo "FLASH: $(shell avr-size -d main.bin | awk '/[0-9]/ {print $$1 + $$2}') bytes" 50 | @echo "SRAM: $(shell avr-size -d main.bin | awk '/[0-9]/ {print $$2 + $$3}') bytes" 51 | @echo "Removing main.bin ..." 52 | @rm -f main.bin 53 | 54 | asm: bin 55 | @echo "Disassembling to main.asm ..." 56 | @avr-objdump -d main.bin > main.asm 57 | @echo "Removing main.bin ..." 58 | @rm -f main.bin 59 | 60 | upload: hex 61 | @echo "Uploading to $(TGTDEV) ..." 62 | @$(AVRDUDE) -U flash:w:main.hex:i 63 | 64 | fuses: 65 | @echo "Burning fuses of $(TGTDEV) ..." 66 | @$(AVRDUDE) -U hfuse:w:$(HFUSE):m -U lfuse:w:$(LFUSE):m 67 | 68 | install: upload fuses 69 | 70 | clean: 71 | @echo "Cleaning all up ..." 72 | @$(CLEAN) 73 | @rm -f main.bin main.hex main.asm 74 | 75 | .c.o: 76 | @echo "Compiling $< for $(DEVICE) @ $(CLOCK)Hz ..." 77 | @$(COMPILE) -c $< -o $@ 78 | 79 | .S.o: 80 | @echo "Compiling $< for $(DEVICE) @ $(CLOCK)Hz ..." 81 | @$(COMPILE) -x assembler-with-cpp -c $< -o $@ 82 | 83 | .c.s: 84 | @echo "Compiling $< for $(DEVICE) @ $(CLOCK)Hz ..." 85 | @$(COMPILE) -S $< -o $@ 86 | -------------------------------------------------------------------------------- /software/RC5_5_buttons/TinyRemote_t13_RC5_5B.ino: -------------------------------------------------------------------------------- 1 | // tinyIRremote for ATtiny13A - RC-5, 5 Buttons 2 | // 3 | // IR remote control using an ATtiny 13A. Timer0 generates a 36kHz 4 | // carrier frequency with a duty cycle of 25% on the output pin to the 5 | // IR LED. The signal (RC-5 protocol) is modulated by toggling the pin 6 | // to input/output. The protocol uses bi-phase modulation (Manchester 7 | // coding). 8 | // 9 | // +-------+ +-------+ ON 10 | // | | 11 | // 889us | 889us 889us | 889us 12 | // | | 13 | // +-------+ +-------+ OFF 14 | // 15 | // |<-- Bit "0" -->| |<-- Bit "1" -->| 16 | // 17 | // IR telegram starts with two start bits. The first bit is always "1", 18 | // the second bit is "1" in the original protocol and inverted 7th bit 19 | // of the command in the extended RC-5 protocol. The third bit toggles 20 | // after each button release. The next five bits represent the device 21 | // address, MSB first and the last six bits represent the command, MSB 22 | // first. 23 | // 24 | // As long as a key remains down the telegram will be repeated every 25 | // 114ms without changing the toggle bit. 26 | // 27 | // The code utilizes the sleep mode power down function. The device will 28 | // work several months on a CR2032 battery. 29 | // 30 | // +-\/-+ 31 | // KEY5 --- A0 (D5) PB5 1| |8 Vcc 32 | // KEY3 --- A3 (D3) PB3 2| |7 PB2 (D2) A1 --- KEY2 33 | // KEY4 --- A2 (D4) PB4 3| |6 PB1 (D1) ------ IR LED 34 | // GND 4| |5 PB0 (D0) ------ KEY1 35 | // +----+ 36 | // 37 | // Controller: ATtiny13 38 | // Core: MicroCore (https://github.com/MCUdude/MicroCore) 39 | // Clockspeed: 1.2 MHz internal 40 | // BOD: BOD disabled (energy saving) 41 | // Timing: Micros disabled (Timer0 is in use) 42 | // 43 | // Reset pin must be disabled by writing respective fuse after uploading the code: 44 | // avrdude -p attiny13 -c usbasp -U lfuse:w:0x2a:m -U hfuse:w:0xfe:m 45 | // Warning: You will need a high voltage fuse resetter to undo this change! 46 | // 47 | // Note: The internal oscillator may need to be calibrated for the device 48 | // to function properly. 49 | // 50 | // 2019 by Stefan Wagner 51 | // Project Files (EasyEDA): https://easyeda.com/wagiminator 52 | // Project Files (Github): https://github.com/wagiminator 53 | // License: http://creativecommons.org/licenses/by-sa/3.0/ 54 | // 55 | // Based on the work of: 56 | // - San Bergmans (https://www.sbprojects.net/knowledge/ir/index.php), 57 | // - Christoph Niessen (http://chris.cnie.de/avr/tcm231421.html), 58 | // - David Johnson-Davies (http://www.technoblogy.com/show?UVE). 59 | 60 | 61 | // oscillator calibration value (uncomment and set if necessary) 62 | //#define OSCCAL_VAL 48 63 | 64 | // libraries 65 | #include 66 | #include 67 | #include 68 | #include 69 | 70 | // IR codes 71 | #define ADDR 0x00 // Address: Philips TV 72 | #define KEY1 0x0D // Command: Volume+ 73 | #define KEY2 0x11 // Command: Channel+ 74 | #define KEY3 0x0E // Command: Volume- 75 | #define KEY4 0x12 // Command: Channel- 76 | #define KEY5 0x0B // Command: Standby 77 | 78 | // define values for 36kHz PWM frequency and 25% duty cycle 79 | #define TOP 32 // 1200kHz / 36kHz - 1 = 32 80 | #define DUTY 7 // 1200kHz / 36kHz / 4 - 1 = 7 81 | 82 | // macros to switch on/off IR LED 83 | #define IRon() DDRB |= 0b00000010 // PB1 as output = IR at OC0B (36 kHz) 84 | #define IRoff() DDRB &= 0b11111101 // PB1 as input = LED off 85 | 86 | // macros to modulate the signals according to RC-5 protocol with compensated timings 87 | #define bit0Pulse() {IRon(); _delay_us(889); IRoff(); _delay_us(884);} 88 | #define bit1Pulse() {IRoff(); _delay_us(889); IRon(); _delay_us(884);} 89 | #define repeatDelay() _delay_ms(89) // 114ms - 14 * 2 * 889us 90 | 91 | // bitmasks 92 | #define startBit 0b0010000000000000 93 | #define cmdBit7 0b0001000000000000 94 | #define toggleBit 0b0000100000000000 95 | 96 | // toggle variable 97 | uint8_t toggle = 0; 98 | 99 | // send complete telegram (startbits + togglebit + address + command) via IR 100 | void sendCode(uint8_t cmd) { 101 | // prepare the message 102 | uint16_t message = ADDR << 6; // shift address to the right position 103 | message |= (cmd & 0x3f); // add the low 6 bits of the command 104 | if (~cmd & 0x40) message |= cmdBit7; // add inverse of 7th command bit 105 | message |= startBit; // add start bit 106 | if (toggle) message |= toggleBit; // add toggle bit 107 | 108 | // send the message 109 | do { 110 | uint16_t bitmask = startBit; // set the bitmask to first bit to send 111 | for(uint8_t i=14; i; i--, bitmask>>=1) { // 14 bits, MSB first 112 | (message & bitmask) ? (bit1Pulse()) : (bit0Pulse()); // send the bit 113 | } 114 | IRoff(); // switch off IR LED 115 | repeatDelay(); // wait for next repeat 116 | } while(~PINB & 0b00111101); // repeat sending until button is released 117 | toggle ^= 1; // toggle the toggle bit 118 | } 119 | 120 | // main function 121 | int main(void) { 122 | // set oscillator calibration value 123 | #ifdef OSCCAL_VAL 124 | OSCCAL = OSCCAL_VAL; // set the value if defined above 125 | #endif 126 | 127 | // setup pins 128 | DDRB = 0b00000000; // all pins are input pins by now 129 | PORTB = 0b00111101; // pull-up for button pins 130 | 131 | // set timer0 to toggle IR pin at 38 kHz 132 | TCCR0A = 0b00100011; // PWM on OC0B (PB1) 133 | TCCR0B = 0b00001001; // no prescaler 134 | OCR0A = TOP; // 38 kHz PWM frequency 135 | OCR0B = DUTY; // 25 % duty cycle 136 | 137 | // setup pin change interrupt 138 | GIMSK = 0b00100000; // turn on pin change interrupts 139 | PCMSK = 0b00111101; // turn on interrupt on button pins 140 | SREG |= 0b10000000; // enable global interrupts 141 | 142 | // disable unused peripherals and set sleep mode to save power 143 | ADCSRA = 0b00000000; // disable ADC 144 | ACSR = 0b10000000; // disable analog comperator 145 | PRR = 0b00000001; // shut down ADC 146 | set_sleep_mode(SLEEP_MODE_PWR_DOWN); // set sleep mode to power down 147 | 148 | // main loop 149 | while(1) { 150 | sleep_mode(); // sleep until button is pressed 151 | _delay_ms(1); // debounce 152 | uint8_t buttons = ~PINB & 0b00111101; // read button pins 153 | switch (buttons) { // send corresponding IR code 154 | case 0b00000001: sendCode(KEY1); break; 155 | case 0b00000100: sendCode(KEY2); break; 156 | case 0b00001000: sendCode(KEY3); break; 157 | case 0b00010000: sendCode(KEY4); break; 158 | case 0b00100000: sendCode(KEY5); break; 159 | default: break; 160 | } 161 | } 162 | } 163 | 164 | // pin change interrupt service routine 165 | EMPTY_INTERRUPT (PCINT0_vect); // nothing to be done here, just wake up from sleep 166 | -------------------------------------------------------------------------------- /software/RC5_5_buttons/main.c: -------------------------------------------------------------------------------- 1 | // tinyIRremote for ATtiny13A - RC-5, 5 Buttons 2 | // 3 | // IR remote control using an ATtiny 13A. Timer0 generates a 36kHz 4 | // carrier frequency with a duty cycle of 25% on the output pin to the 5 | // IR LED. The signal (RC-5 protocol) is modulated by toggling the pin 6 | // to input/output. The protocol uses bi-phase modulation (Manchester 7 | // coding). 8 | // 9 | // +-------+ +-------+ ON 10 | // | | 11 | // 889us | 889us 889us | 889us 12 | // | | 13 | // +-------+ +-------+ OFF 14 | // 15 | // |<-- Bit "0" -->| |<-- Bit "1" -->| 16 | // 17 | // IR telegram starts with two start bits. The first bit is always "1", 18 | // the second bit is "1" in the original protocol and inverted 7th bit 19 | // of the command in the extended RC-5 protocol. The third bit toggles 20 | // after each button release. The next five bits represent the device 21 | // address, MSB first and the last six bits represent the command, MSB 22 | // first. 23 | // 24 | // As long as a key remains down the telegram will be repeated every 25 | // 114ms without changing the toggle bit. 26 | // 27 | // The code utilizes the sleep mode power down function. The device will 28 | // work several months on a CR2032 battery. 29 | // 30 | // +-\/-+ 31 | // KEY5 --- A0 (D5) PB5 1| |8 Vcc 32 | // KEY3 --- A3 (D3) PB3 2| |7 PB2 (D2) A1 --- KEY2 33 | // KEY4 --- A2 (D4) PB4 3| |6 PB1 (D1) ------ IR LED 34 | // GND 4| |5 PB0 (D0) ------ KEY1 35 | // +----+ 36 | // 37 | // Controller: ATtiny13A 38 | // Clockspeed: 1.2 MHz internal 39 | // 40 | // Reset pin must be disabled by writing respective fuse after uploading the code: 41 | // avrdude -p attiny13 -c usbasp -U lfuse:w:0x2a:m -U hfuse:w:0xfe:m 42 | // Warning: You will need a high voltage fuse resetter to undo this change! 43 | // 44 | // Note: The internal oscillator may need to be calibrated for the device 45 | // to function properly. 46 | // 47 | // 2019 by Stefan Wagner 48 | // Project Files (EasyEDA): https://easyeda.com/wagiminator 49 | // Project Files (Github): https://github.com/wagiminator 50 | // License: http://creativecommons.org/licenses/by-sa/3.0/ 51 | // 52 | // Based on the work of: 53 | // - San Bergmans (https://www.sbprojects.net/knowledge/ir/index.php), 54 | // - Christoph Niessen (http://chris.cnie.de/avr/tcm231421.html), 55 | // - David Johnson-Davies (http://www.technoblogy.com/show?UVE). 56 | 57 | 58 | // oscillator calibration value (uncomment and set if necessary) 59 | //#define OSCCAL_VAL 48 60 | 61 | // libraries 62 | #include 63 | #include 64 | #include 65 | #include 66 | 67 | // IR codes 68 | #define ADDR 0x00 // Address: Philips TV 69 | #define KEY1 0x0D // Command: Volume+ 70 | #define KEY2 0x11 // Command: Channel+ 71 | #define KEY3 0x0E // Command: Volume- 72 | #define KEY4 0x12 // Command: Channel- 73 | #define KEY5 0x0B // Command: Standby 74 | 75 | // define values for 36kHz PWM frequency and 25% duty cycle 76 | #define TOP 32 // 1200kHz / 36kHz - 1 = 32 77 | #define DUTY 7 // 1200kHz / 36kHz / 4 - 1 = 7 78 | 79 | // macros to switch on/off IR LED 80 | #define IRon() DDRB |= 0b00000010 // PB1 as output = IR at OC0B (36 kHz) 81 | #define IRoff() DDRB &= 0b11111101 // PB1 as input = LED off 82 | 83 | // macros to modulate the signals according to RC-5 protocol with compensated timings 84 | #define bit0Pulse() {IRon(); _delay_us(889); IRoff(); _delay_us(884);} 85 | #define bit1Pulse() {IRoff(); _delay_us(889); IRon(); _delay_us(884);} 86 | #define repeatDelay() _delay_ms(89) // 114ms - 14 * 2 * 889us 87 | 88 | // bitmasks 89 | #define startBit 0b0010000000000000 90 | #define cmdBit7 0b0001000000000000 91 | #define toggleBit 0b0000100000000000 92 | 93 | // toggle variable 94 | uint8_t toggle = 0; 95 | 96 | // send complete telegram (startbits + togglebit + address + command) via IR 97 | void sendCode(uint8_t cmd) { 98 | // prepare the message 99 | uint16_t message = ADDR << 6; // shift address to the right position 100 | message |= (cmd & 0x3f); // add the low 6 bits of the command 101 | if (~cmd & 0x40) message |= cmdBit7; // add inverse of 7th command bit 102 | message |= startBit; // add start bit 103 | if (toggle) message |= toggleBit; // add toggle bit 104 | 105 | // send the message 106 | do { 107 | uint16_t bitmask = startBit; // set the bitmask to first bit to send 108 | for(uint8_t i=14; i; i--, bitmask>>=1) { // 14 bits, MSB first 109 | (message & bitmask) ? (bit1Pulse()) : (bit0Pulse()); // send the bit 110 | } 111 | IRoff(); // switch off IR LED 112 | repeatDelay(); // wait for next repeat 113 | } while(~PINB & 0b00111101); // repeat sending until button is released 114 | toggle ^= 1; // toggle the toggle bit 115 | } 116 | 117 | // main function 118 | int main(void) { 119 | // set oscillator calibration value 120 | #ifdef OSCCAL_VAL 121 | OSCCAL = OSCCAL_VAL; // set the value if defined above 122 | #endif 123 | 124 | // setup pins 125 | DDRB = 0b00000000; // all pins are input pins by now 126 | PORTB = 0b00111101; // pull-up for button pins 127 | 128 | // set timer0 to toggle IR pin at 38 kHz 129 | TCCR0A = 0b00100011; // PWM on OC0B (PB1) 130 | TCCR0B = 0b00001001; // no prescaler 131 | OCR0A = TOP; // 38 kHz PWM frequency 132 | OCR0B = DUTY; // 25 % duty cycle 133 | 134 | // setup pin change interrupt 135 | GIMSK = 0b00100000; // turn on pin change interrupts 136 | PCMSK = 0b00111101; // turn on interrupt on button pins 137 | SREG |= 0b10000000; // enable global interrupts 138 | 139 | // disable unused peripherals and set sleep mode to save power 140 | ADCSRA = 0b00000000; // disable ADC 141 | ACSR = 0b10000000; // disable analog comperator 142 | PRR = 0b00000001; // shut down ADC 143 | set_sleep_mode(SLEEP_MODE_PWR_DOWN); // set sleep mode to power down 144 | 145 | // main loop 146 | while(1) { 147 | sleep_mode(); // sleep until button is pressed 148 | _delay_ms(1); // debounce 149 | uint8_t buttons = ~PINB & 0b00111101; // read button pins 150 | switch (buttons) { // send corresponding IR code 151 | case 0b00000001: sendCode(KEY1); break; 152 | case 0b00000100: sendCode(KEY2); break; 153 | case 0b00001000: sendCode(KEY3); break; 154 | case 0b00010000: sendCode(KEY4); break; 155 | case 0b00100000: sendCode(KEY5); break; 156 | default: break; 157 | } 158 | } 159 | } 160 | 161 | // pin change interrupt service routine 162 | EMPTY_INTERRUPT (PCINT0_vect); // nothing to be done here, just wake up from sleep 163 | -------------------------------------------------------------------------------- /software/RC5_5_buttons/makefile: -------------------------------------------------------------------------------- 1 | # Project: tinyRemote - 5 buttons 2 | # Author: Stefan Wagner 3 | # Year: 2019 4 | # URL: https://easyeda.com/wagiminator 5 | # https://github.com/wagiminator 6 | # 7 | # Type "make help" in the command line. 8 | 9 | # Compiler Options 10 | DEVICE = attiny13a 11 | CLOCK = 1200000 12 | 13 | # Programmer Options 14 | PROGRMR = usbasp 15 | TGTDEV = attiny13 16 | LFUSE = 0x2a 17 | HFUSE = 0xfe 18 | 19 | # Objects 20 | OBJECTS = main.o 21 | 22 | # Commands 23 | AVRDUDE = avrdude -c $(PROGRMR) -p $(TGTDEV) 24 | COMPILE = avr-gcc -Wall -Os -flto -mmcu=$(DEVICE) -DF_CPU=$(CLOCK) -DDEBUG_LEVEL=0 25 | CLEAN = rm -f main.lst main.obj main.cof main.list main.map main.eep.hex *.o main.s 26 | 27 | # Symbolic Targets 28 | all: help 29 | 30 | help: 31 | @echo "Use the following commands:" 32 | @echo "make hex compile and build main.hex for $(DEVICE)" 33 | @echo "make asm compile and disassemble to main.asm for $(DEVICE)" 34 | @echo "make bin compile and build main.bin for $(DEVICE)" 35 | @echo "make upload compile and upload to $(DEVICE) using $(PROGRMR)" 36 | @echo "make fuses burn fuses of $(DEVICE) using $(PROGRMR) programmer" 37 | @echo "make install compile, upload and burn fuses for $(DEVICE)" 38 | @echo "make clean remove all build files" 39 | 40 | bin: $(OBJECTS) 41 | @echo "Building main.bin ..." 42 | @$(COMPILE) -o main.bin $(OBJECTS) 43 | @echo "Removing temporary files ..." 44 | @$(CLEAN) 45 | 46 | hex: bin 47 | @echo "Building main.hex ..." 48 | @avr-objcopy -j .text -j .data -O ihex main.bin main.hex 49 | @echo "FLASH: $(shell avr-size -d main.bin | awk '/[0-9]/ {print $$1 + $$2}') bytes" 50 | @echo "SRAM: $(shell avr-size -d main.bin | awk '/[0-9]/ {print $$2 + $$3}') bytes" 51 | @echo "Removing main.bin ..." 52 | @rm -f main.bin 53 | 54 | asm: bin 55 | @echo "Disassembling to main.asm ..." 56 | @avr-objdump -d main.bin > main.asm 57 | @echo "Removing main.bin ..." 58 | @rm -f main.bin 59 | 60 | upload: hex 61 | @echo "Uploading to $(TGTDEV) ..." 62 | @$(AVRDUDE) -U flash:w:main.hex:i 63 | 64 | fuses: 65 | @echo "Burning fuses of $(TGTDEV) ..." 66 | @echo "Warning: RESET pin will be disabled !" 67 | @$(AVRDUDE) -U hfuse:w:$(HFUSE):m -U lfuse:w:$(LFUSE):m 68 | 69 | install: upload fuses 70 | 71 | clean: 72 | @echo "Cleaning all up ..." 73 | @$(CLEAN) 74 | @rm -f main.bin main.hex main.asm 75 | 76 | .c.o: 77 | @echo "Compiling $< for $(DEVICE) @ $(CLOCK)Hz ..." 78 | @$(COMPILE) -c $< -o $@ 79 | 80 | .S.o: 81 | @echo "Compiling $< for $(DEVICE) @ $(CLOCK)Hz ..." 82 | @$(COMPILE) -x assembler-with-cpp -c $< -o $@ 83 | 84 | .c.s: 85 | @echo "Compiling $< for $(DEVICE) @ $(CLOCK)Hz ..." 86 | @$(COMPILE) -S $< -o $@ 87 | -------------------------------------------------------------------------------- /software/SONY_4_buttons/TinyRemote_t13_SONY_4B.ino: -------------------------------------------------------------------------------- 1 | // tinyIRremote for ATtiny13A - SONY SIRC, 4 Buttons 2 | // 3 | // IR remote control using an ATtiny 13A. Timer0 generates a 40kHz 4 | // carrier frequency with a duty cycle of 25% on the output pin to the 5 | // IR LED. The message (SONY protocol) is modulated by toggling the pin 6 | // to input/output. The protocol uses pulse length modulation. 7 | // 8 | // +--------------------+ +-----+ +----------+ +-- ON 9 | // | | | | | | | 10 | // | 2400us |600us|600us|600us| 1200us |600us| ... 11 | // | | | | | | | 12 | // ------+ +-----+ +-----+ +-----+ OFF 13 | // 14 | // |<------ Start Frame ----->|<- Bit=0 ->|<--- Bit=1 ---->| 15 | // 16 | // A "0" bit is a 600us burst followed by a 600us space, a "1" bit is a 17 | // 1200us burst followed by a 600us space. An IR telegram starts with a 18 | // 2400us leading burst followed by a 600us space. The command and 19 | // address bits are then transmitted, LSB first. Depending on the 20 | // protocol version, these are in detail: 21 | // - 12-bit version: 7 command bits, 5 address bits 22 | // - 15-bit version: 7 command bits, 8 address bits 23 | // - 20-bit version: 7 command bits, 5 address bits, 8 extended bits 24 | // 25 | // As long as a key remains down the message will be repeated every 45ms. 26 | // 27 | // The code utilizes the sleep mode power down function. The device will 28 | // work several months on a CR2032 battery. 29 | // 30 | // +-\/-+ 31 | // KEY5 --- A0 (D5) PB5 1| |8 Vcc 32 | // KEY3 --- A3 (D3) PB3 2| |7 PB2 (D2) A1 --- KEY2 33 | // KEY4 --- A2 (D4) PB4 3| |6 PB1 (D1) ------ IR LED 34 | // GND 4| |5 PB0 (D0) ------ KEY1 35 | // +----+ 36 | // 37 | // Controller: ATtiny13 38 | // Core: MicroCore (https://github.com/MCUdude/MicroCore) 39 | // Clockspeed: 1.2 MHz internal 40 | // BOD: BOD disabled (energy saving) 41 | // Timing: Micros disabled (Timer0 is in use) 42 | // 43 | // Note: The internal oscillator may need to be calibrated for the device 44 | // to function properly. 45 | // 46 | // 2019 by Stefan Wagner 47 | // Project Files (EasyEDA): https://easyeda.com/wagiminator 48 | // Project Files (Github): https://github.com/wagiminator 49 | // License: http://creativecommons.org/licenses/by-sa/3.0/ 50 | // 51 | // Based on the work of: 52 | // - San Bergmans (https://www.sbprojects.net/knowledge/ir/index.php), 53 | // - Christoph Niessen (http://chris.cnie.de/avr/tcm231421.html), 54 | // - David Johnson-Davies (http://www.technoblogy.com/show?UVE). 55 | 56 | 57 | // oscillator calibration value (uncomment and set if necessary) 58 | //#define OSCCAL_VAL 48 59 | 60 | // libraries 61 | #include 62 | #include 63 | #include 64 | #include 65 | 66 | // IR codes 67 | #define BITS 12 // SIRC version: 12, 15 or 20 bits 68 | #define EXTB 0x00 // Extended byte in 20 bits version 69 | #define ADDR 0x01 // Address: SONY TV 70 | #define KEY1 0x12 // Command: Volume+ 71 | #define KEY2 0x10 // Command: Channel+ 72 | #define KEY3 0x13 // Command: Volume- 73 | #define KEY4 0x11 // Command: Channel- 74 | //#define KEY5 0x15 // Command: Power 75 | 76 | // define values for 40kHz PWM frequency and 25% duty cycle 77 | #define TOP 29 // 1200kHz / 40kHz - 1 = 29 78 | #define DUTY 7 // 1200kHz / 40kHz / 4 - 1 = 7 79 | 80 | // macros to switch on/off IR LED 81 | #define IRon() DDRB |= 0b00000010 // PB1 as output = IR at OC0B (40kHz) 82 | #define IRoff() DDRB &= 0b11111101 // PB1 as input = LED off 83 | 84 | // macros to modulate the signals according to SONY protocol with compensated timings 85 | #define startPulse() {IRon(); _delay_us(2400); IRoff(); _delay_us( 595);} 86 | #define bit0Pulse() {IRon(); _delay_us( 600); IRoff(); _delay_us( 595);} 87 | #define bit1Pulse() {IRon(); _delay_us(1200); IRoff(); _delay_us( 595);} 88 | #define repeatPause() _delay_ms(27) 89 | 90 | 91 | // send "number" of bits of "value" via IR 92 | void sendByte(uint8_t value, uint8_t number) { 93 | do { // send number of bits, LSB first 94 | (value & 1) ? (bit1Pulse()) : (bit0Pulse()); // send bit 95 | value>>=1; // next bit 96 | } while(--number); 97 | } 98 | 99 | // send complete telegram (start frame + command + address) via IR 100 | void sendCode(uint8_t cmd) { 101 | do { 102 | startPulse(); // signify start of transmission 103 | sendByte(cmd, 7); // send 7 command bits 104 | #if BITS == 12 // if 12-bit version: 105 | sendByte(ADDR, 5); // send 5 address bits 106 | #elif BITS == 15 // if 15-bit version: 107 | sendByte(ADDR, 8); // send 8 address bits 108 | #elif BITS == 20 // if 20-bit version: 109 | sendByte(ADDR, 5); // send 5 address bits 110 | sendByte(EXTB, 8); // send 8 extended bits 111 | #endif 112 | repeatPause(); // wait until next repeat 113 | } while (~PINB & 0b00011101); // repeat sending until button is released 114 | } 115 | 116 | // main function 117 | int main(void) { 118 | // set oscillator calibration value 119 | #ifdef OSCCAL_VAL 120 | OSCCAL = OSCCAL_VAL; // set the value if defined above 121 | #endif 122 | 123 | // setup pins 124 | DDRB = 0b00000000; // all pins are input pins by now 125 | PORTB = 0b00011101; // pull-up for button pins 126 | 127 | // set timer0 to toggle IR pin at 38 kHz 128 | TCCR0A = 0b00100011; // PWM on OC0B (PB1) 129 | TCCR0B = 0b00001001; // no prescaler 130 | OCR0A = TOP; // 38 kHz PWM frequency 131 | OCR0B = DUTY; // 25 % duty cycle 132 | 133 | // setup pin change interrupt 134 | GIMSK = 0b00100000; // turn on pin change interrupts 135 | PCMSK = 0b00011101; // turn on interrupt on button pins 136 | SREG |= 0b10000000; // enable global interrupts 137 | 138 | // disable unused peripherals and set sleep mode to save power 139 | ADCSRA = 0b00000000; // disable ADC 140 | ACSR = 0b10000000; // disable analog comperator 141 | PRR = 0b00000001; // shut down ADC 142 | set_sleep_mode(SLEEP_MODE_PWR_DOWN); // set sleep mode to power down 143 | 144 | // main loop 145 | while(1) { 146 | sleep_mode(); // sleep until button is pressed 147 | _delay_ms(1); // debounce 148 | uint8_t buttons = ~PINB & 0b00011101; // read button pins 149 | switch (buttons) { // send corresponding IR code 150 | case 0b00000001: sendCode(KEY1); break; 151 | case 0b00000100: sendCode(KEY2); break; 152 | case 0b00001000: sendCode(KEY3); break; 153 | case 0b00010000: sendCode(KEY4); break; 154 | default: break; 155 | } 156 | } 157 | } 158 | 159 | // pin change interrupt service routine 160 | EMPTY_INTERRUPT (PCINT0_vect); // nothing to be done here, just wake up from sleep 161 | -------------------------------------------------------------------------------- /software/SONY_4_buttons/main.c: -------------------------------------------------------------------------------- 1 | // tinyIRremote for ATtiny13A - SONY SIRC, 4 Buttons 2 | // 3 | // IR remote control using an ATtiny 13A. Timer0 generates a 40kHz 4 | // carrier frequency with a duty cycle of 25% on the output pin to the 5 | // IR LED. The message (SONY protocol) is modulated by toggling the pin 6 | // to input/output. The protocol uses pulse length modulation. 7 | // 8 | // +--------------------+ +-----+ +----------+ +-- ON 9 | // | | | | | | | 10 | // | 2400us |600us|600us|600us| 1200us |600us| ... 11 | // | | | | | | | 12 | // ------+ +-----+ +-----+ +-----+ OFF 13 | // 14 | // |<------ Start Frame ----->|<- Bit=0 ->|<--- Bit=1 ---->| 15 | // 16 | // A "0" bit is a 600us burst followed by a 600us space, a "1" bit is a 17 | // 1200us burst followed by a 600us space. An IR telegram starts with a 18 | // 2400us leading burst followed by a 600us space. The command and 19 | // address bits are then transmitted, LSB first. Depending on the 20 | // protocol version, these are in detail: 21 | // - 12-bit version: 7 command bits, 5 address bits 22 | // - 15-bit version: 7 command bits, 8 address bits 23 | // - 20-bit version: 7 command bits, 5 address bits, 8 extended bits 24 | // 25 | // As long as a key remains down the message will be repeated every 45ms. 26 | // 27 | // The code utilizes the sleep mode power down function. The device will 28 | // work several months on a CR2032 battery. 29 | // 30 | // +-\/-+ 31 | // KEY5 --- A0 (D5) PB5 1| |8 Vcc 32 | // KEY3 --- A3 (D3) PB3 2| |7 PB2 (D2) A1 --- KEY2 33 | // KEY4 --- A2 (D4) PB4 3| |6 PB1 (D1) ------ IR LED 34 | // GND 4| |5 PB0 (D0) ------ KEY1 35 | // +----+ 36 | // 37 | // Controller: ATtiny13A 38 | // Clockspeed: 1.2 MHz internal 39 | // 40 | // Note: The internal oscillator may need to be calibrated for the device 41 | // to function properly. 42 | // 43 | // 2019 by Stefan Wagner 44 | // Project Files (EasyEDA): https://easyeda.com/wagiminator 45 | // Project Files (Github): https://github.com/wagiminator 46 | // License: http://creativecommons.org/licenses/by-sa/3.0/ 47 | // 48 | // Based on the work of: 49 | // - San Bergmans (https://www.sbprojects.net/knowledge/ir/index.php), 50 | // - Christoph Niessen (http://chris.cnie.de/avr/tcm231421.html), 51 | // - David Johnson-Davies (http://www.technoblogy.com/show?UVE). 52 | 53 | 54 | // oscillator calibration value (uncomment and set if necessary) 55 | //#define OSCCAL_VAL 48 56 | 57 | // libraries 58 | #include 59 | #include 60 | #include 61 | #include 62 | 63 | // IR codes 64 | #define BITS 12 // SIRC version: 12, 15 or 20 bits 65 | #define EXTB 0x00 // Extended byte in 20 bits version 66 | #define ADDR 0x01 // Address: SONY TV 67 | #define KEY1 0x12 // Command: Volume+ 68 | #define KEY2 0x10 // Command: Channel+ 69 | #define KEY3 0x13 // Command: Volume- 70 | #define KEY4 0x11 // Command: Channel- 71 | //#define KEY5 0x15 // Command: Power 72 | 73 | // define values for 40kHz PWM frequency and 25% duty cycle 74 | #define TOP 29 // 1200kHz / 40kHz - 1 = 29 75 | #define DUTY 7 // 1200kHz / 40kHz / 4 - 1 = 7 76 | 77 | // macros to switch on/off IR LED 78 | #define IRon() DDRB |= 0b00000010 // PB1 as output = IR at OC0B (40kHz) 79 | #define IRoff() DDRB &= 0b11111101 // PB1 as input = LED off 80 | 81 | // macros to modulate the signals according to SONY protocol with compensated timings 82 | #define startPulse() {IRon(); _delay_us(2400); IRoff(); _delay_us( 595);} 83 | #define bit0Pulse() {IRon(); _delay_us( 600); IRoff(); _delay_us( 595);} 84 | #define bit1Pulse() {IRon(); _delay_us(1200); IRoff(); _delay_us( 595);} 85 | #define repeatPause() _delay_ms(27) 86 | 87 | 88 | // send "number" of bits of "value" via IR 89 | void sendByte(uint8_t value, uint8_t number) { 90 | do { // send number of bits, LSB first 91 | (value & 1) ? (bit1Pulse()) : (bit0Pulse()); // send bit 92 | value>>=1; // next bit 93 | } while(--number); 94 | } 95 | 96 | // send complete telegram (start frame + command + address) via IR 97 | void sendCode(uint8_t cmd) { 98 | do { 99 | startPulse(); // signify start of transmission 100 | sendByte(cmd, 7); // send 7 command bits 101 | #if BITS == 12 // if 12-bit version: 102 | sendByte(ADDR, 5); // send 5 address bits 103 | #elif BITS == 15 // if 15-bit version: 104 | sendByte(ADDR, 8); // send 8 address bits 105 | #elif BITS == 20 // if 20-bit version: 106 | sendByte(ADDR, 5); // send 5 address bits 107 | sendByte(EXTB, 8); // send 8 extended bits 108 | #endif 109 | repeatPause(); // wait until next repeat 110 | } while (~PINB & 0b00011101); // repeat sending until button is released 111 | } 112 | 113 | // main function 114 | int main(void) { 115 | // set oscillator calibration value 116 | #ifdef OSCCAL_VAL 117 | OSCCAL = OSCCAL_VAL; // set the value if defined above 118 | #endif 119 | 120 | // setup pins 121 | DDRB = 0b00000000; // all pins are input pins by now 122 | PORTB = 0b00011101; // pull-up for button pins 123 | 124 | // set timer0 to toggle IR pin at 38 kHz 125 | TCCR0A = 0b00100011; // PWM on OC0B (PB1) 126 | TCCR0B = 0b00001001; // no prescaler 127 | OCR0A = TOP; // 38 kHz PWM frequency 128 | OCR0B = DUTY; // 25 % duty cycle 129 | 130 | // setup pin change interrupt 131 | GIMSK = 0b00100000; // turn on pin change interrupts 132 | PCMSK = 0b00011101; // turn on interrupt on button pins 133 | SREG |= 0b10000000; // enable global interrupts 134 | 135 | // disable unused peripherals and set sleep mode to save power 136 | ADCSRA = 0b00000000; // disable ADC 137 | ACSR = 0b10000000; // disable analog comperator 138 | PRR = 0b00000001; // shut down ADC 139 | set_sleep_mode(SLEEP_MODE_PWR_DOWN); // set sleep mode to power down 140 | 141 | // main loop 142 | while(1) { 143 | sleep_mode(); // sleep until button is pressed 144 | _delay_ms(1); // debounce 145 | uint8_t buttons = ~PINB & 0b00011101; // read button pins 146 | switch (buttons) { // send corresponding IR code 147 | case 0b00000001: sendCode(KEY1); break; 148 | case 0b00000100: sendCode(KEY2); break; 149 | case 0b00001000: sendCode(KEY3); break; 150 | case 0b00010000: sendCode(KEY4); break; 151 | default: break; 152 | } 153 | } 154 | } 155 | 156 | // pin change interrupt service routine 157 | EMPTY_INTERRUPT (PCINT0_vect); // nothing to be done here, just wake up from sleep 158 | -------------------------------------------------------------------------------- /software/SONY_4_buttons/makefile: -------------------------------------------------------------------------------- 1 | # Project: tinyRemote - 4 buttons 2 | # Author: Stefan Wagner 3 | # Year: 2019 4 | # URL: https://easyeda.com/wagiminator 5 | # https://github.com/wagiminator 6 | # 7 | # Type "make help" in the command line. 8 | 9 | # Compiler Options 10 | DEVICE = attiny13a 11 | CLOCK = 1200000 12 | 13 | # Programmer Options 14 | PROGRMR = usbasp 15 | TGTDEV = attiny13 16 | LFUSE = 0x2a 17 | HFUSE = 0xff 18 | 19 | # Objects 20 | OBJECTS = main.o 21 | 22 | # Commands 23 | AVRDUDE = avrdude -c $(PROGRMR) -p $(TGTDEV) 24 | COMPILE = avr-gcc -Wall -Os -flto -mmcu=$(DEVICE) -DF_CPU=$(CLOCK) -DDEBUG_LEVEL=0 25 | CLEAN = rm -f main.lst main.obj main.cof main.list main.map main.eep.hex *.o main.s 26 | 27 | # Symbolic Targets 28 | all: help 29 | 30 | help: 31 | @echo "Use the following commands:" 32 | @echo "make hex compile and build main.hex for $(DEVICE)" 33 | @echo "make asm compile and disassemble to main.asm for $(DEVICE)" 34 | @echo "make bin compile and build main.bin for $(DEVICE)" 35 | @echo "make upload compile and upload to $(DEVICE) using $(PROGRMR)" 36 | @echo "make fuses burn fuses of $(DEVICE) using $(PROGRMR) programmer" 37 | @echo "make install compile, upload and burn fuses for $(DEVICE)" 38 | @echo "make clean remove all build files" 39 | 40 | bin: $(OBJECTS) 41 | @echo "Building main.bin ..." 42 | @$(COMPILE) -o main.bin $(OBJECTS) 43 | @echo "Removing temporary files ..." 44 | @$(CLEAN) 45 | 46 | hex: bin 47 | @echo "Building main.hex ..." 48 | @avr-objcopy -j .text -j .data -O ihex main.bin main.hex 49 | @echo "FLASH: $(shell avr-size -d main.bin | awk '/[0-9]/ {print $$1 + $$2}') bytes" 50 | @echo "SRAM: $(shell avr-size -d main.bin | awk '/[0-9]/ {print $$2 + $$3}') bytes" 51 | @echo "Removing main.bin ..." 52 | @rm -f main.bin 53 | 54 | asm: bin 55 | @echo "Disassembling to main.asm ..." 56 | @avr-objdump -d main.bin > main.asm 57 | @echo "Removing main.bin ..." 58 | @rm -f main.bin 59 | 60 | upload: hex 61 | @echo "Uploading to $(TGTDEV) ..." 62 | @$(AVRDUDE) -U flash:w:main.hex:i 63 | 64 | fuses: 65 | @echo "Burning fuses of $(TGTDEV) ..." 66 | @$(AVRDUDE) -U hfuse:w:$(HFUSE):m -U lfuse:w:$(LFUSE):m 67 | 68 | install: upload fuses 69 | 70 | clean: 71 | @echo "Cleaning all up ..." 72 | @$(CLEAN) 73 | @rm -f main.bin main.hex main.asm 74 | 75 | .c.o: 76 | @echo "Compiling $< for $(DEVICE) @ $(CLOCK)Hz ..." 77 | @$(COMPILE) -c $< -o $@ 78 | 79 | .S.o: 80 | @echo "Compiling $< for $(DEVICE) @ $(CLOCK)Hz ..." 81 | @$(COMPILE) -x assembler-with-cpp -c $< -o $@ 82 | 83 | .c.s: 84 | @echo "Compiling $< for $(DEVICE) @ $(CLOCK)Hz ..." 85 | @$(COMPILE) -S $< -o $@ 86 | -------------------------------------------------------------------------------- /software/SONY_5_buttons/TinyRemote_t13_SONY_5B.ino: -------------------------------------------------------------------------------- 1 | // tinyIRremote for ATtiny13A - SONY SIRC, 5 Buttons 2 | // 3 | // IR remote control using an ATtiny 13A. Timer0 generates a 40kHz 4 | // carrier frequency with a duty cycle of 25% on the output pin to the 5 | // IR LED. The message (SONY protocol) is modulated by toggling the pin 6 | // to input/output. The protocol uses pulse length modulation. 7 | // 8 | // +--------------------+ +-----+ +----------+ +-- ON 9 | // | | | | | | | 10 | // | 2400us |600us|600us|600us| 1200us |600us| ... 11 | // | | | | | | | 12 | // ------+ +-----+ +-----+ +-----+ OFF 13 | // 14 | // |<------ Start Frame ----->|<- Bit=0 ->|<--- Bit=1 ---->| 15 | // 16 | // A "0" bit is a 600us burst followed by a 600us space, a "1" bit is a 17 | // 1200us burst followed by a 600us space. An IR telegram starts with a 18 | // 2400us leading burst followed by a 600us space. The command and 19 | // address bits are then transmitted, LSB first. Depending on the 20 | // protocol version, these are in detail: 21 | // - 12-bit version: 7 command bits, 5 address bits 22 | // - 15-bit version: 7 command bits, 8 address bits 23 | // - 20-bit version: 7 command bits, 5 address bits, 8 extended bits 24 | // 25 | // As long as a key remains down the message will be repeated every 45ms. 26 | // 27 | // The code utilizes the sleep mode power down function. The device will 28 | // work several months on a CR2032 battery. 29 | // 30 | // +-\/-+ 31 | // KEY5 --- A0 (D5) PB5 1| |8 Vcc 32 | // KEY3 --- A3 (D3) PB3 2| |7 PB2 (D2) A1 --- KEY2 33 | // KEY4 --- A2 (D4) PB4 3| |6 PB1 (D1) ------ IR LED 34 | // GND 4| |5 PB0 (D0) ------ KEY1 35 | // +----+ 36 | // 37 | // Controller: ATtiny13 38 | // Core: MicroCore (https://github.com/MCUdude/MicroCore) 39 | // Clockspeed: 1.2 MHz internal 40 | // BOD: BOD disabled (energy saving) 41 | // Timing: Micros disabled (Timer0 is in use) 42 | // 43 | // Reset pin must be disabled by writing respective fuse after uploading the code: 44 | // avrdude -p attiny13 -c usbasp -U lfuse:w:0x2a:m -U hfuse:w:0xfe:m 45 | // Warning: You will need a high voltage fuse resetter to undo this change! 46 | // 47 | // Note: The internal oscillator may need to be calibrated for the device 48 | // to function properly. 49 | // 50 | // 2019 by Stefan Wagner 51 | // Project Files (EasyEDA): https://easyeda.com/wagiminator 52 | // Project Files (Github): https://github.com/wagiminator 53 | // License: http://creativecommons.org/licenses/by-sa/3.0/ 54 | // 55 | // Based on the work of: 56 | // - San Bergmans (https://www.sbprojects.net/knowledge/ir/index.php), 57 | // - Christoph Niessen (http://chris.cnie.de/avr/tcm231421.html), 58 | // - David Johnson-Davies (http://www.technoblogy.com/show?UVE). 59 | 60 | 61 | // oscillator calibration value (uncomment and set if necessary) 62 | //#define OSCCAL_VAL 48 63 | 64 | // libraries 65 | #include 66 | #include 67 | #include 68 | #include 69 | 70 | // IR codes 71 | #define BITS 12 // SIRC version: 12, 15 or 20 bits 72 | #define EXTB 0x00 // Extended byte in 20 bits version 73 | #define ADDR 0x01 // Address: SONY TV 74 | #define KEY1 0x12 // Command: Volume+ 75 | #define KEY2 0x10 // Command: Channel+ 76 | #define KEY3 0x13 // Command: Volume- 77 | #define KEY4 0x11 // Command: Channel- 78 | #define KEY5 0x15 // Command: Power 79 | 80 | // define values for 40kHz PWM frequency and 25% duty cycle 81 | #define TOP 29 // 1200kHz / 40kHz - 1 = 29 82 | #define DUTY 7 // 1200kHz / 40kHz / 4 - 1 = 7 83 | 84 | // macros to switch on/off IR LED 85 | #define IRon() DDRB |= 0b00000010 // PB1 as output = IR at OC0B (40kHz) 86 | #define IRoff() DDRB &= 0b11111101 // PB1 as input = LED off 87 | 88 | // macros to modulate the signals according to SONY protocol with compensated timings 89 | #define startPulse() {IRon(); _delay_us(2400); IRoff(); _delay_us( 595);} 90 | #define bit0Pulse() {IRon(); _delay_us( 600); IRoff(); _delay_us( 595);} 91 | #define bit1Pulse() {IRon(); _delay_us(1200); IRoff(); _delay_us( 595);} 92 | #define repeatPause() _delay_ms(27) 93 | 94 | 95 | // send "number" of bits of "value" via IR 96 | void sendByte(uint8_t value, uint8_t number) { 97 | do { // send number of bits, LSB first 98 | (value & 1) ? (bit1Pulse()) : (bit0Pulse()); // send bit 99 | value>>=1; // next bit 100 | } while(--number); 101 | } 102 | 103 | // send complete telegram (start frame + command + address) via IR 104 | void sendCode(uint8_t cmd) { 105 | do { 106 | startPulse(); // signify start of transmission 107 | sendByte(cmd, 7); // send 7 command bits 108 | #if BITS == 12 // if 12-bit version: 109 | sendByte(ADDR, 5); // send 5 address bits 110 | #elif BITS == 15 // if 15-bit version: 111 | sendByte(ADDR, 8); // send 8 address bits 112 | #elif BITS == 20 // if 20-bit version: 113 | sendByte(ADDR, 5); // send 5 address bits 114 | sendByte(EXTB, 8); // send 8 extended bits 115 | #endif 116 | repeatPause(); // wait until next repeat 117 | } while (~PINB & 0b00111101); // repeat sending until button is released 118 | } 119 | 120 | // main function 121 | int main(void) { 122 | // set oscillator calibration value 123 | #ifdef OSCCAL_VAL 124 | OSCCAL = OSCCAL_VAL; // set the value if defined above 125 | #endif 126 | 127 | // setup pins 128 | DDRB = 0b00000000; // all pins are input pins by now 129 | PORTB = 0b00111101; // pull-up for button pins 130 | 131 | // set timer0 to toggle IR pin at 38 kHz 132 | TCCR0A = 0b00100011; // PWM on OC0B (PB1) 133 | TCCR0B = 0b00001001; // no prescaler 134 | OCR0A = TOP; // 38 kHz PWM frequency 135 | OCR0B = DUTY; // 25 % duty cycle 136 | 137 | // setup pin change interrupt 138 | GIMSK = 0b00100000; // turn on pin change interrupts 139 | PCMSK = 0b00111101; // turn on interrupt on button pins 140 | SREG |= 0b10000000; // enable global interrupts 141 | 142 | // disable unused peripherals and set sleep mode to save power 143 | ADCSRA = 0b00000000; // disable ADC 144 | ACSR = 0b10000000; // disable analog comperator 145 | PRR = 0b00000001; // shut down ADC 146 | set_sleep_mode(SLEEP_MODE_PWR_DOWN); // set sleep mode to power down 147 | 148 | // main loop 149 | while(1) { 150 | sleep_mode(); // sleep until button is pressed 151 | _delay_ms(1); // debounce 152 | uint8_t buttons = ~PINB & 0b00111101; // read button pins 153 | switch (buttons) { // send corresponding IR code 154 | case 0b00000001: sendCode(KEY1); break; 155 | case 0b00000100: sendCode(KEY2); break; 156 | case 0b00001000: sendCode(KEY3); break; 157 | case 0b00010000: sendCode(KEY4); break; 158 | case 0b00100000: sendCode(KEY5); break; 159 | default: break; 160 | } 161 | } 162 | } 163 | 164 | // pin change interrupt service routine 165 | EMPTY_INTERRUPT (PCINT0_vect); // nothing to be done here, just wake up from sleep 166 | -------------------------------------------------------------------------------- /software/SONY_5_buttons/main.c: -------------------------------------------------------------------------------- 1 | // tinyIRremote for ATtiny13A - SONY SIRC, 5 Buttons 2 | // 3 | // IR remote control using an ATtiny 13A. Timer0 generates a 40kHz 4 | // carrier frequency with a duty cycle of 25% on the output pin to the 5 | // IR LED. The message (SONY protocol) is modulated by toggling the pin 6 | // to input/output. The protocol uses pulse length modulation. 7 | // 8 | // +--------------------+ +-----+ +----------+ +-- ON 9 | // | | | | | | | 10 | // | 2400us |600us|600us|600us| 1200us |600us| ... 11 | // | | | | | | | 12 | // ------+ +-----+ +-----+ +-----+ OFF 13 | // 14 | // |<------ Start Frame ----->|<- Bit=0 ->|<--- Bit=1 ---->| 15 | // 16 | // A "0" bit is a 600us burst followed by a 600us space, a "1" bit is a 17 | // 1200us burst followed by a 600us space. An IR telegram starts with a 18 | // 2400us leading burst followed by a 600us space. The command and 19 | // address bits are then transmitted, LSB first. Depending on the 20 | // protocol version, these are in detail: 21 | // - 12-bit version: 7 command bits, 5 address bits 22 | // - 15-bit version: 7 command bits, 8 address bits 23 | // - 20-bit version: 7 command bits, 5 address bits, 8 extended bits 24 | // 25 | // As long as a key remains down the message will be repeated every 45ms. 26 | // 27 | // The code utilizes the sleep mode power down function. The device will 28 | // work several months on a CR2032 battery. 29 | // 30 | // +-\/-+ 31 | // KEY5 --- A0 (D5) PB5 1| |8 Vcc 32 | // KEY3 --- A3 (D3) PB3 2| |7 PB2 (D2) A1 --- KEY2 33 | // KEY4 --- A2 (D4) PB4 3| |6 PB1 (D1) ------ IR LED 34 | // GND 4| |5 PB0 (D0) ------ KEY1 35 | // +----+ 36 | // 37 | // Controller: ATtiny13A 38 | // Clockspeed: 1.2 MHz internal 39 | // 40 | // Reset pin must be disabled by writing respective fuse after uploading the code: 41 | // avrdude -p attiny13 -c usbasp -U lfuse:w:0x2a:m -U hfuse:w:0xfe:m 42 | // Warning: You will need a high voltage fuse resetter to undo this change! 43 | // 44 | // Note: The internal oscillator may need to be calibrated for the device 45 | // to function properly. 46 | // 47 | // 2019 by Stefan Wagner 48 | // Project Files (EasyEDA): https://easyeda.com/wagiminator 49 | // Project Files (Github): https://github.com/wagiminator 50 | // License: http://creativecommons.org/licenses/by-sa/3.0/ 51 | // 52 | // Based on the work of: 53 | // - San Bergmans (https://www.sbprojects.net/knowledge/ir/index.php), 54 | // - Christoph Niessen (http://chris.cnie.de/avr/tcm231421.html), 55 | // - David Johnson-Davies (http://www.technoblogy.com/show?UVE). 56 | 57 | 58 | // oscillator calibration value (uncomment and set if necessary) 59 | //#define OSCCAL_VAL 48 60 | 61 | // libraries 62 | #include 63 | #include 64 | #include 65 | #include 66 | 67 | // IR codes 68 | #define BITS 12 // SIRC version: 12, 15 or 20 bits 69 | #define EXTB 0x00 // Extended byte in 20 bits version 70 | #define ADDR 0x01 // Address: SONY TV 71 | #define KEY1 0x12 // Command: Volume+ 72 | #define KEY2 0x10 // Command: Channel+ 73 | #define KEY3 0x13 // Command: Volume- 74 | #define KEY4 0x11 // Command: Channel- 75 | #define KEY5 0x15 // Command: Power 76 | 77 | // define values for 40kHz PWM frequency and 25% duty cycle 78 | #define TOP 29 // 1200kHz / 40kHz - 1 = 29 79 | #define DUTY 7 // 1200kHz / 40kHz / 4 - 1 = 7 80 | 81 | // macros to switch on/off IR LED 82 | #define IRon() DDRB |= 0b00000010 // PB1 as output = IR at OC0B (40kHz) 83 | #define IRoff() DDRB &= 0b11111101 // PB1 as input = LED off 84 | 85 | // macros to modulate the signals according to SONY protocol with compensated timings 86 | #define startPulse() {IRon(); _delay_us(2400); IRoff(); _delay_us( 595);} 87 | #define bit0Pulse() {IRon(); _delay_us( 600); IRoff(); _delay_us( 595);} 88 | #define bit1Pulse() {IRon(); _delay_us(1200); IRoff(); _delay_us( 595);} 89 | #define repeatPause() _delay_ms(27) 90 | 91 | 92 | // send "number" of bits of "value" via IR 93 | void sendByte(uint8_t value, uint8_t number) { 94 | do { // send number of bits, LSB first 95 | (value & 1) ? (bit1Pulse()) : (bit0Pulse()); // send bit 96 | value>>=1; // next bit 97 | } while(--number); 98 | } 99 | 100 | // send complete telegram (start frame + command + address) via IR 101 | void sendCode(uint8_t cmd) { 102 | do { 103 | startPulse(); // signify start of transmission 104 | sendByte(cmd, 7); // send 7 command bits 105 | #if BITS == 12 // if 12-bit version: 106 | sendByte(ADDR, 5); // send 5 address bits 107 | #elif BITS == 15 // if 15-bit version: 108 | sendByte(ADDR, 8); // send 8 address bits 109 | #elif BITS == 20 // if 20-bit version: 110 | sendByte(ADDR, 5); // send 5 address bits 111 | sendByte(EXTB, 8); // send 8 extended bits 112 | #endif 113 | repeatPause(); // wait until next repeat 114 | } while (~PINB & 0b00111101); // repeat sending until button is released 115 | } 116 | 117 | // main function 118 | int main(void) { 119 | // set oscillator calibration value 120 | #ifdef OSCCAL_VAL 121 | OSCCAL = OSCCAL_VAL; // set the value if defined above 122 | #endif 123 | 124 | // setup pins 125 | DDRB = 0b00000000; // all pins are input pins by now 126 | PORTB = 0b00111101; // pull-up for button pins 127 | 128 | // set timer0 to toggle IR pin at 38 kHz 129 | TCCR0A = 0b00100011; // PWM on OC0B (PB1) 130 | TCCR0B = 0b00001001; // no prescaler 131 | OCR0A = TOP; // 38 kHz PWM frequency 132 | OCR0B = DUTY; // 25 % duty cycle 133 | 134 | // setup pin change interrupt 135 | GIMSK = 0b00100000; // turn on pin change interrupts 136 | PCMSK = 0b00111101; // turn on interrupt on button pins 137 | SREG |= 0b10000000; // enable global interrupts 138 | 139 | // disable unused peripherals and set sleep mode to save power 140 | ADCSRA = 0b00000000; // disable ADC 141 | ACSR = 0b10000000; // disable analog comperator 142 | PRR = 0b00000001; // shut down ADC 143 | set_sleep_mode(SLEEP_MODE_PWR_DOWN); // set sleep mode to power down 144 | 145 | // main loop 146 | while(1) { 147 | sleep_mode(); // sleep until button is pressed 148 | _delay_ms(1); // debounce 149 | uint8_t buttons = ~PINB & 0b00111101; // read button pins 150 | switch (buttons) { // send corresponding IR code 151 | case 0b00000001: sendCode(KEY1); break; 152 | case 0b00000100: sendCode(KEY2); break; 153 | case 0b00001000: sendCode(KEY3); break; 154 | case 0b00010000: sendCode(KEY4); break; 155 | case 0b00100000: sendCode(KEY5); break; 156 | default: break; 157 | } 158 | } 159 | } 160 | 161 | // pin change interrupt service routine 162 | EMPTY_INTERRUPT (PCINT0_vect); // nothing to be done here, just wake up from sleep 163 | -------------------------------------------------------------------------------- /software/SONY_5_buttons/makefile: -------------------------------------------------------------------------------- 1 | # Project: tinyRemote - 5 buttons 2 | # Author: Stefan Wagner 3 | # Year: 2019 4 | # URL: https://easyeda.com/wagiminator 5 | # https://github.com/wagiminator 6 | # 7 | # Type "make help" in the command line. 8 | 9 | # Compiler Options 10 | DEVICE = attiny13a 11 | CLOCK = 1200000 12 | 13 | # Programmer Options 14 | PROGRMR = usbasp 15 | TGTDEV = attiny13 16 | LFUSE = 0x2a 17 | HFUSE = 0xfe 18 | 19 | # Objects 20 | OBJECTS = main.o 21 | 22 | # Commands 23 | AVRDUDE = avrdude -c $(PROGRMR) -p $(TGTDEV) 24 | COMPILE = avr-gcc -Wall -Os -flto -mmcu=$(DEVICE) -DF_CPU=$(CLOCK) -DDEBUG_LEVEL=0 25 | CLEAN = rm -f main.lst main.obj main.cof main.list main.map main.eep.hex *.o main.s 26 | 27 | # Symbolic Targets 28 | all: help 29 | 30 | help: 31 | @echo "Use the following commands:" 32 | @echo "make hex compile and build main.hex for $(DEVICE)" 33 | @echo "make asm compile and disassemble to main.asm for $(DEVICE)" 34 | @echo "make bin compile and build main.bin for $(DEVICE)" 35 | @echo "make upload compile and upload to $(DEVICE) using $(PROGRMR)" 36 | @echo "make fuses burn fuses of $(DEVICE) using $(PROGRMR) programmer" 37 | @echo "make install compile, upload and burn fuses for $(DEVICE)" 38 | @echo "make clean remove all build files" 39 | 40 | bin: $(OBJECTS) 41 | @echo "Building main.bin ..." 42 | @$(COMPILE) -o main.bin $(OBJECTS) 43 | @echo "Removing temporary files ..." 44 | @$(CLEAN) 45 | 46 | hex: bin 47 | @echo "Building main.hex ..." 48 | @avr-objcopy -j .text -j .data -O ihex main.bin main.hex 49 | @echo "FLASH: $(shell avr-size -d main.bin | awk '/[0-9]/ {print $$1 + $$2}') bytes" 50 | @echo "SRAM: $(shell avr-size -d main.bin | awk '/[0-9]/ {print $$2 + $$3}') bytes" 51 | @echo "Removing main.bin ..." 52 | @rm -f main.bin 53 | 54 | asm: bin 55 | @echo "Disassembling to main.asm ..." 56 | @avr-objdump -d main.bin > main.asm 57 | @echo "Removing main.bin ..." 58 | @rm -f main.bin 59 | 60 | upload: hex 61 | @echo "Uploading to $(TGTDEV) ..." 62 | @$(AVRDUDE) -U flash:w:main.hex:i 63 | 64 | fuses: 65 | @echo "Burning fuses of $(TGTDEV) ..." 66 | @echo "Warning: RESET pin will be disabled !" 67 | @$(AVRDUDE) -U hfuse:w:$(HFUSE):m -U lfuse:w:$(LFUSE):m 68 | 69 | install: upload fuses 70 | 71 | clean: 72 | @echo "Cleaning all up ..." 73 | @$(CLEAN) 74 | @rm -f main.bin main.hex main.asm 75 | 76 | .c.o: 77 | @echo "Compiling $< for $(DEVICE) @ $(CLOCK)Hz ..." 78 | @$(COMPILE) -c $< -o $@ 79 | 80 | .S.o: 81 | @echo "Compiling $< for $(DEVICE) @ $(CLOCK)Hz ..." 82 | @$(COMPILE) -x assembler-with-cpp -c $< -o $@ 83 | 84 | .c.s: 85 | @echo "Compiling $< for $(DEVICE) @ $(CLOCK)Hz ..." 86 | @$(COMPILE) -S $< -o $@ 87 | --------------------------------------------------------------------------------