FabGL
ESP32 Display Controller and Graphics Library
ps2controller.cpp
1/*
2 Created by Fabrizio Di Vittorio (fdivitto2013@gmail.com) - <http://www.fabgl.com>
3 Copyright (c) 2019-2022 Fabrizio Di Vittorio.
4 All rights reserved.
5
6
7* Please contact fdivitto2013@gmail.com if you need a commercial license.
8
9
10* This library and related software is available under GPL v3.
11
12 FabGL is free software: you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation, either version 3 of the License, or
15 (at your option) any later version.
16
17 FabGL is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
21
22 You should have received a copy of the GNU General Public License
23 along with FabGL. If not, see <http://www.gnu.org/licenses/>.
24 */
25
26
27#include <strings.h>
28
29#include "freertos/FreeRTOS.h"
30
31#include "esp32/ulp.h"
32#include "driver/rtc_io.h"
33#include "soc/sens_reg.h"
34#if __has_include("soc/rtc_io_periph.h")
35 #include "soc/rtc_io_periph.h"
36#endif
37#include "esp_log.h"
38
39#include "ps2controller.h"
40#include "fabutils.h"
41#include "ulp_macro_ex.h"
42#include "devdrivers/keyboard.h"
43#include "devdrivers/mouse.h"
44
45
46
47#pragma GCC optimize ("O2")
48#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
49
50
51namespace fabgl {
52
53
54
55
57// placeholders
58
59#define OPCODE_PLACEHOLDER 12 // 12 is an unused ULP opcode we can use as placeholder
60
61#define SUB_OPCODE_DAT_ENABLE_OUTPUT 0
62#define SUB_OPCODE_DAT_ENABLE_INPUT 1
63#define SUB_OPCODE_CLK_ENABLE_OUTPUT 2
64#define SUB_OPCODE_CLK_ENABLE_INPUT 3
65#define SUB_OPCODE_READ_CLK 4
66#define SUB_OPCODE_READ_DAT 5
67#define SUB_OPCODE_WRITE_CLK 6
68#define SUB_OPCODE_WRITE_DAT 7
69
70#define PS2_PORT0 0
71#define PS2_PORT1 1
72
73
74#define DAT_ENABLE_OUTPUT(ps2port, value) { .macro = { \
75 .label = value, \
76 .unused = ps2port, \
77 .sub_opcode = SUB_OPCODE_DAT_ENABLE_OUTPUT, \
78 .opcode = OPCODE_PLACEHOLDER } }
79
80#define DAT_ENABLE_INPUT(ps2port, value) { .macro = { \
81 .label = value, \
82 .unused = ps2port, \
83 .sub_opcode = SUB_OPCODE_DAT_ENABLE_INPUT, \
84 .opcode = OPCODE_PLACEHOLDER } }
85
86#define CLK_ENABLE_OUTPUT(ps2port, value) { .macro = { \
87 .label = value, \
88 .unused = ps2port, \
89 .sub_opcode = SUB_OPCODE_CLK_ENABLE_OUTPUT, \
90 .opcode = OPCODE_PLACEHOLDER } }
91
92#define CLK_ENABLE_INPUT(ps2port, value) { .macro = { \
93 .label = value, \
94 .unused = ps2port, \
95 .sub_opcode = SUB_OPCODE_CLK_ENABLE_INPUT, \
96 .opcode = OPCODE_PLACEHOLDER } }
97
98// equivalent to rtc_gpio_get_level()
99#define READ_CLK(ps2port) { .macro = { \
100 .label = 0, \
101 .unused = ps2port, \
102 .sub_opcode = SUB_OPCODE_READ_CLK, \
103 .opcode = OPCODE_PLACEHOLDER } }
104
105// equivalent to rtc_gpio_get_level()
106#define READ_DAT(ps2port) { .macro = { \
107 .label = 0, \
108 .unused = ps2port, \
109 .sub_opcode = SUB_OPCODE_READ_DAT, \
110 .opcode = OPCODE_PLACEHOLDER } }
111
112// equivalent to rtc_gpio_set_level()
113#define WRITE_CLK(ps2port, value) { .macro = { \
114 .label = value, \
115 .unused = ps2port, \
116 .sub_opcode = SUB_OPCODE_WRITE_CLK, \
117 .opcode = OPCODE_PLACEHOLDER } }
118
119// equivalent to rtc_gpio_set_level()
120#define WRITE_DAT(ps2port, value) { .macro = { \
121 .label = value, \
122 .unused = ps2port, \
123 .sub_opcode = SUB_OPCODE_WRITE_DAT, \
124 .opcode = OPCODE_PLACEHOLDER } }
125
126
128// macro instructions
129
130// equivalent to rtc_gpio_set_direction(..., RTC_GPIO_MODE_INPUT_ONLY)
131#define CONFIGURE_DAT_INPUT(ps2port) \
132 DAT_ENABLE_OUTPUT(ps2port, 0), \
133 DAT_ENABLE_INPUT(ps2port, 1)
134
135// equivalent to rtc_gpio_set_direction(..., RTC_GPIO_MODE_OUTPUT_ONLY)
136#define CONFIGURE_DAT_OUTPUT(ps2port) \
137 DAT_ENABLE_OUTPUT(ps2port, 1), \
138 DAT_ENABLE_INPUT(ps2port, 0)
139
140// equivalent to rtc_gpio_set_direction(..., RTC_GPIO_MODE_INPUT_ONLY)
141#define CONFIGURE_CLK_INPUT(ps2port) \
142 CLK_ENABLE_OUTPUT(ps2port, 0), \
143 CLK_ENABLE_INPUT(ps2port, 1)
144
145// equivalent to rtc_gpio_set_direction(..., RTC_GPIO_MODE_OUTPUT_ONLY)
146#define CONFIGURE_CLK_OUTPUT(ps2port) \
147 CLK_ENABLE_OUTPUT(ps2port, 1), \
148 CLK_ENABLE_INPUT(ps2port, 0)
149
150// equivalent to rtc_gpio_set_level()
151// WRITE bit 0 of R0
152#define WRITE_DAT_R0(ps2port) \
153 I_BL(3, 1), \
154 WRITE_DAT(ps2port, 1), \
155 I_BGE(2, 1), \
156 WRITE_DAT(ps2port, 0)
157
158// performs [addr] = value
159// changes: R0, R1
160#define MEM_WRITEI(addr, value) \
161 I_MOVI(R0, addr), \
162 I_MOVI(R1, value), \
163 I_ST(R1, R0, 0)
164
165// performs [[addr]] = value
166// changes: R0, R1
167#define MEM_INDWRITEI(addr, value) \
168 I_MOVI(R0, addr), \
169 I_LD(R0, R0, 0), \
170 I_MOVI(R1, value), \
171 I_ST(R1, R0, 0)
172
173// performs [addr] = reg (reg cannot be R0)
174// changes R0
175#define MEM_WRITER(addr, reg) \
176 I_MOVI(R0, addr), \
177 I_ST(reg, R0, 0)
178
179// performs [[addr]] = reg (reg cannot be R0)
180// changes R0
181#define MEM_INDWRITER(addr, reg) \
182 I_MOVI(R0, addr), \
183 I_LD(R0, R0, 0), \
184 I_ST(reg, R0, 0)
185
186// performs reg = [addr]
187#define MEM_READR(reg, addr) \
188 I_MOVI(reg, addr), \
189 I_LD(reg, reg, 0)
190
191// performs reg = [[addr]] (reg cannot be R0)
192#define MEM_INDREADR(reg, addr) \
193 I_MOVI(reg, addr), \
194 I_LD(reg, reg, 0), \
195 I_LD(reg, reg, 0)
196
197// performs [addr] = [addr] + 1
198// changes: R0, R1
199#define MEM_INC(addr) \
200 I_MOVI(R0, addr), \
201 I_LD(R1, R0, 0), \
202 I_ADDI(R1, R1, 1), \
203 I_ST(R1, R0, 0)
204
205// jump to "label" if [addr] < value
206// changes: R0
207#define MEM_BL(label, addr, value) \
208 I_MOVI(R0, addr), \
209 I_LD(R0, R0, 0), \
210 M_BL(label, value)
211
212// jump to "label" if [addr] >= value
213// changes: R0
214#define MEM_BGE(label, addr, value) \
215 I_MOVI(R0, addr), \
216 I_LD(R0, R0, 0), \
217 M_BGE(label, value)
218
219// long jump version of M_BGE
220#define M_LONG_BGE(label, value) \
221 I_BL(2, value), \
222 M_BX(label)
223
224// long jump version of M_BL
225#define M_LONG_BL(label, value) \
226 I_BGE(2, value), \
227 M_BX(label)
228
229// long jump to "label" if R0 == value
230#define M_LONG_BE(label, value) \
231 I_BL(3, value), \
232 I_BGE(2, value + 1), \
233 M_BX(label)
234
235// long jump to "label" if R0 != value
236#define M_LONG_BNE(label, value) \
237 I_BGE(2, value), \
238 M_BX(label), \
239 I_BL(2, value + 1), \
240 M_BX(label)
241
242#define M_LONG_STAGEBLE(label_num, imm_value) \
243 I_STAGEBLE(2, imm_value), \
244 I_STAGEBGE(2, imm_value), \
245 M_BX(label_num)
246
247#define M_LONG_STAGEBL(label_num, imm_value) \
248 I_STAGEBGE(2, imm_value), \
249 M_BX(label_num)
250
251#define M_LONG_STAGEBGE(label_num, imm_value) \
252 I_STAGEBL(2, imm_value), \
253 M_BX(label_num)
254
255// ULP clock is 8MHz, so every cycle takes 0.125us and 1us = 8 cycles
256#define M_DELAY_US(us) I_DELAY(us * 8)
257
258
260
261
262
263// Locations inside RTC low speed memory
264
265#define RTCMEM_PROG_START 0x000 // where the program begins
266#define RTCMEM_VARS_START 0x200 // where the variables begin
267
268// commands (set by CPU, reset by ULP)
269#define RTCMEM_PORT0_TX (RTCMEM_VARS_START + 0) // 1 = send data to port 0
270#define RTCMEM_PORT1_TX (RTCMEM_VARS_START + 1) // 1 = send data to port 1
271#define RTCMEM_PORT0_RX_ENABLE (RTCMEM_VARS_START + 2) // 1 = Request to enable RX from port 0
272#define RTCMEM_PORT1_RX_ENABLE (RTCMEM_VARS_START + 3) // 1 = Request to enable RX from port 1
273#define RTCMEM_PORT0_RX_DISABLE (RTCMEM_VARS_START + 4) // 1 = Request to disable RX from port 0
274#define RTCMEM_PORT1_RX_DISABLE (RTCMEM_VARS_START + 5) // 1 = Request to disable RX from port 1
275// commands parameters (set by CPU)
276#define RTCMEM_PORT0_DATAOUT (RTCMEM_VARS_START + 6) // Data to send to port 0 (when RTCMEM_PORT0_TX = 1)
277#define RTCMEM_PORT1_DATAOUT (RTCMEM_VARS_START + 7) // Data to send to port 1 (when RTCMEM_PORT1_TX = 1)
278
279// flags (set by ULP, reset by CPU), generate interrupt
280#define RTCMEM_PORT0_RX (RTCMEM_VARS_START + 8) // Data received from port 0
281#define RTCMEM_PORT1_RX (RTCMEM_VARS_START + 9) // Data received from port 1
282#define RTCMEM_PORT0_RX_CLK_TIMEOUT (RTCMEM_VARS_START + 10) // RX port 0 CLK timeout
283#define RTCMEM_PORT1_RX_CLK_TIMEOUT (RTCMEM_VARS_START + 11) // RX port 1 CLK timeout
284// flags parameters (set by ULP)
285#define RTCMEM_PORT0_DATAIN (RTCMEM_VARS_START + 12) // Data received from port 0 (when RTCMEM_PORT0_RX = 1)
286#define RTCMEM_PORT1_DATAIN (RTCMEM_VARS_START + 13) // Data received from port 1 (when RTCMEM_PORT1_RX = 1)
287
288// internal variables
289#define RTCMEM_PORT0_RX_ENABLED (RTCMEM_VARS_START + 14) // 0 = port 0 not configured for RX, 1 = port 0 configured for RX
290#define RTCMEM_PORT1_RX_ENABLED (RTCMEM_VARS_START + 15) // 0 = port 1 not configured for RX, 1 = port 1 configured for RX
291
292#define RTCMEM_LASTVAR (RTCMEM_VARS_START + 15)
293
294
295// RX maximum time between CLK cycles (reliable minimum is about 15)
296#define CLK_RX_TIMEOUT_VAL 100
297
298// TX maximum time between CLK cycles
299#define CLK_TX_TIMEOUT_VAL 1200 // fine tuned to work with PERIBOARD 409P
300
301// counter (R2) re-wake value
302#define WAKE_THRESHOLD 3000
303
304
305// check RTC memory occupation
306#if RTCMEM_LASTVAR >= 0x800
307#error "ULP program too big"
308#endif
309
310
311// ULP program labels
312
313#define LABEL_WAIT_COMMAND 0
314
315#define LABEL_RX 1
316#define LABEL_RX_NEXT 2
317
318#define LABEL_PORT0_ENABLE_RX 3
319#define LABEL_PORT0_STOP_RX 4
320#define LABEL_PORT0_TX 5
321#define LABEL_PORT0_TX_NEXT_BIT 6
322#define LABEL_PORT0_TX_WAIT_CLK_HIGH 7
323#define LABEL_PORT0_TX_EXIT 8
324#define LABEL_PORT0_RX_WAIT_LOOP 9
325#define LABEL_PORT0_RX_CLK_IS_HIGH 10
326#define LABEL_PORT0_RX_CLK_IS_LOW 11
327#define LABEL_PORT0_RX_CLK_TIMEOUT 12
328#define LABEL_PORT0_RX_CHECK_CLK 13
329
330#define LABEL_PORT1_ENABLE_RX 14
331#define LABEL_PORT1_STOP_RX 15
332#define LABEL_PORT1_TX 16
333#define LABEL_PORT1_TX_NEXT_BIT 17
334#define LABEL_PORT1_TX_WAIT_CLK_HIGH 18
335#define LABEL_PORT1_TX_EXIT 19
336#define LABEL_PORT1_RX_WAIT_LOOP 20
337#define LABEL_PORT1_RX_CLK_IS_HIGH 21
338#define LABEL_PORT1_RX_CLK_IS_LOW 22
339#define LABEL_PORT1_RX_CLK_TIMEOUT 23
340#define LABEL_PORT1_RX_CHECK_CLK 24
341
342
343// Helpers
344
345// temporarily disable port 0
346// note: before disable check if it was enabled. If it wasn't enabled just exit.
347// changes: R0
348#define TEMP_PORT0_DISABLE() \
349 I_LD(R0, R3, RTCMEM_PORT0_RX_ENABLED), \
350 I_BL(4, 1), \
351 CONFIGURE_CLK_OUTPUT(PS2_PORT0), \
352 WRITE_CLK(PS2_PORT0, 0)
353
354// temporarily enable port 0
355// note: before enable check if it was enabled. If it wasn't enabled just exit
356// changes: R0
357#define TEMP_PORT0_ENABLE() \
358 I_LD(R0, R3, RTCMEM_PORT0_RX_ENABLED), \
359 I_BL(3, 1), \
360 CONFIGURE_CLK_INPUT(PS2_PORT0)
361
362// temporarily disable port 1
363// note: before disable check if it was enabled. If it wasn't enabled just exit.
364// changes: R0
365#define TEMP_PORT1_DISABLE() \
366 I_LD(R0, R3, RTCMEM_PORT1_RX_ENABLED), \
367 I_BL(4, 1), \
368 CONFIGURE_CLK_OUTPUT(PS2_PORT1), \
369 WRITE_CLK(PS2_PORT1, 0)
370
371// temporarily enable port 1
372// note: before enable check if it was enabled. If it wasn't enabled just exit
373// changes: R0
374#define TEMP_PORT1_ENABLE() \
375 I_LD(R0, R3, RTCMEM_PORT1_RX_ENABLED), \
376 I_BL(3, 1), \
377 CONFIGURE_CLK_INPUT(PS2_PORT1)
378
379// permanently disable port 0
380// changes: R0
381#define PERM_PORT0_DISABLE() \
382 I_MOVI(R0, 0), \
383 I_ST(R0, R3, RTCMEM_PORT0_RX_ENABLED), \
384 CONFIGURE_CLK_OUTPUT(PS2_PORT0), \
385 WRITE_CLK(PS2_PORT0, 0)
386
387// permanently disable port 1
388// changes: R0
389#define PERM_PORT1_DISABLE() \
390 I_MOVI(R0, 0), \
391 I_ST(R0, R3, RTCMEM_PORT1_RX_ENABLED), \
392 CONFIGURE_CLK_OUTPUT(PS2_PORT1), \
393 WRITE_CLK(PS2_PORT1, 0)
394
395
396/*
397 Notes about ULP registers usage
398
399 R0:
400 General purpose temporary accumulator
401
402 R1:
403 General purpose temporary register
404
405 R2:
406 LABEL_PORT0_TX / LABEL_PORT1_TX: Word to send. Reset to 0 on exit.
407 LABEL_RX: Re-wake counter register, when port 0 or port 1 is disabled.
408 General purpose temporary register on receiving data (reset at the end or timeout).
409
410 R3:
411 Base address for variables (0x0000)
412 LABEL_PORT0_TX / LABEL_PORT1_TX: timeout counter waiting for CLK to be Low
413 LABEL_RX: timeout counter waiting for CLK changes
414
415 STAGE:
416 RX, TX bit counter
417
418*/
419
420
421const ulp_insn_t ULP_code[] = {
422
423 // Stop ULP timer, not necessary because this routine never ends
424 I_END(),
425
426 // R3 contains just 0x0000, for entire execution, which is the base address of all variabiles load, so it is possible to do
427 // I_LD(R#, R3, RTC_MEM_XX) instead of MEM_READR(), saving one instruction
428 I_MOVI(R3, 0x0000),
429
430
432 // Command wait main loop
433
434M_LABEL(LABEL_WAIT_COMMAND),
435
436 // port 0 TX?
437 I_LD(R0, R3, RTCMEM_PORT0_TX),
438 M_BGE(LABEL_PORT0_TX, 1), // yes, jump to LABEL_PORT0_TX
439
440 // port 0 enable RX?
441 I_LD(R0, R3, RTCMEM_PORT0_RX_ENABLE),
442 M_BGE(LABEL_PORT0_ENABLE_RX, 1), // yes, jump to LABEL_PORT0_ENABLE_RX
443
444 // port 0 disable RX?
445 I_LD(R0, R3, RTCMEM_PORT0_RX_DISABLE),
446 M_BGE(LABEL_PORT0_STOP_RX, 1), // yes, jump to LABEL_PORT0_STOP_RX
447
448 // port 1 TX?
449 I_LD(R0, R3, RTCMEM_PORT1_TX),
450 M_BGE(LABEL_PORT1_TX, 1), // yes, jump to LABEL_PORT1_TX
451
452 // port 1 enable RX?
453 I_LD(R0, R3, RTCMEM_PORT1_RX_ENABLE),
454 M_BGE(LABEL_PORT1_ENABLE_RX, 1), // yes, jump to LABEL_PORT1_ENABLE_RX
455
456 // port 1 disable RX?
457 I_LD(R0, R3, RTCMEM_PORT1_RX_DISABLE),
458 M_BGE(LABEL_PORT1_STOP_RX, 1), // yes, jump to LABEL_PORT1_STOP_RX
459
460 // check RX from port 0 or port 1
461 M_BX(LABEL_RX),
462
463
465 // LABEL_PORT0_ENABLE_RX - Configure port 0 as RX
466
467M_LABEL(LABEL_PORT0_ENABLE_RX),
468
469 // set RX flag for port 0
470 I_MOVI(R0, 1),
471 I_ST(R0, R3, RTCMEM_PORT0_RX_ENABLED), // [RTCMEM_PORT0_RX_ENABLED] = 1
472
473 // Configure CLK and DAT as inputs
474 CONFIGURE_CLK_INPUT(PS2_PORT0),
475 CONFIGURE_DAT_INPUT(PS2_PORT0),
476
477 // Reset command
478 I_MOVI(R0, 0),
479 I_ST(R0, R3, RTCMEM_PORT0_RX_ENABLE), // [RTCMEM_PORT0_RX_ENABLE] = 0
480
481 M_BX(LABEL_RX),
482
483
485 // LABEL_PORT1_ENABLE_RX - Configure port 1 as RX
486
487M_LABEL(LABEL_PORT1_ENABLE_RX),
488
489 // set RX flag for port 1
490 I_MOVI(R0, 1),
491 I_ST(R0, R3, RTCMEM_PORT1_RX_ENABLED), // [RTCMEM_PORT1_RX_ENABLED] = 1
492
493 // Configure CLK and DAT as inputs
494 CONFIGURE_CLK_INPUT(PS2_PORT1),
495 CONFIGURE_DAT_INPUT(PS2_PORT1),
496
497 // Reset command
498 I_MOVI(R0, 0),
499 I_ST(R0, R3, RTCMEM_PORT1_RX_ENABLE), // [RTCMEM_PORT1_RX_ENABLE] = 0
500
501 M_BX(LABEL_RX),
502
503
505 // LABEL_PORT0_STOP_RX - Stop port 0 RX (drive CLK low)
506
507M_LABEL(LABEL_PORT0_STOP_RX),
508
509 PERM_PORT0_DISABLE(),
510
511 // Reset command
512 I_MOVI(R0, 0),
513 I_ST(R0, R3, RTCMEM_PORT0_RX_DISABLE), // [RTCMEM_PORT0_RX_DISABLE] = 0
514
515 M_BX(LABEL_RX),
516
517
519 // LABEL_PORT1_STOP_RX - Stop port 1 RX (drive CLK low)
520
521M_LABEL(LABEL_PORT1_STOP_RX),
522
523 PERM_PORT1_DISABLE(),
524
525 // Reset command
526 I_MOVI(R0, 0),
527 I_ST(R0, R3, RTCMEM_PORT1_RX_DISABLE), // [RTCMEM_PORT1_RX_DISABLE] = 0
528
529 M_BX(LABEL_RX),
530
531
533 // LABEL_PORT0_TX - Send data
534 // Port 0 is automatically enabled for RX at the end
535
536M_LABEL(LABEL_PORT0_TX),
537
538 // Send the word in RTCMEM_PORT0_DATAOUT
539
540 // put in R2 the word to send (10 bits: data, parity and stop bit)
541 I_LD(R2, R3, RTCMEM_PORT0_DATAOUT), // R2 = [RTCMEM_PORT0_DATAOUT]
542
543 // reset the bit counter (STAGE register: 0...7 = data0, 8 = parity, 9 = stop bit)
544 I_STAGERSTI(), // STAGE = 0
545
546 // disable port 1?
547 TEMP_PORT1_DISABLE(),
548
549 // maintain CLK and DAT low for 200us
550 CONFIGURE_CLK_OUTPUT(PS2_PORT0),
551 WRITE_CLK(PS2_PORT0, 0),
552 M_DELAY_US(200),
553 CONFIGURE_DAT_OUTPUT(PS2_PORT0),
554 WRITE_DAT(PS2_PORT0, 0),
555
556 // configure CLK as input
557 CONFIGURE_CLK_INPUT(PS2_PORT0),
558
559M_LABEL(LABEL_PORT0_TX_NEXT_BIT),
560
561 // wait for CLK = LOW
562
563 // temporarily use R3 (which is 0) as timeout counter
564 I_ADDI(R3, R3, 1),
565 I_MOVR(R0, R3),
566 M_BGE(LABEL_PORT0_TX_EXIT, CLK_TX_TIMEOUT_VAL), // jump to LABEL_PORT0_TX_EXIT on CLK timeout
567
568 // read CLK
569 READ_CLK(PS2_PORT0), // R0 = CLK
570
571 // repeat if CLK is high
572 M_BGE(LABEL_PORT0_TX_NEXT_BIT, 1), // jump to LABEL_PORT0_TX_NEXT_BIT if R0 >= 1
573
574 // R3 is no more timeout counter, but variables base
575 I_MOVI(R3, 0),
576
577 // bit 10 is the ACK from keyboard, don't send anything, just bypass
578 M_STAGEBGE(LABEL_PORT0_TX_WAIT_CLK_HIGH, 10), // jump to LABEL_PORT0_TX_WAIT_CLK_HIGH if STAGE >= 10
579
580 // CLK is LOW, we are ready to send the bit (LSB of R0)
581 I_ANDI(R0, R2, 1), // R0 = R2 & 1
582 WRITE_DAT_R0(PS2_PORT0), // DAT = LSB of R0
583
584M_LABEL(LABEL_PORT0_TX_WAIT_CLK_HIGH),
585
586 // Wait for CLK = HIGH
587
588 // read CLK
589 READ_CLK(PS2_PORT0), // R0 = CLK
590
591 // repeat if CLK is low
592 M_BL(LABEL_PORT0_TX_WAIT_CLK_HIGH, 1), // jump to LABEL_PORT0_TX_WAIT_CLK_HIGH if R0 < 1
593
594 // shift the sending word 1 bit to the right (prepare next bit to send)
595 I_RSHI(R2, R2, 1), // R2 = R2 >> 1
596
597 // increment bit count
598 I_STAGEINCI(1), // STAGE = STAGE + 1
599
600 // end of word? if not send then another bit
601 M_STAGEBL(LABEL_PORT0_TX_NEXT_BIT, 11), // jump to LABEL_PORT0_TX_NEXT_BIT if STAGE < 11
602
603M_LABEL(LABEL_PORT0_TX_EXIT),
604
605 // R3 is no more timeout counter, but variables base
606 I_MOVI(R3, 0),
607
608 // back to RX mode
609 CONFIGURE_DAT_INPUT(PS2_PORT0),
610 I_MOVI(R0, 1),
611 I_ST(R0, R3, RTCMEM_PORT0_RX_ENABLED), // [RTCMEM_PORT0_RX_ENABLED] = 1
612
613 // reset command field
614 I_MOVI(R0, 0),
615 I_ST(R0, R3, RTCMEM_PORT0_TX), // [RTCMEM_PORT0_TX] = 0
616
617 // re-enable port 1?
618 TEMP_PORT1_ENABLE(),
619
620 // from now R2 is used as CPU re-wake timeout
621 I_MOVI(R2, 0),
622
623 // go to command loop
624 M_BX(LABEL_RX),
625
626
628 // LABEL_PORT1_TX - Send data
629 // Port 1 is automatically enabled for RX at the end
630
631M_LABEL(LABEL_PORT1_TX),
632
633 // Send the word in RTCMEM_PORT1_DATAOUT
634
635 // put in R2 the word to send (10 bits: data, parity and stop bit)
636 I_LD(R2, R3, RTCMEM_PORT1_DATAOUT), // R2 = [RTCMEM_PORT1_DATAOUT]
637
638 // reset the bit counter (STAGE register: 0...7 = data0, 8 = parity, 9 = stop bit)
639 I_STAGERSTI(), // STAGE = 0
640
641 // disable port 0?
642 TEMP_PORT0_DISABLE(),
643
644 // maintain CLK and DAT low for 200us
645 CONFIGURE_CLK_OUTPUT(PS2_PORT1),
646 WRITE_CLK(PS2_PORT1, 0),
647 M_DELAY_US(200),
648 CONFIGURE_DAT_OUTPUT(PS2_PORT1),
649 WRITE_DAT(PS2_PORT1, 0),
650
651 // configure CLK as input
652 CONFIGURE_CLK_INPUT(PS2_PORT1),
653
654M_LABEL(LABEL_PORT1_TX_NEXT_BIT),
655
656 // wait for CLK = LOW
657
658 // temporarily use R3 (which is 0) as timeout counter
659 I_ADDI(R3, R3, 1),
660 I_MOVR(R0, R3),
661 M_BGE(LABEL_PORT1_TX_EXIT, CLK_TX_TIMEOUT_VAL), // jump to LABEL_PORT1_TX_EXIT on CLK timeout
662
663 // read CLK
664 READ_CLK(PS2_PORT1), // R0 = CLK
665
666 // repeat if CLK is high
667 M_BGE(LABEL_PORT1_TX_NEXT_BIT, 1), // jump to LABEL_PORT1_TX_NEXT_BIT if R0 >= 1
668
669 // R3 is no more timeout counter, but variables base
670 I_MOVI(R3, 0),
671
672 // bit 10 is the ACK from keyboard, don't send anything, just bypass
673 M_STAGEBGE(LABEL_PORT1_TX_WAIT_CLK_HIGH, 10), // jump to LABEL_PORT1_TX_WAIT_CLK_HIGH if STAGE >= 10
674
675 // CLK is LOW, we are ready to send the bit (LSB of R0)
676 I_ANDI(R0, R2, 1), // R0 = R2 & 1
677 WRITE_DAT_R0(PS2_PORT1), // DAT = LSB of R0
678
679M_LABEL(LABEL_PORT1_TX_WAIT_CLK_HIGH),
680
681 // Wait for CLK = HIGH
682
683 // read CLK
684 READ_CLK(PS2_PORT1), // R0 = CLK
685
686 // repeat if CLK is low
687 M_BL(LABEL_PORT1_TX_WAIT_CLK_HIGH, 1), // jump to LABEL_PORT1_TX_WAIT_CLK_HIGH if R0 < 1
688
689 // shift the sending word 1 bit to the right (prepare next bit to send)
690 I_RSHI(R2, R2, 1), // R2 = R2 >> 1
691
692 // increment bit count
693 I_STAGEINCI(1), // STAGE = STAGE + 1
694
695 // end of word? if not send then another bit
696 M_STAGEBL(LABEL_PORT1_TX_NEXT_BIT, 11), // jump to LABEL_PORT1_TX_NEXT_BIT if STAGE < 11
697
698M_LABEL(LABEL_PORT1_TX_EXIT),
699
700 // R3 is no more timeout counter, but variables base
701 I_MOVI(R3, 0),
702
703 // back to RX mode
704 CONFIGURE_DAT_INPUT(PS2_PORT1),
705 I_MOVI(R0, 1),
706 I_ST(R0, R3, RTCMEM_PORT1_RX_ENABLED), // [RTCMEM_PORT1_RX_ENABLED] = 1
707
708 // reset command field
709 I_MOVI(R0, 0),
710 I_ST(R0, R3, RTCMEM_PORT1_TX), // [RTCMEM_PORT0_TX] = 0
711
712 // re-enable port 0?
713 TEMP_PORT0_ENABLE(),
714
715 // from now R2 is used as CPU re-wake timeout
716 I_MOVI(R2, 0),
717
718 // go to command loop
719 M_BX(LABEL_RX),
720
721
723 // LABEL_PORT0_RX_CLK_TIMEOUT
724
725M_LABEL(LABEL_PORT0_RX_CLK_TIMEOUT),
726
727 // R3 is no more timeout counter, but variables base
728 I_MOVI(R3, 0),
729
730 // disable port 0
731 PERM_PORT0_DISABLE(),
732
733 // re-enable port 1?
734 TEMP_PORT1_ENABLE(),
735
736 // set port 0 timeout flag
737 I_MOVI(R0, 1),
738 I_ST(R0, R3, RTCMEM_PORT0_RX_CLK_TIMEOUT),
739
740 I_WAKE(),
741
742 // from now R2 is used as CPU re-wake timeout
743 I_MOVI(R2, 0),
744
745 M_BX(LABEL_RX),
746
747
749 // LABEL_PORT1_RX_CLK_TIMEOUT
750
751M_LABEL(LABEL_PORT1_RX_CLK_TIMEOUT),
752
753 // R3 is no more timeout counter, but variables base
754 I_MOVI(R3, 0),
755
756 // disable port 1
757 PERM_PORT1_DISABLE(),
758
759 // re-enable port 0?
760 TEMP_PORT0_ENABLE(),
761
762 // set port 1 timeout flag
763 I_MOVI(R0, 1),
764 I_ST(R0, R3, RTCMEM_PORT1_RX_CLK_TIMEOUT),
765
766 I_WAKE(),
767
768 // from now R2 is used as CPU re-wake timeout
769 I_MOVI(R2, 0),
770
771 //M_BX(LABEL_RX),
772
773
775 // LABEL_RX - Check for new data from port 0 and port 1
776 // During reception port 1 is disabled for RX. It will be enabled at the end.
777 // After received data the port 0 remains disabled for RX.
778
779M_LABEL(LABEL_RX),
780
781 // Check for RX from port 0?
782 I_LD(R0, R3, RTCMEM_PORT0_RX_ENABLED),
783 M_BGE(LABEL_PORT0_RX_CHECK_CLK, 1), // yes, jump to LABEL_PORT0_RX_CHECK_CLK
784
785 // no, port 0 is not enabled, maybe we are waiting for an ack from SoC, increment and check the re-wake timeout counter (R2)
786 I_ADDI(R2, R2, 1), // R2 = R2 + 1 (increment counter)
787 I_MOVR(R0, R2),
788 M_BL(LABEL_RX_NEXT, WAKE_THRESHOLD), // check the other port if counter is below WAKE_THRESHOLD
789 I_WAKE(), // need to wake CPU
790 I_MOVI(R2, 0), // reset counter
791 M_BX(LABEL_RX_NEXT), // check the other port
792
793M_LABEL(LABEL_PORT0_RX_CHECK_CLK),
794
795 // read CLK
796 READ_CLK(PS2_PORT0), // R0 = CLK
797
798 // CLK is low?
799 M_BGE(LABEL_RX_NEXT, 1), // no, jump to LABEL_RX_NEXT
800
801 // yes, from here we are receiving data from port 0
802
803 // reset state variables
804 I_MOVI(R0, 0),
805 I_ST(R0, R3, RTCMEM_PORT0_DATAIN), // [RTCMEM_PORT0_DATAIN] = 0
806 I_STAGERSTI(), // STAGE = 0 (STAGE is the bit count)
807 I_MOVI(R2, 0), // R2 = 0 (R2 represents last read value from CLK)
808
809 // disable port 1?
810 TEMP_PORT1_DISABLE(),
811
812 // CLK is low so jump directly to DAT capture without recheck
813 M_BX(LABEL_PORT0_RX_CLK_IS_LOW),
814
815M_LABEL(LABEL_PORT0_RX_WAIT_LOOP),
816
817 // temporarily use R3 (which is 0) as timeout counter
818 I_ADDI(R3, R3, 1),
819 I_MOVR(R0, R3),
820 M_BGE(LABEL_PORT0_RX_CLK_TIMEOUT, CLK_RX_TIMEOUT_VAL), // jump to LABEL_PORT0_RX_CLK_TIMEOUT on CLK timeout
821
822 // read CLK
823 READ_CLK(PS2_PORT0), // R0 = CLK
824
825 // CLK changed?
826 I_SUBR(R1, R2, R0), // R1 = R2 - R0
827 M_BXZ(LABEL_PORT0_RX_WAIT_LOOP), // no, repeat to LABEL_PORT0_RX_WAIT_LOOP
828
829 // R3 is no more timeout counter, but variables base
830 I_MOVI(R3, 0),
831
832 // update last bit received
833 I_MOVR(R2, R0), // R2 = R0
834
835 // is CLK high?
836 M_BGE(LABEL_PORT0_RX_CLK_IS_HIGH, 1),
837
838M_LABEL(LABEL_PORT0_RX_CLK_IS_LOW),
839
840 // CLK is Low, capture DAT value
841 READ_DAT(PS2_PORT0), // R0 = DAT
842
843 // merge with data word and shift right by 1 the received word
844 I_LSHI(R0, R0, 11), // R0 = R0 << 11
845 I_LD(R1, R3, RTCMEM_PORT0_DATAIN), // R1 = [RTCMEM_PORT0_DATAIN]
846 I_ORR(R1, R1, R0), // R1 = R1 | R0
847 I_RSHI(R1, R1, 1), // R1 = R1 >> 1
848 I_ST(R1, R3, RTCMEM_PORT0_DATAIN), // [RTCMEM_PORT0_DATAIN] = R1
849
850 // repeat
851 M_BX(LABEL_PORT0_RX_WAIT_LOOP),
852
853M_LABEL(LABEL_PORT0_RX_CLK_IS_HIGH),
854
855 // increment bit count
856 I_STAGEINCI(1), // STAGE = STAGE + 1
857
858 // end of word? if not get another bit
859 M_STAGEBL(LABEL_PORT0_RX_WAIT_LOOP, 11), // jump to LABEL_PORT0_RX_WAIT_LOOP if STAGE < 11
860
861 // End of word, disable port 0
862 PERM_PORT0_DISABLE(),
863
864 // set RX flag
865 I_MOVI(R0, 1),
866 I_ST(R0, R3, RTCMEM_PORT0_RX), // [RTCMEM_PORT0_RX] = 1
867
868 // re-enable port 1?
869 TEMP_PORT1_ENABLE(),
870
871 // don't check RTC_CNTL_RDY_FOR_WAKEUP_S of RTC_CNTL_LOW_POWER_ST_REG because it never gets active, due
872 // the fact ULP never calls HALT or SoC is always active.
873 I_WAKE(),
874
875 // from now R2 is used as CPU re-wake timeout
876 I_MOVI(R2, 0),
877
878
880 // Check for new data from port 1
881 // During reception port 0 is disabled for RX. It will be enabled at the end.
882 // After received data the port 0 remains disabled for RX.
883
884M_LABEL(LABEL_RX_NEXT),
885
886 // Check for RX from port 1?
887 I_LD(R0, R3, RTCMEM_PORT1_RX_ENABLED),
888 M_BGE(LABEL_PORT1_RX_CHECK_CLK, 1), // yes, jump to LABEL_PORT1_RX_CHECK_CLK
889
890 // no, port 1 is not enabled, maybe we are waiting for an ack from SoC, increment and check the re-wake timeout counter (R2)
891 I_ADDI(R2, R2, 1), // R2 = R2 + 1 (increment counter)
892 I_MOVR(R0, R2),
893 M_LONG_BL(LABEL_WAIT_COMMAND, WAKE_THRESHOLD), // return to main loop if counter is below WAKE_THRESHOLD
894 I_WAKE(), // need to wake CPU
895 I_MOVI(R2, 0), // reset counter
896 M_BX(LABEL_WAIT_COMMAND), // return to main loop
897
898M_LABEL(LABEL_PORT1_RX_CHECK_CLK),
899
900 // read CLK
901 READ_CLK(PS2_PORT1), // R0 = CLK
902
903 // CLK is low?
904 M_LONG_BGE(LABEL_WAIT_COMMAND, 1), // no, jump to LABEL_WAIT_COMMAND
905
906 // yes, from here we are receiving data from port 0
907
908 // reset state variables
909 I_MOVI(R0, 0),
910 I_ST(R0, R3, RTCMEM_PORT1_DATAIN), // [RTCMEM_PORT1_DATAIN] = 0
911 I_STAGERSTI(), // STAGE = 0 (STAGE is the bit count)
912 I_MOVI(R2, 0), // R2 = 0 (R2 represents last read value from CLK)
913
914 // disable port 0?
915 TEMP_PORT0_DISABLE(),
916
917 // CLK is low so jump directly to DAT capture without recheck
918 M_BX(LABEL_PORT1_RX_CLK_IS_LOW),
919
920M_LABEL(LABEL_PORT1_RX_WAIT_LOOP),
921
922 // temporarily use R3 (which is 0) as timeout counter
923 I_ADDI(R3, R3, 1),
924 I_MOVR(R0, R3),
925 M_BGE(LABEL_PORT1_RX_CLK_TIMEOUT, CLK_RX_TIMEOUT_VAL), // jump to LABEL_PORT1_RX_CLK_TIMEOUT on CLK timeout
926
927 // read CLK
928 READ_CLK(PS2_PORT1), // R0 = CLK
929
930 // CLK changed?
931 I_SUBR(R1, R2, R0), // R1 = R2 - R0
932 M_BXZ(LABEL_PORT1_RX_WAIT_LOOP), // no, repeat to LABEL_PORT1_RX_WAIT_LOOP
933
934 // R3 is no more timeout counter, but variables base
935 I_MOVI(R3, 0),
936
937 // update last bit received
938 I_MOVR(R2, R0), // R2 = R0
939
940 // is CLK high?
941 M_BGE(LABEL_PORT1_RX_CLK_IS_HIGH, 1),
942
943M_LABEL(LABEL_PORT1_RX_CLK_IS_LOW),
944
945 // CLK is Low, capture DAT value
946 READ_DAT(PS2_PORT1), // R0 = DAT
947
948 // merge with data word and shift right by 1 the received word
949 I_LSHI(R0, R0, 11), // R0 = R0 << 11
950 I_LD(R1, R3, RTCMEM_PORT1_DATAIN), // R1 = [RTCMEM_PORT1_DATAIN]
951 I_ORR(R1, R1, R0), // R1 = R1 | R0
952 I_RSHI(R1, R1, 1), // R1 = R1 >> 1
953 I_ST(R1, R3, RTCMEM_PORT1_DATAIN), // [RTCMEM_PORT1_DATAIN] = R1
954
955 // repeat
956 M_BX(LABEL_PORT1_RX_WAIT_LOOP),
957
958M_LABEL(LABEL_PORT1_RX_CLK_IS_HIGH),
959
960 // increment bit count
961 I_STAGEINCI(1), // STAGE = STAGE + 1
962
963 // end of word? if not get another bit
964 M_STAGEBL(LABEL_PORT1_RX_WAIT_LOOP, 11), // jump to LABEL_PORT1_RX_WAIT_LOOP if STAGE < 11
965
966 // End of word, disable port 1
967 PERM_PORT1_DISABLE(),
968
969 // set RX flag
970 I_MOVI(R0, 1),
971 I_ST(R0, R3, RTCMEM_PORT1_RX), // [RTCMEM_PORT1_RX] = 1
972
973 // re-enable port 0?
974 TEMP_PORT0_ENABLE(),
975
976 // don't check RTC_CNTL_RDY_FOR_WAKEUP_S of RTC_CNTL_LOW_POWER_ST_REG because it never gets active, due
977 // the fact ULP never calls HALT or SoC is always active.
978 I_WAKE(),
979
980 // from now R2 is used as CPU re-wake timeout
981 I_MOVI(R2, 0),
982
983 // return to main loop
984 M_BX(LABEL_WAIT_COMMAND),
985
986};
987
988
989
990
991// Allowed GPIOs: GPIO_NUM_0, GPIO_NUM_2, GPIO_NUM_4, GPIO_NUM_12, GPIO_NUM_13, GPIO_NUM_14, GPIO_NUM_15, GPIO_NUM_25, GPIO_NUM_26, GPIO_NUM_27, GPIO_NUM_32, GPIO_NUM_33
992// Not allowed from GPIO_NUM_34 to GPIO_NUM_39
993// prg_start in 32 bit words
994// size in 32 bit words
995static void replace_placeholders(uint32_t prg_start, int size, bool port0Enabled, gpio_num_t port0_clkGPIO, gpio_num_t port0_datGPIO, bool port1Enabled, gpio_num_t port1_clkGPIO, gpio_num_t port1_datGPIO)
996{
997 uint32_t CLK_rtc_gpio_num[2], CLK_rtc_gpio_reg[2], CLK_rtc_gpio_ie_s[2];
998 uint32_t DAT_rtc_gpio_num[2], DAT_rtc_gpio_reg[2], DAT_rtc_gpio_ie_s[2];
999
1000 if (port0Enabled) {
1001 #if FABGL_ESP_IDF_VERSION <= FABGL_ESP_IDF_VERSION_VAL(3, 3, 5)
1002 CLK_rtc_gpio_num[0] = (uint32_t) rtc_gpio_desc[port0_clkGPIO].rtc_num;
1003 CLK_rtc_gpio_reg[0] = rtc_gpio_desc[port0_clkGPIO].reg;
1004 CLK_rtc_gpio_ie_s[0] = (uint32_t) ffs(rtc_gpio_desc[port0_clkGPIO].ie) - 1;
1005 DAT_rtc_gpio_num[0] = (uint32_t) rtc_gpio_desc[port0_datGPIO].rtc_num;
1006 DAT_rtc_gpio_reg[0] = rtc_gpio_desc[port0_datGPIO].reg;
1007 DAT_rtc_gpio_ie_s[0] = (uint32_t) ffs(rtc_gpio_desc[port0_datGPIO].ie) - 1;
1008 #else
1009 int port0_clkIO = rtc_io_number_get(port0_clkGPIO);
1010 CLK_rtc_gpio_num[0] = port0_clkIO;
1011 CLK_rtc_gpio_reg[0] = rtc_io_desc[port0_clkIO].reg;
1012 CLK_rtc_gpio_ie_s[0] = (uint32_t) ffs(rtc_io_desc[port0_clkIO].ie) - 1;
1013 int port0_datIO = rtc_io_number_get(port0_datGPIO);
1014 DAT_rtc_gpio_num[0] = port0_datIO;
1015 DAT_rtc_gpio_reg[0] = rtc_io_desc[port0_datIO].reg;
1016 DAT_rtc_gpio_ie_s[0] = (uint32_t) ffs(rtc_io_desc[port0_datIO].ie) - 1;
1017 #endif
1018 }
1019
1020 if (port1Enabled) {
1021 #if FABGL_ESP_IDF_VERSION <= FABGL_ESP_IDF_VERSION_VAL(3, 3, 5)
1022 CLK_rtc_gpio_num[1] = (uint32_t) rtc_gpio_desc[port1_clkGPIO].rtc_num;
1023 CLK_rtc_gpio_reg[1] = rtc_gpio_desc[port1_clkGPIO].reg;
1024 CLK_rtc_gpio_ie_s[1] = (uint32_t) ffs(rtc_gpio_desc[port1_clkGPIO].ie) - 1;
1025 DAT_rtc_gpio_num[1] = (uint32_t) rtc_gpio_desc[port1_datGPIO].rtc_num;
1026 DAT_rtc_gpio_reg[1] = rtc_gpio_desc[port1_datGPIO].reg;
1027 DAT_rtc_gpio_ie_s[1] = (uint32_t) ffs(rtc_gpio_desc[port1_datGPIO].ie) - 1;
1028 #else
1029 int port1_clkIO = rtc_io_number_get(port1_clkGPIO);
1030 CLK_rtc_gpio_num[1] = port1_clkIO;
1031 CLK_rtc_gpio_reg[1] = rtc_io_desc[port1_clkIO].reg;
1032 CLK_rtc_gpio_ie_s[1] = (uint32_t) ffs(rtc_io_desc[port1_clkIO].ie) - 1;
1033 int port1_datIO = rtc_io_number_get(port1_datGPIO);
1034 DAT_rtc_gpio_num[1] = port1_datIO;
1035 DAT_rtc_gpio_reg[1] = rtc_io_desc[port1_datIO].reg;
1036 DAT_rtc_gpio_ie_s[1] = (uint32_t) ffs(rtc_io_desc[port1_datIO].ie) - 1;
1037 #endif
1038 }
1039
1040 for (uint32_t i = 0; i < size; ++i) {
1041 ulp_insn_t * ins = (ulp_insn_t *) RTC_SLOW_MEM + i;
1042 if (ins->macro.opcode == OPCODE_PLACEHOLDER) {
1043 int ps2port = ins->macro.unused;
1044 if ((port0Enabled && ps2port == 0) || (port1Enabled && ps2port == 1)) {
1045 ins->macro.unused = 0;
1046 switch (ins->macro.sub_opcode) {
1047 case SUB_OPCODE_DAT_ENABLE_OUTPUT:
1048 if (ins->macro.label)
1049 *ins = (ulp_insn_t) I_WR_REG_BIT(RTC_GPIO_ENABLE_W1TS_REG, DAT_rtc_gpio_num[ps2port] + RTC_GPIO_ENABLE_W1TS_S, 1);
1050 else
1051 *ins = (ulp_insn_t) I_WR_REG_BIT(RTC_GPIO_ENABLE_W1TC_REG, DAT_rtc_gpio_num[ps2port] + RTC_GPIO_ENABLE_W1TC_S, 1);
1052 break;
1053 case SUB_OPCODE_DAT_ENABLE_INPUT:
1054 *ins = (ulp_insn_t) I_WR_REG_BIT(DAT_rtc_gpio_reg[ps2port], DAT_rtc_gpio_ie_s[ps2port], ins->macro.label);
1055 break;
1056 case SUB_OPCODE_CLK_ENABLE_OUTPUT:
1057 if (ins->macro.label)
1058 *ins = (ulp_insn_t) I_WR_REG_BIT(RTC_GPIO_ENABLE_W1TS_REG, CLK_rtc_gpio_num[ps2port] + RTC_GPIO_ENABLE_W1TS_S, 1);
1059 else
1060 *ins = (ulp_insn_t) I_WR_REG_BIT(RTC_GPIO_ENABLE_W1TC_REG, CLK_rtc_gpio_num[ps2port] + RTC_GPIO_ENABLE_W1TC_S, 1);
1061 break;
1062 case SUB_OPCODE_CLK_ENABLE_INPUT:
1063 *ins = (ulp_insn_t) I_WR_REG_BIT(CLK_rtc_gpio_reg[ps2port], CLK_rtc_gpio_ie_s[ps2port], ins->macro.label);
1064 break;
1065 case SUB_OPCODE_READ_CLK:
1066 *ins = (ulp_insn_t) I_RD_REG(RTC_GPIO_IN_REG, CLK_rtc_gpio_num[ps2port] + RTC_GPIO_IN_NEXT_S, CLK_rtc_gpio_num[ps2port] + RTC_GPIO_IN_NEXT_S);
1067 break;
1068 case SUB_OPCODE_READ_DAT:
1069 *ins = (ulp_insn_t) I_RD_REG(RTC_GPIO_IN_REG, DAT_rtc_gpio_num[ps2port] + RTC_GPIO_IN_NEXT_S, DAT_rtc_gpio_num[ps2port] + RTC_GPIO_IN_NEXT_S);
1070 break;
1071 case SUB_OPCODE_WRITE_CLK:
1072 *ins = (ulp_insn_t) I_WR_REG_BIT(RTC_GPIO_OUT_REG, CLK_rtc_gpio_num[ps2port] + RTC_GPIO_IN_NEXT_S, ins->macro.label);
1073 break;
1074 case SUB_OPCODE_WRITE_DAT:
1075 *ins = (ulp_insn_t) I_WR_REG_BIT(RTC_GPIO_OUT_REG, DAT_rtc_gpio_num[ps2port] + RTC_GPIO_IN_NEXT_S, ins->macro.label);
1076 break;
1077 }
1078 }
1079 }
1080 }
1081}
1082
1083
1084PS2Controller * PS2Controller::s_instance = nullptr;
1085Keyboard * PS2Controller::s_keyboard = nullptr;
1086Mouse * PS2Controller::s_mouse = nullptr;
1087bool PS2Controller::s_keyboardAllocated = false;
1088bool PS2Controller::s_mouseAllocated = false;
1089bool PS2Controller::s_portEnabled[2];
1090intr_handle_t PS2Controller::s_ULPWakeISRHandle;
1091bool PS2Controller::s_parityError[2];
1092bool PS2Controller::s_syncError[2];
1093bool PS2Controller::s_CLKTimeOutError[2];
1094QueueHandle_t PS2Controller::s_dataIn[2];
1095SemaphoreHandle_t PS2Controller::s_portLock[2];
1096bool PS2Controller::s_initDone = false;
1097
1098
1099PS2Controller::PS2Controller()
1100{
1101 if (!s_instance)
1102 s_instance = this;
1103}
1104
1105
1106PS2Controller::~PS2Controller()
1107{
1108 end();
1109 if (this == s_instance)
1110 s_instance = nullptr;
1111}
1112
1113
1114// Note: GPIO_UNUSED is a placeholder used to disable PS/2 port 1.
1115void PS2Controller::begin(gpio_num_t port0_clkGPIO, gpio_num_t port0_datGPIO, gpio_num_t port1_clkGPIO, gpio_num_t port1_datGPIO)
1116{
1117 // ULP stuff is always active, even when end() is called
1118 if (!s_initDone) {
1119
1120 s_portEnabled[0] = (port0_clkGPIO != GPIO_UNUSED && port0_datGPIO != GPIO_UNUSED);
1121 s_portEnabled[1] = (port1_clkGPIO != GPIO_UNUSED && port1_datGPIO != GPIO_UNUSED);
1122
1123 if (s_portEnabled[0]) {
1124 if (!rtc_gpio_is_valid_gpio(port0_clkGPIO) || !rtc_gpio_is_valid_gpio(port0_datGPIO)) {
1125 ESP_LOGE("FabGL", "Invalid PS/2 Port 0 pins");
1126 s_portEnabled[0] = false;
1127 } else {
1128 rtc_gpio_init(port0_clkGPIO);
1129 rtc_gpio_init(port0_datGPIO);
1130 }
1131 }
1132
1133 if (s_portEnabled[1]) {
1134 if (!rtc_gpio_is_valid_gpio(port1_clkGPIO) || !rtc_gpio_is_valid_gpio(port1_datGPIO)) {
1135 ESP_LOGE("FabGL", "Invalid PS/2 Port 1 pins");
1136 s_portEnabled[1] = false;
1137 } else {
1138 rtc_gpio_init(port1_clkGPIO);
1139 rtc_gpio_init(port1_datGPIO);
1140 }
1141 }
1142
1143 // clear ULP memory (without this may fail to run ULP on softreset)
1144 for (int i = RTCMEM_PROG_START; i < RTCMEM_LASTVAR; ++i)
1145 RTC_SLOW_MEM[i] = 0x0000;
1146
1147 // process, load and execute ULP program
1148 size_t size = sizeof(ULP_code) / sizeof(ulp_insn_t);
1149 ulp_process_macros_and_load_ex(RTCMEM_PROG_START, ULP_code, &size); // convert macros to ULP code
1150 replace_placeholders(RTCMEM_PROG_START, size, s_portEnabled[0], port0_clkGPIO, port0_datGPIO, s_portEnabled[1], port1_clkGPIO, port1_datGPIO); // replace GPIO placeholders
1151 //printf("prog size = %d\n", size);
1152 assert(size < RTCMEM_VARS_START && "ULP Program too long, increase RTCMEM_VARS_START");
1153
1154 REG_SET_FIELD(SENS_SAR_START_FORCE_REG, SENS_PC_INIT, RTCMEM_PROG_START); // set entry point
1155 SET_PERI_REG_MASK(SENS_SAR_START_FORCE_REG, SENS_ULP_CP_FORCE_START_TOP); // enable FORCE START
1156
1157 for (int p = 0; p < 2; ++p) {
1158 RTC_SLOW_MEM[RTCMEM_PORT0_TX + p] = 0;
1159 RTC_SLOW_MEM[RTCMEM_PORT0_RX_ENABLE + p] = 0;
1160 RTC_SLOW_MEM[RTCMEM_PORT0_RX_DISABLE + p] = 0;
1161 RTC_SLOW_MEM[RTCMEM_PORT0_RX + p] = 0;
1162 RTC_SLOW_MEM[RTCMEM_PORT0_RX_ENABLED + p] = 0;
1163 s_parityError[p] = s_syncError[p] = false;
1164 s_dataIn[p] = (s_portEnabled[p] ? xQueueCreate(1, sizeof(uint16_t)) : nullptr);
1165 s_portLock[p] = (s_portEnabled[p] ? xSemaphoreCreateRecursiveMutex() : nullptr);
1166 enableRX(p);
1167 }
1168
1169 // ULP start
1170 SET_PERI_REG_MASK(SENS_SAR_START_FORCE_REG, SENS_ULP_CP_START_TOP);
1171
1172 // install RTC interrupt handler (on ULP Wake() instruction)
1173 // note about ESP_INTR_FLAG_LEVEL2: this is necessary in order to work reliably with interrupt intensive VGATextController, when running on the same core
1174 // On some boards only core "1" can -read- RTC slow memory and receive interrupts, hence we have to force core 1.
1175 esp_intr_alloc_pinnedToCore(ETS_RTC_CORE_INTR_SOURCE, ESP_INTR_FLAG_LEVEL2, ULPWakeISR, nullptr, &s_ULPWakeISRHandle, 1);
1176 SET_PERI_REG_MASK(RTC_CNTL_INT_ENA_REG, RTC_CNTL_ULP_CP_INT_ENA);
1177
1178 s_initDone = true;
1179
1180 } else {
1181
1182 // ULP already initialized
1183 for (int p = 0; p < 2; ++p) {
1184 RTC_SLOW_MEM[RTCMEM_PORT0_TX + p] = 0;
1185 RTC_SLOW_MEM[RTCMEM_PORT0_RX_ENABLE + p] = 0;
1186 RTC_SLOW_MEM[RTCMEM_PORT0_RX_DISABLE + p] = 0;
1187 RTC_SLOW_MEM[RTCMEM_PORT0_RX + p] = 0;
1188 RTC_SLOW_MEM[RTCMEM_PORT0_RX_ENABLED + p] = 0;
1189 s_parityError[p] = s_syncError[p] = false;
1190 if (s_portEnabled[p]) {
1191 xQueueReset(s_dataIn[p]);
1192 xSemaphoreGiveRecursive(s_portLock[p]);
1193 enableRX(p);
1194 }
1195 }
1196 }
1197}
1198
1199
1200void PS2Controller::begin(PS2Preset preset, KbdMode keyboardMode)
1201{
1202 end();
1203
1204 bool generateVirtualKeys = (keyboardMode == KbdMode::GenerateVirtualKeys || keyboardMode == KbdMode::CreateVirtualKeysQueue);
1205 bool createVKQueue = (keyboardMode == KbdMode::CreateVirtualKeysQueue);
1206 switch (preset) {
1208 // both keyboard (port 0) and mouse configured (port 1)
1209 begin(GPIO_NUM_33, GPIO_NUM_32, GPIO_NUM_26, GPIO_NUM_27);
1210 setKeyboard(new Keyboard);
1211 keyboard()->begin(generateVirtualKeys, createVKQueue, 0);
1212 setMouse(new Mouse);
1213 mouse()->begin(1);
1214 s_keyboardAllocated = s_mouseAllocated = true;
1215 break;
1217 // both keyboard (port 1) and mouse configured (port 0)
1218 begin(GPIO_NUM_33, GPIO_NUM_32, GPIO_NUM_26, GPIO_NUM_27);
1219 setMouse(new Mouse);
1220 mouse()->begin(0);
1221 setKeyboard(new Keyboard);
1222 keyboard()->begin(generateVirtualKeys, createVKQueue, 1);
1223 s_keyboardAllocated = s_mouseAllocated = true;
1224 break;
1226 // only keyboard configured on port 0
1227 // this will call setKeyboard and begin()
1228 (new Keyboard)->begin(GPIO_NUM_33, GPIO_NUM_32, generateVirtualKeys, createVKQueue);
1229 s_keyboardAllocated = true;
1230 break;
1232 // only keyboard configured on port 1
1233 // this will call setKeyboard and begin()
1234 (new Keyboard)->begin(GPIO_NUM_26, GPIO_NUM_27, generateVirtualKeys, createVKQueue);
1235 s_keyboardAllocated = true;
1236 break;
1238 // only mouse configured on port 0
1239 // this will call setMouse and begin()
1240 (new Mouse)->begin(GPIO_NUM_33, GPIO_NUM_32);
1241 s_mouseAllocated = true;
1242 break;
1244 // only mouse configured on port 1
1245 // this will call setMouse and begin()
1246 (new Mouse)->begin(GPIO_NUM_26, GPIO_NUM_27);
1247 s_mouseAllocated = true;
1248 break;
1249 };
1250}
1251
1252
1253void PS2Controller::end()
1254{
1255 if (s_initDone) {
1256 if (s_keyboardAllocated)
1257 delete s_keyboard;
1258 s_keyboard = nullptr;
1259
1260 if (s_mouseAllocated)
1261 delete s_mouse;
1262 s_mouse = nullptr;
1263
1264 for (int p = 0; p < 2; ++p)
1265 disableRX(p);
1266 }
1267}
1268
1269
1271{
1272 if (s_portEnabled[PS2Port])
1273 RTC_SLOW_MEM[RTCMEM_PORT0_RX_DISABLE + PS2Port] = 1;
1274}
1275
1276
1278{
1279 if (s_portEnabled[PS2Port]) {
1280 // enable RX only if there is not data waiting
1281 if (!dataAvailable(PS2Port))
1282 RTC_SLOW_MEM[RTCMEM_PORT0_RX_ENABLE + PS2Port] = 1;
1283 }
1284}
1285
1286
1288{
1289 return uxQueueMessagesWaiting(s_dataIn[PS2Port]);
1290}
1291
1292
1293// return -1 when no data is available
1294int PS2Controller::getData(int PS2Port, int timeOutMS)
1295{
1296 int r = -1;
1297
1298 uint16_t w;
1299 if (xQueueReceive(s_dataIn[PS2Port], &w, msToTicks(timeOutMS))) {
1300
1301 // check CLK timeout, parity, start and stop bits
1302 s_CLKTimeOutError[PS2Port] = (w == 0xffff);
1303 if (!s_CLKTimeOutError[PS2Port]) {
1304 uint8_t startBit = w & 1;
1305 uint8_t stopBit = (w >> 10) & 1;
1306 uint8_t parity = (w >> 9) & 1;
1307 r = (w >> 1) & 0xff;
1308 s_parityError[PS2Port] = (parity != !calcParity(r));
1309 s_syncError[PS2Port] = (startBit != 0 || stopBit != 1);
1310 if (s_parityError[PS2Port] || s_syncError[PS2Port])
1311 r = -1;
1312 }
1313
1314 // ULP leaves RX disables whenever receives data or CLK timeout, so we need to enable it here
1315 RTC_SLOW_MEM[RTCMEM_PORT0_RX_ENABLE + PS2Port] = 1;
1316
1317 }
1318
1319 return r;
1320}
1321
1322
1323void PS2Controller::sendData(uint8_t data, int PS2Port)
1324{
1325 if (s_portEnabled[PS2Port]) {
1326 RTC_SLOW_MEM[RTCMEM_PORT0_DATAOUT + PS2Port] = 0x200 | ((!calcParity(data) & 1) << 8) | data; // 0x200 = stop bit. Start bit is not specified here.
1327 RTC_SLOW_MEM[RTCMEM_PORT0_TX + PS2Port] = 1;
1328 }
1329}
1330
1331
1332bool PS2Controller::lock(int PS2Port, int timeOutMS)
1333{
1334 return s_portEnabled[PS2Port] ? xSemaphoreTakeRecursive(s_portLock[PS2Port], msToTicks(timeOutMS)) : true;
1335}
1336
1337
1338void PS2Controller::unlock(int PS2Port)
1339{
1340 if (s_portEnabled[PS2Port])
1341 xSemaphoreGiveRecursive(s_portLock[PS2Port]);
1342}
1343
1344
1345
1346void IRAM_ATTR PS2Controller::ULPWakeISR(void * arg)
1347{
1348 uint32_t rtc_intr = READ_PERI_REG(RTC_CNTL_INT_ST_REG);
1349
1350 if (rtc_intr & RTC_CNTL_SAR_INT_ST) {
1351
1352 for (int p = 0; p < 2; ++p) {
1353 if (RTC_SLOW_MEM[RTCMEM_PORT0_RX + p] & 0xffff) {
1354 // RX
1355 uint16_t d = RTC_SLOW_MEM[RTCMEM_PORT0_DATAIN + p] & 0xffff;
1356 xQueueOverwriteFromISR(PS2Controller::s_dataIn[p], &d, nullptr);
1357 RTC_SLOW_MEM[RTCMEM_PORT0_RX + p] = 0;
1358 } else if (RTC_SLOW_MEM[RTCMEM_PORT0_RX_CLK_TIMEOUT + p] & 0xffff) {
1359 // CLK timeout
1360 uint16_t d = 0xffff;
1361 xQueueOverwriteFromISR(PS2Controller::s_dataIn[p], &d, nullptr);
1362 RTC_SLOW_MEM[RTCMEM_PORT0_RX_CLK_TIMEOUT + p] = 0;
1363 }
1364 }
1365
1366 }
1367
1368 // clear interrupt
1369 WRITE_PERI_REG(RTC_CNTL_INT_CLR_REG, rtc_intr);
1370}
1371
1372
1373} // end of namespace
1374
1375
void begin(gpio_num_t clkGPIO, gpio_num_t dataGPIO, bool generateVirtualKeys=true, bool createVKQueue=true)
Initializes Keyboard specifying CLOCK and DATA GPIOs.
Definition: keyboard.cpp:92
The PS2 Keyboard controller class.
Definition: keyboard.h:77
void begin(gpio_num_t clkGPIO, gpio_num_t dataGPIO)
Initializes Mouse specifying CLOCK and DATA GPIOs.
Definition: mouse.cpp:83
The PS2 Mouse controller class.
Definition: mouse.h:111
static Mouse * mouse()
Returns the instance of Mouse object automatically created by PS2Controller.
static void sendData(uint8_t data, int PS2Port)
Sends a command to the device.
static bool lock(int PS2Port, int timeOutMS)
Gets exclusive access to the specified PS/2 port.
static void disableRX(int PS2Port)
Disables inputs from PS/2 port driving the CLK line Low.
static Keyboard * keyboard()
Returns the instance of Keyboard object automatically created by PS2Controller.
static void unlock(int PS2Port)
Releases port from exclusive access.
static void begin(gpio_num_t port0_clkGPIO, gpio_num_t port0_datGPIO, gpio_num_t port1_clkGPIO=GPIO_UNUSED, gpio_num_t port1_datGPIO=GPIO_UNUSED)
Initializes PS2 device controller.
static int getData(int PS2Port, int timeOutMS)
Gets a scancode from the queue.
static bool dataAvailable(int PS2Port)
Determines if one byte has been received from the specified port.
static void enableRX(int PS2Port)
Enables inputs from PS/2 port releasing CLK line.
uint8_t const * data
This file contains some utility classes and functions.
KbdMode
This enum defines how handle keyboard virtual keys.
Definition: ps2controller.h:65
PS2Preset
This enum defines what is connected to PS/2 ports.
Definition: ps2controller.h:52
This file contains fabgl::Keyboard definition.
This file contains fabgl::Mouse definition.
This file contains fabgl::PS2Controller definition.