Skip to content

Commit b065772

Browse files
committed
Re-queue user input after some delay
While key is pressed, automatically re-queue last keystroke after some interval has elapsed. By default, initial repetition happens after 1s; all others, after 0.125s.
1 parent 4dfc5ca commit b065772

File tree

1 file changed

+96
-25
lines changed

1 file changed

+96
-25
lines changed

src/main.c

Lines changed: 96 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
* main.c - p18clock source file
33
*
4-
* Copyright (C) 2011-2023 Javier Lopez-Gomez
4+
* Copyright (C) 2011-2024 Javier Lopez-Gomez
55
*
66
* This program is free software; you can redistribute it and/or modify
77
* it under the terms of the GNU General Public License as published by
@@ -78,8 +78,8 @@ LEDMTX_END_MODULES_INIT
7878

7979
LEDMTX_FRAMEBUFFER_RES(28)
8080

81-
#define CLASS_INPUT 0x00
82-
#define CLASS_RTC 0x40
81+
#define CLASS_INPUT (0 << 6)
82+
#define CLASS_RTC (1 << 6)
8383

8484
/// A circular buffer is used to communicate ISR code with the main loop. Data
8585
/// is typically queued from an ISR and dispatched from the main loop. The 2 MSb
@@ -98,9 +98,18 @@ LEDMTX_FRAMEBUFFER_RES(28)
9898

9999
DECLARE_RBUF(_mbuf, 32)
100100

101+
// # Summary of PIC peripheral usage:
102+
// - Timer0: used by libledmtx ISR and INT0 interrupt unmasking
103+
// - Timer1: used by RTC; external 32.768 kHz clock
104+
// - Timer2/CCP1: used by CCP1 module in PWM mode (for buzzer)
105+
// - Timer3: used for keystroke repetition
106+
//
107+
// # External interrupt sources:
108+
// - INT0: user input (keystroke)
101109
DEF_INTHIGH(_high_int)
102110
DEF_HANDLER(SIG_TMR1, _tmr1_handler)
103111
DEF_HANDLER(SIG_INT0, _int0_handler)
112+
DEF_HANDLER(SIG_TMR3, _tmr3_handler)
104113
END_DEF
105114

106115
DEF_INTLOW(_low_int)
@@ -174,7 +183,7 @@ SIGHANDLERNAKED(_tmr1_handler)
174183
banksel __time+3
175184
incf __time+3, f ; _time.mday++
176185
cpfsgt __time+3
177-
bra @tmr1_ret
186+
bra @tmr1__ret
178187

179188
; _time.mday overflow
180189
movlw 1
@@ -183,7 +192,7 @@ SIGHANDLERNAKED(_tmr1_handler)
183192
incf __time+4, f ; _time.mon++
184193
movlw 12
185194
cpfsgt __time+4
186-
bra @tmr1_ret
195+
bra @tmr1__ret
187196

188197
; _time.mon overflow
189198
movlw 1
@@ -202,7 +211,7 @@ SIGHANDLERNAKED(_tmr1_handler)
202211
movlw 2
203212
addwf _FSR1L, f, 0
204213

205-
@tmr1_ret:
214+
@tmr1__ret:
206215
movff _PREINC1, _FSR0L ; pop FSR0x
207216
movff _PREINC1, _FSR0H
208217
retfie 1
@@ -211,23 +220,18 @@ SIGHANDLERNAKED(_tmr1_handler)
211220
// clang-format on
212221

213222
#define INT0_UNMASK_TIMEOUT 7
214-
/// An INT0 interrupt is triggered externally when PORTB value changes. PORTB
215-
/// is indirectly driven by push buttons, an thus requires some debouncing
216-
/// logic. In particular, the INT0 handler below masks INT0 interrupts for some
217-
/// time after which they are enabled again (see `_tmr0_handler`). This amount
218-
/// of time is given by `INT0_UNMASK_TIMEOUT`.
223+
/// INT0 interrupt is externally triggered on RB0/INT0 pin. RB0:RB3 is
224+
/// (indirectly) driven by push buttons, and thus requires some debouncing
225+
/// logic. In particular, `_int0_handler` masks INT0 interrupts for some
226+
/// time after which they are enabled again. This interval is given by
227+
/// `INT0_UNMASK_TIMEOUT`.
219228
static volatile unsigned char _int0_timeout;
220229

230+
/// Queue user input (4 LSb of PORTB) in the `_mbuf` message buffer.
221231
// clang-format off
222-
SIGHANDLERNAKED(_int0_handler)
232+
void I_queue_keystroke(void) __naked
223233
{
224234
__asm
225-
bcf _INTCON, 1, 0 ; ack interrupt
226-
btfsc _INTCON2, 6, 0 ; INTEDG0 falling edge?
227-
bra @int0_rising
228-
229-
movff _FSR0H, _POSTDEC1 ; push FSR0x
230-
movff _FSR0L, _POSTDEC1
231235
movlw high __mbuf ; push &_mbuf
232236
movwf _POSTDEC1, 0
233237
movlw low __mbuf
@@ -238,17 +242,80 @@ SIGHANDLERNAKED(_int0_handler)
238242
call _rbuf_put
239243
movlw 2
240244
addwf _FSR1L, f, 0
245+
return
246+
__endasm;
247+
}
248+
// clang-format on
249+
250+
/// Base delay for re-queueing last keystroke, in Timer3 ticks after prescaler.
251+
/// The effective delay can be adjusted by changing Timer3 prescaler. Default to
252+
/// initial repetition after 1s; all other repetitions happen every 0.125s.
253+
#define INPUT_REP_DELAY_BASE 4096
254+
#define INPUT_REP_PRESCALER_INITIAL 0x3
255+
#define INPUT_REP_PRESCALER 0x1
256+
257+
// clang-format off
258+
SIGHANDLERNAKED(_int0_handler)
259+
{
260+
__asm
261+
bcf _INTCON, 1, 0 ; ack interrupt
262+
btfsc _INTCON2, 6, 0
263+
bra @int0__rising_edge
264+
265+
;; Triggered due to falling edge, i.e. key press
266+
movff _FSR0H, _POSTDEC1 ; push FSR0x
267+
movff _FSR0L, _POSTDEC1
268+
call _I_queue_keystroke
241269
movff _PREINC1, _FSR0L ; pop FSR0x
242270
movff _PREINC1, _FSR0H
243-
244-
@int0_rising:
271+
272+
movlw ((0xffff - INPUT_REP_DELAY_BASE) >> 8)
273+
movwf _TMR3H, 0
274+
movlw ((0xffff - INPUT_REP_DELAY_BASE) & 0xff)
275+
movwf _TMR3L, 0
276+
movlw (0x83 | (INPUT_REP_PRESCALER_INITIAL << 3))
277+
movwf _T3CON, 0
278+
@int0__toggle_edge:
245279
bcf _INTCON, 4, 0 ; mask INT0 interrupt
246280
btg _INTCON2, 6, 0 ; toggle edge
247-
248281
movlw INT0_UNMASK_TIMEOUT ; INT0 unmasking is done in _tmr0_handler
249282
banksel __int0_timeout
250283
movwf __int0_timeout
251284
retfie 1
285+
286+
;; Triggered due to rising edge, i.e. key release
287+
@int0__rising_edge:
288+
bcf _T3CON, 0, 0
289+
bra @int0__toggle_edge
290+
__endasm;
291+
}
292+
// clang-format on
293+
294+
// clang-format off
295+
SIGHANDLERNAKED(_tmr3_handler)
296+
{
297+
__asm
298+
bcf _PIR2, 1, 0 ; TMR3IF ack interrupt
299+
btfsc _PORTB, 0, 0 ; INT0 pin driven high, i.e. key released; stop Timer3
300+
bra @tmr3__stop
301+
302+
movff _FSR0H, _POSTDEC1 ; push FSR0x
303+
movff _FSR0L, _POSTDEC1
304+
call _I_queue_keystroke
305+
movff _PREINC1, _FSR0L ; pop FSR0x
306+
movff _PREINC1, _FSR0H
307+
308+
movlw ((0xffff - INPUT_REP_DELAY_BASE) >> 8)
309+
movwf _TMR3H, 0
310+
movlw ((0xffff - INPUT_REP_DELAY_BASE) & 0xff)
311+
movwf _TMR3L, 0
312+
movlw (0x83 | (INPUT_REP_PRESCALER << 3))
313+
movwf _T3CON, 0
314+
retfie 1
315+
316+
@tmr3__stop:
317+
bcf _T3CON, 0, 0
318+
retfie 1
252319
__endasm;
253320
}
254321
// clang-format on
@@ -265,15 +332,14 @@ SIGHANDLERNAKED(_tmr0_handler)
265332

266333
__asm
267334
btfsc _INTCON, 4, 0 ; INT0 interrupt masked?
268-
bra @int0_unmasked
335+
bra @int0__unmasked
269336

270337
movff _BSR, _POSTDEC1 ; push BSR
271338
banksel __int0_timeout
272339
dcfsnz __int0_timeout, f ; _int0_timeout--
273340
bsf _INTCON, 4, 0 ; _int0_timeout==0? unmask INT0 interrupt
274341
movff _PREINC1, _BSR ; pop BSR
275-
276-
@int0_unmasked:
342+
@int0__unmasked:
277343
__endasm;
278344

279345
LEDMTX_END_ISR
@@ -300,11 +366,16 @@ void uc_init(void) {
300366

301367
lm35_init();
302368

303-
/* TMR1 RTC, see p18f2550 datasheet */
369+
/* Timer1, see p18f2550 datasheet. RTC */
304370
TMR1H = 0x80;
305371
TMR1L = 0x00;
306372
T1CON = 0x0f;
307373
PIE1bits.TMR1IE = 1;
374+
IPR1bits.TMR1IP = 1;
375+
376+
/* Timer3 */
377+
PIE2bits.TMR3IE = 1;
378+
IPR2bits.TMR3IP = 1;
308379

309380
PR2 = CCP1_PR2;
310381
CCPR1L = (CCP1_R >> 2);

0 commit comments

Comments
 (0)