├── LICENSE ├── README.md ├── documentation ├── TinyDecoder_pic1.jpg ├── TinyDecoder_pic2.jpg ├── TinyDecoder_pic3.jpg ├── TinyDecoder_pic4.jpg ├── TinyDecoder_pic5.jpg └── TinyDecoder_wiring.png ├── hardware ├── TinyDecoder_BOM.tsv ├── TinyDecoder_gerber.zip └── TinyDecoder_schematic.pdf └── software ├── tiny13 ├── TinyDecoder_t13.ino ├── makefile └── tinydecoder_t13.hex └── tiny25 ├── TinyDecoder_t25.ino ├── makefile └── tinydecoder_t25.hex /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 | # TinyDecoder - IR Remote Receiver and NEC Decoder based on ATtiny13A 2 | TinyDecoderIR is a simple stand-alone IR remote control receiver and protocol decoder with an OLED display based on ATtiny13A (NEC protocol only) or ATtiny25 (NEC, RC-5, SONY SIRC, SAMSUNG protocols). 3 | 4 | - Project Video (YouTube): https://youtu.be/LEl5Z9QBuHo 5 | - Design Files (EasyEDA): https://easyeda.com/wagiminator/attiny13-tinydetectorir 6 | 7 | ![pic1.jpg](https://raw.githubusercontent.com/wagiminator/ATtiny13-TinyDecoder/main/documentation/TinyDecoder_pic1.jpg) 8 | 9 | # Hardware 10 | The basic wiring is shown below: 11 | 12 | ![wiring.png](https://raw.githubusercontent.com/wagiminator/ATtiny13-TinyDecoder/main/documentation/TinyDecoder_wiring.png) 13 | 14 | The device is powered by a 1220 coin cell battery. Please remember that only the rechargeable LIR1220 Li-Ion batteries work. The "normal" CR1220s don't deliver enough power. 15 | 16 | ![pic3.jpg](https://raw.githubusercontent.com/wagiminator/ATtiny13-TinyDecoder/main/documentation/TinyDecoder_pic3.jpg) 17 | 18 | # Software 19 | ## IR Receiving and Decoding 20 | The IR NEC decoding function utilizes timer0 to measure the burst and pause lengths of the signal. The timer is automatically started and stopped or reset by the IR receiver via a pin change interrupt. The measured lengths are interpreted according to the NEC protocol and the transmitted code is calculated accordingly. The program was tested with the TSOP4838, but it should also work with other 38kHz IR receivers (note different pinout if necessary). 21 | 22 | The output of the IR reciever is inverted (active LOW), a burst is indicated by a LOW signal, a pause by a HIGH signal. IR message starts with a 9ms leading burst followed by a 4.5ms pause. Afterwards 4 data bytes are transmitted, least significant bit first. A "0" bit is a 562.5µs burst followed by a 562.5µs pause, a "1" bit is a 562.5µs burst followed by a 1687.5µs pause. A final 562.5µs burst signifies the end of the transmission. According to the data sheet of the TSOP4838, the length of the output signal differs from the transmitted signal by up to 158 microseconds, which the code must take into account. The four data bytes are in order: 23 | - the 8-bit address for the receiving device, 24 | - the 8-bit logical inverse of the address, 25 | - the 8-bit command and 26 | - the 8-bit logical inverse of the command. 27 | 28 | 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. 29 | 30 | For a more detailed explanation on the NEC protocol refer to [TinyRemote](https://github.com/wagiminator/ATtiny13-TinyRemote). 31 | 32 | ```c 33 | // pin definitions 34 | #define IR_OUT PB3 // IR receiver pin 35 | 36 | // IR receiver definitions and macros 37 | #define IR_WAIT_LOW() while( PINB & (1<> 3; if (error < 6) error = 6; 54 | if (IR_duration > dur) return ((IR_duration - dur) < error); 55 | return ((dur - IR_duration) < error); 56 | } 57 | 58 | // IR initialize the receiver 59 | void IR_init(void) { 60 | DDRB &= ~(1<>= 1; // LSB first 74 | IR_WAIT_HIGH(); // wait for end of burst 75 | if (!IR_checkDur(IR_562us)) return 0; // exit if burst has incorrect length 76 | IR_WAIT_LOW(); // wait for end of pause 77 | if (IR_checkDur(IR_1687us)) data |= 0x80000000; // bit "0" or "1" depends on pause duration 78 | else if (!IR_checkDur(IR_562us)) return 0; // exit if it's neither "0" nor "1" 79 | } 80 | IR_WAIT_HIGH(); // wait for end of final burst 81 | if (!IR_checkDur(IR_562us)) return 0; // exit if burst has incorrect length 82 | uint8_t addr1 = data; // get first address byte 83 | uint8_t addr2 = data >> 8; // get second address byte 84 | uint8_t cmd1 = data >> 16; // get first command byte 85 | uint8_t cmd2 = data >> 24; // get second command byte 86 | if ((cmd1 + cmd2) < 255) return 0; // if second command byte is not the inverse of the first 87 | cmd = cmd1; // get the command 88 | if ((addr1 + addr2) == 255) addr = addr1; // check if it's extended NEC-protocol ... 89 | else addr = data; // ... and get the correct address 90 | return IR_NEC; // return NEC success 91 | } 92 | 93 | // wait for and read valid IR command (repeat code will be ignored) 94 | uint8_t IR_read(void) { 95 | uint8_t protocol = IR_FAIL; // variables for received protocol 96 | GIMSK |= (1< 255) OLED_printHex(addr >> 8); // extended NEC 137 | else OLED_printString(SPC); 138 | OLED_printHex(addr); // print received address 139 | OLED_setCursor(0,2); // set cursor to start of third line 140 | OLED_printString(CMD); // print "COMMAND: " 141 | OLED_printHex(cmd); // print received command 142 | } 143 | } 144 | ``` 145 | 146 | ![pic2.jpg](https://raw.githubusercontent.com/wagiminator/ATtiny13-TinyDecoder/main/documentation/TinyDecoder_pic2.jpg) 147 | 148 | ## Compiling and Uploading 149 | Since there is no ICSP header on the board, you have to program the ATtiny either before soldering using an [SOP adapter](https://aliexpress.com/wholesale?SearchText=sop-8+150mil+adapter), or after soldering using an [EEPROM clip](https://aliexpress.com/wholesale?SearchText=sop8+eeprom+programming+clip). The [AVR Programmer Adapter](https://github.com/wagiminator/AVR-Programmer/tree/master/AVR_Programmer_Adapter) can help with this. 150 | 151 | ### If using the Arduino IDE 152 | - Make sure you have installed [MicroCore](https://github.com/MCUdude/MicroCore). 153 | - Go to **Tools -> Board -> MicroCore** and select **ATtiny13**. 154 | - Go to **Tools** and choose the following board options: 155 | - **Clock:** 1.2 MHz internal osc. 156 | - **BOD:** BOD disabled 157 | - **Timing:** Micros disabled 158 | - Connect your programmer to your PC and to the ATtiny. 159 | - Go to **Tools -> Programmer** and select your ISP programmer (e.g. [USBasp](https://aliexpress.com/wholesale?SearchText=usbasp)). 160 | - Go to **Tools -> Burn Bootloader** to burn the fuses. 161 | - Open TinyDecoder_t13.ino and click **Upload**. 162 | 163 | ### If using the precompiled hex-file 164 | - Make sure you have installed [avrdude](https://learn.adafruit.com/usbtinyisp/avrdude). 165 | - Connect your programmer to your PC and to the ATtiny. 166 | - Open a terminal. 167 | - Navigate to the folder with the hex-file. 168 | - Execute the following command (if necessary replace "usbasp" with the programmer you use): 169 | ``` 170 | avrdude -c usbasp -p t13 -U lfuse:w:0x2a:m -U hfuse:w:0xff:m -U flash:w:tinydecoder_t13.hex 171 | ``` 172 | 173 | ### If using the makefile (Linux/Mac) 174 | - Make sure you have installed [avr-gcc toolchain and avrdude](http://maxembedded.com/2015/06/setting-up-avr-gcc-toolchain-on-linux-and-mac-os-x/). 175 | - Connect your programmer to your PC and to the ATtiny. 176 | - Open a terminal. 177 | - Navigate to the folder with the makefile and sketch. 178 | - Run `PROGRMR=usbasp make install` to compile, burn the fuses and upload the firmware (change PROGRMR accordingly). 179 | 180 | ## Upgrading to an ATtiny25 181 | The 1 KB flash of the ATtiny13 is too small to implement the decoding of several protocols in combination with an OLED display (at least I didn't manage to do it). Fortunately, there are pin-compatible models with more memory. The ATtiny25 is available in the same package (150mil SOIC-8, e.g. ATtiny25-20SSU) and double flash memory. In order not to reinvent the wheel, I took David Johnson-Davies' excellent implementation of his [IR Remote Control Detective](http://www.technoblogy.com/show?24A9) for the ATtiny85 and adapted it so that it works with the 2 KB flash of the ATtiny25. In addition to the NEC protocol, Samsung, Sony and RC-5 can also be decoded without further hardware adjustments. 182 | 183 | ![pic4.jpg](https://raw.githubusercontent.com/wagiminator/ATtiny13-TinyDecoder/main/documentation/TinyDecoder_pic4.jpg) 184 | ![pic5.jpg](https://raw.githubusercontent.com/wagiminator/ATtiny13-TinyDecoder/main/documentation/TinyDecoder_pic5.jpg) 185 | 186 | # References, Links and Notes 187 | 1. [ATtiny13A Datasheet](http://ww1.microchip.com/downloads/en/DeviceDoc/doc8126.pdf) 188 | 2. [SSD1306 Datasheet](https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf) 189 | 3. [TSOP4838 Datasheet](https://www.vishay.com/docs/82459/tsop48.pdf) 190 | 4. [IR Remote Control based on ATtiny13A](https://github.com/wagiminator/ATtiny13-TinyRemote) 191 | 5. [How to use an I²C OLED with ATtiny13](https://github.com/wagiminator/ATtiny13-TinyOLEDdemo) 192 | 6. [IR Remote Control Detective](http://www.technoblogy.com/show?24A9) 193 | -------------------------------------------------------------------------------- /documentation/TinyDecoder_pic1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/ATtiny13-TinyDecoder/3877329cc3b6bfa8e39de6c8100b29e98eada681/documentation/TinyDecoder_pic1.jpg -------------------------------------------------------------------------------- /documentation/TinyDecoder_pic2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/ATtiny13-TinyDecoder/3877329cc3b6bfa8e39de6c8100b29e98eada681/documentation/TinyDecoder_pic2.jpg -------------------------------------------------------------------------------- /documentation/TinyDecoder_pic3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/ATtiny13-TinyDecoder/3877329cc3b6bfa8e39de6c8100b29e98eada681/documentation/TinyDecoder_pic3.jpg -------------------------------------------------------------------------------- /documentation/TinyDecoder_pic4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/ATtiny13-TinyDecoder/3877329cc3b6bfa8e39de6c8100b29e98eada681/documentation/TinyDecoder_pic4.jpg -------------------------------------------------------------------------------- /documentation/TinyDecoder_pic5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/ATtiny13-TinyDecoder/3877329cc3b6bfa8e39de6c8100b29e98eada681/documentation/TinyDecoder_pic5.jpg -------------------------------------------------------------------------------- /documentation/TinyDecoder_wiring.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/ATtiny13-TinyDecoder/3877329cc3b6bfa8e39de6c8100b29e98eada681/documentation/TinyDecoder_wiring.png -------------------------------------------------------------------------------- /hardware/TinyDecoder_BOM.tsv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/ATtiny13-TinyDecoder/3877329cc3b6bfa8e39de6c8100b29e98eada681/hardware/TinyDecoder_BOM.tsv -------------------------------------------------------------------------------- /hardware/TinyDecoder_gerber.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/ATtiny13-TinyDecoder/3877329cc3b6bfa8e39de6c8100b29e98eada681/hardware/TinyDecoder_gerber.zip -------------------------------------------------------------------------------- /hardware/TinyDecoder_schematic.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/ATtiny13-TinyDecoder/3877329cc3b6bfa8e39de6c8100b29e98eada681/hardware/TinyDecoder_schematic.pdf -------------------------------------------------------------------------------- /software/tiny13/TinyDecoder_t13.ino: -------------------------------------------------------------------------------- 1 | // =================================================================================== 2 | // Project: TinyDecoderIR - IR remote receiver and NEC protocol decoder 3 | // Version: v1.0 4 | // Year: 2020 5 | // Author: Stefan Wagner 6 | // Github: https://github.com/wagiminator 7 | // EasyEDA: https://easyeda.com/wagiminator 8 | // License: http://creativecommons.org/licenses/by-sa/3.0/ 9 | // =================================================================================== 10 | // 11 | // Description: 12 | // ------------ 13 | // ATtiny13A receives IR signal via TSOP4838, decodes the signal 14 | // (NEC protocol) and displays address and command (hex values) on an 15 | // SSD1306 128x32 OLED display. 16 | // 17 | // The I²C protocol implementation is based on a crude bitbanging method. 18 | // It was specifically designed for the limited resources of ATtiny10 and 19 | // ATtiny13, but should work with some other AVRs as well. 20 | // The functions for the OLED are adapted to the SSD1306 128x32 OLED module, 21 | // but they can easily be modified to be used for other modules. In order to 22 | // save resources, only the basic functionalities which are needed for this 23 | // application are implemented. 24 | // For a detailed information on the working principle of the I²C OLED 25 | // implementation visit https://github.com/wagiminator/attiny13-tinyoleddemo 26 | // 27 | // The IR NEC decoding function utilizes timer0 to measure the burst and 28 | // pause lengths of the signal. The timer is automatically started and 29 | // stopped or reset by the IR receiver via a pin change interrupt. The 30 | // measured lengths are interpreted according to the NEC protocol and the 31 | // transmitted code is calculated accordingly. The program was tested with 32 | // the TSOP4838, but it should also work with other 38kHz IR receivers 33 | // (note different pinout if necessary). 34 | // 35 | // ------+ +-----+ +-+ +---+ +---+ +-+ 36 | // | | | | | | | | | | | bit0: 562.5us 37 | // | 9ms |4.5ms| |0| | 1 | | 1 | |0| ... 38 | // | | | | | | | | | | | bit1: 1687.5us 39 | // +---------+ +-+ +-+ +-+ +-+ +- 40 | // 41 | // The output of the IR reciever is inverted, a burst is indicated by a 42 | // LOW signal, a pause by a HIGH signal. IR message starts with a 9ms 43 | // leading burst followed by a 4.5ms pause. Afterwards 4 data bytes are 44 | // transmitted, least significant bit first. A "0" bit is a 562.5us burst 45 | // followed by a 562.5us pause, a "1" bit is a 562.5us burst followed by 46 | // a 1687.5us pause. A final 562.5us burst signifies the end of the 47 | // transmission. The four data bytes are in order: 48 | // - the 8-bit address for the receiving device, 49 | // - the 8-bit logical inverse of the address, 50 | // - the 8-bit command and 51 | // - the 8-bit logical inverse of the command. 52 | // The Extended NEC protocol uses 16-bit addresses. Instead of sending 53 | // an 8-bit address and its logically inverse, first the low byte and 54 | // then the high byte of the address is transmitted. 55 | // If the key on the remote controller is kept depressed, a repeat code 56 | // will be issued consisting of a 9ms leading burst, a 2.25ms pause and 57 | // a 562.5us burst to mark the end. The repeat code will continue to be 58 | // sent out at 108ms intervals, until the key is finally released. 59 | // For this application the repeat code will be ignored because it's not 60 | // needed here. 61 | // 62 | // Wiring: 63 | // ------- 64 | // +-------+ 65 | // OUT ------ 1| -- | 66 | // GND ------ 2| ( ) | TSOP4838 67 | // VCC ------ 3| -- | 68 | // +-------+ 69 | // 70 | // +-----------------------------+ 71 | // ---|SDA +--------------------+ | 72 | // ---|SCL | SSD1306 OLED | | 73 | // ---|VCC | 128x36 | | 74 | // ---|GND +--------------------+ | 75 | // +-----------------------------+ 76 | // 77 | // +-\/-+ 78 | // --- RST ADC0 PB5 1|° |8 Vcc 79 | // TSOP4838 ------- ADC3 PB3 2| |7 PB2 ADC1 -------- SDA OLED 80 | // ------- ADC2 PB4 3| |6 PB1 AIN1 OC0B --- SCL OLED 81 | // GND 4| |5 PB0 AIN0 OC0A --- 82 | // +----+ 83 | // 84 | // Compilation Settings: 85 | // --------------------- 86 | // Controller: ATtiny13A 87 | // Core: MicroCore (https://github.com/MCUdude/MicroCore) 88 | // Clockspeed: 1.2 MHz internal 89 | // BOD: BOD disabled 90 | // Timing: Micros disabled 91 | // 92 | // Leave the rest on default settings. Don't forget to "Burn bootloader"! 93 | // No Arduino core functions or libraries are used. Use the makefile if 94 | // you want to compile without Arduino IDE. 95 | // 96 | // Fuse settings: -U lfuse:w:0x2a:m -U hfuse:w:0xff:m 97 | 98 | 99 | // =================================================================================== 100 | // Libraries and Definitions 101 | // =================================================================================== 102 | 103 | // Libraries 104 | #include // for GPIO 105 | #include // for interrupts 106 | #include // to store data in program memory 107 | 108 | // Pin definitions 109 | #define I2C_SCL PB1 // I2C serial clock pin 110 | #define I2C_SDA PB2 // I2C serial data pin 111 | #define IR_OUT PB3 // IR receiver pin 112 | 113 | // =================================================================================== 114 | // I2C Implementation 115 | // =================================================================================== 116 | 117 | // I2C macros 118 | #define I2C_SDA_HIGH() DDRB &= ~(1< pulled HIGH by resistor 119 | #define I2C_SDA_LOW() DDRB |= (1< pulled LOW by MCU 120 | #define I2C_SCL_HIGH() DDRB &= ~(1< pulled HIGH by resistor 121 | #define I2C_SCL_LOW() DDRB |= (1< pulled LOW by MCU 122 | 123 | // I2C init function 124 | void I2C_init(void) { 125 | DDRB &= ~((1< lines released 126 | PORTB &= ~((1< slave reads the bit 135 | I2C_SCL_LOW(); // clock LOW again 136 | } 137 | I2C_SDA_HIGH(); // release SDA for ACK bit of slave 138 | I2C_SCL_HIGH(); // 9th clock pulse is for the ACK bit 139 | I2C_SCL_LOW(); // but ACK bit is ignored 140 | } 141 | 142 | // I2C start transmission 143 | void I2C_start(uint8_t addr) { 144 | I2C_SDA_LOW(); // start condition: SDA goes LOW first 145 | I2C_SCL_LOW(); // start condition: SCL goes LOW second 146 | I2C_write(addr); // send slave address 147 | } 148 | 149 | // I2C stop transmission 150 | void I2C_stop(void) { 151 | I2C_SDA_LOW(); // prepare SDA for LOW to HIGH transition 152 | I2C_SCL_HIGH(); // stop condition: SCL goes HIGH first 153 | I2C_SDA_HIGH(); // stop condition: SDA goes HIGH second 154 | } 155 | 156 | // =================================================================================== 157 | // OLED Implementation 158 | // =================================================================================== 159 | 160 | // OLED definitions 161 | #define OLED_ADDR 0x78 // OLED write address 162 | #define OLED_CMD_MODE 0x00 // set command mode 163 | #define OLED_DAT_MODE 0x40 // set data mode 164 | #define OLED_INIT_LEN 9 // 9: no screen flip, 11: screen flip 165 | 166 | // OLED init settings 167 | const uint8_t OLED_INIT_CMD[] PROGMEM = { 168 | 0xA8, 0x1F, // set multiplex (HEIGHT-1): 0x1F for 128x32, 0x3F for 128x64 169 | 0x20, 0x01, // set vertical memory addressing mode 170 | 0xDA, 0x02, // set COM pins hardware configuration to sequential 171 | 0x8D, 0x14, // enable charge pump 172 | 0xAF, // switch on OLED 173 | 0xA1, 0xC8 // flip the screen 174 | }; 175 | 176 | // OLED simple reduced 3x8 font 177 | const uint8_t OLED_FONT[] PROGMEM = { 178 | 0x7F, 0x41, 0x7F, // 0 0 179 | 0x00, 0x00, 0x7F, // 1 1 180 | 0x79, 0x49, 0x4F, // 2 2 181 | 0x41, 0x49, 0x7F, // 3 3 182 | 0x0F, 0x08, 0x7E, // 4 4 183 | 0x4F, 0x49, 0x79, // 5 5 184 | 0x7F, 0x49, 0x79, // 6 6 185 | 0x03, 0x01, 0x7F, // 7 7 186 | 0x7F, 0x49, 0x7F, // 8 8 187 | 0x4F, 0x49, 0x7F, // 9 9 188 | 0x7F, 0x09, 0x7F, // A 10 189 | 0x7F, 0x49, 0x36, // B 11 190 | 0x7F, 0x41, 0x63, // C 12 191 | 0x7F, 0x41, 0x3E, // D 13 192 | 0x7F, 0x49, 0x41, // E 14 193 | 0x7F, 0x09, 0x01, // F 15 194 | 0x41, 0x7F, 0x41, // I 16 195 | 0x7F, 0x02, 0x7F, // M 17 196 | 0x7F, 0x01, 0x7E, // N 18 197 | 0x7F, 0x09, 0x76, // R 19 198 | 0x3F, 0x40, 0x3F, // V 20 199 | 0x00, 0x36, 0x00, // : 21 200 | 0x00, 0x00, 0x00 // 22 201 | }; 202 | 203 | // OLED init function 204 | void OLED_init(void) { 205 | I2C_init(); // initialize I2C first 206 | I2C_start(OLED_ADDR); // start transmission to OLED 207 | I2C_write(OLED_CMD_MODE); // set command mode 208 | for(uint8_t i = 0; i < OLED_INIT_LEN; i++) 209 | I2C_write(pgm_read_byte(&OLED_INIT_CMD[i])); // send the command bytes 210 | I2C_stop(); // stop transmission 211 | } 212 | 213 | // OLED set the cursor 214 | void OLED_setCursor(uint8_t xpos, uint8_t ypos) { 215 | I2C_start(OLED_ADDR); // start transmission to OLED 216 | I2C_write(OLED_CMD_MODE); // set command mode 217 | I2C_write(0x22); // command for min/max page 218 | I2C_write(ypos); I2C_write(ypos+1); // min: ypos; max: ypos+1 219 | I2C_write(xpos & 0x0F); // set low nibble of start column 220 | I2C_write(0x10 | (xpos >> 4)); // set high nibble of start column 221 | I2C_write(0xB0 | (ypos)); // set start page 222 | I2C_stop(); // stop transmission 223 | } 224 | 225 | // OLED clear screen 226 | void OLED_clearScreen(void) { 227 | OLED_setCursor(0, 0); // set cursor at upper half 228 | I2C_start(OLED_ADDR); // start transmission to OLED 229 | I2C_write(OLED_DAT_MODE); // set data mode 230 | uint8_t i = 0; // count variable 231 | do {I2C_write(0x00);} while(--i); // clear upper half 232 | I2C_stop(); // stop transmission 233 | OLED_setCursor(0, 2); // set cursor at lower half 234 | I2C_start(OLED_ADDR); // start transmission to OLED 235 | I2C_write(OLED_DAT_MODE); // set data mode 236 | do {I2C_write(0x00);} while(--i); // clear lower half 237 | I2C_stop(); // stop transmission 238 | } 239 | 240 | // OLED stretch 8-bit value (x) to 16-bit and write it several times (t) 241 | // abcdefgh -> aabbccddeeffgghh 242 | // refer to http://www.technoblogy.com/show?LKP 243 | void OLED_stretch(uint16_t x, uint8_t t) { 244 | x = (x & 0xF0)<<4 | (x & 0x0F); 245 | x = (x<<2 | x) & 0x3333; 246 | x = (x<<1 | x) & 0x5555; 247 | x |= x<<1; 248 | for(; t; t--) { // print t-times on the OLED 249 | I2C_write(x); // write low byte 250 | I2C_write(x>>8); // write high byte 251 | } 252 | } 253 | 254 | // OLED print a big character 255 | void OLED_printChar(uint8_t ch) { 256 | ch += ch << 1; // calculate position of character in font array 257 | OLED_stretch(pgm_read_byte(&OLED_FONT[ch++]), 2); 258 | OLED_stretch(pgm_read_byte(&OLED_FONT[ch++]), 3); 259 | OLED_stretch(pgm_read_byte(&OLED_FONT[ch ]), 2); 260 | for(ch=4; ch; ch--) I2C_write(0x00); // print spacing between characters 261 | } 262 | 263 | // OLED print a string from program memory; terminator: 255 264 | void OLED_printString(const uint8_t* p) { 265 | I2C_start(OLED_ADDR); // start transmission to OLED 266 | I2C_write(OLED_DAT_MODE); // set data mode 267 | uint8_t ch = pgm_read_byte(p); // read first character from program memory 268 | while(ch < 255) { // repeat until string terminator 269 | OLED_printChar(ch); // print character on OLED 270 | ch = pgm_read_byte(++p); // read next character 271 | } 272 | I2C_stop(); // stop transmission 273 | } 274 | 275 | // OLED print byte as hex value 276 | void OLED_printHex(uint8_t val) { 277 | I2C_start(OLED_ADDR); // start transmission to OLED 278 | I2C_write(OLED_DAT_MODE); // set data mode 279 | OLED_printChar(val>>4); // print high nibble of the byte 280 | OLED_printChar(val & 0x0F); // print low nibble of the byte 281 | I2C_stop(); // stop transmission 282 | } 283 | 284 | // =================================================================================== 285 | // IR Receiver Implementation 286 | // =================================================================================== 287 | 288 | // IR receiver definitions and macros 289 | #define IR_WAIT_LOW() while( PINB & (1<> 3; if(error < 6) error = 6; 315 | if(IR_duration > dur) return((IR_duration - dur) < error); 316 | return((dur - IR_duration) < error); 317 | } 318 | 319 | // IR read data according to NEC protocol 320 | uint8_t IR_readNEC(void) { 321 | uint32_t data; 322 | IR_WAIT_LOW(); // wait for end of start pause 323 | if(!IR_checkDur(IR_4500us)) return 0; // exit if no start condition 324 | for(uint8_t i=32; i; i--) { // receive 32 bits 325 | data >>= 1; // LSB first 326 | IR_WAIT_HIGH(); // wait for end of burst 327 | if(!IR_checkDur(IR_562us)) return 0; // exit if burst has incorrect length 328 | IR_WAIT_LOW(); // wait for end of pause 329 | if(IR_checkDur(IR_1687us)) data |= 0x80000000; // bit "0" or "1" depends on pause duration 330 | else if(!IR_checkDur(IR_562us)) return 0; // exit if it's neither "0" nor "1" 331 | } 332 | IR_WAIT_HIGH(); // wait for end of final burst 333 | if (!IR_checkDur(IR_562us)) return 0; // exit if burst has incorrect length 334 | uint8_t addr1 = data; // get first address byte 335 | uint8_t addr2 = data >> 8; // get second address byte 336 | uint8_t cmd1 = data >> 16; // get first command byte 337 | uint8_t cmd2 = data >> 24; // get second command byte 338 | if((cmd1 + cmd2) < 255) return 0; // if second command byte is not the inverse of the first 339 | cmd = cmd1; // get the command 340 | if((addr1 + addr2) == 255) addr = addr1;// check if it's extended NEC-protocol ... 341 | else addr = data; // ... and get the correct address 342 | return IR_NEC; // return NEC success 343 | } 344 | 345 | // IR wait for and read valid IR command (repeat code will be ignored) 346 | uint8_t IR_read(void) { 347 | uint8_t protocol = IR_FAIL; // variables for received protocol 348 | GIMSK |= (1< 255) OLED_printHex(addr >> 8); // extended NEC 391 | else OLED_printString(SPC); 392 | OLED_printHex(addr); // print received address 393 | OLED_setCursor(0,2); // set cursor to start of third line 394 | OLED_printString(CMD); // print "COMMAND: " 395 | OLED_printHex(cmd); // print received command 396 | } 397 | } 398 | -------------------------------------------------------------------------------- /software/tiny13/makefile: -------------------------------------------------------------------------------- 1 | # =================================================================================== 2 | # Project: TinyDecoder t13 3 | # Author: Stefan Wagner 4 | # Year: 2020 5 | # URL: https://github.com/wagiminator 6 | # =================================================================================== 7 | # Type "make help" in the command line. 8 | # =================================================================================== 9 | 10 | # Input and Output File Names 11 | SKETCH = TinyDecoder_t13.ino 12 | TARGET = tinydecoder_t13 13 | 14 | # Microcontroller Settings 15 | DEVICE = attiny13a 16 | CLOCK = 1200000 17 | LFUSE = 0x2a 18 | HFUSE = 0xff 19 | 20 | # Programmer Settings 21 | PROGRMR ?= usbasp 22 | TGTDEV = attiny13 23 | 24 | # Toolchain 25 | CC = avr-gcc 26 | OBJCOPY = avr-objcopy 27 | OBJDUMP = avr-objdump 28 | AVRSIZE = avr-size 29 | AVRDUDE = avrdude -c $(PROGRMR) -p $(TGTDEV) 30 | CLEAN = rm -f *.lst *.obj *.cof *.list *.map *.eep.hex *.o *.s *.d 31 | 32 | # Compiler Flags 33 | CFLAGS = -Wall -Os -flto -mmcu=$(DEVICE) -DF_CPU=$(CLOCK) -x c++ 34 | 35 | # Symbolic Targets 36 | help: 37 | @echo "Use the following commands:" 38 | @echo "make all compile and build $(TARGET).elf/.bin/.hex/.asm for $(DEVICE)" 39 | @echo "make hex compile and build $(TARGET).hex for $(DEVICE)" 40 | @echo "make asm compile and disassemble to $(TARGET).asm for $(DEVICE)" 41 | @echo "make bin compile and build $(TARGET).bin for $(DEVICE)" 42 | @echo "make upload compile and upload to $(DEVICE) using $(PROGRMR)" 43 | @echo "make fuses burn fuses of $(DEVICE) using $(PROGRMR) programmer" 44 | @echo "make install compile, upload and burn fuses for $(DEVICE)" 45 | @echo "make clean remove all build files" 46 | 47 | all: buildelf buildbin buildhex buildasm removetemp size 48 | 49 | elf: buildelf removetemp size 50 | 51 | bin: buildelf buildbin removetemp size removeelf 52 | 53 | hex: buildelf buildhex removetemp size removeelf 54 | 55 | asm: buildelf buildasm removetemp size removeelf 56 | 57 | flash: upload fuses 58 | 59 | install: upload fuses 60 | 61 | upload: hex 62 | @echo "Uploading $(TARGET).hex to $(DEVICE) using $(PROGRMR) ..." 63 | @$(AVRDUDE) -U flash:w:$(TARGET).hex:i 64 | 65 | fuses: 66 | @echo "Burning fuses of $(DEVICE) ..." 67 | @$(AVRDUDE) -U lfuse:w:$(LFUSE):m -U hfuse:w:$(HFUSE):m 68 | 69 | clean: 70 | @echo "Cleaning all up ..." 71 | @$(CLEAN) 72 | @rm -f $(TARGET).elf $(TARGET).bin $(TARGET).hex $(TARGET).asm 73 | 74 | buildelf: 75 | @echo "Compiling $(SKETCH) for $(DEVICE) @ $(CLOCK)Hz ..." 76 | @$(CC) $(CFLAGS) $(SKETCH) -o $(TARGET).elf 77 | 78 | buildbin: 79 | @echo "Building $(TARGET).bin ..." 80 | @$(OBJCOPY) -O binary -R .eeprom $(TARGET).elf $(TARGET).bin 81 | 82 | buildhex: 83 | @echo "Building $(TARGET).hex ..." 84 | @$(OBJCOPY) -j .text -j .data -O ihex $(TARGET).elf $(TARGET).hex 85 | 86 | buildasm: 87 | @echo "Disassembling to $(TARGET).asm ..." 88 | @$(OBJDUMP) -d $(TARGET).elf > $(TARGET).asm 89 | 90 | size: 91 | @echo "------------------" 92 | @echo "FLASH: $(shell $(AVRSIZE) -d $(TARGET).elf | awk '/[0-9]/ {print $$1 + $$2}') bytes" 93 | @echo "SRAM: $(shell $(AVRSIZE) -d $(TARGET).elf | awk '/[0-9]/ {print $$2 + $$3}') bytes" 94 | @echo "------------------" 95 | 96 | removetemp: 97 | @echo "Removing temporary files ..." 98 | @$(CLEAN) 99 | 100 | removeelf: 101 | @echo "Removing $(TARGET).elf ..." 102 | @rm -f $(TARGET).elf 103 | -------------------------------------------------------------------------------- /software/tiny13/tinydecoder_t13.hex: -------------------------------------------------------------------------------- 1 | :1000000046C053C019C151C050C04FC04EC04DC0B2 2 | :100010004CC04BC07F417F00007F79494F41497FF1 3 | :100020000F087E4F49797F497903017F7F497F4FD0 4 | :10003000497F7F097F7F49367F41637F413E7F490A 5 | :10004000417F0901417F417F027F7F017E7F0976E9 6 | :100050003F403F003600000000A81F2001DA028D5B 7 | :1000600014AFA1C80C0011110A120D1516161616A0 8 | :10007000FF1616FF0A0D0D130E0505151616FF01C6 9 | :100080001316120E0C160D0E0C000D0E13FF11247C 10 | :100090001FBECFE9CDBF20E0A0E6B0E001C01D92B9 11 | :1000A000A436B207E1F7D8D080C1AACF682F70E09C 12 | :1000B000282F269526952695263008F426E090913F 13 | :1000C000600030E0891750F44091600050E0461B1A 14 | :1000D000570B81E04217530754F008C0809160002D 15 | :1000E000681B710981E0621773070CF080E00895C6 16 | :1000F000BA9AB998BA98089598E0BA9A87FDBA98CA 17 | :10010000B998B99A880F9150C1F7BA98B998B99A25 18 | :100110000895BA9AB99A88E7EFCFCF93C82FF9DF3D 19 | :1001200080E0EADF82E2E8DF8C2FE6DF81E08C0FFF 20 | :10013000E3DF80E0E1DF80E1DFDF8C2F806BDCDF5D 21 | :10014000CF91D6CF0F931F93CF93DF93162F9C01A0 22 | :10015000207F332744E0220F331F4A95E1F78F7049 23 | :100160009927822B932B9C01220F331F220F331FC1 24 | :10017000822B932B837393739C01220F331F822B4B 25 | :10018000932B85759575EC01CC0FDD1FC82BD92BF2 26 | :100190000D2F112331F08C2FAFDF802FADDF1150E9 27 | :1001A000F8CFDF91CF911F910F910895CF93982FA2 28 | :1001B000990FC82FC90FEC2FF0E0EC5EFF4F849130 29 | :1001C00062E090E0BFDFE1E0EC0FF0E0EC5EFF4FBB 30 | :1001D000849163E090E0B6DFCE5FEC2FF0E0EC5E60 31 | :1001E000FF4F849162E090E0ADDF80E085DF80E04A 32 | :1001F00083DF80E081DF80E0CF917ECFCF93C82F77 33 | :1002000088DF80E479DF8C2F82958F70CFDF8C2F91 34 | :100210008F70CCDFCF916CCFCF93DF93EC0179DF80 35 | :1002200080E46ADFFE0184918F3F19F0BFDF2196E1 36 | :10023000F9CFDF91CF915CCF1F920F920FB60F9243 37 | :1002400011248F9382B78093600012BE8F910F901C 38 | :100250000FBE0F901F901895BB98AB9A1FBC83E000 39 | :1002600083BF789487B3897F87BB88B3897F88BB36 40 | :1002700050DF80E041DFC9E5D0E0FE0184913CDF42 41 | :100280002196F0E0C236DF07C1F732DF80E045DFBC 42 | :1002900040DF80E431DFC0E080E02EDFC150E1F7D5 43 | :1002A00027DF82E03ADF35DF80E426DF80E024DFED 44 | :1002B000C150E1F71DDF81E030DF8FE790E0ACDF78 45 | :1002C0008BB780628BBFB39BFECFB399FECFB39B3E 46 | :1002D000FECF89EAEBDE8823B1F3B399FECF84E544 47 | :1002E000E5DE882381F3C0E2F694E794D794C794BF 48 | :1002F000B39BFECF8BE0DADE882329F3B399FECFE0 49 | :1003000080E2D4DE882319F06894F7F804C08BE00B 50 | :10031000CDDE8823C1F2C15039F7B39BFECF8BE00D 51 | :10032000C5DE882381F2A701662777278F2D9927BD 52 | :10033000AA27BB27242F30E0820F931F8F3F910500 53 | :1003400014F2409361008C2D90E09C012D0D311D25 54 | :100350002F3F310529F4909363008093620004C01D 55 | :10036000D0926300C09262008BB78F7D8BBF80E01C 56 | :10037000D4DE84E790E050DFC0916200D09163004A 57 | :10038000CF3FD10521F018F08D2F38DF03C081E772 58 | :1003900090E042DF8C2F32DF82E0BFDE84E690E027 59 | :0E03A0003BDF809161002ADF8BCFF894FFCF06 60 | :00000001FF 61 | -------------------------------------------------------------------------------- /software/tiny25/TinyDecoder_t25.ino: -------------------------------------------------------------------------------- 1 | // =================================================================================== 2 | // Project: TinyDecoderIR - IR remote receiver and NEC protocol decoder 3 | // Version: v1.0 4 | // Year: 2020 5 | // Author: Stefan Wagner 6 | // Github: https://github.com/wagiminator 7 | // EasyEDA: https://easyeda.com/wagiminator 8 | // License: http://creativecommons.org/licenses/by-sa/3.0/ 9 | // =================================================================================== 10 | // 11 | // Description: 12 | // ------------ 13 | // ATtiny25 receives IR signal via TSOP4838, decodes the signal and 14 | // displays address and command (hex values) on an SSD1306 128x32 OLED 15 | // display. 16 | // 17 | // The I²C protocol implementation is based on a crude bitbanging method. 18 | // It was specifically designed for the limited resources of ATtiny10 and 19 | // ATtiny13, but should work with some other AVRs as well. 20 | // The functions for the OLED are adapted to the SSD1306 128x32 OLED module, 21 | // but they can easily be modified to be used for other modules. In order to 22 | // save resources, only the basic functionalities which are needed for this 23 | // application are implemented. 24 | // For a detailed information on the working principle of the I²C OLED 25 | // implementation visit https://github.com/wagiminator/attiny13-tinyoleddemo 26 | // 27 | // References: 28 | // ----------- 29 | // The IR decoding function is taken from David Johnson-Davies' IR Remote 30 | // Control Detective: http://www.technoblogy.com/show?24A9 31 | // 32 | // Wiring: 33 | // ------- 34 | // +-------+ 35 | // OUT ------ 1| -- | 36 | // GND ------ 2| ( ) | TSOP4838 37 | // VCC ------ 3| -- | 38 | // +-------+ 39 | // 40 | // +-----------------------------+ 41 | // ---|SDA +--------------------+ | 42 | // ---|SCL | SSD1306 OLED | | 43 | // ---|VCC | 128x36 | | 44 | // ---|GND +--------------------+ | 45 | // +-----------------------------+ 46 | // 47 | // +-\/-+ 48 | // --- RST ADC0 PB5 1|° |8 Vcc 49 | // TSOP4838 ------- ADC3 PB3 2| |7 PB2 ADC1 -------- SDA OLED 50 | // ------- ADC2 PB4 3| |6 PB1 AIN1 OC0B --- SCL OLED 51 | // GND 4| |5 PB0 AIN0 OC0A --- 52 | // +----+ 53 | // 54 | // Compilation Settings: 55 | // --------------------- 56 | // Core: ATtinyCore (https://github.com/SpenceKonde/ATTinyCore) 57 | // Board: ATtiny25/45/85 (No bootloader) 58 | // Chip: ATtiny25 or 45 or 85 (depending on your chip) 59 | // Clock: 1 MHz (internal) 60 | // Millis: disabled 61 | // B.O.D.: 2.7V 62 | // 63 | // Leave the rest on default settings. Don't forget to "Burn bootloader"! 64 | // No Arduino core functions or libraries are used. Use the makefile if 65 | // you want to compile without Arduino IDE. 66 | // 67 | // Fuse settings: -U lfuse:w:0x62:m -U hfuse:w:0xd7:m -U efuse:w:0xff:m 68 | 69 | 70 | // =================================================================================== 71 | // Libraries and Definitions 72 | // =================================================================================== 73 | 74 | // Libraries 75 | #include // for GPIO 76 | #include // for interrupts 77 | #include // to store data in program memory 78 | 79 | // Pin definitions 80 | #define I2C_SCL PB1 // I2C serial clock pin 81 | #define I2C_SDA PB2 // I2C serial data pin 82 | #define IR_OUT PB3 // IR receiver pin 83 | 84 | // =================================================================================== 85 | // I2C Implementation 86 | // =================================================================================== 87 | 88 | // I2C macros 89 | #define I2C_SDA_HIGH() DDRB &= ~(1< pulled HIGH by resistor 90 | #define I2C_SDA_LOW() DDRB |= (1< pulled LOW by MCU 91 | #define I2C_SCL_HIGH() DDRB &= ~(1< pulled HIGH by resistor 92 | #define I2C_SCL_LOW() DDRB |= (1< pulled LOW by MCU 93 | 94 | // I2C init function 95 | void I2C_init(void) { 96 | DDRB &= ~((1< lines released 97 | PORTB &= ~((1< slave reads the bit 106 | I2C_SCL_LOW(); // clock LOW again 107 | } 108 | I2C_SDA_HIGH(); // release SDA for ACK bit of slave 109 | I2C_SCL_HIGH(); // 9th clock pulse is for the ACK bit 110 | I2C_SCL_LOW(); // but ACK bit is ignored 111 | } 112 | 113 | // I2C start transmission 114 | void I2C_start(uint8_t addr) { 115 | I2C_SDA_LOW(); // start condition: SDA goes LOW first 116 | I2C_SCL_LOW(); // start condition: SCL goes LOW second 117 | I2C_write(addr); // send slave address 118 | } 119 | 120 | // I2C stop transmission 121 | void I2C_stop(void) { 122 | I2C_SDA_LOW(); // prepare SDA for LOW to HIGH transition 123 | I2C_SCL_HIGH(); // stop condition: SCL goes HIGH first 124 | I2C_SDA_HIGH(); // stop condition: SDA goes HIGH second 125 | } 126 | 127 | // =================================================================================== 128 | // OLED Implementation 129 | // =================================================================================== 130 | 131 | // OLED definitions 132 | #define OLED_ADDR 0x78 // OLED write address 133 | #define OLED_CMD_MODE 0x00 // set command mode 134 | #define OLED_DAT_MODE 0x40 // set data mode 135 | #define OLED_INIT_LEN 9 // 9: no screen flip, 11: screen flip 136 | 137 | // OLED init settings 138 | const uint8_t OLED_INIT_CMD[] PROGMEM = { 139 | 0xA8, 0x1F, // set multiplex (HEIGHT-1): 0x1F for 128x32, 0x3F for 128x64 140 | 0x20, 0x01, // set vertical memory addressing mode 141 | 0xDA, 0x02, // set COM pins hardware configuration to sequential 142 | 0x8D, 0x14, // enable charge pump 143 | 0xAF, // switch on OLED 144 | 0xA1, 0xC8 // flip the screen 145 | }; 146 | 147 | // Simple reduced 3x8 font 148 | const uint8_t OLED_FONT[] PROGMEM = { 149 | 0x7F, 0x41, 0x7F, // 0 0 150 | 0x00, 0x00, 0x7F, // 1 1 151 | 0x79, 0x49, 0x4F, // 2 2 152 | 0x41, 0x49, 0x7F, // 3 3 153 | 0x0F, 0x08, 0x7E, // 4 4 154 | 0x4F, 0x49, 0x79, // 5 5 155 | 0x7F, 0x49, 0x79, // 6 6 156 | 0x03, 0x01, 0x7F, // 7 7 157 | 0x7F, 0x49, 0x7F, // 8 8 158 | 0x4F, 0x49, 0x7F, // 9 9 159 | 0x7F, 0x09, 0x7F, // A 10 160 | 0x7F, 0x49, 0x36, // B 11 161 | 0x7F, 0x41, 0x63, // C 12 162 | 0x7F, 0x41, 0x3E, // D 13 163 | 0x7F, 0x49, 0x41, // E 14 164 | 0x7F, 0x09, 0x01, // F 15 165 | 0x4F, 0x48, 0x7F, // Y 16 166 | 0x7F, 0x02, 0x7F, // M 17 167 | 0x7F, 0x01, 0x7E, // N 18 168 | 0x7F, 0x09, 0x76, // R 19 169 | 0x08, 0x08, 0x08, // - 20 170 | 0x00, 0x36, 0x00, // : 21 171 | 0x00, 0x00, 0x00 // 22 172 | }; 173 | 174 | // OLED init function 175 | void OLED_init(void) { 176 | I2C_init(); // initialize I2C first 177 | I2C_start(OLED_ADDR); // start transmission to OLED 178 | I2C_write(OLED_CMD_MODE); // set command mode 179 | for(uint8_t i = 0; i < OLED_INIT_LEN; i++) 180 | I2C_write(pgm_read_byte(&OLED_INIT_CMD[i])); // send the command bytes 181 | I2C_stop(); // stop transmission 182 | } 183 | 184 | // OLED set the cursor 185 | void OLED_setCursor(uint8_t xpos, uint8_t ypos) { 186 | I2C_start(OLED_ADDR); // start transmission to OLED 187 | I2C_write(OLED_CMD_MODE); // set command mode 188 | I2C_write(0x22); // command for min/max page 189 | I2C_write(ypos); I2C_write(ypos+1); // min: ypos; max: ypos+1 190 | I2C_write(xpos & 0x0F); // set low nibble of start column 191 | I2C_write(0x10 | (xpos >> 4)); // set high nibble of start column 192 | I2C_write(0xB0 | (ypos)); // set start page 193 | I2C_stop(); // stop transmission 194 | } 195 | 196 | // OLED clear screen 197 | void OLED_clearScreen(void) { 198 | OLED_setCursor(0, 0); // set cursor at upper half 199 | I2C_start(OLED_ADDR); // start transmission to OLED 200 | I2C_write(OLED_DAT_MODE); // set data mode 201 | uint8_t i = 0; // count variable 202 | do {I2C_write(0x00);} while(--i); // clear upper half 203 | I2C_stop(); // stop transmission 204 | OLED_setCursor(0, 2); // set cursor at lower half 205 | I2C_start(OLED_ADDR); // start transmission to OLED 206 | I2C_write(OLED_DAT_MODE); // set data mode 207 | do {I2C_write(0x00);} while(--i); // clear lower half 208 | I2C_stop(); // stop transmission 209 | } 210 | 211 | // OLED stretch 8-bit (x) to 16-bit and write it several times (t) 212 | // abcdefgh -> aabbccddeeffgghh 213 | // refer to http://www.technoblogy.com/show?LKP 214 | void OLED_stretch(uint16_t x, uint8_t t) { 215 | x = (x & 0xF0)<<4 | (x & 0x0F); 216 | x = (x<<2 | x) & 0x3333; 217 | x = (x<<1 | x) & 0x5555; 218 | x |= x<<1; 219 | for(; t; t--) { // print t-times on the OLED 220 | I2C_write(x); // write low byte 221 | I2C_write(x>>8); // write high byte 222 | } 223 | } 224 | 225 | // OLED plot a big character 226 | void OLED_plotChar(uint8_t ch) { 227 | ch += ch << 1; // calculate position of character in font array 228 | OLED_stretch(pgm_read_byte(&OLED_FONT[ch++]), 2); 229 | OLED_stretch(pgm_read_byte(&OLED_FONT[ch++]), 3); 230 | OLED_stretch(pgm_read_byte(&OLED_FONT[ch ]), 2); 231 | for(ch=4; ch; ch--) I2C_write(0x00); // print spacing between characters 232 | } 233 | 234 | // OLED print a character on a specified position 235 | void OLED_printChar(uint8_t ch, uint8_t xpos, uint8_t ypos) { 236 | OLED_setCursor(xpos, ypos); // set cursor 237 | I2C_start(OLED_ADDR); // start transmission to OLED 238 | I2C_write(OLED_DAT_MODE); // set data mode 239 | OLED_plotChar(ch); // plot the character 240 | I2C_stop(); // stop transmission 241 | } 242 | 243 | // OLED print a string from program memory; terminator: 255 244 | void OLED_printString(const uint8_t* p, uint8_t xpos, uint8_t ypos) { 245 | OLED_setCursor(xpos, ypos); // set cursor 246 | I2C_start(OLED_ADDR); // start transmission to OLED 247 | I2C_write(OLED_DAT_MODE); // set data mode 248 | uint8_t ch = pgm_read_byte(p); // read first character from program memory 249 | while(ch < 255) { // repeat until string terminator 250 | OLED_plotChar(ch); // plot character on OLED 251 | ch = pgm_read_byte(++p); // read next character 252 | } 253 | I2C_stop(); // stop transmission 254 | } 255 | 256 | // OLED print 16-bit value as hex 257 | void OLED_printHex(uint16_t val, uint8_t xpos, uint8_t ypos) { 258 | OLED_setCursor(xpos, ypos); // set cursor 259 | I2C_start(OLED_ADDR); // start transmission to OLED 260 | I2C_write(OLED_DAT_MODE); // set data mode 261 | OLED_plotChar((val>>12) & 0x0F); // print high nibble of high byte 262 | OLED_plotChar((val>>8) & 0x0F); // print low nibble of high byte 263 | OLED_plotChar((val>>4) & 0x0F); // print high nibble of low byte 264 | OLED_plotChar(val & 0x0F); // print low nibble of low byte 265 | I2C_stop(); // stop transmission 266 | } 267 | 268 | // =================================================================================== 269 | // IR Receiver Implementation (from http://www.technoblogy.com/show?24A9) 270 | // =================================================================================== 271 | 272 | // Global variables 273 | volatile uint32_t RecdData; 274 | volatile int8_t Bit, Edge; 275 | volatile uint8_t IRtype; 276 | uint8_t StartScreen = 0; 277 | 278 | // Protocol timings 279 | const uint8_t Micros = 64; // number of microseconds per TCNT1 count 280 | const uint8_t SonyIntro = 37; // 2400/Micros 281 | const uint8_t SonyMean = 14; // 900/Micros 282 | const uint8_t NECIntro = 140; // 9000/Micros 283 | const uint8_t NECGap = 70; // 4500/Micros 284 | const uint8_t NECPulse = 8; // 562/Micros 285 | const uint8_t NECMean = 17; // 1125/Micros 286 | const uint8_t SamsungIntro = 78; // 5000/Micros 287 | const uint8_t SamsungGap = 78; // 5000/Micros 288 | const uint8_t RC5Half = 13; // 889/Micros 289 | const uint8_t RC5Full = 27; // 1778/Micros 290 | const uint8_t RC5Mean = 20; // 1334/Micros 291 | 292 | // IR types 293 | const uint8_t NECtype = 1; 294 | const uint8_t SAMtype = 2; 295 | const uint8_t RC5type = 3; 296 | const uint8_t SONtype = 4; 297 | 298 | // Some "strings" 299 | const uint8_t IRD[] PROGMEM = { 1, 19, 22, 13, 14, 12, 0, 13, 14, 19, 255}; // "IR DECODER" 300 | const uint8_t ADR[] PROGMEM = {10, 13, 19, 21, 255}; // "ADR:" 301 | const uint8_t CMD[] PROGMEM = {12, 17, 13, 21, 255}; // "CMD:" 302 | const uint8_t IRT[] PROGMEM = {22, 22, 22, 22, 255, // " " 303 | 18, 14, 12, 22, 255, // "NEC " 304 | 5, 10, 17, 5, 255, // "SAMS" 305 | 19, 12, 20, 5, 255, // "RC-5" 306 | 5, 0, 18, 16, 255}; // "SONY" 307 | 308 | // Print received code 309 | void ReceivedCode(uint8_t IRtype, uint16_t Address, uint16_t Command) { 310 | if(!StartScreen) { 311 | StartScreen = 1; 312 | OLED_clearScreen(); 313 | OLED_printString(ADR, 50, 0); 314 | OLED_printString(CMD, 50, 2); 315 | } 316 | if(IRtype < 4) { 317 | OLED_printString(IRT + IRtype + (IRtype<<2), 0, 0); 318 | OLED_printChar(22, 9, 2); OLED_printChar(22, 18, 2); 319 | } 320 | else { 321 | OLED_printString(IRT + 20, 0, 0); 322 | OLED_printChar(IRtype/10, 9, 2); OLED_printChar(IRtype%10, 18, 2); 323 | } 324 | OLED_printHex(Address, 92, 0); 325 | OLED_printHex(Command, 92, 2); 326 | } 327 | 328 | // Calculate difference 329 | uint16_t diff(uint16_t a, uint16_t b) { 330 | if (a > b) return (a - b); 331 | return (b - a); 332 | } 333 | 334 | // Interrupt service routine - called on Timer/Counter1 overflow 335 | ISR(TIMER1_OVF_vect) { 336 | ReceivedCode(Bit, RecdData>>7, RecdData & 0x7F); 337 | TIMSK = TIMSK & ~(1<>PINB3 & 1)); // ignore if wrong edge 347 | else if(IRtype == 0) { 348 | 349 | // End of intro pulse 350 | if(diff(Time, RC5Half) < 5) { 351 | IRtype = RC5type; RecdData = 0x2000; Bit = 12; Edge = 0; Mid = 0; 352 | } else if(diff(Time, RC5Full) < 5) { 353 | IRtype = RC5type; RecdData = 0x2000; Bit = 11; Edge = 0; Mid = 1; 354 | } else if((diff(Time, SonyIntro) < 5) && (Edge == 1)) { 355 | IRtype = SONtype; RecdData = 0; Bit = 0; 356 | TIMSK = TIMSK | 1<>6 & 0x1F, 372 | (~(RecdData>>6) & 0x40) | (RecdData & 0x3F)); 373 | Bit--; 374 | } 375 | } else if(IRtype == SONtype) { 376 | if (Time > SonyMean) RecdData = RecdData | ((uint32_t) 1< NECMean && Bit >= 0) RecdData = RecdData | ((uint32_t) 1<>16); 382 | } 383 | 384 | TCNT1 = 0; // clear counter 385 | TIFR = TIFR | 1< $(TARGET).asm 89 | 90 | size: 91 | @echo "------------------" 92 | @echo "FLASH: $(shell $(AVRSIZE) -d $(TARGET).elf | awk '/[0-9]/ {print $$1 + $$2}') bytes" 93 | @echo "SRAM: $(shell $(AVRSIZE) -d $(TARGET).elf | awk '/[0-9]/ {print $$2 + $$3}') bytes" 94 | @echo "------------------" 95 | 96 | removetemp: 97 | @echo "Removing temporary files ..." 98 | @$(CLEAN) 99 | 100 | removeelf: 101 | @echo "Removing $(TARGET).elf ..." 102 | @rm -f $(TARGET).elf 103 | -------------------------------------------------------------------------------- /software/tiny25/tinydecoder_t25.hex: -------------------------------------------------------------------------------- 1 | :100000004DC05AC085C158C05DC356C055C054C00C 2 | :1000100053C052C051C050C04FC04EC04DC07F41B0 3 | :100020007F00007F79494F41497F0F087E4F497912 4 | :100030007F497903017F7F497F4F497F7F097F7F18 5 | :1000400049367F41637F413E7F49417F09014F48E7 6 | :100050007F7F027F7F017E7F0976080808003600D7 7 | :1000600000000016161616FF120E0C16FF050A11D8 8 | :1000700005FF130C1405FF05001210FF0C110D15E0 9 | :10008000FF0A0D1315FFA81F2001DA028D14AFA17E 10 | :10009000C80113160D0E0C000D0E13FF11241FBE08 11 | :1000A000CFEDCDBF20E0A0E6B0E001C01D92A936A3 12 | :1000B000B207E1F752D37FC3A3CFBA9AB998BA98DF 13 | :1000C000089598E0BA9A87FDBA98B998B99A880FB6 14 | :1000D0009150C1F7BA98B998B99A0895BA9AB99A4D 15 | :1000E00088E7EFCF0F931F93CF93DF93162F9C01D9 16 | :1000F000207F332744E0220F331F4A95E1F78F70AA 17 | :100100009927822B932B9C01220F331F220F331F21 18 | :10011000822B932B837393739C01220F331F822BAB 19 | :10012000932B85759575EC01CC0FDD1FC82BD92B52 20 | :100130000D2F112331F08C2FC4DF802FC2DF11501F 21 | :10014000F8CFDF91CF911F910F910895CF93982F02 22 | :10015000990FC82FC90FEC2FF0E0E25EFF4F84919A 23 | :1001600062E090E0BFDFE1E0EC0FF0E0E25EFF4F25 24 | :10017000849163E090E0B6DFCE5FEC2FF0E0E25ECA 25 | :10018000FF4F849162E090E0ADDF80E09ADF80E095 26 | :1001900098DF80E096DF80E0CF9193CFCF93DF931D 27 | :1001A000C82FD62F9BDF80E08CDF82E28ADF8D2F85 28 | :1001B00088DF81E08D0F85DF8C2F8F7082DF8C2FA1 29 | :1001C00082958F7080617DDF8D2F806B7ADFDF916C 30 | :1001D000CF9173CFCF93C82F862F62E0DFDF7EDF12 31 | :1001E00080E46FDF8C2FB2DFCF9167CFCF93DF93A7 32 | :1001F000EC018CE5D3DF72DF80E463DF8D2F829525 33 | :100200008F70A4DF8D2F8F70A1DFCE0124E0969533 34 | :1002100087952A95E1F78F7099DF8C2F8F7096DF85 35 | :10022000DF91CF914ACFCF93DF93EC01862F642FDC 36 | :10023000B5DF54DF80E445DFFE0184918F3F19F084 37 | :1002400085DF2196F9CFDF91CF9137CFCF9360E053 38 | :1002500080E0A4DF43DF80E434DFC0E080E031DF12 39 | :10026000C150E1F72ADF62E080E098DF37DF80E409 40 | :1002700028DF80E026DFC150E1F7CF911ECFEF925B 41 | :10028000FF920F931F93CF93C82F7B018A01809118 42 | :10029000600081110EC081E080936000D7DF40E0F4 43 | :1002A00062E381E890E0BFDF42E062E38CE790E048 44 | :1002B000BADFC430A0F48C2F90E09C01220F331FD2 45 | :1002C000220F331F820F931F40E060E08D599F4F34 46 | :1002D000AADF69E086E17EDF62E186E10DC040E0F1 47 | :1002E00060E087E790E09FDF8C2F6AE058D2C92F4B 48 | :1002F00069E070DF62E18C2F6DDF60E0C70176DFBF 49 | :1003000062E0C801CF911F910F91FF90EF906ECFE7 50 | :100310001F920F920FB60F9211240F931F932F93DA 51 | :100320003F934F935F936F937F938F939F93AF937D 52 | :10033000BF93EF93FF938FB508B602FE06C01092ED 53 | :10034000680081E08093670048C12091670096B300 54 | :10035000022E000C330B93FB442740F950E0241786 55 | :10036000350709F03AC190E02091680021116EC074 56 | :100370008E30910520F09C012D50310904C02DE0F4 57 | :1003800030E0281B390B25303105A8F483E0809339 58 | :10039000680080E090E2A0E0B0E08093630090937A 59 | :1003A0006400A0936500B09366008CE080936200C7 60 | :1003B000109267005DC08C31910520F09C012B519B 61 | :1003C000310904C02BE130E0281B390B2530310501 62 | :1003D000C0F483E08093680080E090E2A0E0B0E0A9 63 | :1003E0008093630090936400A0936500B0936600CF 64 | :1003F0008BE0809362001092670081E0809361003F 65 | :10040000ECC08632910520F09C012552310904C0D0 66 | :1004100025E230E0281B390B2530310508F0C4C037 67 | :1004200020916700213009F0BFC084E0809368000C 68 | :1004300010926300109264001092650010926600A2 69 | :100440001092620089B7846089BFC7C0209168009C 70 | :10045000233009F072C03091670021E0311120E0B3 71 | :1004600020936700449738F480916100882319F045 72 | :1004700010926100B2C081E08093610080916700BA 73 | :10048000209162004091630050916400609165008A 74 | :1004900070916600082E000C990BAA0BBB0B04C0D0 75 | :1004A000880F991FAA1FBB1F2A95D2F7842B952B63 76 | :1004B000A62BB72B8093630090936400A0936500F4 77 | :1004C000B093660080916200811133C08091630017 78 | :1004D00090916400A0916500B09166004091630026 79 | :1004E0005091640060916500709166000091630016 80 | :1004F00010916400209165003091660066E0B69529 81 | :10050000A795979587956A95D1F780959095807472 82 | :1005100099274F735527482B592B76E03695279509 83 | :10052000179507957A95D1F7B8016F71772783E012 84 | :10053000A6DE8091620081502AC02091680024309C 85 | :1005400049F50F9708F120916200409163005091A6 86 | :100550006400609165007091660081E090E0A0E029 87 | :10056000B0E004C0880F991FAA1FBB1F2A95D2F7BD 88 | :10057000842B952BA62BB72B80936300909364005C 89 | :10058000A0936500B0936600809162008F5F8093B6 90 | :10059000620023C020916800213009F44AC02091F4 91 | :1005A00068002230D1F445C08F34910568F19C0178 92 | :1005B0002E5431092231310560F582E080936800C4 93 | :1005C0001092630010926400109265001092660011 94 | :1005D0008FEF80936200109267001FBC88B7846021 95 | :1005E00088BFFF91EF91BF91AF919F918F917F91C4 96 | :1005F0006F915F914F913F912F911F910F910F90AC 97 | :100600000FBE0F901F9018952EE430E0281B390B79 98 | :10061000D1CF8D38910518F08C58910906C02CE87F 99 | :1006200030E0A901481B590BCA014297B0F681E09E 100 | :10063000C5CF429720F08091620087FF21C0809152 101 | :1006400062008F5F8093620080916200803229F6A1 102 | :100650004091630050916400609165007091660064 103 | :100660000091630010916400209165003091660054 104 | :1006700080916800AB0166277727B80100DEADCF17 105 | :100680002091620040916300509164006091650088 106 | :100690007091660081E090E0A0E0B0E004C0880FB7 107 | :1006A000991FAA1FBB1F2A95D2F7842B952BA62B27 108 | :1006B000B72B8093630090936400A0936500B09380 109 | :1006C0006600BDCF1F920F920FB60F9211240F93A9 110 | :1006D0001F932F933F934F935F936F937F938F93CA 111 | :1006E0009F93AF93BF93EF93FF930091630010919B 112 | :1006F00064002091650030916600809163009091C4 113 | :100700006400A0916500B0916600E0916200A801CC 114 | :100710004F775527BC01CD0127E0969587957795B2 115 | :1007200067952A95D1F78E2FAADD89B78B7F89BF70 116 | :100730008AEF8FBDFF91EF91BF91AF919F918F9104 117 | :100740007F916F915F914F913F912F911F910F91E9 118 | :100750000F900FBE0F901F90189587B3897F87BBAE 119 | :1007600088B3897F88BBBADC80E0ABDCC6E8D0E028 120 | :10077000FE018491A6DC2196F0E0CF38DF07C1F7B7 121 | :100780009CDC64DD87E080BF88E085BB80E28BBFB6 122 | :10079000789441E062E181E990E045DDFFCF991B6B 123 | :1007A00079E004C0991F961708F0961B881F7A9568 124 | :0A07B000C9F780950895F894FFCF73 125 | :00000001FF 126 | --------------------------------------------------------------------------------