~ruther/qmk_firmware

bcb6e23387290afb64712314bfb095f8d96c1a4e — JohSchneider 4 years ago 5bbc527
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>
M docs/feature_ps2_mouse.md => docs/feature_ps2_mouse.md +26 -2
@@ 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.


M tmk_core/protocol.mk => tmk_core/protocol.mk +2 -2
@@ 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


M tmk_core/protocol/ps2_interrupt.c => tmk_core/protocol/ps2_interrupt.c +80 -11
@@ 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
}

A tmk_core/protocol/ps2_io_chibios.c => tmk_core/protocol/ps2_io_chibios.c +55 -0
@@ 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);
}

M tmk_core/protocol/ps2_mouse.c => tmk_core/protocol/ps2_mouse.c +9 -5
@@ 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