aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/avr/i2c_master.c19
-rw-r--r--drivers/avr/i2c_slave.c29
-rw-r--r--drivers/avr/i2c_slave.h13
-rw-r--r--drivers/avr/serial.c82
-rw-r--r--drivers/avr/serial.h62
-rw-r--r--drivers/awinic/aw20216.c166
-rw-r--r--drivers/awinic/aw20216.h251
-rw-r--r--drivers/chibios/serial.c52
-rw-r--r--drivers/chibios/serial.h62
-rw-r--r--drivers/chibios/serial_usart.c47
-rw-r--r--drivers/chibios/spi_master.c70
-rw-r--r--drivers/chibios/spi_master.h19
-rw-r--r--drivers/eeprom/eeprom_i2c.c23
-rw-r--r--drivers/haptic/haptic.c71
-rw-r--r--drivers/lcd/st7565.c496
-rw-r--r--drivers/lcd/st7565.h219
-rw-r--r--drivers/oled/oled_driver.c26
-rw-r--r--drivers/oled/oled_driver.h4
-rw-r--r--drivers/sensors/adns5050.c193
-rw-r--r--drivers/sensors/adns5050.h79
-rw-r--r--drivers/sensors/adns9800.c219
-rw-r--r--drivers/sensors/adns9800.h35
-rw-r--r--drivers/sensors/adns9800_srom_A6.h3078
-rw-r--r--drivers/sensors/pimoroni_trackball.c140
-rw-r--r--drivers/sensors/pimoroni_trackball.h35
-rw-r--r--drivers/sensors/pmw3360.c237
-rw-r--r--drivers/sensors/pmw3360.h104
-rw-r--r--drivers/sensors/pmw3360_firmware.h300
-rw-r--r--drivers/serial.h46
29 files changed, 5916 insertions, 261 deletions
diff --git a/drivers/avr/i2c_master.c b/drivers/avr/i2c_master.c
index b1e488529..2773e0077 100644
--- a/drivers/avr/i2c_master.c
+++ b/drivers/avr/i2c_master.c
@@ -28,8 +28,14 @@
28# define F_SCL 400000UL // SCL frequency 28# define F_SCL 400000UL // SCL frequency
29#endif 29#endif
30 30
31#ifndef I2C_START_RETRY_COUNT
32# define I2C_START_RETRY_COUNT 20
33#endif // I2C_START_RETRY_COUNT
34
31#define TWBR_val (((F_CPU / F_SCL) - 16) / 2) 35#define TWBR_val (((F_CPU / F_SCL) - 16) / 2)
32 36
37#define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
38
33void i2c_init(void) { 39void i2c_init(void) {
34 TWSR = 0; /* no prescaler */ 40 TWSR = 0; /* no prescaler */
35 TWBR = (uint8_t)TWBR_val; 41 TWBR = (uint8_t)TWBR_val;
@@ -47,7 +53,7 @@ void i2c_init(void) {
47#endif 53#endif
48} 54}
49 55
50i2c_status_t i2c_start(uint8_t address, uint16_t timeout) { 56static i2c_status_t i2c_start_impl(uint8_t address, uint16_t timeout) {
51 // reset TWI control register 57 // reset TWI control register
52 TWCR = 0; 58 TWCR = 0;
53 // transmit START condition 59 // transmit START condition
@@ -86,6 +92,17 @@ i2c_status_t i2c_start(uint8_t address, uint16_t timeout) {
86 return I2C_STATUS_SUCCESS; 92 return I2C_STATUS_SUCCESS;
87} 93}
88 94
95i2c_status_t i2c_start(uint8_t address, uint16_t timeout) {
96 // Retry i2c_start_impl a bunch times in case the remote side has interrupts disabled.
97 uint16_t timeout_timer = timer_read();
98 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
99 i2c_status_t status;
100 do {
101 status = i2c_start_impl(address, time_slice);
102 } while ((status < 0) && ((timeout == I2C_TIMEOUT_INFINITE) || (timer_elapsed(timeout_timer) < timeout)));
103 return status;
104}
105
89i2c_status_t i2c_write(uint8_t data, uint16_t timeout) { 106i2c_status_t i2c_write(uint8_t data, uint16_t timeout) {
90 // load data into data register 107 // load data into data register
91 TWDR = data; 108 TWDR = data;
diff --git a/drivers/avr/i2c_slave.c b/drivers/avr/i2c_slave.c
index 62a378165..2907f164c 100644
--- a/drivers/avr/i2c_slave.c
+++ b/drivers/avr/i2c_slave.c
@@ -17,6 +17,7 @@
17 * GitHub repository: https://github.com/g4lvanix/I2C-slave-lib 17 * GitHub repository: https://github.com/g4lvanix/I2C-slave-lib
18 */ 18 */
19 19
20#include <stddef.h>
20#include <avr/io.h> 21#include <avr/io.h>
21#include <util/twi.h> 22#include <util/twi.h>
22#include <avr/interrupt.h> 23#include <avr/interrupt.h>
@@ -24,6 +25,12 @@
24 25
25#include "i2c_slave.h" 26#include "i2c_slave.h"
26 27
28#if defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)
29# include "transactions.h"
30
31static volatile bool is_callback_executor = false;
32#endif // defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)
33
27volatile uint8_t i2c_slave_reg[I2C_SLAVE_REG_COUNT]; 34volatile uint8_t i2c_slave_reg[I2C_SLAVE_REG_COUNT];
28 35
29static volatile uint8_t buffer_address; 36static volatile uint8_t buffer_address;
@@ -48,11 +55,14 @@ ISR(TWI_vect) {
48 case TW_SR_SLA_ACK: 55 case TW_SR_SLA_ACK:
49 // The device is now a slave receiver 56 // The device is now a slave receiver
50 slave_has_register_set = false; 57 slave_has_register_set = false;
58#if defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)
59 is_callback_executor = false;
60#endif // defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)
51 break; 61 break;
52 62
53 case TW_SR_DATA_ACK: 63 case TW_SR_DATA_ACK:
54 // This device is a slave receiver and has received data 64 // This device is a slave receiver and has received data
55 // First byte is the location then the bytes will be writen in buffer with auto-incriment 65 // First byte is the location then the bytes will be writen in buffer with auto-increment
56 if (!slave_has_register_set) { 66 if (!slave_has_register_set) {
57 buffer_address = TWDR; 67 buffer_address = TWDR;
58 68
@@ -60,10 +70,25 @@ ISR(TWI_vect) {
60 ack = 0; 70 ack = 0;
61 buffer_address = 0; 71 buffer_address = 0;
62 } 72 }
63 slave_has_register_set = true; // address has been receaved now fill in buffer 73 slave_has_register_set = true; // address has been received now fill in buffer
74
75#if defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)
76 // Work out if we're attempting to execute a callback
77 is_callback_executor = buffer_address == split_transaction_table[I2C_EXECUTE_CALLBACK].initiator2target_offset;
78#endif // defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)
64 } else { 79 } else {
65 i2c_slave_reg[buffer_address] = TWDR; 80 i2c_slave_reg[buffer_address] = TWDR;
66 buffer_address++; 81 buffer_address++;
82
83#if defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)
84 // If we're intending to execute a transaction callback, do so, as we've just received the transaction ID
85 if (is_callback_executor) {
86 split_transaction_desc_t *trans = &split_transaction_table[split_shmem->transaction_id];
87 if (trans->slave_callback) {
88 trans->slave_callback(trans->initiator2target_buffer_size, split_trans_initiator2target_buffer(trans), trans->target2initiator_buffer_size, split_trans_target2initiator_buffer(trans));
89 }
90 }
91#endif // defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)
67 } 92 }
68 break; 93 break;
69 94
diff --git a/drivers/avr/i2c_slave.h b/drivers/avr/i2c_slave.h
index 1cd0625ef..a8647c9da 100644
--- a/drivers/avr/i2c_slave.h
+++ b/drivers/avr/i2c_slave.h
@@ -22,7 +22,18 @@
22 22
23#pragma once 23#pragma once
24 24
25#define I2C_SLAVE_REG_COUNT 30 25#ifndef I2C_SLAVE_REG_COUNT
26
27# if defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)
28# include "transport.h"
29# define I2C_SLAVE_REG_COUNT sizeof(split_shared_memory_t)
30# else // defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)
31# define I2C_SLAVE_REG_COUNT 30
32# endif // defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)
33
34#endif // I2C_SLAVE_REG_COUNT
35
36_Static_assert(I2C_SLAVE_REG_COUNT < 256, "I2C target registers must be single byte");
26 37
27extern volatile uint8_t i2c_slave_reg[I2C_SLAVE_REG_COUNT]; 38extern volatile uint8_t i2c_slave_reg[I2C_SLAVE_REG_COUNT];
28 39
diff --git a/drivers/avr/serial.c b/drivers/avr/serial.c
index 3647bee0d..9a7345a53 100644
--- a/drivers/avr/serial.c
+++ b/drivers/avr/serial.c
@@ -224,15 +224,8 @@
224# define SERIAL_DELAY_HALF2 (SERIAL_DELAY - SERIAL_DELAY / 2) 224# define SERIAL_DELAY_HALF2 (SERIAL_DELAY - SERIAL_DELAY / 2)
225 225
226# define SLAVE_INT_WIDTH_US 1 226# define SLAVE_INT_WIDTH_US 1
227# ifndef SERIAL_USE_MULTI_TRANSACTION 227# define SLAVE_INT_ACK_WIDTH_UNIT 2
228# define SLAVE_INT_RESPONSE_TIME SERIAL_DELAY 228# define SLAVE_INT_ACK_WIDTH 4
229# else
230# define SLAVE_INT_ACK_WIDTH_UNIT 2
231# define SLAVE_INT_ACK_WIDTH 4
232# endif
233
234static SSTD_t *Transaction_table = NULL;
235static uint8_t Transaction_table_size = 0;
236 229
237inline static void serial_delay(void) ALWAYS_INLINE; 230inline static void serial_delay(void) ALWAYS_INLINE;
238inline static void serial_delay(void) { _delay_us(SERIAL_DELAY); } 231inline static void serial_delay(void) { _delay_us(SERIAL_DELAY); }
@@ -259,16 +252,12 @@ inline static void serial_low(void) { writePinLow(SOFT_SERIAL_PIN); }
259inline static void serial_high(void) ALWAYS_INLINE; 252inline static void serial_high(void) ALWAYS_INLINE;
260inline static void serial_high(void) { writePinHigh(SOFT_SERIAL_PIN); } 253inline static void serial_high(void) { writePinHigh(SOFT_SERIAL_PIN); }
261 254
262void soft_serial_initiator_init(SSTD_t *sstd_table, int sstd_table_size) { 255void soft_serial_initiator_init(void) {
263 Transaction_table = sstd_table;
264 Transaction_table_size = (uint8_t)sstd_table_size;
265 serial_output(); 256 serial_output();
266 serial_high(); 257 serial_high();
267} 258}
268 259
269void soft_serial_target_init(SSTD_t *sstd_table, int sstd_table_size) { 260void soft_serial_target_init(void) {
270 Transaction_table = sstd_table;
271 Transaction_table_size = (uint8_t)sstd_table_size;
272 serial_input_with_pullup(); 261 serial_input_with_pullup();
273 262
274 // Enable INT0-INT7 263 // Enable INT0-INT7
@@ -395,19 +384,14 @@ static inline uint8_t nibble_bits_count(uint8_t bits) {
395 384
396// interrupt handle to be used by the target device 385// interrupt handle to be used by the target device
397ISR(SERIAL_PIN_INTERRUPT) { 386ISR(SERIAL_PIN_INTERRUPT) {
398# ifndef SERIAL_USE_MULTI_TRANSACTION
399 serial_low();
400 serial_output();
401 SSTD_t *trans = Transaction_table;
402# else
403 // recive transaction table index 387 // recive transaction table index
404 uint8_t tid, bits; 388 uint8_t tid, bits;
405 uint8_t pecount = 0; 389 uint8_t pecount = 0;
406 sync_recv(); 390 sync_recv();
407 bits = serial_read_chunk(&pecount, 7); 391 bits = serial_read_chunk(&pecount, 8);
408 tid = bits >> 3; 392 tid = bits >> 3;
409 bits = (bits & 7) != nibble_bits_count(tid); 393 bits = (bits & 7) != (nibble_bits_count(tid) & 7);
410 if (bits || pecount > 0 || tid > Transaction_table_size) { 394 if (bits || pecount > 0 || tid > NUM_TOTAL_TRANSACTIONS) {
411 return; 395 return;
412 } 396 }
413 serial_delay_half1(); 397 serial_delay_half1();
@@ -415,18 +399,22 @@ ISR(SERIAL_PIN_INTERRUPT) {
415 serial_high(); // response step1 low->high 399 serial_high(); // response step1 low->high
416 serial_output(); 400 serial_output();
417 _delay_sub_us(SLAVE_INT_ACK_WIDTH_UNIT * SLAVE_INT_ACK_WIDTH); 401 _delay_sub_us(SLAVE_INT_ACK_WIDTH_UNIT * SLAVE_INT_ACK_WIDTH);
418 SSTD_t *trans = &Transaction_table[tid]; 402 split_transaction_desc_t *trans = &split_transaction_table[tid];
419 serial_low(); // response step2 ack high->low 403 serial_low(); // response step2 ack high->low
420# endif 404
405 // If the transaction has a callback, we can execute it now
406 if (trans->slave_callback) {
407 trans->slave_callback(trans->initiator2target_buffer_size, split_trans_initiator2target_buffer(trans), trans->target2initiator_buffer_size, split_trans_target2initiator_buffer(trans));
408 }
421 409
422 // target send phase 410 // target send phase
423 if (trans->target2initiator_buffer_size > 0) serial_send_packet((uint8_t *)trans->target2initiator_buffer, trans->target2initiator_buffer_size); 411 if (trans->target2initiator_buffer_size > 0) serial_send_packet((uint8_t *)split_trans_target2initiator_buffer(trans), trans->target2initiator_buffer_size);
424 // target switch to input 412 // target switch to input
425 change_sender2reciver(); 413 change_sender2reciver();
426 414
427 // target recive phase 415 // target recive phase
428 if (trans->initiator2target_buffer_size > 0) { 416 if (trans->initiator2target_buffer_size > 0) {
429 if (serial_recive_packet((uint8_t *)trans->initiator2target_buffer, trans->initiator2target_buffer_size)) { 417 if (serial_recive_packet((uint8_t *)split_trans_initiator2target_buffer(trans), trans->initiator2target_buffer_size)) {
430 *trans->status = TRANSACTION_ACCEPTED; 418 *trans->status = TRANSACTION_ACCEPTED;
431 } else { 419 } else {
432 *trans->status = TRANSACTION_DATA_ERROR; 420 *trans->status = TRANSACTION_DATA_ERROR;
@@ -448,14 +436,12 @@ ISR(SERIAL_PIN_INTERRUPT) {
448// TRANSACTION_NO_RESPONSE 436// TRANSACTION_NO_RESPONSE
449// TRANSACTION_DATA_ERROR 437// TRANSACTION_DATA_ERROR
450// this code is very time dependent, so we need to disable interrupts 438// this code is very time dependent, so we need to disable interrupts
451# ifndef SERIAL_USE_MULTI_TRANSACTION
452int soft_serial_transaction(void) {
453 SSTD_t *trans = Transaction_table;
454# else
455int soft_serial_transaction(int sstd_index) { 439int soft_serial_transaction(int sstd_index) {
456 if (sstd_index > Transaction_table_size) return TRANSACTION_TYPE_ERROR; 440 if (sstd_index > NUM_TOTAL_TRANSACTIONS) return TRANSACTION_TYPE_ERROR;
457 SSTD_t *trans = &Transaction_table[sstd_index]; 441 split_transaction_desc_t *trans = &split_transaction_table[sstd_index];
458# endif 442
443 if (!trans->status) return TRANSACTION_TYPE_ERROR; // not registered
444
459 cli(); 445 cli();
460 446
461 // signal to the target that we want to start a transaction 447 // signal to the target that we want to start a transaction
@@ -463,27 +449,11 @@ int soft_serial_transaction(int sstd_index) {
463 serial_low(); 449 serial_low();
464 _delay_us(SLAVE_INT_WIDTH_US); 450 _delay_us(SLAVE_INT_WIDTH_US);
465 451
466# ifndef SERIAL_USE_MULTI_TRANSACTION
467 // wait for the target response
468 serial_input_with_pullup();
469 _delay_us(SLAVE_INT_RESPONSE_TIME);
470
471 // check if the target is present
472 if (serial_read_pin()) {
473 // target failed to pull the line low, assume not present
474 serial_output();
475 serial_high();
476 *trans->status = TRANSACTION_NO_RESPONSE;
477 sei();
478 return TRANSACTION_NO_RESPONSE;
479 }
480
481# else
482 // send transaction table index 452 // send transaction table index
483 int tid = (sstd_index << 3) | (7 & nibble_bits_count(sstd_index)); 453 int tid = (sstd_index << 3) | (7 & nibble_bits_count(sstd_index));
484 sync_send(); 454 sync_send();
485 _delay_sub_us(TID_SEND_ADJUST); 455 _delay_sub_us(TID_SEND_ADJUST);
486 serial_write_chunk(tid, 7); 456 serial_write_chunk(tid, 8);
487 serial_delay_half1(); 457 serial_delay_half1();
488 458
489 // wait for the target response (step1 low->high) 459 // wait for the target response (step1 low->high)
@@ -504,12 +474,11 @@ int soft_serial_transaction(int sstd_index) {
504 } 474 }
505 _delay_sub_us(SLAVE_INT_ACK_WIDTH_UNIT); 475 _delay_sub_us(SLAVE_INT_ACK_WIDTH_UNIT);
506 } 476 }
507# endif
508 477
509 // initiator recive phase 478 // initiator recive phase
510 // if the target is present syncronize with it 479 // if the target is present syncronize with it
511 if (trans->target2initiator_buffer_size > 0) { 480 if (trans->target2initiator_buffer_size > 0) {
512 if (!serial_recive_packet((uint8_t *)trans->target2initiator_buffer, trans->target2initiator_buffer_size)) { 481 if (!serial_recive_packet((uint8_t *)split_trans_target2initiator_buffer(trans), trans->target2initiator_buffer_size)) {
513 serial_output(); 482 serial_output();
514 serial_high(); 483 serial_high();
515 *trans->status = TRANSACTION_DATA_ERROR; 484 *trans->status = TRANSACTION_DATA_ERROR;
@@ -523,7 +492,7 @@ int soft_serial_transaction(int sstd_index) {
523 492
524 // initiator send phase 493 // initiator send phase
525 if (trans->initiator2target_buffer_size > 0) { 494 if (trans->initiator2target_buffer_size > 0) {
526 serial_send_packet((uint8_t *)trans->initiator2target_buffer, trans->initiator2target_buffer_size); 495 serial_send_packet((uint8_t *)split_trans_initiator2target_buffer(trans), trans->initiator2target_buffer_size);
527 } 496 }
528 497
529 // always, release the line when not in use 498 // always, release the line when not in use
@@ -534,9 +503,8 @@ int soft_serial_transaction(int sstd_index) {
534 return TRANSACTION_END; 503 return TRANSACTION_END;
535} 504}
536 505
537# ifdef SERIAL_USE_MULTI_TRANSACTION
538int soft_serial_get_and_clean_status(int sstd_index) { 506int soft_serial_get_and_clean_status(int sstd_index) {
539 SSTD_t *trans = &Transaction_table[sstd_index]; 507 split_transaction_desc_t *trans = &split_transaction_table[sstd_index];
540 cli(); 508 cli();
541 int retval = *trans->status; 509 int retval = *trans->status;
542 *trans->status = 0; 510 *trans->status = 0;
@@ -544,8 +512,6 @@ int soft_serial_get_and_clean_status(int sstd_index) {
544 sei(); 512 sei();
545 return retval; 513 return retval;
546} 514}
547# endif
548
549#endif 515#endif
550 516
551// Helix serial.c history 517// Helix serial.c history
diff --git a/drivers/avr/serial.h b/drivers/avr/serial.h
deleted file mode 100644
index 53e66cf90..000000000
--- a/drivers/avr/serial.h
+++ /dev/null
@@ -1,62 +0,0 @@
1#pragma once
2
3#include <stdbool.h>
4
5// /////////////////////////////////////////////////////////////////
6// Need Soft Serial defines in config.h
7// /////////////////////////////////////////////////////////////////
8// ex.
9// #define SOFT_SERIAL_PIN ?? // ?? = D0,D1,D2,D3,E6
10// OPTIONAL: #define SELECT_SOFT_SERIAL_SPEED ? // ? = 1,2,3,4,5
11// // 1: about 137kbps (default)
12// // 2: about 75kbps
13// // 3: about 39kbps
14// // 4: about 26kbps
15// // 5: about 20kbps
16//
17// //// USE simple API (using signle-type transaction function)
18// /* nothing */
19// //// USE flexible API (using multi-type transaction function)
20// #define SERIAL_USE_MULTI_TRANSACTION
21//
22// /////////////////////////////////////////////////////////////////
23
24// Soft Serial Transaction Descriptor
25typedef struct _SSTD_t {
26 uint8_t *status;
27 uint8_t initiator2target_buffer_size;
28 uint8_t *initiator2target_buffer;
29 uint8_t target2initiator_buffer_size;
30 uint8_t *target2initiator_buffer;
31} SSTD_t;
32#define TID_LIMIT(table) (sizeof(table) / sizeof(SSTD_t))
33
34// initiator is transaction start side
35void soft_serial_initiator_init(SSTD_t *sstd_table, int sstd_table_size);
36// target is interrupt accept side
37void soft_serial_target_init(SSTD_t *sstd_table, int sstd_table_size);
38
39// initiator resullt
40#define TRANSACTION_END 0
41#define TRANSACTION_NO_RESPONSE 0x1
42#define TRANSACTION_DATA_ERROR 0x2
43#define TRANSACTION_TYPE_ERROR 0x4
44#ifndef SERIAL_USE_MULTI_TRANSACTION
45int soft_serial_transaction(void);
46#else
47int soft_serial_transaction(int sstd_index);
48#endif
49
50// target status
51// *SSTD_t.status has
52// initiator:
53// TRANSACTION_END
54// or TRANSACTION_NO_RESPONSE
55// or TRANSACTION_DATA_ERROR
56// target:
57// TRANSACTION_DATA_ERROR
58// or TRANSACTION_ACCEPTED
59#define TRANSACTION_ACCEPTED 0x8
60#ifdef SERIAL_USE_MULTI_TRANSACTION
61int soft_serial_get_and_clean_status(int sstd_index);
62#endif
diff --git a/drivers/awinic/aw20216.c b/drivers/awinic/aw20216.c
new file mode 100644
index 000000000..236c42a3c
--- /dev/null
+++ b/drivers/awinic/aw20216.c
@@ -0,0 +1,166 @@
1/* Copyright 2021 Jasper Chan
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "aw20216.h"
18#include "spi_master.h"
19
20/* The AW20216 appears to be somewhat similar to the IS31FL743, although quite
21 * a few things are different, such as the command byte format and page ordering.
22 * The LED addresses start from 0x00 instead of 0x01.
23 */
24#define AWINIC_ID 0b1010 << 4
25
26#define AW_PAGE_FUNCTION 0x00 << 1 // PG0, Function registers
27#define AW_PAGE_PWM 0x01 << 1 // PG1, LED PWM control
28#define AW_PAGE_SCALING 0x02 << 1 // PG2, LED current scaling control
29#define AW_PAGE_PATCHOICE 0x03 << 1 // PG3, Pattern choice?
30#define AW_PAGE_PWMSCALING 0x04 << 1 // PG4, LED PWM + Scaling control?
31
32#define AW_WRITE 0
33#define AW_READ 1
34
35#define AW_REG_CONFIGURATION 0x00 // PG0
36#define AW_REG_GLOBALCURRENT 0x01 // PG0
37
38// Default value of AW_REG_CONFIGURATION
39// D7:D4 = 1011, SWSEL (SW1~SW12 active)
40// D3 = 0?, reserved (apparently this should be 1 but it doesn't seem to matter)
41// D2:D1 = 00, OSDE (open/short detection enable)
42// D0 = 0, CHIPEN (write 1 to enable LEDs when hardware enable pulled high)
43#define AW_CONFIG_DEFAULT 0b10110000
44#define AW_CHIPEN 1
45
46#ifndef AW_SCALING_MAX
47# define AW_SCALING_MAX 150
48#endif
49
50#ifndef AW_GLOBAL_CURRENT_MAX
51# define AW_GLOBAL_CURRENT_MAX 150
52#endif
53
54#ifndef DRIVER_1_CS
55# define DRIVER_1_CS B13
56#endif
57
58#ifndef DRIVER_1_EN
59# define DRIVER_1_EN C13
60#endif
61
62uint8_t g_spi_transfer_buffer[20] = {0};
63aw_led g_pwm_buffer[DRIVER_LED_TOTAL];
64bool g_pwm_buffer_update_required[DRIVER_LED_TOTAL];
65
66bool AW20216_write_register(pin_t slave_pin, uint8_t page, uint8_t reg, uint8_t data) {
67 // Do we need to call spi_stop() if this fails?
68 if (!spi_start(slave_pin, false, 0, 16)) {
69 return false;
70 }
71
72 g_spi_transfer_buffer[0] = (AWINIC_ID | page | AW_WRITE);
73 g_spi_transfer_buffer[1] = reg;
74 g_spi_transfer_buffer[2] = data;
75
76 if (spi_transmit(g_spi_transfer_buffer, 3) != SPI_STATUS_SUCCESS) {
77 spi_stop();
78 return false;
79 }
80 spi_stop();
81 return true;
82}
83
84bool AW20216_init_scaling(void) {
85 // Set constant current to the max, control brightness with PWM
86 aw_led led;
87 for (uint8_t i = 0; i < DRIVER_LED_TOTAL; i++) {
88 led = g_aw_leds[i];
89 if (led.driver == 0) {
90 AW20216_write_register(DRIVER_1_CS, AW_PAGE_SCALING, led.r, AW_SCALING_MAX);
91 AW20216_write_register(DRIVER_1_CS, AW_PAGE_SCALING, led.g, AW_SCALING_MAX);
92 AW20216_write_register(DRIVER_1_CS, AW_PAGE_SCALING, led.b, AW_SCALING_MAX);
93 }
94#ifdef DRIVER_2_CS
95 else if (led.driver == 1) {
96 AW20216_write_register(DRIVER_2_CS, AW_PAGE_SCALING, led.r, AW_SCALING_MAX);
97 AW20216_write_register(DRIVER_2_CS, AW_PAGE_SCALING, led.g, AW_SCALING_MAX);
98 AW20216_write_register(DRIVER_2_CS, AW_PAGE_SCALING, led.b, AW_SCALING_MAX);
99 }
100#endif
101 }
102 return true;
103}
104
105bool AW20216_soft_enable(void) {
106 AW20216_write_register(DRIVER_1_CS, AW_PAGE_FUNCTION, AW_REG_CONFIGURATION, AW_CONFIG_DEFAULT | AW_CHIPEN);
107#ifdef DRIVER_2_CS
108 AW20216_write_register(DRIVER_2_CS, AW_PAGE_FUNCTION, AW_REG_CONFIGURATION, AW_CONFIG_DEFAULT | AW_CHIPEN);
109#endif
110 return true;
111}
112
113void AW20216_update_pwm(int index, uint8_t red, uint8_t green, uint8_t blue) {
114 aw_led led = g_aw_leds[index];
115 if (led.driver == 0) {
116 AW20216_write_register(DRIVER_1_CS, AW_PAGE_PWM, led.r, red);
117 AW20216_write_register(DRIVER_1_CS, AW_PAGE_PWM, led.g, green);
118 AW20216_write_register(DRIVER_1_CS, AW_PAGE_PWM, led.b, blue);
119 }
120#ifdef DRIVER_2_CS
121 else if (led.driver == 1) {
122 AW20216_write_register(DRIVER_2_CS, AW_PAGE_PWM, led.r, red);
123 AW20216_write_register(DRIVER_2_CS, AW_PAGE_PWM, led.g, green);
124 AW20216_write_register(DRIVER_2_CS, AW_PAGE_PWM, led.b, blue);
125 }
126#endif
127 return;
128}
129
130void AW20216_init(void) {
131 // All LEDs should start with all scaling and PWM registers as off
132 setPinOutput(DRIVER_1_EN);
133 writePinHigh(DRIVER_1_EN);
134 AW20216_write_register(DRIVER_1_CS, AW_PAGE_FUNCTION, AW_REG_GLOBALCURRENT, AW_GLOBAL_CURRENT_MAX);
135#ifdef DRIVER_2_EN
136 setPinOutput(DRIVER_2_EN);
137 writePinHigh(DRIVER_2_EN);
138 AW20216_write_register(DRIVER_2_CS, AW_PAGE_FUNCTION, AW_REG_GLOBALCURRENT, AW_GLOBAL_CURRENT_MAX);
139#endif
140 AW20216_init_scaling();
141 AW20216_soft_enable();
142 return;
143}
144
145void AW20216_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
146 g_pwm_buffer[index].r = red;
147 g_pwm_buffer[index].g = green;
148 g_pwm_buffer[index].b = blue;
149 g_pwm_buffer_update_required[index] = true;
150 return;
151}
152void AW20216_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
153 for (uint8_t i = 0; i < DRIVER_LED_TOTAL; i++) {
154 AW20216_set_color(i, red, green, blue);
155 }
156 return;
157}
158void AW20216_update_pwm_buffers(void) {
159 for (uint8_t i = 0; i < DRIVER_LED_TOTAL; i++) {
160 if (g_pwm_buffer_update_required[i]) {
161 AW20216_update_pwm(i, g_pwm_buffer[i].r, g_pwm_buffer[i].g, g_pwm_buffer[i].b);
162 g_pwm_buffer_update_required[i] = false;
163 }
164 }
165 return;
166}
diff --git a/drivers/awinic/aw20216.h b/drivers/awinic/aw20216.h
new file mode 100644
index 000000000..9c6865cc8
--- /dev/null
+++ b/drivers/awinic/aw20216.h
@@ -0,0 +1,251 @@
1/* Copyright 2021 Jasper Chan (Gigahawk)
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#pragma once
18
19#include <stdint.h>
20#include <stdbool.h>
21
22typedef struct aw_led {
23 uint8_t driver : 2;
24 uint8_t r;
25 uint8_t g;
26 uint8_t b;
27} aw_led;
28
29extern const aw_led g_aw_leds[DRIVER_LED_TOTAL];
30
31void AW20216_init(void);
32void AW20216_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
33void AW20216_set_color_all(uint8_t red, uint8_t green, uint8_t blue);
34void AW20216_update_pwm_buffers(void);
35
36#define CS1_SW1 0x00
37#define CS2_SW1 0x01
38#define CS3_SW1 0x02
39#define CS4_SW1 0x03
40#define CS5_SW1 0x04
41#define CS6_SW1 0x05
42#define CS7_SW1 0x06
43#define CS8_SW1 0x07
44#define CS9_SW1 0x08
45#define CS10_SW1 0x09
46#define CS11_SW1 0x0A
47#define CS12_SW1 0x0B
48#define CS13_SW1 0x0C
49#define CS14_SW1 0x0D
50#define CS15_SW1 0x0E
51#define CS16_SW1 0x0F
52#define CS17_SW1 0x10
53#define CS18_SW1 0x11
54#define CS1_SW2 0x12
55#define CS2_SW2 0x13
56#define CS3_SW2 0x14
57#define CS4_SW2 0x15
58#define CS5_SW2 0x16
59#define CS6_SW2 0x17
60#define CS7_SW2 0x18
61#define CS8_SW2 0x19
62#define CS9_SW2 0x1A
63#define CS10_SW2 0x1B
64#define CS11_SW2 0x1C
65#define CS12_SW2 0x1D
66#define CS13_SW2 0x1E
67#define CS14_SW2 0x1F
68#define CS15_SW2 0x20
69#define CS16_SW2 0x21
70#define CS17_SW2 0x22
71#define CS18_SW2 0x23
72#define CS1_SW3 0x24
73#define CS2_SW3 0x25
74#define CS3_SW3 0x26
75#define CS4_SW3 0x27
76#define CS5_SW3 0x28
77#define CS6_SW3 0x29
78#define CS7_SW3 0x2A
79#define CS8_SW3 0x2B
80#define CS9_SW3 0x2C
81#define CS10_SW3 0x2D
82#define CS11_SW3 0x2E
83#define CS12_SW3 0x2F
84#define CS13_SW3 0x30
85#define CS14_SW3 0x31
86#define CS15_SW3 0x32
87#define CS16_SW3 0x33
88#define CS17_SW3 0x34
89#define CS18_SW3 0x35
90#define CS1_SW4 0x36
91#define CS2_SW4 0x37
92#define CS3_SW4 0x38
93#define CS4_SW4 0x39
94#define CS5_SW4 0x3A
95#define CS6_SW4 0x3B
96#define CS7_SW4 0x3C
97#define CS8_SW4 0x3D
98#define CS9_SW4 0x3E
99#define CS10_SW4 0x3F
100#define CS11_SW4 0x40
101#define CS12_SW4 0x41
102#define CS13_SW4 0x42
103#define CS14_SW4 0x43
104#define CS15_SW4 0x44
105#define CS16_SW4 0x45
106#define CS17_SW4 0x46
107#define CS18_SW4 0x47
108#define CS1_SW5 0x48
109#define CS2_SW5 0x49
110#define CS3_SW5 0x4A
111#define CS4_SW5 0x4B
112#define CS5_SW5 0x4C
113#define CS6_SW5 0x4D
114#define CS7_SW5 0x4E
115#define CS8_SW5 0x4F
116#define CS9_SW5 0x50
117#define CS10_SW5 0x51
118#define CS11_SW5 0x52
119#define CS12_SW5 0x53
120#define CS13_SW5 0x54
121#define CS14_SW5 0x55
122#define CS15_SW5 0x56
123#define CS16_SW5 0x57
124#define CS17_SW5 0x58
125#define CS18_SW5 0x59
126#define CS1_SW6 0x5A
127#define CS2_SW6 0x5B
128#define CS3_SW6 0x5C
129#define CS4_SW6 0x5D
130#define CS5_SW6 0x5E
131#define CS6_SW6 0x5F
132#define CS7_SW6 0x60
133#define CS8_SW6 0x61
134#define CS9_SW6 0x62
135#define CS10_SW6 0x63
136#define CS11_SW6 0x64
137#define CS12_SW6 0x65
138#define CS13_SW6 0x66
139#define CS14_SW6 0x67
140#define CS15_SW6 0x68
141#define CS16_SW6 0x69
142#define CS17_SW6 0x6A
143#define CS18_SW6 0x6B
144#define CS1_SW7 0x6C
145#define CS2_SW7 0x6D
146#define CS3_SW7 0x6E
147#define CS4_SW7 0x6F
148#define CS5_SW7 0x70
149#define CS6_SW7 0x71
150#define CS7_SW7 0x72
151#define CS8_SW7 0x73
152#define CS9_SW7 0x74
153#define CS10_SW7 0x75
154#define CS11_SW7 0x76
155#define CS12_SW7 0x77
156#define CS13_SW7 0x78
157#define CS14_SW7 0x79
158#define CS15_SW7 0x7A
159#define CS16_SW7 0x7B
160#define CS17_SW7 0x7C
161#define CS18_SW7 0x7D
162#define CS1_SW8 0x7E
163#define CS2_SW8 0x7F
164#define CS3_SW8 0x80
165#define CS4_SW8 0x81
166#define CS5_SW8 0x82
167#define CS6_SW8 0x83
168#define CS7_SW8 0x84
169#define CS8_SW8 0x85
170#define CS9_SW8 0x86
171#define CS10_SW8 0x87
172#define CS11_SW8 0x88
173#define CS12_SW8 0x89
174#define CS13_SW8 0x8A
175#define CS14_SW8 0x8B
176#define CS15_SW8 0x8C
177#define CS16_SW8 0x8D
178#define CS17_SW8 0x8E
179#define CS18_SW8 0x8F
180#define CS1_SW9 0x90
181#define CS2_SW9 0x91
182#define CS3_SW9 0x92
183#define CS4_SW9 0x93
184#define CS5_SW9 0x94
185#define CS6_SW9 0x95
186#define CS7_SW9 0x96
187#define CS8_SW9 0x97
188#define CS9_SW9 0x98
189#define CS10_SW9 0x99
190#define CS11_SW9 0x9A
191#define CS12_SW9 0x9B
192#define CS13_SW9 0x9C
193#define CS14_SW9 0x9D
194#define CS15_SW9 0x9E
195#define CS16_SW9 0x9F
196#define CS17_SW9 0xA0
197#define CS18_SW9 0xA1
198#define CS1_SW10 0xA2
199#define CS2_SW10 0xA3
200#define CS3_SW10 0xA4
201#define CS4_SW10 0xA5
202#define CS5_SW10 0xA6
203#define CS6_SW10 0xA7
204#define CS7_SW10 0xA8
205#define CS8_SW10 0xA9
206#define CS9_SW10 0xAA
207#define CS10_SW10 0xAB
208#define CS11_SW10 0xAC
209#define CS12_SW10 0xAD
210#define CS13_SW10 0xAE
211#define CS14_SW10 0xAF
212#define CS15_SW10 0xB0
213#define CS16_SW10 0xB1
214#define CS17_SW10 0xB2
215#define CS18_SW10 0xB3
216#define CS1_SW11 0xB4
217#define CS2_SW11 0xB5
218#define CS3_SW11 0xB6
219#define CS4_SW11 0xB7
220#define CS5_SW11 0xB8
221#define CS6_SW11 0xB9
222#define CS7_SW11 0xBA
223#define CS8_SW11 0xBB
224#define CS9_SW11 0xBC
225#define CS10_SW11 0xBD
226#define CS11_SW11 0xBE
227#define CS12_SW11 0xBF
228#define CS13_SW11 0xC0
229#define CS14_SW11 0xC1
230#define CS15_SW11 0xC2
231#define CS16_SW11 0xC3
232#define CS17_SW11 0xC4
233#define CS18_SW11 0xC5
234#define CS1_SW12 0xC6
235#define CS2_SW12 0xC7
236#define CS3_SW12 0xC8
237#define CS4_SW12 0xC9
238#define CS5_SW12 0xCA
239#define CS6_SW12 0xCB
240#define CS7_SW12 0xCC
241#define CS8_SW12 0xCD
242#define CS9_SW12 0xCE
243#define CS10_SW12 0xCF
244#define CS11_SW12 0xD0
245#define CS12_SW12 0xD1
246#define CS13_SW12 0xD2
247#define CS14_SW12 0xD3
248#define CS15_SW12 0xD4
249#define CS16_SW12 0xD5
250#define CS17_SW12 0xD6
251#define CS18_SW12 0xD7
diff --git a/drivers/chibios/serial.c b/drivers/chibios/serial.c
index 54f7e1321..f54fbcee4 100644
--- a/drivers/chibios/serial.c
+++ b/drivers/chibios/serial.c
@@ -74,21 +74,12 @@ static THD_FUNCTION(Thread1, arg) {
74 } 74 }
75} 75}
76 76
77static SSTD_t *Transaction_table = NULL; 77void soft_serial_initiator_init(void) {
78static uint8_t Transaction_table_size = 0;
79
80void soft_serial_initiator_init(SSTD_t *sstd_table, int sstd_table_size) {
81 Transaction_table = sstd_table;
82 Transaction_table_size = (uint8_t)sstd_table_size;
83
84 serial_output(); 78 serial_output();
85 serial_high(); 79 serial_high();
86} 80}
87 81
88void soft_serial_target_init(SSTD_t *sstd_table, int sstd_table_size) { 82void soft_serial_target_init(void) {
89 Transaction_table = sstd_table;
90 Transaction_table_size = (uint8_t)sstd_table_size;
91
92 serial_input(); 83 serial_input();
93 84
94 palEnablePadEvent(PAL_PORT(SOFT_SERIAL_PIN), PAL_PAD(SOFT_SERIAL_PIN), PAL_EVENT_MODE_FALLING_EDGE); 85 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) {
154 uint8_t checksum_computed = 0; 145 uint8_t checksum_computed = 0;
155 int sstd_index = 0; 146 int sstd_index = 0;
156 147
157#ifdef SERIAL_USE_MULTI_TRANSACTION
158 sstd_index = serial_read_byte(); 148 sstd_index = serial_read_byte();
159 sync_send(); 149 sync_send();
160#endif
161 150
162 SSTD_t *trans = &Transaction_table[sstd_index]; 151 split_transaction_desc_t *trans = &split_transaction_table[sstd_index];
163 for (int i = 0; i < trans->initiator2target_buffer_size; ++i) { 152 for (int i = 0; i < trans->initiator2target_buffer_size; ++i) {
164 trans->initiator2target_buffer[i] = serial_read_byte(); 153 split_trans_initiator2target_buffer(trans)[i] = serial_read_byte();
165 sync_send(); 154 sync_send();
166 checksum_computed += trans->initiator2target_buffer[i]; 155 checksum_computed += split_trans_initiator2target_buffer(trans)[i];
167 } 156 }
168 checksum_computed ^= 7; 157 checksum_computed ^= 7;
169 uint8_t checksum_received = serial_read_byte(); 158 uint8_t checksum_received = serial_read_byte();
@@ -172,12 +161,17 @@ void interrupt_handler(void *arg) {
172 // wait for the sync to finish sending 161 // wait for the sync to finish sending
173 serial_delay(); 162 serial_delay();
174 163
164 // Allow any slave processing to occur
165 if (trans->slave_callback) {
166 trans->slave_callback(trans->initiator2target_buffer_size, split_trans_initiator2target_buffer(trans), trans->target2initiator_buffer_size, split_trans_target2initiator_buffer(trans));
167 }
168
175 uint8_t checksum = 0; 169 uint8_t checksum = 0;
176 for (int i = 0; i < trans->target2initiator_buffer_size; ++i) { 170 for (int i = 0; i < trans->target2initiator_buffer_size; ++i) {
177 serial_write_byte(trans->target2initiator_buffer[i]); 171 serial_write_byte(split_trans_target2initiator_buffer(trans)[i]);
178 sync_send(); 172 sync_send();
179 serial_delay_half(); 173 serial_delay_half();
180 checksum += trans->target2initiator_buffer[i]; 174 checksum += split_trans_target2initiator_buffer(trans)[i];
181 } 175 }
182 serial_write_byte(checksum ^ 7); 176 serial_write_byte(checksum ^ 7);
183 sync_send(); 177 sync_send();
@@ -206,15 +200,10 @@ void interrupt_handler(void *arg) {
206// TRANSACTION_NO_RESPONSE 200// TRANSACTION_NO_RESPONSE
207// TRANSACTION_DATA_ERROR 201// TRANSACTION_DATA_ERROR
208// this code is very time dependent, so we need to disable interrupts 202// this code is very time dependent, so we need to disable interrupts
209#ifndef SERIAL_USE_MULTI_TRANSACTION
210int soft_serial_transaction(void) {
211 int sstd_index = 0;
212#else
213int soft_serial_transaction(int sstd_index) { 203int soft_serial_transaction(int sstd_index) {
214#endif 204 if (sstd_index > NUM_TOTAL_TRANSACTIONS) return TRANSACTION_TYPE_ERROR;
215 205 split_transaction_desc_t *trans = &split_transaction_table[sstd_index];
216 if (sstd_index > Transaction_table_size) return TRANSACTION_TYPE_ERROR; 206 if (!trans->status) return TRANSACTION_TYPE_ERROR; // not registered
217 SSTD_t *trans = &Transaction_table[sstd_index];
218 207
219 // TODO: remove extra delay between transactions 208 // TODO: remove extra delay between transactions
220 serial_delay(); 209 serial_delay();
@@ -244,14 +233,13 @@ int soft_serial_transaction(int sstd_index) {
244 233
245 uint8_t checksum = 0; 234 uint8_t checksum = 0;
246 // send data to the slave 235 // send data to the slave
247#ifdef SERIAL_USE_MULTI_TRANSACTION
248 serial_write_byte(sstd_index); // first chunk is transaction id 236 serial_write_byte(sstd_index); // first chunk is transaction id
249 sync_recv(); 237 sync_recv();
250#endif 238
251 for (int i = 0; i < trans->initiator2target_buffer_size; ++i) { 239 for (int i = 0; i < trans->initiator2target_buffer_size; ++i) {
252 serial_write_byte(trans->initiator2target_buffer[i]); 240 serial_write_byte(split_trans_initiator2target_buffer(trans)[i]);
253 sync_recv(); 241 sync_recv();
254 checksum += trans->initiator2target_buffer[i]; 242 checksum += split_trans_initiator2target_buffer(trans)[i];
255 } 243 }
256 serial_write_byte(checksum ^ 7); 244 serial_write_byte(checksum ^ 7);
257 sync_recv(); 245 sync_recv();
@@ -262,9 +250,9 @@ int soft_serial_transaction(int sstd_index) {
262 // receive data from the slave 250 // receive data from the slave
263 uint8_t checksum_computed = 0; 251 uint8_t checksum_computed = 0;
264 for (int i = 0; i < trans->target2initiator_buffer_size; ++i) { 252 for (int i = 0; i < trans->target2initiator_buffer_size; ++i) {
265 trans->target2initiator_buffer[i] = serial_read_byte(); 253 split_trans_target2initiator_buffer(trans)[i] = serial_read_byte();
266 sync_recv(); 254 sync_recv();
267 checksum_computed += trans->target2initiator_buffer[i]; 255 checksum_computed += split_trans_target2initiator_buffer(trans)[i];
268 } 256 }
269 checksum_computed ^= 7; 257 checksum_computed ^= 7;
270 uint8_t checksum_received = serial_read_byte(); 258 uint8_t checksum_received = serial_read_byte();
diff --git a/drivers/chibios/serial.h b/drivers/chibios/serial.h
deleted file mode 100644
index 0c1857d52..000000000
--- a/drivers/chibios/serial.h
+++ /dev/null
@@ -1,62 +0,0 @@
1#pragma once
2
3#include <stdbool.h>
4
5// /////////////////////////////////////////////////////////////////
6// Need Soft Serial defines in config.h
7// /////////////////////////////////////////////////////////////////
8// ex.
9// #define SOFT_SERIAL_PIN ?? // ?? = D0,D1,D2,D3,E6
10// OPTIONAL: #define SELECT_SOFT_SERIAL_SPEED ? // ? = 1,2,3,4,5
11// // 1: about 137kbps (default)
12// // 2: about 75kbps
13// // 3: about 39kbps
14// // 4: about 26kbps
15// // 5: about 20kbps
16//
17// //// USE simple API (using signle-type transaction function)
18// /* nothing */
19// //// USE flexible API (using multi-type transaction function)
20// #define SERIAL_USE_MULTI_TRANSACTION
21//
22// /////////////////////////////////////////////////////////////////
23
24// Soft Serial Transaction Descriptor
25typedef struct _SSTD_t {
26 uint8_t *status;
27 uint8_t initiator2target_buffer_size;
28 uint8_t *initiator2target_buffer;
29 uint8_t target2initiator_buffer_size;
30 uint8_t *target2initiator_buffer;
31} SSTD_t;
32#define TID_LIMIT(table) (sizeof(table) / sizeof(SSTD_t))
33
34// initiator is transaction start side
35void soft_serial_initiator_init(SSTD_t *sstd_table, int sstd_table_size);
36// target is interrupt accept side
37void soft_serial_target_init(SSTD_t *sstd_table, int sstd_table_size);
38
39// initiator result
40#define TRANSACTION_END 0
41#define TRANSACTION_NO_RESPONSE 0x1
42#define TRANSACTION_DATA_ERROR 0x2
43#define TRANSACTION_TYPE_ERROR 0x4
44#ifndef SERIAL_USE_MULTI_TRANSACTION
45int soft_serial_transaction(void);
46#else
47int soft_serial_transaction(int sstd_index);
48#endif
49
50// target status
51// *SSTD_t.status has
52// initiator:
53// TRANSACTION_END
54// or TRANSACTION_NO_RESPONSE
55// or TRANSACTION_DATA_ERROR
56// target:
57// TRANSACTION_DATA_ERROR
58// or TRANSACTION_ACCEPTED
59#define TRANSACTION_ACCEPTED 0x8
60#ifdef SERIAL_USE_MULTI_TRANSACTION
61int soft_serial_get_and_clean_status(int sstd_index);
62#endif
diff --git a/drivers/chibios/serial_usart.c b/drivers/chibios/serial_usart.c
index cae29388c..9f180d2d7 100644
--- a/drivers/chibios/serial_usart.c
+++ b/drivers/chibios/serial_usart.c
@@ -113,37 +113,29 @@ void usart_slave_init(void) {
113 chThdCreateStatic(waSlaveThread, sizeof(waSlaveThread), HIGHPRIO, SlaveThread, NULL); 113 chThdCreateStatic(waSlaveThread, sizeof(waSlaveThread), HIGHPRIO, SlaveThread, NULL);
114} 114}
115 115
116static SSTD_t* Transaction_table = NULL; 116void soft_serial_initiator_init(void) { usart_master_init(); }
117static uint8_t Transaction_table_size = 0;
118 117
119void soft_serial_initiator_init(SSTD_t* sstd_table, int sstd_table_size) { 118void soft_serial_target_init(void) { usart_slave_init(); }
120 Transaction_table = sstd_table;
121 Transaction_table_size = (uint8_t)sstd_table_size;
122
123 usart_master_init();
124}
125
126void soft_serial_target_init(SSTD_t* sstd_table, int sstd_table_size) {
127 Transaction_table = sstd_table;
128 Transaction_table_size = (uint8_t)sstd_table_size;
129
130 usart_slave_init();
131}
132 119
133void handle_soft_serial_slave(void) { 120void handle_soft_serial_slave(void) {
134 uint8_t sstd_index = sdGet(&SERIAL_USART_DRIVER); // first chunk is always transaction id 121 uint8_t sstd_index = sdGet(&SERIAL_USART_DRIVER); // first chunk is always transaction id
135 SSTD_t* trans = &Transaction_table[sstd_index]; 122 split_transaction_desc_t* trans = &split_transaction_table[sstd_index];
136 123
137 // Always write back the sstd_index as part of a basic handshake 124 // Always write back the sstd_index as part of a basic handshake
138 sstd_index ^= HANDSHAKE_MAGIC; 125 sstd_index ^= HANDSHAKE_MAGIC;
139 sdWrite(&SERIAL_USART_DRIVER, &sstd_index, sizeof(sstd_index)); 126 sdWrite(&SERIAL_USART_DRIVER, &sstd_index, sizeof(sstd_index));
140 127
141 if (trans->initiator2target_buffer_size) { 128 if (trans->initiator2target_buffer_size) {
142 sdRead(&SERIAL_USART_DRIVER, trans->initiator2target_buffer, trans->initiator2target_buffer_size); 129 sdRead(&SERIAL_USART_DRIVER, split_trans_initiator2target_buffer(trans), trans->initiator2target_buffer_size);
130 }
131
132 // Allow any slave processing to occur
133 if (trans->slave_callback) {
134 trans->slave_callback(trans->initiator2target_buffer_size, split_trans_initiator2target_buffer(trans), trans->target2initiator_buffer_size, split_trans_target2initiator_buffer(trans));
143 } 135 }
144 136
145 if (trans->target2initiator_buffer_size) { 137 if (trans->target2initiator_buffer_size) {
146 sdWrite(&SERIAL_USART_DRIVER, trans->target2initiator_buffer, trans->target2initiator_buffer_size); 138 sdWrite(&SERIAL_USART_DRIVER, split_trans_target2initiator_buffer(trans), trans->target2initiator_buffer_size);
147 } 139 }
148 140
149 if (trans->status) { 141 if (trans->status) {
@@ -160,17 +152,14 @@ void handle_soft_serial_slave(void) {
160// TRANSACTION_END 152// TRANSACTION_END
161// TRANSACTION_NO_RESPONSE 153// TRANSACTION_NO_RESPONSE
162// TRANSACTION_DATA_ERROR 154// TRANSACTION_DATA_ERROR
163#ifndef SERIAL_USE_MULTI_TRANSACTION
164int soft_serial_transaction(void) {
165 uint8_t sstd_index = 0;
166#else
167int soft_serial_transaction(int index) { 155int soft_serial_transaction(int index) {
168 uint8_t sstd_index = index; 156 uint8_t sstd_index = index;
169#endif
170 157
171 if (sstd_index > Transaction_table_size) return TRANSACTION_TYPE_ERROR; 158 if (sstd_index > NUM_TOTAL_TRANSACTIONS) return TRANSACTION_TYPE_ERROR;
172 SSTD_t* trans = &Transaction_table[sstd_index]; 159 split_transaction_desc_t* trans = &split_transaction_table[sstd_index];
173 msg_t res = 0; 160 msg_t res = 0;
161
162 if (!trans->status) return TRANSACTION_TYPE_ERROR; // not registered
174 163
175 sdClear(&SERIAL_USART_DRIVER); 164 sdClear(&SERIAL_USART_DRIVER);
176 165
@@ -189,7 +178,7 @@ int soft_serial_transaction(int index) {
189 } 178 }
190 179
191 if (trans->initiator2target_buffer_size) { 180 if (trans->initiator2target_buffer_size) {
192 res = sdWriteTimeout(&SERIAL_USART_DRIVER, trans->initiator2target_buffer, trans->initiator2target_buffer_size, TIME_MS2I(SERIAL_USART_TIMEOUT)); 181 res = sdWriteTimeout(&SERIAL_USART_DRIVER, split_trans_initiator2target_buffer(trans), trans->initiator2target_buffer_size, TIME_MS2I(SERIAL_USART_TIMEOUT));
193 if (res < 0) { 182 if (res < 0) {
194 dprintf("serial::usart_transmit NO_RESPONSE\n"); 183 dprintf("serial::usart_transmit NO_RESPONSE\n");
195 return TRANSACTION_NO_RESPONSE; 184 return TRANSACTION_NO_RESPONSE;
@@ -197,7 +186,7 @@ int soft_serial_transaction(int index) {
197 } 186 }
198 187
199 if (trans->target2initiator_buffer_size) { 188 if (trans->target2initiator_buffer_size) {
200 res = sdReadTimeout(&SERIAL_USART_DRIVER, trans->target2initiator_buffer, trans->target2initiator_buffer_size, TIME_MS2I(SERIAL_USART_TIMEOUT)); 189 res = sdReadTimeout(&SERIAL_USART_DRIVER, split_trans_target2initiator_buffer(trans), trans->target2initiator_buffer_size, TIME_MS2I(SERIAL_USART_TIMEOUT));
201 if (res < 0) { 190 if (res < 0) {
202 dprintf("serial::usart_receive NO_RESPONSE\n"); 191 dprintf("serial::usart_receive NO_RESPONSE\n");
203 return TRANSACTION_NO_RESPONSE; 192 return TRANSACTION_NO_RESPONSE;
diff --git a/drivers/chibios/spi_master.c b/drivers/chibios/spi_master.c
index 4852a6eba..28ddcbb2b 100644
--- a/drivers/chibios/spi_master.c
+++ b/drivers/chibios/spi_master.c
@@ -18,8 +18,13 @@
18 18
19#include "timer.h" 19#include "timer.h"
20 20
21static pin_t currentSlavePin = NO_PIN; 21static pin_t currentSlavePin = NO_PIN;
22static SPIConfig spiConfig = {false, NULL, 0, 0, 0, 0}; 22
23#if defined(K20x) || defined(KL2x)
24static SPIConfig spiConfig = {NULL, 0, 0, 0};
25#else
26static SPIConfig spiConfig = {false, NULL, 0, 0, 0, 0};
27#endif
23 28
24__attribute__((weak)) void spi_init(void) { 29__attribute__((weak)) void spi_init(void) {
25 static bool is_initialised = false; 30 static bool is_initialised = false;
@@ -27,15 +32,15 @@ __attribute__((weak)) void spi_init(void) {
27 is_initialised = true; 32 is_initialised = true;
28 33
29 // Try releasing special pins for a short time 34 // Try releasing special pins for a short time
30 palSetPadMode(PAL_PORT(SPI_SCK_PIN), PAL_PAD(SPI_SCK_PIN), PAL_MODE_INPUT); 35 setPinInput(SPI_SCK_PIN);
31 palSetPadMode(PAL_PORT(SPI_MOSI_PIN), PAL_PAD(SPI_MOSI_PIN), PAL_MODE_INPUT); 36 setPinInput(SPI_MOSI_PIN);
32 palSetPadMode(PAL_PORT(SPI_MISO_PIN), PAL_PAD(SPI_MISO_PIN), PAL_MODE_INPUT); 37 setPinInput(SPI_MISO_PIN);
33 38
34 chThdSleepMilliseconds(10); 39 chThdSleepMilliseconds(10);
35#if defined(USE_GPIOV1) 40#if defined(USE_GPIOV1)
36 palSetPadMode(PAL_PORT(SPI_SCK_PIN), PAL_PAD(SPI_SCK_PIN), PAL_MODE_STM32_ALTERNATE_PUSHPULL); 41 palSetPadMode(PAL_PORT(SPI_SCK_PIN), PAL_PAD(SPI_SCK_PIN), SPI_SCK_PAL_MODE);
37 palSetPadMode(PAL_PORT(SPI_MOSI_PIN), PAL_PAD(SPI_MOSI_PIN), PAL_MODE_STM32_ALTERNATE_PUSHPULL); 42 palSetPadMode(PAL_PORT(SPI_MOSI_PIN), PAL_PAD(SPI_MOSI_PIN), SPI_MOSI_PAL_MODE);
38 palSetPadMode(PAL_PORT(SPI_MISO_PIN), PAL_PAD(SPI_MISO_PIN), PAL_MODE_STM32_ALTERNATE_PUSHPULL); 43 palSetPadMode(PAL_PORT(SPI_MISO_PIN), PAL_PAD(SPI_MISO_PIN), SPI_MISO_PAL_MODE);
39#else 44#else
40 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); 45 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);
41 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); 46 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) {
58 return false; 63 return false;
59 } 64 }
60 65
66#if defined(K20x) || defined(KL2x)
67 spiConfig.tar0 = SPIx_CTARn_FMSZ(7) | SPIx_CTARn_ASC(1);
68
69 if (lsbFirst) {
70 spiConfig.tar0 |= SPIx_CTARn_LSBFE;
71 }
72
73 switch (mode) {
74 case 0:
75 break;
76 case 1:
77 spiConfig.tar0 |= SPIx_CTARn_CPHA;
78 break;
79 case 2:
80 spiConfig.tar0 |= SPIx_CTARn_CPOL;
81 break;
82 case 3:
83 spiConfig.tar0 |= SPIx_CTARn_CPHA | SPIx_CTARn_CPOL;
84 break;
85 }
86
87 switch (roundedDivisor) {
88 case 2:
89 spiConfig.tar0 |= SPIx_CTARn_BR(0);
90 break;
91 case 4:
92 spiConfig.tar0 |= SPIx_CTARn_BR(1);
93 break;
94 case 8:
95 spiConfig.tar0 |= SPIx_CTARn_BR(3);
96 break;
97 case 16:
98 spiConfig.tar0 |= SPIx_CTARn_BR(4);
99 break;
100 case 32:
101 spiConfig.tar0 |= SPIx_CTARn_BR(5);
102 break;
103 case 64:
104 spiConfig.tar0 |= SPIx_CTARn_BR(6);
105 break;
106 case 128:
107 spiConfig.tar0 |= SPIx_CTARn_BR(7);
108 break;
109 case 256:
110 spiConfig.tar0 |= SPIx_CTARn_BR(8);
111 break;
112 }
113#else
61 spiConfig.cr1 = 0; 114 spiConfig.cr1 = 0;
62 115
63 if (lsbFirst) { 116 if (lsbFirst) {
@@ -103,6 +156,7 @@ bool spi_start(pin_t slavePin, bool lsbFirst, uint8_t mode, uint16_t divisor) {
103 spiConfig.cr1 |= SPI_CR1_BR_2 | SPI_CR1_BR_1 | SPI_CR1_BR_0; 156 spiConfig.cr1 |= SPI_CR1_BR_2 | SPI_CR1_BR_1 | SPI_CR1_BR_0;
104 break; 157 break;
105 } 158 }
159#endif
106 160
107 currentSlavePin = slavePin; 161 currentSlavePin = slavePin;
108 spiConfig.ssport = PAL_PORT(slavePin); 162 spiConfig.ssport = PAL_PORT(slavePin);
diff --git a/drivers/chibios/spi_master.h b/drivers/chibios/spi_master.h
index e93580e31..b5a6ef143 100644
--- a/drivers/chibios/spi_master.h
+++ b/drivers/chibios/spi_master.h
@@ -21,6 +21,7 @@
21#include <stdbool.h> 21#include <stdbool.h>
22 22
23#include "gpio.h" 23#include "gpio.h"
24#include "chibios_config.h"
24 25
25#ifndef SPI_DRIVER 26#ifndef SPI_DRIVER
26# define SPI_DRIVER SPID2 27# define SPI_DRIVER SPID2
@@ -31,7 +32,11 @@
31#endif 32#endif
32 33
33#ifndef SPI_SCK_PAL_MODE 34#ifndef SPI_SCK_PAL_MODE
34# define SPI_SCK_PAL_MODE 5 35# if defined(USE_GPIOV1)
36# define SPI_SCK_PAL_MODE PAL_MODE_STM32_ALTERNATE_PUSHPULL
37# else
38# define SPI_SCK_PAL_MODE 5
39# endif
35#endif 40#endif
36 41
37#ifndef SPI_MOSI_PIN 42#ifndef SPI_MOSI_PIN
@@ -39,7 +44,11 @@
39#endif 44#endif
40 45
41#ifndef SPI_MOSI_PAL_MODE 46#ifndef SPI_MOSI_PAL_MODE
42# define SPI_MOSI_PAL_MODE 5 47# if defined(USE_GPIOV1)
48# define SPI_MOSI_PAL_MODE PAL_MODE_STM32_ALTERNATE_PUSHPULL
49# else
50# define SPI_MOSI_PAL_MODE 5
51# endif
43#endif 52#endif
44 53
45#ifndef SPI_MISO_PIN 54#ifndef SPI_MISO_PIN
@@ -47,7 +56,11 @@
47#endif 56#endif
48 57
49#ifndef SPI_MISO_PAL_MODE 58#ifndef SPI_MISO_PAL_MODE
50# define SPI_MISO_PAL_MODE 5 59# if defined(USE_GPIOV1)
60# define SPI_MISO_PAL_MODE PAL_MODE_STM32_ALTERNATE_PUSHPULL
61# else
62# define SPI_MISO_PAL_MODE 5
63# endif
51#endif 64#endif
52 65
53typedef int16_t spi_status_t; 66typedef int16_t spi_status_t;
diff --git a/drivers/eeprom/eeprom_i2c.c b/drivers/eeprom/eeprom_i2c.c
index 4210f06f9..8e80ff544 100644
--- a/drivers/eeprom/eeprom_i2c.c
+++ b/drivers/eeprom/eeprom_i2c.c
@@ -16,6 +16,9 @@
16 16
17#include <stdint.h> 17#include <stdint.h>
18#include <string.h> 18#include <string.h>
19#if defined(EXTERNAL_EEPROM_WP_PIN)
20# include "gpio.h"
21#endif
19 22
20/* 23/*
21 Note that the implementations of eeprom_XXXX_YYYY on AVR are normally 24 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) {
50 } 53 }
51} 54}
52 55
53void eeprom_driver_init(void) { i2c_init(); } 56void eeprom_driver_init(void) {
57 i2c_init();
58#if defined(EXTERNAL_EEPROM_WP_PIN)
59 /* We are setting the WP pin to high in a way that requires at least two bit-flips to change back to 0 */
60 writePin(EXTERNAL_EEPROM_WP_PIN, 1);
61 setPinInputHigh(EXTERNAL_EEPROM_WP_PIN);
62#endif
63}
54 64
55void eeprom_driver_erase(void) { 65void eeprom_driver_erase(void) {
56#if defined(CONSOLE_ENABLE) && defined(DEBUG_EEPROM_OUTPUT) 66#if defined(CONSOLE_ENABLE) && defined(DEBUG_EEPROM_OUTPUT)
@@ -89,6 +99,11 @@ void eeprom_write_block(const void *buf, void *addr, size_t len) {
89 uint8_t * read_buf = (uint8_t *)buf; 99 uint8_t * read_buf = (uint8_t *)buf;
90 uintptr_t target_addr = (uintptr_t)addr; 100 uintptr_t target_addr = (uintptr_t)addr;
91 101
102#if defined(EXTERNAL_EEPROM_WP_PIN)
103 setPinOutput(EXTERNAL_EEPROM_WP_PIN);
104 writePin(EXTERNAL_EEPROM_WP_PIN, 0);
105#endif
106
92 while (len > 0) { 107 while (len > 0) {
93 uintptr_t page_offset = target_addr % EXTERNAL_EEPROM_PAGE_SIZE; 108 uintptr_t page_offset = target_addr % EXTERNAL_EEPROM_PAGE_SIZE;
94 int write_length = EXTERNAL_EEPROM_PAGE_SIZE - page_offset; 109 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) {
116 target_addr += write_length; 131 target_addr += write_length;
117 len -= write_length; 132 len -= write_length;
118 } 133 }
134
135#if defined(EXTERNAL_EEPROM_WP_PIN)
136 /* We are setting the WP pin to high in a way that requires at least two bit-flips to change back to 0 */
137 writePin(EXTERNAL_EEPROM_WP_PIN, 1);
138 setPinInputHigh(EXTERNAL_EEPROM_WP_PIN);
139#endif
119} 140}
diff --git a/drivers/haptic/haptic.c b/drivers/haptic/haptic.c
index de3f40052..3fab1be1a 100644
--- a/drivers/haptic/haptic.c
+++ b/drivers/haptic/haptic.c
@@ -291,6 +291,73 @@ void haptic_play(void) {
291#endif 291#endif
292} 292}
293 293
294__attribute__((weak)) bool get_haptic_enabled_key(uint16_t keycode, keyrecord_t *record) {
295 switch(keycode) {
296# ifdef NO_HAPTIC_MOD
297 case QK_MOD_TAP ... QK_MOD_TAP_MAX:
298 if (record->tap.count == 0) return false;
299 break;
300 case QK_LAYER_TAP_TOGGLE ... QK_LAYER_TAP_TOGGLE_MAX:
301 if (record->tap.count != TAPPING_TOGGLE) return false;
302 break;
303 case QK_LAYER_TAP ... QK_LAYER_TAP_MAX:
304 if (record->tap.count == 0) return false;
305 break;
306 case KC_LCTRL ... KC_RGUI:
307 case QK_MOMENTARY ... QK_MOMENTARY_MAX:
308# endif
309# ifdef NO_HAPTIC_FN
310 case KC_FN0 ... KC_FN31:
311# endif
312# ifdef NO_HAPTIC_ALPHA
313 case KC_A ... KC_Z:
314# endif
315# ifdef NO_HAPTIC_PUNCTUATION
316 case KC_ENTER:
317 case KC_ESCAPE:
318 case KC_BSPACE:
319 case KC_SPACE:
320 case KC_MINUS:
321 case KC_EQUAL:
322 case KC_LBRACKET:
323 case KC_RBRACKET:
324 case KC_BSLASH:
325 case KC_NONUS_HASH:
326 case KC_SCOLON:
327 case KC_QUOTE:
328 case KC_GRAVE:
329 case KC_COMMA:
330 case KC_SLASH:
331 case KC_DOT:
332 case KC_NONUS_BSLASH:
333# endif
334# ifdef NO_HAPTIC_LOCKKEYS
335 case KC_CAPSLOCK:
336 case KC_SCROLLLOCK:
337 case KC_NUMLOCK:
338# endif
339# ifdef NO_HAPTIC_NAV
340 case KC_PSCREEN:
341 case KC_PAUSE:
342 case KC_INSERT:
343 case KC_DELETE:
344 case KC_PGDOWN:
345 case KC_PGUP:
346 case KC_LEFT:
347 case KC_UP:
348 case KC_RIGHT:
349 case KC_DOWN:
350 case KC_END:
351 case KC_HOME:
352# endif
353# ifdef NO_HAPTIC_NUMERIC
354 case KC_1 ... KC_0:
355# endif
356 return false;
357 }
358 return true;
359}
360
294bool process_haptic(uint16_t keycode, keyrecord_t *record) { 361bool process_haptic(uint16_t keycode, keyrecord_t *record) {
295 if (keycode == HPT_ON && record->event.pressed) { 362 if (keycode == HPT_ON && record->event.pressed) {
296 haptic_enable(); 363 haptic_enable();
@@ -335,12 +402,12 @@ bool process_haptic(uint16_t keycode, keyrecord_t *record) {
335 if (haptic_config.enable) { 402 if (haptic_config.enable) {
336 if (record->event.pressed) { 403 if (record->event.pressed) {
337 // keypress 404 // keypress
338 if (haptic_config.feedback < 2) { 405 if (haptic_config.feedback < 2 && get_haptic_enabled_key(keycode, record)) {
339 haptic_play(); 406 haptic_play();
340 } 407 }
341 } else { 408 } else {
342 // keyrelease 409 // keyrelease
343 if (haptic_config.feedback > 0) { 410 if (haptic_config.feedback > 0 && get_haptic_enabled_key(keycode, record)) {
344 haptic_play(); 411 haptic_play();
345 } 412 }
346 } 413 }
diff --git a/drivers/lcd/st7565.c b/drivers/lcd/st7565.c
new file mode 100644
index 000000000..49b13c00f
--- /dev/null
+++ b/drivers/lcd/st7565.c
@@ -0,0 +1,496 @@
1/*
2Copyright 2021
3
4This program is free software: you can redistribute it and/or modify
5it under the terms of the GNU General Public License as published by
6the Free Software Foundation, either version 2 of the License, or
7(at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License
15along with this program. If not, see <http://www.gnu.org/licenses/>.
16*/
17
18#include "st7565.h"
19
20#include <string.h>
21
22#include "keyboard.h"
23#include "progmem.h"
24#include "timer.h"
25#include "wait.h"
26
27#include ST7565_FONT_H
28
29// Fundamental Commands
30#define CONTRAST 0x81
31#define DISPLAY_ALL_ON 0xA5
32#define DISPLAY_ALL_ON_RESUME 0xA4
33#define NORMAL_DISPLAY 0xA6
34#define INVERT_DISPLAY 0xA7
35#define DISPLAY_ON 0xAF
36#define DISPLAY_OFF 0xAE
37#define NOP 0xE3
38
39// Addressing Setting Commands
40#define PAM_SETCOLUMN_LSB 0x00
41#define PAM_SETCOLUMN_MSB 0x10
42#define PAM_PAGE_ADDR 0xB0 // 0xb0 -- 0xb7
43
44// Hardware Configuration Commands
45#define DISPLAY_START_LINE 0x40
46#define SEGMENT_REMAP 0xA0
47#define SEGMENT_REMAP_INV 0xA1
48#define COM_SCAN_INC 0xC0
49#define COM_SCAN_DEC 0xC8
50#define LCD_BIAS_7 0xA3
51#define LCD_BIAS_9 0xA2
52#define RESISTOR_RATIO 0x20
53#define POWER_CONTROL 0x28
54
55// Misc defines
56#ifndef ST7565_BLOCK_COUNT
57# define ST7565_BLOCK_COUNT (sizeof(ST7565_BLOCK_TYPE) * 8)
58#endif
59#ifndef ST7565_BLOCK_SIZE
60# define ST7565_BLOCK_SIZE (ST7565_MATRIX_SIZE / ST7565_BLOCK_COUNT)
61#endif
62
63#define ST7565_ALL_BLOCKS_MASK (((((ST7565_BLOCK_TYPE)1 << (ST7565_BLOCK_COUNT - 1)) - 1) << 1) | 1)
64
65#define HAS_FLAGS(bits, flags) ((bits & flags) == flags)
66
67// Display buffer's is the same as the display memory layout
68// this is so we don't end up with rounding errors with
69// parts of the display unusable or don't get cleared correctly
70// and also allows for drawing & inverting
71uint8_t st7565_buffer[ST7565_MATRIX_SIZE];
72uint8_t * st7565_cursor;
73ST7565_BLOCK_TYPE st7565_dirty = 0;
74bool st7565_initialized = false;
75bool st7565_active = false;
76bool st7565_inverted = false;
77display_rotation_t st7565_rotation = DISPLAY_ROTATION_0;
78#if ST7565_TIMEOUT > 0
79uint32_t st7565_timeout;
80#endif
81#if ST7565_UPDATE_INTERVAL > 0
82uint16_t st7565_update_timeout;
83#endif
84
85// Flips the rendering bits for a character at the current cursor position
86static void InvertCharacter(uint8_t *cursor) {
87 const uint8_t *end = cursor + ST7565_FONT_WIDTH;
88 while (cursor < end) {
89 *cursor = ~(*cursor);
90 cursor++;
91 }
92}
93
94bool st7565_init(display_rotation_t rotation) {
95 setPinOutput(ST7565_A0_PIN);
96 writePinHigh(ST7565_A0_PIN);
97 setPinOutput(ST7565_RST_PIN);
98 writePinHigh(ST7565_RST_PIN);
99
100 st7565_rotation = st7565_init_user(rotation);
101
102 spi_init();
103 spi_start(ST7565_SS_PIN, false, 0, ST7565_SPI_CLK_DIVISOR);
104
105 st7565_reset();
106
107 st7565_send_cmd(LCD_BIAS_7);
108 if (!HAS_FLAGS(st7565_rotation, DISPLAY_ROTATION_180)) {
109 st7565_send_cmd(SEGMENT_REMAP);
110 st7565_send_cmd(COM_SCAN_DEC);
111 } else {
112 st7565_send_cmd(SEGMENT_REMAP_INV);
113 st7565_send_cmd(COM_SCAN_INC);
114 }
115 st7565_send_cmd(DISPLAY_START_LINE | 0x00);
116 st7565_send_cmd(CONTRAST);
117 st7565_send_cmd(ST7565_CONTRAST);
118 st7565_send_cmd(RESISTOR_RATIO | 0x01);
119 st7565_send_cmd(POWER_CONTROL | 0x04);
120 wait_ms(50);
121 st7565_send_cmd(POWER_CONTROL | 0x06);
122 wait_ms(50);
123 st7565_send_cmd(POWER_CONTROL | 0x07);
124 wait_ms(10);
125 st7565_send_cmd(DISPLAY_ON);
126 st7565_send_cmd(DISPLAY_ALL_ON_RESUME);
127 st7565_send_cmd(NORMAL_DISPLAY);
128
129 spi_stop();
130
131#if ST7565_TIMEOUT > 0
132 st7565_timeout = timer_read32() + ST7565_TIMEOUT;
133#endif
134
135 st7565_clear();
136 st7565_initialized = true;
137 st7565_active = true;
138 return true;
139}
140
141__attribute__((weak)) display_rotation_t st7565_init_user(display_rotation_t rotation) { return rotation; }
142
143void st7565_clear(void) {
144 memset(st7565_buffer, 0, sizeof(st7565_buffer));
145 st7565_cursor = &st7565_buffer[0];
146 st7565_dirty = ST7565_ALL_BLOCKS_MASK;
147}
148
149uint8_t crot(uint8_t a, int8_t n) {
150 const uint8_t mask = 0x7;
151 n &= mask;
152 return a << n | a >> (-n & mask);
153}
154
155void st7565_render(void) {
156 if (!st7565_initialized) {
157 return;
158 }
159
160 // Do we have work to do?
161 st7565_dirty &= ST7565_ALL_BLOCKS_MASK;
162 if (!st7565_dirty) {
163 return;
164 }
165
166 // Find first dirty block
167 uint8_t update_start = 0;
168 while (!(st7565_dirty & ((ST7565_BLOCK_TYPE)1 << update_start))) {
169 ++update_start;
170 }
171
172 // Calculate commands to set memory addressing bounds.
173 uint8_t start_page = ST7565_BLOCK_SIZE * update_start / ST7565_DISPLAY_WIDTH;
174 uint8_t start_column = ST7565_BLOCK_SIZE * update_start % ST7565_DISPLAY_WIDTH;
175 // IC has 132 segment drivers, for panels with less width we need to offset the starting column
176 if (HAS_FLAGS(st7565_rotation, DISPLAY_ROTATION_180)) {
177 start_column += (132 - ST7565_DISPLAY_WIDTH);
178 }
179
180 spi_start(ST7565_SS_PIN, false, 0, ST7565_SPI_CLK_DIVISOR);
181
182 st7565_send_cmd(PAM_PAGE_ADDR | start_page);
183 st7565_send_cmd(PAM_SETCOLUMN_LSB | ((ST7565_COLUMN_OFFSET + start_column) & 0x0f));
184 st7565_send_cmd(PAM_SETCOLUMN_MSB | ((ST7565_COLUMN_OFFSET + start_column) >> 4 & 0x0f));
185
186 st7565_send_data(&st7565_buffer[ST7565_BLOCK_SIZE * update_start], ST7565_BLOCK_SIZE);
187
188 // Turn on display if it is off
189 st7565_on();
190
191 // Clear dirty flag
192 st7565_dirty &= ~((ST7565_BLOCK_TYPE)1 << update_start);
193}
194
195void st7565_set_cursor(uint8_t col, uint8_t line) {
196 uint16_t index = line * ST7565_DISPLAY_WIDTH + col * ST7565_FONT_WIDTH;
197
198 // Out of bounds?
199 if (index >= ST7565_MATRIX_SIZE) {
200 index = 0;
201 }
202
203 st7565_cursor = &st7565_buffer[index];
204}
205
206void st7565_advance_page(bool clearPageRemainder) {
207 uint16_t index = st7565_cursor - &st7565_buffer[0];
208 uint8_t remaining = ST7565_DISPLAY_WIDTH - (index % ST7565_DISPLAY_WIDTH);
209
210 if (clearPageRemainder) {
211 // Remaining Char count
212 remaining = remaining / ST7565_FONT_WIDTH;
213
214 // Write empty character until next line
215 while (remaining--) st7565_write_char(' ', false);
216 } else {
217 // Next page index out of bounds?
218 if (index + remaining >= ST7565_MATRIX_SIZE) {
219 index = 0;
220 remaining = 0;
221 }
222
223 st7565_cursor = &st7565_buffer[index + remaining];
224 }
225}
226
227void st7565_advance_char(void) {
228 uint16_t nextIndex = st7565_cursor - &st7565_buffer[0] + ST7565_FONT_WIDTH;
229 uint8_t remainingSpace = ST7565_DISPLAY_WIDTH - (nextIndex % ST7565_DISPLAY_WIDTH);
230
231 // Do we have enough space on the current line for the next character
232 if (remainingSpace < ST7565_FONT_WIDTH) {
233 nextIndex += remainingSpace;
234 }
235
236 // Did we go out of bounds
237 if (nextIndex >= ST7565_MATRIX_SIZE) {
238 nextIndex = 0;
239 }
240
241 // Update cursor position
242 st7565_cursor = &st7565_buffer[nextIndex];
243}
244
245// Main handler that writes character data to the display buffer
246void st7565_write_char(const char data, bool invert) {
247 // Advance to the next line if newline
248 if (data == '\n') {
249 // Old source wrote ' ' until end of line...
250 st7565_advance_page(true);
251 return;
252 }
253
254 if (data == '\r') {
255 st7565_advance_page(false);
256 return;
257 }
258
259 // copy the current render buffer to check for dirty after
260 static uint8_t st7565_temp_buffer[ST7565_FONT_WIDTH];
261 memcpy(&st7565_temp_buffer, st7565_cursor, ST7565_FONT_WIDTH);
262
263 _Static_assert(sizeof(font) >= ((ST7565_FONT_END + 1 - ST7565_FONT_START) * ST7565_FONT_WIDTH), "ST7565_FONT_END references outside array");
264
265 // set the reder buffer data
266 uint8_t cast_data = (uint8_t)data; // font based on unsigned type for index
267 if (cast_data < ST7565_FONT_START || cast_data > ST7565_FONT_END) {
268 memset(st7565_cursor, 0x00, ST7565_FONT_WIDTH);
269 } else {
270 const uint8_t *glyph = &font[(cast_data - ST7565_FONT_START) * ST7565_FONT_WIDTH];
271 memcpy_P(st7565_cursor, glyph, ST7565_FONT_WIDTH);
272 }
273
274 // Invert if needed
275 if (invert) {
276 InvertCharacter(st7565_cursor);
277 }
278
279 // Dirty check
280 if (memcmp(&st7565_temp_buffer, st7565_cursor, ST7565_FONT_WIDTH)) {
281 uint16_t index = st7565_cursor - &st7565_buffer[0];
282 st7565_dirty |= ((ST7565_BLOCK_TYPE)1 << (index / ST7565_BLOCK_SIZE));
283 // Edgecase check if the written data spans the 2 chunks
284 st7565_dirty |= ((ST7565_BLOCK_TYPE)1 << ((index + ST7565_FONT_WIDTH - 1) / ST7565_BLOCK_SIZE));
285 }
286
287 // Finally move to the next char
288 st7565_advance_char();
289}
290
291void st7565_write(const char *data, bool invert) {
292 const char *end = data + strlen(data);
293 while (data < end) {
294 st7565_write_char(*data, invert);
295 data++;
296 }
297}
298
299void st7565_write_ln(const char *data, bool invert) {
300 st7565_write(data, invert);
301 st7565_advance_page(true);
302}
303
304void st7565_pan(bool left) {
305 uint16_t i = 0;
306 for (uint16_t y = 0; y < ST7565_DISPLAY_HEIGHT / 8; y++) {
307 if (left) {
308 for (uint16_t x = 0; x < ST7565_DISPLAY_WIDTH - 1; x++) {
309 i = y * ST7565_DISPLAY_WIDTH + x;
310 st7565_buffer[i] = st7565_buffer[i + 1];
311 }
312 } else {
313 for (uint16_t x = ST7565_DISPLAY_WIDTH - 1; x > 0; x--) {
314 i = y * ST7565_DISPLAY_WIDTH + x;
315 st7565_buffer[i] = st7565_buffer[i - 1];
316 }
317 }
318 }
319 st7565_dirty = ST7565_ALL_BLOCKS_MASK;
320}
321
322display_buffer_reader_t st7565_read_raw(uint16_t start_index) {
323 if (start_index > ST7565_MATRIX_SIZE) start_index = ST7565_MATRIX_SIZE;
324 display_buffer_reader_t ret_reader;
325 ret_reader.current_element = &st7565_buffer[start_index];
326 ret_reader.remaining_element_count = ST7565_MATRIX_SIZE - start_index;
327 return ret_reader;
328}
329
330void st7565_write_raw_byte(const char data, uint16_t index) {
331 if (index > ST7565_MATRIX_SIZE) index = ST7565_MATRIX_SIZE;
332 if (st7565_buffer[index] == data) return;
333 st7565_buffer[index] = data;
334 st7565_dirty |= ((ST7565_BLOCK_TYPE)1 << (index / ST7565_BLOCK_SIZE));
335}
336
337void st7565_write_raw(const char *data, uint16_t size) {
338 uint16_t cursor_start_index = st7565_cursor - &st7565_buffer[0];
339 if ((size + cursor_start_index) > ST7565_MATRIX_SIZE) size = ST7565_MATRIX_SIZE - cursor_start_index;
340 for (uint16_t i = cursor_start_index; i < cursor_start_index + size; i++) {
341 uint8_t c = *data++;
342 if (st7565_buffer[i] == c) continue;
343 st7565_buffer[i] = c;
344 st7565_dirty |= ((ST7565_BLOCK_TYPE)1 << (i / ST7565_BLOCK_SIZE));
345 }
346}
347
348void st7565_write_pixel(uint8_t x, uint8_t y, bool on) {
349 if (x >= ST7565_DISPLAY_WIDTH) {
350 return;
351 }
352 uint16_t index = x + (y / 8) * ST7565_DISPLAY_WIDTH;
353 if (index >= ST7565_MATRIX_SIZE) {
354 return;
355 }
356 uint8_t data = st7565_buffer[index];
357 if (on) {
358 data |= (1 << (y % 8));
359 } else {
360 data &= ~(1 << (y % 8));
361 }
362 if (st7565_buffer[index] != data) {
363 st7565_buffer[index] = data;
364 st7565_dirty |= ((ST7565_BLOCK_TYPE)1 << (index / ST7565_BLOCK_SIZE));
365 }
366}
367
368#if defined(__AVR__)
369void st7565_write_P(const char *data, bool invert) {
370 uint8_t c = pgm_read_byte(data);
371 while (c != 0) {
372 st7565_write_char(c, invert);
373 c = pgm_read_byte(++data);
374 }
375}
376
377void st7565_write_ln_P(const char *data, bool invert) {
378 st7565_write_P(data, invert);
379 st7565_advance_page(true);
380}
381
382void st7565_write_raw_P(const char *data, uint16_t size) {
383 uint16_t cursor_start_index = st7565_cursor - &st7565_buffer[0];
384 if ((size + cursor_start_index) > ST7565_MATRIX_SIZE) size = ST7565_MATRIX_SIZE - cursor_start_index;
385 for (uint16_t i = cursor_start_index; i < cursor_start_index + size; i++) {
386 uint8_t c = pgm_read_byte(data++);
387 if (st7565_buffer[i] == c) continue;
388 st7565_buffer[i] = c;
389 st7565_dirty |= ((ST7565_BLOCK_TYPE)1 << (i / ST7565_BLOCK_SIZE));
390 }
391}
392#endif // defined(__AVR__)
393
394bool st7565_on(void) {
395 if (!st7565_initialized) {
396 return st7565_active;
397 }
398
399#if ST7565_TIMEOUT > 0
400 st7565_timeout = timer_read32() + ST7565_TIMEOUT;
401#endif
402
403 if (!st7565_active) {
404 spi_start(ST7565_SS_PIN, false, 0, ST7565_SPI_CLK_DIVISOR);
405 st7565_send_cmd(DISPLAY_ON);
406 spi_stop();
407 st7565_active = true;
408 st7565_on_user();
409 }
410 return st7565_active;
411}
412
413__attribute__((weak)) void st7565_on_user(void) {}
414
415bool st7565_off(void) {
416 if (!st7565_initialized) {
417 return !st7565_active;
418 }
419
420 if (st7565_active) {
421 spi_start(ST7565_SS_PIN, false, 0, ST7565_SPI_CLK_DIVISOR);
422 st7565_send_cmd(DISPLAY_OFF);
423 spi_stop();
424 st7565_active = false;
425 st7565_off_user();
426 }
427 return !st7565_active;
428}
429
430__attribute__((weak)) void st7565_off_user(void) {}
431
432bool st7565_is_on(void) { return st7565_active; }
433
434bool st7565_invert(bool invert) {
435 if (!st7565_initialized) {
436 return st7565_inverted;
437 }
438
439 if (invert != st7565_inverted) {
440 spi_start(ST7565_SS_PIN, false, 0, ST7565_SPI_CLK_DIVISOR);
441 st7565_send_cmd(invert ? INVERT_DISPLAY : NORMAL_DISPLAY);
442 spi_stop();
443 st7565_inverted = invert;
444 }
445 return st7565_inverted;
446}
447
448uint8_t st7565_max_chars(void) { return ST7565_DISPLAY_WIDTH / ST7565_FONT_WIDTH; }
449
450uint8_t st7565_max_lines(void) { return ST7565_DISPLAY_HEIGHT / ST7565_FONT_HEIGHT; }
451
452void st7565_task(void) {
453 if (!st7565_initialized) {
454 return;
455 }
456
457#if ST7565_UPDATE_INTERVAL > 0
458 if (timer_elapsed(st7565_update_timeout) >= ST7565_UPDATE_INTERVAL) {
459 st7565_update_timeout = timer_read();
460 st7565_set_cursor(0, 0);
461 st7565_task_user();
462 }
463#else
464 st7565_set_cursor(0, 0);
465 st7565_task_user();
466#endif
467
468 // Smart render system, no need to check for dirty
469 st7565_render();
470
471 // Display timeout check
472#if ST7565_TIMEOUT > 0
473 if (st7565_active && timer_expired32(timer_read32(), st7565_timeout)) {
474 st7565_off();
475 }
476#endif
477}
478
479__attribute__((weak)) void st7565_task_user(void) {}
480
481void st7565_reset(void) {
482 writePinLow(ST7565_RST_PIN);
483 wait_ms(20);
484 writePinHigh(ST7565_RST_PIN);
485 wait_ms(20);
486}
487
488spi_status_t st7565_send_cmd(uint8_t cmd) {
489 writePinLow(ST7565_A0_PIN);
490 return spi_write(cmd);
491}
492
493spi_status_t st7565_send_data(uint8_t *data, uint16_t length) {
494 writePinHigh(ST7565_A0_PIN);
495 return spi_transmit(data, length);
496}
diff --git a/drivers/lcd/st7565.h b/drivers/lcd/st7565.h
new file mode 100644
index 000000000..d453dbe6d
--- /dev/null
+++ b/drivers/lcd/st7565.h
@@ -0,0 +1,219 @@
1/*
2Copyright 2021
3
4This program is free software: you can redistribute it and/or modify
5it under the terms of the GNU General Public License as published by
6the Free Software Foundation, either version 2 of the License, or
7(at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License
15along with this program. If not, see <http://www.gnu.org/licenses/>.
16*/
17
18#pragma once
19
20#include <stdint.h>
21#include <stdbool.h>
22
23#include "spi_master.h"
24
25#ifndef ST7565_DISPLAY_WIDTH
26# define ST7565_DISPLAY_WIDTH 128
27#endif
28#ifndef ST7565_DISPLAY_HEIGHT
29# define ST7565_DISPLAY_HEIGHT 32
30#endif
31#ifndef ST7565_MATRIX_SIZE
32# define ST7565_MATRIX_SIZE (ST7565_DISPLAY_HEIGHT / 8 * ST7565_DISPLAY_WIDTH) // 1024 (compile time mathed)
33#endif
34#ifndef ST7565_BLOCK_TYPE
35# define ST7565_BLOCK_TYPE uint16_t
36#endif
37#ifndef ST7565_BLOCK_COUNT
38# define ST7565_BLOCK_COUNT (sizeof(ST7565_BLOCK_TYPE) * 8) // 32 (compile time mathed)
39#endif
40#ifndef ST7565_BLOCK_SIZE
41# define ST7565_BLOCK_SIZE (ST7565_MATRIX_SIZE / ST7565_BLOCK_COUNT) // 32 (compile time mathed)
42#endif
43
44// the column address corresponding to the first column in the display hardware
45#if !defined(ST7565_COLUMN_OFFSET)
46# define ST7565_COLUMN_OFFSET 0
47#endif
48
49// spi clock divisor
50#if !defined(ST7565_SPI_CLK_DIVISOR)
51# define ST7565_SPI_CLK_DIVISOR 4
52#endif
53
54// Custom font file to use
55#if !defined(ST7565_FONT_H)
56# define ST7565_FONT_H "glcdfont.c"
57#endif
58// unsigned char value of the first character in the font file
59#if !defined(ST7565_FONT_START)
60# define ST7565_FONT_START 0
61#endif
62// unsigned char value of the last character in the font file
63#if !defined(ST7565_FONT_END)
64# define ST7565_FONT_END 223
65#endif
66// Font render width
67#if !defined(ST7565_FONT_WIDTH)
68# define ST7565_FONT_WIDTH 6
69#endif
70// Font render height
71#if !defined(ST7565_FONT_HEIGHT)
72# define ST7565_FONT_HEIGHT 8
73#endif
74// Default contrast level
75#if !defined(ST7565_CONTRAST)
76# define ST7565_CONTRAST 32
77#endif
78
79#if !defined(ST7565_TIMEOUT)
80# if defined(ST7565_DISABLE_TIMEOUT)
81# define ST7565_TIMEOUT 0
82# else
83# define ST7565_TIMEOUT 60000
84# endif
85#endif
86
87#if !defined(ST7565_UPDATE_INTERVAL) && defined(SPLIT_KEYBOARD)
88# define ST7565_UPDATE_INTERVAL 50
89#endif
90
91typedef struct __attribute__((__packed__)) {
92 uint8_t *current_element;
93 uint16_t remaining_element_count;
94} display_buffer_reader_t;
95
96// Rotation enum values are flags
97typedef enum { DISPLAY_ROTATION_0, DISPLAY_ROTATION_180 } display_rotation_t;
98
99// Initialize the display, rotating the rendered output based on the define passed in.
100// Returns true if the display was initialized successfully
101bool st7565_init(display_rotation_t rotation);
102
103// Called at the start of st7565_init, weak function overridable by the user
104// rotation - the value passed into st7565_init
105// Return new display_rotation_t if you want to override default rotation
106display_rotation_t st7565_init_user(display_rotation_t rotation);
107
108// Clears the display buffer, resets cursor position to 0, and sets the buffer to dirty for rendering
109void st7565_clear(void);
110
111// Renders the dirty chunks of the buffer to display
112void st7565_render(void);
113
114// Moves cursor to character position indicated by column and line, wraps if out of bounds
115// Max column denoted by 'st7565_max_chars()' and max lines by 'st7565_max_lines()' functions
116void st7565_set_cursor(uint8_t col, uint8_t line);
117
118// Advances the cursor to the next page, writing ' ' if true
119// Wraps to the begining when out of bounds
120void st7565_advance_page(bool clearPageRemainder);
121
122// Moves the cursor forward 1 character length
123// Advance page if there is not enough room for the next character
124// Wraps to the begining when out of bounds
125void st7565_advance_char(void);
126
127// Writes a single character to the buffer at current cursor position
128// Advances the cursor while writing, inverts the pixels if true
129// Main handler that writes character data to the display buffer
130void st7565_write_char(const char data, bool invert);
131
132// Writes a string to the buffer at current cursor position
133// Advances the cursor while writing, inverts the pixels if true
134void st7565_write(const char *data, bool invert);
135
136// Writes a string to the buffer at current cursor position
137// Advances the cursor while writing, inverts the pixels if true
138// Advances the cursor to the next page, wiring ' ' to the remainder of the current page
139void st7565_write_ln(const char *data, bool invert);
140
141// Pans the buffer to the right (or left by passing true) by moving contents of the buffer
142// Useful for moving the screen in preparation for new drawing
143void st7565_pan(bool left);
144
145// Returns a pointer to the requested start index in the buffer plus remaining
146// buffer length as struct
147display_buffer_reader_t st7565_read_raw(uint16_t start_index);
148
149// Writes a string to the buffer at current cursor position
150void st7565_write_raw(const char *data, uint16_t size);
151
152// Writes a single byte into the buffer at the specified index
153void st7565_write_raw_byte(const char data, uint16_t index);
154
155// Sets a specific pixel on or off
156// Coordinates start at top-left and go right and down for positive x and y
157void st7565_write_pixel(uint8_t x, uint8_t y, bool on);
158
159#if defined(__AVR__)
160// Writes a PROGMEM string to the buffer at current cursor position
161// Advances the cursor while writing, inverts the pixels if true
162// Remapped to call 'void st7565_write(const char *data, bool invert);' on ARM
163void st7565_write_P(const char *data, bool invert);
164
165// Writes a PROGMEM string to the buffer at current cursor position
166// Advances the cursor while writing, inverts the pixels if true
167// Advances the cursor to the next page, wiring ' ' to the remainder of the current page
168// Remapped to call 'void st7565_write_ln(const char *data, bool invert);' on ARM
169void st7565_write_ln_P(const char *data, bool invert);
170
171// Writes a PROGMEM string to the buffer at current cursor position
172void st7565_write_raw_P(const char *data, uint16_t size);
173#else
174# define st7565_write_P(data, invert) st7565_write(data, invert)
175# define st7565_write_ln_P(data, invert) st7565_write_ln(data, invert)
176# define st7565_write_raw_P(data, size) st7565_write_raw(data, size)
177#endif // defined(__AVR__)
178
179// Can be used to manually turn on the screen if it is off
180// Returns true if the screen was on or turns on
181bool st7565_on(void);
182
183// Called when st7565_on() turns on the screen, weak function overridable by the user
184// Not called if the screen is already on
185void st7565_on_user(void);
186
187// Can be used to manually turn off the screen if it is on
188// Returns true if the screen was off or turns off
189bool st7565_off(void);
190
191// Called when st7565_off() turns off the screen, weak function overridable by the user
192// Not called if the screen is already off
193void st7565_off_user(void);
194
195// Returns true if the screen is currently on, false if it is
196// not
197bool st7565_is_on(void);
198
199// Basically it's st7565_render, but with timeout management and st7565_task_user calling!
200void st7565_task(void);
201
202// Called at the start of st7565_task, weak function overridable by the user
203void st7565_task_user(void);
204
205// Inverts the display
206// Returns true if the screen was or is inverted
207bool st7565_invert(bool invert);
208
209// Returns the maximum number of characters that will fit on a line
210uint8_t st7565_max_chars(void);
211
212// Returns the maximum number of lines that will fit on the display
213uint8_t st7565_max_lines(void);
214
215void st7565_reset(void);
216
217spi_status_t st7565_send_cmd(uint8_t cmd);
218
219spi_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 8e5ed5f07..7d4197890 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/>.
34#define DISPLAY_ALL_ON 0xA5 34#define DISPLAY_ALL_ON 0xA5
35#define DISPLAY_ALL_ON_RESUME 0xA4 35#define DISPLAY_ALL_ON_RESUME 0xA4
36#define NORMAL_DISPLAY 0xA6 36#define NORMAL_DISPLAY 0xA6
37#define INVERT_DISPLAY 0xA7
37#define DISPLAY_ON 0xAF 38#define DISPLAY_ON 0xAF
38#define DISPLAY_OFF 0xAE 39#define DISPLAY_OFF 0xAE
39#define NOP 0xE3 40#define NOP 0xE3
@@ -114,6 +115,7 @@ OLED_BLOCK_TYPE oled_dirty = 0;
114bool oled_initialized = false; 115bool oled_initialized = false;
115bool oled_active = false; 116bool oled_active = false;
116bool oled_scrolling = false; 117bool oled_scrolling = false;
118bool oled_inverted = false;
117uint8_t oled_brightness = OLED_BRIGHTNESS; 119uint8_t oled_brightness = OLED_BRIGHTNESS;
118oled_rotation_t oled_rotation = 0; 120oled_rotation_t oled_rotation = 0;
119uint8_t oled_rotation_width = 0; 121uint8_t oled_rotation_width = 0;
@@ -690,6 +692,30 @@ bool oled_scroll_off(void) {
690 return !oled_scrolling; 692 return !oled_scrolling;
691} 693}
692 694
695bool oled_invert(bool invert) {
696 if (!oled_initialized) {
697 return oled_inverted;
698 }
699
700 if (invert && !oled_inverted) {
701 static const uint8_t PROGMEM display_inverted[] = {I2C_CMD, INVERT_DISPLAY};
702 if (I2C_TRANSMIT_P(display_inverted) != I2C_STATUS_SUCCESS) {
703 print("oled_invert cmd failed\n");
704 return oled_inverted;
705 }
706 oled_inverted = true;
707 } else if (!invert && oled_inverted) {
708 static const uint8_t PROGMEM display_normal[] = {I2C_CMD, NORMAL_DISPLAY};
709 if (I2C_TRANSMIT_P(display_normal) != I2C_STATUS_SUCCESS) {
710 print("oled_invert cmd failed\n");
711 return oled_inverted;
712 }
713 oled_inverted = false;
714 }
715
716 return oled_inverted;
717}
718
693uint8_t oled_max_chars(void) { 719uint8_t oled_max_chars(void) {
694 if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_90)) { 720 if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_90)) {
695 return OLED_DISPLAY_WIDTH / OLED_FONT_WIDTH; 721 return OLED_DISPLAY_WIDTH / OLED_FONT_WIDTH;
diff --git a/drivers/oled/oled_driver.h b/drivers/oled/oled_driver.h
index a6b85f37e..fc68f0ec9 100644
--- a/drivers/oled/oled_driver.h
+++ b/drivers/oled/oled_driver.h
@@ -313,6 +313,10 @@ bool oled_scroll_left(void);
313// Returns true if the screen was not scrolling or stops scrolling 313// Returns true if the screen was not scrolling or stops scrolling
314bool oled_scroll_off(void); 314bool oled_scroll_off(void);
315 315
316// Inverts the display
317// Returns true if the screen was or is inverted
318bool oled_invert(bool invert);
319
316// Returns the maximum number of characters that will fit on a line 320// Returns the maximum number of characters that will fit on a line
317uint8_t oled_max_chars(void); 321uint8_t oled_max_chars(void);
318 322
diff --git a/drivers/sensors/adns5050.c b/drivers/sensors/adns5050.c
new file mode 100644
index 000000000..e7273977d
--- /dev/null
+++ b/drivers/sensors/adns5050.c
@@ -0,0 +1,193 @@
1/* Copyright 2021 Colin Lam (Ploopy Corporation)
2 * Copyright 2020 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com>
3 * Copyright 2019 Sunjun Kim
4 * Copyright 2019 Hiroyuki Okada
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20
21#include "adns5050.h"
22#include "wait.h"
23#include "debug.h"
24#include "print.h"
25#include "gpio.h"
26
27#ifndef OPTIC_ROTATED
28# define OPTIC_ROTATED false
29#endif
30
31// Definitions for the ADNS serial line.
32#ifndef ADNS_SCLK_PIN
33# define ADNS_SCLK_PIN B7
34#endif
35
36#ifndef ADNS_SDIO_PIN
37# define ADNS_SDIO_PIN C6
38#endif
39
40#ifndef ADNS_CS_PIN
41# define ADNS_CS_PIN B4
42#endif
43
44#ifdef CONSOLE_ENABLE
45void 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')); }
46#endif
47
48// Initialize the ADNS serial pins.
49void adns_init(void) {
50 setPinOutput(ADNS_SCLK_PIN);
51 setPinOutput(ADNS_SDIO_PIN);
52 setPinOutput(ADNS_CS_PIN);
53}
54
55// Perform a synchronization with the ADNS.
56// Just as with the serial protocol, this is used by the slave to send a
57// synchronization signal to the master.
58void adns_sync(void) {
59 writePinLow(ADNS_CS_PIN);
60 wait_us(1);
61 writePinHigh(ADNS_CS_PIN);
62}
63
64void adns_cs_select(void) {
65 writePinLow(ADNS_CS_PIN);
66}
67
68void adns_cs_deselect(void) {
69 writePinHigh(ADNS_CS_PIN);
70}
71
72uint8_t adns_serial_read(void) {
73 setPinInput(ADNS_SDIO_PIN);
74 uint8_t byte = 0;
75
76 for (uint8_t i = 0; i < 8; ++i) {
77 writePinLow(ADNS_SCLK_PIN);
78 wait_us(1);
79
80 byte = (byte << 1) | readPin(ADNS_SDIO_PIN);
81
82 writePinHigh(ADNS_SCLK_PIN);
83 wait_us(1);
84 }
85
86 return byte;
87}
88
89void adns_serial_write(uint8_t data) {
90 setPinOutput(ADNS_SDIO_PIN);
91
92 for (int8_t b = 7; b >= 0; b--) {
93 writePinLow(ADNS_SCLK_PIN);
94
95 if (data & (1 << b))
96 writePinHigh(ADNS_SDIO_PIN);
97 else
98 writePinLow(ADNS_SDIO_PIN);
99
100 wait_us(2);
101
102 writePinHigh(ADNS_SCLK_PIN);
103 }
104
105 // tSWR. See page 15 of the ADNS spec sheet.
106 // Technically, this is only necessary if the next operation is an SDIO
107 // read. This is not guaranteed to be the case, but we're being lazy.
108 wait_us(4);
109
110 // Note that tSWW is never necessary. All write operations require at
111 // least 32us, which exceeds tSWW, so there's never a need to wait for it.
112}
113
114// Read a byte of data from a register on the ADNS.
115// Don't forget to use the register map (as defined in the header file).
116uint8_t adns_read_reg(uint8_t reg_addr) {
117 adns_cs_select();
118
119 adns_serial_write(reg_addr);
120
121 // We don't need a minimum tSRAD here. That's because a 4ms wait time is
122 // already included in adns_serial_write(), so we're good.
123 // See page 10 and 15 of the ADNS spec sheet.
124 //wait_us(4);
125
126 uint8_t byte = adns_serial_read();
127
128 // tSRW & tSRR. See page 15 of the ADNS spec sheet.
129 // Technically, this is only necessary if the next operation is an SDIO
130 // read or write. This is not guaranteed to be the case.
131 // Honestly, this wait could probably be removed.
132 wait_us(1);
133
134 adns_cs_deselect();
135
136 return byte;
137}
138
139void adns_write_reg(uint8_t reg_addr, uint8_t data) {
140 adns_cs_select();
141 adns_serial_write( 0b10000000 | reg_addr );
142 adns_serial_write(data);
143 adns_cs_deselect();
144}
145
146report_adns_t adns_read_burst(void) {
147 adns_cs_select();
148
149 report_adns_t data;
150 data.dx = 0;
151 data.dy = 0;
152
153 adns_serial_write(REG_MOTION_BURST);
154
155 // We don't need a minimum tSRAD here. That's because a 4ms wait time is
156 // already included in adns_serial_write(), so we're good.
157 // See page 10 and 15 of the ADNS spec sheet.
158 //wait_us(4);
159
160 uint8_t x = adns_serial_read();
161 uint8_t y = adns_serial_read();
162
163 // Burst mode returns a bunch of other shit that we don't really need.
164 // Setting CS to high ends burst mode early.
165 adns_cs_deselect();
166
167 data.dx = convert_twoscomp(x);
168 data.dy = convert_twoscomp(y);
169
170 return data;
171}
172
173// Convert a two's complement byte from an unsigned data type into a signed
174// data type.
175int8_t convert_twoscomp(uint8_t data) {
176 if ((data & 0x80) == 0x80)
177 return -128 + (data & 0x7F);
178 else
179 return data;
180}
181
182// Don't forget to use the definitions for CPI in the header file.
183void adns_set_cpi(uint8_t cpi) {
184 adns_write_reg(REG_MOUSE_CONTROL2, cpi);
185}
186
187bool adns_check_signature(void) {
188 uint8_t pid = adns_read_reg(REG_PRODUCT_ID);
189 uint8_t rid = adns_read_reg(REG_REVISION_ID);
190 uint8_t pid2 = adns_read_reg(REG_PRODUCT_ID2);
191
192 return (pid == 0x12 && rid == 0x01 && pid2 == 0x26);
193}
diff --git a/drivers/sensors/adns5050.h b/drivers/sensors/adns5050.h
new file mode 100644
index 000000000..ff8e8f78e
--- /dev/null
+++ b/drivers/sensors/adns5050.h
@@ -0,0 +1,79 @@
1/* Copyright 2021 Colin Lam (Ploopy Corporation)
2 * Copyright 2020 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com>
3 * Copyright 2019 Sunjun Kim
4 * Copyright 2019 Hiroyuki Okada
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#pragma once
21
22#include <stdbool.h>
23
24// Registers
25#define REG_PRODUCT_ID 0x00
26#define REG_REVISION_ID 0x01
27#define REG_MOTION 0x02
28#define REG_DELTA_X 0x03
29#define REG_DELTA_Y 0x04
30#define REG_SQUAL 0x05
31#define REG_SHUTTER_UPPER 0x06
32#define REG_SHUTTER_LOWER 0x07
33#define REG_MAXIMUM_PIXEL 0x08
34#define REG_PIXEL_SUM 0x09
35#define REG_MINIMUM_PIXEL 0x0a
36#define REG_PIXEL_GRAB 0x0b
37#define REG_MOUSE_CONTROL 0x0d
38#define REG_MOUSE_CONTROL2 0x19
39#define REG_LED_DC_MODE 0x22
40#define REG_CHIP_RESET 0x3a
41#define REG_PRODUCT_ID2 0x3e
42#define REG_INV_REV_ID 0x3f
43#define REG_MOTION_BURST 0x63
44
45// CPI values
46#define CPI125 0x11
47#define CPI250 0x12
48#define CPI375 0x13
49#define CPI500 0x14
50#define CPI625 0x15
51#define CPI750 0x16
52#define CPI875 0x17
53#define CPI1000 0x18
54#define CPI1125 0x19
55#define CPI1250 0x1a
56#define CPI1375 0x1b
57
58#ifdef CONSOLE_ENABLE
59void print_byte(uint8_t byte);
60#endif
61
62typedef struct {
63 int8_t dx;
64 int8_t dy;
65} report_adns_t;
66
67// A bunch of functions to implement the ADNS5050-specific serial protocol.
68// Note that the "serial.h" driver is insufficient, because it does not
69// manually manipulate a serial clock signal.
70void adns_init(void);
71void adns_sync(void);
72uint8_t adns_serial_read(void);
73void adns_serial_write(uint8_t data);
74uint8_t adns_read_reg(uint8_t reg_addr);
75void adns_write_reg(uint8_t reg_addr, uint8_t data);
76report_adns_t adns_read_burst(void);
77int8_t convert_twoscomp(uint8_t data);
78void adns_set_cpi(uint8_t cpi);
79bool adns_check_signature(void);
diff --git a/drivers/sensors/adns9800.c b/drivers/sensors/adns9800.c
new file mode 100644
index 000000000..36213179f
--- /dev/null
+++ b/drivers/sensors/adns9800.c
@@ -0,0 +1,219 @@
1/* Copyright 2020 Alexander Tulloh
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "spi_master.h"
18#include "quantum.h"
19#include "adns9800_srom_A6.h"
20#include "adns9800.h"
21
22// registers
23#define REG_Product_ID 0x00
24#define REG_Revision_ID 0x01
25#define REG_Motion 0x02
26#define REG_Delta_X_L 0x03
27#define REG_Delta_X_H 0x04
28#define REG_Delta_Y_L 0x05
29#define REG_Delta_Y_H 0x06
30#define REG_SQUAL 0x07
31#define REG_Pixel_Sum 0x08
32#define REG_Maximum_Pixel 0x09
33#define REG_Minimum_Pixel 0x0a
34#define REG_Shutter_Lower 0x0b
35#define REG_Shutter_Upper 0x0c
36#define REG_Frame_Period_Lower 0x0d
37#define REG_Frame_Period_Upper 0x0e
38#define REG_Configuration_I 0x0f
39#define REG_Configuration_II 0x10
40#define REG_Frame_Capture 0x12
41#define REG_SROM_Enable 0x13
42#define REG_Run_Downshift 0x14
43#define REG_Rest1_Rate 0x15
44#define REG_Rest1_Downshift 0x16
45#define REG_Rest2_Rate 0x17
46#define REG_Rest2_Downshift 0x18
47#define REG_Rest3_Rate 0x19
48#define REG_Frame_Period_Max_Bound_Lower 0x1a
49#define REG_Frame_Period_Max_Bound_Upper 0x1b
50#define REG_Frame_Period_Min_Bound_Lower 0x1c
51#define REG_Frame_Period_Min_Bound_Upper 0x1d
52#define REG_Shutter_Max_Bound_Lower 0x1e
53#define REG_Shutter_Max_Bound_Upper 0x1f
54#define REG_LASER_CTRL0 0x20
55#define REG_Observation 0x24
56#define REG_Data_Out_Lower 0x25
57#define REG_Data_Out_Upper 0x26
58#define REG_SROM_ID 0x2a
59#define REG_Lift_Detection_Thr 0x2e
60#define REG_Configuration_V 0x2f
61#define REG_Configuration_IV 0x39
62#define REG_Power_Up_Reset 0x3a
63#define REG_Shutdown 0x3b
64#define REG_Inverse_Product_ID 0x3f
65#define REG_Motion_Burst 0x50
66#define REG_SROM_Load_Burst 0x62
67#define REG_Pixel_Burst 0x64
68
69#define ADNS_CLOCK_SPEED 2000000
70#define MIN_CPI 200
71#define MAX_CPI 8200
72#define CPI_STEP 200
73#define CLAMP_CPI(value) value < MIN_CPI ? MIN_CPI : value > MAX_CPI ? MAX_CPI : value
74#define SPI_MODE 3
75#define SPI_DIVISOR (F_CPU / ADNS_CLOCK_SPEED)
76#define US_BETWEEN_WRITES 120
77#define US_BETWEEN_READS 20
78#define US_BEFORE_MOTION 100
79#define MSB1 0x80
80
81extern const uint16_t adns_firmware_length;
82extern const uint8_t adns_firmware_data[];
83
84void adns_spi_start(void){
85 spi_start(SPI_SS_PIN, false, SPI_MODE, SPI_DIVISOR);
86}
87
88void adns_write(uint8_t reg_addr, uint8_t data){
89
90 adns_spi_start();
91 spi_write(reg_addr | MSB1);
92 spi_write(data);
93 spi_stop();
94 wait_us(US_BETWEEN_WRITES);
95}
96
97uint8_t adns_read(uint8_t reg_addr){
98
99 adns_spi_start();
100 spi_write(reg_addr & 0x7f );
101 uint8_t data = spi_read();
102 spi_stop();
103 wait_us(US_BETWEEN_READS);
104
105 return data;
106}
107
108void adns_init() {
109
110 setPinOutput(SPI_SS_PIN);
111
112 spi_init();
113
114 // reboot
115 adns_write(REG_Power_Up_Reset, 0x5a);
116 wait_ms(50);
117
118 // read registers and discard
119 adns_read(REG_Motion);
120 adns_read(REG_Delta_X_L);
121 adns_read(REG_Delta_X_H);
122 adns_read(REG_Delta_Y_L);
123 adns_read(REG_Delta_Y_H);
124
125 // upload firmware
126
127 // 3k firmware mode
128 adns_write(REG_Configuration_IV, 0x02);
129
130 // enable initialisation
131 adns_write(REG_SROM_Enable, 0x1d);
132
133 // wait a frame
134 wait_ms(10);
135
136 // start SROM download
137 adns_write(REG_SROM_Enable, 0x18);
138
139 // write the SROM file
140
141 adns_spi_start();
142
143 spi_write(REG_SROM_Load_Burst | 0x80);
144 wait_us(15);
145
146 // send all bytes of the firmware
147 unsigned char c;
148 for(int i = 0; i < adns_firmware_length; i++){
149 c = (unsigned char)pgm_read_byte(adns_firmware_data + i);
150 spi_write(c);
151 wait_us(15);
152 }
153
154 spi_stop();
155
156 wait_ms(10);
157
158 // enable laser
159 uint8_t laser_ctrl0 = adns_read(REG_LASER_CTRL0);
160 adns_write(REG_LASER_CTRL0, laser_ctrl0 & 0xf0);
161}
162
163config_adns_t adns_get_config(void) {
164 uint8_t config_1 = adns_read(REG_Configuration_I);
165 return (config_adns_t){ (config_1 & 0xFF) * CPI_STEP };
166}
167
168void adns_set_config(config_adns_t config) {
169 uint8_t config_1 = (CLAMP_CPI(config.cpi) / CPI_STEP) & 0xFF;
170 adns_write(REG_Configuration_I, config_1);
171}
172
173static int16_t convertDeltaToInt(uint8_t high, uint8_t low){
174
175 // join bytes into twos compliment
176 uint16_t twos_comp = (high << 8) | low;
177
178 // convert twos comp to int
179 if (twos_comp & 0x8000)
180 return -1 * (~twos_comp + 1);
181
182 return twos_comp;
183}
184
185report_adns_t adns_get_report(void) {
186
187 report_adns_t report = {0, 0};
188
189 adns_spi_start();
190
191 // start burst mode
192 spi_write(REG_Motion_Burst & 0x7f);
193
194 wait_us(US_BEFORE_MOTION);
195
196 uint8_t motion = spi_read();
197
198 if(motion & 0x80) {
199
200 // clear observation register
201 spi_read();
202
203 // delta registers
204 uint8_t delta_x_l = spi_read();
205 uint8_t delta_x_h = spi_read();
206 uint8_t delta_y_l = spi_read();
207 uint8_t delta_y_h = spi_read();
208
209 report.x = convertDeltaToInt(delta_x_h, delta_x_l);
210 report.y = convertDeltaToInt(delta_y_h, delta_y_l);
211 }
212
213 // clear residual motion
214 spi_write(REG_Motion & 0x7f);
215
216 spi_stop();
217
218 return report;
219}
diff --git a/drivers/sensors/adns9800.h b/drivers/sensors/adns9800.h
new file mode 100644
index 000000000..2f50b8f1b
--- /dev/null
+++ b/drivers/sensors/adns9800.h
@@ -0,0 +1,35 @@
1/* Copyright 2020 Alexander Tulloh
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#pragma once
18
19#include <stdint.h>
20
21typedef struct {
22 /* 200 - 8200 CPI supported */
23 uint16_t cpi;
24} config_adns_t;
25
26typedef struct {
27 int16_t x;
28 int16_t y;
29} report_adns_t;
30
31void adns_init(void);
32config_adns_t adns_get_config(void);
33void adns_set_config(config_adns_t);
34/* Reads and clears the current delta values on the ADNS sensor */
35report_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 000000000..f5b3abeb6
--- /dev/null
+++ b/drivers/sensors/adns9800_srom_A6.h
@@ -0,0 +1,3078 @@
1#pragma once
2
3#include "progmem.h"
4
5const uint16_t adns_firmware_length = 3070;
6
7const uint8_t adns_firmware_data[] PROGMEM = {
80x03,
90xa6,
100x68,
110x1e,
120x7d,
130x10,
140x7e,
150x7e,
160x5f,
170x1c,
180xb8,
190xf2,
200x47,
210x0c,
220x7b,
230x74,
240x4b,
250x14,
260x8b,
270x75,
280x66,
290x51,
300x0b,
310x8c,
320x76,
330x74,
340x4b,
350x14,
360xaa,
370xd6,
380x0f,
390x9c,
400xba,
410xf6,
420x6e,
430x3f,
440xdd,
450x38,
460xd5,
470x02,
480x80,
490x9b,
500x82,
510x6d,
520x58,
530x13,
540xa4,
550xab,
560xb5,
570xc9,
580x10,
590xa2,
600xc6,
610x0a,
620x7f,
630x5d,
640x19,
650x91,
660xa0,
670xa3,
680xce,
690xeb,
700x3e,
710xc9,
720xf1,
730x60,
740x42,
750xe7,
760x4c,
770xfb,
780x74,
790x6a,
800x56,
810x2e,
820xbf,
830xdd,
840x38,
850xd3,
860x05,
870x88,
880x92,
890xa6,
900xce,
910xff,
920x5d,
930x38,
940xd1,
950xcf,
960xef,
970x58,
980xcb,
990x65,
1000x48,
1010xf0,
1020x35,
1030x85,
1040xa9,
1050xb2,
1060x8f,
1070x5e,
1080xf3,
1090x80,
1100x94,
1110x97,
1120x7e,
1130x75,
1140x97,
1150x87,
1160x73,
1170x13,
1180xb0,
1190x8a,
1200x69,
1210xd4,
1220x0a,
1230xde,
1240xc1,
1250x79,
1260x59,
1270x36,
1280xdb,
1290x9d,
1300xd6,
1310xb8,
1320x15,
1330x6f,
1340xce,
1350x3c,
1360x72,
1370x32,
1380x45,
1390x88,
1400xdf,
1410x6c,
1420xa5,
1430x6d,
1440xe8,
1450x76,
1460x96,
1470x14,
1480x74,
1490x20,
1500xdc,
1510xf4,
1520xfa,
1530x37,
1540x6a,
1550x27,
1560x32,
1570xe3,
1580x29,
1590xbf,
1600xc4,
1610xc7,
1620x06,
1630x9d,
1640x58,
1650xe7,
1660x87,
1670x7c,
1680x2e,
1690x9f,
1700x6e,
1710x49,
1720x07,
1730x5d,
1740x23,
1750x64,
1760x54,
1770x83,
1780x6e,
1790xcb,
1800xb7,
1810x77,
1820xf7,
1830x2b,
1840x6e,
1850x0f,
1860x2e,
1870x66,
1880x12,
1890x60,
1900x55,
1910x65,
1920xfc,
1930x43,
1940xb3,
1950x58,
1960x73,
1970x5b,
1980xe8,
1990x67,
2000x04,
2010x43,
2020x02,
2030xde,
2040xb3,
2050x89,
2060xa0,
2070x6d,
2080x3a,
2090x27,
2100x79,
2110x64,
2120x5b,
2130x0c,
2140x16,
2150x9e,
2160x66,
2170xb1,
2180x8b,
2190x87,
2200x0c,
2210x5d,
2220xf2,
2230xb6,
2240x3d,
2250x71,
2260xdf,
2270x42,
2280x03,
2290x8a,
2300x06,
2310x8d,
2320xef,
2330x1d,
2340xa8,
2350x96,
2360x5c,
2370xed,
2380x31,
2390x61,
2400x5c,
2410xa1,
2420x34,
2430xf6,
2440x8c,
2450x08,
2460x60,
2470x33,
2480x07,
2490x00,
2500x3e,
2510x79,
2520x95,
2530x1b,
2540x43,
2550x7f,
2560xfe,
2570xb6,
2580xa6,
2590xd4,
2600x9d,
2610x76,
2620x72,
2630xbf,
2640xad,
2650xc0,
2660x15,
2670xe8,
2680x37,
2690x31,
2700xa3,
2710x72,
2720x63,
2730x52,
2740x1d,
2750x1c,
2760x5d,
2770x51,
2780x1b,
2790xe1,
2800xa9,
2810xed,
2820x60,
2830x32,
2840x3e,
2850xa9,
2860x50,
2870x28,
2880x53,
2890x06,
2900x59,
2910xe2,
2920xfc,
2930xe7,
2940x02,
2950x64,
2960x39,
2970x21,
2980x56,
2990x4a,
3000xa5,
3010x40,
3020x80,
3030x81,
3040xd5,
3050x5a,
3060x60,
3070x7b,
3080x68,
3090x84,
3100xf1,
3110xe0,
3120xb1,
3130xb6,
3140x5b,
3150xdf,
3160xa8,
3170x1d,
3180x6d,
3190x65,
3200x20,
3210xc0,
3220xa2,
3230xb9,
3240xd9,
3250xbb,
3260x00,
3270xa6,
3280xdb,
3290x8b,
3300x01,
3310x53,
3320x91,
3330xfe,
3340xc4,
3350x51,
3360x85,
3370xb0,
3380x96,
3390x7f,
3400xfd,
3410x51,
3420xdd,
3430x14,
3440x03,
3450x67,
3460x2e,
3470x75,
3480x1c,
3490x76,
3500xd3,
3510x6e,
3520xdd,
3530x99,
3540x55,
3550x76,
3560xe5,
3570xab,
3580x23,
3590xfc,
3600x4a,
3610xd5,
3620xc6,
3630xe8,
3640x2e,
3650xca,
3660x8a,
3670xb3,
3680xf6,
3690x8c,
3700x6c,
3710xb0,
3720xe9,
3730xf2,
3740xe7,
3750x9e,
3760x69,
3770x41,
3780xed,
3790xf1,
3800x6d,
3810xd2,
3820x86,
3830xd8,
3840x7e,
3850xcb,
3860x5d,
3870x47,
3880x6c,
3890x85,
3900x6a,
3910x23,
3920xed,
3930x20,
3940x40,
3950x93,
3960xb4,
3970x20,
3980xc7,
3990xa5,
4000xc9,
4010xaf,
4020x03,
4030x15,
4040xac,
4050x19,
4060xe5,
4070x2a,
4080x36,
4090xdf,
4100x6d,
4110xc5,
4120x8c,
4130x80,
4140x07,
4150xce,
4160x92,
4170x0c,
4180xd8,
4190x06,
4200x62,
4210x0f,
4220xdd,
4230x48,
4240x46,
4250x1a,
4260x53,
4270xc7,
4280x8a,
4290x8c,
4300x5d,
4310x5d,
4320xb4,
4330xa1,
4340x02,
4350xd3,
4360xa9,
4370xb8,
4380xf3,
4390x94,
4400x8f,
4410x3f,
4420xe5,
4430x54,
4440xd4,
4450x11,
4460x65,
4470xb2,
4480x5e,
4490x09,
4500x0b,
4510x81,
4520xe3,
4530x75,
4540xa7,
4550x89,
4560x81,
4570x39,
4580x6c,
4590x46,
4600xf6,
4610x06,
4620x9f,
4630x27,
4640x3b,
4650xb6,
4660x2d,
4670x5f,
4680x1d,
4690x4b,
4700xd4,
4710x7b,
4720x1d,
4730x61,
4740x74,
4750x89,
4760xe4,
4770xe3,
4780xbd,
4790x98,
4800x1b,
4810xc4,
4820x51,
4830x3b,
4840xa4,
4850xfa,
4860xe0,
4870x92,
4880xf7,
4890xbe,
4900xf2,
4910x4d,
4920xbb,
4930xff,
4940xad,
4950x4f,
4960x6d,
4970x68,
4980xc2,
4990x79,
5000x40,
5010xaa,
5020x9b,
5030x8f,
5040x0c,
5050x32,
5060x4b,
5070x5f,
5080x3e,
5090xab,
5100x59,
5110x98,
5120xb3,
5130xf5,
5140x1d,
5150xac,
5160x5e,
5170xbc,
5180x78,
5190xd3,
5200x01,
5210x6c,
5220x64,
5230x15,
5240x2f,
5250xd8,
5260x71,
5270xa6,
5280x2d,
5290x45,
5300xe1,
5310x22,
5320x42,
5330xe4,
5340x4e,
5350x04,
5360x3c,
5370x7d,
5380xf4,
5390x40,
5400x21,
5410xb4,
5420x67,
5430x05,
5440xa8,
5450xe2,
5460xf3,
5470x72,
5480x87,
5490x4c,
5500x7d,
5510xd9,
5520x1b,
5530x65,
5540x97,
5550xf3,
5560xc2,
5570xe3,
5580xe4,
5590xc8,
5600xd2,
5610xde,
5620xf6,
5630xef,
5640xdc,
5650xbb,
5660x44,
5670x08,
5680x5e,
5690xe2,
5700x45,
5710x27,
5720x01,
5730xb0,
5740xf6,
5750x43,
5760xe7,
5770x3a,
5780xf6,
5790xdc,
5800x9d,
5810xed,
5820xf3,
5830xc5,
5840x0c,
5850xb8,
5860x9c,
5870x98,
5880x3a,
5890xd8,
5900x36,
5910xee,
5920x96,
5930x72,
5940x67,
5950xe7,
5960x81,
5970x91,
5980xd5,
5990x05,
6000x0a,
6010xe0,
6020x82,
6030xd5,
6040x8f,
6050xe8,
6060xf9,
6070xb0,
6080xc9,
6090xcf,
6100x93,
6110xe7,
6120x04,
6130xc5,
6140xbc,
6150x2b,
6160x43,
6170x56,
6180x7e,
6190xe8,
6200x67,
6210x7c,
6220xe5,
6230xfb,
6240x49,
6250xad,
6260x5e,
6270x9f,
6280x25,
6290x13,
6300xde,
6310x6e,
6320x6e,
6330xe9,
6340xf1,
6350xec,
6360x87,
6370x0b,
6380x59,
6390x81,
6400x76,
6410x84,
6420x76,
6430xb3,
6440x24,
6450xaf,
6460x30,
6470xfd,
6480x27,
6490x8b,
6500xab,
6510xd8,
6520x00,
6530x8b,
6540x9b,
6550x0c,
6560xd2,
6570xb2,
6580x4e,
6590x5e,
6600x9d,
6610x1d,
6620x96,
6630x01,
6640x00,
6650x67,
6660xc1,
6670x5f,
6680x02,
6690x20,
6700xfd,
6710x45,
6720x6a,
6730x01,
6740x60,
6750x58,
6760x45,
6770xca,
6780x47,
6790x21,
6800x90,
6810x5a,
6820xc4,
6830x43,
6840x26,
6850x1a,
6860xd7,
6870xa5,
6880x4a,
6890xb2,
6900x5d,
6910x2b,
6920x35,
6930x49,
6940xfb,
6950xa5,
6960x17,
6970x92,
6980x21,
6990x1e,
7000x93,
7010x96,
7020x67,
7030xa2,
7040x7e,
7050x36,
7060x7a,
7070xde,
7080x5f,
7090xbe,
7100x7a,
7110x58,
7120x9d,
7130xf8,
7140x78,
7150xa3,
7160xfa,
7170xc8,
7180xd5,
7190x17,
7200xf0,
7210x21,
7220x97,
7230x8c,
7240x80,
7250xb5,
7260x4b,
7270x3b,
7280xbd,
7290xbb,
7300x41,
7310x21,
7320xa8,
7330x50,
7340x67,
7350xf7,
7360xe7,
7370x19,
7380x80,
7390x10,
7400x8e,
7410xce,
7420x04,
7430x18,
7440x3f,
7450x51,
7460x6b,
7470x77,
7480xd8,
7490x9e,
7500x16,
7510xaf,
7520xec,
7530xef,
7540x48,
7550x16,
7560x4d,
7570x9e,
7580x85,
7590x38,
7600x18,
7610x3e,
7620xd4,
7630x28,
7640x87,
7650x60,
7660x2a,
7670xf6,
7680x7f,
7690x09,
7700x86,
7710x6f,
7720x9c,
7730x3c,
7740x3a,
7750xff,
7760xab,
7770xd0,
7780x61,
7790xa2,
7800x97,
7810x0d,
7820x71,
7830x94,
7840x7e,
7850xfd,
7860xb9,
7870x80,
7880x02,
7890x89,
7900x6a,
7910xb3,
7920x84,
7930x6c,
7940x2a,
7950x77,
7960x62,
7970xbe,
7980x0b,
7990xf4,
8000xaf,
8010xac,
8020x7b,
8030x7c,
8040x8e,
8050xca,
8060x01,
8070xba,
8080x71,
8090x78,
8100x94,
8110xfd,
8120xb5,
8130x39,
8140xa4,
8150x4d,
8160x2f,
8170x78,
8180xcf,
8190xca,
8200x92,
8210x0c,
8220x1a,
8230x99,
8240x48,
8250x4c,
8260x11,
8270x96,
8280xb5,
8290x4e,
8300x41,
8310x28,
8320xe4,
8330xa6,
8340xfe,
8350x4b,
8360x72,
8370x91,
8380xe7,
8390xd4,
8400xdd,
8410x9f,
8420x12,
8430xe6,
8440x29,
8450x38,
8460xce,
8470x45,
8480xae,
8490x02,
8500xb8,
8510x24,
8520xae,
8530xbd,
8540xe9,
8550x66,
8560x08,
8570x62,
8580xa2,
8590x2c,
8600x2b,
8610x00,
8620xe2,
8630x23,
8640xd9,
8650xc4,
8660x48,
8670xe4,
8680xd3,
8690xac,
8700xbb,
8710x34,
8720xc7,
8730xf0,
8740xe3,
8750x4f,
8760xb9,
8770x30,
8780xea,
8790xa2,
8800x12,
8810xf1,
8820x30,
8830x2c,
8840x36,
8850xde,
8860x48,
8870xf2,
8880xb0,
8890x4c,
8900x43,
8910x3f,
8920x2e,
8930x58,
8940xe4,
8950x20,
8960xe3,
8970x58,
8980xcd,
8990x31,
9000x22,
9010xf0,
9020xa2,
9030x2a,
9040xe6,
9050x19,
9060x90,
9070x55,
9080x86,
9090xf6,
9100x55,
9110x79,
9120xd1,
9130xd7,
9140x46,
9150x2f,
9160xc0,
9170xdc,
9180x99,
9190xe8,
9200xf3,
9210x6a,
9220xdf,
9230x7f,
9240xeb,
9250x24,
9260x4a,
9270x1e,
9280x5a,
9290x75,
9300xde,
9310x2f,
9320x5c,
9330x19,
9340x61,
9350x03,
9360x53,
9370x54,
9380x6a,
9390x3b,
9400x18,
9410x70,
9420xb6,
9430x4f,
9440xf1,
9450x9c,
9460x0a,
9470x59,
9480x9d,
9490x19,
9500x92,
9510x65,
9520x8c,
9530x83,
9540x14,
9550x2d,
9560x44,
9570x8a,
9580x75,
9590xa9,
9600xf5,
9610x90,
9620xd2,
9630x66,
9640x4e,
9650xfa,
9660x69,
9670x0f,
9680x5b,
9690x0b,
9700x98,
9710x65,
9720xc8,
9730x11,
9740x42,
9750x59,
9760x7f,
9770xdd,
9780x1b,
9790x75,
9800x17,
9810x31,
9820x4c,
9830x75,
9840x58,
9850xeb,
9860x58,
9870x63,
9880x7d,
9890xf2,
9900xa6,
9910xc2,
9920x6e,
9930xb7,
9940x3f,
9950x3e,
9960x5e,
9970x47,
9980xad,
9990xb7,
10000x04,
10010xe8,
10020x05,
10030xf8,
10040xb2,
10050xcf,
10060x19,
10070xf3,
10080xd2,
10090x85,
10100xfe,
10110x3e,
10120x3e,
10130xb1,
10140x62,
10150x08,
10160x2c,
10170x10,
10180x07,
10190x0d,
10200x73,
10210x90,
10220x17,
10230xfa,
10240x9b,
10250x56,
10260x02,
10270x75,
10280xf9,
10290x51,
10300xe0,
10310xe9,
10320x1a,
10330x7b,
10340x9f,
10350xb3,
10360xf3,
10370x98,
10380xb8,
10390x1c,
10400x9c,
10410xe1,
10420xd5,
10430x35,
10440xae,
10450xc8,
10460x60,
10470x48,
10480x11,
10490x09,
10500x94,
10510x6b,
10520xd0,
10530x8b,
10540x15,
10550xbc,
10560x05,
10570x68,
10580xd3,
10590x54,
10600x8a,
10610x51,
10620x39,
10630x5c,
10640x42,
10650x76,
10660xce,
10670xd8,
10680xad,
10690x89,
10700x30,
10710xc9,
10720x05,
10730x1c,
10740xcc,
10750x94,
10760x3f,
10770x0f,
10780x90,
10790x6f,
10800x72,
10810x2d,
10820x85,
10830x64,
10840x9a,
10850xb9,
10860x23,
10870xf9,
10880x0b,
10890xc3,
10900x7c,
10910x39,
10920x0f,
10930x97,
10940x07,
10950x97,
10960xda,
10970x58,
10980x48,
10990x33,
11000x05,
11010x23,
11020xb8,
11030x82,
11040xe8,
11050xd3,
11060x53,
11070x89,
11080xaf,
11090x33,
11100x80,
11110x22,
11120x84,
11130x0c,
11140x95,
11150x5c,
11160x67,
11170xb8,
11180x77,
11190x0c,
11200x5c,
11210xa2,
11220x5f,
11230x3d,
11240x58,
11250x0f,
11260x27,
11270xf3,
11280x2f,
11290xae,
11300x48,
11310xbd,
11320x0b,
11330x6f,
11340x54,
11350xfb,
11360x67,
11370x4c,
11380xea,
11390x32,
11400x27,
11410xf1,
11420xfa,
11430xe2,
11440xb0,
11450xec,
11460x0b,
11470x15,
11480xb4,
11490x70,
11500xf6,
11510x5c,
11520xdd,
11530x71,
11540x60,
11550xc3,
11560xc1,
11570xa8,
11580x32,
11590x65,
11600xac,
11610x7a,
11620x77,
11630x41,
11640xe5,
11650xa9,
11660x6b,
11670x11,
11680x81,
11690xfa,
11700x34,
11710x8d,
11720xfb,
11730xc1,
11740x80,
11750x6e,
11760xc4,
11770x60,
11780x30,
11790x07,
11800xd4,
11810x8b,
11820x67,
11830xbd,
11840xaa,
11850x8c,
11860x9c,
11870x64,
11880xac,
11890xdb,
11900x0b,
11910x24,
11920x8b,
11930x63,
11940x6f,
11950xe6,
11960xbc,
11970xe7,
11980x33,
11990xa4,
12000x4a,
12010x4c,
12020xa7,
12030x9f,
12040x43,
12050x53,
12060xd2,
12070xbb,
12080x8f,
12090x43,
12100xc7,
12110x3d,
12120x78,
12130x68,
12140x3f,
12150xa5,
12160x3d,
12170xca,
12180x69,
12190x84,
12200xa6,
12210x97,
12220x2d,
12230xc0,
12240x7d,
12250x31,
12260x34,
12270x55,
12280x1d,
12290x07,
12300xb1,
12310x5f,
12320x40,
12330x5c,
12340x93,
12350xb0,
12360xbc,
12370x7c,
12380xb0,
12390xbc,
12400xe7,
12410x12,
12420xee,
12430x6b,
12440x2b,
12450xd3,
12460x4d,
12470x67,
12480x70,
12490x3a,
12500x9a,
12510xf2,
12520x3c,
12530x7c,
12540x81,
12550xfa,
12560xd7,
12570xd9,
12580x90,
12590x91,
12600x81,
12610xb8,
12620xb1,
12630xf3,
12640x48,
12650x6a,
12660x26,
12670x4f,
12680x0c,
12690xce,
12700xb0,
12710x9e,
12720xfd,
12730x4a,
12740x3a,
12750xaf,
12760xac,
12770x5b,
12780x3f,
12790xbf,
12800x44,
12810x5a,
12820xa3,
12830x19,
12840x1e,
12850x4b,
12860xe7,
12870x36,
12880x6a,
12890xd7,
12900x20,
12910xae,
12920xd7,
12930x7d,
12940x3b,
12950xe7,
12960xff,
12970x3a,
12980x86,
12990x2e,
13000xd0,
13010x4a,
13020x3e,
13030xaf,
13040x9f,
13050x8e,
13060x01,
13070xbf,
13080xf8,
13090x4f,
13100xc1,
13110xe8,
13120x6f,
13130x74,
13140xe1,
13150x45,
13160xd3,
13170xf7,
13180x04,
13190x6a,
13200x4b,
13210x9d,
13220xec,
13230x33,
13240x27,
13250x76,
13260xd7,
13270xc5,
13280xe1,
13290xb0,
13300x3b,
13310x0e,
13320x23,
13330xec,
13340xf0,
13350x86,
13360xd2,
13370x1a,
13380xbf,
13390x3d,
13400x04,
13410x62,
13420xb3,
13430x6c,
13440xb2,
13450xeb,
13460x17,
13470x05,
13480xa6,
13490x0a,
13500x8a,
13510x7e,
13520x83,
13530x1c,
13540xb6,
13550x37,
13560x09,
13570xc6,
13580x0b,
13590x70,
13600x3c,
13610xb5,
13620x93,
13630x81,
13640xd8,
13650x93,
13660xa0,
13670x5f,
13680x1e,
13690x08,
13700xe2,
13710xc6,
13720xe5,
13730xc9,
13740x72,
13750xf1,
13760xf1,
13770xc1,
13780xed,
13790xd5,
13800x58,
13810x93,
13820x83,
13830xf8,
13840x65,
13850x67,
13860x2e,
13870x0d,
13880xa9,
13890xf1,
13900x64,
13910x12,
13920xe6,
13930x4c,
13940xea,
13950x15,
13960x3f,
13970x8c,
13980x1a,
13990xb6,
14000xbf,
14010xf6,
14020xb9,
14030x52,
14040x35,
14050x09,
14060xb0,
14070xe6,
14080xf7,
14090xcd,
14100xf1,
14110xa5,
14120xaa,
14130x81,
14140xd1,
14150x81,
14160x6f,
14170xb4,
14180xa9,
14190x66,
14200x1f,
14210xfc,
14220x48,
14230xc0,
14240xb6,
14250xd1,
14260x8b,
14270x06,
14280x2f,
14290xf6,
14300xef,
14310x1f,
14320x0a,
14330xe6,
14340xce,
14350x3a,
14360x4a,
14370x55,
14380xbf,
14390x6d,
14400xf9,
14410x4d,
14420xd4,
14430x08,
14440x45,
14450x4b,
14460xc3,
14470x66,
14480x19,
14490x92,
14500x10,
14510xe1,
14520x17,
14530x8e,
14540x28,
14550x91,
14560x16,
14570xbf,
14580x3c,
14590xee,
14600xa3,
14610xa6,
14620x99,
14630x92,
14640x10,
14650xe1,
14660xf6,
14670xcc,
14680xac,
14690xb8,
14700x65,
14710x0b,
14720x43,
14730x66,
14740xf8,
14750xe3,
14760xe5,
14770x3f,
14780x24,
14790x89,
14800x47,
14810x5d,
14820x78,
14830x43,
14840xd0,
14850x61,
14860x17,
14870xbd,
14880x5b,
14890x64,
14900x54,
14910x08,
14920x45,
14930x59,
14940x93,
14950xf6,
14960x95,
14970x8a,
14980x41,
14990x51,
15000x62,
15010x4b,
15020x51,
15030x02,
15040x30,
15050x73,
15060xc7,
15070x87,
15080xc5,
15090x4b,
15100xa2,
15110x97,
15120x0f,
15130xe8,
15140x46,
15150x5f,
15160x7e,
15170x2a,
15180xe1,
15190x30,
15200x20,
15210xb0,
15220xfa,
15230xe7,
15240xce,
15250x61,
15260x42,
15270x57,
15280x6e,
15290x21,
15300xf3,
15310x7a,
15320xec,
15330xe3,
15340x25,
15350xc7,
15360x25,
15370xf3,
15380x67,
15390xa7,
15400x57,
15410x40,
15420x00,
15430x02,
15440xcf,
15450x1c,
15460x80,
15470x77,
15480x67,
15490xbd,
15500x70,
15510xa1,
15520x19,
15530x92,
15540x31,
15550x75,
15560x93,
15570x27,
15580x27,
15590xb6,
15600x82,
15610xe4,
15620xeb,
15630x1d,
15640x78,
15650x48,
15660xe7,
15670xa5,
15680x5e,
15690x57,
15700xef,
15710x64,
15720x28,
15730x64,
15740x1b,
15750xf6,
15760x11,
15770xb2,
15780x03,
15790x9d,
15800xb9,
15810x18,
15820x02,
15830x27,
15840xf7,
15850xbe,
15860x9d,
15870x55,
15880xfc,
15890x00,
15900xd2,
15910xc7,
15920xae,
15930xad,
15940x0b,
15950xc5,
15960xe9,
15970x42,
15980x41,
15990x48,
16000xd8,
16010x32,
16020xcf,
16030xf6,
16040x0f,
16050xf5,
16060xbc,
16070x97,
16080xc6,
16090x99,
16100x47,
16110x76,
16120xbd,
16130x89,
16140x06,
16150x0f,
16160x63,
16170x0c,
16180x51,
16190xd4,
16200x5e,
16210xea,
16220x48,
16230xa8,
16240xa2,
16250x56,
16260x1c,
16270x79,
16280x84,
16290x86,
16300x40,
16310x88,
16320x41,
16330x76,
16340x55,
16350xfc,
16360xc2,
16370xd7,
16380xfd,
16390xc9,
16400xc7,
16410x80,
16420x61,
16430x35,
16440xa7,
16450x43,
16460x20,
16470xf7,
16480xeb,
16490x6c,
16500x66,
16510x13,
16520xb0,
16530xec,
16540x02,
16550x75,
16560x3e,
16570x4b,
16580xaf,
16590xb9,
16600x5d,
16610x40,
16620xda,
16630xd6,
16640x6e,
16650x2d,
16660x39,
16670x54,
16680xc2,
16690x95,
16700x35,
16710x54,
16720x25,
16730x72,
16740xe1,
16750x78,
16760xb8,
16770xeb,
16780xc1,
16790x16,
16800x58,
16810x0f,
16820x9c,
16830x9b,
16840xb4,
16850xea,
16860x37,
16870xec,
16880x3b,
16890x11,
16900xba,
16910xd5,
16920x8a,
16930xa9,
16940xe3,
16950x98,
16960x00,
16970x51,
16980x1c,
16990x14,
17000xe0,
17010x40,
17020x96,
17030xe5,
17040xe9,
17050xf2,
17060x21,
17070x22,
17080xb1,
17090x23,
17100x60,
17110x78,
17120xd3,
17130x17,
17140xf8,
17150x7a,
17160xa5,
17170xa8,
17180xba,
17190x20,
17200xd3,
17210x15,
17220x1e,
17230x32,
17240xe4,
17250x5e,
17260x15,
17270x48,
17280xae,
17290xa9,
17300xe5,
17310xb8,
17320x33,
17330xec,
17340xe8,
17350xa2,
17360x42,
17370xac,
17380xbf,
17390x10,
17400x84,
17410x53,
17420x87,
17430x19,
17440xb4,
17450x5f,
17460x76,
17470x4d,
17480x01,
17490x9d,
17500x56,
17510x74,
17520xd9,
17530x5c,
17540x97,
17550xe7,
17560x88,
17570xea,
17580x3a,
17590xbf,
17600xdc,
17610x4c,
17620x33,
17630x8a,
17640x16,
17650xb9,
17660x5b,
17670xfa,
17680xd8,
17690x42,
17700xa7,
17710xbb,
17720x3c,
17730x04,
17740x27,
17750x78,
17760x49,
17770x81,
17780x2a,
17790x5a,
17800x7d,
17810x7c,
17820x23,
17830xa8,
17840xba,
17850xf7,
17860x9a,
17870x9f,
17880xd2,
17890x66,
17900x3e,
17910x38,
17920x3c,
17930x75,
17940xf9,
17950xd1,
17960x30,
17970x26,
17980x30,
17990x6e,
18000x5a,
18010x6e,
18020xdc,
18030x6a,
18040x69,
18050x32,
18060x50,
18070x33,
18080x47,
18090x9e,
18100xa4,
18110xa8,
18120x64,
18130x66,
18140xf0,
18150x8a,
18160xe4,
18170xfd,
18180x27,
18190x6f,
18200x51,
18210x25,
18220x8b,
18230x43,
18240x74,
18250xc9,
18260x8e,
18270xbd,
18280x88,
18290x31,
18300xbe,
18310xec,
18320x65,
18330xd2,
18340xcb,
18350x8d,
18360x5a,
18370x13,
18380x48,
18390x16,
18400x8c,
18410x61,
18420x0b,
18430x11,
18440xf6,
18450xc6,
18460x66,
18470xae,
18480xc3,
18490xcc,
18500x0c,
18510xd2,
18520xe1,
18530x9f,
18540x82,
18550x41,
18560x3f,
18570x56,
18580xf9,
18590x73,
18600xef,
18610xdc,
18620x30,
18630x50,
18640xcf,
18650xb6,
18660x7f,
18670xbc,
18680xd0,
18690xb3,
18700x10,
18710xab,
18720x24,
18730xe4,
18740xec,
18750xad,
18760x18,
18770x8c,
18780x39,
18790x2d,
18800x30,
18810x4c,
18820xc5,
18830x40,
18840x0d,
18850xf6,
18860xac,
18870xd6,
18880x18,
18890x5d,
18900x96,
18910xbf,
18920x5f,
18930x71,
18940x75,
18950x96,
18960x22,
18970x97,
18980x0f,
18990x02,
19000x94,
19010x6e,
19020xa6,
19030xae,
19040x6d,
19050x8f,
19060x1e,
19070xca,
19080x12,
19090x9b,
19100x2a,
19110x1c,
19120xce,
19130xa9,
19140xee,
19150xfd,
19160x12,
19170x8e,
19180xfc,
19190xed,
19200x09,
19210x33,
19220xba,
19230xf4,
19240x1a,
19250x15,
19260xf6,
19270x9d,
19280x87,
19290x16,
19300x43,
19310x7c,
19320x78,
19330x57,
19340xe1,
19350x44,
19360xc9,
19370xeb,
19380x1f,
19390x58,
19400x4d,
19410xc1,
19420x49,
19430x11,
19440x5c,
19450xb2,
19460x11,
19470xa8,
19480x55,
19490x16,
19500xf1,
19510xc6,
19520x50,
19530xe9,
19540x87,
19550x89,
19560xf6,
19570xcf,
19580xd8,
19590x9c,
19600x51,
19610xa7,
19620xbc,
19630x5b,
19640x31,
19650x6d,
19660x4d,
19670x51,
19680xd0,
19690x4c,
19700xbc,
19710x0d,
19720x58,
19730x2d,
19740x7b,
19750x88,
19760x7a,
19770xf9,
19780x8e,
19790xd6,
19800x40,
19810x4d,
19820xbb,
19830xbe,
19840xc4,
19850xe5,
19860x07,
19870xfc,
19880xd9,
19890x7b,
19900x6d,
19910xa6,
19920x42,
19930x57,
19940x8f,
19950x02,
19960x94,
19970x4f,
19980xe4,
19990x2a,
20000x65,
20010xe2,
20020x19,
20030x5a,
20040x50,
20050xe1,
20060x25,
20070x65,
20080x4a,
20090x60,
20100xc2,
20110xcd,
20120xa8,
20130xec,
20140x05,
20150x2e,
20160x87,
20170x7b,
20180x95,
20190xb7,
20200x4f,
20210xa0,
20220x0b,
20230x1b,
20240x4a,
20250x7f,
20260x92,
20270xc8,
20280x90,
20290xee,
20300x89,
20310x1e,
20320x10,
20330xd2,
20340x85,
20350xe4,
20360x9f,
20370x63,
20380xc8,
20390x12,
20400xbb,
20410x4e,
20420xb8,
20430xcf,
20440x0a,
20450xec,
20460x18,
20470x4e,
20480xe6,
20490x7c,
20500xb3,
20510x33,
20520x26,
20530xc7,
20540x1f,
20550xd2,
20560x04,
20570x23,
20580xea,
20590x07,
20600x0c,
20610x5f,
20620x90,
20630xbd,
20640xa7,
20650x6a,
20660x0f,
20670x4a,
20680xd6,
20690x10,
20700x01,
20710x3c,
20720x12,
20730x29,
20740x2e,
20750x96,
20760xc0,
20770x4d,
20780xbb,
20790xbe,
20800xe5,
20810xa7,
20820x83,
20830xd5,
20840x6a,
20850x3c,
20860xe3,
20870x5b,
20880xb8,
20890xf2,
20900x5c,
20910x6d,
20920x1f,
20930xa6,
20940xf3,
20950x12,
20960x24,
20970xf6,
20980xd6,
20990x3b,
21000x10,
21010x14,
21020x09,
21030x07,
21040x82,
21050xe8,
21060x30,
21070x6a,
21080x99,
21090xdc,
21100x95,
21110x01,
21120x9c,
21130xd4,
21140x68,
21150x3b,
21160xca,
21170x98,
21180x12,
21190xab,
21200x77,
21210x25,
21220x15,
21230x7d,
21240x10,
21250x32,
21260x45,
21270x98,
21280xcd,
21290x7a,
21300xdf,
21310x71,
21320x8a,
21330x75,
21340xc1,
21350x1c,
21360xd4,
21370x68,
21380x25,
21390xeb,
21400xbb,
21410x54,
21420x27,
21430x6f,
21440x2a,
21450xf7,
21460xb9,
21470x98,
21480x03,
21490x27,
21500xde,
21510x24,
21520xa8,
21530xbb,
21540x98,
21550xc2,
21560x84,
21570xff,
21580x9b,
21590x51,
21600xd8,
21610x53,
21620x50,
21630xda,
21640xf5,
21650x88,
21660xaa,
21670x87,
21680x2f,
21690xae,
21700xd6,
21710xea,
21720x6b,
21730xde,
21740xc8,
21750xd7,
21760xa7,
21770x28,
21780x65,
21790x81,
21800xe8,
21810xb2,
21820x3b,
21830x1d,
21840x4f,
21850x75,
21860x8f,
21870x9f,
21880x7a,
21890x74,
21900x8e,
21910xc1,
21920x5f,
21930x9a,
21940xa8,
21950x9d,
21960xfa,
21970x03,
21980xa3,
21990x71,
22000x9b,
22010x37,
22020x6d,
22030xd5,
22040x0b,
22050xf5,
22060xe1,
22070xa1,
22080x1b,
22090x01,
22100x6a,
22110xc6,
22120x67,
22130xaa,
22140xea,
22150x2c,
22160x9d,
22170xa4,
22180xd2,
22190x6e,
22200xfc,
22210xde,
22220x2e,
22230x7f,
22240x94,
22250x69,
22260xe5,
22270x4a,
22280xe0,
22290x01,
22300x48,
22310x3c,
22320x6b,
22330xf7,
22340x1e,
22350xb6,
22360x0b,
22370x5f,
22380xf9,
22390x2e,
22400x07,
22410xc5,
22420xe8,
22430xae,
22440x37,
22450x1b,
22460xbc,
22470x3c,
22480xd8,
22490xd5,
22500x0b,
22510x91,
22520x9e,
22530x80,
22540x24,
22550xf5,
22560x06,
22570x0c,
22580x0e,
22590x98,
22600x07,
22610x96,
22620x2d,
22630x19,
22640xdc,
22650x58,
22660x93,
22670xcc,
22680xfb,
22690x4e,
22700xeb,
22710xbd,
22720x0f,
22730xf5,
22740xaf,
22750x01,
22760xfa,
22770xf1,
22780x7c,
22790x43,
22800x8c,
22810xb8,
22820x56,
22830x3e,
22840xbe,
22850x77,
22860x4e,
22870x2b,
22880xf7,
22890xbb,
22900xb7,
22910x45,
22920x47,
22930xcd,
22940xcc,
22950xa6,
22960x4c,
22970x72,
22980x7b,
22990x6a,
23000x2a,
23010x70,
23020x13,
23030x07,
23040xfd,
23050xb8,
23060x9c,
23070x98,
23080x3a,
23090xd8,
23100x23,
23110x67,
23120x5b,
23130x34,
23140xd5,
23150x14,
23160x0c,
23170xab,
23180x77,
23190x1f,
23200xf8,
23210x3d,
23220x5a,
23230x9f,
23240x92,
23250xb7,
23260x2c,
23270xad,
23280x31,
23290xde,
23300x61,
23310x07,
23320xb3,
23330x6b,
23340xf7,
23350x38,
23360x15,
23370x95,
23380x46,
23390x14,
23400x48,
23410x53,
23420x69,
23430x52,
23440x66,
23450x07,
23460x6d,
23470x83,
23480x71,
23490x8a,
23500x67,
23510x25,
23520x20,
23530x0f,
23540xfe,
23550xd7,
23560x02,
23570xd7,
23580x6e,
23590x2c,
23600xd2,
23610x1a,
23620x0a,
23630x5d,
23640xfd,
23650x0f,
23660x74,
23670xe3,
23680xa4,
23690x36,
23700x07,
23710x9a,
23720xdf,
23730xd4,
23740x79,
23750xbf,
23760xef,
23770x59,
23780xc0,
23790x44,
23800x52,
23810x87,
23820x9a,
23830x6e,
23840x1d,
23850x0e,
23860xee,
23870xde,
23880x2e,
23890x1a,
23900xa9,
23910x8f,
23920x3a,
23930xc9,
23940xba,
23950xec,
23960x99,
23970x78,
23980x2d,
23990x55,
24000x6b,
24010x14,
24020xc2,
24030x06,
24040xd5,
24050xfc,
24060x93,
24070x53,
24080x4d,
24090x11,
24100x8c,
24110xf8,
24120xfa,
24130x79,
24140x7c,
24150xa6,
24160x64,
24170xae,
24180x61,
24190xb8,
24200x7b,
24210x94,
24220x56,
24230xa6,
24240x39,
24250x78,
24260x9a,
24270xe5,
24280xc7,
24290xdf,
24300x18,
24310x63,
24320x23,
24330x9c,
24340xfa,
24350x66,
24360xbb,
24370xb7,
24380x5a,
24390x27,
24400x4c,
24410xd1,
24420xa1,
24430x83,
24440x22,
24450xb3,
24460x52,
24470x49,
24480x35,
24490xb0,
24500x22,
24510x83,
24520x59,
24530x12,
24540x00,
24550x16,
24560x98,
24570xdd,
24580xad,
24590xc2,
24600x94,
24610xf9,
24620xd3,
24630x7b,
24640x64,
24650x7f,
24660x44,
24670x3e,
24680x3c,
24690x8b,
24700x9a,
24710x83,
24720x9c,
24730x69,
24740x6b,
24750xe4,
24760xdf,
24770x9f,
24780xed,
24790x54,
24800x1f,
24810xe5,
24820x5d,
24830x7a,
24840x05,
24850x82,
24860xb3,
24870xdd,
24880xef,
24890xfc,
24900x53,
24910x96,
24920xb0,
24930x2c,
24940x5a,
24950xf8,
24960xdf,
24970x9c,
24980x8b,
24990x16,
25000x4e,
25010xdf,
25020xda,
25030x4d,
25040x09,
25050x09,
25060x69,
25070x50,
25080x03,
25090x65,
25100xd8,
25110x73,
25120x70,
25130xe8,
25140x86,
25150xbf,
25160xbb,
25170x35,
25180xce,
25190xb2,
25200x46,
25210xcb,
25220x02,
25230x00,
25240x5b,
25250xb4,
25260xe2,
25270xc6,
25280x8f,
25290x2f,
25300x98,
25310xaf,
25320x87,
25330x4b,
25340x48,
25350x45,
25360xed,
25370xcc,
25380x1d,
25390xe6,
25400x58,
25410xd6,
25420xf2,
25430x50,
25440x25,
25450x9f,
25460x52,
25470xc7,
25480xcb,
25490x8a,
25500x17,
25510x9d,
25520x5b,
25530xe5,
25540xc8,
25550xd7,
25560x72,
25570xb7,
25580x52,
25590xb2,
25600xc4,
25610x98,
25620xe3,
25630x7a,
25640x17,
25650x3e,
25660xc6,
25670x60,
25680xa7,
25690x97,
25700xb0,
25710xcf,
25720x18,
25730x81,
25740x53,
25750x84,
25760x4c,
25770xd5,
25780x17,
25790x32,
25800x03,
25810x13,
25820x39,
25830x51,
25840x09,
25850x10,
25860xe3,
25870x77,
25880x49,
25890x4f,
25900x62,
25910x01,
25920xbf,
25930x8c,
25940x9a,
25950xe0,
25960x41,
25970x9e,
25980x89,
25990x74,
26000x36,
26010xf9,
26020x96,
26030x86,
26040x2e,
26050x96,
26060x1c,
26070x4a,
26080xb7,
26090x2b,
26100x4a,
26110x97,
26120xbc,
26130x99,
26140x40,
26150xa3,
26160xe0,
26170x3d,
26180xc8,
26190xad,
26200x2f,
26210xdf,
26220x4f,
26230x2c,
26240xc4,
26250x69,
26260x82,
26270x9f,
26280x9b,
26290x81,
26300x0c,
26310x61,
26320x5c,
26330xa5,
26340x9d,
26350x8c,
26360x89,
26370xc0,
26380x2c,
26390xb4,
26400x4a,
26410x33,
26420x4e,
26430xeb,
26440xa2,
26450x56,
26460x40,
26470xc0,
26480xc2,
26490x46,
26500xaf,
26510x6a,
26520xfc,
26530x67,
26540xd1,
26550x80,
26560x5e,
26570xc5,
26580x6d,
26590x84,
26600x43,
26610x27,
26620x3f,
26630x55,
26640x15,
26650x96,
26660x6a,
26670xa0,
26680xa5,
26690xda,
26700xb7,
26710xff,
26720xb7,
26730x75,
26740x6e,
26750x4c,
26760x49,
26770x91,
26780x9d,
26790x22,
26800xa3,
26810x46,
26820xea,
26830xed,
26840x9a,
26850x00,
26860xe2,
26870x32,
26880xc3,
26890xd6,
26900xa9,
26910x71,
26920x20,
26930x55,
26940xa3,
26950x19,
26960xed,
26970xf8,
26980x4f,
26990xa7,
27000x12,
27010x9c,
27020x66,
27030x87,
27040xaf,
27050x4e,
27060xb7,
27070xf0,
27080xdb,
27090xbf,
27100xef,
27110xf0,
27120xf6,
27130xaf,
27140xea,
27150xda,
27160x09,
27170xfe,
27180xde,
27190x38,
27200x5c,
27210xa5,
27220xa2,
27230xdf,
27240x99,
27250x45,
27260xa8,
27270xe4,
27280xe7,
27290x92,
27300xac,
27310x67,
27320xaa,
27330x4f,
27340xbf,
27350x77,
27360x3e,
27370xa2,
27380x40,
27390x49,
27400x22,
27410x4a,
27420x1e,
27430x3b,
27440xaa,
27450x70,
27460x7f,
27470x95,
27480xaf,
27490x37,
27500x4b,
27510xfc,
27520x99,
27530xe2,
27540xe0,
27550xba,
27560xd7,
27570x34,
27580xce,
27590x55,
27600x88,
27610x5b,
27620x84,
27630x1b,
27640x57,
27650xc4,
27660x80,
27670x03,
27680x53,
27690xc9,
27700x2f,
27710x93,
27720x04,
27730x4d,
27740xd5,
27750x96,
27760xe5,
27770x70,
27780xa6,
27790x6e,
27800x63,
27810x5d,
27820x9d,
27830x6c,
27840xdb,
27850x02,
27860x0a,
27870xa9,
27880xda,
27890x8b,
27900x53,
27910xdc,
27920xd9,
27930x9a,
27940xc5,
27950x94,
27960x2c,
27970x91,
27980x92,
27990x2a,
28000xde,
28010xbb,
28020x8b,
28030x13,
28040xb9,
28050x19,
28060x96,
28070x64,
28080xcc,
28090xf2,
28100x64,
28110x39,
28120xb7,
28130x75,
28140x49,
28150xe9,
28160x86,
28170xc2,
28180x86,
28190x62,
28200xd9,
28210x24,
28220xd3,
28230x81,
28240x35,
28250x49,
28260xfc,
28270xa0,
28280xa5,
28290xa0,
28300x93,
28310x05,
28320x64,
28330xb4,
28340x1a,
28350x57,
28360xce,
28370x0c,
28380x90,
28390x02,
28400x27,
28410xc5,
28420x7a,
28430x2b,
28440x5d,
28450xae,
28460x3e,
28470xd5,
28480xdd,
28490x10,
28500x7c,
28510x14,
28520xea,
28530x3a,
28540x08,
28550xac,
28560x72,
28570x4e,
28580x90,
28590x3d,
28600x3b,
28610x7c,
28620x86,
28630x2e,
28640xeb,
28650xd4,
28660x06,
28670x70,
28680xe6,
28690xc7,
28700xfb,
28710x5f,
28720xbd,
28730x18,
28740xf4,
28750x11,
28760xa4,
28770x1a,
28780x93,
28790xc3,
28800xbe,
28810xd9,
28820xfb,
28830x26,
28840x48,
28850x2f,
28860x37,
28870x3c,
28880xd0,
28890x03,
28900x47,
28910x1a,
28920xf7,
28930x62,
28940x19,
28950x24,
28960x5c,
28970xf4,
28980xa8,
28990x92,
29000x20,
29010x7a,
29020xf2,
29030x9e,
29040x2a,
29050xc5,
29060x95,
29070xa2,
29080xfb,
29090xa4,
29100xea,
29110x85,
29120xd8,
29130x56,
29140xb7,
29150x70,
29160xd1,
29170x60,
29180x30,
29190xa5,
29200x30,
29210x82,
29220x70,
29230xdc,
29240x7a,
29250x65,
29260x8a,
29270x36,
29280x3f,
29290x5b,
29300x0c,
29310xae,
29320x54,
29330x7c,
29340xd3,
29350x57,
29360x84,
29370x7b,
29380x3a,
29390x65,
29400x18,
29410x81,
29420xee,
29430x05,
29440x9b,
29450x44,
29460x4d,
29470xb8,
29480xda,
29490xa2,
29500xa1,
29510xc9,
29520x15,
29530xd3,
29540x73,
29550x03,
29560x0e,
29570x43,
29580xe9,
29590x8e,
29600x15,
29610xf9,
29620xbe,
29630xc6,
29640xc5,
29650x8a,
29660xe5,
29670xc0,
29680x1e,
29690xc2,
29700x37,
29710x9e,
29720x2a,
29730x26,
29740xa5,
29750xa0,
29760xbd,
29770x24,
29780x5f,
29790xb9,
29800xc1,
29810xab,
29820x34,
29830x48,
29840xb9,
29850x5d,
29860x98,
29870xb4,
29880x65,
29890x18,
29900xf3,
29910x63,
29920x19,
29930x44,
29940x1b,
29950x11,
29960x16,
29970xff,
29980xdc,
29990xf1,
30000x79,
30010x08,
30020x86,
30030x0f,
30040x52,
30050x98,
30060x73,
30070xc4,
30080x92,
30090x90,
30100x2b,
30110x47,
30120x09,
30130xd0,
30140x43,
30150x6c,
30160x2f,
30170x20,
30180xeb,
30190xdc,
30200xda,
30210xc5,
30220x08,
30230x7b,
30240x94,
30250x42,
30260x30,
30270x6a,
30280xc7,
30290xda,
30300x8c,
30310xc3,
30320x76,
30330xa7,
30340xa5,
30350xcc,
30360x62,
30370x13,
30380x00,
30390x60,
30400x31,
30410x58,
30420x44,
30430x9b,
30440xf5,
30450x64,
30460x14,
30470xf5,
30480x11,
30490xc5,
30500x54,
30510x52,
30520x83,
30530xd4,
30540x73,
30550x01,
30560x16,
30570x0e,
30580xb3,
30590x7a,
30600x29,
30610x69,
30620x35,
30630x56,
30640xd4,
30650xee,
30660x8a,
30670x17,
30680xa2,
30690x99,
30700x24,
30710x9c,
30720xd7,
30730x8f,
30740xdb,
30750x55,
30760xb5,
30770x3e
3078};
diff --git a/drivers/sensors/pimoroni_trackball.c b/drivers/sensors/pimoroni_trackball.c
new file mode 100644
index 000000000..c0ac644f7
--- /dev/null
+++ b/drivers/sensors/pimoroni_trackball.c
@@ -0,0 +1,140 @@
1/* Copyright 2020 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com>
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "pimoroni_trackball.h"
18#include "i2c_master.h"
19
20static uint8_t scrolling = 0;
21static int16_t x_offset = 0;
22static int16_t y_offset = 0;
23static int16_t h_offset = 0;
24static int16_t v_offset = 0;
25static float precisionSpeed = 1;
26
27static uint16_t i2c_timeout_timer;
28
29#ifndef PIMORONI_I2C_TIMEOUT
30# define PIMORONI_I2C_TIMEOUT 100
31#endif
32#ifndef I2C_WAITCHECK
33# define I2C_WAITCHECK 1000
34#endif
35#ifndef MOUSE_DEBOUNCE
36# define MOUSE_DEBOUNCE 5
37#endif
38
39void trackball_set_rgbw(uint8_t red, uint8_t green, uint8_t blue, uint8_t white) {
40 uint8_t data[] = {0x00, red, green, blue, white};
41 i2c_transmit(TRACKBALL_WRITE, data, sizeof(data), PIMORONI_I2C_TIMEOUT);
42}
43
44int16_t mouse_offset(uint8_t positive, uint8_t negative, int16_t scale) {
45 int16_t offset = (int16_t)positive - (int16_t)negative;
46 int16_t magnitude = (int16_t)(scale * offset * offset * precisionSpeed);
47 return offset < 0 ? -magnitude : magnitude;
48}
49
50void update_member(int8_t* member, int16_t* offset) {
51 if (*offset > 127) {
52 *member = 127;
53 *offset -= 127;
54 } else if (*offset < -127) {
55 *member = -127;
56 *offset += 127;
57 } else {
58 *member = *offset;
59 *offset = 0;
60 }
61}
62
63__attribute__((weak)) void trackball_check_click(bool pressed, report_mouse_t* mouse) {
64 if (pressed) {
65 mouse->buttons |= MOUSE_BTN1;
66 } else {
67 mouse->buttons &= ~MOUSE_BTN1;
68 }
69}
70
71float trackball_get_precision(void) { return precisionSpeed; }
72void trackball_set_precision(float precision) { precisionSpeed = precision; }
73bool trackball_is_scrolling(void) { return scrolling; }
74void trackball_set_scrolling(bool scroll) { scrolling = scroll; }
75
76__attribute__((weak)) void pointing_device_init(void) { i2c_init(); trackball_set_rgbw(0x00, 0x00, 0x00, 0x00); }
77
78void pointing_device_task(void) {
79 static bool debounce;
80 static uint16_t debounce_timer;
81 uint8_t state[5] = {};
82 if (timer_elapsed(i2c_timeout_timer) > I2C_WAITCHECK) {
83 if (i2c_readReg(TRACKBALL_READ, 0x04, state, 5, PIMORONI_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) {
84 if (!state[4] && !debounce) {
85 if (scrolling) {
86#ifdef PIMORONI_TRACKBALL_INVERT_X
87 h_offset += mouse_offset(state[2], state[3], 1);
88#else
89 h_offset -= mouse_offset(state[2], state[3], 1);
90#endif
91#ifdef PIMORONI_TRACKBALL_INVERT_Y
92 v_offset += mouse_offset(state[1], state[0], 1);
93#else
94 v_offset -= mouse_offset(state[1], state[0], 1);
95#endif
96 } else {
97#ifdef PIMORONI_TRACKBALL_INVERT_X
98 x_offset -= mouse_offset(state[2], state[3], 5);
99#else
100 x_offset += mouse_offset(state[2], state[3], 5);
101#endif
102#ifdef PIMORONI_TRACKBALL_INVERT_Y
103 y_offset -= mouse_offset(state[1], state[0], 5);
104#else
105 y_offset += mouse_offset(state[1], state[0], 5);
106#endif
107 }
108 } else {
109 if (state[4]) {
110 debounce = true;
111 debounce_timer = timer_read();
112 }
113 }
114 } else {
115 i2c_timeout_timer = timer_read();
116 }
117 }
118
119 if (timer_elapsed(debounce_timer) > MOUSE_DEBOUNCE) debounce = false;
120
121 report_mouse_t mouse = pointing_device_get_report();
122
123#ifdef PIMORONI_TRACKBALL_CLICK
124 trackball_check_click(state[4] & (1 << 7), &mouse);
125#endif
126
127#ifndef PIMORONI_TRACKBALL_ROTATE
128 update_member(&mouse.x, &x_offset);
129 update_member(&mouse.y, &y_offset);
130 update_member(&mouse.h, &h_offset);
131 update_member(&mouse.v, &v_offset);
132#else
133 update_member(&mouse.x, &y_offset);
134 update_member(&mouse.y, &x_offset);
135 update_member(&mouse.h, &v_offset);
136 update_member(&mouse.v, &h_offset);
137#endif
138 pointing_device_set_report(mouse);
139 pointing_device_send();
140}
diff --git a/drivers/sensors/pimoroni_trackball.h b/drivers/sensors/pimoroni_trackball.h
new file mode 100644
index 000000000..a30fb0bb8
--- /dev/null
+++ b/drivers/sensors/pimoroni_trackball.h
@@ -0,0 +1,35 @@
1/* Copyright 2020 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com>
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#pragma once
18
19#include "quantum.h"
20#include "pointing_device.h"
21
22#ifndef TRACKBALL_ADDRESS
23# define TRACKBALL_ADDRESS 0x0A
24#endif
25#define TRACKBALL_WRITE ((TRACKBALL_ADDRESS << 1) | I2C_WRITE)
26#define TRACKBALL_READ ((TRACKBALL_ADDRESS << 1) | I2C_READ)
27
28void trackball_set_rgbw(uint8_t red, uint8_t green, uint8_t blue, uint8_t white);
29void trackball_check_click(bool pressed, report_mouse_t *mouse);
30void trackball_register_button(bool pressed, enum mouse_buttons button);
31
32float trackball_get_precision(void);
33void trackball_set_precision(float precision);
34bool trackball_is_scrolling(void);
35void trackball_set_scrolling(bool scroll);
diff --git a/drivers/sensors/pmw3360.c b/drivers/sensors/pmw3360.c
new file mode 100644
index 000000000..91ee87b95
--- /dev/null
+++ b/drivers/sensors/pmw3360.c
@@ -0,0 +1,237 @@
1/* Copyright 2020 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com>
2 * Copyright 2019 Sunjun Kim
3 * Copyright 2020 Ploopy Corporation
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include "wait.h"
20#include "debug.h"
21#include "print.h"
22#include "pmw3360.h"
23#include "pmw3360_firmware.h"
24
25bool _inBurst = false;
26
27#ifndef PMW_CPI
28# define PMW_CPI 1600
29#endif
30#ifndef PMW_CLOCK_SPEED
31# define PMW_CLOCK_SPEED 70000000
32#endif
33#ifndef SPI_MODE
34# define SPI_MODE 3
35#endif
36#ifndef SPI_DIVISOR
37# define SPI_DIVISOR (F_CPU / PMW_CLOCK_SPEED)
38#endif
39#ifndef ROTATIONAL_TRANSFORM_ANGLE
40# define ROTATIONAL_TRANSFORM_ANGLE 0x00
41#endif
42#ifndef PMW_CS_PIN
43# define PMW_CS_PIN SPI_SS_PIN
44#endif
45
46void 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')); }
47
48bool spi_start_adv(void) {
49 bool status = spi_start(PMW_CS_PIN, false, SPI_MODE, SPI_DIVISOR);
50 wait_us(1);
51 return status;
52}
53
54void spi_stop_adv(void) {
55 wait_us(1);
56 spi_stop();
57}
58
59spi_status_t spi_write_adv(uint8_t reg_addr, uint8_t data) {
60 if (reg_addr != REG_Motion_Burst) {
61 _inBurst = false;
62 }
63
64 spi_start_adv();
65 // send address of the register, with MSBit = 1 to indicate it's a write
66 spi_status_t status = spi_write(reg_addr | 0x80);
67 status = spi_write(data);
68
69 // tSCLK-NCS for write operation
70 wait_us(20);
71
72 // tSWW/tSWR (=120us) minus tSCLK-NCS. Could be shortened, but is looks like a safe lower bound
73 wait_us(100);
74 spi_stop();
75 return status;
76}
77
78uint8_t spi_read_adv(uint8_t reg_addr) {
79 spi_start_adv();
80 // send adress of the register, with MSBit = 0 to indicate it's a read
81 spi_write(reg_addr & 0x7f);
82
83 uint8_t data = spi_read();
84
85 // tSCLK-NCS for read operation is 120ns
86 wait_us(1);
87
88 // tSRW/tSRR (=20us) minus tSCLK-NCS
89 wait_us(19);
90
91 spi_stop();
92 return data;
93}
94
95void pmw_set_cpi(uint16_t cpi) {
96 uint8_t cpival = constrain((cpi / 100) - 1, 0, 0x77); // limits to 0--119
97
98 spi_start_adv();
99 spi_write_adv(REG_Config1, cpival);
100 spi_stop();
101}
102
103uint16_t pmw_get_cpi(void) {
104 uint8_t cpival = spi_read_adv(REG_Config1);
105 return (uint16_t)(cpival & 0xFF) * 100;
106}
107
108bool pmw_spi_init(void) {
109 setPinOutput(PMW_CS_PIN);
110
111 spi_init();
112 _inBurst = false;
113
114 spi_stop();
115 spi_start_adv();
116 spi_stop();
117
118 spi_write_adv(REG_Shutdown, 0xb6); // Shutdown first
119 wait_ms(300);
120
121 spi_start_adv();
122 wait_us(40);
123 spi_stop_adv();
124 wait_us(40);
125
126 spi_write_adv(REG_Power_Up_Reset, 0x5a);
127 wait_ms(50);
128
129 spi_read_adv(REG_Motion);
130 spi_read_adv(REG_Delta_X_L);
131 spi_read_adv(REG_Delta_X_H);
132 spi_read_adv(REG_Delta_Y_L);
133 spi_read_adv(REG_Delta_Y_H);
134
135 pmw_upload_firmware();
136
137 spi_stop_adv();
138
139 wait_ms(10);
140 pmw_set_cpi(PMW_CPI);
141
142 wait_ms(1);
143
144 spi_write_adv(REG_Config2, 0x00);
145
146 spi_write_adv(REG_Angle_Tune, constrain(ROTATIONAL_TRANSFORM_ANGLE, -30, 30));
147
148 bool init_success = pmw_check_signature();
149
150 writePinLow(PMW_CS_PIN);
151
152 return init_success;
153}
154
155void pmw_upload_firmware(void) {
156 spi_write_adv(REG_SROM_Enable, 0x1d);
157
158 wait_ms(10);
159
160 spi_write_adv(REG_SROM_Enable, 0x18);
161
162 spi_start_adv();
163 spi_write(REG_SROM_Load_Burst | 0x80);
164 wait_us(15);
165
166 unsigned char c;
167 for (int i = 0; i < firmware_length; i++) {
168 c = (unsigned char)pgm_read_byte(firmware_data + i);
169 spi_write(c);
170 wait_us(15);
171 }
172 wait_us(200);
173
174 spi_read_adv(REG_SROM_ID);
175
176 spi_write_adv(REG_Config2, 0x00);
177
178 spi_stop();
179 wait_ms(10);
180}
181
182bool pmw_check_signature(void) {
183 uint8_t pid = spi_read_adv(REG_Product_ID);
184 uint8_t iv_pid = spi_read_adv(REG_Inverse_Product_ID);
185 uint8_t SROM_ver = spi_read_adv(REG_SROM_ID);
186 return (pid == 0x42 && iv_pid == 0xBD && SROM_ver == 0x04); // signature for SROM 0x04
187}
188
189report_pmw_t pmw_read_burst(void) {
190 if (!_inBurst) {
191 dprintf("burst on");
192 spi_write_adv(REG_Motion_Burst, 0x00);
193 _inBurst = true;
194 }
195
196 spi_start_adv();
197 spi_write(REG_Motion_Burst);
198 wait_us(35); // waits for tSRAD
199
200 report_pmw_t data;
201 data.motion = 0;
202 data.dx = 0;
203 data.mdx = 0;
204 data.dy = 0;
205 data.mdx = 0;
206
207 data.motion = spi_read();
208 spi_write(0x00); // skip Observation
209 data.dx = spi_read();
210 data.mdx = spi_read();
211 data.dy = spi_read();
212 data.mdy = spi_read();
213
214 spi_stop();
215
216 print_byte(data.motion);
217 print_byte(data.dx);
218 print_byte(data.mdx);
219 print_byte(data.dy);
220 print_byte(data.mdy);
221 dprintf("\n");
222
223 data.isMotion = (data.motion & 0x80) != 0;
224 data.isOnSurface = (data.motion & 0x08) == 0;
225 data.dx |= (data.mdx << 8);
226 data.dx = data.dx * -1;
227 data.dy |= (data.mdy << 8);
228 data.dy = data.dy * -1;
229
230 spi_stop();
231
232 if (data.motion & 0b111) { // panic recovery, sometimes burst mode works weird.
233 _inBurst = false;
234 }
235
236 return data;
237}
diff --git a/drivers/sensors/pmw3360.h b/drivers/sensors/pmw3360.h
new file mode 100644
index 000000000..d5b174179
--- /dev/null
+++ b/drivers/sensors/pmw3360.h
@@ -0,0 +1,104 @@
1/* Copyright 2020 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com>
2 * Copyright 2019 Sunjun Kim
3 * Copyright 2020 Ploopy Corporation
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#pragma once
20
21#include "spi_master.h"
22
23// Registers
24#define REG_Product_ID 0x00
25#define REG_Revision_ID 0x01
26#define REG_Motion 0x02
27#define REG_Delta_X_L 0x03
28#define REG_Delta_X_H 0x04
29#define REG_Delta_Y_L 0x05
30#define REG_Delta_Y_H 0x06
31#define REG_SQUAL 0x07
32#define REG_Raw_Data_Sum 0x08
33#define REG_Maximum_Raw_data 0x09
34#define REG_Minimum_Raw_data 0x0A
35#define REG_Shutter_Lower 0x0B
36#define REG_Shutter_Upper 0x0C
37#define REG_Control 0x0D
38#define REG_Config1 0x0F
39#define REG_Config2 0x10
40#define REG_Angle_Tune 0x11
41#define REG_Frame_Capture 0x12
42#define REG_SROM_Enable 0x13
43#define REG_Run_Downshift 0x14
44#define REG_Rest1_Rate_Lower 0x15
45#define REG_Rest1_Rate_Upper 0x16
46#define REG_Rest1_Downshift 0x17
47#define REG_Rest2_Rate_Lower 0x18
48#define REG_Rest2_Rate_Upper 0x19
49#define REG_Rest2_Downshift 0x1A
50#define REG_Rest3_Rate_Lower 0x1B
51#define REG_Rest3_Rate_Upper 0x1C
52#define REG_Observation 0x24
53#define REG_Data_Out_Lower 0x25
54#define REG_Data_Out_Upper 0x26
55#define REG_Raw_Data_Dump 0x29
56#define REG_SROM_ID 0x2A
57#define REG_Min_SQ_Run 0x2B
58#define REG_Raw_Data_Threshold 0x2C
59#define REG_Config5 0x2F
60#define REG_Power_Up_Reset 0x3A
61#define REG_Shutdown 0x3B
62#define REG_Inverse_Product_ID 0x3F
63#define REG_LiftCutoff_Tune3 0x41
64#define REG_Angle_Snap 0x42
65#define REG_LiftCutoff_Tune1 0x4A
66#define REG_Motion_Burst 0x50
67#define REG_LiftCutoff_Tune_Timeout 0x58
68#define REG_LiftCutoff_Tune_Min_Length 0x5A
69#define REG_SROM_Load_Burst 0x62
70#define REG_Lift_Config 0x63
71#define REG_Raw_Data_Burst 0x64
72#define REG_LiftCutoff_Tune2 0x65
73
74#ifdef CONSOLE_ENABLE
75void print_byte(uint8_t byte);
76#endif
77
78typedef struct {
79 int8_t motion;
80 bool isMotion; // True if a motion is detected.
81 bool isOnSurface; // True when a chip is on a surface
82 int16_t dx; // displacement on x directions. Unit: Count. (CPI * Count = Inch value)
83 int8_t mdx;
84 int16_t dy; // displacement on y directions.
85 int8_t mdy;
86} report_pmw_t;
87
88
89
90bool spi_start_adv(void);
91void spi_stop_adv(void);
92spi_status_t spi_write_adv(uint8_t reg_addr, uint8_t data);
93uint8_t spi_read_adv(uint8_t reg_addr);
94bool pmw_spi_init(void);
95void pmw_set_cpi(uint16_t cpi);
96uint16_t pmw_get_cpi(void);
97void pmw_upload_firmware(void);
98bool pmw_check_signature(void);
99report_pmw_t pmw_read_burst(void);
100
101
102#define degToRad(angleInDegrees) ((angleInDegrees)*M_PI / 180.0)
103#define radToDeg(angleInRadians) ((angleInRadians)*180.0 / M_PI)
104#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 000000000..cca5a6a4d
--- /dev/null
+++ b/drivers/sensors/pmw3360_firmware.h
@@ -0,0 +1,300 @@
1/* Copyright 2020 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com>
2 * Copyright 2019 Sunjun Kim
3 * Copyright 2020 Ploopy Corporation
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#pragma once
20
21// clang-format off
22// Firmware Blob foor PMW3360
23const uint16_t firmware_length = 4094;
24// clang-format off
25const uint8_t firmware_data[] PROGMEM = { // SROM 0x04
260x01, 0x04, 0x8e, 0x96, 0x6e, 0x77, 0x3e, 0xfe, 0x7e, 0x5f, 0x1d, 0xb8, 0xf2, 0x66, 0x4e,
270xff, 0x5d, 0x19, 0xb0, 0xc2, 0x04, 0x69, 0x54, 0x2a, 0xd6, 0x2e, 0xbf, 0xdd, 0x19, 0xb0,
280xc3, 0xe5, 0x29, 0xb1, 0xe0, 0x23, 0xa5, 0xa9, 0xb1, 0xc1, 0x00, 0x82, 0x67, 0x4c, 0x1a,
290x97, 0x8d, 0x79, 0x51, 0x20, 0xc7, 0x06, 0x8e, 0x7c, 0x7c, 0x7a, 0x76, 0x4f, 0xfd, 0x59,
300x30, 0xe2, 0x46, 0x0e, 0x9e, 0xbe, 0xdf, 0x1d, 0x99, 0x91, 0xa0, 0xa5, 0xa1, 0xa9, 0xd0,
310x22, 0xc6, 0xef, 0x5c, 0x1b, 0x95, 0x89, 0x90, 0xa2, 0xa7, 0xcc, 0xfb, 0x55, 0x28, 0xb3,
320xe4, 0x4a, 0xf7, 0x6c, 0x3b, 0xf4, 0x6a, 0x56, 0x2e, 0xde, 0x1f, 0x9d, 0xb8, 0xd3, 0x05,
330x88, 0x92, 0xa6, 0xce, 0x1e, 0xbe, 0xdf, 0x1d, 0x99, 0xb0, 0xe2, 0x46, 0xef, 0x5c, 0x07,
340x11, 0x5d, 0x98, 0x0b, 0x9d, 0x94, 0x97, 0xee, 0x4e, 0x45, 0x33, 0x6b, 0x44, 0xc7, 0x29,
350x56, 0x27, 0x30, 0xc6, 0xa7, 0xd5, 0xf2, 0x56, 0xdf, 0xb4, 0x38, 0x62, 0xcb, 0xa0, 0xb6,
360xe3, 0x0f, 0x84, 0x06, 0x24, 0x05, 0x65, 0x6f, 0x76, 0x89, 0xb5, 0x77, 0x41, 0x27, 0x82,
370x66, 0x65, 0x82, 0xcc, 0xd5, 0xe6, 0x20, 0xd5, 0x27, 0x17, 0xc5, 0xf8, 0x03, 0x23, 0x7c,
380x5f, 0x64, 0xa5, 0x1d, 0xc1, 0xd6, 0x36, 0xcb, 0x4c, 0xd4, 0xdb, 0x66, 0xd7, 0x8b, 0xb1,
390x99, 0x7e, 0x6f, 0x4c, 0x36, 0x40, 0x06, 0xd6, 0xeb, 0xd7, 0xa2, 0xe4, 0xf4, 0x95, 0x51,
400x5a, 0x54, 0x96, 0xd5, 0x53, 0x44, 0xd7, 0x8c, 0xe0, 0xb9, 0x40, 0x68, 0xd2, 0x18, 0xe9,
410xdd, 0x9a, 0x23, 0x92, 0x48, 0xee, 0x7f, 0x43, 0xaf, 0xea, 0x77, 0x38, 0x84, 0x8c, 0x0a,
420x72, 0xaf, 0x69, 0xf8, 0xdd, 0xf1, 0x24, 0x83, 0xa3, 0xf8, 0x4a, 0xbf, 0xf5, 0x94, 0x13,
430xdb, 0xbb, 0xd8, 0xb4, 0xb3, 0xa0, 0xfb, 0x45, 0x50, 0x60, 0x30, 0x59, 0x12, 0x31, 0x71,
440xa2, 0xd3, 0x13, 0xe7, 0xfa, 0xe7, 0xce, 0x0f, 0x63, 0x15, 0x0b, 0x6b, 0x94, 0xbb, 0x37,
450x83, 0x26, 0x05, 0x9d, 0xfb, 0x46, 0x92, 0xfc, 0x0a, 0x15, 0xd1, 0x0d, 0x73, 0x92, 0xd6,
460x8c, 0x1b, 0x8c, 0xb8, 0x55, 0x8a, 0xce, 0xbd, 0xfe, 0x8e, 0xfc, 0xed, 0x09, 0x12, 0x83,
470x91, 0x82, 0x51, 0x31, 0x23, 0xfb, 0xb4, 0x0c, 0x76, 0xad, 0x7c, 0xd9, 0xb4, 0x4b, 0xb2,
480x67, 0x14, 0x09, 0x9c, 0x7f, 0x0c, 0x18, 0xba, 0x3b, 0xd6, 0x8e, 0x14, 0x2a, 0xe4, 0x1b,
490x52, 0x9f, 0x2b, 0x7d, 0xe1, 0xfb, 0x6a, 0x33, 0x02, 0xfa, 0xac, 0x5a, 0xf2, 0x3e, 0x88,
500x7e, 0xae, 0xd1, 0xf3, 0x78, 0xe8, 0x05, 0xd1, 0xe3, 0xdc, 0x21, 0xf6, 0xe1, 0x9a, 0xbd,
510x17, 0x0e, 0xd9, 0x46, 0x9b, 0x88, 0x03, 0xea, 0xf6, 0x66, 0xbe, 0x0e, 0x1b, 0x50, 0x49,
520x96, 0x40, 0x97, 0xf1, 0xf1, 0xe4, 0x80, 0xa6, 0x6e, 0xe8, 0x77, 0x34, 0xbf, 0x29, 0x40,
530x44, 0xc2, 0xff, 0x4e, 0x98, 0xd3, 0x9c, 0xa3, 0x32, 0x2b, 0x76, 0x51, 0x04, 0x09, 0xe7,
540xa9, 0xd1, 0xa6, 0x32, 0xb1, 0x23, 0x53, 0xe2, 0x47, 0xab, 0xd6, 0xf5, 0x69, 0x5c, 0x3e,
550x5f, 0xfa, 0xae, 0x45, 0x20, 0xe5, 0xd2, 0x44, 0xff, 0x39, 0x32, 0x6d, 0xfd, 0x27, 0x57,
560x5c, 0xfd, 0xf0, 0xde, 0xc1, 0xb5, 0x99, 0xe5, 0xf5, 0x1c, 0x77, 0x01, 0x75, 0xc5, 0x6d,
570x58, 0x92, 0xf2, 0xb2, 0x47, 0x00, 0x01, 0x26, 0x96, 0x7a, 0x30, 0xff, 0xb7, 0xf0, 0xef,
580x77, 0xc1, 0x8a, 0x5d, 0xdc, 0xc0, 0xd1, 0x29, 0x30, 0x1e, 0x77, 0x38, 0x7a, 0x94, 0xf1,
590xb8, 0x7a, 0x7e, 0xef, 0xa4, 0xd1, 0xac, 0x31, 0x4a, 0xf2, 0x5d, 0x64, 0x3d, 0xb2, 0xe2,
600xf0, 0x08, 0x99, 0xfc, 0x70, 0xee, 0x24, 0xa7, 0x7e, 0xee, 0x1e, 0x20, 0x69, 0x7d, 0x44,
610xbf, 0x87, 0x42, 0xdf, 0x88, 0x3b, 0x0c, 0xda, 0x42, 0xc9, 0x04, 0xf9, 0x45, 0x50, 0xfc,
620x83, 0x8f, 0x11, 0x6a, 0x72, 0xbc, 0x99, 0x95, 0xf0, 0xac, 0x3d, 0xa7, 0x3b, 0xcd, 0x1c,
630xe2, 0x88, 0x79, 0x37, 0x11, 0x5f, 0x39, 0x89, 0x95, 0x0a, 0x16, 0x84, 0x7a, 0xf6, 0x8a,
640xa4, 0x28, 0xe4, 0xed, 0x83, 0x80, 0x3b, 0xb1, 0x23, 0xa5, 0x03, 0x10, 0xf4, 0x66, 0xea,
650xbb, 0x0c, 0x0f, 0xc5, 0xec, 0x6c, 0x69, 0xc5, 0xd3, 0x24, 0xab, 0xd4, 0x2a, 0xb7, 0x99,
660x88, 0x76, 0x08, 0xa0, 0xa8, 0x95, 0x7c, 0xd8, 0x38, 0x6d, 0xcd, 0x59, 0x02, 0x51, 0x4b,
670xf1, 0xb5, 0x2b, 0x50, 0xe3, 0xb6, 0xbd, 0xd0, 0x72, 0xcf, 0x9e, 0xfd, 0x6e, 0xbb, 0x44,
680xc8, 0x24, 0x8a, 0x77, 0x18, 0x8a, 0x13, 0x06, 0xef, 0x97, 0x7d, 0xfa, 0x81, 0xf0, 0x31,
690xe6, 0xfa, 0x77, 0xed, 0x31, 0x06, 0x31, 0x5b, 0x54, 0x8a, 0x9f, 0x30, 0x68, 0xdb, 0xe2,
700x40, 0xf8, 0x4e, 0x73, 0xfa, 0xab, 0x74, 0x8b, 0x10, 0x58, 0x13, 0xdc, 0xd2, 0xe6, 0x78,
710xd1, 0x32, 0x2e, 0x8a, 0x9f, 0x2c, 0x58, 0x06, 0x48, 0x27, 0xc5, 0xa9, 0x5e, 0x81, 0x47,
720x89, 0x46, 0x21, 0x91, 0x03, 0x70, 0xa4, 0x3e, 0x88, 0x9c, 0xda, 0x33, 0x0a, 0xce, 0xbc,
730x8b, 0x8e, 0xcf, 0x9f, 0xd3, 0x71, 0x80, 0x43, 0xcf, 0x6b, 0xa9, 0x51, 0x83, 0x76, 0x30,
740x82, 0xc5, 0x6a, 0x85, 0x39, 0x11, 0x50, 0x1a, 0x82, 0xdc, 0x1e, 0x1c, 0xd5, 0x7d, 0xa9,
750x71, 0x99, 0x33, 0x47, 0x19, 0x97, 0xb3, 0x5a, 0xb1, 0xdf, 0xed, 0xa4, 0xf2, 0xe6, 0x26,
760x84, 0xa2, 0x28, 0x9a, 0x9e, 0xdf, 0xa6, 0x6a, 0xf4, 0xd6, 0xfc, 0x2e, 0x5b, 0x9d, 0x1a,
770x2a, 0x27, 0x68, 0xfb, 0xc1, 0x83, 0x21, 0x4b, 0x90, 0xe0, 0x36, 0xdd, 0x5b, 0x31, 0x42,
780x55, 0xa0, 0x13, 0xf7, 0xd0, 0x89, 0x53, 0x71, 0x99, 0x57, 0x09, 0x29, 0xc5, 0xf3, 0x21,
790xf8, 0x37, 0x2f, 0x40, 0xf3, 0xd4, 0xaf, 0x16, 0x08, 0x36, 0x02, 0xfc, 0x77, 0xc5, 0x8b,
800x04, 0x90, 0x56, 0xb9, 0xc9, 0x67, 0x9a, 0x99, 0xe8, 0x00, 0xd3, 0x86, 0xff, 0x97, 0x2d,
810x08, 0xe9, 0xb7, 0xb3, 0x91, 0xbc, 0xdf, 0x45, 0xc6, 0xed, 0x0f, 0x8c, 0x4c, 0x1e, 0xe6,
820x5b, 0x6e, 0x38, 0x30, 0xe4, 0xaa, 0xe3, 0x95, 0xde, 0xb9, 0xe4, 0x9a, 0xf5, 0xb2, 0x55,
830x9a, 0x87, 0x9b, 0xf6, 0x6a, 0xb2, 0xf2, 0x77, 0x9a, 0x31, 0xf4, 0x7a, 0x31, 0xd1, 0x1d,
840x04, 0xc0, 0x7c, 0x32, 0xa2, 0x9e, 0x9a, 0xf5, 0x62, 0xf8, 0x27, 0x8d, 0xbf, 0x51, 0xff,
850xd3, 0xdf, 0x64, 0x37, 0x3f, 0x2a, 0x6f, 0x76, 0x3a, 0x7d, 0x77, 0x06, 0x9e, 0x77, 0x7f,
860x5e, 0xeb, 0x32, 0x51, 0xf9, 0x16, 0x66, 0x9a, 0x09, 0xf3, 0xb0, 0x08, 0xa4, 0x70, 0x96,
870x46, 0x30, 0xff, 0xda, 0x4f, 0xe9, 0x1b, 0xed, 0x8d, 0xf8, 0x74, 0x1f, 0x31, 0x92, 0xb3,
880x73, 0x17, 0x36, 0xdb, 0x91, 0x30, 0xd6, 0x88, 0x55, 0x6b, 0x34, 0x77, 0x87, 0x7a, 0xe7,
890xee, 0x06, 0xc6, 0x1c, 0x8c, 0x19, 0x0c, 0x48, 0x46, 0x23, 0x5e, 0x9c, 0x07, 0x5c, 0xbf,
900xb4, 0x7e, 0xd6, 0x4f, 0x74, 0x9c, 0xe2, 0xc5, 0x50, 0x8b, 0xc5, 0x8b, 0x15, 0x90, 0x60,
910x62, 0x57, 0x29, 0xd0, 0x13, 0x43, 0xa1, 0x80, 0x88, 0x91, 0x00, 0x44, 0xc7, 0x4d, 0x19,
920x86, 0xcc, 0x2f, 0x2a, 0x75, 0x5a, 0xfc, 0xeb, 0x97, 0x2a, 0x70, 0xe3, 0x78, 0xd8, 0x91,
930xb0, 0x4f, 0x99, 0x07, 0xa3, 0x95, 0xea, 0x24, 0x21, 0xd5, 0xde, 0x51, 0x20, 0x93, 0x27,
940x0a, 0x30, 0x73, 0xa8, 0xff, 0x8a, 0x97, 0xe9, 0xa7, 0x6a, 0x8e, 0x0d, 0xe8, 0xf0, 0xdf,
950xec, 0xea, 0xb4, 0x6c, 0x1d, 0x39, 0x2a, 0x62, 0x2d, 0x3d, 0x5a, 0x8b, 0x65, 0xf8, 0x90,
960x05, 0x2e, 0x7e, 0x91, 0x2c, 0x78, 0xef, 0x8e, 0x7a, 0xc1, 0x2f, 0xac, 0x78, 0xee, 0xaf,
970x28, 0x45, 0x06, 0x4c, 0x26, 0xaf, 0x3b, 0xa2, 0xdb, 0xa3, 0x93, 0x06, 0xb5, 0x3c, 0xa5,
980xd8, 0xee, 0x8f, 0xaf, 0x25, 0xcc, 0x3f, 0x85, 0x68, 0x48, 0xa9, 0x62, 0xcc, 0x97, 0x8f,
990x7f, 0x2a, 0xea, 0xe0, 0x15, 0x0a, 0xad, 0x62, 0x07, 0xbd, 0x45, 0xf8, 0x41, 0xd8, 0x36,
1000xcb, 0x4c, 0xdb, 0x6e, 0xe6, 0x3a, 0xe7, 0xda, 0x15, 0xe9, 0x29, 0x1e, 0x12, 0x10, 0xa0,
1010x14, 0x2c, 0x0e, 0x3d, 0xf4, 0xbf, 0x39, 0x41, 0x92, 0x75, 0x0b, 0x25, 0x7b, 0xa3, 0xce,
1020x39, 0x9c, 0x15, 0x64, 0xc8, 0xfa, 0x3d, 0xef, 0x73, 0x27, 0xfe, 0x26, 0x2e, 0xce, 0xda,
1030x6e, 0xfd, 0x71, 0x8e, 0xdd, 0xfe, 0x76, 0xee, 0xdc, 0x12, 0x5c, 0x02, 0xc5, 0x3a, 0x4e,
1040x4e, 0x4f, 0xbf, 0xca, 0x40, 0x15, 0xc7, 0x6e, 0x8d, 0x41, 0xf1, 0x10, 0xe0, 0x4f, 0x7e,
1050x97, 0x7f, 0x1c, 0xae, 0x47, 0x8e, 0x6b, 0xb1, 0x25, 0x31, 0xb0, 0x73, 0xc7, 0x1b, 0x97,
1060x79, 0xf9, 0x80, 0xd3, 0x66, 0x22, 0x30, 0x07, 0x74, 0x1e, 0xe4, 0xd0, 0x80, 0x21, 0xd6,
1070xee, 0x6b, 0x6c, 0x4f, 0xbf, 0xf5, 0xb7, 0xd9, 0x09, 0x87, 0x2f, 0xa9, 0x14, 0xbe, 0x27,
1080xd9, 0x72, 0x50, 0x01, 0xd4, 0x13, 0x73, 0xa6, 0xa7, 0x51, 0x02, 0x75, 0x25, 0xe1, 0xb3,
1090x45, 0x34, 0x7d, 0xa8, 0x8e, 0xeb, 0xf3, 0x16, 0x49, 0xcb, 0x4f, 0x8c, 0xa1, 0xb9, 0x36,
1100x85, 0x39, 0x75, 0x5d, 0x08, 0x00, 0xae, 0xeb, 0xf6, 0xea, 0xd7, 0x13, 0x3a, 0x21, 0x5a,
1110x5f, 0x30, 0x84, 0x52, 0x26, 0x95, 0xc9, 0x14, 0xf2, 0x57, 0x55, 0x6b, 0xb1, 0x10, 0xc2,
1120xe1, 0xbd, 0x3b, 0x51, 0xc0, 0xb7, 0x55, 0x4c, 0x71, 0x12, 0x26, 0xc7, 0x0d, 0xf9, 0x51,
1130xa4, 0x38, 0x02, 0x05, 0x7f, 0xb8, 0xf1, 0x72, 0x4b, 0xbf, 0x71, 0x89, 0x14, 0xf3, 0x77,
1140x38, 0xd9, 0x71, 0x24, 0xf3, 0x00, 0x11, 0xa1, 0xd8, 0xd4, 0x69, 0x27, 0x08, 0x37, 0x35,
1150xc9, 0x11, 0x9d, 0x90, 0x1c, 0x0e, 0xe7, 0x1c, 0xff, 0x2d, 0x1e, 0xe8, 0x92, 0xe1, 0x18,
1160x10, 0x95, 0x7c, 0xe0, 0x80, 0xf4, 0x96, 0x43, 0x21, 0xf9, 0x75, 0x21, 0x64, 0x38, 0xdd,
1170x9f, 0x1e, 0x95, 0x16, 0xda, 0x56, 0x1d, 0x4f, 0x9a, 0x53, 0xb2, 0xe2, 0xe4, 0x18, 0xcb,
1180x6b, 0x1a, 0x65, 0xeb, 0x56, 0xc6, 0x3b, 0xe5, 0xfe, 0xd8, 0x26, 0x3f, 0x3a, 0x84, 0x59,
1190x72, 0x66, 0xa2, 0xf3, 0x75, 0xff, 0xfb, 0x60, 0xb3, 0x22, 0xad, 0x3f, 0x2d, 0x6b, 0xf9,
1200xeb, 0xea, 0x05, 0x7c, 0xd8, 0x8f, 0x6d, 0x2c, 0x98, 0x9e, 0x2b, 0x93, 0xf1, 0x5e, 0x46,
1210xf0, 0x87, 0x49, 0x29, 0x73, 0x68, 0xd7, 0x7f, 0xf9, 0xf0, 0xe5, 0x7d, 0xdb, 0x1d, 0x75,
1220x19, 0xf3, 0xc4, 0x58, 0x9b, 0x17, 0x88, 0xa8, 0x92, 0xe0, 0xbe, 0xbd, 0x8b, 0x1d, 0x8d,
1230x9f, 0x56, 0x76, 0xad, 0xaf, 0x29, 0xe2, 0xd9, 0xd5, 0x52, 0xf6, 0xb5, 0x56, 0x35, 0x57,
1240x3a, 0xc8, 0xe1, 0x56, 0x43, 0x19, 0x94, 0xd3, 0x04, 0x9b, 0x6d, 0x35, 0xd8, 0x0b, 0x5f,
1250x4d, 0x19, 0x8e, 0xec, 0xfa, 0x64, 0x91, 0x0a, 0x72, 0x20, 0x2b, 0xbc, 0x1a, 0x4a, 0xfe,
1260x8b, 0xfd, 0xbb, 0xed, 0x1b, 0x23, 0xea, 0xad, 0x72, 0x82, 0xa1, 0x29, 0x99, 0x71, 0xbd,
1270xf0, 0x95, 0xc1, 0x03, 0xdd, 0x7b, 0xc2, 0xb2, 0x3c, 0x28, 0x54, 0xd3, 0x68, 0xa4, 0x72,
1280xc8, 0x66, 0x96, 0xe0, 0xd1, 0xd8, 0x7f, 0xf8, 0xd1, 0x26, 0x2b, 0xf7, 0xad, 0xba, 0x55,
1290xca, 0x15, 0xb9, 0x32, 0xc3, 0xe5, 0x88, 0x97, 0x8e, 0x5c, 0xfb, 0x92, 0x25, 0x8b, 0xbf,
1300xa2, 0x45, 0x55, 0x7a, 0xa7, 0x6f, 0x8b, 0x57, 0x5b, 0xcf, 0x0e, 0xcb, 0x1d, 0xfb, 0x20,
1310x82, 0x77, 0xa8, 0x8c, 0xcc, 0x16, 0xce, 0x1d, 0xfa, 0xde, 0xcc, 0x0b, 0x62, 0xfe, 0xcc,
1320xe1, 0xb7, 0xf0, 0xc3, 0x81, 0x64, 0x73, 0x40, 0xa0, 0xc2, 0x4d, 0x89, 0x11, 0x75, 0x33,
1330x55, 0x33, 0x8d, 0xe8, 0x4a, 0xfd, 0xea, 0x6e, 0x30, 0x0b, 0xd7, 0x31, 0x2c, 0xde, 0x47,
1340xe3, 0xbf, 0xf8, 0x55, 0x42, 0xe2, 0x7f, 0x59, 0xe5, 0x17, 0xef, 0x99, 0x34, 0x69, 0x91,
1350xb1, 0x23, 0x8e, 0x20, 0x87, 0x2d, 0xa8, 0xfe, 0xd5, 0x8a, 0xf3, 0x84, 0x3a, 0xf0, 0x37,
1360xe4, 0x09, 0x00, 0x54, 0xee, 0x67, 0x49, 0x93, 0xe4, 0x81, 0x70, 0xe3, 0x90, 0x4d, 0xef,
1370xfe, 0x41, 0xb7, 0x99, 0x7b, 0xc1, 0x83, 0xba, 0x62, 0x12, 0x6f, 0x7d, 0xde, 0x6b, 0xaf,
1380xda, 0x16, 0xf9, 0x55, 0x51, 0xee, 0xa6, 0x0c, 0x2b, 0x02, 0xa3, 0xfd, 0x8d, 0xfb, 0x30,
1390x17, 0xe4, 0x6f, 0xdf, 0x36, 0x71, 0xc4, 0xca, 0x87, 0x25, 0x48, 0xb0, 0x47, 0xec, 0xea,
1400xb4, 0xbf, 0xa5, 0x4d, 0x9b, 0x9f, 0x02, 0x93, 0xc4, 0xe3, 0xe4, 0xe8, 0x42, 0x2d, 0x68,
1410x81, 0x15, 0x0a, 0xeb, 0x84, 0x5b, 0xd6, 0xa8, 0x74, 0xfb, 0x7d, 0x1d, 0xcb, 0x2c, 0xda,
1420x46, 0x2a, 0x76, 0x62, 0xce, 0xbc, 0x5c, 0x9e, 0x8b, 0xe7, 0xcf, 0xbe, 0x78, 0xf5, 0x7c,
1430xeb, 0xb3, 0x3a, 0x9c, 0xaa, 0x6f, 0xcc, 0x72, 0xd1, 0x59, 0xf2, 0x11, 0x23, 0xd6, 0x3f,
1440x48, 0xd1, 0xb7, 0xce, 0xb0, 0xbf, 0xcb, 0xea, 0x80, 0xde, 0x57, 0xd4, 0x5e, 0x97, 0x2f,
1450x75, 0xd1, 0x50, 0x8e, 0x80, 0x2c, 0x66, 0x79, 0xbf, 0x72, 0x4b, 0xbd, 0x8a, 0x81, 0x6c,
1460xd3, 0xe1, 0x01, 0xdc, 0xd2, 0x15, 0x26, 0xc5, 0x36, 0xda, 0x2c, 0x1a, 0xc0, 0x27, 0x94,
1470xed, 0xb7, 0x9b, 0x85, 0x0b, 0x5e, 0x80, 0x97, 0xc5, 0xec, 0x4f, 0xec, 0x88, 0x5d, 0x50,
1480x07, 0x35, 0x47, 0xdc, 0x0b, 0x3b, 0x3d, 0xdd, 0x60, 0xaf, 0xa8, 0x5d, 0x81, 0x38, 0x24,
1490x25, 0x5d, 0x5c, 0x15, 0xd1, 0xde, 0xb3, 0xab, 0xec, 0x05, 0x69, 0xef, 0x83, 0xed, 0x57,
1500x54, 0xb8, 0x64, 0x64, 0x11, 0x16, 0x32, 0x69, 0xda, 0x9f, 0x2d, 0x7f, 0x36, 0xbb, 0x44,
1510x5a, 0x34, 0xe8, 0x7f, 0xbf, 0x03, 0xeb, 0x00, 0x7f, 0x59, 0x68, 0x22, 0x79, 0xcf, 0x73,
1520x6c, 0x2c, 0x29, 0xa7, 0xa1, 0x5f, 0x38, 0xa1, 0x1d, 0xf0, 0x20, 0x53, 0xe0, 0x1a, 0x63,
1530x14, 0x58, 0x71, 0x10, 0xaa, 0x08, 0x0c, 0x3e, 0x16, 0x1a, 0x60, 0x22, 0x82, 0x7f, 0xba,
1540xa4, 0x43, 0xa0, 0xd0, 0xac, 0x1b, 0xd5, 0x6b, 0x64, 0xb5, 0x14, 0x93, 0x31, 0x9e, 0x53,
1550x50, 0xd0, 0x57, 0x66, 0xee, 0x5a, 0x4f, 0xfb, 0x03, 0x2a, 0x69, 0x58, 0x76, 0xf1, 0x83,
1560xf7, 0x4e, 0xba, 0x8c, 0x42, 0x06, 0x60, 0x5d, 0x6d, 0xce, 0x60, 0x88, 0xae, 0xa4, 0xc3,
1570xf1, 0x03, 0xa5, 0x4b, 0x98, 0xa1, 0xff, 0x67, 0xe1, 0xac, 0xa2, 0xb8, 0x62, 0xd7, 0x6f,
1580xa0, 0x31, 0xb4, 0xd2, 0x77, 0xaf, 0x21, 0x10, 0x06, 0xc6, 0x9a, 0xff, 0x1d, 0x09, 0x17,
1590x0e, 0x5f, 0xf1, 0xaa, 0x54, 0x34, 0x4b, 0x45, 0x8a, 0x87, 0x63, 0xa6, 0xdc, 0xf9, 0x24,
1600x30, 0x67, 0xc6, 0xb2, 0xd6, 0x61, 0x33, 0x69, 0xee, 0x50, 0x61, 0x57, 0x28, 0xe7, 0x7e,
1610xee, 0xec, 0x3a, 0x5a, 0x73, 0x4e, 0xa8, 0x8d, 0xe4, 0x18, 0xea, 0xec, 0x41, 0x64, 0xc8,
1620xe2, 0xe8, 0x66, 0xb6, 0x2d, 0xb6, 0xfb, 0x6a, 0x6c, 0x16, 0xb3, 0xdd, 0x46, 0x43, 0xb9,
1630x73, 0x00, 0x6a, 0x71, 0xed, 0x4e, 0x9d, 0x25, 0x1a, 0xc3, 0x3c, 0x4a, 0x95, 0x15, 0x99,
1640x35, 0x81, 0x14, 0x02, 0xd6, 0x98, 0x9b, 0xec, 0xd8, 0x23, 0x3b, 0x84, 0x29, 0xaf, 0x0c,
1650x99, 0x83, 0xa6, 0x9a, 0x34, 0x4f, 0xfa, 0xe8, 0xd0, 0x3c, 0x4b, 0xd0, 0xfb, 0xb6, 0x68,
1660xb8, 0x9e, 0x8f, 0xcd, 0xf7, 0x60, 0x2d, 0x7a, 0x22, 0xe5, 0x7d, 0xab, 0x65, 0x1b, 0x95,
1670xa7, 0xa8, 0x7f, 0xb6, 0x77, 0x47, 0x7b, 0x5f, 0x8b, 0x12, 0x72, 0xd0, 0xd4, 0x91, 0xef,
1680xde, 0x19, 0x50, 0x3c, 0xa7, 0x8b, 0xc4, 0xa9, 0xb3, 0x23, 0xcb, 0x76, 0xe6, 0x81, 0xf0,
1690xc1, 0x04, 0x8f, 0xa3, 0xb8, 0x54, 0x5b, 0x97, 0xac, 0x19, 0xff, 0x3f, 0x55, 0x27, 0x2f,
1700xe0, 0x1d, 0x42, 0x9b, 0x57, 0xfc, 0x4b, 0x4e, 0x0f, 0xce, 0x98, 0xa9, 0x43, 0x57, 0x03,
1710xbd, 0xe7, 0xc8, 0x94, 0xdf, 0x6e, 0x36, 0x73, 0x32, 0xb4, 0xef, 0x2e, 0x85, 0x7a, 0x6e,
1720xfc, 0x6c, 0x18, 0x82, 0x75, 0x35, 0x90, 0x07, 0xf3, 0xe4, 0x9f, 0x3e, 0xdc, 0x68, 0xf3,
1730xb5, 0xf3, 0x19, 0x80, 0x92, 0x06, 0x99, 0xa2, 0xe8, 0x6f, 0xff, 0x2e, 0x7f, 0xae, 0x42,
1740xa4, 0x5f, 0xfb, 0xd4, 0x0e, 0x81, 0x2b, 0xc3, 0x04, 0xff, 0x2b, 0xb3, 0x74, 0x4e, 0x36,
1750x5b, 0x9c, 0x15, 0x00, 0xc6, 0x47, 0x2b, 0xe8, 0x8b, 0x3d, 0xf1, 0x9c, 0x03, 0x9a, 0x58,
1760x7f, 0x9b, 0x9c, 0xbf, 0x85, 0x49, 0x79, 0x35, 0x2e, 0x56, 0x7b, 0x41, 0x14, 0x39, 0x47,
1770x83, 0x26, 0xaa, 0x07, 0x89, 0x98, 0x11, 0x1b, 0x86, 0xe7, 0x73, 0x7a, 0xd8, 0x7d, 0x78,
1780x61, 0x53, 0xe9, 0x79, 0xf5, 0x36, 0x8d, 0x44, 0x92, 0x84, 0xf9, 0x13, 0x50, 0x58, 0x3b,
1790xa4, 0x6a, 0x36, 0x65, 0x49, 0x8e, 0x3c, 0x0e, 0xf1, 0x6f, 0xd2, 0x84, 0xc4, 0x7e, 0x8e,
1800x3f, 0x39, 0xae, 0x7c, 0x84, 0xf1, 0x63, 0x37, 0x8e, 0x3c, 0xcc, 0x3e, 0x44, 0x81, 0x45,
1810xf1, 0x4b, 0xb9, 0xed, 0x6b, 0x36, 0x5d, 0xbb, 0x20, 0x60, 0x1a, 0x0f, 0xa3, 0xaa, 0x55,
1820x77, 0x3a, 0xa9, 0xae, 0x37, 0x4d, 0xba, 0xb8, 0x86, 0x6b, 0xbc, 0x08, 0x50, 0xf6, 0xcc,
1830xa4, 0xbd, 0x1d, 0x40, 0x72, 0xa5, 0x86, 0xfa, 0xe2, 0x10, 0xae, 0x3d, 0x58, 0x4b, 0x97,
1840xf3, 0x43, 0x74, 0xa9, 0x9e, 0xeb, 0x21, 0xb7, 0x01, 0xa4, 0x86, 0x93, 0x97, 0xee, 0x2f,
1850x4f, 0x3b, 0x86, 0xa1, 0x41, 0x6f, 0x41, 0x26, 0x90, 0x78, 0x5c, 0x7f, 0x30, 0x38, 0x4b,
1860x3f, 0xaa, 0xec, 0xed, 0x5c, 0x6f, 0x0e, 0xad, 0x43, 0x87, 0xfd, 0x93, 0x35, 0xe6, 0x01,
1870xef, 0x41, 0x26, 0x90, 0x99, 0x9e, 0xfb, 0x19, 0x5b, 0xad, 0xd2, 0x91, 0x8a, 0xe0, 0x46,
1880xaf, 0x65, 0xfa, 0x4f, 0x84, 0xc1, 0xa1, 0x2d, 0xcf, 0x45, 0x8b, 0xd3, 0x85, 0x50, 0x55,
1890x7c, 0xf9, 0x67, 0x88, 0xd4, 0x4e, 0xe9, 0xd7, 0x6b, 0x61, 0x54, 0xa1, 0xa4, 0xa6, 0xa2,
1900xc2, 0xbf, 0x30, 0x9c, 0x40, 0x9f, 0x5f, 0xd7, 0x69, 0x2b, 0x24, 0x82, 0x5e, 0xd9, 0xd6,
1910xa7, 0x12, 0x54, 0x1a, 0xf7, 0x55, 0x9f, 0x76, 0x50, 0xa9, 0x95, 0x84, 0xe6, 0x6b, 0x6d,
1920xb5, 0x96, 0x54, 0xd6, 0xcd, 0xb3, 0xa1, 0x9b, 0x46, 0xa7, 0x94, 0x4d, 0xc4, 0x94, 0xb4,
1930x98, 0xe3, 0xe1, 0xe2, 0x34, 0xd5, 0x33, 0x16, 0x07, 0x54, 0xcd, 0xb7, 0x77, 0x53, 0xdb,
1940x4f, 0x4d, 0x46, 0x9d, 0xe9, 0xd4, 0x9c, 0x8a, 0x36, 0xb6, 0xb8, 0x38, 0x26, 0x6c, 0x0e,
1950xff, 0x9c, 0x1b, 0x43, 0x8b, 0x80, 0xcc, 0xb9, 0x3d, 0xda, 0xc7, 0xf1, 0x8a, 0xf2, 0x6d,
1960xb8, 0xd7, 0x74, 0x2f, 0x7e, 0x1e, 0xb7, 0xd3, 0x4a, 0xb4, 0xac, 0xfc, 0x79, 0x48, 0x6c,
1970xbc, 0x96, 0xb6, 0x94, 0x46, 0x57, 0x2d, 0xb0, 0xa3, 0xfc, 0x1e, 0xb9, 0x52, 0x60, 0x85,
1980x2d, 0x41, 0xd0, 0x43, 0x01, 0x1e, 0x1c, 0xd5, 0x7d, 0xfc, 0xf3, 0x96, 0x0d, 0xc7, 0xcb,
1990x2a, 0x29, 0x9a, 0x93, 0xdd, 0x88, 0x2d, 0x37, 0x5d, 0xaa, 0xfb, 0x49, 0x68, 0xa0, 0x9c,
2000x50, 0x86, 0x7f, 0x68, 0x56, 0x57, 0xf9, 0x79, 0x18, 0x39, 0xd4, 0xe0, 0x01, 0x84, 0x33,
2010x61, 0xca, 0xa5, 0xd2, 0xd6, 0xe4, 0xc9, 0x8a, 0x4a, 0x23, 0x44, 0x4e, 0xbc, 0xf0, 0xdc,
2020x24, 0xa1, 0xa0, 0xc4, 0xe2, 0x07, 0x3c, 0x10, 0xc4, 0xb5, 0x25, 0x4b, 0x65, 0x63, 0xf4,
2030x80, 0xe7, 0xcf, 0x61, 0xb1, 0x71, 0x82, 0x21, 0x87, 0x2c, 0xf5, 0x91, 0x00, 0x32, 0x0c,
2040xec, 0xa9, 0xb5, 0x9a, 0x74, 0x85, 0xe3, 0x36, 0x8f, 0x76, 0x4f, 0x9c, 0x6d, 0xce, 0xbc,
2050xad, 0x0a, 0x4b, 0xed, 0x76, 0x04, 0xcb, 0xc3, 0xb9, 0x33, 0x9e, 0x01, 0x93, 0x96, 0x69,
2060x7d, 0xc5, 0xa2, 0x45, 0x79, 0x9b, 0x04, 0x5c, 0x84, 0x09, 0xed, 0x88, 0x43, 0xc7, 0xab,
2070x93, 0x14, 0x26, 0xa1, 0x40, 0xb5, 0xce, 0x4e, 0xbf, 0x2a, 0x42, 0x85, 0x3e, 0x2c, 0x3b,
2080x54, 0xe8, 0x12, 0x1f, 0x0e, 0x97, 0x59, 0xb2, 0x27, 0x89, 0xfa, 0xf2, 0xdf, 0x8e, 0x68,
2090x59, 0xdc, 0x06, 0xbc, 0xb6, 0x85, 0x0d, 0x06, 0x22, 0xec, 0xb1, 0xcb, 0xe5, 0x04, 0xe6,
2100x3d, 0xb3, 0xb0, 0x41, 0x73, 0x08, 0x3f, 0x3c, 0x58, 0x86, 0x63, 0xeb, 0x50, 0xee, 0x1d,
2110x2c, 0x37, 0x74, 0xa9, 0xd3, 0x18, 0xa3, 0x47, 0x6e, 0x93, 0x54, 0xad, 0x0a, 0x5d, 0xb8,
2120x2a, 0x55, 0x5d, 0x78, 0xf6, 0xee, 0xbe, 0x8e, 0x3c, 0x76, 0x69, 0xb9, 0x40, 0xc2, 0x34,
2130xec, 0x2a, 0xb9, 0xed, 0x7e, 0x20, 0xe4, 0x8d, 0x00, 0x38, 0xc7, 0xe6, 0x8f, 0x44, 0xa8,
2140x86, 0xce, 0xeb, 0x2a, 0xe9, 0x90, 0xf1, 0x4c, 0xdf, 0x32, 0xfb, 0x73, 0x1b, 0x6d, 0x92,
2150x1e, 0x95, 0xfe, 0xb4, 0xdb, 0x65, 0xdf, 0x4d, 0x23, 0x54, 0x89, 0x48, 0xbf, 0x4a, 0x2e,
2160x70, 0xd6, 0xd7, 0x62, 0xb4, 0x33, 0x29, 0xb1, 0x3a, 0x33, 0x4c, 0x23, 0x6d, 0xa6, 0x76,
2170xa5, 0x21, 0x63, 0x48, 0xe6, 0x90, 0x5d, 0xed, 0x90, 0x95, 0x0b, 0x7a, 0x84, 0xbe, 0xb8,
2180x0d, 0x5e, 0x63, 0x0c, 0x62, 0x26, 0x4c, 0x14, 0x5a, 0xb3, 0xac, 0x23, 0xa4, 0x74, 0xa7,
2190x6f, 0x33, 0x30, 0x05, 0x60, 0x01, 0x42, 0xa0, 0x28, 0xb7, 0xee, 0x19, 0x38, 0xf1, 0x64,
2200x80, 0x82, 0x43, 0xe1, 0x41, 0x27, 0x1f, 0x1f, 0x90, 0x54, 0x7a, 0xd5, 0x23, 0x2e, 0xd1,
2210x3d, 0xcb, 0x28, 0xba, 0x58, 0x7f, 0xdc, 0x7c, 0x91, 0x24, 0xe9, 0x28, 0x51, 0x83, 0x6e,
2220xc5, 0x56, 0x21, 0x42, 0xed, 0xa0, 0x56, 0x22, 0xa1, 0x40, 0x80, 0x6b, 0xa8, 0xf7, 0x94,
2230xca, 0x13, 0x6b, 0x0c, 0x39, 0xd9, 0xfd, 0xe9, 0xf3, 0x6f, 0xa6, 0x9e, 0xfc, 0x70, 0x8a,
2240xb3, 0xbc, 0x59, 0x3c, 0x1e, 0x1d, 0x6c, 0xf9, 0x7c, 0xaf, 0xf9, 0x88, 0x71, 0x95, 0xeb,
2250x57, 0x00, 0xbd, 0x9f, 0x8c, 0x4f, 0xe1, 0x24, 0x83, 0xc5, 0x22, 0xea, 0xfd, 0xd3, 0x0c,
2260xe2, 0x17, 0x18, 0x7c, 0x6a, 0x4c, 0xde, 0x77, 0xb4, 0x53, 0x9b, 0x4c, 0x81, 0xcd, 0x23,
2270x60, 0xaa, 0x0e, 0x25, 0x73, 0x9c, 0x02, 0x79, 0x32, 0x30, 0xdf, 0x74, 0xdf, 0x75, 0x19,
2280xf4, 0xa5, 0x14, 0x5c, 0xf7, 0x7a, 0xa8, 0xa5, 0x91, 0x84, 0x7c, 0x60, 0x03, 0x06, 0x3b,
2290xcd, 0x50, 0xb6, 0x27, 0x9c, 0xfe, 0xb1, 0xdd, 0xcc, 0xd3, 0xb0, 0x59, 0x24, 0xb2, 0xca,
2300xe2, 0x1c, 0x81, 0x22, 0x9d, 0x07, 0x8f, 0x8e, 0xb9, 0xbe, 0x4e, 0xfa, 0xfc, 0x39, 0x65,
2310xba, 0xbf, 0x9d, 0x12, 0x37, 0x5e, 0x97, 0x7e, 0xf3, 0x89, 0xf5, 0x5d, 0xf5, 0xe3, 0x09,
2320x8c, 0x62, 0xb5, 0x20, 0x9d, 0x0c, 0x53, 0x8a, 0x68, 0x1b, 0xd2, 0x8f, 0x75, 0x17, 0x5d,
2330xd4, 0xe5, 0xda, 0x75, 0x62, 0x19, 0x14, 0x6a, 0x26, 0x2d, 0xeb, 0xf8, 0xaf, 0x37, 0xf0,
2340x6c, 0xa4, 0x55, 0xb1, 0xbc, 0xe2, 0x33, 0xc0, 0x9a, 0xca, 0xb0, 0x11, 0x49, 0x4f, 0x68,
2350x9b, 0x3b, 0x6b, 0x3c, 0xcc, 0x13, 0xf6, 0xc7, 0x85, 0x61, 0x68, 0x42, 0xae, 0xbb, 0xdd,
2360xcd, 0x45, 0x16, 0x29, 0x1d, 0xea, 0xdb, 0xc8, 0x03, 0x94, 0x3c, 0xee, 0x4f, 0x82, 0x11,
2370xc3, 0xec, 0x28, 0xbd, 0x97, 0x05, 0x99, 0xde, 0xd7, 0xbb, 0x5e, 0x22, 0x1f, 0xd4, 0xeb,
2380x64, 0xd9, 0x92, 0xd9, 0x85, 0xb7, 0x6a, 0x05, 0x6a, 0xe4, 0x24, 0x41, 0xf1, 0xcd, 0xf0,
2390xd8, 0x3f, 0xf8, 0x9e, 0x0e, 0xcd, 0x0b, 0x7a, 0x70, 0x6b, 0x5a, 0x75, 0x0a, 0x6a, 0x33,
2400x88, 0xec, 0x17, 0x75, 0x08, 0x70, 0x10, 0x2f, 0x24, 0xcf, 0xc4, 0xe9, 0x42, 0x00, 0x61,
2410x94, 0xca, 0x1f, 0x3a, 0x76, 0x06, 0xfa, 0xd2, 0x48, 0x81, 0xf0, 0x77, 0x60, 0x03, 0x45,
2420xd9, 0x61, 0xf4, 0xa4, 0x6f, 0x3d, 0xd9, 0x30, 0xc3, 0x04, 0x6b, 0x54, 0x2a, 0xb7, 0xec,
2430x3b, 0xf4, 0x4b, 0xf5, 0x68, 0x52, 0x26, 0xce, 0xff, 0x5d, 0x19, 0x91, 0xa0, 0xa3, 0xa5,
2440xa9, 0xb1, 0xe0, 0x23, 0xc4, 0x0a, 0x77, 0x4d, 0xf9, 0x51, 0x20, 0xa3, 0xa5, 0xa9, 0xb1,
2450xc1, 0x00, 0x82, 0x86, 0x8e, 0x7f, 0x5d, 0x19, 0x91, 0xa0, 0xa3, 0xc4, 0xeb, 0x54, 0x0b,
2460x75, 0x68, 0x52, 0x07, 0x8c, 0x9a, 0x97, 0x8d, 0x79, 0x70, 0x62, 0x46, 0xef, 0x5c, 0x1b,
2470x95, 0x89, 0x71, 0x41, 0xe1, 0x21, 0xa1, 0xa1, 0xa1, 0xc0, 0x02, 0x67, 0x4c, 0x1a, 0xb6,
2480xcf, 0xfd, 0x78, 0x53, 0x24, 0xab, 0xb5, 0xc9, 0xf1, 0x60, 0x23, 0xa5, 0xc8, 0x12, 0x87,
2490x6d, 0x58, 0x13, 0x85, 0x88, 0x92, 0x87, 0x6d, 0x58, 0x32, 0xc7, 0x0c, 0x9a, 0x97, 0xac,
2500xda, 0x36, 0xee, 0x5e, 0x3e, 0xdf, 0x1d, 0xb8, 0xf2, 0x66, 0x2f, 0xbd, 0xf8, 0x72, 0x47,
2510xed, 0x58, 0x13, 0x85, 0x88, 0x92, 0x87, 0x8c, 0x7b, 0x55, 0x09, 0x90, 0xa2, 0xc6, 0xef,
2520x3d, 0xf8, 0x53, 0x24, 0xab, 0xd4, 0x2a, 0xb7, 0xec, 0x5a, 0x36, 0xee, 0x5e, 0x3e, 0xdf,
2530x3c, 0xfa, 0x76, 0x4f, 0xfd, 0x59, 0x30, 0xe2, 0x46, 0xef, 0x3d, 0xf8, 0x53, 0x05, 0x69,
2540x31, 0xc1, 0x00, 0x82, 0x86, 0x8e, 0x7f, 0x5d, 0x19, 0xb0, 0xe2, 0x27, 0xcc, 0xfb, 0x74,
2550x4b, 0x14, 0x8b, 0x94, 0x8b, 0x75, 0x68, 0x33, 0xc5, 0x08, 0x92, 0x87, 0x8c, 0x9a, 0xb6,
2560xcf, 0x1c, 0xba, 0xd7, 0x0d, 0x98, 0xb2, 0xe6, 0x2f, 0xdc, 0x1b, 0x95, 0x89, 0x71, 0x60,
2570x23, 0xc4, 0x0a, 0x96, 0x8f, 0x9c, 0xba, 0xf6, 0x6e, 0x3f, 0xfc, 0x5b, 0x15, 0xa8, 0xd2,
2580x26, 0xaf, 0xbd, 0xf8, 0x72, 0x66, 0x2f, 0xdc, 0x1b, 0xb4, 0xcb, 0x14, 0x8b, 0x94, 0xaa,
2590xb7, 0xcd, 0xf9, 0x51, 0x01, 0x80, 0x82, 0x86, 0x6f, 0x3d, 0xd9, 0x30, 0xe2, 0x27, 0xcc,
2600xfb, 0x74, 0x4b, 0x14, 0xaa, 0xb7, 0xcd, 0xf9, 0x70, 0x43, 0x04, 0x6b, 0x35, 0xc9, 0xf1,
2610x60, 0x23, 0xa5, 0xc8, 0xf3, 0x45, 0x08, 0x92, 0x87, 0x6d, 0x58, 0x32, 0xe6, 0x2f, 0xbd,
2620xf8, 0x72, 0x66, 0x4e, 0x1e, 0xbe, 0xfe, 0x7e, 0x7e, 0x7e, 0x5f, 0x1d, 0x99, 0x91, 0xa0,
2630xa3, 0xc4, 0x0a, 0x77, 0x4d, 0x18, 0x93, 0xa4, 0xab, 0xd4, 0x0b, 0x75, 0x49, 0x10, 0xa2,
2640xc6, 0xef, 0x3d, 0xf8, 0x53, 0x24, 0xab, 0xb5, 0xe8, 0x33, 0xe4, 0x4a, 0x16, 0xae, 0xde,
2650x1f, 0xbc, 0xdb, 0x15, 0xa8, 0xb3, 0xc5, 0x08, 0x73, 0x45, 0xe9, 0x31, 0xc1, 0xe1, 0x21,
2660xa1, 0xa1, 0xa1, 0xc0, 0x02, 0x86, 0x6f, 0x5c, 0x3a, 0xd7, 0x0d, 0x98, 0x93, 0xa4, 0xca,
2670x16, 0xae, 0xde, 0x1f, 0x9d, 0x99, 0xb0, 0xe2, 0x46, 0xef, 0x3d, 0xf8, 0x72, 0x47, 0x0c,
2680x9a, 0xb6, 0xcf, 0xfd, 0x59, 0x11, 0xa0, 0xa3, 0xa5, 0xc8, 0xf3, 0x45, 0x08, 0x92, 0x87,
2690x6d, 0x39, 0xf0, 0x43, 0x04, 0x8a, 0x96, 0xae, 0xde, 0x3e, 0xdf, 0x1d, 0x99, 0x91, 0xa0,
2700xc2, 0x06, 0x6f, 0x3d, 0xf8, 0x72, 0x47, 0x0c, 0x9a, 0x97, 0x8d, 0x98, 0x93, 0x85, 0x88,
2710x73, 0x45, 0xe9, 0x31, 0xe0, 0x23, 0xa5, 0xa9, 0xd0, 0x03, 0x84, 0x8a, 0x96, 0xae, 0xde,
2720x1f, 0xbc, 0xdb, 0x15, 0xa8, 0xd2, 0x26, 0xce, 0xff, 0x5d, 0x19, 0x91, 0x81, 0x80, 0x82,
2730x67, 0x2d, 0xd8, 0x13, 0xa4, 0xab, 0xd4, 0x0b, 0x94, 0xaa, 0xb7, 0xcd, 0xf9, 0x51, 0x20,
2740xa3, 0xa5, 0xc8, 0xf3, 0x45, 0xe9, 0x50, 0x22, 0xc6, 0xef, 0x5c, 0x3a, 0xd7, 0x0d, 0x98,
2750x93, 0x85, 0x88, 0x73, 0x64, 0x4a, 0xf7, 0x4d, 0xf9, 0x51, 0x20, 0xa3, 0xc4, 0x0a, 0x96,
2760xae, 0xde, 0x3e, 0xfe, 0x7e, 0x7e, 0x7e, 0x5f, 0x3c, 0xfa, 0x76, 0x4f, 0xfd, 0x78, 0x72,
2770x66, 0x2f, 0xbd, 0xd9, 0x30, 0xc3, 0xe5, 0x48, 0x12, 0x87, 0x8c, 0x7b, 0x55, 0x28, 0xd2,
2780x07, 0x8c, 0x9a, 0x97, 0xac, 0xda, 0x17, 0x8d, 0x79, 0x51, 0x20, 0xa3, 0xc4, 0xeb, 0x54,
2790x0b, 0x94, 0x8b, 0x94, 0xaa, 0xd6, 0x2e, 0xbf, 0xfc, 0x5b, 0x15, 0xa8, 0xd2, 0x26, 0xaf,
2800xdc, 0x1b, 0xb4, 0xea, 0x37, 0xec, 0x3b, 0xf4, 0x6a, 0x37, 0xcd, 0x18, 0x93, 0x85, 0x69,
2810x31, 0xc1, 0xe1, 0x40, 0xe3, 0x25, 0xc8, 0x12, 0x87, 0x8c, 0x9a, 0xb6, 0xcf, 0xfd, 0x59,
2820x11, 0xa0, 0xc2, 0x06, 0x8e, 0x7f, 0x5d, 0x38, 0xf2, 0x47, 0x0c, 0x7b, 0x74, 0x6a, 0x37,
2830xec, 0x5a, 0x36, 0xee, 0x3f, 0xfc, 0x7a, 0x76, 0x4f, 0x1c, 0x9b, 0x95, 0x89, 0x71, 0x41,
2840x00, 0x63, 0x44, 0xeb, 0x54, 0x2a, 0xd6, 0x0f, 0x9c, 0xba, 0xd7, 0x0d, 0x98, 0x93, 0x85,
2850x69, 0x31, 0xc1, 0x00, 0x82, 0x86, 0x8e, 0x9e, 0xbe, 0xdf, 0x3c, 0xfa, 0x57, 0x2c, 0xda,
2860x36, 0xee, 0x3f, 0xfc, 0x5b, 0x15, 0x89, 0x71, 0x41, 0x00, 0x82, 0x86, 0x8e, 0x7f, 0x5d,
2870x38, 0xf2, 0x47, 0xed, 0x58, 0x13, 0xa4, 0xca, 0xf7, 0x4d, 0xf9, 0x51, 0x01, 0x80, 0x63,
2880x44, 0xeb, 0x54, 0x2a, 0xd6, 0x2e, 0xbf, 0xdd, 0x19, 0x91, 0xa0, 0xa3, 0xa5, 0xa9, 0xb1,
2890xe0, 0x42, 0x06, 0x8e, 0x7f, 0x5d, 0x19, 0x91, 0xa0, 0xa3, 0xc4, 0x0a, 0x96, 0x8f, 0x7d,
2900x78, 0x72, 0x47, 0x0c, 0x7b, 0x74, 0x6a, 0x56, 0x2e, 0xde, 0x1f, 0xbc, 0xfa, 0x57, 0x0d,
2910x79, 0x51, 0x01, 0x61, 0x21, 0xa1, 0xc0, 0xe3, 0x25, 0xa9, 0xb1, 0xc1, 0xe1, 0x40, 0x02,
2920x67, 0x4c, 0x1a, 0x97, 0x8d, 0x98, 0x93, 0xa4, 0xab, 0xd4, 0x2a, 0xd6, 0x0f, 0x9c, 0x9b,
2930xb4, 0xcb, 0x14, 0xaa, 0xb7, 0xcd, 0xf9, 0x51, 0x20, 0xa3, 0xc4, 0xeb, 0x35, 0xc9, 0xf1,
2940x60, 0x42, 0x06, 0x8e, 0x7f, 0x7c, 0x7a, 0x76, 0x6e, 0x3f, 0xfc, 0x7a, 0x76, 0x6e, 0x5e,
2950x3e, 0xfe, 0x7e, 0x5f, 0x3c, 0xdb, 0x15, 0x89, 0x71, 0x41, 0xe1, 0x21, 0xc0, 0xe3, 0x44,
2960xeb, 0x54, 0x2a, 0xb7, 0xcd, 0xf9, 0x70, 0x62, 0x27, 0xad, 0xd8, 0x32, 0xc7, 0x0c, 0x7b,
2970x74, 0x4b, 0x14, 0xaa, 0xb7, 0xec, 0x3b, 0xd5, 0x28, 0xd2, 0x07, 0x6d, 0x39, 0xd1, 0x20,
2980xc2, 0xe7, 0x4c, 0x1a, 0x97, 0x8d, 0x98, 0xb2, 0xc7, 0x0c, 0x59, 0x28, 0xf3, 0x9b };
299
300// clang-format off
diff --git a/drivers/serial.h b/drivers/serial.h
new file mode 100644
index 000000000..d9c2a69e9
--- /dev/null
+++ b/drivers/serial.h
@@ -0,0 +1,46 @@
1/* Copyright 2021 QMK
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#pragma once
18
19#include <stdint.h>
20#include <stdbool.h>
21
22#include <transactions.h>
23
24// initiator is transaction start side
25void soft_serial_initiator_init(void);
26// target is interrupt accept side
27void soft_serial_target_init(void);
28
29// initiator result
30#define TRANSACTION_END 0
31#define TRANSACTION_NO_RESPONSE 0x1
32#define TRANSACTION_DATA_ERROR 0x2
33#define TRANSACTION_TYPE_ERROR 0x4
34int soft_serial_transaction(int sstd_index);
35
36// target status
37// *SSTD_t.status has
38// initiator:
39// TRANSACTION_END
40// or TRANSACTION_NO_RESPONSE
41// or TRANSACTION_DATA_ERROR
42// target:
43// TRANSACTION_DATA_ERROR
44// or TRANSACTION_ACCEPTED
45#define TRANSACTION_ACCEPTED 0x8
46int soft_serial_get_and_clean_status(int sstd_index);