Skip to content

Commit d0e75e6

Browse files
authored
Merge pull request #3714 from microDev1/ps2io-S2
ESP32S2: Support for PS/2-IO
2 parents 0836abb + 9dd1783 commit d0e75e6

File tree

6 files changed

+474
-2
lines changed

6 files changed

+474
-2
lines changed

ports/esp32s2/common-hal/ps2io/Ps2.c

Lines changed: 392 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,392 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2020 microDev
7+
*
8+
* Permission is hereby granted, free of charge, to any person obtaining a copy
9+
* of this software and associated documentation files (the "Software"), to deal
10+
* in the Software without restriction, including without limitation the rights
11+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
* copies of the Software, and to permit persons to whom the Software is
13+
* furnished to do so, subject to the following conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be included in
16+
* all copies or substantial portions of the Software.
17+
*
18+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24+
* THE SOFTWARE.
25+
*/
26+
27+
#include "common-hal/ps2io/Ps2.h"
28+
29+
#include "supervisor/port.h"
30+
#include "shared-bindings/microcontroller/__init__.h"
31+
32+
#define STATE_IDLE 0
33+
#define STATE_RECV 1
34+
#define STATE_RECV_PARITY 2
35+
#define STATE_RECV_STOP 3
36+
#define STATE_RECV_ERR 10
37+
38+
#define ERROR_STARTBIT 0x01
39+
#define ERROR_TIMEOUT 0x02
40+
#define ERROR_PARITY 0x04
41+
#define ERROR_STOPBIT 0x08
42+
#define ERROR_BUFFER 0x10
43+
44+
#define ERROR_TX_CLKLO 0x100
45+
#define ERROR_TX_CLKHI 0x200
46+
#define ERROR_TX_ACKDATA 0x400
47+
#define ERROR_TX_ACKCLK 0x800
48+
#define ERROR_TX_RTS 0x1000
49+
#define ERROR_TX_NORESP 0x2000
50+
51+
void ps2_reset(void) {
52+
gpio_uninstall_isr_service();
53+
}
54+
55+
static void delay_us(uint32_t t) {
56+
common_hal_mcu_delay_us(t);
57+
}
58+
59+
/* interrupt handling */
60+
61+
static void IRAM_ATTR ps2_interrupt_handler(void *self_in) {
62+
// Grab the current time first.
63+
uint64_t current_tick = port_get_raw_ticks(NULL);
64+
65+
ps2io_ps2_obj_t * self = self_in;
66+
int data_bit = gpio_get_level(self->data_pin) ? 1 : 0;
67+
68+
// test for timeout
69+
if (self->state != STATE_IDLE) {
70+
int64_t diff_ms = current_tick - self->last_raw_ticks;
71+
if (diff_ms > 1) { // a.k.a. > 1.001ms
72+
self->last_errors |= ERROR_TIMEOUT;
73+
self->state = STATE_IDLE;
74+
}
75+
}
76+
77+
self->last_raw_ticks = current_tick;
78+
79+
if (self->state == STATE_IDLE) {
80+
self->bits = 0;
81+
self->parity = false;
82+
self->bitcount = 0;
83+
self->state = STATE_RECV;
84+
if (data_bit) {
85+
// start bit should be 0
86+
self->last_errors |= ERROR_STARTBIT;
87+
self->state = STATE_RECV_ERR;
88+
} else {
89+
self->state = STATE_RECV;
90+
}
91+
92+
} else if (self->state == STATE_RECV) {
93+
if (data_bit) {
94+
self->bits |= data_bit << self->bitcount;
95+
self->parity = !self->parity;
96+
}
97+
++self->bitcount;
98+
if (self->bitcount >= 8) {
99+
self->state = STATE_RECV_PARITY;
100+
}
101+
102+
} else if (self->state == STATE_RECV_PARITY) {
103+
++self->bitcount;
104+
if (data_bit) {
105+
self->parity = !self->parity;
106+
}
107+
if (!self->parity) {
108+
self->last_errors |= ERROR_PARITY;
109+
self->state = STATE_RECV_ERR;
110+
} else {
111+
self->state = STATE_RECV_STOP;
112+
}
113+
114+
} else if (self->state == STATE_RECV_STOP) {
115+
++self->bitcount;
116+
if (! data_bit) {
117+
self->last_errors |= ERROR_STOPBIT;
118+
} else if (self->waiting_cmd_response) {
119+
self->cmd_response = self->bits;
120+
self->waiting_cmd_response = false;
121+
} else if (self->bufcount >= sizeof(self->buffer)) {
122+
self->last_errors |= ERROR_BUFFER;
123+
} else {
124+
self->buffer[self->bufposw] = self->bits;
125+
self->bufposw = (self->bufposw + 1) % sizeof(self->buffer);
126+
self->bufcount++;
127+
}
128+
self->state = STATE_IDLE;
129+
130+
} else if (self->state == STATE_RECV_ERR) {
131+
// just count the bits until idle
132+
if (++self->bitcount >= 10) {
133+
self->state = STATE_IDLE;
134+
}
135+
}
136+
}
137+
138+
static void enable_interrupt(ps2io_ps2_obj_t* self) {
139+
// turn on falling edge interrupt
140+
gpio_install_isr_service(ESP_INTR_FLAG_IRAM);
141+
gpio_set_intr_type(self->clk_pin, GPIO_INTR_NEGEDGE);
142+
gpio_isr_handler_add(self->clk_pin, ps2_interrupt_handler, (void*)self);
143+
}
144+
145+
static void disable_interrupt(ps2io_ps2_obj_t* self) {
146+
// turn off fallling edge interrupt
147+
gpio_isr_handler_remove(self->clk_pin);
148+
}
149+
150+
static void resume_interrupt(ps2io_ps2_obj_t* self) {
151+
self->state = STATE_IDLE;
152+
gpio_isr_handler_add(self->clk_pin, ps2_interrupt_handler, (void*)self);
153+
}
154+
155+
/* gpio handling */
156+
157+
static void clk_hi(ps2io_ps2_obj_t* self) {
158+
// external pull-up
159+
gpio_set_direction(self->clk_pin, GPIO_MODE_INPUT);
160+
}
161+
162+
static bool wait_clk_lo(ps2io_ps2_obj_t* self, uint32_t us) {
163+
clk_hi(self);
164+
delay_us(1);
165+
while (gpio_get_level(self->clk_pin) && us) {
166+
--us;
167+
delay_us(1);
168+
}
169+
return us;
170+
}
171+
172+
static bool wait_clk_hi(ps2io_ps2_obj_t* self, uint32_t us) {
173+
clk_hi(self);
174+
delay_us(1);
175+
while (!gpio_get_level(self->clk_pin) && us) {
176+
--us;
177+
delay_us(1);
178+
}
179+
return us;
180+
}
181+
182+
static void clk_lo(ps2io_ps2_obj_t* self) {
183+
gpio_set_direction(self->clk_pin, GPIO_MODE_OUTPUT);
184+
gpio_set_level(self->clk_pin, 0);
185+
}
186+
187+
static void data_hi(ps2io_ps2_obj_t* self) {
188+
// external pull-up
189+
gpio_set_direction(self->data_pin, GPIO_MODE_INPUT);
190+
}
191+
192+
static bool wait_data_lo(ps2io_ps2_obj_t* self, uint32_t us) {
193+
data_hi(self);
194+
delay_us(1);
195+
while (gpio_get_level(self->data_pin) && us) {
196+
--us;
197+
delay_us(1);
198+
}
199+
return us;
200+
}
201+
202+
static bool wait_data_hi(ps2io_ps2_obj_t* self, uint32_t us) {
203+
data_hi(self);
204+
delay_us(1);
205+
while (!gpio_get_level(self->data_pin) && us) {
206+
--us;
207+
delay_us(1);
208+
}
209+
return us;
210+
}
211+
212+
static void data_lo(ps2io_ps2_obj_t* self) {
213+
gpio_set_direction(self->data_pin, GPIO_MODE_OUTPUT);
214+
gpio_set_level(self->data_pin, 0);
215+
}
216+
217+
static void idle(ps2io_ps2_obj_t* self) {
218+
clk_hi(self);
219+
data_hi(self);
220+
}
221+
222+
static void inhibit(ps2io_ps2_obj_t* self) {
223+
clk_lo(self);
224+
data_hi(self);
225+
}
226+
227+
/* ps2io module functions */
228+
229+
void common_hal_ps2io_ps2_construct(ps2io_ps2_obj_t* self,
230+
const mcu_pin_obj_t* data_pin, const mcu_pin_obj_t* clk_pin) {
231+
self->clk_pin = (gpio_num_t)clk_pin->number;
232+
self->data_pin = (gpio_num_t)data_pin->number;
233+
self->state = STATE_IDLE;
234+
self->bufcount = 0;
235+
self->bufposr = 0;
236+
self->bufposw = 0;
237+
self->waiting_cmd_response = false;
238+
239+
idle(self);
240+
enable_interrupt(self);
241+
242+
claim_pin(clk_pin);
243+
claim_pin(data_pin);
244+
}
245+
246+
bool common_hal_ps2io_ps2_deinited(ps2io_ps2_obj_t* self) {
247+
return self->clk_pin == GPIO_NUM_MAX;
248+
}
249+
250+
void common_hal_ps2io_ps2_deinit(ps2io_ps2_obj_t* self) {
251+
if (common_hal_ps2io_ps2_deinited(self)) {
252+
return;
253+
}
254+
gpio_uninstall_isr_service();
255+
reset_pin_number(self->clk_pin);
256+
reset_pin_number(self->data_pin);
257+
self->clk_pin = GPIO_NUM_MAX;
258+
self->data_pin = GPIO_NUM_MAX;
259+
}
260+
261+
uint16_t common_hal_ps2io_ps2_get_len(ps2io_ps2_obj_t* self) {
262+
return self->bufcount;
263+
}
264+
265+
int16_t common_hal_ps2io_ps2_popleft(ps2io_ps2_obj_t* self) {
266+
common_hal_mcu_disable_interrupts();
267+
if (self->bufcount <= 0) {
268+
common_hal_mcu_enable_interrupts();
269+
return -1;
270+
}
271+
uint8_t b = self->buffer[self->bufposr];
272+
self->bufposr = (self->bufposr + 1) % sizeof(self->buffer);
273+
self->bufcount -= 1;
274+
common_hal_mcu_enable_interrupts();
275+
return b;
276+
}
277+
278+
uint16_t common_hal_ps2io_ps2_clear_errors(ps2io_ps2_obj_t* self) {
279+
common_hal_mcu_disable_interrupts();
280+
uint16_t errors = self->last_errors;
281+
self->last_errors = 0;
282+
common_hal_mcu_enable_interrupts();
283+
return errors;
284+
}
285+
286+
// Based upon TMK implementation of PS/2 protocol
287+
// https://github.com/tmk/tmk_keyboard/blob/master/tmk_core/protocol/ps2_interrupt.c
288+
289+
int16_t common_hal_ps2io_ps2_sendcmd(ps2io_ps2_obj_t* self, uint8_t b) {
290+
disable_interrupt(self);
291+
292+
/* terminate a transmission if we have */
293+
inhibit(self);
294+
delay_us(100);
295+
296+
/* RTS and start bit */
297+
data_lo(self);
298+
clk_hi(self);
299+
if (!wait_clk_lo(self, 10000)) {
300+
self->last_errors |= ERROR_TX_RTS;
301+
goto ERROR;
302+
}
303+
304+
bool parity = true;
305+
for (uint8_t i = 0; i < 8; i++) {
306+
delay_us(15);
307+
if (b & (1 << i)) {
308+
parity = !parity;
309+
data_hi(self);
310+
} else {
311+
data_lo(self);
312+
}
313+
if (!wait_clk_hi(self, 50)) {
314+
self->last_errors |= ERROR_TX_CLKHI;
315+
goto ERROR;
316+
}
317+
if (!wait_clk_lo(self, 50)) {
318+
self->last_errors |= ERROR_TX_CLKLO;
319+
goto ERROR;
320+
}
321+
}
322+
323+
/* Parity bit */
324+
delay_us(15);
325+
if (parity) {
326+
data_hi(self);
327+
} else {
328+
data_lo(self);
329+
}
330+
if (!wait_clk_hi(self, 50)) {
331+
self->last_errors |= ERROR_TX_CLKHI;
332+
goto ERROR;
333+
}
334+
if (!wait_clk_lo(self, 50)) {
335+
self->last_errors |= ERROR_TX_CLKLO;
336+
goto ERROR;
337+
}
338+
339+
/* Stop bit */
340+
delay_us(15);
341+
data_hi(self);
342+
343+
/* Ack */
344+
if (!wait_data_lo(self, 50)) {
345+
self->last_errors |= ERROR_TX_ACKDATA;
346+
goto ERROR;
347+
}
348+
if (!wait_clk_lo(self, 50)) {
349+
self->last_errors |= ERROR_TX_ACKCLK;
350+
goto ERROR;
351+
}
352+
353+
/* wait for idle state */
354+
if (!wait_clk_hi(self, 50)) {
355+
self->last_errors |= ERROR_TX_ACKCLK;
356+
goto ERROR;
357+
}
358+
if (!wait_data_hi(self, 50)) {
359+
self->last_errors |= ERROR_TX_ACKDATA;
360+
goto ERROR;
361+
}
362+
363+
/* Wait for response byte */
364+
self->waiting_cmd_response = true;
365+
idle(self);
366+
resume_interrupt(self);
367+
368+
for (int i = 0; i < 25; ++i) {
369+
delay_us(1000);
370+
common_hal_mcu_disable_interrupts();
371+
bool has_response = !self->waiting_cmd_response;
372+
uint8_t response = self->cmd_response;
373+
common_hal_mcu_enable_interrupts();
374+
375+
if (has_response) {
376+
return response;
377+
}
378+
}
379+
380+
/* No response */
381+
common_hal_mcu_disable_interrupts();
382+
self->waiting_cmd_response = false;
383+
self->last_errors |= ERROR_TX_NORESP;
384+
common_hal_mcu_enable_interrupts();
385+
return -1;
386+
387+
/* Other errors */
388+
ERROR:
389+
idle(self);
390+
resume_interrupt(self);
391+
return -1;
392+
}

0 commit comments

Comments
 (0)