Arm ps2 mouse interrupt (#6490)

* ps2_mouse on ARM: an interrupt-version of the ps2-mouse code ported to ARM/chibios

* ps2_mouse on ARM: link EXT callback-channel selection to the user defined PS2_LINE_CLOCK

* ps2_mouse on ARM: replace DELAY_X defines with hardware-agnostic wait_X

* ps2_mouse on ARM: replace chibios-specific defines for the pins/lines with defines from quantum/config_common.h

and drop the '_LINE' component from teh define name

* ps2_mouse on ARM: expose the software-intterupt port as a user editable define

* Update docs/feature_ps2_mouse.md

Co-Authored-By: Hugo van Kemenade <hugovk@users.noreply.github.com>

* Update feature_ps2_mouse.md

* use a define to deduce the PS_DATA_PORT instead

* reduce all-zero extcfg to oneliner

* ps2_mouse: use generic wait instead of avr-delay

* Update docs/feature_ps2_mouse.md

* ps2_mouse: changes for new chibios version

(17.6.0 -> 19.1.0)
replacing the legacy externa-interrupt driver with pal-callbacks

* ps2_mouse: use PLATFORM_KEY

Co-Authored-By: Joel Challis <git@zvecr.com>

* ps2_mouse: clang-format corrections

* ps2_mouse: add systemlocks

using the chibios equivalent to AVRs cli: chSys[Unl|L]ock

Co-authored-by: Johannes <you@example.com>
Co-authored-by: Hugo van Kemenade <hugovk@users.noreply.github.com>
Co-authored-by: Joel Challis <git@zvecr.com>
This commit is contained in:
JohSchneider 2021-08-05 21:51:24 +00:00 committed by GitHub
parent 5bbc527460
commit bcb6e23387
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 172 additions and 20 deletions

View file

@ -50,7 +50,7 @@ In your keyboard config.h:
#endif
```
## Interrupt Version :id=interrupt-version
### Interrupt Version (AVR/ATMega32u4) :id=interrupt-version-avr
The following example uses D2 for clock and D5 for data. You can use any INT or PCINT pin for clock, and any pin for data.
@ -88,7 +88,31 @@ In your keyboard config.h:
#endif
```
## USART Version :id=usart-version
### Interrupt Version (ARM chibios) :id=interrupt-version-chibios
Pretty much any two pins can be used for the (software) interrupt variant on ARM cores. The example below uses A8 for clock, and A9 for data.
In rules.mk:
```
PS2_MOUSE_ENABLE = yes
PS2_USE_INT = yes
```
In your keyboard config.h:
```c
#define PS2_CLOCK A8
#define PS2_DATA A9
```
And in the chibios specifig halconf.h:
```c
#define PAL_USE_CALLBACKS TRUE
```
### USART Version :id=usart-version
To use USART on the ATMega32u4, you have to use PD5 for clock and PD2 for data. If one of those are unavailable, you need to use interrupt version.

View file

@ -14,13 +14,13 @@ endif
ifeq ($(strip $(PS2_USE_INT)), yes)
SRC += protocol/ps2_interrupt.c
SRC += protocol/ps2_io_avr.c
SRC += protocol/ps2_io_$(PLATFORM_KEY).c
OPT_DEFS += -DPS2_USE_INT
endif
ifeq ($(strip $(PS2_USE_USART)), yes)
SRC += protocol/ps2_usart.c
SRC += protocol/ps2_io_avr.c
SRC += protocol/ps2_io_$(PLATFORM_KEY).c
OPT_DEFS += -DPS2_USE_USART
endif

View file

@ -40,11 +40,19 @@ POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdbool.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#if defined(__AVR__)
# include <avr/interrupt.h>
#elif defined(PROTOCOL_CHIBIOS) // TODO: or STM32 ?
// chibiOS headers
# include "ch.h"
# include "hal.h"
#endif
#include "ps2.h"
#include "ps2_io.h"
#include "print.h"
#include "wait.h"
#define WAIT(stat, us, err) \
do { \
@ -61,12 +69,30 @@ static inline void pbuf_enqueue(uint8_t data);
static inline bool pbuf_has_data(void);
static inline void pbuf_clear(void);
#if defined(PROTOCOL_CHIBIOS)
void ps2_interrupt_service_routine(void);
void palCallback(void *arg) { ps2_interrupt_service_routine(); }
# define PS2_INT_INIT() \
{ palSetLineMode(PS2_CLOCK, PAL_MODE_INPUT); } \
while (0)
# define PS2_INT_ON() \
{ \
palEnableLineEvent(PS2_CLOCK, PAL_EVENT_MODE_FALLING_EDGE); \
palSetLineCallback(PS2_CLOCK, palCallback, NULL); \
} \
while (0)
# define PS2_INT_OFF() \
{ palDisableLineEvent(PS2_CLOCK); } \
while (0)
#endif // PROTOCOL_CHIBIOS
void ps2_host_init(void) {
idle();
PS2_INT_INIT();
PS2_INT_ON();
// POR(150-2000ms) plus BAT(300-500ms) may take 2.5sec([3]p.20)
//_delay_ms(2500);
// wait_ms(2500);
}
uint8_t ps2_host_send(uint8_t data) {
@ -77,7 +103,7 @@ uint8_t ps2_host_send(uint8_t data) {
/* terminate a transmission if we have */
inhibit();
_delay_us(100); // 100us [4]p.13, [5]p.50
wait_us(100); // 100us [4]p.13, [5]p.50
/* 'Request to Send' and Start bit */
data_lo();
@ -86,7 +112,6 @@ uint8_t ps2_host_send(uint8_t data) {
/* Data bit[2-9] */
for (uint8_t i = 0; i < 8; i++) {
_delay_us(15);
if (data & (1 << i)) {
parity = !parity;
data_hi();
@ -98,7 +123,7 @@ uint8_t ps2_host_send(uint8_t data) {
}
/* Parity bit */
_delay_us(15);
wait_us(15);
if (parity) {
data_hi();
} else {
@ -108,7 +133,7 @@ uint8_t ps2_host_send(uint8_t data) {
WAIT(clock_lo, 50, 5);
/* Stop bit */
_delay_us(15);
wait_us(15);
data_hi();
/* Ack */
@ -132,7 +157,7 @@ uint8_t ps2_host_recv_response(void) {
// Command may take 25ms/20ms at most([5]p.46, [3]p.21)
uint8_t retry = 25;
while (retry-- && !pbuf_has_data()) {
_delay_ms(1);
wait_ms(1);
}
return pbuf_dequeue();
}
@ -148,7 +173,7 @@ uint8_t ps2_host_recv(void) {
}
}
ISR(PS2_INT_VECT) {
void ps2_interrupt_service_routine(void) {
static enum {
INIT,
START,
@ -218,6 +243,10 @@ RETURN:
return;
}
#if defined(__AVR__)
ISR(PS2_INT_VECT) { ps2_interrupt_service_routine(); }
#endif
/* send LED state to keyboard */
void ps2_host_set_led(uint8_t led) {
ps2_host_send(0xED);
@ -232,8 +261,13 @@ static uint8_t pbuf[PBUF_SIZE];
static uint8_t pbuf_head = 0;
static uint8_t pbuf_tail = 0;
static inline void pbuf_enqueue(uint8_t data) {
#if defined(__AVR__)
uint8_t sreg = SREG;
cli();
#elif defined(PROTOCOL_CHIBIOS)
chSysLockFromISR();
#endif
uint8_t next = (pbuf_head + 1) % PBUF_SIZE;
if (next != pbuf_tail) {
pbuf[pbuf_head] = data;
@ -241,31 +275,66 @@ static inline void pbuf_enqueue(uint8_t data) {
} else {
print("pbuf: full\n");
}
#if defined(__AVR__)
SREG = sreg;
#elif defined(PROTOCOL_CHIBIOS)
chSysUnlockFromISR();
#endif
}
static inline uint8_t pbuf_dequeue(void) {
uint8_t val = 0;
#if defined(__AVR__)
uint8_t sreg = SREG;
cli();
#elif defined(PROTOCOL_CHIBIOS)
chSysLock();
#endif
if (pbuf_head != pbuf_tail) {
val = pbuf[pbuf_tail];
pbuf_tail = (pbuf_tail + 1) % PBUF_SIZE;
}
#if defined(__AVR__)
SREG = sreg;
#elif defined(PROTOCOL_CHIBIOS)
chSysUnlock();
#endif
return val;
}
static inline bool pbuf_has_data(void) {
#if defined(__AVR__)
uint8_t sreg = SREG;
cli();
#elif defined(PROTOCOL_CHIBIOS)
chSysLock();
#endif
bool has_data = (pbuf_head != pbuf_tail);
SREG = sreg;
#if defined(__AVR__)
SREG = sreg;
#elif defined(PROTOCOL_CHIBIOS)
chSysUnlock();
#endif
return has_data;
}
static inline void pbuf_clear(void) {
#if defined(__AVR__)
uint8_t sreg = SREG;
cli();
#elif defined(PROTOCOL_CHIBIOS)
chSysLock();
#endif
pbuf_head = pbuf_tail = 0;
SREG = sreg;
#if defined(__AVR__)
SREG = sreg;
#elif defined(PROTOCOL_CHIBIOS)
chSysUnlock();
#endif
}

View file

@ -0,0 +1,55 @@
#include <stdbool.h>
#include "ps2_io.h"
// chibiOS headers
#include "ch.h"
#include "hal.h"
/* Check port settings for clock and data line */
#if !(defined(PS2_CLOCK))
# error "PS/2 clock setting is required in config.h"
#endif
#if !(defined(PS2_DATA))
# error "PS/2 data setting is required in config.h"
#endif
/*
* Clock
*/
void clock_init(void) {}
void clock_lo(void) {
palSetLineMode(PS2_CLOCK, PAL_MODE_OUTPUT_OPENDRAIN);
palWriteLine(PS2_CLOCK, PAL_LOW);
}
void clock_hi(void) {
palSetLineMode(PS2_CLOCK, PAL_MODE_OUTPUT_OPENDRAIN);
palWriteLine(PS2_CLOCK, PAL_HIGH);
}
bool clock_in(void) {
palSetLineMode(PS2_CLOCK, PAL_MODE_INPUT);
return palReadLine(PS2_CLOCK);
}
/*
* Data
*/
void data_init(void) {}
void data_lo(void) {
palSetLineMode(PS2_DATA, PAL_MODE_OUTPUT_OPENDRAIN);
palWriteLine(PS2_DATA, PAL_LOW);
}
void data_hi(void) {
palSetLineMode(PS2_DATA, PAL_MODE_OUTPUT_OPENDRAIN);
palWriteLine(PS2_DATA, PAL_HIGH);
}
bool data_in(void) {
palSetLineMode(PS2_DATA, PAL_MODE_INPUT);
return palReadLine(PS2_DATA);
}

View file

@ -16,9 +16,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdbool.h>
#include <avr/io.h>
#include <util/delay.h>
#if defined(__AVR__)
# include <avr/io.h>
#endif
#include "ps2_mouse.h"
#include "wait.h"
#include "host.h"
#include "timer.h"
#include "print.h"
@ -42,7 +46,7 @@ static inline void ps2_mouse_scroll_button_task(report_mouse_t *mouse_report);
void ps2_mouse_init(void) {
ps2_host_init();
_delay_ms(PS2_MOUSE_INIT_DELAY); // wait for powering up
wait_ms(PS2_MOUSE_INIT_DELAY); // wait for powering up
PS2_MOUSE_SEND(PS2_MOUSE_RESET, "ps2_mouse_init: sending reset");
@ -210,7 +214,7 @@ static inline void ps2_mouse_enable_scrolling(void) {
PS2_MOUSE_SEND(PS2_MOUSE_SET_SAMPLE_RATE, "Set sample rate");
PS2_MOUSE_SEND(80, "80");
PS2_MOUSE_SEND(PS2_MOUSE_GET_DEVICE_ID, "Finished enabling scroll wheel");
_delay_ms(20);
wait_ms(20);
}
#define PRESS_SCROLL_BUTTONS mouse_report->buttons |= (PS2_MOUSE_SCROLL_BTN_MASK)
@ -252,7 +256,7 @@ static inline void ps2_mouse_scroll_button_task(report_mouse_t *mouse_report) {
if (scroll_state == SCROLL_BTN && timer_elapsed(scroll_button_time) < PS2_MOUSE_SCROLL_BTN_SEND) {
PRESS_SCROLL_BUTTONS;
host_mouse_send(mouse_report);
_delay_ms(100);
wait_ms(100);
RELEASE_SCROLL_BUTTONS;
}
#endif