diff options
Diffstat (limited to 'drivers')
32 files changed, 6207 insertions, 646 deletions
| diff --git a/drivers/avr/i2c_master.c b/drivers/avr/i2c_master.c index b1e4885298..2773e00778 100644 --- a/drivers/avr/i2c_master.c +++ b/drivers/avr/i2c_master.c @@ -28,8 +28,14 @@  #    define F_SCL 400000UL  // SCL frequency  #endif +#ifndef I2C_START_RETRY_COUNT +#    define I2C_START_RETRY_COUNT 20 +#endif  // I2C_START_RETRY_COUNT +  #define TWBR_val (((F_CPU / F_SCL) - 16) / 2) +#define MAX(X, Y) ((X) > (Y) ? (X) : (Y)) +  void i2c_init(void) {      TWSR = 0; /* no prescaler */      TWBR = (uint8_t)TWBR_val; @@ -47,7 +53,7 @@ void i2c_init(void) {  #endif  } -i2c_status_t i2c_start(uint8_t address, uint16_t timeout) { +static i2c_status_t i2c_start_impl(uint8_t address, uint16_t timeout) {      // reset TWI control register      TWCR = 0;      // transmit START condition @@ -86,6 +92,17 @@ i2c_status_t i2c_start(uint8_t address, uint16_t timeout) {      return I2C_STATUS_SUCCESS;  } +i2c_status_t i2c_start(uint8_t address, uint16_t timeout) { +    // Retry i2c_start_impl a bunch times in case the remote side has interrupts disabled. +    uint16_t     timeout_timer = timer_read(); +    uint16_t     time_slice    = MAX(1, (timeout == (I2C_TIMEOUT_INFINITE)) ? 5 : (timeout / (I2C_START_RETRY_COUNT)));  // if it's infinite, wait 1ms between attempts, otherwise split up the entire timeout into the number of retries +    i2c_status_t status; +    do { +        status = i2c_start_impl(address, time_slice); +    } while ((status < 0) && ((timeout == I2C_TIMEOUT_INFINITE) || (timer_elapsed(timeout_timer) < timeout))); +    return status; +} +  i2c_status_t i2c_write(uint8_t data, uint16_t timeout) {      // load data into data register      TWDR = data; diff --git a/drivers/avr/i2c_slave.c b/drivers/avr/i2c_slave.c index 62a378165a..2907f164c0 100644 --- a/drivers/avr/i2c_slave.c +++ b/drivers/avr/i2c_slave.c @@ -17,6 +17,7 @@   * GitHub repository: https://github.com/g4lvanix/I2C-slave-lib   */ +#include <stddef.h>  #include <avr/io.h>  #include <util/twi.h>  #include <avr/interrupt.h> @@ -24,6 +25,12 @@  #include "i2c_slave.h" +#if defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS) +#    include "transactions.h" + +static volatile bool is_callback_executor = false; +#endif  // defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS) +  volatile uint8_t i2c_slave_reg[I2C_SLAVE_REG_COUNT];  static volatile uint8_t buffer_address; @@ -48,11 +55,14 @@ ISR(TWI_vect) {          case TW_SR_SLA_ACK:              // The device is now a slave receiver              slave_has_register_set = false; +#if defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS) +            is_callback_executor = false; +#endif  // defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)              break;          case TW_SR_DATA_ACK:              // This device is a slave receiver and has received data -            // First byte is the location then the bytes will be writen in buffer with auto-incriment +            // First byte is the location then the bytes will be writen in buffer with auto-increment              if (!slave_has_register_set) {                  buffer_address = TWDR; @@ -60,10 +70,25 @@ ISR(TWI_vect) {                      ack            = 0;                      buffer_address = 0;                  } -                slave_has_register_set = true;  // address has been receaved now fill in buffer +                slave_has_register_set = true;  // address has been received now fill in buffer + +#if defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS) +                // Work out if we're attempting to execute a callback +                is_callback_executor = buffer_address == split_transaction_table[I2C_EXECUTE_CALLBACK].initiator2target_offset; +#endif  // defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)              } else {                  i2c_slave_reg[buffer_address] = TWDR;                  buffer_address++; + +#if defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS) +                // If we're intending to execute a transaction callback, do so, as we've just received the transaction ID +                if (is_callback_executor) { +                    split_transaction_desc_t *trans = &split_transaction_table[split_shmem->transaction_id]; +                    if (trans->slave_callback) { +                        trans->slave_callback(trans->initiator2target_buffer_size, split_trans_initiator2target_buffer(trans), trans->target2initiator_buffer_size, split_trans_target2initiator_buffer(trans)); +                    } +                } +#endif  // defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)              }              break; diff --git a/drivers/avr/i2c_slave.h b/drivers/avr/i2c_slave.h index 1cd0625ef4..a8647c9da3 100644 --- a/drivers/avr/i2c_slave.h +++ b/drivers/avr/i2c_slave.h @@ -22,7 +22,18 @@  #pragma once -#define I2C_SLAVE_REG_COUNT 30 +#ifndef I2C_SLAVE_REG_COUNT + +#    if defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS) +#        include "transport.h" +#        define I2C_SLAVE_REG_COUNT sizeof(split_shared_memory_t) +#    else  // defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS) +#        define I2C_SLAVE_REG_COUNT 30 +#    endif  // defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS) + +#endif  // I2C_SLAVE_REG_COUNT + +_Static_assert(I2C_SLAVE_REG_COUNT < 256, "I2C target registers must be single byte");  extern volatile uint8_t i2c_slave_reg[I2C_SLAVE_REG_COUNT]; diff --git a/drivers/avr/serial.c b/drivers/avr/serial.c index 3647bee0d3..9a7345a53d 100644 --- a/drivers/avr/serial.c +++ b/drivers/avr/serial.c @@ -224,15 +224,8 @@  #    define SERIAL_DELAY_HALF2 (SERIAL_DELAY - SERIAL_DELAY / 2)  #    define SLAVE_INT_WIDTH_US 1 -#    ifndef SERIAL_USE_MULTI_TRANSACTION -#        define SLAVE_INT_RESPONSE_TIME SERIAL_DELAY -#    else -#        define SLAVE_INT_ACK_WIDTH_UNIT 2 -#        define SLAVE_INT_ACK_WIDTH 4 -#    endif - -static SSTD_t *Transaction_table      = NULL; -static uint8_t Transaction_table_size = 0; +#    define SLAVE_INT_ACK_WIDTH_UNIT 2 +#    define SLAVE_INT_ACK_WIDTH 4  inline static void serial_delay(void) ALWAYS_INLINE;  inline static void serial_delay(void) { _delay_us(SERIAL_DELAY); } @@ -259,16 +252,12 @@ inline static void serial_low(void) { writePinLow(SOFT_SERIAL_PIN); }  inline static void serial_high(void) ALWAYS_INLINE;  inline static void serial_high(void) { writePinHigh(SOFT_SERIAL_PIN); } -void soft_serial_initiator_init(SSTD_t *sstd_table, int sstd_table_size) { -    Transaction_table      = sstd_table; -    Transaction_table_size = (uint8_t)sstd_table_size; +void soft_serial_initiator_init(void) {      serial_output();      serial_high();  } -void soft_serial_target_init(SSTD_t *sstd_table, int sstd_table_size) { -    Transaction_table      = sstd_table; -    Transaction_table_size = (uint8_t)sstd_table_size; +void soft_serial_target_init(void) {      serial_input_with_pullup();      // Enable INT0-INT7 @@ -395,19 +384,14 @@ static inline uint8_t nibble_bits_count(uint8_t bits) {  // interrupt handle to be used by the target device  ISR(SERIAL_PIN_INTERRUPT) { -#    ifndef SERIAL_USE_MULTI_TRANSACTION -    serial_low(); -    serial_output(); -    SSTD_t *trans = Transaction_table; -#    else      // recive transaction table index      uint8_t tid, bits;      uint8_t pecount = 0;      sync_recv(); -    bits = serial_read_chunk(&pecount, 7); +    bits = serial_read_chunk(&pecount, 8);      tid  = bits >> 3; -    bits = (bits & 7) != nibble_bits_count(tid); -    if (bits || pecount > 0 || tid > Transaction_table_size) { +    bits = (bits & 7) != (nibble_bits_count(tid) & 7); +    if (bits || pecount > 0 || tid > NUM_TOTAL_TRANSACTIONS) {          return;      }      serial_delay_half1(); @@ -415,18 +399,22 @@ ISR(SERIAL_PIN_INTERRUPT) {      serial_high();  // response step1 low->high      serial_output();      _delay_sub_us(SLAVE_INT_ACK_WIDTH_UNIT * SLAVE_INT_ACK_WIDTH); -    SSTD_t *trans = &Transaction_table[tid]; +    split_transaction_desc_t *trans = &split_transaction_table[tid];      serial_low();  // response step2 ack high->low -#    endif + +    // If the transaction has a callback, we can execute it now +    if (trans->slave_callback) { +        trans->slave_callback(trans->initiator2target_buffer_size, split_trans_initiator2target_buffer(trans), trans->target2initiator_buffer_size, split_trans_target2initiator_buffer(trans)); +    }      // target send phase -    if (trans->target2initiator_buffer_size > 0) serial_send_packet((uint8_t *)trans->target2initiator_buffer, trans->target2initiator_buffer_size); +    if (trans->target2initiator_buffer_size > 0) serial_send_packet((uint8_t *)split_trans_target2initiator_buffer(trans), trans->target2initiator_buffer_size);      // target switch to input      change_sender2reciver();      // target recive phase      if (trans->initiator2target_buffer_size > 0) { -        if (serial_recive_packet((uint8_t *)trans->initiator2target_buffer, trans->initiator2target_buffer_size)) { +        if (serial_recive_packet((uint8_t *)split_trans_initiator2target_buffer(trans), trans->initiator2target_buffer_size)) {              *trans->status = TRANSACTION_ACCEPTED;          } else {              *trans->status = TRANSACTION_DATA_ERROR; @@ -448,14 +436,12 @@ ISR(SERIAL_PIN_INTERRUPT) {  //    TRANSACTION_NO_RESPONSE  //    TRANSACTION_DATA_ERROR  // this code is very time dependent, so we need to disable interrupts -#    ifndef SERIAL_USE_MULTI_TRANSACTION -int soft_serial_transaction(void) { -    SSTD_t *trans = Transaction_table; -#    else  int soft_serial_transaction(int sstd_index) { -    if (sstd_index > Transaction_table_size) return TRANSACTION_TYPE_ERROR; -    SSTD_t *trans = &Transaction_table[sstd_index]; -#    endif +    if (sstd_index > NUM_TOTAL_TRANSACTIONS) return TRANSACTION_TYPE_ERROR; +    split_transaction_desc_t *trans = &split_transaction_table[sstd_index]; + +    if (!trans->status) return TRANSACTION_TYPE_ERROR;  // not registered +      cli();      // signal to the target that we want to start a transaction @@ -463,27 +449,11 @@ int soft_serial_transaction(int sstd_index) {      serial_low();      _delay_us(SLAVE_INT_WIDTH_US); -#    ifndef SERIAL_USE_MULTI_TRANSACTION -    // wait for the target response -    serial_input_with_pullup(); -    _delay_us(SLAVE_INT_RESPONSE_TIME); - -    // check if the target is present -    if (serial_read_pin()) { -        // target failed to pull the line low, assume not present -        serial_output(); -        serial_high(); -        *trans->status = TRANSACTION_NO_RESPONSE; -        sei(); -        return TRANSACTION_NO_RESPONSE; -    } - -#    else      // send transaction table index      int tid = (sstd_index << 3) | (7 & nibble_bits_count(sstd_index));      sync_send();      _delay_sub_us(TID_SEND_ADJUST); -    serial_write_chunk(tid, 7); +    serial_write_chunk(tid, 8);      serial_delay_half1();      // wait for the target response (step1 low->high) @@ -504,12 +474,11 @@ int soft_serial_transaction(int sstd_index) {          }          _delay_sub_us(SLAVE_INT_ACK_WIDTH_UNIT);      } -#    endif      // initiator recive phase      // if the target is present syncronize with it      if (trans->target2initiator_buffer_size > 0) { -        if (!serial_recive_packet((uint8_t *)trans->target2initiator_buffer, trans->target2initiator_buffer_size)) { +        if (!serial_recive_packet((uint8_t *)split_trans_target2initiator_buffer(trans), trans->target2initiator_buffer_size)) {              serial_output();              serial_high();              *trans->status = TRANSACTION_DATA_ERROR; @@ -523,7 +492,7 @@ int soft_serial_transaction(int sstd_index) {      // initiator send phase      if (trans->initiator2target_buffer_size > 0) { -        serial_send_packet((uint8_t *)trans->initiator2target_buffer, trans->initiator2target_buffer_size); +        serial_send_packet((uint8_t *)split_trans_initiator2target_buffer(trans), trans->initiator2target_buffer_size);      }      // always, release the line when not in use @@ -534,9 +503,8 @@ int soft_serial_transaction(int sstd_index) {      return TRANSACTION_END;  } -#    ifdef SERIAL_USE_MULTI_TRANSACTION  int soft_serial_get_and_clean_status(int sstd_index) { -    SSTD_t *trans = &Transaction_table[sstd_index]; +    split_transaction_desc_t *trans = &split_transaction_table[sstd_index];      cli();      int retval     = *trans->status;      *trans->status = 0; @@ -544,8 +512,6 @@ int soft_serial_get_and_clean_status(int sstd_index) {      sei();      return retval;  } -#    endif -  #endif  // Helix serial.c history diff --git a/drivers/avr/serial.h b/drivers/avr/serial.h deleted file mode 100644 index 53e66cf905..0000000000 --- a/drivers/avr/serial.h +++ /dev/null @@ -1,62 +0,0 @@ -#pragma once - -#include <stdbool.h> - -// ///////////////////////////////////////////////////////////////// -// Need Soft Serial defines in config.h -// ///////////////////////////////////////////////////////////////// -// ex. -//  #define SOFT_SERIAL_PIN ??   // ?? = D0,D1,D2,D3,E6 -//  OPTIONAL: #define SELECT_SOFT_SERIAL_SPEED ? // ? = 1,2,3,4,5 -//                                               //  1: about 137kbps (default) -//                                               //  2: about 75kbps -//                                               //  3: about 39kbps -//                                               //  4: about 26kbps -//                                               //  5: about 20kbps -// -// //// USE simple API (using signle-type transaction function) -//   /* nothing */ -// //// USE flexible API (using multi-type transaction function) -//   #define SERIAL_USE_MULTI_TRANSACTION -// -// ///////////////////////////////////////////////////////////////// - -// Soft Serial Transaction Descriptor -typedef struct _SSTD_t { -    uint8_t *status; -    uint8_t  initiator2target_buffer_size; -    uint8_t *initiator2target_buffer; -    uint8_t  target2initiator_buffer_size; -    uint8_t *target2initiator_buffer; -} SSTD_t; -#define TID_LIMIT(table) (sizeof(table) / sizeof(SSTD_t)) - -// initiator is transaction start side -void soft_serial_initiator_init(SSTD_t *sstd_table, int sstd_table_size); -// target is interrupt accept side -void soft_serial_target_init(SSTD_t *sstd_table, int sstd_table_size); - -// initiator resullt -#define TRANSACTION_END 0 -#define TRANSACTION_NO_RESPONSE 0x1 -#define TRANSACTION_DATA_ERROR 0x2 -#define TRANSACTION_TYPE_ERROR 0x4 -#ifndef SERIAL_USE_MULTI_TRANSACTION -int soft_serial_transaction(void); -#else -int soft_serial_transaction(int sstd_index); -#endif - -// target status -// *SSTD_t.status has -//   initiator: -//       TRANSACTION_END -//    or TRANSACTION_NO_RESPONSE -//    or TRANSACTION_DATA_ERROR -//   target: -//       TRANSACTION_DATA_ERROR -//    or TRANSACTION_ACCEPTED -#define TRANSACTION_ACCEPTED 0x8 -#ifdef SERIAL_USE_MULTI_TRANSACTION -int soft_serial_get_and_clean_status(int sstd_index); -#endif diff --git a/drivers/awinic/aw20216.c b/drivers/awinic/aw20216.c new file mode 100644 index 0000000000..776653fa6c --- /dev/null +++ b/drivers/awinic/aw20216.c @@ -0,0 +1,182 @@ +/* Copyright 2021 Jasper Chan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#include "aw20216.h" +#include "spi_master.h" + +/* The AW20216 appears to be somewhat similar to the IS31FL743, although quite + * a few things are different, such as the command byte format and page ordering. + * The LED addresses start from 0x00 instead of 0x01. + */ +#define AWINIC_ID 0b1010 << 4 + +#define AW_PAGE_FUNCTION 0x00 << 1    // PG0, Function registers +#define AW_PAGE_PWM 0x01 << 1         // PG1, LED PWM control +#define AW_PAGE_SCALING 0x02 << 1     // PG2, LED current scaling control +#define AW_PAGE_PATCHOICE 0x03 << 1   // PG3, Pattern choice? +#define AW_PAGE_PWMSCALING 0x04 << 1  // PG4, LED PWM + Scaling control? + +#define AW_WRITE 0 +#define AW_READ 1 + +#define AW_REG_CONFIGURATION 0x00  // PG0 +#define AW_REG_GLOBALCURRENT 0x01  // PG0 + +// Default value of AW_REG_CONFIGURATION +// D7:D4 = 1011, SWSEL (SW1~SW12 active) +// D3 = 0?, reserved (apparently this should be 1 but it doesn't seem to matter) +// D2:D1 = 00, OSDE (open/short detection enable) +// D0 = 0, CHIPEN (write 1 to enable LEDs when hardware enable pulled high) +#define AW_CONFIG_DEFAULT 0b10110000 +#define AW_CHIPEN 1 + +#define AW_PWM_REGISTER_COUNT 216 + +#define AW_SPI_START(PIN) spi_start(PIN, false, 0, AW_SPI_DIVISOR) + +#ifndef AW_SCALING_MAX +#    define AW_SCALING_MAX 150 +#endif + +#ifndef AW_GLOBAL_CURRENT_MAX +#    define AW_GLOBAL_CURRENT_MAX 150 +#endif + +#ifndef DRIVER_1_CS +#    define DRIVER_1_CS B13 +#endif + +#ifndef DRIVER_1_EN +#    define DRIVER_1_EN C13 +#endif + +#ifndef AW_SPI_DIVISOR +#    define AW_SPI_DIVISOR 4 +#endif + +uint8_t g_spi_transfer_buffer[3] = {0}; +uint8_t g_pwm_buffer[DRIVER_COUNT][AW_PWM_REGISTER_COUNT]; +bool    g_pwm_buffer_update_required[DRIVER_COUNT] = {false}; + +bool AW20216_write_register(pin_t slave_pin, uint8_t page, uint8_t reg, uint8_t data) { +    // Do we need to call spi_stop() if this fails? +    if (!AW_SPI_START(slave_pin)) { +        return false; +    } + +    g_spi_transfer_buffer[0] = (AWINIC_ID | page | AW_WRITE); +    g_spi_transfer_buffer[1] = reg; +    g_spi_transfer_buffer[2] = data; + +    if (spi_transmit(g_spi_transfer_buffer, 3) != SPI_STATUS_SUCCESS) { +        spi_stop(); +        return false; +    } +    spi_stop(); +    return true; +} + +bool AW20216_init_scaling(void) { +    // Set constant current to the max, control brightness with PWM +    aw_led led; +    for (uint8_t i = 0; i < DRIVER_LED_TOTAL; i++) { +        led = g_aw_leds[i]; +        if (led.driver == 0) { +            AW20216_write_register(DRIVER_1_CS, AW_PAGE_SCALING, led.r, AW_SCALING_MAX); +            AW20216_write_register(DRIVER_1_CS, AW_PAGE_SCALING, led.g, AW_SCALING_MAX); +            AW20216_write_register(DRIVER_1_CS, AW_PAGE_SCALING, led.b, AW_SCALING_MAX); +        } +#ifdef DRIVER_2_CS +        else if (led.driver == 1) { +            AW20216_write_register(DRIVER_2_CS, AW_PAGE_SCALING, led.r, AW_SCALING_MAX); +            AW20216_write_register(DRIVER_2_CS, AW_PAGE_SCALING, led.g, AW_SCALING_MAX); +            AW20216_write_register(DRIVER_2_CS, AW_PAGE_SCALING, led.b, AW_SCALING_MAX); +        } +#endif +    } +    return true; +} + +bool AW20216_soft_enable(void) { +    AW20216_write_register(DRIVER_1_CS, AW_PAGE_FUNCTION, AW_REG_CONFIGURATION, AW_CONFIG_DEFAULT | AW_CHIPEN); +#ifdef DRIVER_2_CS +    AW20216_write_register(DRIVER_2_CS, AW_PAGE_FUNCTION, AW_REG_CONFIGURATION, AW_CONFIG_DEFAULT | AW_CHIPEN); +#endif +    return true; +} + +void AW20216_update_pwm(int index, uint8_t red, uint8_t green, uint8_t blue) { +    aw_led led = g_aw_leds[index]; +    if (led.driver == 0) { +        AW20216_write_register(DRIVER_1_CS, AW_PAGE_PWM, led.r, red); +        AW20216_write_register(DRIVER_1_CS, AW_PAGE_PWM, led.g, green); +        AW20216_write_register(DRIVER_1_CS, AW_PAGE_PWM, led.b, blue); +    } +#ifdef DRIVER_2_CS +    else if (led.driver == 1) { +        AW20216_write_register(DRIVER_2_CS, AW_PAGE_PWM, led.r, red); +        AW20216_write_register(DRIVER_2_CS, AW_PAGE_PWM, led.g, green); +        AW20216_write_register(DRIVER_2_CS, AW_PAGE_PWM, led.b, blue); +    } +#endif +    return; +} + +void AW20216_init(void) { +    // All LEDs should start with all scaling and PWM registers as off +    setPinOutput(DRIVER_1_EN); +    writePinHigh(DRIVER_1_EN); +    AW20216_write_register(DRIVER_1_CS, AW_PAGE_FUNCTION, AW_REG_GLOBALCURRENT, AW_GLOBAL_CURRENT_MAX); +#ifdef DRIVER_2_EN +    setPinOutput(DRIVER_2_EN); +    writePinHigh(DRIVER_2_EN); +    AW20216_write_register(DRIVER_2_CS, AW_PAGE_FUNCTION, AW_REG_GLOBALCURRENT, AW_GLOBAL_CURRENT_MAX); +#endif +    AW20216_init_scaling(); +    AW20216_soft_enable(); +    return; +} + +void AW20216_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) { +    aw_led led                               = g_aw_leds[index]; +    g_pwm_buffer[led.driver][led.r]          = red; +    g_pwm_buffer[led.driver][led.g]          = green; +    g_pwm_buffer[led.driver][led.b]          = blue; +    g_pwm_buffer_update_required[led.driver] = true; +    return; +} +void AW20216_set_color_all(uint8_t red, uint8_t green, uint8_t blue) { +    for (uint8_t i = 0; i < DRIVER_LED_TOTAL; i++) { +        AW20216_set_color(i, red, green, blue); +    } +    return; +} + +void AW20216_write_pwm_buffer(pin_t slave_pin, uint8_t buffer_idx) { +    AW_SPI_START(slave_pin); +    spi_write((AWINIC_ID | AW_PAGE_PWM | AW_WRITE)); +    spi_write(0); +    spi_transmit(g_pwm_buffer[buffer_idx], AW_PWM_REGISTER_COUNT); +    spi_stop(); +} + +void AW20216_update_pwm_buffers(void) { +    AW20216_write_pwm_buffer(DRIVER_1_CS, 0); +#ifdef DRIVER_2_CS +    AW20216_write_pwm_buffer(DRIVER_2_CS, 1); +#endif +    return; +} diff --git a/drivers/awinic/aw20216.h b/drivers/awinic/aw20216.h new file mode 100644 index 0000000000..9c6865cc82 --- /dev/null +++ b/drivers/awinic/aw20216.h @@ -0,0 +1,251 @@ +/* Copyright 2021 Jasper Chan (Gigahawk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <stdint.h> +#include <stdbool.h> + +typedef struct aw_led { +    uint8_t driver : 2; +    uint8_t r; +    uint8_t g; +    uint8_t b; +} aw_led; + +extern const aw_led g_aw_leds[DRIVER_LED_TOTAL]; + +void AW20216_init(void); +void AW20216_set_color(int index, uint8_t red, uint8_t green, uint8_t blue); +void AW20216_set_color_all(uint8_t red, uint8_t green, uint8_t blue); +void AW20216_update_pwm_buffers(void); + +#define CS1_SW1 0x00 +#define CS2_SW1 0x01 +#define CS3_SW1 0x02 +#define CS4_SW1 0x03 +#define CS5_SW1 0x04 +#define CS6_SW1 0x05 +#define CS7_SW1 0x06 +#define CS8_SW1 0x07 +#define CS9_SW1 0x08 +#define CS10_SW1 0x09 +#define CS11_SW1 0x0A +#define CS12_SW1 0x0B +#define CS13_SW1 0x0C +#define CS14_SW1 0x0D +#define CS15_SW1 0x0E +#define CS16_SW1 0x0F +#define CS17_SW1 0x10 +#define CS18_SW1 0x11 +#define CS1_SW2 0x12 +#define CS2_SW2 0x13 +#define CS3_SW2 0x14 +#define CS4_SW2 0x15 +#define CS5_SW2 0x16 +#define CS6_SW2 0x17 +#define CS7_SW2 0x18 +#define CS8_SW2 0x19 +#define CS9_SW2 0x1A +#define CS10_SW2 0x1B +#define CS11_SW2 0x1C +#define CS12_SW2 0x1D +#define CS13_SW2 0x1E +#define CS14_SW2 0x1F +#define CS15_SW2 0x20 +#define CS16_SW2 0x21 +#define CS17_SW2 0x22 +#define CS18_SW2 0x23 +#define CS1_SW3 0x24 +#define CS2_SW3 0x25 +#define CS3_SW3 0x26 +#define CS4_SW3 0x27 +#define CS5_SW3 0x28 +#define CS6_SW3 0x29 +#define CS7_SW3 0x2A +#define CS8_SW3 0x2B +#define CS9_SW3 0x2C +#define CS10_SW3 0x2D +#define CS11_SW3 0x2E +#define CS12_SW3 0x2F +#define CS13_SW3 0x30 +#define CS14_SW3 0x31 +#define CS15_SW3 0x32 +#define CS16_SW3 0x33 +#define CS17_SW3 0x34 +#define CS18_SW3 0x35 +#define CS1_SW4 0x36 +#define CS2_SW4 0x37 +#define CS3_SW4 0x38 +#define CS4_SW4 0x39 +#define CS5_SW4 0x3A +#define CS6_SW4 0x3B +#define CS7_SW4 0x3C +#define CS8_SW4 0x3D +#define CS9_SW4 0x3E +#define CS10_SW4 0x3F +#define CS11_SW4 0x40 +#define CS12_SW4 0x41 +#define CS13_SW4 0x42 +#define CS14_SW4 0x43 +#define CS15_SW4 0x44 +#define CS16_SW4 0x45 +#define CS17_SW4 0x46 +#define CS18_SW4 0x47 +#define CS1_SW5 0x48 +#define CS2_SW5 0x49 +#define CS3_SW5 0x4A +#define CS4_SW5 0x4B +#define CS5_SW5 0x4C +#define CS6_SW5 0x4D +#define CS7_SW5 0x4E +#define CS8_SW5 0x4F +#define CS9_SW5 0x50 +#define CS10_SW5 0x51 +#define CS11_SW5 0x52 +#define CS12_SW5 0x53 +#define CS13_SW5 0x54 +#define CS14_SW5 0x55 +#define CS15_SW5 0x56 +#define CS16_SW5 0x57 +#define CS17_SW5 0x58 +#define CS18_SW5 0x59 +#define CS1_SW6 0x5A +#define CS2_SW6 0x5B +#define CS3_SW6 0x5C +#define CS4_SW6 0x5D +#define CS5_SW6 0x5E +#define CS6_SW6 0x5F +#define CS7_SW6 0x60 +#define CS8_SW6 0x61 +#define CS9_SW6 0x62 +#define CS10_SW6 0x63 +#define CS11_SW6 0x64 +#define CS12_SW6 0x65 +#define CS13_SW6 0x66 +#define CS14_SW6 0x67 +#define CS15_SW6 0x68 +#define CS16_SW6 0x69 +#define CS17_SW6 0x6A +#define CS18_SW6 0x6B +#define CS1_SW7 0x6C +#define CS2_SW7 0x6D +#define CS3_SW7 0x6E +#define CS4_SW7 0x6F +#define CS5_SW7 0x70 +#define CS6_SW7 0x71 +#define CS7_SW7 0x72 +#define CS8_SW7 0x73 +#define CS9_SW7 0x74 +#define CS10_SW7 0x75 +#define CS11_SW7 0x76 +#define CS12_SW7 0x77 +#define CS13_SW7 0x78 +#define CS14_SW7 0x79 +#define CS15_SW7 0x7A +#define CS16_SW7 0x7B +#define CS17_SW7 0x7C +#define CS18_SW7 0x7D +#define CS1_SW8 0x7E +#define CS2_SW8 0x7F +#define CS3_SW8 0x80 +#define CS4_SW8 0x81 +#define CS5_SW8 0x82 +#define CS6_SW8 0x83 +#define CS7_SW8 0x84 +#define CS8_SW8 0x85 +#define CS9_SW8 0x86 +#define CS10_SW8 0x87 +#define CS11_SW8 0x88 +#define CS12_SW8 0x89 +#define CS13_SW8 0x8A +#define CS14_SW8 0x8B +#define CS15_SW8 0x8C +#define CS16_SW8 0x8D +#define CS17_SW8 0x8E +#define CS18_SW8 0x8F +#define CS1_SW9 0x90 +#define CS2_SW9 0x91 +#define CS3_SW9 0x92 +#define CS4_SW9 0x93 +#define CS5_SW9 0x94 +#define CS6_SW9 0x95 +#define CS7_SW9 0x96 +#define CS8_SW9 0x97 +#define CS9_SW9 0x98 +#define CS10_SW9 0x99 +#define CS11_SW9 0x9A +#define CS12_SW9 0x9B +#define CS13_SW9 0x9C +#define CS14_SW9 0x9D +#define CS15_SW9 0x9E +#define CS16_SW9 0x9F +#define CS17_SW9 0xA0 +#define CS18_SW9 0xA1 +#define CS1_SW10 0xA2 +#define CS2_SW10 0xA3 +#define CS3_SW10 0xA4 +#define CS4_SW10 0xA5 +#define CS5_SW10 0xA6 +#define CS6_SW10 0xA7 +#define CS7_SW10 0xA8 +#define CS8_SW10 0xA9 +#define CS9_SW10 0xAA +#define CS10_SW10 0xAB +#define CS11_SW10 0xAC +#define CS12_SW10 0xAD +#define CS13_SW10 0xAE +#define CS14_SW10 0xAF +#define CS15_SW10 0xB0 +#define CS16_SW10 0xB1 +#define CS17_SW10 0xB2 +#define CS18_SW10 0xB3 +#define CS1_SW11 0xB4 +#define CS2_SW11 0xB5 +#define CS3_SW11 0xB6 +#define CS4_SW11 0xB7 +#define CS5_SW11 0xB8 +#define CS6_SW11 0xB9 +#define CS7_SW11 0xBA +#define CS8_SW11 0xBB +#define CS9_SW11 0xBC +#define CS10_SW11 0xBD +#define CS11_SW11 0xBE +#define CS12_SW11 0xBF +#define CS13_SW11 0xC0 +#define CS14_SW11 0xC1 +#define CS15_SW11 0xC2 +#define CS16_SW11 0xC3 +#define CS17_SW11 0xC4 +#define CS18_SW11 0xC5 +#define CS1_SW12 0xC6 +#define CS2_SW12 0xC7 +#define CS3_SW12 0xC8 +#define CS4_SW12 0xC9 +#define CS5_SW12 0xCA +#define CS6_SW12 0xCB +#define CS7_SW12 0xCC +#define CS8_SW12 0xCD +#define CS9_SW12 0xCE +#define CS10_SW12 0xCF +#define CS11_SW12 0xD0 +#define CS12_SW12 0xD1 +#define CS13_SW12 0xD2 +#define CS14_SW12 0xD3 +#define CS15_SW12 0xD4 +#define CS16_SW12 0xD5 +#define CS17_SW12 0xD6 +#define CS18_SW12 0xD7 diff --git a/drivers/chibios/serial.c b/drivers/chibios/serial.c index 54f7e1321f..f54fbcee4e 100644 --- a/drivers/chibios/serial.c +++ b/drivers/chibios/serial.c @@ -74,21 +74,12 @@ static THD_FUNCTION(Thread1, arg) {      }  } -static SSTD_t *Transaction_table      = NULL; -static uint8_t Transaction_table_size = 0; - -void soft_serial_initiator_init(SSTD_t *sstd_table, int sstd_table_size) { -    Transaction_table      = sstd_table; -    Transaction_table_size = (uint8_t)sstd_table_size; - +void soft_serial_initiator_init(void) {      serial_output();      serial_high();  } -void soft_serial_target_init(SSTD_t *sstd_table, int sstd_table_size) { -    Transaction_table      = sstd_table; -    Transaction_table_size = (uint8_t)sstd_table_size; - +void soft_serial_target_init(void) {      serial_input();      palEnablePadEvent(PAL_PORT(SOFT_SERIAL_PIN), PAL_PAD(SOFT_SERIAL_PIN), PAL_EVENT_MODE_FALLING_EDGE); @@ -154,16 +145,14 @@ void interrupt_handler(void *arg) {      uint8_t checksum_computed = 0;      int     sstd_index        = 0; -#ifdef SERIAL_USE_MULTI_TRANSACTION      sstd_index = serial_read_byte();      sync_send(); -#endif -    SSTD_t *trans = &Transaction_table[sstd_index]; +    split_transaction_desc_t *trans = &split_transaction_table[sstd_index];      for (int i = 0; i < trans->initiator2target_buffer_size; ++i) { -        trans->initiator2target_buffer[i] = serial_read_byte(); +        split_trans_initiator2target_buffer(trans)[i] = serial_read_byte();          sync_send(); -        checksum_computed += trans->initiator2target_buffer[i]; +        checksum_computed += split_trans_initiator2target_buffer(trans)[i];      }      checksum_computed ^= 7;      uint8_t checksum_received = serial_read_byte(); @@ -172,12 +161,17 @@ void interrupt_handler(void *arg) {      // wait for the sync to finish sending      serial_delay(); +    // Allow any slave processing to occur +    if (trans->slave_callback) { +        trans->slave_callback(trans->initiator2target_buffer_size, split_trans_initiator2target_buffer(trans), trans->target2initiator_buffer_size, split_trans_target2initiator_buffer(trans)); +    } +      uint8_t checksum = 0;      for (int i = 0; i < trans->target2initiator_buffer_size; ++i) { -        serial_write_byte(trans->target2initiator_buffer[i]); +        serial_write_byte(split_trans_target2initiator_buffer(trans)[i]);          sync_send();          serial_delay_half(); -        checksum += trans->target2initiator_buffer[i]; +        checksum += split_trans_target2initiator_buffer(trans)[i];      }      serial_write_byte(checksum ^ 7);      sync_send(); @@ -206,15 +200,10 @@ void interrupt_handler(void *arg) {  //    TRANSACTION_NO_RESPONSE  //    TRANSACTION_DATA_ERROR  // this code is very time dependent, so we need to disable interrupts -#ifndef SERIAL_USE_MULTI_TRANSACTION -int soft_serial_transaction(void) { -    int sstd_index = 0; -#else  int soft_serial_transaction(int sstd_index) { -#endif - -    if (sstd_index > Transaction_table_size) return TRANSACTION_TYPE_ERROR; -    SSTD_t *trans = &Transaction_table[sstd_index]; +    if (sstd_index > NUM_TOTAL_TRANSACTIONS) return TRANSACTION_TYPE_ERROR; +    split_transaction_desc_t *trans = &split_transaction_table[sstd_index]; +    if (!trans->status) return TRANSACTION_TYPE_ERROR;  // not registered      // TODO: remove extra delay between transactions      serial_delay(); @@ -244,14 +233,13 @@ int soft_serial_transaction(int sstd_index) {      uint8_t checksum = 0;      // send data to the slave -#ifdef SERIAL_USE_MULTI_TRANSACTION      serial_write_byte(sstd_index);  // first chunk is transaction id      sync_recv(); -#endif +      for (int i = 0; i < trans->initiator2target_buffer_size; ++i) { -        serial_write_byte(trans->initiator2target_buffer[i]); +        serial_write_byte(split_trans_initiator2target_buffer(trans)[i]);          sync_recv(); -        checksum += trans->initiator2target_buffer[i]; +        checksum += split_trans_initiator2target_buffer(trans)[i];      }      serial_write_byte(checksum ^ 7);      sync_recv(); @@ -262,9 +250,9 @@ int soft_serial_transaction(int sstd_index) {      // receive data from the slave      uint8_t checksum_computed = 0;      for (int i = 0; i < trans->target2initiator_buffer_size; ++i) { -        trans->target2initiator_buffer[i] = serial_read_byte(); +        split_trans_target2initiator_buffer(trans)[i] = serial_read_byte();          sync_recv(); -        checksum_computed += trans->target2initiator_buffer[i]; +        checksum_computed += split_trans_target2initiator_buffer(trans)[i];      }      checksum_computed ^= 7;      uint8_t checksum_received = serial_read_byte(); diff --git a/drivers/chibios/serial.h b/drivers/chibios/serial.h deleted file mode 100644 index 0c1857d52e..0000000000 --- a/drivers/chibios/serial.h +++ /dev/null @@ -1,62 +0,0 @@ -#pragma once - -#include <stdbool.h> - -// ///////////////////////////////////////////////////////////////// -// Need Soft Serial defines in config.h -// ///////////////////////////////////////////////////////////////// -// ex. -//  #define SOFT_SERIAL_PIN ??   // ?? = D0,D1,D2,D3,E6 -//  OPTIONAL: #define SELECT_SOFT_SERIAL_SPEED ? // ? = 1,2,3,4,5 -//                                               //  1: about 137kbps (default) -//                                               //  2: about 75kbps -//                                               //  3: about 39kbps -//                                               //  4: about 26kbps -//                                               //  5: about 20kbps -// -// //// USE simple API (using signle-type transaction function) -//   /* nothing */ -// //// USE flexible API (using multi-type transaction function) -//   #define SERIAL_USE_MULTI_TRANSACTION -// -// ///////////////////////////////////////////////////////////////// - -// Soft Serial Transaction Descriptor -typedef struct _SSTD_t { -    uint8_t *status; -    uint8_t  initiator2target_buffer_size; -    uint8_t *initiator2target_buffer; -    uint8_t  target2initiator_buffer_size; -    uint8_t *target2initiator_buffer; -} SSTD_t; -#define TID_LIMIT(table) (sizeof(table) / sizeof(SSTD_t)) - -// initiator is transaction start side -void soft_serial_initiator_init(SSTD_t *sstd_table, int sstd_table_size); -// target is interrupt accept side -void soft_serial_target_init(SSTD_t *sstd_table, int sstd_table_size); - -// initiator result -#define TRANSACTION_END 0 -#define TRANSACTION_NO_RESPONSE 0x1 -#define TRANSACTION_DATA_ERROR 0x2 -#define TRANSACTION_TYPE_ERROR 0x4 -#ifndef SERIAL_USE_MULTI_TRANSACTION -int soft_serial_transaction(void); -#else -int soft_serial_transaction(int sstd_index); -#endif - -// target status -// *SSTD_t.status has -//   initiator: -//       TRANSACTION_END -//    or TRANSACTION_NO_RESPONSE -//    or TRANSACTION_DATA_ERROR -//   target: -//       TRANSACTION_DATA_ERROR -//    or TRANSACTION_ACCEPTED -#define TRANSACTION_ACCEPTED 0x8 -#ifdef SERIAL_USE_MULTI_TRANSACTION -int soft_serial_get_and_clean_status(int sstd_index); -#endif diff --git a/drivers/chibios/serial_usart.c b/drivers/chibios/serial_usart.c index cae29388c3..ea4473791c 100644 --- a/drivers/chibios/serial_usart.c +++ b/drivers/chibios/serial_usart.c @@ -16,190 +16,300 @@  #include "serial_usart.h" -#ifndef USE_GPIOV1 -// The default PAL alternate modes are used to signal that the pins are used for USART -#    ifndef SERIAL_USART_TX_PAL_MODE -#        define SERIAL_USART_TX_PAL_MODE 7 +#if defined(SERIAL_USART_CONFIG) +static SerialConfig serial_config = SERIAL_USART_CONFIG; +#else +static SerialConfig serial_config = { +    .speed = (SERIAL_USART_SPEED), /* speed - mandatory */ +    .cr1   = (SERIAL_USART_CR1), +    .cr2   = (SERIAL_USART_CR2), +#    if !defined(SERIAL_USART_FULL_DUPLEX) +    .cr3   = ((SERIAL_USART_CR3) | USART_CR3_HDSEL) /* activate half-duplex mode */ +#    else +    .cr3 = (SERIAL_USART_CR3)  #    endif +};  #endif -#ifndef SERIAL_USART_DRIVER -#    define SERIAL_USART_DRIVER SD1 -#endif - -#ifdef SOFT_SERIAL_PIN -#    define SERIAL_USART_TX_PIN SOFT_SERIAL_PIN -#endif - -static inline msg_t sdWriteHalfDuplex(SerialDriver* driver, uint8_t* data, uint8_t size) { -    msg_t ret = sdWrite(driver, data, size); +static SerialDriver* serial_driver = &SERIAL_USART_DRIVER; -    // Half duplex requires us to read back the data we just wrote - just throw it away -    uint8_t dump[size]; -    sdRead(driver, dump, size); +static inline bool react_to_transactions(void); +static inline bool __attribute__((nonnull)) receive(uint8_t* destination, const size_t size); +static inline bool __attribute__((nonnull)) send(const uint8_t* source, const size_t size); +static inline int  initiate_transaction(uint8_t sstd_index); +static inline void usart_clear(void); -    return ret; +/** + * @brief Clear the receive input queue. + */ +static inline void usart_clear(void) { +    osalSysLock(); +    bool volatile queue_not_empty = !iqIsEmptyI(&serial_driver->iqueue); +    osalSysUnlock(); + +    while (queue_not_empty) { +        osalSysLock(); +        /* Hard reset the input queue. */ +        iqResetI(&serial_driver->iqueue); +        osalSysUnlock(); +        /* Allow pending interrupts to preempt. +         * Do not merge the lock/unlock blocks into one +         * or the code will not work properly. +         * The empty read adds a tiny amount of delay. */ +        (void)queue_not_empty; +        osalSysLock(); +        queue_not_empty = !iqIsEmptyI(&serial_driver->iqueue); +        osalSysUnlock(); +    }  } -#undef sdWrite -#define sdWrite sdWriteHalfDuplex -static inline msg_t sdWriteTimeoutHalfDuplex(SerialDriver* driver, uint8_t* data, uint8_t size, uint32_t timeout) { -    msg_t ret = sdWriteTimeout(driver, data, size, timeout); - -    // Half duplex requires us to read back the data we just wrote - just throw it away -    uint8_t dump[size]; -    sdReadTimeout(driver, dump, size, timeout); +/** + * @brief Blocking send of buffer with timeout. + * + * @return true Send success. + * @return false Send failed. + */ +static inline bool send(const uint8_t* source, const size_t size) { +    bool success = (size_t)sdWriteTimeout(serial_driver, source, size, TIME_MS2I(SERIAL_USART_TIMEOUT)) == size; + +#if !defined(SERIAL_USART_FULL_DUPLEX) +    if (success) { +        /* Half duplex fills the input queue with the data we wrote - just throw it away. +           Under the right circumstances (e.g. bad cables paired with high baud rates) +           less bytes can be present in the input queue, therefore a timeout is needed. */ +        uint8_t dump[size]; +        return receive(dump, size); +    } +#endif -    return ret; +    return success;  } -#undef sdWriteTimeout -#define sdWriteTimeout sdWriteTimeoutHalfDuplex -static inline void sdClear(SerialDriver* driver) { -    while (sdGetTimeout(driver, TIME_IMMEDIATE) != MSG_TIMEOUT) { -        // Do nothing with the data -    } +/** + * @brief  Blocking receive of size * bytes with timeout. + * + * @return true Receive success. + * @return false Receive failed. + */ +static inline bool receive(uint8_t* destination, const size_t size) { +    bool success = (size_t)sdReadTimeout(serial_driver, destination, size, TIME_MS2I(SERIAL_USART_TIMEOUT)) == size; +    return success;  } -static SerialConfig sdcfg = { -    (SERIAL_USART_SPEED),  // speed - mandatory -    (SERIAL_USART_CR1),    // CR1 -    (SERIAL_USART_CR2),    // CR2 -    (SERIAL_USART_CR3)     // CR3 -}; - -void handle_soft_serial_slave(void); +#if !defined(SERIAL_USART_FULL_DUPLEX) -/* - * This thread runs on the slave and responds to transactions initiated - * by the master +/** + * @brief Initiate pins for USART peripheral. Half-duplex configuration.   */ -static THD_WORKING_AREA(waSlaveThread, 2048); -static THD_FUNCTION(SlaveThread, arg) { -    (void)arg; -    chRegSetThreadName("slave_transport"); +__attribute__((weak)) void usart_init(void) { +#    if defined(MCU_STM32) +#        if defined(USE_GPIOV1) +    palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_STM32_ALTERNATE_OPENDRAIN); +#        else +    palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE(SERIAL_USART_TX_PAL_MODE) | PAL_STM32_OTYPE_OPENDRAIN); +#        endif -    while (true) { -        handle_soft_serial_slave(); -    } +#        if defined(USART_REMAP) +    USART_REMAP; +#        endif +#    else +#        pragma message "usart_init: MCU Familiy not supported by default, please supply your own init code by implementing usart_init() in your keyboard files." +#    endif  } -__attribute__((weak)) void usart_init(void) { -#if defined(USE_GPIOV1) -    palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_STM32_ALTERNATE_OPENDRAIN);  #else -    palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE(SERIAL_USART_TX_PAL_MODE) | PAL_STM32_OTYPE_OPENDRAIN); -#endif -#if defined(USART_REMAP) +/** + * @brief Initiate pins for USART peripheral. Full-duplex configuration. + */ +__attribute__((weak)) void usart_init(void) { +#    if defined(MCU_STM32) +#        if defined(USE_GPIOV1) +    palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_STM32_ALTERNATE_PUSHPULL); +    palSetLineMode(SERIAL_USART_RX_PIN, PAL_MODE_INPUT); +#        else +    palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE(SERIAL_USART_TX_PAL_MODE) | PAL_STM32_OTYPE_PUSHPULL | PAL_STM32_OSPEED_HIGHEST); +    palSetLineMode(SERIAL_USART_RX_PIN, PAL_MODE_ALTERNATE(SERIAL_USART_RX_PAL_MODE) | PAL_STM32_OTYPE_PUSHPULL | PAL_STM32_OSPEED_HIGHEST); +#        endif + +#        if defined(USART_REMAP)      USART_REMAP; -#endif +#        endif +#    else +#        pragma message "usart_init: MCU Familiy not supported by default, please supply your own init code by implementing usart_init() in your keyboard files." +#    endif  } -void usart_master_init(void) { -    usart_init(); +#endif -    sdcfg.cr3 |= USART_CR3_HDSEL; -    sdStart(&SERIAL_USART_DRIVER, &sdcfg); +/** + * @brief Overridable master specific initializations. + */ +__attribute__((weak, nonnull)) void usart_master_init(SerialDriver** driver) { +    (void)driver; +    usart_init();  } -void usart_slave_init(void) { +/** + * @brief Overridable slave specific initializations. + */ +__attribute__((weak, nonnull)) void usart_slave_init(SerialDriver** driver) { +    (void)driver;      usart_init(); +} -    sdcfg.cr3 |= USART_CR3_HDSEL; -    sdStart(&SERIAL_USART_DRIVER, &sdcfg); +/** + * @brief This thread runs on the slave and responds to transactions initiated + * by the master. + */ +static THD_WORKING_AREA(waSlaveThread, 1024); +static THD_FUNCTION(SlaveThread, arg) { +    (void)arg; +    chRegSetThreadName("usart_tx_rx"); -    // Start transport thread -    chThdCreateStatic(waSlaveThread, sizeof(waSlaveThread), HIGHPRIO, SlaveThread, NULL); +    while (true) { +        if (!react_to_transactions()) { +            /* Clear the receive queue, to start with a clean slate. +             * Parts of failed transactions or spurious bytes could still be in it. */ +            usart_clear(); +        } +    }  } -static SSTD_t* Transaction_table      = NULL; -static uint8_t Transaction_table_size = 0; +/** + * @brief Slave specific initializations. + */ +void soft_serial_target_init(void) { +    usart_slave_init(&serial_driver); -void soft_serial_initiator_init(SSTD_t* sstd_table, int sstd_table_size) { -    Transaction_table      = sstd_table; -    Transaction_table_size = (uint8_t)sstd_table_size; +    sdStart(serial_driver, &serial_config); -    usart_master_init(); +    /* Start transport thread. */ +    chThdCreateStatic(waSlaveThread, sizeof(waSlaveThread), HIGHPRIO, SlaveThread, NULL);  } -void soft_serial_target_init(SSTD_t* sstd_table, int sstd_table_size) { -    Transaction_table      = sstd_table; -    Transaction_table_size = (uint8_t)sstd_table_size; +/** + * @brief React to transactions started by the master. + */ +static inline bool react_to_transactions(void) { +    /* Wait until there is a transaction for us. */ +    uint8_t sstd_index = (uint8_t)sdGet(serial_driver); -    usart_slave_init(); -} +    /* Sanity check that we are actually responding to a valid transaction. */ +    if (sstd_index >= NUM_TOTAL_TRANSACTIONS) { +        return false; +    } -void handle_soft_serial_slave(void) { -    uint8_t sstd_index = sdGet(&SERIAL_USART_DRIVER);  // first chunk is always transaction id -    SSTD_t* trans      = &Transaction_table[sstd_index]; +    split_transaction_desc_t* trans = &split_transaction_table[sstd_index]; -    // Always write back the sstd_index as part of a basic handshake +    /* Send back the handshake which is XORed as a simple checksum, +     to signal that the slave is ready to receive possible transaction buffers  */      sstd_index ^= HANDSHAKE_MAGIC; -    sdWrite(&SERIAL_USART_DRIVER, &sstd_index, sizeof(sstd_index)); +    if (!send(&sstd_index, sizeof(sstd_index))) { +        *trans->status = TRANSACTION_DATA_ERROR; +        return false; +    } +    /* Receive transaction buffer from the master. If this transaction requires it.*/      if (trans->initiator2target_buffer_size) { -        sdRead(&SERIAL_USART_DRIVER, trans->initiator2target_buffer, trans->initiator2target_buffer_size); +        if (!receive(split_trans_initiator2target_buffer(trans), trans->initiator2target_buffer_size)) { +            *trans->status = TRANSACTION_DATA_ERROR; +            return false; +        }      } -    if (trans->target2initiator_buffer_size) { -        sdWrite(&SERIAL_USART_DRIVER, trans->target2initiator_buffer, trans->target2initiator_buffer_size); +    /* Allow any slave processing to occur. */ +    if (trans->slave_callback) { +        trans->slave_callback(trans->initiator2target_buffer_size, split_trans_initiator2target_buffer(trans), trans->initiator2target_buffer_size, split_trans_target2initiator_buffer(trans));      } -    if (trans->status) { -        *trans->status = TRANSACTION_ACCEPTED; +    /* Send transaction buffer to the master. If this transaction requires it. */ +    if (trans->target2initiator_buffer_size) { +        if (!send(split_trans_target2initiator_buffer(trans), trans->target2initiator_buffer_size)) { +            *trans->status = TRANSACTION_DATA_ERROR; +            return false; +        }      } + +    *trans->status = TRANSACTION_ACCEPTED; +    return true;  } -///////// -//  start transaction by initiator -// -// int  soft_serial_transaction(int sstd_index) -// -// Returns: -//    TRANSACTION_END -//    TRANSACTION_NO_RESPONSE -//    TRANSACTION_DATA_ERROR -#ifndef SERIAL_USE_MULTI_TRANSACTION -int soft_serial_transaction(void) { -    uint8_t sstd_index = 0; -#else -int soft_serial_transaction(int index) { -    uint8_t sstd_index = index; +/** + * @brief Master specific initializations. + */ +void soft_serial_initiator_init(void) { +    usart_master_init(&serial_driver); + +#if defined(MCU_STM32) && defined(SERIAL_USART_PIN_SWAP) +    serial_config.cr2 |= USART_CR2_SWAP;  // master has swapped TX/RX pins  #endif -    if (sstd_index > Transaction_table_size) return TRANSACTION_TYPE_ERROR; -    SSTD_t* trans = &Transaction_table[sstd_index]; -    msg_t   res   = 0; +    sdStart(serial_driver, &serial_config); +} + +/** + * @brief Start transaction from the master half to the slave half. + * + * @param index Transaction Table index of the transaction to start. + * @return int TRANSACTION_NO_RESPONSE in case of Timeout. + *             TRANSACTION_TYPE_ERROR in case of invalid transaction index. + *             TRANSACTION_END in case of success. + */ +int soft_serial_transaction(int index) { +    /* Clear the receive queue, to start with a clean slate. +     * Parts of failed transactions or spurious bytes could still be in it. */ +    usart_clear(); +    return initiate_transaction((uint8_t)index); +} + +/** + * @brief Initiate transaction to slave half. + */ +static inline int initiate_transaction(uint8_t sstd_index) { +    /* Sanity check that we are actually starting a valid transaction. */ +    if (sstd_index >= NUM_TOTAL_TRANSACTIONS) { +        dprintln("USART: Illegal transaction Id."); +        return TRANSACTION_TYPE_ERROR; +    } + +    split_transaction_desc_t* trans = &split_transaction_table[sstd_index]; -    sdClear(&SERIAL_USART_DRIVER); +    /* Transaction is not registered. Abort. */ +    if (!trans->status) { +        dprintln("USART: Transaction not registered."); +        return TRANSACTION_TYPE_ERROR; +    } -    // First chunk is always transaction id -    sdWriteTimeout(&SERIAL_USART_DRIVER, &sstd_index, sizeof(sstd_index), TIME_MS2I(SERIAL_USART_TIMEOUT)); +    /* Send transaction table index to the slave, which doubles as basic handshake token. */ +    if (!send(&sstd_index, sizeof(sstd_index))) { +        dprintln("USART: Send Handshake failed."); +        return TRANSACTION_TYPE_ERROR; +    }      uint8_t sstd_index_shake = 0xFF; -    // Which we always read back first so that we can error out correctly -    //   - due to the half duplex limitations on return codes, we always have to read *something* -    //   - without the read, write only transactions *always* succeed, even during the boot process where the slave is not ready -    res = sdReadTimeout(&SERIAL_USART_DRIVER, &sstd_index_shake, sizeof(sstd_index_shake), TIME_MS2I(SERIAL_USART_TIMEOUT)); -    if (res < 0 || (sstd_index_shake != (sstd_index ^ HANDSHAKE_MAGIC))) { -        dprintf("serial::usart_shake NO_RESPONSE\n"); +    /* Which we always read back first so that we can error out correctly. +     *   - due to the half duplex limitations on return codes, we always have to read *something*. +     *   - without the read, write only transactions *always* succeed, even during the boot process where the slave is not ready. +     */ +    if (!receive(&sstd_index_shake, sizeof(sstd_index_shake)) || (sstd_index_shake != (sstd_index ^ HANDSHAKE_MAGIC))) { +        dprintln("USART: Handshake failed.");          return TRANSACTION_NO_RESPONSE;      } +    /* Send transaction buffer to the slave. If this transaction requires it. */      if (trans->initiator2target_buffer_size) { -        res = sdWriteTimeout(&SERIAL_USART_DRIVER, trans->initiator2target_buffer, trans->initiator2target_buffer_size, TIME_MS2I(SERIAL_USART_TIMEOUT)); -        if (res < 0) { -            dprintf("serial::usart_transmit NO_RESPONSE\n"); +        if (!send(split_trans_initiator2target_buffer(trans), trans->initiator2target_buffer_size)) { +            dprintln("USART: Send failed.");              return TRANSACTION_NO_RESPONSE;          }      } +    /* Receive transaction buffer from the slave. If this transaction requires it. */      if (trans->target2initiator_buffer_size) { -        res = sdReadTimeout(&SERIAL_USART_DRIVER, trans->target2initiator_buffer, trans->target2initiator_buffer_size, TIME_MS2I(SERIAL_USART_TIMEOUT)); -        if (res < 0) { -            dprintf("serial::usart_receive NO_RESPONSE\n"); +        if (!receive(split_trans_target2initiator_buffer(trans), trans->target2initiator_buffer_size)) { +            dprintln("USART: Receive failed.");              return TRANSACTION_NO_RESPONSE;          }      } diff --git a/drivers/chibios/serial_usart.h b/drivers/chibios/serial_usart.h index fee7b4d159..c64e15566f 100644 --- a/drivers/chibios/serial_usart.h +++ b/drivers/chibios/serial_usart.h @@ -23,19 +23,45 @@  #include <ch.h>  #include <hal.h> -#ifndef USART_CR1_M0 +#if !defined(SERIAL_USART_DRIVER) +#    define SERIAL_USART_DRIVER SD1 +#endif + +#if !defined(USE_GPIOV1) +/* The default PAL alternate modes are used to signal that the pins are used for USART. */ +#    if !defined(SERIAL_USART_TX_PAL_MODE) +#        define SERIAL_USART_TX_PAL_MODE 7 +#    endif +#    if !defined(SERIAL_USART_RX_PAL_MODE) +#        define SERIAL_USART_RX_PAL_MODE 7 +#    endif +#endif + +#if defined(SOFT_SERIAL_PIN) +#    define SERIAL_USART_TX_PIN SOFT_SERIAL_PIN +#endif + +#if !defined(SERIAL_USART_TX_PIN) +#    define SERIAL_USART_TX_PIN A9 +#endif + +#if !defined(SERIAL_USART_RX_PIN) +#    define SERIAL_USART_RX_PIN A10 +#endif + +#if !defined(USART_CR1_M0)  #    define USART_CR1_M0 USART_CR1_M  // some platforms (f1xx) dont have this so  #endif -#ifndef SERIAL_USART_CR1 +#if !defined(SERIAL_USART_CR1)  #    define SERIAL_USART_CR1 (USART_CR1_PCE | USART_CR1_PS | USART_CR1_M0)  // parity enable, odd parity, 9 bit length  #endif -#ifndef SERIAL_USART_CR2 +#if !defined(SERIAL_USART_CR2)  #    define SERIAL_USART_CR2 (USART_CR2_STOP_1)  // 2 stop bits  #endif -#ifndef SERIAL_USART_CR3 +#if !defined(SERIAL_USART_CR3)  #    define SERIAL_USART_CR3 0  #endif @@ -61,11 +87,11 @@          } while (0)  #endif -#ifndef SELECT_SOFT_SERIAL_SPEED +#if !defined(SELECT_SOFT_SERIAL_SPEED)  #    define SELECT_SOFT_SERIAL_SPEED 1  #endif -#ifdef SERIAL_USART_SPEED +#if defined(SERIAL_USART_SPEED)  // Allow advanced users to directly set SERIAL_USART_SPEED  #elif SELECT_SOFT_SERIAL_SPEED == 0  #    define SERIAL_USART_SPEED 460800 @@ -83,7 +109,7 @@  #    error invalid SELECT_SOFT_SERIAL_SPEED value  #endif -#ifndef SERIAL_USART_TIMEOUT +#if !defined(SERIAL_USART_TIMEOUT)  #    define SERIAL_USART_TIMEOUT 100  #endif diff --git a/drivers/chibios/serial_usart_duplex.c b/drivers/chibios/serial_usart_duplex.c deleted file mode 100644 index cc9b889ac2..0000000000 --- a/drivers/chibios/serial_usart_duplex.c +++ /dev/null @@ -1,261 +0,0 @@ -/* Copyright 2021 QMK - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program.  If not, see <http://www.gnu.org/licenses/>. - */ - -#include "serial_usart.h" - -#include <stdatomic.h> - -#if !defined(USE_GPIOV1) -// The default PAL alternate modes are used to signal that the pins are used for USART -#    if !defined(SERIAL_USART_TX_PAL_MODE) -#        define SERIAL_USART_TX_PAL_MODE 7 -#    endif -#    if !defined(SERIAL_USART_RX_PAL_MODE) -#        define SERIAL_USART_RX_PAL_MODE 7 -#    endif -#endif - -#if !defined(SERIAL_USART_DRIVER) -#    define SERIAL_USART_DRIVER UARTD1 -#endif - -#if !defined(SERIAL_USART_TX_PIN) -#    define SERIAL_USART_TX_PIN A9 -#endif - -#if !defined(SERIAL_USART_RX_PIN) -#    define SERIAL_USART_RX_PIN A10 -#endif - -#define SIGNAL_HANDSHAKE_RECEIVED 0x1 - -void        handle_transactions_slave(uint8_t sstd_index); -static void receive_transaction_handshake(UARTDriver* uartp, uint16_t received_handshake); - -/* - * UART driver configuration structure. We use the blocking DMA enabled API and - * the rxchar callback to receive handshake tokens but only on the slave halve. - */ -// clang-format off -static UARTConfig uart_config = { -    .txend1_cb = NULL, -    .txend2_cb = NULL, -    .rxend_cb = NULL, -    .rxchar_cb = NULL, -    .rxerr_cb = NULL, -    .timeout_cb = NULL, -    .speed = (SERIAL_USART_SPEED), -    .cr1 = (SERIAL_USART_CR1), -    .cr2 = (SERIAL_USART_CR2), -    .cr3 = (SERIAL_USART_CR3) -}; -// clang-format on - -static SSTD_t*              Transaction_table      = NULL; -static uint8_t              Transaction_table_size = 0; -static atomic_uint_least8_t handshake              = 0xFF; -static thread_reference_t   tp_target              = NULL; - -/* - * This callback is invoked when a character is received but the application - * was not ready to receive it, the character is passed as parameter. - * Receive transaction table index from initiator, which doubles as basic handshake token. */ -static void receive_transaction_handshake(UARTDriver* uartp, uint16_t received_handshake) { -    /* Check if received handshake is not a valid transaction id. -     * Please note that we can still catch a seemingly valid handshake -     * i.e. a byte from a ongoing transfer which is in the allowed range. -     * So this check mainly prevents any obviously wrong handshakes and -     * subsequent wakeups of the receiving thread, which is a costly operation. */ -    if (received_handshake > Transaction_table_size) { -        return; -    } - -    handshake = (uint8_t)received_handshake; -    chSysLockFromISR(); -    /* Wakeup receiving thread to start a transaction. */ -    chEvtSignalI(tp_target, (eventmask_t)SIGNAL_HANDSHAKE_RECEIVED); -    chSysUnlockFromISR(); -} - -__attribute__((weak)) void usart_init(void) { -#if defined(USE_GPIOV1) -    palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_STM32_ALTERNATE_PUSHPULL); -    palSetLineMode(SERIAL_USART_RX_PIN, PAL_MODE_INPUT); -#else -    palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE(SERIAL_USART_TX_PAL_MODE) | PAL_STM32_OTYPE_PUSHPULL | PAL_STM32_OSPEED_HIGHEST); -    palSetLineMode(SERIAL_USART_RX_PIN, PAL_MODE_ALTERNATE(SERIAL_USART_RX_PAL_MODE) | PAL_STM32_OTYPE_PUSHPULL | PAL_STM32_OSPEED_HIGHEST); -#endif -} - -/* - * This thread runs on the slave half and reacts to transactions initiated from the master. - */ -static THD_WORKING_AREA(waSlaveThread, 1024); -static THD_FUNCTION(SlaveThread, arg) { -    (void)arg; -    chRegSetThreadName("slave_usart_tx_rx"); - -    while (true) { -        /* We sleep as long as there is no handshake waiting for us. */ -        chEvtWaitAny((eventmask_t)SIGNAL_HANDSHAKE_RECEIVED); -        handle_transactions_slave(handshake); -    } -} - -void soft_serial_target_init(SSTD_t* const sstd_table, int sstd_table_size) { -    Transaction_table      = sstd_table; -    Transaction_table_size = (uint8_t)sstd_table_size; -    usart_init(); - -#if defined(USART_REMAP) -    USART_REMAP; -#endif - -    tp_target = chThdCreateStatic(waSlaveThread, sizeof(waSlaveThread), HIGHPRIO, SlaveThread, NULL); - -    // Start receiving handshake tokens on slave halve -    uart_config.rxchar_cb = receive_transaction_handshake; -    uartStart(&SERIAL_USART_DRIVER, &uart_config); -} - -/** - * @brief React to transactions started by the master. - * This version uses duplex send and receive usart pheriphals and DMA backed transfers. - */ -void inline handle_transactions_slave(uint8_t sstd_index) { -    size_t  buffer_size = 0; -    msg_t   msg         = 0; -    SSTD_t* trans       = &Transaction_table[sstd_index]; - -    /* Send back the handshake which is XORed as a simple checksum, -     to signal that the slave is ready to receive possible transaction buffers  */ -    sstd_index ^= HANDSHAKE_MAGIC; -    buffer_size = (size_t)sizeof(sstd_index); -    msg         = uartSendTimeout(&SERIAL_USART_DRIVER, &buffer_size, &sstd_index, TIME_MS2I(SERIAL_USART_TIMEOUT)); - -    if (msg != MSG_OK) { -        if (trans->status) { -            *trans->status = TRANSACTION_NO_RESPONSE; -        } -        return; -    } - -    /* Receive transaction buffer from the master. If this transaction requires it.*/ -    buffer_size = (size_t)trans->initiator2target_buffer_size; -    if (buffer_size) { -        msg = uartReceiveTimeout(&SERIAL_USART_DRIVER, &buffer_size, trans->initiator2target_buffer, TIME_MS2I(SERIAL_USART_TIMEOUT)); -        if (msg != MSG_OK) { -            if (trans->status) { -                *trans->status = TRANSACTION_NO_RESPONSE; -            } -            return; -        } -    } - -    /* Send transaction buffer to the master. If this transaction requires it. */ -    buffer_size = (size_t)trans->target2initiator_buffer_size; -    if (buffer_size) { -        msg = uartSendFullTimeout(&SERIAL_USART_DRIVER, &buffer_size, trans->target2initiator_buffer, TIME_MS2I(SERIAL_USART_TIMEOUT)); -        if (msg != MSG_OK) { -            if (trans->status) { -                *trans->status = TRANSACTION_NO_RESPONSE; -            } -            return; -        } -    } - -    if (trans->status) { -        *trans->status = TRANSACTION_ACCEPTED; -    } -} - -void soft_serial_initiator_init(SSTD_t* const sstd_table, int sstd_table_size) { -    Transaction_table      = sstd_table; -    Transaction_table_size = (uint8_t)sstd_table_size; -    usart_init(); - -#if defined(SERIAL_USART_PIN_SWAP) -    uart_config.cr2 |= USART_CR2_SWAP;  // master has swapped TX/RX pins -#endif - -#if defined(USART_REMAP) -    USART_REMAP; -#endif - -    uartStart(&SERIAL_USART_DRIVER, &uart_config); -} - -/** - * @brief Start transaction from the master to the slave. - * This version uses duplex send and receive usart pheriphals and DMA backed transfers. - * - * @param index Transaction Table index of the transaction to start. - * @return int TRANSACTION_NO_RESPONSE in case of Timeout. - *             TRANSACTION_TYPE_ERROR in case of invalid transaction index. - *             TRANSACTION_END in case of success. - */ -#if !defined(SERIAL_USE_MULTI_TRANSACTION) -int soft_serial_transaction(void) { -    uint8_t sstd_index = 0; -#else -int soft_serial_transaction(int index) { -    uint8_t sstd_index = index; -#endif - -    if (sstd_index > Transaction_table_size) { -        return TRANSACTION_TYPE_ERROR; -    } - -    SSTD_t* const trans       = &Transaction_table[sstd_index]; -    msg_t         msg         = 0; -    size_t        buffer_size = (size_t)sizeof(sstd_index); - -    /* Send transaction table index to the slave, which doubles as basic handshake token. */ -    uartSendFullTimeout(&SERIAL_USART_DRIVER, &buffer_size, &sstd_index, TIME_MS2I(SERIAL_USART_TIMEOUT)); - -    uint8_t sstd_index_shake = 0xFF; -    buffer_size              = (size_t)sizeof(sstd_index_shake); - -    /* Receive the handshake token from the slave. The token was XORed by the slave as a simple checksum. -     If the tokens match, the master will start to send and receive possible transaction buffers. */ -    msg = uartReceiveTimeout(&SERIAL_USART_DRIVER, &buffer_size, &sstd_index_shake, TIME_MS2I(SERIAL_USART_TIMEOUT)); -    if (msg != MSG_OK || (sstd_index_shake != (sstd_index ^ HANDSHAKE_MAGIC))) { -        dprintln("USART: Handshake Failed"); -        return TRANSACTION_NO_RESPONSE; -    } - -    /* Send transaction buffer to the slave. If this transaction requires it. */ -    buffer_size = (size_t)trans->initiator2target_buffer_size; -    if (buffer_size) { -        msg = uartSendFullTimeout(&SERIAL_USART_DRIVER, &buffer_size, trans->initiator2target_buffer, TIME_MS2I(SERIAL_USART_TIMEOUT)); -        if (msg != MSG_OK) { -            dprintln("USART: Send Failed"); -            return TRANSACTION_NO_RESPONSE; -        } -    } - -    /* Receive transaction buffer from the slave. If this transaction requires it. */ -    buffer_size = (size_t)trans->target2initiator_buffer_size; -    if (buffer_size) { -        msg = uartReceiveTimeout(&SERIAL_USART_DRIVER, &buffer_size, trans->target2initiator_buffer, TIME_MS2I(SERIAL_USART_TIMEOUT)); -        if (msg != MSG_OK) { -            dprintln("USART: Receive Failed"); -            return TRANSACTION_NO_RESPONSE; -        } -    } - -    return TRANSACTION_END; -} diff --git a/drivers/chibios/spi_master.c b/drivers/chibios/spi_master.c index 4852a6eba4..28ddcbb2ba 100644 --- a/drivers/chibios/spi_master.c +++ b/drivers/chibios/spi_master.c @@ -18,8 +18,13 @@  #include "timer.h" -static pin_t     currentSlavePin = NO_PIN; -static SPIConfig spiConfig       = {false, NULL, 0, 0, 0, 0}; +static pin_t currentSlavePin = NO_PIN; + +#if defined(K20x) || defined(KL2x) +static SPIConfig spiConfig = {NULL, 0, 0, 0}; +#else +static SPIConfig spiConfig = {false, NULL, 0, 0, 0, 0}; +#endif  __attribute__((weak)) void spi_init(void) {      static bool is_initialised = false; @@ -27,15 +32,15 @@ __attribute__((weak)) void spi_init(void) {          is_initialised = true;          // Try releasing special pins for a short time -        palSetPadMode(PAL_PORT(SPI_SCK_PIN), PAL_PAD(SPI_SCK_PIN), PAL_MODE_INPUT); -        palSetPadMode(PAL_PORT(SPI_MOSI_PIN), PAL_PAD(SPI_MOSI_PIN), PAL_MODE_INPUT); -        palSetPadMode(PAL_PORT(SPI_MISO_PIN), PAL_PAD(SPI_MISO_PIN), PAL_MODE_INPUT); +        setPinInput(SPI_SCK_PIN); +        setPinInput(SPI_MOSI_PIN); +        setPinInput(SPI_MISO_PIN);          chThdSleepMilliseconds(10);  #if defined(USE_GPIOV1) -        palSetPadMode(PAL_PORT(SPI_SCK_PIN), PAL_PAD(SPI_SCK_PIN), PAL_MODE_STM32_ALTERNATE_PUSHPULL); -        palSetPadMode(PAL_PORT(SPI_MOSI_PIN), PAL_PAD(SPI_MOSI_PIN), PAL_MODE_STM32_ALTERNATE_PUSHPULL); -        palSetPadMode(PAL_PORT(SPI_MISO_PIN), PAL_PAD(SPI_MISO_PIN), PAL_MODE_STM32_ALTERNATE_PUSHPULL); +        palSetPadMode(PAL_PORT(SPI_SCK_PIN), PAL_PAD(SPI_SCK_PIN), SPI_SCK_PAL_MODE); +        palSetPadMode(PAL_PORT(SPI_MOSI_PIN), PAL_PAD(SPI_MOSI_PIN), SPI_MOSI_PAL_MODE); +        palSetPadMode(PAL_PORT(SPI_MISO_PIN), PAL_PAD(SPI_MISO_PIN), SPI_MISO_PAL_MODE);  #else          palSetPadMode(PAL_PORT(SPI_SCK_PIN), PAL_PAD(SPI_SCK_PIN), PAL_MODE_ALTERNATE(SPI_SCK_PAL_MODE) | PAL_STM32_OTYPE_PUSHPULL | PAL_STM32_OSPEED_HIGHEST);          palSetPadMode(PAL_PORT(SPI_MOSI_PIN), PAL_PAD(SPI_MOSI_PIN), PAL_MODE_ALTERNATE(SPI_MOSI_PAL_MODE) | PAL_STM32_OTYPE_PUSHPULL | PAL_STM32_OSPEED_HIGHEST); @@ -58,6 +63,54 @@ bool spi_start(pin_t slavePin, bool lsbFirst, uint8_t mode, uint16_t divisor) {          return false;      } +#if defined(K20x) || defined(KL2x) +    spiConfig.tar0 = SPIx_CTARn_FMSZ(7) | SPIx_CTARn_ASC(1); + +    if (lsbFirst) { +        spiConfig.tar0 |= SPIx_CTARn_LSBFE; +    } + +    switch (mode) { +        case 0: +            break; +        case 1: +            spiConfig.tar0 |= SPIx_CTARn_CPHA; +            break; +        case 2: +            spiConfig.tar0 |= SPIx_CTARn_CPOL; +            break; +        case 3: +            spiConfig.tar0 |= SPIx_CTARn_CPHA | SPIx_CTARn_CPOL; +            break; +    } + +    switch (roundedDivisor) { +        case 2: +            spiConfig.tar0 |= SPIx_CTARn_BR(0); +            break; +        case 4: +            spiConfig.tar0 |= SPIx_CTARn_BR(1); +            break; +        case 8: +            spiConfig.tar0 |= SPIx_CTARn_BR(3); +            break; +        case 16: +            spiConfig.tar0 |= SPIx_CTARn_BR(4); +            break; +        case 32: +            spiConfig.tar0 |= SPIx_CTARn_BR(5); +            break; +        case 64: +            spiConfig.tar0 |= SPIx_CTARn_BR(6); +            break; +        case 128: +            spiConfig.tar0 |= SPIx_CTARn_BR(7); +            break; +        case 256: +            spiConfig.tar0 |= SPIx_CTARn_BR(8); +            break; +    } +#else      spiConfig.cr1 = 0;      if (lsbFirst) { @@ -103,6 +156,7 @@ bool spi_start(pin_t slavePin, bool lsbFirst, uint8_t mode, uint16_t divisor) {              spiConfig.cr1 |= SPI_CR1_BR_2 | SPI_CR1_BR_1 | SPI_CR1_BR_0;              break;      } +#endif      currentSlavePin  = slavePin;      spiConfig.ssport = PAL_PORT(slavePin); diff --git a/drivers/chibios/spi_master.h b/drivers/chibios/spi_master.h index e93580e319..b5a6ef1437 100644 --- a/drivers/chibios/spi_master.h +++ b/drivers/chibios/spi_master.h @@ -21,6 +21,7 @@  #include <stdbool.h>  #include "gpio.h" +#include "chibios_config.h"  #ifndef SPI_DRIVER  #    define SPI_DRIVER SPID2 @@ -31,7 +32,11 @@  #endif  #ifndef SPI_SCK_PAL_MODE -#    define SPI_SCK_PAL_MODE 5 +#    if defined(USE_GPIOV1) +#        define SPI_SCK_PAL_MODE PAL_MODE_STM32_ALTERNATE_PUSHPULL +#    else +#        define SPI_SCK_PAL_MODE 5 +#    endif  #endif  #ifndef SPI_MOSI_PIN @@ -39,7 +44,11 @@  #endif  #ifndef SPI_MOSI_PAL_MODE -#    define SPI_MOSI_PAL_MODE 5 +#    if defined(USE_GPIOV1) +#        define SPI_MOSI_PAL_MODE PAL_MODE_STM32_ALTERNATE_PUSHPULL +#    else +#        define SPI_MOSI_PAL_MODE 5 +#    endif  #endif  #ifndef SPI_MISO_PIN @@ -47,7 +56,11 @@  #endif  #ifndef SPI_MISO_PAL_MODE -#    define SPI_MISO_PAL_MODE 5 +#    if defined(USE_GPIOV1) +#        define SPI_MISO_PAL_MODE PAL_MODE_STM32_ALTERNATE_PUSHPULL +#    else +#        define SPI_MISO_PAL_MODE 5 +#    endif  #endif  typedef int16_t spi_status_t; diff --git a/drivers/eeprom/eeprom_i2c.c b/drivers/eeprom/eeprom_i2c.c index 4210f06f9f..8e80ff544f 100644 --- a/drivers/eeprom/eeprom_i2c.c +++ b/drivers/eeprom/eeprom_i2c.c @@ -16,6 +16,9 @@  #include <stdint.h>  #include <string.h> +#if defined(EXTERNAL_EEPROM_WP_PIN) +#    include "gpio.h" +#endif  /*      Note that the implementations of eeprom_XXXX_YYYY on AVR are normally @@ -50,7 +53,14 @@ static inline void fill_target_address(uint8_t *buffer, const void *addr) {      }  } -void eeprom_driver_init(void) { i2c_init(); } +void eeprom_driver_init(void) { +    i2c_init(); +#if defined(EXTERNAL_EEPROM_WP_PIN) +    /* We are setting the WP pin to high in a way that requires at least two bit-flips to change back to 0 */ +    writePin(EXTERNAL_EEPROM_WP_PIN, 1); +    setPinInputHigh(EXTERNAL_EEPROM_WP_PIN); +#endif +}  void eeprom_driver_erase(void) {  #if defined(CONSOLE_ENABLE) && defined(DEBUG_EEPROM_OUTPUT) @@ -89,6 +99,11 @@ void eeprom_write_block(const void *buf, void *addr, size_t len) {      uint8_t * read_buf    = (uint8_t *)buf;      uintptr_t target_addr = (uintptr_t)addr; +#if defined(EXTERNAL_EEPROM_WP_PIN) +    setPinOutput(EXTERNAL_EEPROM_WP_PIN); +    writePin(EXTERNAL_EEPROM_WP_PIN, 0); +#endif +      while (len > 0) {          uintptr_t page_offset  = target_addr % EXTERNAL_EEPROM_PAGE_SIZE;          int       write_length = EXTERNAL_EEPROM_PAGE_SIZE - page_offset; @@ -116,4 +131,10 @@ void eeprom_write_block(const void *buf, void *addr, size_t len) {          target_addr += write_length;          len -= write_length;      } + +#if defined(EXTERNAL_EEPROM_WP_PIN) +    /* We are setting the WP pin to high in a way that requires at least two bit-flips to change back to 0 */ +    writePin(EXTERNAL_EEPROM_WP_PIN, 1); +    setPinInputHigh(EXTERNAL_EEPROM_WP_PIN); +#endif  } diff --git a/drivers/haptic/haptic.c b/drivers/haptic/haptic.c index de3f400527..3fab1be1ae 100644 --- a/drivers/haptic/haptic.c +++ b/drivers/haptic/haptic.c @@ -291,6 +291,73 @@ void haptic_play(void) {  #endif  } +__attribute__((weak)) bool get_haptic_enabled_key(uint16_t keycode, keyrecord_t *record) { +    switch(keycode) { +#    ifdef NO_HAPTIC_MOD +        case QK_MOD_TAP ... QK_MOD_TAP_MAX: +            if (record->tap.count == 0) return false; +            break; +        case QK_LAYER_TAP_TOGGLE ... QK_LAYER_TAP_TOGGLE_MAX: +            if (record->tap.count != TAPPING_TOGGLE) return false; +            break; +        case QK_LAYER_TAP ... QK_LAYER_TAP_MAX: +            if (record->tap.count == 0) return false; +            break; +        case KC_LCTRL ... KC_RGUI: +        case QK_MOMENTARY ... QK_MOMENTARY_MAX: +#    endif +#    ifdef NO_HAPTIC_FN +        case KC_FN0 ... KC_FN31: +#    endif +#    ifdef NO_HAPTIC_ALPHA +        case KC_A ... KC_Z: +#    endif +#    ifdef NO_HAPTIC_PUNCTUATION +        case KC_ENTER: +        case KC_ESCAPE: +        case KC_BSPACE: +        case KC_SPACE: +        case KC_MINUS: +        case KC_EQUAL: +        case KC_LBRACKET: +        case KC_RBRACKET: +        case KC_BSLASH: +        case KC_NONUS_HASH: +        case KC_SCOLON: +        case KC_QUOTE: +        case KC_GRAVE: +        case KC_COMMA: +        case KC_SLASH: +        case KC_DOT: +        case KC_NONUS_BSLASH: +#    endif +#    ifdef NO_HAPTIC_LOCKKEYS +        case KC_CAPSLOCK: +        case KC_SCROLLLOCK: +        case KC_NUMLOCK: +#    endif +#    ifdef NO_HAPTIC_NAV +        case KC_PSCREEN: +        case KC_PAUSE: +        case KC_INSERT: +        case KC_DELETE: +        case KC_PGDOWN: +        case KC_PGUP: +        case KC_LEFT: +        case KC_UP: +        case KC_RIGHT: +        case KC_DOWN: +        case KC_END: +        case KC_HOME: +#    endif +#    ifdef NO_HAPTIC_NUMERIC +        case KC_1 ... KC_0: +#    endif +         return false; +    } +    return true; +} +  bool process_haptic(uint16_t keycode, keyrecord_t *record) {      if (keycode == HPT_ON && record->event.pressed) {          haptic_enable(); @@ -335,12 +402,12 @@ bool process_haptic(uint16_t keycode, keyrecord_t *record) {      if (haptic_config.enable) {          if (record->event.pressed) {              // keypress -            if (haptic_config.feedback < 2) { +            if (haptic_config.feedback < 2 && get_haptic_enabled_key(keycode, record)) {                  haptic_play();              }          } else {              // keyrelease -            if (haptic_config.feedback > 0) { +            if (haptic_config.feedback > 0 && get_haptic_enabled_key(keycode, record)) {                  haptic_play();              }          } diff --git a/drivers/issi/is31fl3737.c b/drivers/issi/is31fl3737.c index 8647c93cc1..30906b4840 100644 --- a/drivers/issi/is31fl3737.c +++ b/drivers/issi/is31fl3737.c @@ -19,6 +19,7 @@  #include "is31fl3737.h"  #include "i2c_master.h"  #include "wait.h" +#include "progmem.h"  // This is a 7-bit address, that gets left-shifted and bit 0  // set to 0 for write, 1 for read (as per I2C protocol) @@ -65,11 +66,12 @@ uint8_t g_twi_transfer_buffer[20];  // We could optimize this and take out the unused registers from these  // buffers and the transfers in IS31FL3737_write_pwm_buffer() but it's  // probably not worth the extra complexity. +  uint8_t g_pwm_buffer[DRIVER_COUNT][192]; -bool    g_pwm_buffer_update_required = false; +bool    g_pwm_buffer_update_required[DRIVER_COUNT] = {false}; -uint8_t g_led_control_registers[DRIVER_COUNT][24] = {{0}}; -bool    g_led_control_registers_update_required   = false; +uint8_t g_led_control_registers[DRIVER_COUNT][24]             = {0}; +bool    g_led_control_registers_update_required[DRIVER_COUNT] = {false};  void IS31FL3737_write_register(uint8_t addr, uint8_t reg, uint8_t data) {      g_twi_transfer_buffer[0] = reg; @@ -153,12 +155,14 @@ void IS31FL3737_init(uint8_t addr) {  void IS31FL3737_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {      if (index >= 0 && index < DRIVER_LED_TOTAL) { -        is31_led led = g_is31_leds[index]; - -        g_pwm_buffer[led.driver][led.r] = red; -        g_pwm_buffer[led.driver][led.g] = green; -        g_pwm_buffer[led.driver][led.b] = blue; -        g_pwm_buffer_update_required    = true; +        // copy the led config from progmem to SRAM +        is31_led led; +        memcpy_P(&led, (&g_is31_leds[index]), sizeof(led)); + +        g_pwm_buffer[led.driver][led.r]          = red; +        g_pwm_buffer[led.driver][led.g]          = green; +        g_pwm_buffer[led.driver][led.b]          = blue; +        g_pwm_buffer_update_required[led.driver] = true;      }  } @@ -169,7 +173,9 @@ void IS31FL3737_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {  }  void IS31FL3737_set_led_control_register(uint8_t index, bool red, bool green, bool blue) { -    is31_led led = g_is31_leds[index]; +    // copy the led config from progmem to SRAM +    is31_led led; +    memcpy_P(&led, (&g_is31_leds[index]), sizeof(led));      uint8_t control_register_r = led.r / 8;      uint8_t control_register_g = led.g / 8; @@ -194,30 +200,28 @@ void IS31FL3737_set_led_control_register(uint8_t index, bool red, bool green, bo          g_led_control_registers[led.driver][control_register_b] &= ~(1 << bit_b);      } -    g_led_control_registers_update_required = true; +    g_led_control_registers_update_required[led.driver] = true;  } -void IS31FL3737_update_pwm_buffers(uint8_t addr1, uint8_t addr2) { -    if (g_pwm_buffer_update_required) { +void IS31FL3737_update_pwm_buffers(uint8_t addr, uint8_t index) { +    if (g_pwm_buffer_update_required[index]) {          // Firstly we need to unlock the command register and select PG1 -        IS31FL3737_write_register(addr1, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5); -        IS31FL3737_write_register(addr1, ISSI_COMMANDREGISTER, ISSI_PAGE_PWM); +        IS31FL3737_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5); +        IS31FL3737_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_PWM); -        IS31FL3737_write_pwm_buffer(addr1, g_pwm_buffer[0]); -        // IS31FL3737_write_pwm_buffer(addr2, g_pwm_buffer[1]); +        IS31FL3737_write_pwm_buffer(addr, g_pwm_buffer[index]);      } -    g_pwm_buffer_update_required = false; +    g_pwm_buffer_update_required[index] = false;  } -void IS31FL3737_update_led_control_registers(uint8_t addr1, uint8_t addr2) { -    if (g_led_control_registers_update_required) { +void IS31FL3737_update_led_control_registers(uint8_t addr, uint8_t index) { +    if (g_led_control_registers_update_required[index]) {          // Firstly we need to unlock the command register and select PG0 -        IS31FL3737_write_register(addr1, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5); -        IS31FL3737_write_register(addr1, ISSI_COMMANDREGISTER, ISSI_PAGE_LEDCONTROL); +        IS31FL3737_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5); +        IS31FL3737_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_LEDCONTROL);          for (int i = 0; i < 24; i++) { -            IS31FL3737_write_register(addr1, i, g_led_control_registers[0][i]); -            // IS31FL3737_write_register(addr2, i, g_led_control_registers[1][i]); +            IS31FL3737_write_register(addr, i, g_led_control_registers[index][i]);          } -        g_led_control_registers_update_required = false;      } +    g_led_control_registers_update_required[index] = false;  } diff --git a/drivers/lcd/st7565.c b/drivers/lcd/st7565.c new file mode 100644 index 0000000000..49b13c00f1 --- /dev/null +++ b/drivers/lcd/st7565.c @@ -0,0 +1,496 @@ +/* +Copyright 2021 + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program.  If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "st7565.h" + +#include <string.h> + +#include "keyboard.h" +#include "progmem.h" +#include "timer.h" +#include "wait.h" + +#include ST7565_FONT_H + +// Fundamental Commands +#define CONTRAST 0x81 +#define DISPLAY_ALL_ON 0xA5 +#define DISPLAY_ALL_ON_RESUME 0xA4 +#define NORMAL_DISPLAY 0xA6 +#define INVERT_DISPLAY 0xA7 +#define DISPLAY_ON 0xAF +#define DISPLAY_OFF 0xAE +#define NOP 0xE3 + +// Addressing Setting Commands +#define PAM_SETCOLUMN_LSB 0x00 +#define PAM_SETCOLUMN_MSB 0x10 +#define PAM_PAGE_ADDR 0xB0  // 0xb0 -- 0xb7 + +// Hardware Configuration Commands +#define DISPLAY_START_LINE 0x40 +#define SEGMENT_REMAP 0xA0 +#define SEGMENT_REMAP_INV 0xA1 +#define COM_SCAN_INC 0xC0 +#define COM_SCAN_DEC 0xC8 +#define LCD_BIAS_7 0xA3 +#define LCD_BIAS_9 0xA2 +#define RESISTOR_RATIO 0x20 +#define POWER_CONTROL 0x28 + +// Misc defines +#ifndef ST7565_BLOCK_COUNT +#    define ST7565_BLOCK_COUNT (sizeof(ST7565_BLOCK_TYPE) * 8) +#endif +#ifndef ST7565_BLOCK_SIZE +#    define ST7565_BLOCK_SIZE (ST7565_MATRIX_SIZE / ST7565_BLOCK_COUNT) +#endif + +#define ST7565_ALL_BLOCKS_MASK (((((ST7565_BLOCK_TYPE)1 << (ST7565_BLOCK_COUNT - 1)) - 1) << 1) | 1) + +#define HAS_FLAGS(bits, flags) ((bits & flags) == flags) + +// Display buffer's is the same as the display memory layout +// this is so we don't end up with rounding errors with +// parts of the display unusable or don't get cleared correctly +// and also allows for drawing & inverting +uint8_t            st7565_buffer[ST7565_MATRIX_SIZE]; +uint8_t *          st7565_cursor; +ST7565_BLOCK_TYPE  st7565_dirty       = 0; +bool               st7565_initialized = false; +bool               st7565_active      = false; +bool               st7565_inverted    = false; +display_rotation_t st7565_rotation    = DISPLAY_ROTATION_0; +#if ST7565_TIMEOUT > 0 +uint32_t st7565_timeout; +#endif +#if ST7565_UPDATE_INTERVAL > 0 +uint16_t st7565_update_timeout; +#endif + +// Flips the rendering bits for a character at the current cursor position +static void InvertCharacter(uint8_t *cursor) { +    const uint8_t *end = cursor + ST7565_FONT_WIDTH; +    while (cursor < end) { +        *cursor = ~(*cursor); +        cursor++; +    } +} + +bool st7565_init(display_rotation_t rotation) { +    setPinOutput(ST7565_A0_PIN); +    writePinHigh(ST7565_A0_PIN); +    setPinOutput(ST7565_RST_PIN); +    writePinHigh(ST7565_RST_PIN); + +    st7565_rotation = st7565_init_user(rotation); + +    spi_init(); +    spi_start(ST7565_SS_PIN, false, 0, ST7565_SPI_CLK_DIVISOR); + +    st7565_reset(); + +    st7565_send_cmd(LCD_BIAS_7); +    if (!HAS_FLAGS(st7565_rotation, DISPLAY_ROTATION_180)) { +        st7565_send_cmd(SEGMENT_REMAP); +        st7565_send_cmd(COM_SCAN_DEC); +    } else { +        st7565_send_cmd(SEGMENT_REMAP_INV); +        st7565_send_cmd(COM_SCAN_INC); +    } +    st7565_send_cmd(DISPLAY_START_LINE | 0x00); +    st7565_send_cmd(CONTRAST); +    st7565_send_cmd(ST7565_CONTRAST); +    st7565_send_cmd(RESISTOR_RATIO | 0x01); +    st7565_send_cmd(POWER_CONTROL | 0x04); +    wait_ms(50); +    st7565_send_cmd(POWER_CONTROL | 0x06); +    wait_ms(50); +    st7565_send_cmd(POWER_CONTROL | 0x07); +    wait_ms(10); +    st7565_send_cmd(DISPLAY_ON); +    st7565_send_cmd(DISPLAY_ALL_ON_RESUME); +    st7565_send_cmd(NORMAL_DISPLAY); + +    spi_stop(); + +#if ST7565_TIMEOUT > 0 +    st7565_timeout = timer_read32() + ST7565_TIMEOUT; +#endif + +    st7565_clear(); +    st7565_initialized = true; +    st7565_active      = true; +    return true; +} + +__attribute__((weak)) display_rotation_t st7565_init_user(display_rotation_t rotation) { return rotation; } + +void st7565_clear(void) { +    memset(st7565_buffer, 0, sizeof(st7565_buffer)); +    st7565_cursor = &st7565_buffer[0]; +    st7565_dirty  = ST7565_ALL_BLOCKS_MASK; +} + +uint8_t crot(uint8_t a, int8_t n) { +    const uint8_t mask = 0x7; +    n &= mask; +    return a << n | a >> (-n & mask); +} + +void st7565_render(void) { +    if (!st7565_initialized) { +        return; +    } + +    // Do we have work to do? +    st7565_dirty &= ST7565_ALL_BLOCKS_MASK; +    if (!st7565_dirty) { +        return; +    } + +    // Find first dirty block +    uint8_t update_start = 0; +    while (!(st7565_dirty & ((ST7565_BLOCK_TYPE)1 << update_start))) { +        ++update_start; +    } + +    // Calculate commands to set memory addressing bounds. +    uint8_t start_page   = ST7565_BLOCK_SIZE * update_start / ST7565_DISPLAY_WIDTH; +    uint8_t start_column = ST7565_BLOCK_SIZE * update_start % ST7565_DISPLAY_WIDTH; +    // IC has 132 segment drivers, for panels with less width we need to offset the starting column +    if (HAS_FLAGS(st7565_rotation, DISPLAY_ROTATION_180)) { +        start_column += (132 - ST7565_DISPLAY_WIDTH); +    } + +    spi_start(ST7565_SS_PIN, false, 0, ST7565_SPI_CLK_DIVISOR); + +    st7565_send_cmd(PAM_PAGE_ADDR | start_page); +    st7565_send_cmd(PAM_SETCOLUMN_LSB | ((ST7565_COLUMN_OFFSET + start_column) & 0x0f)); +    st7565_send_cmd(PAM_SETCOLUMN_MSB | ((ST7565_COLUMN_OFFSET + start_column) >> 4 & 0x0f)); + +    st7565_send_data(&st7565_buffer[ST7565_BLOCK_SIZE * update_start], ST7565_BLOCK_SIZE); + +    // Turn on display if it is off +    st7565_on(); + +    // Clear dirty flag +    st7565_dirty &= ~((ST7565_BLOCK_TYPE)1 << update_start); +} + +void st7565_set_cursor(uint8_t col, uint8_t line) { +    uint16_t index = line * ST7565_DISPLAY_WIDTH + col * ST7565_FONT_WIDTH; + +    // Out of bounds? +    if (index >= ST7565_MATRIX_SIZE) { +        index = 0; +    } + +    st7565_cursor = &st7565_buffer[index]; +} + +void st7565_advance_page(bool clearPageRemainder) { +    uint16_t index     = st7565_cursor - &st7565_buffer[0]; +    uint8_t  remaining = ST7565_DISPLAY_WIDTH - (index % ST7565_DISPLAY_WIDTH); + +    if (clearPageRemainder) { +        // Remaining Char count +        remaining = remaining / ST7565_FONT_WIDTH; + +        // Write empty character until next line +        while (remaining--) st7565_write_char(' ', false); +    } else { +        // Next page index out of bounds? +        if (index + remaining >= ST7565_MATRIX_SIZE) { +            index     = 0; +            remaining = 0; +        } + +        st7565_cursor = &st7565_buffer[index + remaining]; +    } +} + +void st7565_advance_char(void) { +    uint16_t nextIndex      = st7565_cursor - &st7565_buffer[0] + ST7565_FONT_WIDTH; +    uint8_t  remainingSpace = ST7565_DISPLAY_WIDTH - (nextIndex % ST7565_DISPLAY_WIDTH); + +    // Do we have enough space on the current line for the next character +    if (remainingSpace < ST7565_FONT_WIDTH) { +        nextIndex += remainingSpace; +    } + +    // Did we go out of bounds +    if (nextIndex >= ST7565_MATRIX_SIZE) { +        nextIndex = 0; +    } + +    // Update cursor position +    st7565_cursor = &st7565_buffer[nextIndex]; +} + +// Main handler that writes character data to the display buffer +void st7565_write_char(const char data, bool invert) { +    // Advance to the next line if newline +    if (data == '\n') { +        // Old source wrote ' ' until end of line... +        st7565_advance_page(true); +        return; +    } + +    if (data == '\r') { +        st7565_advance_page(false); +        return; +    } + +    // copy the current render buffer to check for dirty after +    static uint8_t st7565_temp_buffer[ST7565_FONT_WIDTH]; +    memcpy(&st7565_temp_buffer, st7565_cursor, ST7565_FONT_WIDTH); + +    _Static_assert(sizeof(font) >= ((ST7565_FONT_END + 1 - ST7565_FONT_START) * ST7565_FONT_WIDTH), "ST7565_FONT_END references outside array"); + +    // set the reder buffer data +    uint8_t cast_data = (uint8_t)data;  // font based on unsigned type for index +    if (cast_data < ST7565_FONT_START || cast_data > ST7565_FONT_END) { +        memset(st7565_cursor, 0x00, ST7565_FONT_WIDTH); +    } else { +        const uint8_t *glyph = &font[(cast_data - ST7565_FONT_START) * ST7565_FONT_WIDTH]; +        memcpy_P(st7565_cursor, glyph, ST7565_FONT_WIDTH); +    } + +    // Invert if needed +    if (invert) { +        InvertCharacter(st7565_cursor); +    } + +    // Dirty check +    if (memcmp(&st7565_temp_buffer, st7565_cursor, ST7565_FONT_WIDTH)) { +        uint16_t index = st7565_cursor - &st7565_buffer[0]; +        st7565_dirty |= ((ST7565_BLOCK_TYPE)1 << (index / ST7565_BLOCK_SIZE)); +        // Edgecase check if the written data spans the 2 chunks +        st7565_dirty |= ((ST7565_BLOCK_TYPE)1 << ((index + ST7565_FONT_WIDTH - 1) / ST7565_BLOCK_SIZE)); +    } + +    // Finally move to the next char +    st7565_advance_char(); +} + +void st7565_write(const char *data, bool invert) { +    const char *end = data + strlen(data); +    while (data < end) { +        st7565_write_char(*data, invert); +        data++; +    } +} + +void st7565_write_ln(const char *data, bool invert) { +    st7565_write(data, invert); +    st7565_advance_page(true); +} + +void st7565_pan(bool left) { +    uint16_t i = 0; +    for (uint16_t y = 0; y < ST7565_DISPLAY_HEIGHT / 8; y++) { +        if (left) { +            for (uint16_t x = 0; x < ST7565_DISPLAY_WIDTH - 1; x++) { +                i                = y * ST7565_DISPLAY_WIDTH + x; +                st7565_buffer[i] = st7565_buffer[i + 1]; +            } +        } else { +            for (uint16_t x = ST7565_DISPLAY_WIDTH - 1; x > 0; x--) { +                i                = y * ST7565_DISPLAY_WIDTH + x; +                st7565_buffer[i] = st7565_buffer[i - 1]; +            } +        } +    } +    st7565_dirty = ST7565_ALL_BLOCKS_MASK; +} + +display_buffer_reader_t st7565_read_raw(uint16_t start_index) { +    if (start_index > ST7565_MATRIX_SIZE) start_index = ST7565_MATRIX_SIZE; +    display_buffer_reader_t ret_reader; +    ret_reader.current_element         = &st7565_buffer[start_index]; +    ret_reader.remaining_element_count = ST7565_MATRIX_SIZE - start_index; +    return ret_reader; +} + +void st7565_write_raw_byte(const char data, uint16_t index) { +    if (index > ST7565_MATRIX_SIZE) index = ST7565_MATRIX_SIZE; +    if (st7565_buffer[index] == data) return; +    st7565_buffer[index] = data; +    st7565_dirty |= ((ST7565_BLOCK_TYPE)1 << (index / ST7565_BLOCK_SIZE)); +} + +void st7565_write_raw(const char *data, uint16_t size) { +    uint16_t cursor_start_index = st7565_cursor - &st7565_buffer[0]; +    if ((size + cursor_start_index) > ST7565_MATRIX_SIZE) size = ST7565_MATRIX_SIZE - cursor_start_index; +    for (uint16_t i = cursor_start_index; i < cursor_start_index + size; i++) { +        uint8_t c = *data++; +        if (st7565_buffer[i] == c) continue; +        st7565_buffer[i] = c; +        st7565_dirty |= ((ST7565_BLOCK_TYPE)1 << (i / ST7565_BLOCK_SIZE)); +    } +} + +void st7565_write_pixel(uint8_t x, uint8_t y, bool on) { +    if (x >= ST7565_DISPLAY_WIDTH) { +        return; +    } +    uint16_t index = x + (y / 8) * ST7565_DISPLAY_WIDTH; +    if (index >= ST7565_MATRIX_SIZE) { +        return; +    } +    uint8_t data = st7565_buffer[index]; +    if (on) { +        data |= (1 << (y % 8)); +    } else { +        data &= ~(1 << (y % 8)); +    } +    if (st7565_buffer[index] != data) { +        st7565_buffer[index] = data; +        st7565_dirty |= ((ST7565_BLOCK_TYPE)1 << (index / ST7565_BLOCK_SIZE)); +    } +} + +#if defined(__AVR__) +void st7565_write_P(const char *data, bool invert) { +    uint8_t c = pgm_read_byte(data); +    while (c != 0) { +        st7565_write_char(c, invert); +        c = pgm_read_byte(++data); +    } +} + +void st7565_write_ln_P(const char *data, bool invert) { +    st7565_write_P(data, invert); +    st7565_advance_page(true); +} + +void st7565_write_raw_P(const char *data, uint16_t size) { +    uint16_t cursor_start_index = st7565_cursor - &st7565_buffer[0]; +    if ((size + cursor_start_index) > ST7565_MATRIX_SIZE) size = ST7565_MATRIX_SIZE - cursor_start_index; +    for (uint16_t i = cursor_start_index; i < cursor_start_index + size; i++) { +        uint8_t c = pgm_read_byte(data++); +        if (st7565_buffer[i] == c) continue; +        st7565_buffer[i] = c; +        st7565_dirty |= ((ST7565_BLOCK_TYPE)1 << (i / ST7565_BLOCK_SIZE)); +    } +} +#endif  // defined(__AVR__) + +bool st7565_on(void) { +    if (!st7565_initialized) { +        return st7565_active; +    } + +#if ST7565_TIMEOUT > 0 +    st7565_timeout = timer_read32() + ST7565_TIMEOUT; +#endif + +    if (!st7565_active) { +        spi_start(ST7565_SS_PIN, false, 0, ST7565_SPI_CLK_DIVISOR); +        st7565_send_cmd(DISPLAY_ON); +        spi_stop(); +        st7565_active = true; +        st7565_on_user(); +    } +    return st7565_active; +} + +__attribute__((weak)) void st7565_on_user(void) {} + +bool st7565_off(void) { +    if (!st7565_initialized) { +        return !st7565_active; +    } + +    if (st7565_active) { +        spi_start(ST7565_SS_PIN, false, 0, ST7565_SPI_CLK_DIVISOR); +        st7565_send_cmd(DISPLAY_OFF); +        spi_stop(); +        st7565_active = false; +        st7565_off_user(); +    } +    return !st7565_active; +} + +__attribute__((weak)) void st7565_off_user(void) {} + +bool st7565_is_on(void) { return st7565_active; } + +bool st7565_invert(bool invert) { +    if (!st7565_initialized) { +        return st7565_inverted; +    } + +    if (invert != st7565_inverted) { +        spi_start(ST7565_SS_PIN, false, 0, ST7565_SPI_CLK_DIVISOR); +        st7565_send_cmd(invert ? INVERT_DISPLAY : NORMAL_DISPLAY); +        spi_stop(); +        st7565_inverted = invert; +    } +    return st7565_inverted; +} + +uint8_t st7565_max_chars(void) { return ST7565_DISPLAY_WIDTH / ST7565_FONT_WIDTH; } + +uint8_t st7565_max_lines(void) { return ST7565_DISPLAY_HEIGHT / ST7565_FONT_HEIGHT; } + +void st7565_task(void) { +    if (!st7565_initialized) { +        return; +    } + +#if ST7565_UPDATE_INTERVAL > 0 +    if (timer_elapsed(st7565_update_timeout) >= ST7565_UPDATE_INTERVAL) { +        st7565_update_timeout = timer_read(); +        st7565_set_cursor(0, 0); +        st7565_task_user(); +    } +#else +    st7565_set_cursor(0, 0); +    st7565_task_user(); +#endif + +    // Smart render system, no need to check for dirty +    st7565_render(); + +    // Display timeout check +#if ST7565_TIMEOUT > 0 +    if (st7565_active && timer_expired32(timer_read32(), st7565_timeout)) { +        st7565_off(); +    } +#endif +} + +__attribute__((weak)) void st7565_task_user(void) {} + +void st7565_reset(void) { +    writePinLow(ST7565_RST_PIN); +    wait_ms(20); +    writePinHigh(ST7565_RST_PIN); +    wait_ms(20); +} + +spi_status_t st7565_send_cmd(uint8_t cmd) { +    writePinLow(ST7565_A0_PIN); +    return spi_write(cmd); +} + +spi_status_t st7565_send_data(uint8_t *data, uint16_t length) { +    writePinHigh(ST7565_A0_PIN); +    return spi_transmit(data, length); +} diff --git a/drivers/lcd/st7565.h b/drivers/lcd/st7565.h new file mode 100644 index 0000000000..d453dbe6da --- /dev/null +++ b/drivers/lcd/st7565.h @@ -0,0 +1,219 @@ +/* +Copyright 2021 + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program.  If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include <stdint.h> +#include <stdbool.h> + +#include "spi_master.h" + +#ifndef ST7565_DISPLAY_WIDTH +#    define ST7565_DISPLAY_WIDTH 128 +#endif +#ifndef ST7565_DISPLAY_HEIGHT +#    define ST7565_DISPLAY_HEIGHT 32 +#endif +#ifndef ST7565_MATRIX_SIZE +#    define ST7565_MATRIX_SIZE (ST7565_DISPLAY_HEIGHT / 8 * ST7565_DISPLAY_WIDTH)  // 1024 (compile time mathed) +#endif +#ifndef ST7565_BLOCK_TYPE +#    define ST7565_BLOCK_TYPE uint16_t +#endif +#ifndef ST7565_BLOCK_COUNT +#    define ST7565_BLOCK_COUNT (sizeof(ST7565_BLOCK_TYPE) * 8)  // 32 (compile time mathed) +#endif +#ifndef ST7565_BLOCK_SIZE +#    define ST7565_BLOCK_SIZE (ST7565_MATRIX_SIZE / ST7565_BLOCK_COUNT)  // 32 (compile time mathed) +#endif + +// the column address corresponding to the first column in the display hardware +#if !defined(ST7565_COLUMN_OFFSET) +#    define ST7565_COLUMN_OFFSET 0 +#endif + +// spi clock divisor +#if !defined(ST7565_SPI_CLK_DIVISOR) +#    define ST7565_SPI_CLK_DIVISOR 4 +#endif + +// Custom font file to use +#if !defined(ST7565_FONT_H) +#    define ST7565_FONT_H "glcdfont.c" +#endif +// unsigned char value of the first character in the font file +#if !defined(ST7565_FONT_START) +#    define ST7565_FONT_START 0 +#endif +// unsigned char value of the last character in the font file +#if !defined(ST7565_FONT_END) +#    define ST7565_FONT_END 223 +#endif +// Font render width +#if !defined(ST7565_FONT_WIDTH) +#    define ST7565_FONT_WIDTH 6 +#endif +// Font render height +#if !defined(ST7565_FONT_HEIGHT) +#    define ST7565_FONT_HEIGHT 8 +#endif +// Default contrast level +#if !defined(ST7565_CONTRAST) +#    define ST7565_CONTRAST 32 +#endif + +#if !defined(ST7565_TIMEOUT) +#    if defined(ST7565_DISABLE_TIMEOUT) +#        define ST7565_TIMEOUT 0 +#    else +#        define ST7565_TIMEOUT 60000 +#    endif +#endif + +#if !defined(ST7565_UPDATE_INTERVAL) && defined(SPLIT_KEYBOARD) +#    define ST7565_UPDATE_INTERVAL 50 +#endif + +typedef struct __attribute__((__packed__)) { +    uint8_t *current_element; +    uint16_t remaining_element_count; +} display_buffer_reader_t; + +// Rotation enum values are flags +typedef enum { DISPLAY_ROTATION_0, DISPLAY_ROTATION_180 } display_rotation_t; + +// Initialize the display, rotating the rendered output based on the define passed in. +// Returns true if the display was initialized successfully +bool st7565_init(display_rotation_t rotation); + +// Called at the start of st7565_init, weak function overridable by the user +// rotation - the value passed into st7565_init +// Return new display_rotation_t if you want to override default rotation +display_rotation_t st7565_init_user(display_rotation_t rotation); + +// Clears the display buffer, resets cursor position to 0, and sets the buffer to dirty for rendering +void st7565_clear(void); + +// Renders the dirty chunks of the buffer to display +void st7565_render(void); + +// Moves cursor to character position indicated by column and line, wraps if out of bounds +// Max column denoted by 'st7565_max_chars()' and max lines by 'st7565_max_lines()' functions +void st7565_set_cursor(uint8_t col, uint8_t line); + +// Advances the cursor to the next page, writing ' ' if true +// Wraps to the begining when out of bounds +void st7565_advance_page(bool clearPageRemainder); + +// Moves the cursor forward 1 character length +// Advance page if there is not enough room for the next character +// Wraps to the begining when out of bounds +void st7565_advance_char(void); + +// Writes a single character to the buffer at current cursor position +// Advances the cursor while writing, inverts the pixels if true +// Main handler that writes character data to the display buffer +void st7565_write_char(const char data, bool invert); + +// Writes a string to the buffer at current cursor position +// Advances the cursor while writing, inverts the pixels if true +void st7565_write(const char *data, bool invert); + +// Writes a string to the buffer at current cursor position +// Advances the cursor while writing, inverts the pixels if true +// Advances the cursor to the next page, wiring ' ' to the remainder of the current page +void st7565_write_ln(const char *data, bool invert); + +// Pans the buffer to the right (or left by passing true) by moving contents of the buffer +// Useful for moving the screen in preparation for new drawing +void st7565_pan(bool left); + +// Returns a pointer to the requested start index in the buffer plus remaining +// buffer length as struct +display_buffer_reader_t st7565_read_raw(uint16_t start_index); + +// Writes a string to the buffer at current cursor position +void st7565_write_raw(const char *data, uint16_t size); + +// Writes a single byte into the buffer at the specified index +void st7565_write_raw_byte(const char data, uint16_t index); + +// Sets a specific pixel on or off +// Coordinates start at top-left and go right and down for positive x and y +void st7565_write_pixel(uint8_t x, uint8_t y, bool on); + +#if defined(__AVR__) +// Writes a PROGMEM string to the buffer at current cursor position +// Advances the cursor while writing, inverts the pixels if true +// Remapped to call 'void st7565_write(const char *data, bool invert);' on ARM +void st7565_write_P(const char *data, bool invert); + +// Writes a PROGMEM string to the buffer at current cursor position +// Advances the cursor while writing, inverts the pixels if true +// Advances the cursor to the next page, wiring ' ' to the remainder of the current page +// Remapped to call 'void st7565_write_ln(const char *data, bool invert);' on ARM +void st7565_write_ln_P(const char *data, bool invert); + +// Writes a PROGMEM string to the buffer at current cursor position +void st7565_write_raw_P(const char *data, uint16_t size); +#else +#    define st7565_write_P(data, invert) st7565_write(data, invert) +#    define st7565_write_ln_P(data, invert) st7565_write_ln(data, invert) +#    define st7565_write_raw_P(data, size) st7565_write_raw(data, size) +#endif  // defined(__AVR__) + +// Can be used to manually turn on the screen if it is off +// Returns true if the screen was on or turns on +bool st7565_on(void); + +// Called when st7565_on() turns on the screen, weak function overridable by the user +// Not called if the screen is already on +void st7565_on_user(void); + +// Can be used to manually turn off the screen if it is on +// Returns true if the screen was off or turns off +bool st7565_off(void); + +// Called when st7565_off() turns off the screen, weak function overridable by the user +// Not called if the screen is already off +void st7565_off_user(void); + +// Returns true if the screen is currently on, false if it is +// not +bool st7565_is_on(void); + +// Basically it's st7565_render, but with timeout management and st7565_task_user calling! +void st7565_task(void); + +// Called at the start of st7565_task, weak function overridable by the user +void st7565_task_user(void); + +// Inverts the display +// Returns true if the screen was or is inverted +bool st7565_invert(bool invert); + +// Returns the maximum number of characters that will fit on a line +uint8_t st7565_max_chars(void); + +// Returns the maximum number of lines that will fit on the display +uint8_t st7565_max_lines(void); + +void st7565_reset(void); + +spi_status_t st7565_send_cmd(uint8_t cmd); + +spi_status_t st7565_send_data(uint8_t *data, uint16_t length); diff --git a/drivers/oled/oled_driver.c b/drivers/oled/oled_driver.c index 8e5ed5f070..7d41978905 100644 --- a/drivers/oled/oled_driver.c +++ b/drivers/oled/oled_driver.c @@ -34,6 +34,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.  #define DISPLAY_ALL_ON 0xA5  #define DISPLAY_ALL_ON_RESUME 0xA4  #define NORMAL_DISPLAY 0xA6 +#define INVERT_DISPLAY 0xA7  #define DISPLAY_ON 0xAF  #define DISPLAY_OFF 0xAE  #define NOP 0xE3 @@ -114,6 +115,7 @@ OLED_BLOCK_TYPE oled_dirty          = 0;  bool            oled_initialized    = false;  bool            oled_active         = false;  bool            oled_scrolling      = false; +bool            oled_inverted       = false;  uint8_t         oled_brightness     = OLED_BRIGHTNESS;  oled_rotation_t oled_rotation       = 0;  uint8_t         oled_rotation_width = 0; @@ -690,6 +692,30 @@ bool oled_scroll_off(void) {      return !oled_scrolling;  } +bool oled_invert(bool invert) { +    if (!oled_initialized) { +        return oled_inverted; +    } + +    if (invert && !oled_inverted) { +        static const uint8_t PROGMEM display_inverted[] = {I2C_CMD, INVERT_DISPLAY}; +        if (I2C_TRANSMIT_P(display_inverted) != I2C_STATUS_SUCCESS) { +            print("oled_invert cmd failed\n"); +            return oled_inverted; +        } +        oled_inverted = true; +    } else if (!invert && oled_inverted) { +        static const uint8_t PROGMEM display_normal[] = {I2C_CMD, NORMAL_DISPLAY}; +        if (I2C_TRANSMIT_P(display_normal) != I2C_STATUS_SUCCESS) { +            print("oled_invert cmd failed\n"); +            return oled_inverted; +        } +        oled_inverted = false; +    } + +    return oled_inverted; +} +  uint8_t oled_max_chars(void) {      if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_90)) {          return OLED_DISPLAY_WIDTH / OLED_FONT_WIDTH; diff --git a/drivers/oled/oled_driver.h b/drivers/oled/oled_driver.h index a6b85f37e6..fc68f0ec95 100644 --- a/drivers/oled/oled_driver.h +++ b/drivers/oled/oled_driver.h @@ -313,6 +313,10 @@ bool oled_scroll_left(void);  // Returns true if the screen was not scrolling or stops scrolling  bool oled_scroll_off(void); +// Inverts the display +// Returns true if the screen was or is inverted +bool oled_invert(bool invert); +  // Returns the maximum number of characters that will fit on a line  uint8_t oled_max_chars(void); diff --git a/drivers/sensors/adns5050.c b/drivers/sensors/adns5050.c new file mode 100644 index 0000000000..e7273977d5 --- /dev/null +++ b/drivers/sensors/adns5050.c @@ -0,0 +1,193 @@ +/* Copyright 2021 Colin Lam (Ploopy Corporation) + * Copyright 2020 Christopher Courtney, aka Drashna Jael're  (@drashna) <[email protected]> + * Copyright 2019 Sunjun Kim + * Copyright 2019 Hiroyuki Okada + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "adns5050.h" +#include "wait.h" +#include "debug.h" +#include "print.h" +#include "gpio.h" + +#ifndef OPTIC_ROTATED +#    define OPTIC_ROTATED false +#endif + +// Definitions for the ADNS serial line. +#ifndef ADNS_SCLK_PIN +#    define ADNS_SCLK_PIN B7 +#endif + +#ifndef ADNS_SDIO_PIN +#    define ADNS_SDIO_PIN C6 +#endif + +#ifndef ADNS_CS_PIN +#    define ADNS_CS_PIN B4 +#endif + +#ifdef CONSOLE_ENABLE +void print_byte(uint8_t byte) { dprintf("%c%c%c%c%c%c%c%c|", (byte & 0x80 ? '1' : '0'), (byte & 0x40 ? '1' : '0'), (byte & 0x20 ? '1' : '0'), (byte & 0x10 ? '1' : '0'), (byte & 0x08 ? '1' : '0'), (byte & 0x04 ? '1' : '0'), (byte & 0x02 ? '1' : '0'), (byte & 0x01 ? '1' : '0')); } +#endif + +// Initialize the ADNS serial pins. +void adns_init(void) { +    setPinOutput(ADNS_SCLK_PIN); +    setPinOutput(ADNS_SDIO_PIN); +    setPinOutput(ADNS_CS_PIN); +} + +// Perform a synchronization with the ADNS. +// Just as with the serial protocol, this is used by the slave to send a +// synchronization signal to the master. +void adns_sync(void) { +    writePinLow(ADNS_CS_PIN); +    wait_us(1); +    writePinHigh(ADNS_CS_PIN); +} + +void adns_cs_select(void) { +    writePinLow(ADNS_CS_PIN); +} + +void adns_cs_deselect(void) { +    writePinHigh(ADNS_CS_PIN); +} + +uint8_t adns_serial_read(void) { +    setPinInput(ADNS_SDIO_PIN); +    uint8_t byte = 0; + +    for (uint8_t i = 0; i < 8; ++i) { +        writePinLow(ADNS_SCLK_PIN); +        wait_us(1); + +        byte = (byte << 1) | readPin(ADNS_SDIO_PIN); + +        writePinHigh(ADNS_SCLK_PIN); +        wait_us(1); +    } + +    return byte; +} + +void adns_serial_write(uint8_t data) { +    setPinOutput(ADNS_SDIO_PIN); + +    for (int8_t b = 7; b >= 0; b--) { +        writePinLow(ADNS_SCLK_PIN); + +        if (data & (1 << b)) +            writePinHigh(ADNS_SDIO_PIN); +        else +            writePinLow(ADNS_SDIO_PIN); + +        wait_us(2); + +        writePinHigh(ADNS_SCLK_PIN); +    } + +    // tSWR. See page 15 of the ADNS spec sheet. +    // Technically, this is only necessary if the next operation is an SDIO +    // read. This is not guaranteed to be the case, but we're being lazy. +    wait_us(4); + +    // Note that tSWW is never necessary. All write operations require at +    // least 32us, which exceeds tSWW, so there's never a need to wait for it. +} + +// Read a byte of data from a register on the ADNS. +// Don't forget to use the register map (as defined in the header file). +uint8_t adns_read_reg(uint8_t reg_addr) { +    adns_cs_select(); + +    adns_serial_write(reg_addr); + +    // We don't need a minimum tSRAD here. That's because a 4ms wait time is +    // already included in adns_serial_write(), so we're good. +    // See page 10 and 15 of the ADNS spec sheet. +    //wait_us(4); + +    uint8_t byte = adns_serial_read(); + +    // tSRW & tSRR. See page 15 of the ADNS spec sheet. +    // Technically, this is only necessary if the next operation is an SDIO +    // read or write. This is not guaranteed to be the case. +    // Honestly, this wait could probably be removed. +    wait_us(1); + +    adns_cs_deselect(); + +    return byte; +} + +void adns_write_reg(uint8_t reg_addr, uint8_t data) { +    adns_cs_select(); +    adns_serial_write( 0b10000000 | reg_addr ); +    adns_serial_write(data); +    adns_cs_deselect(); +} + +report_adns_t adns_read_burst(void) { +    adns_cs_select(); + +    report_adns_t data; +    data.dx = 0; +    data.dy = 0; + +    adns_serial_write(REG_MOTION_BURST); + +    // We don't need a minimum tSRAD here. That's because a 4ms wait time is +    // already included in adns_serial_write(), so we're good. +    // See page 10 and 15 of the ADNS spec sheet. +    //wait_us(4); + +    uint8_t x = adns_serial_read(); +    uint8_t y = adns_serial_read(); + +    // Burst mode returns a bunch of other shit that we don't really need. +    // Setting CS to high ends burst mode early. +    adns_cs_deselect(); + +    data.dx = convert_twoscomp(x); +    data.dy = convert_twoscomp(y); + +    return data; +} + +// Convert a two's complement byte from an unsigned data type into a signed +// data type. +int8_t convert_twoscomp(uint8_t data) { +    if ((data & 0x80) == 0x80) +        return -128 + (data & 0x7F); +    else +        return data; +} + +// Don't forget to use the definitions for CPI in the header file. +void adns_set_cpi(uint8_t cpi) { +    adns_write_reg(REG_MOUSE_CONTROL2, cpi); +} + +bool adns_check_signature(void) { +    uint8_t pid = adns_read_reg(REG_PRODUCT_ID); +    uint8_t rid = adns_read_reg(REG_REVISION_ID); +    uint8_t pid2 = adns_read_reg(REG_PRODUCT_ID2); + +    return (pid == 0x12 && rid == 0x01 && pid2 == 0x26); +} diff --git a/drivers/sensors/adns5050.h b/drivers/sensors/adns5050.h new file mode 100644 index 0000000000..ff8e8f78e9 --- /dev/null +++ b/drivers/sensors/adns5050.h @@ -0,0 +1,79 @@ +/* Copyright 2021 Colin Lam (Ploopy Corporation) + * Copyright 2020 Christopher Courtney, aka Drashna Jael're  (@drashna) <[email protected]> + * Copyright 2019 Sunjun Kim + * Copyright 2019 Hiroyuki Okada + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <stdbool.h> + +// Registers +#define REG_PRODUCT_ID 0x00 +#define REG_REVISION_ID 0x01 +#define REG_MOTION 0x02 +#define REG_DELTA_X 0x03 +#define REG_DELTA_Y 0x04 +#define REG_SQUAL 0x05 +#define REG_SHUTTER_UPPER 0x06 +#define REG_SHUTTER_LOWER 0x07 +#define REG_MAXIMUM_PIXEL 0x08 +#define REG_PIXEL_SUM 0x09 +#define REG_MINIMUM_PIXEL 0x0a +#define REG_PIXEL_GRAB 0x0b +#define REG_MOUSE_CONTROL 0x0d +#define REG_MOUSE_CONTROL2 0x19 +#define REG_LED_DC_MODE 0x22 +#define REG_CHIP_RESET 0x3a +#define REG_PRODUCT_ID2 0x3e +#define REG_INV_REV_ID 0x3f +#define REG_MOTION_BURST 0x63 + +// CPI values +#define CPI125 0x11 +#define CPI250 0x12 +#define CPI375 0x13 +#define CPI500 0x14 +#define CPI625 0x15 +#define CPI750 0x16 +#define CPI875 0x17 +#define CPI1000 0x18 +#define CPI1125 0x19 +#define CPI1250 0x1a +#define CPI1375 0x1b + +#ifdef CONSOLE_ENABLE +void print_byte(uint8_t byte); +#endif + +typedef struct { +    int8_t dx; +    int8_t dy; +} report_adns_t; + +// A bunch of functions to implement the ADNS5050-specific serial protocol. +// Note that the "serial.h" driver is insufficient, because it does not +// manually manipulate a serial clock signal. +void adns_init(void); +void adns_sync(void); +uint8_t adns_serial_read(void); +void adns_serial_write(uint8_t data); +uint8_t adns_read_reg(uint8_t reg_addr); +void adns_write_reg(uint8_t reg_addr, uint8_t data); +report_adns_t adns_read_burst(void); +int8_t convert_twoscomp(uint8_t data); +void adns_set_cpi(uint8_t cpi); +bool adns_check_signature(void); diff --git a/drivers/sensors/adns9800.c b/drivers/sensors/adns9800.c new file mode 100644 index 0000000000..36213179f7 --- /dev/null +++ b/drivers/sensors/adns9800.c @@ -0,0 +1,219 @@ +/* Copyright 2020 Alexander Tulloh + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#include "spi_master.h" +#include "quantum.h" +#include "adns9800_srom_A6.h" +#include "adns9800.h" + +// registers +#define REG_Product_ID                           0x00 +#define REG_Revision_ID                          0x01 +#define REG_Motion                               0x02 +#define REG_Delta_X_L                            0x03 +#define REG_Delta_X_H                            0x04 +#define REG_Delta_Y_L                            0x05 +#define REG_Delta_Y_H                            0x06 +#define REG_SQUAL                                0x07 +#define REG_Pixel_Sum                            0x08 +#define REG_Maximum_Pixel                        0x09 +#define REG_Minimum_Pixel                        0x0a +#define REG_Shutter_Lower                        0x0b +#define REG_Shutter_Upper                        0x0c +#define REG_Frame_Period_Lower                   0x0d +#define REG_Frame_Period_Upper                   0x0e +#define REG_Configuration_I                      0x0f +#define REG_Configuration_II                     0x10 +#define REG_Frame_Capture                        0x12 +#define REG_SROM_Enable                          0x13 +#define REG_Run_Downshift                        0x14 +#define REG_Rest1_Rate                           0x15 +#define REG_Rest1_Downshift                      0x16 +#define REG_Rest2_Rate                           0x17 +#define REG_Rest2_Downshift                      0x18 +#define REG_Rest3_Rate                           0x19 +#define REG_Frame_Period_Max_Bound_Lower         0x1a +#define REG_Frame_Period_Max_Bound_Upper         0x1b +#define REG_Frame_Period_Min_Bound_Lower         0x1c +#define REG_Frame_Period_Min_Bound_Upper         0x1d +#define REG_Shutter_Max_Bound_Lower              0x1e +#define REG_Shutter_Max_Bound_Upper              0x1f +#define REG_LASER_CTRL0                          0x20 +#define REG_Observation                          0x24 +#define REG_Data_Out_Lower                       0x25 +#define REG_Data_Out_Upper                       0x26 +#define REG_SROM_ID                              0x2a +#define REG_Lift_Detection_Thr                   0x2e +#define REG_Configuration_V                      0x2f +#define REG_Configuration_IV                     0x39 +#define REG_Power_Up_Reset                       0x3a +#define REG_Shutdown                             0x3b +#define REG_Inverse_Product_ID                   0x3f +#define REG_Motion_Burst                         0x50 +#define REG_SROM_Load_Burst                      0x62 +#define REG_Pixel_Burst                          0x64 + +#define ADNS_CLOCK_SPEED 2000000 +#define MIN_CPI 200 +#define MAX_CPI 8200 +#define CPI_STEP 200 +#define CLAMP_CPI(value) value < MIN_CPI ? MIN_CPI : value > MAX_CPI ? MAX_CPI : value +#define SPI_MODE 3 +#define SPI_DIVISOR (F_CPU / ADNS_CLOCK_SPEED) +#define US_BETWEEN_WRITES 120 +#define US_BETWEEN_READS 20 +#define US_BEFORE_MOTION 100 +#define MSB1 0x80 + +extern const uint16_t adns_firmware_length; +extern const uint8_t adns_firmware_data[]; + +void adns_spi_start(void){ +    spi_start(SPI_SS_PIN, false, SPI_MODE, SPI_DIVISOR); +} + +void adns_write(uint8_t reg_addr, uint8_t data){ + +    adns_spi_start(); +    spi_write(reg_addr | MSB1); +    spi_write(data); +    spi_stop(); +    wait_us(US_BETWEEN_WRITES); +} + +uint8_t adns_read(uint8_t reg_addr){ + +    adns_spi_start(); +    spi_write(reg_addr & 0x7f ); +    uint8_t data = spi_read(); +    spi_stop(); +    wait_us(US_BETWEEN_READS); + +    return data; +} + +void adns_init() { + +    setPinOutput(SPI_SS_PIN); + +    spi_init(); + +    // reboot +    adns_write(REG_Power_Up_Reset, 0x5a); +    wait_ms(50); + +    // read registers and discard +    adns_read(REG_Motion); +    adns_read(REG_Delta_X_L); +    adns_read(REG_Delta_X_H); +    adns_read(REG_Delta_Y_L); +    adns_read(REG_Delta_Y_H); + +    // upload firmware + +    // 3k firmware mode +    adns_write(REG_Configuration_IV, 0x02); + +    // enable initialisation +    adns_write(REG_SROM_Enable, 0x1d); + +    // wait a frame +    wait_ms(10); + +    // start SROM download +    adns_write(REG_SROM_Enable, 0x18); + +    // write the SROM file + +    adns_spi_start(); + +    spi_write(REG_SROM_Load_Burst | 0x80); +    wait_us(15); + +    // send all bytes of the firmware +    unsigned char c; +    for(int i = 0; i < adns_firmware_length; i++){ +        c = (unsigned char)pgm_read_byte(adns_firmware_data + i); +        spi_write(c); +        wait_us(15); +    } + +    spi_stop(); + +    wait_ms(10); + +    // enable laser +    uint8_t laser_ctrl0 = adns_read(REG_LASER_CTRL0); +    adns_write(REG_LASER_CTRL0, laser_ctrl0 & 0xf0); +} + +config_adns_t adns_get_config(void) { +    uint8_t config_1 = adns_read(REG_Configuration_I); +    return (config_adns_t){ (config_1 & 0xFF) * CPI_STEP }; +} + +void adns_set_config(config_adns_t config) { +    uint8_t config_1 = (CLAMP_CPI(config.cpi) / CPI_STEP) & 0xFF; +    adns_write(REG_Configuration_I, config_1); +} + +static int16_t convertDeltaToInt(uint8_t high, uint8_t low){ + +    // join bytes into twos compliment +    uint16_t twos_comp = (high << 8) | low; + +    // convert twos comp to int +    if (twos_comp & 0x8000) +        return -1 * (~twos_comp + 1); + +    return twos_comp; +} + +report_adns_t adns_get_report(void) { + +    report_adns_t report = {0, 0}; + +    adns_spi_start(); + +    // start burst mode +    spi_write(REG_Motion_Burst & 0x7f); + +    wait_us(US_BEFORE_MOTION); + +    uint8_t motion = spi_read(); + +    if(motion & 0x80) { + +        // clear observation register +        spi_read(); + +        // delta registers +        uint8_t delta_x_l = spi_read(); +        uint8_t delta_x_h = spi_read(); +        uint8_t delta_y_l = spi_read(); +        uint8_t delta_y_h = spi_read(); + +        report.x = convertDeltaToInt(delta_x_h, delta_x_l); +        report.y = convertDeltaToInt(delta_y_h, delta_y_l); +    } + +    // clear residual motion +    spi_write(REG_Motion & 0x7f); + +    spi_stop(); + +    return report; +} diff --git a/drivers/sensors/adns9800.h b/drivers/sensors/adns9800.h new file mode 100644 index 0000000000..2f50b8f1be --- /dev/null +++ b/drivers/sensors/adns9800.h @@ -0,0 +1,35 @@ +/* Copyright 2020 Alexander Tulloh + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <stdint.h> + +typedef struct { +    /* 200 - 8200 CPI supported */ +    uint16_t cpi; +} config_adns_t; + +typedef struct { +    int16_t x; +    int16_t y; +} report_adns_t; + +void adns_init(void); +config_adns_t adns_get_config(void); +void adns_set_config(config_adns_t); +/* Reads and clears the current delta values on the ADNS sensor */ +report_adns_t adns_get_report(void); diff --git a/drivers/sensors/adns9800_srom_A6.h b/drivers/sensors/adns9800_srom_A6.h new file mode 100644 index 0000000000..f5b3abeb62 --- /dev/null +++ b/drivers/sensors/adns9800_srom_A6.h @@ -0,0 +1,3078 @@ +#pragma once + +#include "progmem.h" + +const uint16_t adns_firmware_length = 3070; + +const uint8_t adns_firmware_data[] PROGMEM = { +0x03, +0xa6, +0x68, +0x1e, +0x7d, +0x10, +0x7e, +0x7e, +0x5f, +0x1c, +0xb8, +0xf2, +0x47, +0x0c, +0x7b, +0x74, +0x4b, +0x14, +0x8b, +0x75, +0x66, +0x51, +0x0b, +0x8c, +0x76, +0x74, +0x4b, +0x14, +0xaa, +0xd6, +0x0f, +0x9c, +0xba, +0xf6, +0x6e, +0x3f, +0xdd, +0x38, +0xd5, +0x02, +0x80, +0x9b, +0x82, +0x6d, +0x58, +0x13, +0xa4, +0xab, +0xb5, +0xc9, +0x10, +0xa2, +0xc6, +0x0a, +0x7f, +0x5d, +0x19, +0x91, +0xa0, +0xa3, +0xce, +0xeb, +0x3e, +0xc9, +0xf1, +0x60, +0x42, +0xe7, +0x4c, +0xfb, +0x74, +0x6a, +0x56, +0x2e, +0xbf, +0xdd, +0x38, +0xd3, +0x05, +0x88, +0x92, +0xa6, +0xce, +0xff, +0x5d, +0x38, +0xd1, +0xcf, +0xef, +0x58, +0xcb, +0x65, +0x48, +0xf0, +0x35, +0x85, +0xa9, +0xb2, +0x8f, +0x5e, +0xf3, +0x80, +0x94, +0x97, +0x7e, +0x75, +0x97, +0x87, +0x73, +0x13, +0xb0, +0x8a, +0x69, +0xd4, +0x0a, +0xde, +0xc1, +0x79, +0x59, +0x36, +0xdb, +0x9d, +0xd6, +0xb8, +0x15, +0x6f, +0xce, +0x3c, +0x72, +0x32, +0x45, +0x88, +0xdf, +0x6c, +0xa5, +0x6d, +0xe8, +0x76, +0x96, +0x14, +0x74, +0x20, +0xdc, +0xf4, +0xfa, +0x37, +0x6a, +0x27, +0x32, +0xe3, +0x29, +0xbf, +0xc4, +0xc7, +0x06, +0x9d, +0x58, +0xe7, +0x87, +0x7c, +0x2e, +0x9f, +0x6e, +0x49, +0x07, +0x5d, +0x23, +0x64, +0x54, +0x83, +0x6e, +0xcb, +0xb7, +0x77, +0xf7, +0x2b, +0x6e, +0x0f, +0x2e, +0x66, +0x12, +0x60, +0x55, +0x65, +0xfc, +0x43, +0xb3, +0x58, +0x73, +0x5b, +0xe8, +0x67, +0x04, +0x43, +0x02, +0xde, +0xb3, +0x89, +0xa0, +0x6d, +0x3a, +0x27, +0x79, +0x64, +0x5b, +0x0c, +0x16, +0x9e, +0x66, +0xb1, +0x8b, +0x87, +0x0c, +0x5d, +0xf2, +0xb6, +0x3d, +0x71, +0xdf, +0x42, +0x03, +0x8a, +0x06, +0x8d, +0xef, +0x1d, +0xa8, +0x96, +0x5c, +0xed, +0x31, +0x61, +0x5c, +0xa1, +0x34, +0xf6, +0x8c, +0x08, +0x60, +0x33, +0x07, +0x00, +0x3e, +0x79, +0x95, +0x1b, +0x43, +0x7f, +0xfe, +0xb6, +0xa6, +0xd4, +0x9d, +0x76, +0x72, +0xbf, +0xad, +0xc0, +0x15, +0xe8, +0x37, +0x31, +0xa3, +0x72, +0x63, +0x52, +0x1d, +0x1c, +0x5d, +0x51, +0x1b, +0xe1, +0xa9, +0xed, +0x60, +0x32, +0x3e, +0xa9, +0x50, +0x28, +0x53, +0x06, +0x59, +0xe2, +0xfc, +0xe7, +0x02, +0x64, +0x39, +0x21, +0x56, +0x4a, +0xa5, +0x40, +0x80, +0x81, +0xd5, +0x5a, +0x60, +0x7b, +0x68, +0x84, +0xf1, +0xe0, +0xb1, +0xb6, +0x5b, +0xdf, +0xa8, +0x1d, +0x6d, +0x65, +0x20, +0xc0, +0xa2, +0xb9, +0xd9, +0xbb, +0x00, +0xa6, +0xdb, +0x8b, +0x01, +0x53, +0x91, +0xfe, +0xc4, +0x51, +0x85, +0xb0, +0x96, +0x7f, +0xfd, +0x51, +0xdd, +0x14, +0x03, +0x67, +0x2e, +0x75, +0x1c, +0x76, +0xd3, +0x6e, +0xdd, +0x99, +0x55, +0x76, +0xe5, +0xab, +0x23, +0xfc, +0x4a, +0xd5, +0xc6, +0xe8, +0x2e, +0xca, +0x8a, +0xb3, +0xf6, +0x8c, +0x6c, +0xb0, +0xe9, +0xf2, +0xe7, +0x9e, +0x69, +0x41, +0xed, +0xf1, +0x6d, +0xd2, +0x86, +0xd8, +0x7e, +0xcb, +0x5d, +0x47, +0x6c, +0x85, +0x6a, +0x23, +0xed, +0x20, +0x40, +0x93, +0xb4, +0x20, +0xc7, +0xa5, +0xc9, +0xaf, +0x03, +0x15, +0xac, +0x19, +0xe5, +0x2a, +0x36, +0xdf, +0x6d, +0xc5, +0x8c, +0x80, +0x07, +0xce, +0x92, +0x0c, +0xd8, +0x06, +0x62, +0x0f, +0xdd, +0x48, +0x46, +0x1a, +0x53, +0xc7, +0x8a, +0x8c, +0x5d, +0x5d, +0xb4, +0xa1, +0x02, +0xd3, +0xa9, +0xb8, +0xf3, +0x94, +0x8f, +0x3f, +0xe5, +0x54, +0xd4, +0x11, +0x65, +0xb2, +0x5e, +0x09, +0x0b, +0x81, +0xe3, +0x75, +0xa7, +0x89, +0x81, +0x39, +0x6c, +0x46, +0xf6, +0x06, +0x9f, +0x27, +0x3b, +0xb6, +0x2d, +0x5f, +0x1d, +0x4b, +0xd4, +0x7b, +0x1d, +0x61, +0x74, +0x89, +0xe4, +0xe3, +0xbd, +0x98, +0x1b, +0xc4, +0x51, +0x3b, +0xa4, +0xfa, +0xe0, +0x92, +0xf7, +0xbe, +0xf2, +0x4d, +0xbb, +0xff, +0xad, +0x4f, +0x6d, +0x68, +0xc2, +0x79, +0x40, +0xaa, +0x9b, +0x8f, +0x0c, +0x32, +0x4b, +0x5f, +0x3e, +0xab, +0x59, +0x98, +0xb3, +0xf5, +0x1d, +0xac, +0x5e, +0xbc, +0x78, +0xd3, +0x01, +0x6c, +0x64, +0x15, +0x2f, +0xd8, +0x71, +0xa6, +0x2d, +0x45, +0xe1, +0x22, +0x42, +0xe4, +0x4e, +0x04, +0x3c, +0x7d, +0xf4, +0x40, +0x21, +0xb4, +0x67, +0x05, +0xa8, +0xe2, +0xf3, +0x72, +0x87, +0x4c, +0x7d, +0xd9, +0x1b, +0x65, +0x97, +0xf3, +0xc2, +0xe3, +0xe4, +0xc8, +0xd2, +0xde, +0xf6, +0xef, +0xdc, +0xbb, +0x44, +0x08, +0x5e, +0xe2, +0x45, +0x27, +0x01, +0xb0, +0xf6, +0x43, +0xe7, +0x3a, +0xf6, +0xdc, +0x9d, +0xed, +0xf3, +0xc5, +0x0c, +0xb8, +0x9c, +0x98, +0x3a, +0xd8, +0x36, +0xee, +0x96, +0x72, +0x67, +0xe7, +0x81, +0x91, +0xd5, +0x05, +0x0a, +0xe0, +0x82, +0xd5, +0x8f, +0xe8, +0xf9, +0xb0, +0xc9, +0xcf, +0x93, +0xe7, +0x04, +0xc5, +0xbc, +0x2b, +0x43, +0x56, +0x7e, +0xe8, +0x67, +0x7c, +0xe5, +0xfb, +0x49, +0xad, +0x5e, +0x9f, +0x25, +0x13, +0xde, +0x6e, +0x6e, +0xe9, +0xf1, +0xec, +0x87, +0x0b, +0x59, +0x81, +0x76, +0x84, +0x76, +0xb3, +0x24, +0xaf, +0x30, +0xfd, +0x27, +0x8b, +0xab, +0xd8, +0x00, +0x8b, +0x9b, +0x0c, +0xd2, +0xb2, +0x4e, +0x5e, +0x9d, +0x1d, +0x96, +0x01, +0x00, +0x67, +0xc1, +0x5f, +0x02, +0x20, +0xfd, +0x45, +0x6a, +0x01, +0x60, +0x58, +0x45, +0xca, +0x47, +0x21, +0x90, +0x5a, +0xc4, +0x43, +0x26, +0x1a, +0xd7, +0xa5, +0x4a, +0xb2, +0x5d, +0x2b, +0x35, +0x49, +0xfb, +0xa5, +0x17, +0x92, +0x21, +0x1e, +0x93, +0x96, +0x67, +0xa2, +0x7e, +0x36, +0x7a, +0xde, +0x5f, +0xbe, +0x7a, +0x58, +0x9d, +0xf8, +0x78, +0xa3, +0xfa, +0xc8, +0xd5, +0x17, +0xf0, +0x21, +0x97, +0x8c, +0x80, +0xb5, +0x4b, +0x3b, +0xbd, +0xbb, +0x41, +0x21, +0xa8, +0x50, +0x67, +0xf7, +0xe7, +0x19, +0x80, +0x10, +0x8e, +0xce, +0x04, +0x18, +0x3f, +0x51, +0x6b, +0x77, +0xd8, +0x9e, +0x16, +0xaf, +0xec, +0xef, +0x48, +0x16, +0x4d, +0x9e, +0x85, +0x38, +0x18, +0x3e, +0xd4, +0x28, +0x87, +0x60, +0x2a, +0xf6, +0x7f, +0x09, +0x86, +0x6f, +0x9c, +0x3c, +0x3a, +0xff, +0xab, +0xd0, +0x61, +0xa2, +0x97, +0x0d, +0x71, +0x94, +0x7e, +0xfd, +0xb9, +0x80, +0x02, +0x89, +0x6a, +0xb3, +0x84, +0x6c, +0x2a, +0x77, +0x62, +0xbe, +0x0b, +0xf4, +0xaf, +0xac, +0x7b, +0x7c, +0x8e, +0xca, +0x01, +0xba, +0x71, +0x78, +0x94, +0xfd, +0xb5, +0x39, +0xa4, +0x4d, +0x2f, +0x78, +0xcf, +0xca, +0x92, +0x0c, +0x1a, +0x99, +0x48, +0x4c, +0x11, +0x96, +0xb5, +0x4e, +0x41, +0x28, +0xe4, +0xa6, +0xfe, +0x4b, +0x72, +0x91, +0xe7, +0xd4, +0xdd, +0x9f, +0x12, +0xe6, +0x29, +0x38, +0xce, +0x45, +0xae, +0x02, +0xb8, +0x24, +0xae, +0xbd, +0xe9, +0x66, +0x08, +0x62, +0xa2, +0x2c, +0x2b, +0x00, +0xe2, +0x23, +0xd9, +0xc4, +0x48, +0xe4, +0xd3, +0xac, +0xbb, +0x34, +0xc7, +0xf0, +0xe3, +0x4f, +0xb9, +0x30, +0xea, +0xa2, +0x12, +0xf1, +0x30, +0x2c, +0x36, +0xde, +0x48, +0xf2, +0xb0, +0x4c, +0x43, +0x3f, +0x2e, +0x58, +0xe4, +0x20, +0xe3, +0x58, +0xcd, +0x31, +0x22, +0xf0, +0xa2, +0x2a, +0xe6, +0x19, +0x90, +0x55, +0x86, +0xf6, +0x55, +0x79, +0xd1, +0xd7, +0x46, +0x2f, +0xc0, +0xdc, +0x99, +0xe8, +0xf3, +0x6a, +0xdf, +0x7f, +0xeb, +0x24, +0x4a, +0x1e, +0x5a, +0x75, +0xde, +0x2f, +0x5c, +0x19, +0x61, +0x03, +0x53, +0x54, +0x6a, +0x3b, +0x18, +0x70, +0xb6, +0x4f, +0xf1, +0x9c, +0x0a, +0x59, +0x9d, +0x19, +0x92, +0x65, +0x8c, +0x83, +0x14, +0x2d, +0x44, +0x8a, +0x75, +0xa9, +0xf5, +0x90, +0xd2, +0x66, +0x4e, +0xfa, +0x69, +0x0f, +0x5b, +0x0b, +0x98, +0x65, +0xc8, +0x11, +0x42, +0x59, +0x7f, +0xdd, +0x1b, +0x75, +0x17, +0x31, +0x4c, +0x75, +0x58, +0xeb, +0x58, +0x63, +0x7d, +0xf2, +0xa6, +0xc2, +0x6e, +0xb7, +0x3f, +0x3e, +0x5e, +0x47, +0xad, +0xb7, +0x04, +0xe8, +0x05, +0xf8, +0xb2, +0xcf, +0x19, +0xf3, +0xd2, +0x85, +0xfe, +0x3e, +0x3e, +0xb1, +0x62, +0x08, +0x2c, +0x10, +0x07, +0x0d, +0x73, +0x90, +0x17, +0xfa, +0x9b, +0x56, +0x02, +0x75, +0xf9, +0x51, +0xe0, +0xe9, +0x1a, +0x7b, +0x9f, +0xb3, +0xf3, +0x98, +0xb8, +0x1c, +0x9c, +0xe1, +0xd5, +0x35, +0xae, +0xc8, +0x60, +0x48, +0x11, +0x09, +0x94, +0x6b, +0xd0, +0x8b, +0x15, +0xbc, +0x05, +0x68, +0xd3, +0x54, +0x8a, +0x51, +0x39, +0x5c, +0x42, +0x76, +0xce, +0xd8, +0xad, +0x89, +0x30, +0xc9, +0x05, +0x1c, +0xcc, +0x94, +0x3f, +0x0f, +0x90, +0x6f, +0x72, +0x2d, +0x85, +0x64, +0x9a, +0xb9, +0x23, +0xf9, +0x0b, +0xc3, +0x7c, +0x39, +0x0f, +0x97, +0x07, +0x97, +0xda, +0x58, +0x48, +0x33, +0x05, +0x23, +0xb8, +0x82, +0xe8, +0xd3, +0x53, +0x89, +0xaf, +0x33, +0x80, +0x22, +0x84, +0x0c, +0x95, +0x5c, +0x67, +0xb8, +0x77, +0x0c, +0x5c, +0xa2, +0x5f, +0x3d, +0x58, +0x0f, +0x27, +0xf3, +0x2f, +0xae, +0x48, +0xbd, +0x0b, +0x6f, +0x54, +0xfb, +0x67, +0x4c, +0xea, +0x32, +0x27, +0xf1, +0xfa, +0xe2, +0xb0, +0xec, +0x0b, +0x15, +0xb4, +0x70, +0xf6, +0x5c, +0xdd, +0x71, +0x60, +0xc3, +0xc1, +0xa8, +0x32, +0x65, +0xac, +0x7a, +0x77, +0x41, +0xe5, +0xa9, +0x6b, +0x11, +0x81, +0xfa, +0x34, +0x8d, +0xfb, +0xc1, +0x80, +0x6e, +0xc4, +0x60, +0x30, +0x07, +0xd4, +0x8b, +0x67, +0xbd, +0xaa, +0x8c, +0x9c, +0x64, +0xac, +0xdb, +0x0b, +0x24, +0x8b, +0x63, +0x6f, +0xe6, +0xbc, +0xe7, +0x33, +0xa4, +0x4a, +0x4c, +0xa7, +0x9f, +0x43, +0x53, +0xd2, +0xbb, +0x8f, +0x43, +0xc7, +0x3d, +0x78, +0x68, +0x3f, +0xa5, +0x3d, +0xca, +0x69, +0x84, +0xa6, +0x97, +0x2d, +0xc0, +0x7d, +0x31, +0x34, +0x55, +0x1d, +0x07, +0xb1, +0x5f, +0x40, +0x5c, +0x93, +0xb0, +0xbc, +0x7c, +0xb0, +0xbc, +0xe7, +0x12, +0xee, +0x6b, +0x2b, +0xd3, +0x4d, +0x67, +0x70, +0x3a, +0x9a, +0xf2, +0x3c, +0x7c, +0x81, +0xfa, +0xd7, +0xd9, +0x90, +0x91, +0x81, +0xb8, +0xb1, +0xf3, +0x48, +0x6a, +0x26, +0x4f, +0x0c, +0xce, +0xb0, +0x9e, +0xfd, +0x4a, +0x3a, +0xaf, +0xac, +0x5b, +0x3f, +0xbf, +0x44, +0x5a, +0xa3, +0x19, +0x1e, +0x4b, +0xe7, +0x36, +0x6a, +0xd7, +0x20, +0xae, +0xd7, +0x7d, +0x3b, +0xe7, +0xff, +0x3a, +0x86, +0x2e, +0xd0, +0x4a, +0x3e, +0xaf, +0x9f, +0x8e, +0x01, +0xbf, +0xf8, +0x4f, +0xc1, +0xe8, +0x6f, +0x74, +0xe1, +0x45, +0xd3, +0xf7, +0x04, +0x6a, +0x4b, +0x9d, +0xec, +0x33, +0x27, +0x76, +0xd7, +0xc5, +0xe1, +0xb0, +0x3b, +0x0e, +0x23, +0xec, +0xf0, +0x86, +0xd2, +0x1a, +0xbf, +0x3d, +0x04, +0x62, +0xb3, +0x6c, +0xb2, +0xeb, +0x17, +0x05, +0xa6, +0x0a, +0x8a, +0x7e, +0x83, +0x1c, +0xb6, +0x37, +0x09, +0xc6, +0x0b, +0x70, +0x3c, +0xb5, +0x93, +0x81, +0xd8, +0x93, +0xa0, +0x5f, +0x1e, +0x08, +0xe2, +0xc6, +0xe5, +0xc9, +0x72, +0xf1, +0xf1, +0xc1, +0xed, +0xd5, +0x58, +0x93, +0x83, +0xf8, +0x65, +0x67, +0x2e, +0x0d, +0xa9, +0xf1, +0x64, +0x12, +0xe6, +0x4c, +0xea, +0x15, +0x3f, +0x8c, +0x1a, +0xb6, +0xbf, +0xf6, +0xb9, +0x52, +0x35, +0x09, +0xb0, +0xe6, +0xf7, +0xcd, +0xf1, +0xa5, +0xaa, +0x81, +0xd1, +0x81, +0x6f, +0xb4, +0xa9, +0x66, +0x1f, +0xfc, +0x48, +0xc0, +0xb6, +0xd1, +0x8b, +0x06, +0x2f, +0xf6, +0xef, +0x1f, +0x0a, +0xe6, +0xce, +0x3a, +0x4a, +0x55, +0xbf, +0x6d, +0xf9, +0x4d, +0xd4, +0x08, +0x45, +0x4b, +0xc3, +0x66, +0x19, +0x92, +0x10, +0xe1, +0x17, +0x8e, +0x28, +0x91, +0x16, +0xbf, +0x3c, +0xee, +0xa3, +0xa6, +0x99, +0x92, +0x10, +0xe1, +0xf6, +0xcc, +0xac, +0xb8, +0x65, +0x0b, +0x43, +0x66, +0xf8, +0xe3, +0xe5, +0x3f, +0x24, +0x89, +0x47, +0x5d, +0x78, +0x43, +0xd0, +0x61, +0x17, +0xbd, +0x5b, +0x64, +0x54, +0x08, +0x45, +0x59, +0x93, +0xf6, +0x95, +0x8a, +0x41, +0x51, +0x62, +0x4b, +0x51, +0x02, +0x30, +0x73, +0xc7, +0x87, +0xc5, +0x4b, +0xa2, +0x97, +0x0f, +0xe8, +0x46, +0x5f, +0x7e, +0x2a, +0xe1, +0x30, +0x20, +0xb0, +0xfa, +0xe7, +0xce, +0x61, +0x42, +0x57, +0x6e, +0x21, +0xf3, +0x7a, +0xec, +0xe3, +0x25, +0xc7, +0x25, +0xf3, +0x67, +0xa7, +0x57, +0x40, +0x00, +0x02, +0xcf, +0x1c, +0x80, +0x77, +0x67, +0xbd, +0x70, +0xa1, +0x19, +0x92, +0x31, +0x75, +0x93, +0x27, +0x27, +0xb6, +0x82, +0xe4, +0xeb, +0x1d, +0x78, +0x48, +0xe7, +0xa5, +0x5e, +0x57, +0xef, +0x64, +0x28, +0x64, +0x1b, +0xf6, +0x11, +0xb2, +0x03, +0x9d, +0xb9, +0x18, +0x02, +0x27, +0xf7, +0xbe, +0x9d, +0x55, +0xfc, +0x00, +0xd2, +0xc7, +0xae, +0xad, +0x0b, +0xc5, +0xe9, +0x42, +0x41, +0x48, +0xd8, +0x32, +0xcf, +0xf6, +0x0f, +0xf5, +0xbc, +0x97, +0xc6, +0x99, +0x47, +0x76, +0xbd, +0x89, +0x06, +0x0f, +0x63, +0x0c, +0x51, +0xd4, +0x5e, +0xea, +0x48, +0xa8, +0xa2, +0x56, +0x1c, +0x79, +0x84, +0x86, +0x40, +0x88, +0x41, +0x76, +0x55, +0xfc, +0xc2, +0xd7, +0xfd, +0xc9, +0xc7, +0x80, +0x61, +0x35, +0xa7, +0x43, +0x20, +0xf7, +0xeb, +0x6c, +0x66, +0x13, +0xb0, +0xec, +0x02, +0x75, +0x3e, +0x4b, +0xaf, +0xb9, +0x5d, +0x40, +0xda, +0xd6, +0x6e, +0x2d, +0x39, +0x54, +0xc2, +0x95, +0x35, +0x54, +0x25, +0x72, +0xe1, +0x78, +0xb8, +0xeb, +0xc1, +0x16, +0x58, +0x0f, +0x9c, +0x9b, +0xb4, +0xea, +0x37, +0xec, +0x3b, +0x11, +0xba, +0xd5, +0x8a, +0xa9, +0xe3, +0x98, +0x00, +0x51, +0x1c, +0x14, +0xe0, +0x40, +0x96, +0xe5, +0xe9, +0xf2, +0x21, +0x22, +0xb1, +0x23, +0x60, +0x78, +0xd3, +0x17, +0xf8, +0x7a, +0xa5, +0xa8, +0xba, +0x20, +0xd3, +0x15, +0x1e, +0x32, +0xe4, +0x5e, +0x15, +0x48, +0xae, +0xa9, +0xe5, +0xb8, +0x33, +0xec, +0xe8, +0xa2, +0x42, +0xac, +0xbf, +0x10, +0x84, +0x53, +0x87, +0x19, +0xb4, +0x5f, +0x76, +0x4d, +0x01, +0x9d, +0x56, +0x74, +0xd9, +0x5c, +0x97, +0xe7, +0x88, +0xea, +0x3a, +0xbf, +0xdc, +0x4c, +0x33, +0x8a, +0x16, +0xb9, +0x5b, +0xfa, +0xd8, +0x42, +0xa7, +0xbb, +0x3c, +0x04, +0x27, +0x78, +0x49, +0x81, +0x2a, +0x5a, +0x7d, +0x7c, +0x23, +0xa8, +0xba, +0xf7, +0x9a, +0x9f, +0xd2, +0x66, +0x3e, +0x38, +0x3c, +0x75, +0xf9, +0xd1, +0x30, +0x26, +0x30, +0x6e, +0x5a, +0x6e, +0xdc, +0x6a, +0x69, +0x32, +0x50, +0x33, +0x47, +0x9e, +0xa4, +0xa8, +0x64, +0x66, +0xf0, +0x8a, +0xe4, +0xfd, +0x27, +0x6f, +0x51, +0x25, +0x8b, +0x43, +0x74, +0xc9, +0x8e, +0xbd, +0x88, +0x31, +0xbe, +0xec, +0x65, +0xd2, +0xcb, +0x8d, +0x5a, +0x13, +0x48, +0x16, +0x8c, +0x61, +0x0b, +0x11, +0xf6, +0xc6, +0x66, +0xae, +0xc3, +0xcc, +0x0c, +0xd2, +0xe1, +0x9f, +0x82, +0x41, +0x3f, +0x56, +0xf9, +0x73, +0xef, +0xdc, +0x30, +0x50, +0xcf, +0xb6, +0x7f, +0xbc, +0xd0, +0xb3, +0x10, +0xab, +0x24, +0xe4, +0xec, +0xad, +0x18, +0x8c, +0x39, +0x2d, +0x30, +0x4c, +0xc5, +0x40, +0x0d, +0xf6, +0xac, +0xd6, +0x18, +0x5d, +0x96, +0xbf, +0x5f, +0x71, +0x75, +0x96, +0x22, +0x97, +0x0f, +0x02, +0x94, +0x6e, +0xa6, +0xae, +0x6d, +0x8f, +0x1e, +0xca, +0x12, +0x9b, +0x2a, +0x1c, +0xce, +0xa9, +0xee, +0xfd, +0x12, +0x8e, +0xfc, +0xed, +0x09, +0x33, +0xba, +0xf4, +0x1a, +0x15, +0xf6, +0x9d, +0x87, +0x16, +0x43, +0x7c, +0x78, +0x57, +0xe1, +0x44, +0xc9, +0xeb, +0x1f, +0x58, +0x4d, +0xc1, +0x49, +0x11, +0x5c, +0xb2, +0x11, +0xa8, +0x55, +0x16, +0xf1, +0xc6, +0x50, +0xe9, +0x87, +0x89, +0xf6, +0xcf, +0xd8, +0x9c, +0x51, +0xa7, +0xbc, +0x5b, +0x31, +0x6d, +0x4d, +0x51, +0xd0, +0x4c, +0xbc, +0x0d, +0x58, +0x2d, +0x7b, +0x88, +0x7a, +0xf9, +0x8e, +0xd6, +0x40, +0x4d, +0xbb, +0xbe, +0xc4, +0xe5, +0x07, +0xfc, +0xd9, +0x7b, +0x6d, +0xa6, +0x42, +0x57, +0x8f, +0x02, +0x94, +0x4f, +0xe4, +0x2a, +0x65, +0xe2, +0x19, +0x5a, +0x50, +0xe1, +0x25, +0x65, +0x4a, +0x60, +0xc2, +0xcd, +0xa8, +0xec, +0x05, +0x2e, +0x87, +0x7b, +0x95, +0xb7, +0x4f, +0xa0, +0x0b, +0x1b, +0x4a, +0x7f, +0x92, +0xc8, +0x90, +0xee, +0x89, +0x1e, +0x10, +0xd2, +0x85, +0xe4, +0x9f, +0x63, +0xc8, +0x12, +0xbb, +0x4e, +0xb8, +0xcf, +0x0a, +0xec, +0x18, +0x4e, +0xe6, +0x7c, +0xb3, +0x33, +0x26, +0xc7, +0x1f, +0xd2, +0x04, +0x23, +0xea, +0x07, +0x0c, +0x5f, +0x90, +0xbd, +0xa7, +0x6a, +0x0f, +0x4a, +0xd6, +0x10, +0x01, +0x3c, +0x12, +0x29, +0x2e, +0x96, +0xc0, +0x4d, +0xbb, +0xbe, +0xe5, +0xa7, +0x83, +0xd5, +0x6a, +0x3c, +0xe3, +0x5b, +0xb8, +0xf2, +0x5c, +0x6d, +0x1f, +0xa6, +0xf3, +0x12, +0x24, +0xf6, +0xd6, +0x3b, +0x10, +0x14, +0x09, +0x07, +0x82, +0xe8, +0x30, +0x6a, +0x99, +0xdc, +0x95, +0x01, +0x9c, +0xd4, +0x68, +0x3b, +0xca, +0x98, +0x12, +0xab, +0x77, +0x25, +0x15, +0x7d, +0x10, +0x32, +0x45, +0x98, +0xcd, +0x7a, +0xdf, +0x71, +0x8a, +0x75, +0xc1, +0x1c, +0xd4, +0x68, +0x25, +0xeb, +0xbb, +0x54, +0x27, +0x6f, +0x2a, +0xf7, +0xb9, +0x98, +0x03, +0x27, +0xde, +0x24, +0xa8, +0xbb, +0x98, +0xc2, +0x84, +0xff, +0x9b, +0x51, +0xd8, +0x53, +0x50, +0xda, +0xf5, +0x88, +0xaa, +0x87, +0x2f, +0xae, +0xd6, +0xea, +0x6b, +0xde, +0xc8, +0xd7, +0xa7, +0x28, +0x65, +0x81, +0xe8, +0xb2, +0x3b, +0x1d, +0x4f, +0x75, +0x8f, +0x9f, +0x7a, +0x74, +0x8e, +0xc1, +0x5f, +0x9a, +0xa8, +0x9d, +0xfa, +0x03, +0xa3, +0x71, +0x9b, +0x37, +0x6d, +0xd5, +0x0b, +0xf5, +0xe1, +0xa1, +0x1b, +0x01, +0x6a, +0xc6, +0x67, +0xaa, +0xea, +0x2c, +0x9d, +0xa4, +0xd2, +0x6e, +0xfc, +0xde, +0x2e, +0x7f, +0x94, +0x69, +0xe5, +0x4a, +0xe0, +0x01, +0x48, +0x3c, +0x6b, +0xf7, +0x1e, +0xb6, +0x0b, +0x5f, +0xf9, +0x2e, +0x07, +0xc5, +0xe8, +0xae, +0x37, +0x1b, +0xbc, +0x3c, +0xd8, +0xd5, +0x0b, +0x91, +0x9e, +0x80, +0x24, +0xf5, +0x06, +0x0c, +0x0e, +0x98, +0x07, +0x96, +0x2d, +0x19, +0xdc, +0x58, +0x93, +0xcc, +0xfb, +0x4e, +0xeb, +0xbd, +0x0f, +0xf5, +0xaf, +0x01, +0xfa, +0xf1, +0x7c, +0x43, +0x8c, +0xb8, +0x56, +0x3e, +0xbe, +0x77, +0x4e, +0x2b, +0xf7, +0xbb, +0xb7, +0x45, +0x47, +0xcd, +0xcc, +0xa6, +0x4c, +0x72, +0x7b, +0x6a, +0x2a, +0x70, +0x13, +0x07, +0xfd, +0xb8, +0x9c, +0x98, +0x3a, +0xd8, +0x23, +0x67, +0x5b, +0x34, +0xd5, +0x14, +0x0c, +0xab, +0x77, +0x1f, +0xf8, +0x3d, +0x5a, +0x9f, +0x92, +0xb7, +0x2c, +0xad, +0x31, +0xde, +0x61, +0x07, +0xb3, +0x6b, +0xf7, +0x38, +0x15, +0x95, +0x46, +0x14, +0x48, +0x53, +0x69, +0x52, +0x66, +0x07, +0x6d, +0x83, +0x71, +0x8a, +0x67, +0x25, +0x20, +0x0f, +0xfe, +0xd7, +0x02, +0xd7, +0x6e, +0x2c, +0xd2, +0x1a, +0x0a, +0x5d, +0xfd, +0x0f, +0x74, +0xe3, +0xa4, +0x36, +0x07, +0x9a, +0xdf, +0xd4, +0x79, +0xbf, +0xef, +0x59, +0xc0, +0x44, +0x52, +0x87, +0x9a, +0x6e, +0x1d, +0x0e, +0xee, +0xde, +0x2e, +0x1a, +0xa9, +0x8f, +0x3a, +0xc9, +0xba, +0xec, +0x99, +0x78, +0x2d, +0x55, +0x6b, +0x14, +0xc2, +0x06, +0xd5, +0xfc, +0x93, +0x53, +0x4d, +0x11, +0x8c, +0xf8, +0xfa, +0x79, +0x7c, +0xa6, +0x64, +0xae, +0x61, +0xb8, +0x7b, +0x94, +0x56, +0xa6, +0x39, +0x78, +0x9a, +0xe5, +0xc7, +0xdf, +0x18, +0x63, +0x23, +0x9c, +0xfa, +0x66, +0xbb, +0xb7, +0x5a, +0x27, +0x4c, +0xd1, +0xa1, +0x83, +0x22, +0xb3, +0x52, +0x49, +0x35, +0xb0, +0x22, +0x83, +0x59, +0x12, +0x00, +0x16, +0x98, +0xdd, +0xad, +0xc2, +0x94, +0xf9, +0xd3, +0x7b, +0x64, +0x7f, +0x44, +0x3e, +0x3c, +0x8b, +0x9a, +0x83, +0x9c, +0x69, +0x6b, +0xe4, +0xdf, +0x9f, +0xed, +0x54, +0x1f, +0xe5, +0x5d, +0x7a, +0x05, +0x82, +0xb3, +0xdd, +0xef, +0xfc, +0x53, +0x96, +0xb0, +0x2c, +0x5a, +0xf8, +0xdf, +0x9c, +0x8b, +0x16, +0x4e, +0xdf, +0xda, +0x4d, +0x09, +0x09, +0x69, +0x50, +0x03, +0x65, +0xd8, +0x73, +0x70, +0xe8, +0x86, +0xbf, +0xbb, +0x35, +0xce, +0xb2, +0x46, +0xcb, +0x02, +0x00, +0x5b, +0xb4, +0xe2, +0xc6, +0x8f, +0x2f, +0x98, +0xaf, +0x87, +0x4b, +0x48, +0x45, +0xed, +0xcc, +0x1d, +0xe6, +0x58, +0xd6, +0xf2, +0x50, +0x25, +0x9f, +0x52, +0xc7, +0xcb, +0x8a, +0x17, +0x9d, +0x5b, +0xe5, +0xc8, +0xd7, +0x72, +0xb7, +0x52, +0xb2, +0xc4, +0x98, +0xe3, +0x7a, +0x17, +0x3e, +0xc6, +0x60, +0xa7, +0x97, +0xb0, +0xcf, +0x18, +0x81, +0x53, +0x84, +0x4c, +0xd5, +0x17, +0x32, +0x03, +0x13, +0x39, +0x51, +0x09, +0x10, +0xe3, +0x77, +0x49, +0x4f, +0x62, +0x01, +0xbf, +0x8c, +0x9a, +0xe0, +0x41, +0x9e, +0x89, +0x74, +0x36, +0xf9, +0x96, +0x86, +0x2e, +0x96, +0x1c, +0x4a, +0xb7, +0x2b, +0x4a, +0x97, +0xbc, +0x99, +0x40, +0xa3, +0xe0, +0x3d, +0xc8, +0xad, +0x2f, +0xdf, +0x4f, +0x2c, +0xc4, +0x69, +0x82, +0x9f, +0x9b, +0x81, +0x0c, +0x61, +0x5c, +0xa5, +0x9d, +0x8c, +0x89, +0xc0, +0x2c, +0xb4, +0x4a, +0x33, +0x4e, +0xeb, +0xa2, +0x56, +0x40, +0xc0, +0xc2, +0x46, +0xaf, +0x6a, +0xfc, +0x67, +0xd1, +0x80, +0x5e, +0xc5, +0x6d, +0x84, +0x43, +0x27, +0x3f, +0x55, +0x15, +0x96, +0x6a, +0xa0, +0xa5, +0xda, +0xb7, +0xff, +0xb7, +0x75, +0x6e, +0x4c, +0x49, +0x91, +0x9d, +0x22, +0xa3, +0x46, +0xea, +0xed, +0x9a, +0x00, +0xe2, +0x32, +0xc3, +0xd6, +0xa9, +0x71, +0x20, +0x55, +0xa3, +0x19, +0xed, +0xf8, +0x4f, +0xa7, +0x12, +0x9c, +0x66, +0x87, +0xaf, +0x4e, +0xb7, +0xf0, +0xdb, +0xbf, +0xef, +0xf0, +0xf6, +0xaf, +0xea, +0xda, +0x09, +0xfe, +0xde, +0x38, +0x5c, +0xa5, +0xa2, +0xdf, +0x99, +0x45, +0xa8, +0xe4, +0xe7, +0x92, +0xac, +0x67, +0xaa, +0x4f, +0xbf, +0x77, +0x3e, +0xa2, +0x40, +0x49, +0x22, +0x4a, +0x1e, +0x3b, +0xaa, +0x70, +0x7f, +0x95, +0xaf, +0x37, +0x4b, +0xfc, +0x99, +0xe2, +0xe0, +0xba, +0xd7, +0x34, +0xce, +0x55, +0x88, +0x5b, +0x84, +0x1b, +0x57, +0xc4, +0x80, +0x03, +0x53, +0xc9, +0x2f, +0x93, +0x04, +0x4d, +0xd5, +0x96, +0xe5, +0x70, +0xa6, +0x6e, +0x63, +0x5d, +0x9d, +0x6c, +0xdb, +0x02, +0x0a, +0xa9, +0xda, +0x8b, +0x53, +0xdc, +0xd9, +0x9a, +0xc5, +0x94, +0x2c, +0x91, +0x92, +0x2a, +0xde, +0xbb, +0x8b, +0x13, +0xb9, +0x19, +0x96, +0x64, +0xcc, +0xf2, +0x64, +0x39, +0xb7, +0x75, +0x49, +0xe9, +0x86, +0xc2, +0x86, +0x62, +0xd9, +0x24, +0xd3, +0x81, +0x35, +0x49, +0xfc, +0xa0, +0xa5, +0xa0, +0x93, +0x05, +0x64, +0xb4, +0x1a, +0x57, +0xce, +0x0c, +0x90, +0x02, +0x27, +0xc5, +0x7a, +0x2b, +0x5d, +0xae, +0x3e, +0xd5, +0xdd, +0x10, +0x7c, +0x14, +0xea, +0x3a, +0x08, +0xac, +0x72, +0x4e, +0x90, +0x3d, +0x3b, +0x7c, +0x86, +0x2e, +0xeb, +0xd4, +0x06, +0x70, +0xe6, +0xc7, +0xfb, +0x5f, +0xbd, +0x18, +0xf4, +0x11, +0xa4, +0x1a, +0x93, +0xc3, +0xbe, +0xd9, +0xfb, +0x26, +0x48, +0x2f, +0x37, +0x3c, +0xd0, +0x03, +0x47, +0x1a, +0xf7, +0x62, +0x19, +0x24, +0x5c, +0xf4, +0xa8, +0x92, +0x20, +0x7a, +0xf2, +0x9e, +0x2a, +0xc5, +0x95, +0xa2, +0xfb, +0xa4, +0xea, +0x85, +0xd8, +0x56, +0xb7, +0x70, +0xd1, +0x60, +0x30, +0xa5, +0x30, +0x82, +0x70, +0xdc, +0x7a, +0x65, +0x8a, +0x36, +0x3f, +0x5b, +0x0c, +0xae, +0x54, +0x7c, +0xd3, +0x57, +0x84, +0x7b, +0x3a, +0x65, +0x18, +0x81, +0xee, +0x05, +0x9b, +0x44, +0x4d, +0xb8, +0xda, +0xa2, +0xa1, +0xc9, +0x15, +0xd3, +0x73, +0x03, +0x0e, +0x43, +0xe9, +0x8e, +0x15, +0xf9, +0xbe, +0xc6, +0xc5, +0x8a, +0xe5, +0xc0, +0x1e, +0xc2, +0x37, +0x9e, +0x2a, +0x26, +0xa5, +0xa0, +0xbd, +0x24, +0x5f, +0xb9, +0xc1, +0xab, +0x34, +0x48, +0xb9, +0x5d, +0x98, +0xb4, +0x65, +0x18, +0xf3, +0x63, +0x19, +0x44, +0x1b, +0x11, +0x16, +0xff, +0xdc, +0xf1, +0x79, +0x08, +0x86, +0x0f, +0x52, +0x98, +0x73, +0xc4, +0x92, +0x90, +0x2b, +0x47, +0x09, +0xd0, +0x43, +0x6c, +0x2f, +0x20, +0xeb, +0xdc, +0xda, +0xc5, +0x08, +0x7b, +0x94, +0x42, +0x30, +0x6a, +0xc7, +0xda, +0x8c, +0xc3, +0x76, +0xa7, +0xa5, +0xcc, +0x62, +0x13, +0x00, +0x60, +0x31, +0x58, +0x44, +0x9b, +0xf5, +0x64, +0x14, +0xf5, +0x11, +0xc5, +0x54, +0x52, +0x83, +0xd4, +0x73, +0x01, +0x16, +0x0e, +0xb3, +0x7a, +0x29, +0x69, +0x35, +0x56, +0xd4, +0xee, +0x8a, +0x17, +0xa2, +0x99, +0x24, +0x9c, +0xd7, +0x8f, +0xdb, +0x55, +0xb5, +0x3e +}; diff --git a/drivers/sensors/pimoroni_trackball.c b/drivers/sensors/pimoroni_trackball.c new file mode 100644 index 0000000000..c0ac644f70 --- /dev/null +++ b/drivers/sensors/pimoroni_trackball.c @@ -0,0 +1,140 @@ +/* Copyright 2020 Christopher Courtney, aka Drashna Jael're  (@drashna) <[email protected]> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#include "pimoroni_trackball.h" +#include "i2c_master.h" + +static uint8_t scrolling      = 0; +static int16_t x_offset       = 0; +static int16_t y_offset       = 0; +static int16_t h_offset       = 0; +static int16_t v_offset       = 0; +static float   precisionSpeed = 1; + +static uint16_t i2c_timeout_timer; + +#ifndef PIMORONI_I2C_TIMEOUT +#    define PIMORONI_I2C_TIMEOUT 100 +#endif +#ifndef I2C_WAITCHECK +#    define I2C_WAITCHECK 1000 +#endif +#ifndef MOUSE_DEBOUNCE +#    define MOUSE_DEBOUNCE 5 +#endif + +void trackball_set_rgbw(uint8_t red, uint8_t green, uint8_t blue, uint8_t white) { +    uint8_t data[] = {0x00, red, green, blue, white}; +    i2c_transmit(TRACKBALL_WRITE, data, sizeof(data), PIMORONI_I2C_TIMEOUT); +} + +int16_t mouse_offset(uint8_t positive, uint8_t negative, int16_t scale) { +    int16_t offset    = (int16_t)positive - (int16_t)negative; +    int16_t magnitude = (int16_t)(scale * offset * offset * precisionSpeed); +    return offset < 0 ? -magnitude : magnitude; +} + +void update_member(int8_t* member, int16_t* offset) { +    if (*offset > 127) { +        *member = 127; +        *offset -= 127; +    } else if (*offset < -127) { +        *member = -127; +        *offset += 127; +    } else { +        *member = *offset; +        *offset = 0; +    } +} + +__attribute__((weak)) void trackball_check_click(bool pressed, report_mouse_t* mouse) { +    if (pressed) { +        mouse->buttons |= MOUSE_BTN1; +    } else { +        mouse->buttons &= ~MOUSE_BTN1; +    } +} + +float trackball_get_precision(void) { return precisionSpeed; } +void  trackball_set_precision(float precision) { precisionSpeed = precision; } +bool  trackball_is_scrolling(void) { return scrolling; } +void  trackball_set_scrolling(bool scroll) { scrolling = scroll; } + +__attribute__((weak)) void pointing_device_init(void) { i2c_init(); trackball_set_rgbw(0x00, 0x00, 0x00, 0x00); } + +void pointing_device_task(void) { +    static bool     debounce; +    static uint16_t debounce_timer; +    uint8_t         state[5] = {}; +    if (timer_elapsed(i2c_timeout_timer) > I2C_WAITCHECK) { +        if (i2c_readReg(TRACKBALL_READ, 0x04, state, 5, PIMORONI_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) { +            if (!state[4] && !debounce) { +                if (scrolling) { +#ifdef PIMORONI_TRACKBALL_INVERT_X +                    h_offset += mouse_offset(state[2], state[3], 1); +#else +                    h_offset -= mouse_offset(state[2], state[3], 1); +#endif +#ifdef PIMORONI_TRACKBALL_INVERT_Y +                    v_offset += mouse_offset(state[1], state[0], 1); +#else +                    v_offset -= mouse_offset(state[1], state[0], 1); +#endif +                } else { +#ifdef PIMORONI_TRACKBALL_INVERT_X +                    x_offset -= mouse_offset(state[2], state[3], 5); +#else +                    x_offset += mouse_offset(state[2], state[3], 5); +#endif +#ifdef PIMORONI_TRACKBALL_INVERT_Y +                    y_offset -= mouse_offset(state[1], state[0], 5); +#else +                    y_offset += mouse_offset(state[1], state[0], 5); +#endif +                } +            } else { +                if (state[4]) { +                    debounce       = true; +                    debounce_timer = timer_read(); +                } +            } +        } else { +            i2c_timeout_timer = timer_read(); +        } +    } + +    if (timer_elapsed(debounce_timer) > MOUSE_DEBOUNCE) debounce = false; + +    report_mouse_t mouse = pointing_device_get_report(); + +#ifdef PIMORONI_TRACKBALL_CLICK +    trackball_check_click(state[4] & (1 << 7), &mouse); +#endif + +#ifndef PIMORONI_TRACKBALL_ROTATE +    update_member(&mouse.x, &x_offset); +    update_member(&mouse.y, &y_offset); +    update_member(&mouse.h, &h_offset); +    update_member(&mouse.v, &v_offset); +#else +    update_member(&mouse.x, &y_offset); +    update_member(&mouse.y, &x_offset); +    update_member(&mouse.h, &v_offset); +    update_member(&mouse.v, &h_offset); +#endif +    pointing_device_set_report(mouse); +    pointing_device_send(); +} diff --git a/drivers/sensors/pimoroni_trackball.h b/drivers/sensors/pimoroni_trackball.h new file mode 100644 index 0000000000..a30fb0bb8c --- /dev/null +++ b/drivers/sensors/pimoroni_trackball.h @@ -0,0 +1,35 @@ +/* Copyright 2020 Christopher Courtney, aka Drashna Jael're  (@drashna) <[email protected]> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "quantum.h" +#include "pointing_device.h" + +#ifndef TRACKBALL_ADDRESS +#    define TRACKBALL_ADDRESS 0x0A +#endif +#define TRACKBALL_WRITE ((TRACKBALL_ADDRESS << 1) | I2C_WRITE) +#define TRACKBALL_READ ((TRACKBALL_ADDRESS << 1) | I2C_READ) + +void trackball_set_rgbw(uint8_t red, uint8_t green, uint8_t blue, uint8_t white); +void trackball_check_click(bool pressed, report_mouse_t *mouse); +void trackball_register_button(bool pressed, enum mouse_buttons button); + +float trackball_get_precision(void); +void  trackball_set_precision(float precision); +bool  trackball_is_scrolling(void); +void  trackball_set_scrolling(bool scroll); diff --git a/drivers/sensors/pmw3360.c b/drivers/sensors/pmw3360.c new file mode 100644 index 0000000000..91ee87b957 --- /dev/null +++ b/drivers/sensors/pmw3360.c @@ -0,0 +1,237 @@ +/* Copyright 2020 Christopher Courtney, aka Drashna Jael're  (@drashna) <[email protected]> + * Copyright 2019 Sunjun Kim + * Copyright 2020 Ploopy Corporation + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#include "wait.h" +#include "debug.h" +#include "print.h" +#include "pmw3360.h" +#include "pmw3360_firmware.h" + +bool _inBurst = false; + +#ifndef PMW_CPI +#    define PMW_CPI 1600 +#endif +#ifndef PMW_CLOCK_SPEED +#    define PMW_CLOCK_SPEED 70000000 +#endif +#ifndef SPI_MODE +#    define SPI_MODE 3 +#endif +#ifndef SPI_DIVISOR +#    define SPI_DIVISOR (F_CPU / PMW_CLOCK_SPEED) +#endif +#ifndef ROTATIONAL_TRANSFORM_ANGLE +#    define ROTATIONAL_TRANSFORM_ANGLE 0x00 +#endif +#ifndef PMW_CS_PIN +#    define PMW_CS_PIN SPI_SS_PIN +#endif + +void print_byte(uint8_t byte) { dprintf("%c%c%c%c%c%c%c%c|", (byte & 0x80 ? '1' : '0'), (byte & 0x40 ? '1' : '0'), (byte & 0x20 ? '1' : '0'), (byte & 0x10 ? '1' : '0'), (byte & 0x08 ? '1' : '0'), (byte & 0x04 ? '1' : '0'), (byte & 0x02 ? '1' : '0'), (byte & 0x01 ? '1' : '0')); } + +bool spi_start_adv(void) { +    bool status = spi_start(PMW_CS_PIN, false, SPI_MODE, SPI_DIVISOR); +    wait_us(1); +    return status; +} + +void spi_stop_adv(void) { +    wait_us(1); +    spi_stop(); +} + +spi_status_t spi_write_adv(uint8_t reg_addr, uint8_t data) { +    if (reg_addr != REG_Motion_Burst) { +        _inBurst = false; +    } + +    spi_start_adv(); +    // send address of the register, with MSBit = 1 to indicate it's a write +    spi_status_t status = spi_write(reg_addr | 0x80); +    status              = spi_write(data); + +    // tSCLK-NCS for write operation +    wait_us(20); + +    // tSWW/tSWR (=120us) minus tSCLK-NCS. Could be shortened, but is looks like a safe lower bound +    wait_us(100); +    spi_stop(); +    return status; +} + +uint8_t spi_read_adv(uint8_t reg_addr) { +    spi_start_adv(); +    // send adress of the register, with MSBit = 0 to indicate it's a read +    spi_write(reg_addr & 0x7f); + +    uint8_t data = spi_read(); + +    // tSCLK-NCS for read operation is 120ns +    wait_us(1); + +    //  tSRW/tSRR (=20us) minus tSCLK-NCS +    wait_us(19); + +    spi_stop(); +    return data; +} + +void pmw_set_cpi(uint16_t cpi) { +    uint8_t cpival = constrain((cpi / 100) - 1, 0, 0x77);  // limits to 0--119 + +    spi_start_adv(); +    spi_write_adv(REG_Config1, cpival); +    spi_stop(); +} + +uint16_t pmw_get_cpi(void) { +    uint8_t cpival = spi_read_adv(REG_Config1); +    return (uint16_t)(cpival & 0xFF) * 100; +} + +bool pmw_spi_init(void) { +    setPinOutput(PMW_CS_PIN); + +    spi_init(); +    _inBurst = false; + +    spi_stop(); +    spi_start_adv(); +    spi_stop(); + +    spi_write_adv(REG_Shutdown, 0xb6);  // Shutdown first +    wait_ms(300); + +    spi_start_adv(); +    wait_us(40); +    spi_stop_adv(); +    wait_us(40); + +    spi_write_adv(REG_Power_Up_Reset, 0x5a); +    wait_ms(50); + +    spi_read_adv(REG_Motion); +    spi_read_adv(REG_Delta_X_L); +    spi_read_adv(REG_Delta_X_H); +    spi_read_adv(REG_Delta_Y_L); +    spi_read_adv(REG_Delta_Y_H); + +    pmw_upload_firmware(); + +    spi_stop_adv(); + +    wait_ms(10); +    pmw_set_cpi(PMW_CPI); + +    wait_ms(1); + +    spi_write_adv(REG_Config2, 0x00); + +    spi_write_adv(REG_Angle_Tune, constrain(ROTATIONAL_TRANSFORM_ANGLE, -30, 30)); + +    bool init_success = pmw_check_signature(); + +    writePinLow(PMW_CS_PIN); + +    return init_success; +} + +void pmw_upload_firmware(void) { +    spi_write_adv(REG_SROM_Enable, 0x1d); + +    wait_ms(10); + +    spi_write_adv(REG_SROM_Enable, 0x18); + +    spi_start_adv(); +    spi_write(REG_SROM_Load_Burst | 0x80); +    wait_us(15); + +    unsigned char c; +    for (int i = 0; i < firmware_length; i++) { +        c = (unsigned char)pgm_read_byte(firmware_data + i); +        spi_write(c); +        wait_us(15); +    } +    wait_us(200); + +    spi_read_adv(REG_SROM_ID); + +    spi_write_adv(REG_Config2, 0x00); + +    spi_stop(); +    wait_ms(10); +} + +bool pmw_check_signature(void) { +    uint8_t pid      = spi_read_adv(REG_Product_ID); +    uint8_t iv_pid   = spi_read_adv(REG_Inverse_Product_ID); +    uint8_t SROM_ver = spi_read_adv(REG_SROM_ID); +    return (pid == 0x42 && iv_pid == 0xBD && SROM_ver == 0x04);  // signature for SROM 0x04 +} + +report_pmw_t pmw_read_burst(void) { +    if (!_inBurst) { +        dprintf("burst on"); +        spi_write_adv(REG_Motion_Burst, 0x00); +        _inBurst = true; +    } + +    spi_start_adv(); +    spi_write(REG_Motion_Burst); +    wait_us(35);  // waits for tSRAD + +    report_pmw_t data; +    data.motion = 0; +    data.dx     = 0; +    data.mdx    = 0; +    data.dy     = 0; +    data.mdx    = 0; + +    data.motion = spi_read(); +    spi_write(0x00);  // skip Observation +    data.dx  = spi_read(); +    data.mdx = spi_read(); +    data.dy  = spi_read(); +    data.mdy = spi_read(); + +    spi_stop(); + +    print_byte(data.motion); +    print_byte(data.dx); +    print_byte(data.mdx); +    print_byte(data.dy); +    print_byte(data.mdy); +    dprintf("\n"); + +    data.isMotion    = (data.motion & 0x80) != 0; +    data.isOnSurface = (data.motion & 0x08) == 0; +    data.dx |= (data.mdx << 8); +    data.dx = data.dx * -1; +    data.dy |= (data.mdy << 8); +    data.dy = data.dy * -1; + +    spi_stop(); + +    if (data.motion & 0b111) {  // panic recovery, sometimes burst mode works weird. +        _inBurst = false; +    } + +    return data; +} diff --git a/drivers/sensors/pmw3360.h b/drivers/sensors/pmw3360.h new file mode 100644 index 0000000000..d5b1741791 --- /dev/null +++ b/drivers/sensors/pmw3360.h @@ -0,0 +1,104 @@ +/* Copyright 2020 Christopher Courtney, aka Drashna Jael're  (@drashna) <[email protected]> + * Copyright 2019 Sunjun Kim + * Copyright 2020 Ploopy Corporation + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "spi_master.h" + +// Registers +#define REG_Product_ID  0x00 +#define REG_Revision_ID 0x01 +#define REG_Motion  0x02 +#define REG_Delta_X_L 0x03 +#define REG_Delta_X_H 0x04 +#define REG_Delta_Y_L 0x05 +#define REG_Delta_Y_H 0x06 +#define REG_SQUAL 0x07 +#define REG_Raw_Data_Sum  0x08 +#define REG_Maximum_Raw_data  0x09 +#define REG_Minimum_Raw_data  0x0A +#define REG_Shutter_Lower 0x0B +#define REG_Shutter_Upper 0x0C +#define REG_Control 0x0D +#define REG_Config1 0x0F +#define REG_Config2 0x10 +#define REG_Angle_Tune  0x11 +#define REG_Frame_Capture 0x12 +#define REG_SROM_Enable 0x13 +#define REG_Run_Downshift 0x14 +#define REG_Rest1_Rate_Lower  0x15 +#define REG_Rest1_Rate_Upper  0x16 +#define REG_Rest1_Downshift 0x17 +#define REG_Rest2_Rate_Lower  0x18 +#define REG_Rest2_Rate_Upper  0x19 +#define REG_Rest2_Downshift 0x1A +#define REG_Rest3_Rate_Lower  0x1B +#define REG_Rest3_Rate_Upper  0x1C +#define REG_Observation 0x24 +#define REG_Data_Out_Lower  0x25 +#define REG_Data_Out_Upper  0x26 +#define REG_Raw_Data_Dump 0x29 +#define REG_SROM_ID 0x2A +#define REG_Min_SQ_Run  0x2B +#define REG_Raw_Data_Threshold  0x2C +#define REG_Config5 0x2F +#define REG_Power_Up_Reset  0x3A +#define REG_Shutdown  0x3B +#define REG_Inverse_Product_ID  0x3F +#define REG_LiftCutoff_Tune3  0x41 +#define REG_Angle_Snap  0x42 +#define REG_LiftCutoff_Tune1  0x4A +#define REG_Motion_Burst  0x50 +#define REG_LiftCutoff_Tune_Timeout 0x58 +#define REG_LiftCutoff_Tune_Min_Length  0x5A +#define REG_SROM_Load_Burst 0x62 +#define REG_Lift_Config 0x63 +#define REG_Raw_Data_Burst  0x64 +#define REG_LiftCutoff_Tune2  0x65 + +#ifdef CONSOLE_ENABLE +void print_byte(uint8_t byte); +#endif + +typedef struct { +    int8_t  motion; +    bool    isMotion;     // True if a motion is detected. +    bool    isOnSurface;  // True when a chip is on a surface +    int16_t dx;           // displacement on x directions. Unit: Count. (CPI * Count = Inch value) +    int8_t  mdx; +    int16_t dy;  // displacement on y directions. +    int8_t  mdy; +} report_pmw_t; + + + +bool spi_start_adv(void); +void spi_stop_adv(void); +spi_status_t spi_write_adv(uint8_t reg_addr, uint8_t data); +uint8_t spi_read_adv(uint8_t reg_addr); +bool pmw_spi_init(void); +void pmw_set_cpi(uint16_t cpi); +uint16_t pmw_get_cpi(void); +void pmw_upload_firmware(void); +bool pmw_check_signature(void); +report_pmw_t pmw_read_burst(void); + + +#define degToRad(angleInDegrees) ((angleInDegrees)*M_PI / 180.0) +#define radToDeg(angleInRadians) ((angleInRadians)*180.0 / M_PI) +#define constrain(amt, low, high) ((amt) < (low) ? (low) : ((amt) > (high) ? (high) : (amt))) diff --git a/drivers/sensors/pmw3360_firmware.h b/drivers/sensors/pmw3360_firmware.h new file mode 100644 index 0000000000..cca5a6a4d8 --- /dev/null +++ b/drivers/sensors/pmw3360_firmware.h @@ -0,0 +1,300 @@ +/* Copyright 2020 Christopher Courtney, aka Drashna Jael're  (@drashna) <[email protected]> + * Copyright 2019 Sunjun Kim + * Copyright 2020 Ploopy Corporation + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +// clang-format off +// Firmware Blob foor PMW3360 +const uint16_t firmware_length = 4094; +// clang-format off +const uint8_t firmware_data[] PROGMEM = {    // SROM 0x04 +0x01, 0x04, 0x8e, 0x96, 0x6e, 0x77, 0x3e, 0xfe, 0x7e, 0x5f, 0x1d, 0xb8, 0xf2, 0x66, 0x4e, +0xff, 0x5d, 0x19, 0xb0, 0xc2, 0x04, 0x69, 0x54, 0x2a, 0xd6, 0x2e, 0xbf, 0xdd, 0x19, 0xb0, +0xc3, 0xe5, 0x29, 0xb1, 0xe0, 0x23, 0xa5, 0xa9, 0xb1, 0xc1, 0x00, 0x82, 0x67, 0x4c, 0x1a, +0x97, 0x8d, 0x79, 0x51, 0x20, 0xc7, 0x06, 0x8e, 0x7c, 0x7c, 0x7a, 0x76, 0x4f, 0xfd, 0x59, +0x30, 0xe2, 0x46, 0x0e, 0x9e, 0xbe, 0xdf, 0x1d, 0x99, 0x91, 0xa0, 0xa5, 0xa1, 0xa9, 0xd0, +0x22, 0xc6, 0xef, 0x5c, 0x1b, 0x95, 0x89, 0x90, 0xa2, 0xa7, 0xcc, 0xfb, 0x55, 0x28, 0xb3, +0xe4, 0x4a, 0xf7, 0x6c, 0x3b, 0xf4, 0x6a, 0x56, 0x2e, 0xde, 0x1f, 0x9d, 0xb8, 0xd3, 0x05, +0x88, 0x92, 0xa6, 0xce, 0x1e, 0xbe, 0xdf, 0x1d, 0x99, 0xb0, 0xe2, 0x46, 0xef, 0x5c, 0x07, +0x11, 0x5d, 0x98, 0x0b, 0x9d, 0x94, 0x97, 0xee, 0x4e, 0x45, 0x33, 0x6b, 0x44, 0xc7, 0x29, +0x56, 0x27, 0x30, 0xc6, 0xa7, 0xd5, 0xf2, 0x56, 0xdf, 0xb4, 0x38, 0x62, 0xcb, 0xa0, 0xb6, +0xe3, 0x0f, 0x84, 0x06, 0x24, 0x05, 0x65, 0x6f, 0x76, 0x89, 0xb5, 0x77, 0x41, 0x27, 0x82, +0x66, 0x65, 0x82, 0xcc, 0xd5, 0xe6, 0x20, 0xd5, 0x27, 0x17, 0xc5, 0xf8, 0x03, 0x23, 0x7c, +0x5f, 0x64, 0xa5, 0x1d, 0xc1, 0xd6, 0x36, 0xcb, 0x4c, 0xd4, 0xdb, 0x66, 0xd7, 0x8b, 0xb1, +0x99, 0x7e, 0x6f, 0x4c, 0x36, 0x40, 0x06, 0xd6, 0xeb, 0xd7, 0xa2, 0xe4, 0xf4, 0x95, 0x51, +0x5a, 0x54, 0x96, 0xd5, 0x53, 0x44, 0xd7, 0x8c, 0xe0, 0xb9, 0x40, 0x68, 0xd2, 0x18, 0xe9, +0xdd, 0x9a, 0x23, 0x92, 0x48, 0xee, 0x7f, 0x43, 0xaf, 0xea, 0x77, 0x38, 0x84, 0x8c, 0x0a, +0x72, 0xaf, 0x69, 0xf8, 0xdd, 0xf1, 0x24, 0x83, 0xa3, 0xf8, 0x4a, 0xbf, 0xf5, 0x94, 0x13, +0xdb, 0xbb, 0xd8, 0xb4, 0xb3, 0xa0, 0xfb, 0x45, 0x50, 0x60, 0x30, 0x59, 0x12, 0x31, 0x71, +0xa2, 0xd3, 0x13, 0xe7, 0xfa, 0xe7, 0xce, 0x0f, 0x63, 0x15, 0x0b, 0x6b, 0x94, 0xbb, 0x37, +0x83, 0x26, 0x05, 0x9d, 0xfb, 0x46, 0x92, 0xfc, 0x0a, 0x15, 0xd1, 0x0d, 0x73, 0x92, 0xd6, +0x8c, 0x1b, 0x8c, 0xb8, 0x55, 0x8a, 0xce, 0xbd, 0xfe, 0x8e, 0xfc, 0xed, 0x09, 0x12, 0x83, +0x91, 0x82, 0x51, 0x31, 0x23, 0xfb, 0xb4, 0x0c, 0x76, 0xad, 0x7c, 0xd9, 0xb4, 0x4b, 0xb2, +0x67, 0x14, 0x09, 0x9c, 0x7f, 0x0c, 0x18, 0xba, 0x3b, 0xd6, 0x8e, 0x14, 0x2a, 0xe4, 0x1b, +0x52, 0x9f, 0x2b, 0x7d, 0xe1, 0xfb, 0x6a, 0x33, 0x02, 0xfa, 0xac, 0x5a, 0xf2, 0x3e, 0x88, +0x7e, 0xae, 0xd1, 0xf3, 0x78, 0xe8, 0x05, 0xd1, 0xe3, 0xdc, 0x21, 0xf6, 0xe1, 0x9a, 0xbd, +0x17, 0x0e, 0xd9, 0x46, 0x9b, 0x88, 0x03, 0xea, 0xf6, 0x66, 0xbe, 0x0e, 0x1b, 0x50, 0x49, +0x96, 0x40, 0x97, 0xf1, 0xf1, 0xe4, 0x80, 0xa6, 0x6e, 0xe8, 0x77, 0x34, 0xbf, 0x29, 0x40, +0x44, 0xc2, 0xff, 0x4e, 0x98, 0xd3, 0x9c, 0xa3, 0x32, 0x2b, 0x76, 0x51, 0x04, 0x09, 0xe7, +0xa9, 0xd1, 0xa6, 0x32, 0xb1, 0x23, 0x53, 0xe2, 0x47, 0xab, 0xd6, 0xf5, 0x69, 0x5c, 0x3e, +0x5f, 0xfa, 0xae, 0x45, 0x20, 0xe5, 0xd2, 0x44, 0xff, 0x39, 0x32, 0x6d, 0xfd, 0x27, 0x57, +0x5c, 0xfd, 0xf0, 0xde, 0xc1, 0xb5, 0x99, 0xe5, 0xf5, 0x1c, 0x77, 0x01, 0x75, 0xc5, 0x6d, +0x58, 0x92, 0xf2, 0xb2, 0x47, 0x00, 0x01, 0x26, 0x96, 0x7a, 0x30, 0xff, 0xb7, 0xf0, 0xef, +0x77, 0xc1, 0x8a, 0x5d, 0xdc, 0xc0, 0xd1, 0x29, 0x30, 0x1e, 0x77, 0x38, 0x7a, 0x94, 0xf1, +0xb8, 0x7a, 0x7e, 0xef, 0xa4, 0xd1, 0xac, 0x31, 0x4a, 0xf2, 0x5d, 0x64, 0x3d, 0xb2, 0xe2, +0xf0, 0x08, 0x99, 0xfc, 0x70, 0xee, 0x24, 0xa7, 0x7e, 0xee, 0x1e, 0x20, 0x69, 0x7d, 0x44, +0xbf, 0x87, 0x42, 0xdf, 0x88, 0x3b, 0x0c, 0xda, 0x42, 0xc9, 0x04, 0xf9, 0x45, 0x50, 0xfc, +0x83, 0x8f, 0x11, 0x6a, 0x72, 0xbc, 0x99, 0x95, 0xf0, 0xac, 0x3d, 0xa7, 0x3b, 0xcd, 0x1c, +0xe2, 0x88, 0x79, 0x37, 0x11, 0x5f, 0x39, 0x89, 0x95, 0x0a, 0x16, 0x84, 0x7a, 0xf6, 0x8a, +0xa4, 0x28, 0xe4, 0xed, 0x83, 0x80, 0x3b, 0xb1, 0x23, 0xa5, 0x03, 0x10, 0xf4, 0x66, 0xea, +0xbb, 0x0c, 0x0f, 0xc5, 0xec, 0x6c, 0x69, 0xc5, 0xd3, 0x24, 0xab, 0xd4, 0x2a, 0xb7, 0x99, +0x88, 0x76, 0x08, 0xa0, 0xa8, 0x95, 0x7c, 0xd8, 0x38, 0x6d, 0xcd, 0x59, 0x02, 0x51, 0x4b, +0xf1, 0xb5, 0x2b, 0x50, 0xe3, 0xb6, 0xbd, 0xd0, 0x72, 0xcf, 0x9e, 0xfd, 0x6e, 0xbb, 0x44, +0xc8, 0x24, 0x8a, 0x77, 0x18, 0x8a, 0x13, 0x06, 0xef, 0x97, 0x7d, 0xfa, 0x81, 0xf0, 0x31, +0xe6, 0xfa, 0x77, 0xed, 0x31, 0x06, 0x31, 0x5b, 0x54, 0x8a, 0x9f, 0x30, 0x68, 0xdb, 0xe2, +0x40, 0xf8, 0x4e, 0x73, 0xfa, 0xab, 0x74, 0x8b, 0x10, 0x58, 0x13, 0xdc, 0xd2, 0xe6, 0x78, +0xd1, 0x32, 0x2e, 0x8a, 0x9f, 0x2c, 0x58, 0x06, 0x48, 0x27, 0xc5, 0xa9, 0x5e, 0x81, 0x47, +0x89, 0x46, 0x21, 0x91, 0x03, 0x70, 0xa4, 0x3e, 0x88, 0x9c, 0xda, 0x33, 0x0a, 0xce, 0xbc, +0x8b, 0x8e, 0xcf, 0x9f, 0xd3, 0x71, 0x80, 0x43, 0xcf, 0x6b, 0xa9, 0x51, 0x83, 0x76, 0x30, +0x82, 0xc5, 0x6a, 0x85, 0x39, 0x11, 0x50, 0x1a, 0x82, 0xdc, 0x1e, 0x1c, 0xd5, 0x7d, 0xa9, +0x71, 0x99, 0x33, 0x47, 0x19, 0x97, 0xb3, 0x5a, 0xb1, 0xdf, 0xed, 0xa4, 0xf2, 0xe6, 0x26, +0x84, 0xa2, 0x28, 0x9a, 0x9e, 0xdf, 0xa6, 0x6a, 0xf4, 0xd6, 0xfc, 0x2e, 0x5b, 0x9d, 0x1a, +0x2a, 0x27, 0x68, 0xfb, 0xc1, 0x83, 0x21, 0x4b, 0x90, 0xe0, 0x36, 0xdd, 0x5b, 0x31, 0x42, +0x55, 0xa0, 0x13, 0xf7, 0xd0, 0x89, 0x53, 0x71, 0x99, 0x57, 0x09, 0x29, 0xc5, 0xf3, 0x21, +0xf8, 0x37, 0x2f, 0x40, 0xf3, 0xd4, 0xaf, 0x16, 0x08, 0x36, 0x02, 0xfc, 0x77, 0xc5, 0x8b, +0x04, 0x90, 0x56, 0xb9, 0xc9, 0x67, 0x9a, 0x99, 0xe8, 0x00, 0xd3, 0x86, 0xff, 0x97, 0x2d, +0x08, 0xe9, 0xb7, 0xb3, 0x91, 0xbc, 0xdf, 0x45, 0xc6, 0xed, 0x0f, 0x8c, 0x4c, 0x1e, 0xe6, +0x5b, 0x6e, 0x38, 0x30, 0xe4, 0xaa, 0xe3, 0x95, 0xde, 0xb9, 0xe4, 0x9a, 0xf5, 0xb2, 0x55, +0x9a, 0x87, 0x9b, 0xf6, 0x6a, 0xb2, 0xf2, 0x77, 0x9a, 0x31, 0xf4, 0x7a, 0x31, 0xd1, 0x1d, +0x04, 0xc0, 0x7c, 0x32, 0xa2, 0x9e, 0x9a, 0xf5, 0x62, 0xf8, 0x27, 0x8d, 0xbf, 0x51, 0xff, +0xd3, 0xdf, 0x64, 0x37, 0x3f, 0x2a, 0x6f, 0x76, 0x3a, 0x7d, 0x77, 0x06, 0x9e, 0x77, 0x7f, +0x5e, 0xeb, 0x32, 0x51, 0xf9, 0x16, 0x66, 0x9a, 0x09, 0xf3, 0xb0, 0x08, 0xa4, 0x70, 0x96, +0x46, 0x30, 0xff, 0xda, 0x4f, 0xe9, 0x1b, 0xed, 0x8d, 0xf8, 0x74, 0x1f, 0x31, 0x92, 0xb3, +0x73, 0x17, 0x36, 0xdb, 0x91, 0x30, 0xd6, 0x88, 0x55, 0x6b, 0x34, 0x77, 0x87, 0x7a, 0xe7, +0xee, 0x06, 0xc6, 0x1c, 0x8c, 0x19, 0x0c, 0x48, 0x46, 0x23, 0x5e, 0x9c, 0x07, 0x5c, 0xbf, +0xb4, 0x7e, 0xd6, 0x4f, 0x74, 0x9c, 0xe2, 0xc5, 0x50, 0x8b, 0xc5, 0x8b, 0x15, 0x90, 0x60, +0x62, 0x57, 0x29, 0xd0, 0x13, 0x43, 0xa1, 0x80, 0x88, 0x91, 0x00, 0x44, 0xc7, 0x4d, 0x19, +0x86, 0xcc, 0x2f, 0x2a, 0x75, 0x5a, 0xfc, 0xeb, 0x97, 0x2a, 0x70, 0xe3, 0x78, 0xd8, 0x91, +0xb0, 0x4f, 0x99, 0x07, 0xa3, 0x95, 0xea, 0x24, 0x21, 0xd5, 0xde, 0x51, 0x20, 0x93, 0x27, +0x0a, 0x30, 0x73, 0xa8, 0xff, 0x8a, 0x97, 0xe9, 0xa7, 0x6a, 0x8e, 0x0d, 0xe8, 0xf0, 0xdf, +0xec, 0xea, 0xb4, 0x6c, 0x1d, 0x39, 0x2a, 0x62, 0x2d, 0x3d, 0x5a, 0x8b, 0x65, 0xf8, 0x90, +0x05, 0x2e, 0x7e, 0x91, 0x2c, 0x78, 0xef, 0x8e, 0x7a, 0xc1, 0x2f, 0xac, 0x78, 0xee, 0xaf, +0x28, 0x45, 0x06, 0x4c, 0x26, 0xaf, 0x3b, 0xa2, 0xdb, 0xa3, 0x93, 0x06, 0xb5, 0x3c, 0xa5, +0xd8, 0xee, 0x8f, 0xaf, 0x25, 0xcc, 0x3f, 0x85, 0x68, 0x48, 0xa9, 0x62, 0xcc, 0x97, 0x8f, +0x7f, 0x2a, 0xea, 0xe0, 0x15, 0x0a, 0xad, 0x62, 0x07, 0xbd, 0x45, 0xf8, 0x41, 0xd8, 0x36, +0xcb, 0x4c, 0xdb, 0x6e, 0xe6, 0x3a, 0xe7, 0xda, 0x15, 0xe9, 0x29, 0x1e, 0x12, 0x10, 0xa0, +0x14, 0x2c, 0x0e, 0x3d, 0xf4, 0xbf, 0x39, 0x41, 0x92, 0x75, 0x0b, 0x25, 0x7b, 0xa3, 0xce, +0x39, 0x9c, 0x15, 0x64, 0xc8, 0xfa, 0x3d, 0xef, 0x73, 0x27, 0xfe, 0x26, 0x2e, 0xce, 0xda, +0x6e, 0xfd, 0x71, 0x8e, 0xdd, 0xfe, 0x76, 0xee, 0xdc, 0x12, 0x5c, 0x02, 0xc5, 0x3a, 0x4e, +0x4e, 0x4f, 0xbf, 0xca, 0x40, 0x15, 0xc7, 0x6e, 0x8d, 0x41, 0xf1, 0x10, 0xe0, 0x4f, 0x7e, +0x97, 0x7f, 0x1c, 0xae, 0x47, 0x8e, 0x6b, 0xb1, 0x25, 0x31, 0xb0, 0x73, 0xc7, 0x1b, 0x97, +0x79, 0xf9, 0x80, 0xd3, 0x66, 0x22, 0x30, 0x07, 0x74, 0x1e, 0xe4, 0xd0, 0x80, 0x21, 0xd6, +0xee, 0x6b, 0x6c, 0x4f, 0xbf, 0xf5, 0xb7, 0xd9, 0x09, 0x87, 0x2f, 0xa9, 0x14, 0xbe, 0x27, +0xd9, 0x72, 0x50, 0x01, 0xd4, 0x13, 0x73, 0xa6, 0xa7, 0x51, 0x02, 0x75, 0x25, 0xe1, 0xb3, +0x45, 0x34, 0x7d, 0xa8, 0x8e, 0xeb, 0xf3, 0x16, 0x49, 0xcb, 0x4f, 0x8c, 0xa1, 0xb9, 0x36, +0x85, 0x39, 0x75, 0x5d, 0x08, 0x00, 0xae, 0xeb, 0xf6, 0xea, 0xd7, 0x13, 0x3a, 0x21, 0x5a, +0x5f, 0x30, 0x84, 0x52, 0x26, 0x95, 0xc9, 0x14, 0xf2, 0x57, 0x55, 0x6b, 0xb1, 0x10, 0xc2, +0xe1, 0xbd, 0x3b, 0x51, 0xc0, 0xb7, 0x55, 0x4c, 0x71, 0x12, 0x26, 0xc7, 0x0d, 0xf9, 0x51, +0xa4, 0x38, 0x02, 0x05, 0x7f, 0xb8, 0xf1, 0x72, 0x4b, 0xbf, 0x71, 0x89, 0x14, 0xf3, 0x77, +0x38, 0xd9, 0x71, 0x24, 0xf3, 0x00, 0x11, 0xa1, 0xd8, 0xd4, 0x69, 0x27, 0x08, 0x37, 0x35, +0xc9, 0x11, 0x9d, 0x90, 0x1c, 0x0e, 0xe7, 0x1c, 0xff, 0x2d, 0x1e, 0xe8, 0x92, 0xe1, 0x18, +0x10, 0x95, 0x7c, 0xe0, 0x80, 0xf4, 0x96, 0x43, 0x21, 0xf9, 0x75, 0x21, 0x64, 0x38, 0xdd, +0x9f, 0x1e, 0x95, 0x16, 0xda, 0x56, 0x1d, 0x4f, 0x9a, 0x53, 0xb2, 0xe2, 0xe4, 0x18, 0xcb, +0x6b, 0x1a, 0x65, 0xeb, 0x56, 0xc6, 0x3b, 0xe5, 0xfe, 0xd8, 0x26, 0x3f, 0x3a, 0x84, 0x59, +0x72, 0x66, 0xa2, 0xf3, 0x75, 0xff, 0xfb, 0x60, 0xb3, 0x22, 0xad, 0x3f, 0x2d, 0x6b, 0xf9, +0xeb, 0xea, 0x05, 0x7c, 0xd8, 0x8f, 0x6d, 0x2c, 0x98, 0x9e, 0x2b, 0x93, 0xf1, 0x5e, 0x46, +0xf0, 0x87, 0x49, 0x29, 0x73, 0x68, 0xd7, 0x7f, 0xf9, 0xf0, 0xe5, 0x7d, 0xdb, 0x1d, 0x75, +0x19, 0xf3, 0xc4, 0x58, 0x9b, 0x17, 0x88, 0xa8, 0x92, 0xe0, 0xbe, 0xbd, 0x8b, 0x1d, 0x8d, +0x9f, 0x56, 0x76, 0xad, 0xaf, 0x29, 0xe2, 0xd9, 0xd5, 0x52, 0xf6, 0xb5, 0x56, 0x35, 0x57, +0x3a, 0xc8, 0xe1, 0x56, 0x43, 0x19, 0x94, 0xd3, 0x04, 0x9b, 0x6d, 0x35, 0xd8, 0x0b, 0x5f, +0x4d, 0x19, 0x8e, 0xec, 0xfa, 0x64, 0x91, 0x0a, 0x72, 0x20, 0x2b, 0xbc, 0x1a, 0x4a, 0xfe, +0x8b, 0xfd, 0xbb, 0xed, 0x1b, 0x23, 0xea, 0xad, 0x72, 0x82, 0xa1, 0x29, 0x99, 0x71, 0xbd, +0xf0, 0x95, 0xc1, 0x03, 0xdd, 0x7b, 0xc2, 0xb2, 0x3c, 0x28, 0x54, 0xd3, 0x68, 0xa4, 0x72, +0xc8, 0x66, 0x96, 0xe0, 0xd1, 0xd8, 0x7f, 0xf8, 0xd1, 0x26, 0x2b, 0xf7, 0xad, 0xba, 0x55, +0xca, 0x15, 0xb9, 0x32, 0xc3, 0xe5, 0x88, 0x97, 0x8e, 0x5c, 0xfb, 0x92, 0x25, 0x8b, 0xbf, +0xa2, 0x45, 0x55, 0x7a, 0xa7, 0x6f, 0x8b, 0x57, 0x5b, 0xcf, 0x0e, 0xcb, 0x1d, 0xfb, 0x20, +0x82, 0x77, 0xa8, 0x8c, 0xcc, 0x16, 0xce, 0x1d, 0xfa, 0xde, 0xcc, 0x0b, 0x62, 0xfe, 0xcc, +0xe1, 0xb7, 0xf0, 0xc3, 0x81, 0x64, 0x73, 0x40, 0xa0, 0xc2, 0x4d, 0x89, 0x11, 0x75, 0x33, +0x55, 0x33, 0x8d, 0xe8, 0x4a, 0xfd, 0xea, 0x6e, 0x30, 0x0b, 0xd7, 0x31, 0x2c, 0xde, 0x47, +0xe3, 0xbf, 0xf8, 0x55, 0x42, 0xe2, 0x7f, 0x59, 0xe5, 0x17, 0xef, 0x99, 0x34, 0x69, 0x91, +0xb1, 0x23, 0x8e, 0x20, 0x87, 0x2d, 0xa8, 0xfe, 0xd5, 0x8a, 0xf3, 0x84, 0x3a, 0xf0, 0x37, +0xe4, 0x09, 0x00, 0x54, 0xee, 0x67, 0x49, 0x93, 0xe4, 0x81, 0x70, 0xe3, 0x90, 0x4d, 0xef, +0xfe, 0x41, 0xb7, 0x99, 0x7b, 0xc1, 0x83, 0xba, 0x62, 0x12, 0x6f, 0x7d, 0xde, 0x6b, 0xaf, +0xda, 0x16, 0xf9, 0x55, 0x51, 0xee, 0xa6, 0x0c, 0x2b, 0x02, 0xa3, 0xfd, 0x8d, 0xfb, 0x30, +0x17, 0xe4, 0x6f, 0xdf, 0x36, 0x71, 0xc4, 0xca, 0x87, 0x25, 0x48, 0xb0, 0x47, 0xec, 0xea, +0xb4, 0xbf, 0xa5, 0x4d, 0x9b, 0x9f, 0x02, 0x93, 0xc4, 0xe3, 0xe4, 0xe8, 0x42, 0x2d, 0x68, +0x81, 0x15, 0x0a, 0xeb, 0x84, 0x5b, 0xd6, 0xa8, 0x74, 0xfb, 0x7d, 0x1d, 0xcb, 0x2c, 0xda, +0x46, 0x2a, 0x76, 0x62, 0xce, 0xbc, 0x5c, 0x9e, 0x8b, 0xe7, 0xcf, 0xbe, 0x78, 0xf5, 0x7c, +0xeb, 0xb3, 0x3a, 0x9c, 0xaa, 0x6f, 0xcc, 0x72, 0xd1, 0x59, 0xf2, 0x11, 0x23, 0xd6, 0x3f, +0x48, 0xd1, 0xb7, 0xce, 0xb0, 0xbf, 0xcb, 0xea, 0x80, 0xde, 0x57, 0xd4, 0x5e, 0x97, 0x2f, +0x75, 0xd1, 0x50, 0x8e, 0x80, 0x2c, 0x66, 0x79, 0xbf, 0x72, 0x4b, 0xbd, 0x8a, 0x81, 0x6c, +0xd3, 0xe1, 0x01, 0xdc, 0xd2, 0x15, 0x26, 0xc5, 0x36, 0xda, 0x2c, 0x1a, 0xc0, 0x27, 0x94, +0xed, 0xb7, 0x9b, 0x85, 0x0b, 0x5e, 0x80, 0x97, 0xc5, 0xec, 0x4f, 0xec, 0x88, 0x5d, 0x50, +0x07, 0x35, 0x47, 0xdc, 0x0b, 0x3b, 0x3d, 0xdd, 0x60, 0xaf, 0xa8, 0x5d, 0x81, 0x38, 0x24, +0x25, 0x5d, 0x5c, 0x15, 0xd1, 0xde, 0xb3, 0xab, 0xec, 0x05, 0x69, 0xef, 0x83, 0xed, 0x57, +0x54, 0xb8, 0x64, 0x64, 0x11, 0x16, 0x32, 0x69, 0xda, 0x9f, 0x2d, 0x7f, 0x36, 0xbb, 0x44, +0x5a, 0x34, 0xe8, 0x7f, 0xbf, 0x03, 0xeb, 0x00, 0x7f, 0x59, 0x68, 0x22, 0x79, 0xcf, 0x73, +0x6c, 0x2c, 0x29, 0xa7, 0xa1, 0x5f, 0x38, 0xa1, 0x1d, 0xf0, 0x20, 0x53, 0xe0, 0x1a, 0x63, +0x14, 0x58, 0x71, 0x10, 0xaa, 0x08, 0x0c, 0x3e, 0x16, 0x1a, 0x60, 0x22, 0x82, 0x7f, 0xba, +0xa4, 0x43, 0xa0, 0xd0, 0xac, 0x1b, 0xd5, 0x6b, 0x64, 0xb5, 0x14, 0x93, 0x31, 0x9e, 0x53, +0x50, 0xd0, 0x57, 0x66, 0xee, 0x5a, 0x4f, 0xfb, 0x03, 0x2a, 0x69, 0x58, 0x76, 0xf1, 0x83, +0xf7, 0x4e, 0xba, 0x8c, 0x42, 0x06, 0x60, 0x5d, 0x6d, 0xce, 0x60, 0x88, 0xae, 0xa4, 0xc3, +0xf1, 0x03, 0xa5, 0x4b, 0x98, 0xa1, 0xff, 0x67, 0xe1, 0xac, 0xa2, 0xb8, 0x62, 0xd7, 0x6f, +0xa0, 0x31, 0xb4, 0xd2, 0x77, 0xaf, 0x21, 0x10, 0x06, 0xc6, 0x9a, 0xff, 0x1d, 0x09, 0x17, +0x0e, 0x5f, 0xf1, 0xaa, 0x54, 0x34, 0x4b, 0x45, 0x8a, 0x87, 0x63, 0xa6, 0xdc, 0xf9, 0x24, +0x30, 0x67, 0xc6, 0xb2, 0xd6, 0x61, 0x33, 0x69, 0xee, 0x50, 0x61, 0x57, 0x28, 0xe7, 0x7e, +0xee, 0xec, 0x3a, 0x5a, 0x73, 0x4e, 0xa8, 0x8d, 0xe4, 0x18, 0xea, 0xec, 0x41, 0x64, 0xc8, +0xe2, 0xe8, 0x66, 0xb6, 0x2d, 0xb6, 0xfb, 0x6a, 0x6c, 0x16, 0xb3, 0xdd, 0x46, 0x43, 0xb9, +0x73, 0x00, 0x6a, 0x71, 0xed, 0x4e, 0x9d, 0x25, 0x1a, 0xc3, 0x3c, 0x4a, 0x95, 0x15, 0x99, +0x35, 0x81, 0x14, 0x02, 0xd6, 0x98, 0x9b, 0xec, 0xd8, 0x23, 0x3b, 0x84, 0x29, 0xaf, 0x0c, +0x99, 0x83, 0xa6, 0x9a, 0x34, 0x4f, 0xfa, 0xe8, 0xd0, 0x3c, 0x4b, 0xd0, 0xfb, 0xb6, 0x68, +0xb8, 0x9e, 0x8f, 0xcd, 0xf7, 0x60, 0x2d, 0x7a, 0x22, 0xe5, 0x7d, 0xab, 0x65, 0x1b, 0x95, +0xa7, 0xa8, 0x7f, 0xb6, 0x77, 0x47, 0x7b, 0x5f, 0x8b, 0x12, 0x72, 0xd0, 0xd4, 0x91, 0xef, +0xde, 0x19, 0x50, 0x3c, 0xa7, 0x8b, 0xc4, 0xa9, 0xb3, 0x23, 0xcb, 0x76, 0xe6, 0x81, 0xf0, +0xc1, 0x04, 0x8f, 0xa3, 0xb8, 0x54, 0x5b, 0x97, 0xac, 0x19, 0xff, 0x3f, 0x55, 0x27, 0x2f, +0xe0, 0x1d, 0x42, 0x9b, 0x57, 0xfc, 0x4b, 0x4e, 0x0f, 0xce, 0x98, 0xa9, 0x43, 0x57, 0x03, +0xbd, 0xe7, 0xc8, 0x94, 0xdf, 0x6e, 0x36, 0x73, 0x32, 0xb4, 0xef, 0x2e, 0x85, 0x7a, 0x6e, +0xfc, 0x6c, 0x18, 0x82, 0x75, 0x35, 0x90, 0x07, 0xf3, 0xe4, 0x9f, 0x3e, 0xdc, 0x68, 0xf3, +0xb5, 0xf3, 0x19, 0x80, 0x92, 0x06, 0x99, 0xa2, 0xe8, 0x6f, 0xff, 0x2e, 0x7f, 0xae, 0x42, +0xa4, 0x5f, 0xfb, 0xd4, 0x0e, 0x81, 0x2b, 0xc3, 0x04, 0xff, 0x2b, 0xb3, 0x74, 0x4e, 0x36, +0x5b, 0x9c, 0x15, 0x00, 0xc6, 0x47, 0x2b, 0xe8, 0x8b, 0x3d, 0xf1, 0x9c, 0x03, 0x9a, 0x58, +0x7f, 0x9b, 0x9c, 0xbf, 0x85, 0x49, 0x79, 0x35, 0x2e, 0x56, 0x7b, 0x41, 0x14, 0x39, 0x47, +0x83, 0x26, 0xaa, 0x07, 0x89, 0x98, 0x11, 0x1b, 0x86, 0xe7, 0x73, 0x7a, 0xd8, 0x7d, 0x78, +0x61, 0x53, 0xe9, 0x79, 0xf5, 0x36, 0x8d, 0x44, 0x92, 0x84, 0xf9, 0x13, 0x50, 0x58, 0x3b, +0xa4, 0x6a, 0x36, 0x65, 0x49, 0x8e, 0x3c, 0x0e, 0xf1, 0x6f, 0xd2, 0x84, 0xc4, 0x7e, 0x8e, +0x3f, 0x39, 0xae, 0x7c, 0x84, 0xf1, 0x63, 0x37, 0x8e, 0x3c, 0xcc, 0x3e, 0x44, 0x81, 0x45, +0xf1, 0x4b, 0xb9, 0xed, 0x6b, 0x36, 0x5d, 0xbb, 0x20, 0x60, 0x1a, 0x0f, 0xa3, 0xaa, 0x55, +0x77, 0x3a, 0xa9, 0xae, 0x37, 0x4d, 0xba, 0xb8, 0x86, 0x6b, 0xbc, 0x08, 0x50, 0xf6, 0xcc, +0xa4, 0xbd, 0x1d, 0x40, 0x72, 0xa5, 0x86, 0xfa, 0xe2, 0x10, 0xae, 0x3d, 0x58, 0x4b, 0x97, +0xf3, 0x43, 0x74, 0xa9, 0x9e, 0xeb, 0x21, 0xb7, 0x01, 0xa4, 0x86, 0x93, 0x97, 0xee, 0x2f, +0x4f, 0x3b, 0x86, 0xa1, 0x41, 0x6f, 0x41, 0x26, 0x90, 0x78, 0x5c, 0x7f, 0x30, 0x38, 0x4b, +0x3f, 0xaa, 0xec, 0xed, 0x5c, 0x6f, 0x0e, 0xad, 0x43, 0x87, 0xfd, 0x93, 0x35, 0xe6, 0x01, +0xef, 0x41, 0x26, 0x90, 0x99, 0x9e, 0xfb, 0x19, 0x5b, 0xad, 0xd2, 0x91, 0x8a, 0xe0, 0x46, +0xaf, 0x65, 0xfa, 0x4f, 0x84, 0xc1, 0xa1, 0x2d, 0xcf, 0x45, 0x8b, 0xd3, 0x85, 0x50, 0x55, +0x7c, 0xf9, 0x67, 0x88, 0xd4, 0x4e, 0xe9, 0xd7, 0x6b, 0x61, 0x54, 0xa1, 0xa4, 0xa6, 0xa2, +0xc2, 0xbf, 0x30, 0x9c, 0x40, 0x9f, 0x5f, 0xd7, 0x69, 0x2b, 0x24, 0x82, 0x5e, 0xd9, 0xd6, +0xa7, 0x12, 0x54, 0x1a, 0xf7, 0x55, 0x9f, 0x76, 0x50, 0xa9, 0x95, 0x84, 0xe6, 0x6b, 0x6d, +0xb5, 0x96, 0x54, 0xd6, 0xcd, 0xb3, 0xa1, 0x9b, 0x46, 0xa7, 0x94, 0x4d, 0xc4, 0x94, 0xb4, +0x98, 0xe3, 0xe1, 0xe2, 0x34, 0xd5, 0x33, 0x16, 0x07, 0x54, 0xcd, 0xb7, 0x77, 0x53, 0xdb, +0x4f, 0x4d, 0x46, 0x9d, 0xe9, 0xd4, 0x9c, 0x8a, 0x36, 0xb6, 0xb8, 0x38, 0x26, 0x6c, 0x0e, +0xff, 0x9c, 0x1b, 0x43, 0x8b, 0x80, 0xcc, 0xb9, 0x3d, 0xda, 0xc7, 0xf1, 0x8a, 0xf2, 0x6d, +0xb8, 0xd7, 0x74, 0x2f, 0x7e, 0x1e, 0xb7, 0xd3, 0x4a, 0xb4, 0xac, 0xfc, 0x79, 0x48, 0x6c, +0xbc, 0x96, 0xb6, 0x94, 0x46, 0x57, 0x2d, 0xb0, 0xa3, 0xfc, 0x1e, 0xb9, 0x52, 0x60, 0x85, +0x2d, 0x41, 0xd0, 0x43, 0x01, 0x1e, 0x1c, 0xd5, 0x7d, 0xfc, 0xf3, 0x96, 0x0d, 0xc7, 0xcb, +0x2a, 0x29, 0x9a, 0x93, 0xdd, 0x88, 0x2d, 0x37, 0x5d, 0xaa, 0xfb, 0x49, 0x68, 0xa0, 0x9c, +0x50, 0x86, 0x7f, 0x68, 0x56, 0x57, 0xf9, 0x79, 0x18, 0x39, 0xd4, 0xe0, 0x01, 0x84, 0x33, +0x61, 0xca, 0xa5, 0xd2, 0xd6, 0xe4, 0xc9, 0x8a, 0x4a, 0x23, 0x44, 0x4e, 0xbc, 0xf0, 0xdc, +0x24, 0xa1, 0xa0, 0xc4, 0xe2, 0x07, 0x3c, 0x10, 0xc4, 0xb5, 0x25, 0x4b, 0x65, 0x63, 0xf4, +0x80, 0xe7, 0xcf, 0x61, 0xb1, 0x71, 0x82, 0x21, 0x87, 0x2c, 0xf5, 0x91, 0x00, 0x32, 0x0c, +0xec, 0xa9, 0xb5, 0x9a, 0x74, 0x85, 0xe3, 0x36, 0x8f, 0x76, 0x4f, 0x9c, 0x6d, 0xce, 0xbc, +0xad, 0x0a, 0x4b, 0xed, 0x76, 0x04, 0xcb, 0xc3, 0xb9, 0x33, 0x9e, 0x01, 0x93, 0x96, 0x69, +0x7d, 0xc5, 0xa2, 0x45, 0x79, 0x9b, 0x04, 0x5c, 0x84, 0x09, 0xed, 0x88, 0x43, 0xc7, 0xab, +0x93, 0x14, 0x26, 0xa1, 0x40, 0xb5, 0xce, 0x4e, 0xbf, 0x2a, 0x42, 0x85, 0x3e, 0x2c, 0x3b, +0x54, 0xe8, 0x12, 0x1f, 0x0e, 0x97, 0x59, 0xb2, 0x27, 0x89, 0xfa, 0xf2, 0xdf, 0x8e, 0x68, +0x59, 0xdc, 0x06, 0xbc, 0xb6, 0x85, 0x0d, 0x06, 0x22, 0xec, 0xb1, 0xcb, 0xe5, 0x04, 0xe6, +0x3d, 0xb3, 0xb0, 0x41, 0x73, 0x08, 0x3f, 0x3c, 0x58, 0x86, 0x63, 0xeb, 0x50, 0xee, 0x1d, +0x2c, 0x37, 0x74, 0xa9, 0xd3, 0x18, 0xa3, 0x47, 0x6e, 0x93, 0x54, 0xad, 0x0a, 0x5d, 0xb8, +0x2a, 0x55, 0x5d, 0x78, 0xf6, 0xee, 0xbe, 0x8e, 0x3c, 0x76, 0x69, 0xb9, 0x40, 0xc2, 0x34, +0xec, 0x2a, 0xb9, 0xed, 0x7e, 0x20, 0xe4, 0x8d, 0x00, 0x38, 0xc7, 0xe6, 0x8f, 0x44, 0xa8, +0x86, 0xce, 0xeb, 0x2a, 0xe9, 0x90, 0xf1, 0x4c, 0xdf, 0x32, 0xfb, 0x73, 0x1b, 0x6d, 0x92, +0x1e, 0x95, 0xfe, 0xb4, 0xdb, 0x65, 0xdf, 0x4d, 0x23, 0x54, 0x89, 0x48, 0xbf, 0x4a, 0x2e, +0x70, 0xd6, 0xd7, 0x62, 0xb4, 0x33, 0x29, 0xb1, 0x3a, 0x33, 0x4c, 0x23, 0x6d, 0xa6, 0x76, +0xa5, 0x21, 0x63, 0x48, 0xe6, 0x90, 0x5d, 0xed, 0x90, 0x95, 0x0b, 0x7a, 0x84, 0xbe, 0xb8, +0x0d, 0x5e, 0x63, 0x0c, 0x62, 0x26, 0x4c, 0x14, 0x5a, 0xb3, 0xac, 0x23, 0xa4, 0x74, 0xa7, +0x6f, 0x33, 0x30, 0x05, 0x60, 0x01, 0x42, 0xa0, 0x28, 0xb7, 0xee, 0x19, 0x38, 0xf1, 0x64, +0x80, 0x82, 0x43, 0xe1, 0x41, 0x27, 0x1f, 0x1f, 0x90, 0x54, 0x7a, 0xd5, 0x23, 0x2e, 0xd1, +0x3d, 0xcb, 0x28, 0xba, 0x58, 0x7f, 0xdc, 0x7c, 0x91, 0x24, 0xe9, 0x28, 0x51, 0x83, 0x6e, +0xc5, 0x56, 0x21, 0x42, 0xed, 0xa0, 0x56, 0x22, 0xa1, 0x40, 0x80, 0x6b, 0xa8, 0xf7, 0x94, +0xca, 0x13, 0x6b, 0x0c, 0x39, 0xd9, 0xfd, 0xe9, 0xf3, 0x6f, 0xa6, 0x9e, 0xfc, 0x70, 0x8a, +0xb3, 0xbc, 0x59, 0x3c, 0x1e, 0x1d, 0x6c, 0xf9, 0x7c, 0xaf, 0xf9, 0x88, 0x71, 0x95, 0xeb, +0x57, 0x00, 0xbd, 0x9f, 0x8c, 0x4f, 0xe1, 0x24, 0x83, 0xc5, 0x22, 0xea, 0xfd, 0xd3, 0x0c, +0xe2, 0x17, 0x18, 0x7c, 0x6a, 0x4c, 0xde, 0x77, 0xb4, 0x53, 0x9b, 0x4c, 0x81, 0xcd, 0x23, +0x60, 0xaa, 0x0e, 0x25, 0x73, 0x9c, 0x02, 0x79, 0x32, 0x30, 0xdf, 0x74, 0xdf, 0x75, 0x19, +0xf4, 0xa5, 0x14, 0x5c, 0xf7, 0x7a, 0xa8, 0xa5, 0x91, 0x84, 0x7c, 0x60, 0x03, 0x06, 0x3b, +0xcd, 0x50, 0xb6, 0x27, 0x9c, 0xfe, 0xb1, 0xdd, 0xcc, 0xd3, 0xb0, 0x59, 0x24, 0xb2, 0xca, +0xe2, 0x1c, 0x81, 0x22, 0x9d, 0x07, 0x8f, 0x8e, 0xb9, 0xbe, 0x4e, 0xfa, 0xfc, 0x39, 0x65, +0xba, 0xbf, 0x9d, 0x12, 0x37, 0x5e, 0x97, 0x7e, 0xf3, 0x89, 0xf5, 0x5d, 0xf5, 0xe3, 0x09, +0x8c, 0x62, 0xb5, 0x20, 0x9d, 0x0c, 0x53, 0x8a, 0x68, 0x1b, 0xd2, 0x8f, 0x75, 0x17, 0x5d, +0xd4, 0xe5, 0xda, 0x75, 0x62, 0x19, 0x14, 0x6a, 0x26, 0x2d, 0xeb, 0xf8, 0xaf, 0x37, 0xf0, +0x6c, 0xa4, 0x55, 0xb1, 0xbc, 0xe2, 0x33, 0xc0, 0x9a, 0xca, 0xb0, 0x11, 0x49, 0x4f, 0x68, +0x9b, 0x3b, 0x6b, 0x3c, 0xcc, 0x13, 0xf6, 0xc7, 0x85, 0x61, 0x68, 0x42, 0xae, 0xbb, 0xdd, +0xcd, 0x45, 0x16, 0x29, 0x1d, 0xea, 0xdb, 0xc8, 0x03, 0x94, 0x3c, 0xee, 0x4f, 0x82, 0x11, +0xc3, 0xec, 0x28, 0xbd, 0x97, 0x05, 0x99, 0xde, 0xd7, 0xbb, 0x5e, 0x22, 0x1f, 0xd4, 0xeb, +0x64, 0xd9, 0x92, 0xd9, 0x85, 0xb7, 0x6a, 0x05, 0x6a, 0xe4, 0x24, 0x41, 0xf1, 0xcd, 0xf0, +0xd8, 0x3f, 0xf8, 0x9e, 0x0e, 0xcd, 0x0b, 0x7a, 0x70, 0x6b, 0x5a, 0x75, 0x0a, 0x6a, 0x33, +0x88, 0xec, 0x17, 0x75, 0x08, 0x70, 0x10, 0x2f, 0x24, 0xcf, 0xc4, 0xe9, 0x42, 0x00, 0x61, +0x94, 0xca, 0x1f, 0x3a, 0x76, 0x06, 0xfa, 0xd2, 0x48, 0x81, 0xf0, 0x77, 0x60, 0x03, 0x45, +0xd9, 0x61, 0xf4, 0xa4, 0x6f, 0x3d, 0xd9, 0x30, 0xc3, 0x04, 0x6b, 0x54, 0x2a, 0xb7, 0xec, +0x3b, 0xf4, 0x4b, 0xf5, 0x68, 0x52, 0x26, 0xce, 0xff, 0x5d, 0x19, 0x91, 0xa0, 0xa3, 0xa5, +0xa9, 0xb1, 0xe0, 0x23, 0xc4, 0x0a, 0x77, 0x4d, 0xf9, 0x51, 0x20, 0xa3, 0xa5, 0xa9, 0xb1, +0xc1, 0x00, 0x82, 0x86, 0x8e, 0x7f, 0x5d, 0x19, 0x91, 0xa0, 0xa3, 0xc4, 0xeb, 0x54, 0x0b, +0x75, 0x68, 0x52, 0x07, 0x8c, 0x9a, 0x97, 0x8d, 0x79, 0x70, 0x62, 0x46, 0xef, 0x5c, 0x1b, +0x95, 0x89, 0x71, 0x41, 0xe1, 0x21, 0xa1, 0xa1, 0xa1, 0xc0, 0x02, 0x67, 0x4c, 0x1a, 0xb6, +0xcf, 0xfd, 0x78, 0x53, 0x24, 0xab, 0xb5, 0xc9, 0xf1, 0x60, 0x23, 0xa5, 0xc8, 0x12, 0x87, +0x6d, 0x58, 0x13, 0x85, 0x88, 0x92, 0x87, 0x6d, 0x58, 0x32, 0xc7, 0x0c, 0x9a, 0x97, 0xac, +0xda, 0x36, 0xee, 0x5e, 0x3e, 0xdf, 0x1d, 0xb8, 0xf2, 0x66, 0x2f, 0xbd, 0xf8, 0x72, 0x47, +0xed, 0x58, 0x13, 0x85, 0x88, 0x92, 0x87, 0x8c, 0x7b, 0x55, 0x09, 0x90, 0xa2, 0xc6, 0xef, +0x3d, 0xf8, 0x53, 0x24, 0xab, 0xd4, 0x2a, 0xb7, 0xec, 0x5a, 0x36, 0xee, 0x5e, 0x3e, 0xdf, +0x3c, 0xfa, 0x76, 0x4f, 0xfd, 0x59, 0x30, 0xe2, 0x46, 0xef, 0x3d, 0xf8, 0x53, 0x05, 0x69, +0x31, 0xc1, 0x00, 0x82, 0x86, 0x8e, 0x7f, 0x5d, 0x19, 0xb0, 0xe2, 0x27, 0xcc, 0xfb, 0x74, +0x4b, 0x14, 0x8b, 0x94, 0x8b, 0x75, 0x68, 0x33, 0xc5, 0x08, 0x92, 0x87, 0x8c, 0x9a, 0xb6, +0xcf, 0x1c, 0xba, 0xd7, 0x0d, 0x98, 0xb2, 0xe6, 0x2f, 0xdc, 0x1b, 0x95, 0x89, 0x71, 0x60, +0x23, 0xc4, 0x0a, 0x96, 0x8f, 0x9c, 0xba, 0xf6, 0x6e, 0x3f, 0xfc, 0x5b, 0x15, 0xa8, 0xd2, +0x26, 0xaf, 0xbd, 0xf8, 0x72, 0x66, 0x2f, 0xdc, 0x1b, 0xb4, 0xcb, 0x14, 0x8b, 0x94, 0xaa, +0xb7, 0xcd, 0xf9, 0x51, 0x01, 0x80, 0x82, 0x86, 0x6f, 0x3d, 0xd9, 0x30, 0xe2, 0x27, 0xcc, +0xfb, 0x74, 0x4b, 0x14, 0xaa, 0xb7, 0xcd, 0xf9, 0x70, 0x43, 0x04, 0x6b, 0x35, 0xc9, 0xf1, +0x60, 0x23, 0xa5, 0xc8, 0xf3, 0x45, 0x08, 0x92, 0x87, 0x6d, 0x58, 0x32, 0xe6, 0x2f, 0xbd, +0xf8, 0x72, 0x66, 0x4e, 0x1e, 0xbe, 0xfe, 0x7e, 0x7e, 0x7e, 0x5f, 0x1d, 0x99, 0x91, 0xa0, +0xa3, 0xc4, 0x0a, 0x77, 0x4d, 0x18, 0x93, 0xa4, 0xab, 0xd4, 0x0b, 0x75, 0x49, 0x10, 0xa2, +0xc6, 0xef, 0x3d, 0xf8, 0x53, 0x24, 0xab, 0xb5, 0xe8, 0x33, 0xe4, 0x4a, 0x16, 0xae, 0xde, +0x1f, 0xbc, 0xdb, 0x15, 0xa8, 0xb3, 0xc5, 0x08, 0x73, 0x45, 0xe9, 0x31, 0xc1, 0xe1, 0x21, +0xa1, 0xa1, 0xa1, 0xc0, 0x02, 0x86, 0x6f, 0x5c, 0x3a, 0xd7, 0x0d, 0x98, 0x93, 0xa4, 0xca, +0x16, 0xae, 0xde, 0x1f, 0x9d, 0x99, 0xb0, 0xe2, 0x46, 0xef, 0x3d, 0xf8, 0x72, 0x47, 0x0c, +0x9a, 0xb6, 0xcf, 0xfd, 0x59, 0x11, 0xa0, 0xa3, 0xa5, 0xc8, 0xf3, 0x45, 0x08, 0x92, 0x87, +0x6d, 0x39, 0xf0, 0x43, 0x04, 0x8a, 0x96, 0xae, 0xde, 0x3e, 0xdf, 0x1d, 0x99, 0x91, 0xa0, +0xc2, 0x06, 0x6f, 0x3d, 0xf8, 0x72, 0x47, 0x0c, 0x9a, 0x97, 0x8d, 0x98, 0x93, 0x85, 0x88, +0x73, 0x45, 0xe9, 0x31, 0xe0, 0x23, 0xa5, 0xa9, 0xd0, 0x03, 0x84, 0x8a, 0x96, 0xae, 0xde, +0x1f, 0xbc, 0xdb, 0x15, 0xa8, 0xd2, 0x26, 0xce, 0xff, 0x5d, 0x19, 0x91, 0x81, 0x80, 0x82, +0x67, 0x2d, 0xd8, 0x13, 0xa4, 0xab, 0xd4, 0x0b, 0x94, 0xaa, 0xb7, 0xcd, 0xf9, 0x51, 0x20, +0xa3, 0xa5, 0xc8, 0xf3, 0x45, 0xe9, 0x50, 0x22, 0xc6, 0xef, 0x5c, 0x3a, 0xd7, 0x0d, 0x98, +0x93, 0x85, 0x88, 0x73, 0x64, 0x4a, 0xf7, 0x4d, 0xf9, 0x51, 0x20, 0xa3, 0xc4, 0x0a, 0x96, +0xae, 0xde, 0x3e, 0xfe, 0x7e, 0x7e, 0x7e, 0x5f, 0x3c, 0xfa, 0x76, 0x4f, 0xfd, 0x78, 0x72, +0x66, 0x2f, 0xbd, 0xd9, 0x30, 0xc3, 0xe5, 0x48, 0x12, 0x87, 0x8c, 0x7b, 0x55, 0x28, 0xd2, +0x07, 0x8c, 0x9a, 0x97, 0xac, 0xda, 0x17, 0x8d, 0x79, 0x51, 0x20, 0xa3, 0xc4, 0xeb, 0x54, +0x0b, 0x94, 0x8b, 0x94, 0xaa, 0xd6, 0x2e, 0xbf, 0xfc, 0x5b, 0x15, 0xa8, 0xd2, 0x26, 0xaf, +0xdc, 0x1b, 0xb4, 0xea, 0x37, 0xec, 0x3b, 0xf4, 0x6a, 0x37, 0xcd, 0x18, 0x93, 0x85, 0x69, +0x31, 0xc1, 0xe1, 0x40, 0xe3, 0x25, 0xc8, 0x12, 0x87, 0x8c, 0x9a, 0xb6, 0xcf, 0xfd, 0x59, +0x11, 0xa0, 0xc2, 0x06, 0x8e, 0x7f, 0x5d, 0x38, 0xf2, 0x47, 0x0c, 0x7b, 0x74, 0x6a, 0x37, +0xec, 0x5a, 0x36, 0xee, 0x3f, 0xfc, 0x7a, 0x76, 0x4f, 0x1c, 0x9b, 0x95, 0x89, 0x71, 0x41, +0x00, 0x63, 0x44, 0xeb, 0x54, 0x2a, 0xd6, 0x0f, 0x9c, 0xba, 0xd7, 0x0d, 0x98, 0x93, 0x85, +0x69, 0x31, 0xc1, 0x00, 0x82, 0x86, 0x8e, 0x9e, 0xbe, 0xdf, 0x3c, 0xfa, 0x57, 0x2c, 0xda, +0x36, 0xee, 0x3f, 0xfc, 0x5b, 0x15, 0x89, 0x71, 0x41, 0x00, 0x82, 0x86, 0x8e, 0x7f, 0x5d, +0x38, 0xf2, 0x47, 0xed, 0x58, 0x13, 0xa4, 0xca, 0xf7, 0x4d, 0xf9, 0x51, 0x01, 0x80, 0x63, +0x44, 0xeb, 0x54, 0x2a, 0xd6, 0x2e, 0xbf, 0xdd, 0x19, 0x91, 0xa0, 0xa3, 0xa5, 0xa9, 0xb1, +0xe0, 0x42, 0x06, 0x8e, 0x7f, 0x5d, 0x19, 0x91, 0xa0, 0xa3, 0xc4, 0x0a, 0x96, 0x8f, 0x7d, +0x78, 0x72, 0x47, 0x0c, 0x7b, 0x74, 0x6a, 0x56, 0x2e, 0xde, 0x1f, 0xbc, 0xfa, 0x57, 0x0d, +0x79, 0x51, 0x01, 0x61, 0x21, 0xa1, 0xc0, 0xe3, 0x25, 0xa9, 0xb1, 0xc1, 0xe1, 0x40, 0x02, +0x67, 0x4c, 0x1a, 0x97, 0x8d, 0x98, 0x93, 0xa4, 0xab, 0xd4, 0x2a, 0xd6, 0x0f, 0x9c, 0x9b, +0xb4, 0xcb, 0x14, 0xaa, 0xb7, 0xcd, 0xf9, 0x51, 0x20, 0xa3, 0xc4, 0xeb, 0x35, 0xc9, 0xf1, +0x60, 0x42, 0x06, 0x8e, 0x7f, 0x7c, 0x7a, 0x76, 0x6e, 0x3f, 0xfc, 0x7a, 0x76, 0x6e, 0x5e, +0x3e, 0xfe, 0x7e, 0x5f, 0x3c, 0xdb, 0x15, 0x89, 0x71, 0x41, 0xe1, 0x21, 0xc0, 0xe3, 0x44, +0xeb, 0x54, 0x2a, 0xb7, 0xcd, 0xf9, 0x70, 0x62, 0x27, 0xad, 0xd8, 0x32, 0xc7, 0x0c, 0x7b, +0x74, 0x4b, 0x14, 0xaa, 0xb7, 0xec, 0x3b, 0xd5, 0x28, 0xd2, 0x07, 0x6d, 0x39, 0xd1, 0x20, +0xc2, 0xe7, 0x4c, 0x1a, 0x97, 0x8d, 0x98, 0xb2, 0xc7, 0x0c, 0x59, 0x28, 0xf3, 0x9b }; + +// clang-format off diff --git a/drivers/serial.h b/drivers/serial.h new file mode 100644 index 0000000000..d9c2a69e96 --- /dev/null +++ b/drivers/serial.h @@ -0,0 +1,46 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <stdint.h> +#include <stdbool.h> + +#include <transactions.h> + +// initiator is transaction start side +void soft_serial_initiator_init(void); +// target is interrupt accept side +void soft_serial_target_init(void); + +// initiator result +#define TRANSACTION_END 0 +#define TRANSACTION_NO_RESPONSE 0x1 +#define TRANSACTION_DATA_ERROR 0x2 +#define TRANSACTION_TYPE_ERROR 0x4 +int soft_serial_transaction(int sstd_index); + +// target status +// *SSTD_t.status has +//   initiator: +//       TRANSACTION_END +//    or TRANSACTION_NO_RESPONSE +//    or TRANSACTION_DATA_ERROR +//   target: +//       TRANSACTION_DATA_ERROR +//    or TRANSACTION_ACCEPTED +#define TRANSACTION_ACCEPTED 0x8 +int soft_serial_get_and_clean_status(int sstd_index); | 
