forked from semerad/gt3b
-
Notifications
You must be signed in to change notification settings - Fork 0
/
input.c
437 lines (362 loc) · 11.4 KB
/
input.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
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
/**
* @copyright
* Copyright (C) 2011 Pavel Semerad
*
* @file
* $RCSfile: $
*
* @purpose
* gt3b alternative firmware - read keys and ADC values
*
* @cfg_management
* $Source: $
* $Author: $
* $Date: $
*
* @comment
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "input.h"
#include "menu.h"
#include "config.h"
#include "calc.h"
#include "lcd.h"
#include "timer.h"
// autorepeat/long press times in 5ms steps
#define BTN_AUTOREPEAT_DELAY (500 / 5)
#define BTN_AUTOREPEAT_RATE (70 / 5)
#define ENCODER_FAST_THRESHOLD 10
// INPUT task, called every 5ms
TASK(INPUT, 40);
static void input_loop(void);
void input_init(void) {
// ADC inputs
IO_IF(B, 0); // steering ADC
IO_IF(B, 1); // throttle ADC
IO_IF(B, 2); // CH3 button
IO_IF(B, 3); // battery voltage ADC
// ADC init
BSET(CLK_PCKENR2, 3); // enable clock to ADC
ADC_TDRL = 0b00001111; // schmitt-trigger disable 0-3
ADC_CSR = 0b00000011; // end channel 3, no interrupts
ADC_CR3 = 0b00000000; // no-DBUF
ADC_CR2 = 0b00001010; // right align, SCAN
ADC_CR1 = 0b01110001; // clock/18, no-CONT, enable
// buttons - rows are inputs with pullups and are switched to output
// only when reading that row
IO_IP(B, 4); // button row B4
IO_IP(B, 5); // button row B5
IO_IP(C, 4); // button row C4
IO_IP(D, 3); // button row D3
IO_IP(C, 5); // button col C5
IO_IP(C, 6); // button col C6
IO_IP(C, 7); // button col C7
// and set default row values to 1
BSET(PB_ODR, 4);
BSET(PB_ODR, 5);
BSET(PC_ODR, 4);
BSET(PD_ODR, 3);
// rotate encoder - connected to Timer1, using encoder mode
IO_IF(C, 1); // pin TIM1_CH1
IO_IF(C, 2); // pin TIM1_CH2
BSET(CLK_PCKENR1, 7); // enable master clock to TMR1
TIM1_SMCR = 0x01; // encoder mode 1, count on TI2 edges
TIM1_CCMR1 = 0x01; // CC1 is input
TIM1_CCMR2 = 0x01; // CC2 is input
TIM1_ARRH = 0; // max value
TIM1_ARRL = 0xff; // max value
TIM1_CNTRH = 0; // start value
TIM1_CNTRL = 0; // start value
TIM1_IER = 0; // no interrupts
TIM1_CR2 = 0;
TIM1_CR1 = 0x01; // only counter enable
// init task
build(INPUT);
activate(INPUT, input_loop);
}
// read keys, change value only when last 3 reads are the same
// internal state variables
@near static u16 buttons1, buttons2, buttons3; // last 3 reads
u16 buttons_state; // actual combined last buttons state
static u8 buttons_autorepeat; // autorepeat enable for TRIMs and D/R
@near static u8 buttons_timer[12]; // autorepeat/long press buttons timers
static u8 encoder_timer; // for rotate encoder slow/fast
// variables representing pressed buttons
u16 buttons;
u16 buttons_long; // >1s press
// variables for ADC values
u16 adc_all_last[3], adc_battery_last;
@near u16 adc_battery;
@near u32 adc_battery_filt;
// reset pressed button
void button_reset(u16 btn) {
buttons &= ~btn;
buttons_long &= ~btn;
}
void button_reset_nolong(u16 btn) {
buttons &= ~btn;
}
/**
@brief
set autorepeat
@detailed
FIXME
@param[in] btn
FIXME
*/
void button_autorepeat(u8 btn) {
buttons_autorepeat = btn;
}
// read key matrix (11 keys)
#define button_row(port, bit, c5, c6, c7) \
BSET(P ## port ## _DDR, bit); \
BSET(P ## port ## _CR2, bit); \
BRES(P ## port ## _ODR, bit); \
if (!BCHK(PC_IDR, 5)) btn |= c5; \
if (!BCHK(PC_IDR, 6)) btn |= c6; \
if (c7 && !BCHK(PC_IDR, 7)) btn |= c7; \
BSET(P ## port ## _ODR, bit); \
BRES(P ## port ## _CR2, bit); \
BRES(P ## port ## _DDR, bit)
/** @brief
* FIXME
* @detailed
* FIXME
* @returns
* @retval
* FIXME
*/
static u16 read_key_matrix(void) {
u16 btn = 0;
button_row(B, 4, BTN_TRIM_LEFT, BTN_TRIM_CH3_L, BTN_END);
button_row(B, 5, BTN_TRIM_RIGHT, BTN_TRIM_CH3_R, BTN_BACK);
button_row(D, 3, BTN_TRIM_FWD, BTN_DR_R, 0);
button_row(C, 4, BTN_TRIM_BCK, BTN_DR_L, BTN_ENTER);
return btn;
}
// read all keys
static void read_keys(void) {
u16 buttons_state_last = buttons_state;
u16 buttons_last = buttons;
u16 bit;
u8 i, lbs, bs;
// rotate last buttons
buttons3 = buttons2;
buttons2 = buttons1;
// read actual keys status
buttons1 = read_key_matrix();
// add CH3 button, middle state will be only in buttons_state,
// not in buttons
// do only when CH3 is button, not potentiometer
if (!cg.ch3_pot) {
if (adc_ch3_last <= BTN_CH3_LOW) buttons1 |= BTN_CH3;
else if (adc_ch3_last < BTN_CH3_HIGH) buttons1 |= BTN_CH3_MID;
}
// combine last 3 readed buttons
buttons_state |= buttons1 & buttons2 & buttons3;
buttons_state &= buttons1 | buttons2 | buttons3;
// do autorepeat/long_press only when some keys were pressed
if (buttons_state_last || buttons_state) {
// key pressed or released, activate backlight
backlight_on();
// handle autorepeat for first 8 keys (TRIMs and D/R)
if (buttons_autorepeat) {
for (i = 0, bit = 1; i < 8; i++, bit <<= 1) {
if (!(bit & buttons_autorepeat)) continue; // not autorepeated
lbs = (u8)(buttons_state_last & bit ? 1 : 0);
bs = (u8)(buttons_state & bit ? 1 : 0);
if (!lbs) {
// last not pressed
if (bs) {
// now pressed, set it pressed and set autorepeat delay
buttons |= bit;
buttons_timer[i] = BTN_AUTOREPEAT_DELAY;
}
// do nothing for now not pressed
}
else {
// last was pressed
if (bs) {
// now pressed
if (--buttons_timer[i]) continue; // not expired yet
// timer expired, set it pressed and set autorepeat rate
buttons |= bit;
buttons_timer[i] = BTN_AUTOREPEAT_RATE;
}
// do nothing for now not pressed
}
}
}
// handle long presses for first 12 keys
// exclude keys with autorepeat ON
for (i = 0, bit = 1; i < 12; i++, bit <<= 1) {
if (bit & buttons_autorepeat) continue; // handled in autorepeat
lbs = (u8)(buttons_state_last & bit ? 1 : 0);
bs = (u8)(buttons_state & bit ? 1 : 0);
if (!lbs) {
// last not pressed
if (bs) {
// now pressed, set long press delay
buttons_timer[i] = cg.long_press_delay;
}
// do nothing for now not pressed
}
else {
// last was pressed
if (bs) {
// now pressed, check long press delay
// if already long or not long enought, skip
if (!buttons_timer[i] || --buttons_timer[i]) continue;
// set as pressed and long pressed
buttons |= bit;
buttons_long |= bit;
}
else {
// now not pressed, set as pressed when no long press was applied
if (!buttons_timer[i]) continue; // was long before
buttons |= bit;
}
}
}
}
// add rotate encoder
if (encoder_timer) encoder_timer--;
if (TIM1_CNTRL) {
// encoder changed
u16 btn = 0;
if ((s8)TIM1_CNTRL > cg.encoder_2detents) {
// left
if (cg.rotate_reverse) btn = BTN_ROT_R;
else btn = BTN_ROT_L;
}
else if ((s8)TIM1_CNTRL < -cg.encoder_2detents) {
// right
if (cg.rotate_reverse) btn = BTN_ROT_L;
else btn = BTN_ROT_R;
}
if (btn) {
// only when enought rotate was applied
buttons |= btn;
if (encoder_timer) buttons_long |= btn;
// set it back to default value
TIM1_CNTRL = 0;
// init timer
encoder_timer = ENCODER_FAST_THRESHOLD;
backlight_on();
}
}
// if some of the keys changed, wakeup MENU task and reset inactivity timer
if (buttons_last != buttons || buttons_state_last != buttons_state) {
awake(MENU);
reset_inactivity_timer();
}
}
// ADC buffers, last 4 values for each channel
#define ADC_BUFFERS 4
@near u16 adc_buffer0[ADC_BUFFERS];
@near u16 adc_buffer1[ADC_BUFFERS];
@near u16 adc_buffer2[ADC_BUFFERS];
u16 adc_buffer_pos;
// read ADC values
static void read_ADC(void) {
READ_ADC();
}
// average battery voltage and check battery low
static void update_battery(void) {
// ignore very low, which means that it is supplied from SWIM connector
adc_battery_filt = adc_battery_filt * (ADC_BAT_FILT - 1); // splitted - compiler hack
adc_battery_filt = (adc_battery_filt + (ADC_BAT_FILT / 2)) / ADC_BAT_FILT
+ adc_battery_last;
adc_battery = (u16)((adc_battery_filt + (ADC_BAT_FILT / 2)) / ADC_BAT_FILT);
// start checking battery after 5s from power on
if (time_sec >= 5) {
// wakeup task only when something changed
if (adc_battery > 50 && adc_battery < battery_low_raw) {
// bat low
if (!menu_battery_low) {
menu_battery_low = 1;
awake(MENU);
}
}
else {
// bat OK, but apply some hysteresis to not switch quickly ON/OFF
if (menu_battery_low && adc_battery > battery_low_raw + 100) {
menu_battery_low = 0;
awake(MENU);
}
}
}
}
// reset inactivity timer when some steering or throttle applied
static @near u16 last_steering = 30000; // more that it can be
static @near u16 last_throttle = 30000; // more that it can be
#define INACTIVITY_DEAD 20
static void check_inactivity(void) {
if (adc_steering_last < (last_steering - INACTIVITY_DEAD) ||
adc_steering_last > (last_steering + INACTIVITY_DEAD)) {
reset_inactivity_timer();
last_steering = adc_steering_last;
last_throttle = adc_throttle_last;
}
else if (adc_throttle_last < (last_throttle - INACTIVITY_DEAD) ||
adc_throttle_last > (last_throttle + INACTIVITY_DEAD)) {
reset_inactivity_timer();
last_steering = adc_steering_last;
last_throttle = adc_throttle_last;
}
}
// check throttle trigger and start timer
#define THROTTLE_TRIGGER_START 30
static void check_throttle_trigger(void) {
u8 i, tbit, type;
// nothing when throttle is not little forward
if (ADC_OVS(throttle) >= (cg.calib_throttle_mid - THROTTLE_TRIGGER_START)) return;
for (i =0, tbit = 1; i < TIMER_NUM; i++, tbit <<= 1) {
type = TIMER_TYPE(i);
// check if possible
if (type == TIMER_OFF || type == TIMER_LAPCNT) continue; // not for this type
if (!(menu_timer_throttle & tbit)) continue; // not set for this timer
// start timer
menu_timer_running |= tbit;
menu_timer_throttle &= (u8)~tbit;
// awake MENU to possibly display changing time
awake(MENU);
}
}
// read first ADC values
#define ADC_BUFINIT(id) \
adc_buffer ## id ## [1] = adc_buffer ## id ## [2] = \
adc_buffer ## id ## [3] = adc_buffer ## id ## [0];
void input_read_first_values(void) {
// read initial ADC values
BSET(ADC_CR1, 0); // start conversion
while (!BCHK(ADC_CSR, 7)); // wait for end of conversion
read_ADC();
// put initial values to all buffers
ADC_BUFINIT(0);
ADC_BUFINIT(1);
ADC_BUFINIT(2);
adc_battery = adc_battery_last;
adc_battery_filt = (u32)adc_battery * ADC_BAT_FILT;
}
// input task, awaked every 5ms
static void input_loop(void) {
while (1) {
read_keys();
if (menu_timer_throttle) check_throttle_trigger();
check_inactivity();
update_battery();
stop();
}
}