]>
Commit | Line | Data |
---|---|---|
ec1bef8e | 1 | /* |
2 | * ox - infrared to usb keyboard/mouse adapter | |
3 | * | |
4 | * by Alexander Neumann <alexander@lochraster.org> | |
5 | * | |
6 | * inspired by InfraHID by Alex Badea, | |
7 | * see http://vamposdecampos.googlepages.com/infrahid.html | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or modify | |
10 | * it under the terms of the GNU General Public License version 2 as | |
11 | * published by the Free Software Foundation. | |
12 | * | |
13 | * This program is distributed in the hope that it will be useful, | |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | * GNU General Public License for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU General Public License | |
19 | * along with this program; if not, write to the Free Software | |
20 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
21 | * | |
22 | * For more information on the GPL, please go to: | |
23 | * http://www.gnu.org/copyleft/gpl.html | |
24 | */ | |
25 | ||
26 | #include <avr/io.h> | |
27 | #include <avr/interrupt.h> | |
28 | #include <avr/pgmspace.h> | |
29 | #include <stdbool.h> | |
30 | #include <util/crc16.h> | |
31 | #include "config.h" | |
32 | #include "globals.h" | |
33 | #include "../common/common.h" | |
34 | #include "usb.h" | |
35 | #include "ir.h" | |
36 | #include "ir-cluster.h" | |
37 | #include "ui.h" | |
38 | ||
39 | /* macro magic */ | |
40 | #define IR_DDR _DDRPORT(IR_PORTNAME) | |
41 | #define IR_PORT _OUTPORT(IR_PORTNAME) | |
42 | ||
43 | #if IR_INTNUM < 8 | |
44 | # define IR_BANK 0 | |
45 | # define IR_VECT PCINT0_vect | |
46 | #elif IR_INTNUM < 16 | |
47 | # define IR_BANK 1 | |
48 | # define IR_VECT PCINT1_vect | |
49 | #else | |
50 | # define IR_BANK 2 | |
51 | # define IR_VECT PCINT2_vect | |
52 | #endif | |
53 | ||
54 | #define IR_INT (IR_INTNUM - IR_BANK * 8) | |
55 | #define IR_PCIE _PCIE(IR_BANK) | |
56 | #define IR_PCIF _PCIF(IR_BANK) | |
57 | #define IR_PCMSK _PCMSK(IR_BANK) | |
58 | ||
59 | /* 8 should be enough for everybody! */ | |
60 | #define MAX_CLUSTERS 8 | |
61 | ||
62 | /* internal state machine */ | |
63 | static enum { | |
64 | READY = 0, | |
65 | LISTEN = 1, | |
66 | DONE = 2, | |
67 | } state; | |
68 | ||
69 | volatile struct ir_global_t ir_global; | |
70 | ||
71 | void ir_init(void) | |
72 | { | |
73 | /* initialize input pin (with pullup) */ | |
74 | IR_DDR &= ~_BV(IR_PIN); | |
75 | IR_PORT |= _BV(IR_PIN); | |
76 | ||
77 | /* configure timer1 with prescaler 64 and CTC for measuring ir timings */ | |
78 | TCCR1B = _BV(CS11) | _BV(CS10) | _BV(WGM12); | |
79 | /* configure timer action after 20ms: 20mhz/64/50 */ | |
80 | OCR1A = F_CPU/50/64; | |
81 | } | |
82 | ||
83 | void ir_set_mode(enum ir_mode_t mode) | |
84 | { | |
85 | if (ir_global.mode != IR_DISABLED && mode == IR_DISABLED) { | |
86 | /* disable pin change interrupt */ | |
87 | IR_PCMSK &= ~_BV(IR_INT); | |
88 | PCICR &= ~_BV(IR_PCIE); | |
89 | ||
90 | /* clear interrupt flags */ | |
91 | TIFR1 = TIFR1; | |
92 | } else if (ir_global.mode == IR_DISABLED && mode != IR_DISABLED) { | |
93 | /* clear interrupt flags */ | |
94 | TIFR1 = TIFR1; | |
95 | ||
96 | /* reset variables */ | |
97 | ir_global.pos = 0; | |
98 | state = READY; | |
99 | ||
100 | /* enable pin change interrupt */ | |
101 | IR_PCMSK |= _BV(IR_INT); | |
102 | PCICR |= _BV(IR_PCIE); | |
103 | } | |
104 | ||
105 | ir_global.mode = mode; | |
106 | ||
107 | if (mode == IR_DECODE || mode == IR_RAW) | |
108 | ui_blink(0, 0x5); | |
109 | } | |
110 | ||
111 | ISR(IR_VECT, ISR_NOBLOCK) | |
112 | { | |
113 | /* store code */ | |
114 | uint16_t value = TCNT1; | |
115 | TCNT1 = 0; | |
116 | ||
117 | /* do not store the first 'off' timing value */ | |
118 | if (state == READY) | |
119 | state = LISTEN; | |
120 | else if (state == LISTEN) { | |
121 | ir_global.time[ir_global.pos++] = value; | |
122 | ||
123 | /* check if we reached the end of the array */ | |
124 | if (ir_global.pos == MAX_CODE_LENGTH) | |
125 | state = DONE; | |
126 | } | |
127 | } | |
128 | ||
129 | void ir_poll(void) | |
130 | { | |
131 | if (ir_global.mode == IR_DISABLED) | |
132 | return; | |
133 | ||
134 | /* check for timeout */ | |
135 | if (TIFR1 & _BV(OCF1A)) { | |
136 | /* clear interrupt flag */ | |
137 | TIFR1 = _BV(OCF1A); | |
138 | ||
139 | /* if ir has been detected, process code, else return to listening */ | |
140 | if (ir_global.pos > 1) { | |
141 | state = DONE; | |
142 | ||
143 | /* set last timing to zero */ | |
144 | if (ir_global.pos % 2 == 0) | |
145 | ir_global.time[ir_global.pos-1] = 0; | |
146 | else | |
147 | ir_global.time[ir_global.pos++] = 0; | |
148 | } else | |
149 | state = READY; | |
150 | } | |
151 | ||
152 | /* if code is complete, process! */ | |
153 | if (state == DONE) { | |
154 | /* compute clusters */ | |
155 | if (ir_global.mode == IR_RECEIVE || ir_global.mode == IR_DECODE) { | |
156 | uint16_t cluster_on[MAX_CLUSTERS]; | |
157 | uint16_t cluster_off[MAX_CLUSTERS]; | |
158 | ||
159 | uint8_t con = ir_cluster(&ir_global.time[0], ir_global.pos/2, &cluster_on[0], MAX_CLUSTERS); | |
160 | uint8_t coff = ir_cluster(&ir_global.time[1], ir_global.pos/2-1, &cluster_off[0], MAX_CLUSTERS); | |
161 | ||
162 | for (uint8_t i = 0; i < ir_global.pos/2; i++) { | |
163 | uint8_t data_on = ir_min_cluster(ir_global.time[2*i], cluster_on, con); | |
164 | uint8_t data_off = ir_min_cluster(ir_global.time[2*i+1], cluster_off, coff); | |
165 | ||
166 | ir_global.time[i] = data_on | (data_off << 8); | |
167 | } | |
168 | ||
169 | ir_global.pos /= 2; | |
170 | } | |
171 | ||
172 | if (ir_global.mode == IR_RECEIVE) { | |
173 | uint16_t crc = 0; | |
174 | for (uint8_t i = 0; i < ir_global.pos; i++) { | |
175 | crc = _crc16_update(crc, LO8(ir_global.time[i])); | |
176 | crc = _crc16_update(crc, HI8(ir_global.time[i])); | |
177 | } | |
178 | ||
179 | /* remember code and length */ | |
180 | ir_global.last = crc; | |
181 | ir_global.length = ir_global.pos/2; | |
182 | ||
183 | /* | |
184 | * insert code evaluation here... | |
185 | */ | |
186 | ||
187 | ui_blink(0x1, 0); | |
188 | ||
189 | ir_global.pos = 0; | |
190 | state = READY; | |
191 | } else { | |
192 | ui_blink(0, 0x1); | |
193 | ir_set_mode(IR_DISABLED); | |
194 | } | |
195 | } | |
196 | } |