-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathir-rx.c
298 lines (239 loc) · 6.73 KB
/
ir-rx.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
/* Copyright 2008 Stephen English, Jeffrey Gough, Alexis Johnson,
Robert Spanton and Joanna A. Sun.
This file is part of the Formica robot firmware.
The Formica robot firmware is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
The Formica robot firmware is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with the Formica robot firmware.
If not, see <http://www.gnu.org/licenses/>. */
#include "ir-rx.h"
#include "ir.h"
#include "ir-bias.h"
#include "device.h"
#include "freq.h"
#include "net-rx.h"
#include "types.h"
#include <signal.h>
#include "smbus_pec.h"
/* Number of samples to average out */
#define AVERAGE 2
/* The stability threshold for when to accept a new frequency */
#define STABLE_THRESH (110 * AVERAGE)
#define timera_en() do { TACTL |= MC_CONT; } while(0)
static uint8_t cb_pos = 0;
static uint8_t curbyte = 0;
/* The last symbol received */
static uint8_t last_sym = INV_SYM;
#define DATA_LEN 32
static uint8_t data[DATA_LEN];
static uint8_t d_pos = 0;
/* Debug flag pins */
/* #define debug_symbol_lock() do { P1OUT |= 1; } while (0) */
/* #define debug_symbol_nolock() do { P1OUT &= ~1; } while (0) */
/* #define debug_flag_flip() do { P1OUT ^= 2; } while (0) */
#define debug_symbol_lock() do { ; } while (0)
#define debug_symbol_nolock() do { ; } while (0)
#define debug_flag_flip() do { ; } while (0)
/* Resets/initialises the bit decoder.
For calling on initialisation or when the signal is lost. */
static void decoder_reset( void );
/* Decode the new data.
'period' is period that's just been received. */
static inline void decoder_newdata( uint16_t period );
void ir_receive_init( void )
{
/*** Configure Timer A ***/
TACTL = TASSEL_SMCLK /* SMCLK clock source */
/* No clock divider */
| MC_STOP /* Timer stopped for now */
/* Don't reset the timer */
| TAIE /* Interrupt enabled */;
/* Clear the timer */
TAR = 0;
/* Set up CCR1 to trigger off the comparator */
TACCTL1 = CM_POS /* Trigger on positive edges */
| CCIS_0 /* CCI0A trigger source (COMP) */
| SCS /* Synchronize with timer clock */
| CAP /* Capture mode */
| CCIE; /* Interrupt enabled */
/* Set P1.2 to be the CCR1 input CCI0A */
/* Page 57 of the MSP430F2234 datasheet */
P1DIR &= ~0x04;
P1SEL |= 0x04;
/* Make the PD1,2,3 signals low outputs, so they are clamped when */
/* their analogue channels are deselected. This prevents photocurrent */
/* from flowing into the battery measurement input. Insane! */
P2DIR |= 0x0f;
P2OUT &= 0xf0;
/*** Bias resistor config ***/
ir_bias_comms();
decoder_reset();
/* Start the timer */
timera_en();
}
interrupt (TIMERA1_VECTOR) timer_a_isr(void)
{
uint16_t taiv_l = TAIV;
/* Number of times the timer has wrapped since the last edge */
static uint8_t nwraps = 0;
switch( taiv_l )
{
/* CCR1: */
case 0x02: {
/* Previous periods */
static int16_t prev[AVERAGE];
/* Position in the prev buffer */
static uint8_t pos = 0;
/* The last recorded period */
static uint16_t last_period = 0;
uint16_t reading = TACCR1;
/* The last TACCR1 reading: */
static uint16_t last_taccr1 = 0;
uint32_t sum = 0;
uint8_t i;
/* The timer may have wrapped around since the last event */
if( nwraps == 0 )
reading -= last_taccr1;
else if( nwraps == 1 )
/* The timer wrapper - compensate */
reading = ((uint32_t)(reading) + 0xffff) - last_taccr1;
last_taccr1 = TACCR1;
/* Currently triggering on positive edges: */
reading >>= 1;
/* Reset the receiver if the recorded period was too long */
if( nwraps > 1 || reading > (MAX_PERIOD + RANGE) ) {
/* Lost the signal */
decoder_reset();
last_period = 0;
}
/*** Determine whether we've reached a stable period ***/
/* Store the difference between this period and the last recorded */
prev[pos] = reading - last_period;
pos = (pos == AVERAGE-1)?0:pos+1;
/* Sum the absolute values of the last AVERAGE derivatives */
for(i=0; i<AVERAGE; i++)
if( prev[i] < 0 )
sum += (-prev[i]);
else
sum += prev[i];
/* Only accept this frequency if it's stable */
if( sum < STABLE_THRESH ) {
/* Got a stable frequency */
/* It might be the same as the last, but we can't tell until it's been
decoded as a symbol */
decoder_newdata( reading );
debug_symbol_lock();
}
else {
debug_symbol_nolock();
}
last_period = reading;
nwraps = 0;
break;
}
/* Timer overflow */
case 0x0A: {
nwraps++;
break;
}
default:
/* Should never get here. */
nop();
}
}
interrupt (TIMERA0_VECTOR) timer_a1_isr(void)
{
}
static void decoder_reset( void )
{
cb_pos = 0;
last_sym = INV_SYM;
}
static inline void decoder_newdata( uint16_t period )
{
uint8_t sym;
/* Check that the frequency reaches the minimum */
if( period < (period_lut[0] - RANGE) ) {
decoder_reset();
return;
}
for( sym=0;
sym < NFREQ;
sym++ )
if( period < (period_lut[sym] + RANGE) )
break;
if( sym == NFREQ ) {
/* Invalid frequency - reset the decoder */
decoder_reset();
return;
}
/* Ignore repeated symbols */
if( sym == last_sym )
return;
/*** Decode the symbol ***/
if( sym == 0 ) {
cb_pos = 0;
curbyte = 0;
last_sym = 0;
return;
}
/* Remove the step over the last symbol */
if( sym > last_sym ) {
last_sym = sym;
sym--;
} else
last_sym = sym;
/* Start of the byte? */
sym--;
curbyte |= sym << (cb_pos * NBITS);
cb_pos++;
if( cb_pos == SYMBOLS_PER_BYTE ) {
static uint8_t checksum;
static bool escaped = FALSE;
/* We now have a full byte */
cb_pos = 0;
if( curbyte == 0x7e ) {
/* Start of frame */
d_pos = 0;
checksum = 0;
data[0] = 0x7e;
escaped = FALSE;
} else if( data[0] == 0x7e ) {
if( curbyte == 0x7d ) {
escaped = TRUE;
return;
}
else if( escaped ) {
curbyte ^= 0x20;
escaped = FALSE;
}
data[d_pos] = curbyte;
/* data[1] is the length if we've got that far */
if( d_pos > 1 && d_pos == (data[1] + 2) ) {
/* Frame reception complete -- check the checksum */
if( checksum == curbyte )
net_rx_proc_incoming( data+2, data[1] );
}
else
checksum_add( checksum, curbyte );
}
if( data[0] == 0x7e )
d_pos = (d_pos==(DATA_LEN-1))?0:(d_pos+1);
}
}
void ir_receive_en( void )
{
/* Enable the capture interrupt */
TACCTL1 |= CCIE;
}
void ir_receive_dis( void )
{
/* Disable the capture interrupt */
TACCTL1 &= ~CCIE;
}