| 1 | /* $Id: ircontrol.c,v 1.5 2010/06/30 19:38:28 simimeie Exp $ |
| 2 | * Functions for the infrared receiver |
| 3 | * |
| 4 | * The infrared receiver is connected to PB0 / PCINT0. |
| 5 | */ |
| 6 | |
| 7 | #include <avr/io.h> |
| 8 | #include <avr/interrupt.h> |
| 9 | #include "ircontrol.h" |
| 10 | #include "timers.h" |
| 11 | #include "console.h" |
| 12 | |
| 13 | /* NOTE1: Note that the signal we get from the sensor is inverted. If we |
| 14 | * read a zero, it means there was infrared on, if we read a one, infrared |
| 15 | * is off. |
| 16 | * NOTE2: Only NEC is implemented here right now, because MY remote uses |
| 17 | * that protocol. However, there may be references to RC5 because I had |
| 18 | * already thought about that and just left them for possible future use. */ |
| 19 | |
| 20 | /* For RC5, one bit length is 1778 us, when it toggles we see half |
| 21 | * of it, i.e. 889 us. |
| 22 | * that equals around 7100 cpu cycles at 8 MHz. */ |
| 23 | #define RC5HALFLENINCYCLES ((CPUFREQ * 889UL) / 1000000UL) |
| 24 | |
| 25 | /* For NEC, we start with a 9000 us pulse, then 4500 us silence. |
| 26 | * Then the bits follow: |
| 27 | * a 1 is a 560 us pulse followed by 1690 us of silence (=2250 us total). |
| 28 | * a 0 is a 560 us pulse followed by 560 us of silence (=1120 us total). |
| 29 | * These values equal the following cpu cycle counts: |
| 30 | * 9000 us = 72000 cc, 4500 us = 36000 cc, 560 us = 4480, 1690 us = 13520 cc |
| 31 | * When the key stays pressed, it is not resubmitted, but instead a special |
| 32 | * "repeat" code is sent. That is: 9000 us pulse, 2250 us silence, 560 us |
| 33 | * pulse. |
| 34 | */ |
| 35 | #define NECSTARTLEN1 ((CPUFREQ * 9UL) / 1000UL) |
| 36 | #define NECSTARTLEN2 ((CPUFREQ * 45UL) / 10000UL) |
| 37 | #define NECPULSELEN ((CPUFREQ * 56UL) / 100000UL) |
| 38 | #define NECZEROLEN ((CPUFREQ * 112UL) / 100000UL) |
| 39 | #define NECONELEN ((CPUFREQ * 225UL) / 100000UL) |
| 40 | #define NECREPEATLEN ((CPUFREQ * 225UL) / 100000UL) |
| 41 | |
| 42 | /* the NEC code contains 4 bytes, sent with LSB first: |
| 43 | * 0+1 are either the "extended address" or "address and inverted address". |
| 44 | * 2 is the command code |
| 45 | * 3 is the inverted command code |
| 46 | */ |
| 47 | |
| 48 | static struct timestamp last0irqts; |
| 49 | static struct timestamp last1irqts; |
| 50 | static uint8_t lastpin = 0xff; |
| 51 | static uint8_t codebytes[4]; |
| 52 | static uint8_t curcodebit = 0xff; |
| 53 | static uint8_t lastcommand = 0xff; |
| 54 | static uint8_t repeatcommand = 0xff; |
| 55 | static uint16_t repeatticks = 0; |
| 56 | /* Repeat after this many ticks (70 = 0.5s) */ |
| 57 | #define REPEATAFTERTICKS 100 |
| 58 | |
| 59 | /* some example codes |
| 60 | root@moodlight# !NSB! 11111111 00001000 11011111 00100000 (r) |
| 61 | root@moodlight# !NSB! 11111111 00001000 01011111 10100000 (g) |
| 62 | root@moodlight# !NSB! 11111111 00001000 10011111 01100000 (b) |
| 63 | root@moodlight# !NSB! 11111111 00001000 00011111 11100000 (w) |
| 64 | */ |
| 65 | |
| 66 | ISR(PCINT0_vect) { |
| 67 | uint8_t v; |
| 68 | struct timestamp curirqts; |
| 69 | uint32_t ts1diff; /* distance from last 1 */ |
| 70 | uint32_t ts0diff; /* distance from last 0 */ |
| 71 | |
| 72 | v = PINB & _BV(PB0); |
| 73 | if (v == lastpin) { /* No change visible - spurious interrupt */ |
| 74 | return; |
| 75 | } |
| 76 | curirqts = gettimestamp_noirq(); |
| 77 | ts1diff = ((uint32_t)curirqts.ticks << 16) + curirqts.partticks; |
| 78 | ts0diff = ts1diff; |
| 79 | ts1diff -= ((uint32_t)last1irqts.ticks << 16) + last1irqts.partticks; |
| 80 | ts0diff -= ((uint32_t)last0irqts.ticks << 16) + last0irqts.partticks; |
| 81 | if (v) { /* Infrared just went away! */ |
| 82 | if ((ts1diff >= (( 8 * NECSTARTLEN1) / 10)) |
| 83 | && (ts1diff <= ((12 * NECSTARTLEN1) / 10))) { |
| 84 | /* NEC start bit */ |
| 85 | /* console_printpgm_P(PSTR("!NSB!")); */ |
| 86 | curcodebit = 0xfe; /* Wait for second part of start sequence */ |
| 87 | } else { |
| 88 | if (curcodebit <= 32) { /* We're in a decoding attempt, so */ |
| 89 | /* Check pulse length */ |
| 90 | if ((ts1diff < (( 8 * NECPULSELEN) / 10)) |
| 91 | || (ts1diff > ((12 * NECPULSELEN) / 10))) { |
| 92 | /* WRONG */ |
| 93 | curcodebit = 0xff; |
| 94 | } |
| 95 | } |
| 96 | if (curcodebit == 32) { |
| 97 | if (codebytes[2] != (codebytes[3] ^ 0xff)) { |
| 98 | console_printpgm_P(PSTR("!CRC!")); |
| 99 | } else { |
| 100 | /* Successful decode! */ |
| 101 | lastcommand = codebytes[2]; |
| 102 | repeatcommand = codebytes[2]; |
| 103 | repeatticks = curirqts.ticks; |
| 104 | console_printpgm_P(PSTR(" DEC>")); |
| 105 | console_printhex8(codebytes[0]); |
| 106 | console_printhex8(codebytes[1]); |
| 107 | console_printhex8(codebytes[2]); |
| 108 | console_printhex8(codebytes[3]); |
| 109 | } |
| 110 | } |
| 111 | } |
| 112 | last0irqts = curirqts; |
| 113 | } else { /* Infrared went on */ |
| 114 | if ((ts1diff >= (( 8 * NECZEROLEN) / 10)) |
| 115 | && (ts1diff <= ((12 * NECZEROLEN) / 10))) { |
| 116 | /* console_printpgm_P(PSTR("0")); */ |
| 117 | if (curcodebit < 32) { |
| 118 | curcodebit++; |
| 119 | } |
| 120 | } else if ((ts1diff >= (( 8 * NECONELEN) / 10)) |
| 121 | && (ts1diff <= ((12 * NECONELEN) / 10))) { |
| 122 | /* console_printpgm_P(PSTR("1")); */ |
| 123 | if (curcodebit < 32) { |
| 124 | codebytes[curcodebit >> 3] |= (1 << (curcodebit & 0x07)); |
| 125 | curcodebit++; |
| 126 | } else { |
| 127 | curcodebit = 0xff; |
| 128 | } |
| 129 | } else if ((ts0diff >= (( 8 * NECSTARTLEN2) / 10)) |
| 130 | && (ts0diff <= ((12 * NECSTARTLEN2) / 10))) { |
| 131 | if (curcodebit == 0xfe) { /* voila, correct start sequence */ |
| 132 | curcodebit = 0; |
| 133 | codebytes[0] = codebytes[1] = codebytes[2] = codebytes[3] = 0; |
| 134 | } |
| 135 | } else if ((ts0diff >= (( 8 * NECREPEATLEN) / 10)) |
| 136 | && (ts0diff <= ((12 * NECREPEATLEN) / 10))) { |
| 137 | if (curcodebit == 0xfe) { |
| 138 | console_printpgm_P(PSTR(".REP.")); |
| 139 | if ((curirqts.ticks - repeatticks) > REPEATAFTERTICKS) { |
| 140 | if ((repeatcommand == 0x00) || (repeatcommand == 0x01)) { |
| 141 | /* Only the up/down arrows are allowed to be repeated */ |
| 142 | lastcommand = repeatcommand; |
| 143 | } |
| 144 | } |
| 145 | } |
| 146 | } |
| 147 | last1irqts = curirqts; |
| 148 | } |
| 149 | #if 0 |
| 150 | console_printpgm_P(PSTR("!")); |
| 151 | console_printhex8(tsdiff >> 24); |
| 152 | console_printhex8(tsdiff >> 16); |
| 153 | console_printhex8(tsdiff >> 8); |
| 154 | console_printhex8(tsdiff >> 0); |
| 155 | console_printpgm_P(PSTR("!")); |
| 156 | #endif |
| 157 | #if 0 |
| 158 | if (tsdiff > ((24 * HALFRC5LENINCYCLES) / 10)) { |
| 159 | /* Start of new transmission */ |
| 160 | console_printpgm_P(PSTR("!1[")); |
| 161 | console_printhex8(v); |
| 162 | lastbit = 1; |
| 163 | } else if (tsdiff > ((15 * HALFRC5LENINCYCLES) / 10)) { |
| 164 | /* Different bit than last time */ |
| 165 | lastbit = !lastbit; |
| 166 | if (lastbit) { |
| 167 | console_printpgm_P(PSTR("1")); |
| 168 | } else { |
| 169 | console_printpgm_P(PSTR("0")); |
| 170 | } |
| 171 | } else if ((tsdiff < ((15 * HALFRC5LENINCYCLES) / 10)) |
| 172 | && (tsdiff > (( 5 * HALFRC5LENINCYCLES) / 10))) { |
| 173 | /* Same bit as last time */ |
| 174 | if (lastbit) { |
| 175 | console_printpgm_P(PSTR("1")); |
| 176 | } else { |
| 177 | console_printpgm_P(PSTR("0")); |
| 178 | } |
| 179 | } |
| 180 | #endif |
| 181 | lastpin = v; |
| 182 | } |
| 183 | |
| 184 | void ircontrol_init(void) |
| 185 | { |
| 186 | /* Activate pullup */ |
| 187 | PORTB |= _BV(PB0); |
| 188 | /* enable PCINT0 */ |
| 189 | PCICR |= _BV(PCIE0); |
| 190 | /* Enable pin change interrupt 0 (=PB0) in pcint0 */ |
| 191 | PCMSK0 |= _BV(PCINT0); |
| 192 | } |
| 193 | |
| 194 | uint8_t ircontrol_getlastcommand(void) |
| 195 | { |
| 196 | uint8_t res; |
| 197 | res = lastcommand; |
| 198 | lastcommand = 0xff; |
| 199 | return res; |
| 200 | } |