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/chibios/serial.c52
-rw-r--r--drivers/chibios/serial.h62
-rw-r--r--drivers/chibios/serial_usart.c352
-rw-r--r--drivers/chibios/serial_usart.h40
-rw-r--r--drivers/chibios/serial_usart_duplex.c261
-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.c355
-rw-r--r--drivers/haptic/haptic.h81
-rw-r--r--drivers/haptic/solenoid.c1
-rw-r--r--drivers/lcd/st7565.c496
-rw-r--r--drivers/lcd/st7565.h219
-rw-r--r--drivers/led/apa102.c (renamed from drivers/apa102/apa102.c)0
-rw-r--r--drivers/led/apa102.h (renamed from drivers/apa102/apa102.h)0
-rw-r--r--drivers/led/aw20216.c141
-rw-r--r--drivers/led/aw20216.h253
-rw-r--r--drivers/led/issi/is31fl3218.c (renamed from drivers/issi/is31fl3218.c)0
-rw-r--r--drivers/led/issi/is31fl3218.h (renamed from drivers/issi/is31fl3218.h)0
-rw-r--r--drivers/led/issi/is31fl3731-simple.c (renamed from drivers/issi/is31fl3731-simple.c)0
-rw-r--r--drivers/led/issi/is31fl3731-simple.h (renamed from drivers/issi/is31fl3731-simple.h)3
-rw-r--r--drivers/led/issi/is31fl3731.c (renamed from drivers/issi/is31fl3731.c)0
-rw-r--r--drivers/led/issi/is31fl3731.h (renamed from drivers/issi/is31fl3731.h)3
-rw-r--r--drivers/led/issi/is31fl3733.c (renamed from drivers/issi/is31fl3733.c)0
-rw-r--r--drivers/led/issi/is31fl3733.h (renamed from drivers/issi/is31fl3733.h)3
-rw-r--r--drivers/led/issi/is31fl3736.c (renamed from drivers/issi/is31fl3736.c)0
-rw-r--r--drivers/led/issi/is31fl3736.h (renamed from drivers/issi/is31fl3736.h)3
-rw-r--r--drivers/led/issi/is31fl3737.c (renamed from drivers/issi/is31fl3737.c)43
-rw-r--r--drivers/led/issi/is31fl3737.h (renamed from drivers/issi/is31fl3737.h)3
-rw-r--r--drivers/led/issi/is31fl3741.c (renamed from drivers/issi/is31fl3741.c)0
-rw-r--r--drivers/led/issi/is31fl3741.h (renamed from drivers/issi/is31fl3741.h)3
-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
49 files changed, 6104 insertions, 1083 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/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..ea4473791 100644
--- a/drivers/chibios/serial_usart.c
+++ b/drivers/chibios/serial_usart.c
@@ -16,190 +16,300 @@
16 16
17#include "serial_usart.h" 17#include "serial_usart.h"
18 18
19#ifndef USE_GPIOV1 19#if defined(SERIAL_USART_CONFIG)
20// The default PAL alternate modes are used to signal that the pins are used for USART 20static SerialConfig serial_config = SERIAL_USART_CONFIG;
21# ifndef SERIAL_USART_TX_PAL_MODE 21#else
22# define SERIAL_USART_TX_PAL_MODE 7 22static SerialConfig serial_config = {
23 .speed = (SERIAL_USART_SPEED), /* speed - mandatory */
24 .cr1 = (SERIAL_USART_CR1),
25 .cr2 = (SERIAL_USART_CR2),
26# if !defined(SERIAL_USART_FULL_DUPLEX)
27 .cr3 = ((SERIAL_USART_CR3) | USART_CR3_HDSEL) /* activate half-duplex mode */
28# else
29 .cr3 = (SERIAL_USART_CR3)
23# endif 30# endif
31};
24#endif 32#endif
25 33
26#ifndef SERIAL_USART_DRIVER 34static SerialDriver* serial_driver = &SERIAL_USART_DRIVER;
27# define SERIAL_USART_DRIVER SD1
28#endif
29
30#ifdef SOFT_SERIAL_PIN
31# define SERIAL_USART_TX_PIN SOFT_SERIAL_PIN
32#endif
33
34static inline msg_t sdWriteHalfDuplex(SerialDriver* driver, uint8_t* data, uint8_t size) {
35 msg_t ret = sdWrite(driver, data, size);
36 35
37 // Half duplex requires us to read back the data we just wrote - just throw it away 36static inline bool react_to_transactions(void);
38 uint8_t dump[size]; 37static inline bool __attribute__((nonnull)) receive(uint8_t* destination, const size_t size);
39 sdRead(driver, dump, size); 38static inline bool __attribute__((nonnull)) send(const uint8_t* source, const size_t size);
39static inline int initiate_transaction(uint8_t sstd_index);
40static inline void usart_clear(void);
40 41
41 return ret; 42/**
43 * @brief Clear the receive input queue.
44 */
45static inline void usart_clear(void) {
46 osalSysLock();
47 bool volatile queue_not_empty = !iqIsEmptyI(&serial_driver->iqueue);
48 osalSysUnlock();
49
50 while (queue_not_empty) {
51 osalSysLock();
52 /* Hard reset the input queue. */
53 iqResetI(&serial_driver->iqueue);
54 osalSysUnlock();
55 /* Allow pending interrupts to preempt.
56 * Do not merge the lock/unlock blocks into one
57 * or the code will not work properly.
58 * The empty read adds a tiny amount of delay. */
59 (void)queue_not_empty;
60 osalSysLock();
61 queue_not_empty = !iqIsEmptyI(&serial_driver->iqueue);
62 osalSysUnlock();
63 }
42} 64}
43#undef sdWrite
44#define sdWrite sdWriteHalfDuplex
45 65
46static inline msg_t sdWriteTimeoutHalfDuplex(SerialDriver* driver, uint8_t* data, uint8_t size, uint32_t timeout) { 66/**
47 msg_t ret = sdWriteTimeout(driver, data, size, timeout); 67 * @brief Blocking send of buffer with timeout.
48 68 *
49 // Half duplex requires us to read back the data we just wrote - just throw it away 69 * @return true Send success.
50 uint8_t dump[size]; 70 * @return false Send failed.
51 sdReadTimeout(driver, dump, size, timeout); 71 */
72static inline bool send(const uint8_t* source, const size_t size) {
73 bool success = (size_t)sdWriteTimeout(serial_driver, source, size, TIME_MS2I(SERIAL_USART_TIMEOUT)) == size;
74
75#if !defined(SERIAL_USART_FULL_DUPLEX)
76 if (success) {
77 /* Half duplex fills the input queue with the data we wrote - just throw it away.
78 Under the right circumstances (e.g. bad cables paired with high baud rates)
79 less bytes can be present in the input queue, therefore a timeout is needed. */
80 uint8_t dump[size];
81 return receive(dump, size);
82 }
83#endif
52 84
53 return ret; 85 return success;
54} 86}
55#undef sdWriteTimeout
56#define sdWriteTimeout sdWriteTimeoutHalfDuplex
57 87
58static inline void sdClear(SerialDriver* driver) { 88/**
59 while (sdGetTimeout(driver, TIME_IMMEDIATE) != MSG_TIMEOUT) { 89 * @brief Blocking receive of size * bytes with timeout.
60 // Do nothing with the data 90 *
61 } 91 * @return true Receive success.
92 * @return false Receive failed.
93 */
94static inline bool receive(uint8_t* destination, const size_t size) {
95 bool success = (size_t)sdReadTimeout(serial_driver, destination, size, TIME_MS2I(SERIAL_USART_TIMEOUT)) == size;
96 return success;
62} 97}
63 98
64static SerialConfig sdcfg = { 99#if !defined(SERIAL_USART_FULL_DUPLEX)
65 (SERIAL_USART_SPEED), // speed - mandatory
66 (SERIAL_USART_CR1), // CR1
67 (SERIAL_USART_CR2), // CR2
68 (SERIAL_USART_CR3) // CR3
69};
70
71void handle_soft_serial_slave(void);
72 100
73/* 101/**
74 * This thread runs on the slave and responds to transactions initiated 102 * @brief Initiate pins for USART peripheral. Half-duplex configuration.
75 * by the master
76 */ 103 */
77static THD_WORKING_AREA(waSlaveThread, 2048); 104__attribute__((weak)) void usart_init(void) {
78static THD_FUNCTION(SlaveThread, arg) { 105# if defined(MCU_STM32)
79 (void)arg; 106# if defined(USE_GPIOV1)
80 chRegSetThreadName("slave_transport"); 107 palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_STM32_ALTERNATE_OPENDRAIN);
108# else
109 palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE(SERIAL_USART_TX_PAL_MODE) | PAL_STM32_OTYPE_OPENDRAIN);
110# endif
81 111
82 while (true) { 112# if defined(USART_REMAP)
83 handle_soft_serial_slave(); 113 USART_REMAP;
84 } 114# endif
115# else
116# pragma message "usart_init: MCU Familiy not supported by default, please supply your own init code by implementing usart_init() in your keyboard files."
117# endif
85} 118}
86 119
87__attribute__((weak)) void usart_init(void) {
88#if defined(USE_GPIOV1)
89 palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_STM32_ALTERNATE_OPENDRAIN);
90#else 120#else
91 palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE(SERIAL_USART_TX_PAL_MODE) | PAL_STM32_OTYPE_OPENDRAIN);
92#endif
93 121
94#if defined(USART_REMAP) 122/**
123 * @brief Initiate pins for USART peripheral. Full-duplex configuration.
124 */
125__attribute__((weak)) void usart_init(void) {
126# if defined(MCU_STM32)
127# if defined(USE_GPIOV1)
128 palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_STM32_ALTERNATE_PUSHPULL);
129 palSetLineMode(SERIAL_USART_RX_PIN, PAL_MODE_INPUT);
130# else
131 palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE(SERIAL_USART_TX_PAL_MODE) | PAL_STM32_OTYPE_PUSHPULL | PAL_STM32_OSPEED_HIGHEST);
132 palSetLineMode(SERIAL_USART_RX_PIN, PAL_MODE_ALTERNATE(SERIAL_USART_RX_PAL_MODE) | PAL_STM32_OTYPE_PUSHPULL | PAL_STM32_OSPEED_HIGHEST);
133# endif
134
135# if defined(USART_REMAP)
95 USART_REMAP; 136 USART_REMAP;
96#endif 137# endif
138# else
139# pragma message "usart_init: MCU Familiy not supported by default, please supply your own init code by implementing usart_init() in your keyboard files."
140# endif
97} 141}
98 142
99void usart_master_init(void) { 143#endif
100 usart_init();
101 144
102 sdcfg.cr3 |= USART_CR3_HDSEL; 145/**
103 sdStart(&SERIAL_USART_DRIVER, &sdcfg); 146 * @brief Overridable master specific initializations.
147 */
148__attribute__((weak, nonnull)) void usart_master_init(SerialDriver** driver) {
149 (void)driver;
150 usart_init();
104} 151}
105 152
106void usart_slave_init(void) { 153/**
154 * @brief Overridable slave specific initializations.
155 */
156__attribute__((weak, nonnull)) void usart_slave_init(SerialDriver** driver) {
157 (void)driver;
107 usart_init(); 158 usart_init();
159}
108 160
109 sdcfg.cr3 |= USART_CR3_HDSEL; 161/**
110 sdStart(&SERIAL_USART_DRIVER, &sdcfg); 162 * @brief This thread runs on the slave and responds to transactions initiated
163 * by the master.
164 */
165static THD_WORKING_AREA(waSlaveThread, 1024);
166static THD_FUNCTION(SlaveThread, arg) {
167 (void)arg;
168 chRegSetThreadName("usart_tx_rx");
111 169
112 // Start transport thread 170 while (true) {
113 chThdCreateStatic(waSlaveThread, sizeof(waSlaveThread), HIGHPRIO, SlaveThread, NULL); 171 if (!react_to_transactions()) {
172 /* Clear the receive queue, to start with a clean slate.
173 * Parts of failed transactions or spurious bytes could still be in it. */
174 usart_clear();
175 }
176 }
114} 177}
115 178
116static SSTD_t* Transaction_table = NULL; 179/**
117static uint8_t Transaction_table_size = 0; 180 * @brief Slave specific initializations.
181 */
182void soft_serial_target_init(void) {
183 usart_slave_init(&serial_driver);
118 184
119void soft_serial_initiator_init(SSTD_t* sstd_table, int sstd_table_size) { 185 sdStart(serial_driver, &serial_config);
120 Transaction_table = sstd_table;
121 Transaction_table_size = (uint8_t)sstd_table_size;
122 186
123 usart_master_init(); 187 /* Start transport thread. */
188 chThdCreateStatic(waSlaveThread, sizeof(waSlaveThread), HIGHPRIO, SlaveThread, NULL);
124} 189}
125 190
126void soft_serial_target_init(SSTD_t* sstd_table, int sstd_table_size) { 191/**
127 Transaction_table = sstd_table; 192 * @brief React to transactions started by the master.
128 Transaction_table_size = (uint8_t)sstd_table_size; 193 */
194static inline bool react_to_transactions(void) {
195 /* Wait until there is a transaction for us. */
196 uint8_t sstd_index = (uint8_t)sdGet(serial_driver);
129 197
130 usart_slave_init(); 198 /* Sanity check that we are actually responding to a valid transaction. */
131} 199 if (sstd_index >= NUM_TOTAL_TRANSACTIONS) {
200 return false;
201 }
132 202
133void handle_soft_serial_slave(void) { 203 split_transaction_desc_t* trans = &split_transaction_table[sstd_index];
134 uint8_t sstd_index = sdGet(&SERIAL_USART_DRIVER); // first chunk is always transaction id
135 SSTD_t* trans = &Transaction_table[sstd_index];
136 204
137 // Always write back the sstd_index as part of a basic handshake 205 /* Send back the handshake which is XORed as a simple checksum,
206 to signal that the slave is ready to receive possible transaction buffers */
138 sstd_index ^= HANDSHAKE_MAGIC; 207 sstd_index ^= HANDSHAKE_MAGIC;
139 sdWrite(&SERIAL_USART_DRIVER, &sstd_index, sizeof(sstd_index)); 208 if (!send(&sstd_index, sizeof(sstd_index))) {
209 *trans->status = TRANSACTION_DATA_ERROR;
210 return false;
211 }
140 212
213 /* Receive transaction buffer from the master. If this transaction requires it.*/
141 if (trans->initiator2target_buffer_size) { 214 if (trans->initiator2target_buffer_size) {
142 sdRead(&SERIAL_USART_DRIVER, trans->initiator2target_buffer, trans->initiator2target_buffer_size); 215 if (!receive(split_trans_initiator2target_buffer(trans), trans->initiator2target_buffer_size)) {
216 *trans->status = TRANSACTION_DATA_ERROR;
217 return false;
218 }
143 } 219 }
144 220
145 if (trans->target2initiator_buffer_size) { 221 /* Allow any slave processing to occur. */
146 sdWrite(&SERIAL_USART_DRIVER, trans->target2initiator_buffer, trans->target2initiator_buffer_size); 222 if (trans->slave_callback) {
223 trans->slave_callback(trans->initiator2target_buffer_size, split_trans_initiator2target_buffer(trans), trans->initiator2target_buffer_size, split_trans_target2initiator_buffer(trans));
147 } 224 }
148 225
149 if (trans->status) { 226 /* Send transaction buffer to the master. If this transaction requires it. */
150 *trans->status = TRANSACTION_ACCEPTED; 227 if (trans->target2initiator_buffer_size) {
228 if (!send(split_trans_target2initiator_buffer(trans), trans->target2initiator_buffer_size)) {
229 *trans->status = TRANSACTION_DATA_ERROR;
230 return false;
231 }
151 } 232 }
233
234 *trans->status = TRANSACTION_ACCEPTED;
235 return true;
152} 236}
153 237
154///////// 238/**
155// start transaction by initiator 239 * @brief Master specific initializations.
156// 240 */
157// int soft_serial_transaction(int sstd_index) 241void soft_serial_initiator_init(void) {
158// 242 usart_master_init(&serial_driver);
159// Returns: 243
160// TRANSACTION_END 244#if defined(MCU_STM32) && defined(SERIAL_USART_PIN_SWAP)
161// TRANSACTION_NO_RESPONSE 245 serial_config.cr2 |= USART_CR2_SWAP; // master has swapped TX/RX pins
162// 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) {
168 uint8_t sstd_index = index;
169#endif 246#endif
170 247
171 if (sstd_index > Transaction_table_size) return TRANSACTION_TYPE_ERROR; 248 sdStart(serial_driver, &serial_config);
172 SSTD_t* trans = &Transaction_table[sstd_index]; 249}
173 msg_t res = 0; 250
251/**
252 * @brief Start transaction from the master half to the slave half.
253 *
254 * @param index Transaction Table index of the transaction to start.
255 * @return int TRANSACTION_NO_RESPONSE in case of Timeout.
256 * TRANSACTION_TYPE_ERROR in case of invalid transaction index.
257 * TRANSACTION_END in case of success.
258 */
259int soft_serial_transaction(int index) {
260 /* Clear the receive queue, to start with a clean slate.
261 * Parts of failed transactions or spurious bytes could still be in it. */
262 usart_clear();
263 return initiate_transaction((uint8_t)index);
264}
265
266/**
267 * @brief Initiate transaction to slave half.
268 */
269static inline int initiate_transaction(uint8_t sstd_index) {
270 /* Sanity check that we are actually starting a valid transaction. */
271 if (sstd_index >= NUM_TOTAL_TRANSACTIONS) {
272 dprintln("USART: Illegal transaction Id.");
273 return TRANSACTION_TYPE_ERROR;
274 }
275
276 split_transaction_desc_t* trans = &split_transaction_table[sstd_index];
174 277
175 sdClear(&SERIAL_USART_DRIVER); 278 /* Transaction is not registered. Abort. */
279 if (!trans->status) {
280 dprintln("USART: Transaction not registered.");
281 return TRANSACTION_TYPE_ERROR;
282 }
176 283
177 // First chunk is always transaction id 284 /* Send transaction table index to the slave, which doubles as basic handshake token. */
178 sdWriteTimeout(&SERIAL_USART_DRIVER, &sstd_index, sizeof(sstd_index), TIME_MS2I(SERIAL_USART_TIMEOUT)); 285 if (!send(&sstd_index, sizeof(sstd_index))) {
286 dprintln("USART: Send Handshake failed.");
287 return TRANSACTION_TYPE_ERROR;
288 }
179 289
180 uint8_t sstd_index_shake = 0xFF; 290 uint8_t sstd_index_shake = 0xFF;
181 291
182 // Which we always read back first so that we can error out correctly 292 /* Which we always read back first so that we can error out correctly.
183 // - due to the half duplex limitations on return codes, we always have to read *something* 293 * - due to the half duplex limitations on return codes, we always have to read *something*.
184 // - without the read, write only transactions *always* succeed, even during the boot process where the slave is not ready 294 * - without the read, write only transactions *always* succeed, even during the boot process where the slave is not ready.
185 res = sdReadTimeout(&SERIAL_USART_DRIVER, &sstd_index_shake, sizeof(sstd_index_shake), TIME_MS2I(SERIAL_USART_TIMEOUT)); 295 */
186 if (res < 0 || (sstd_index_shake != (sstd_index ^ HANDSHAKE_MAGIC))) { 296 if (!receive(&sstd_index_shake, sizeof(sstd_index_shake)) || (sstd_index_shake != (sstd_index ^ HANDSHAKE_MAGIC))) {
187 dprintf("serial::usart_shake NO_RESPONSE\n"); 297 dprintln("USART: Handshake failed.");
188 return TRANSACTION_NO_RESPONSE; 298 return TRANSACTION_NO_RESPONSE;
189 } 299 }
190 300
301 /* Send transaction buffer to the slave. If this transaction requires it. */
191 if (trans->initiator2target_buffer_size) { 302 if (trans->initiator2target_buffer_size) {
192 res = sdWriteTimeout(&SERIAL_USART_DRIVER, trans->initiator2target_buffer, trans->initiator2target_buffer_size, TIME_MS2I(SERIAL_USART_TIMEOUT)); 303 if (!send(split_trans_initiator2target_buffer(trans), trans->initiator2target_buffer_size)) {
193 if (res < 0) { 304 dprintln("USART: Send failed.");
194 dprintf("serial::usart_transmit NO_RESPONSE\n");
195 return TRANSACTION_NO_RESPONSE; 305 return TRANSACTION_NO_RESPONSE;
196 } 306 }
197 } 307 }
198 308
309 /* Receive transaction buffer from the slave. If this transaction requires it. */
199 if (trans->target2initiator_buffer_size) { 310 if (trans->target2initiator_buffer_size) {
200 res = sdReadTimeout(&SERIAL_USART_DRIVER, trans->target2initiator_buffer, trans->target2initiator_buffer_size, TIME_MS2I(SERIAL_USART_TIMEOUT)); 311 if (!receive(split_trans_target2initiator_buffer(trans), trans->target2initiator_buffer_size)) {
201 if (res < 0) { 312 dprintln("USART: Receive failed.");
202 dprintf("serial::usart_receive NO_RESPONSE\n");
203 return TRANSACTION_NO_RESPONSE; 313 return TRANSACTION_NO_RESPONSE;
204 } 314 }
205 } 315 }
diff --git a/drivers/chibios/serial_usart.h b/drivers/chibios/serial_usart.h
index fee7b4d15..c64e15566 100644
--- a/drivers/chibios/serial_usart.h
+++ b/drivers/chibios/serial_usart.h
@@ -23,19 +23,45 @@
23#include <ch.h> 23#include <ch.h>
24#include <hal.h> 24#include <hal.h>
25 25
26#ifndef USART_CR1_M0 26#if !defined(SERIAL_USART_DRIVER)
27# define SERIAL_USART_DRIVER SD1
28#endif
29
30#if !defined(USE_GPIOV1)
31/* The default PAL alternate modes are used to signal that the pins are used for USART. */
32# if !defined(SERIAL_USART_TX_PAL_MODE)
33# define SERIAL_USART_TX_PAL_MODE 7
34# endif
35# if !defined(SERIAL_USART_RX_PAL_MODE)
36# define SERIAL_USART_RX_PAL_MODE 7
37# endif
38#endif
39
40#if defined(SOFT_SERIAL_PIN)
41# define SERIAL_USART_TX_PIN SOFT_SERIAL_PIN
42#endif
43
44#if !defined(SERIAL_USART_TX_PIN)
45# define SERIAL_USART_TX_PIN A9
46#endif
47
48#if !defined(SERIAL_USART_RX_PIN)
49# define SERIAL_USART_RX_PIN A10
50#endif
51
52#if !defined(USART_CR1_M0)
27# define USART_CR1_M0 USART_CR1_M // some platforms (f1xx) dont have this so 53# define USART_CR1_M0 USART_CR1_M // some platforms (f1xx) dont have this so
28#endif 54#endif
29 55
30#ifndef SERIAL_USART_CR1 56#if !defined(SERIAL_USART_CR1)
31# define SERIAL_USART_CR1 (USART_CR1_PCE | USART_CR1_PS | USART_CR1_M0) // parity enable, odd parity, 9 bit length 57# define SERIAL_USART_CR1 (USART_CR1_PCE | USART_CR1_PS | USART_CR1_M0) // parity enable, odd parity, 9 bit length
32#endif 58#endif
33 59
34#ifndef SERIAL_USART_CR2 60#if !defined(SERIAL_USART_CR2)
35# define SERIAL_USART_CR2 (USART_CR2_STOP_1) // 2 stop bits 61# define SERIAL_USART_CR2 (USART_CR2_STOP_1) // 2 stop bits
36#endif 62#endif
37 63
38#ifndef SERIAL_USART_CR3 64#if !defined(SERIAL_USART_CR3)
39# define SERIAL_USART_CR3 0 65# define SERIAL_USART_CR3 0
40#endif 66#endif
41 67
@@ -61,11 +87,11 @@
61 } while (0) 87 } while (0)
62#endif 88#endif
63 89
64#ifndef SELECT_SOFT_SERIAL_SPEED 90#if !defined(SELECT_SOFT_SERIAL_SPEED)
65# define SELECT_SOFT_SERIAL_SPEED 1 91# define SELECT_SOFT_SERIAL_SPEED 1
66#endif 92#endif
67 93
68#ifdef SERIAL_USART_SPEED 94#if defined(SERIAL_USART_SPEED)
69// Allow advanced users to directly set SERIAL_USART_SPEED 95// Allow advanced users to directly set SERIAL_USART_SPEED
70#elif SELECT_SOFT_SERIAL_SPEED == 0 96#elif SELECT_SOFT_SERIAL_SPEED == 0
71# define SERIAL_USART_SPEED 460800 97# define SERIAL_USART_SPEED 460800
@@ -83,7 +109,7 @@
83# error invalid SELECT_SOFT_SERIAL_SPEED value 109# error invalid SELECT_SOFT_SERIAL_SPEED value
84#endif 110#endif
85 111
86#ifndef SERIAL_USART_TIMEOUT 112#if !defined(SERIAL_USART_TIMEOUT)
87# define SERIAL_USART_TIMEOUT 100 113# define SERIAL_USART_TIMEOUT 100
88#endif 114#endif
89 115
diff --git a/drivers/chibios/serial_usart_duplex.c b/drivers/chibios/serial_usart_duplex.c
deleted file mode 100644
index cc9b889ac..000000000
--- a/drivers/chibios/serial_usart_duplex.c
+++ /dev/null
@@ -1,261 +0,0 @@
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 3 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 "serial_usart.h"
18
19#include <stdatomic.h>
20
21#if !defined(USE_GPIOV1)
22// The default PAL alternate modes are used to signal that the pins are used for USART
23# if !defined(SERIAL_USART_TX_PAL_MODE)
24# define SERIAL_USART_TX_PAL_MODE 7
25# endif
26# if !defined(SERIAL_USART_RX_PAL_MODE)
27# define SERIAL_USART_RX_PAL_MODE 7
28# endif
29#endif
30
31#if !defined(SERIAL_USART_DRIVER)
32# define SERIAL_USART_DRIVER UARTD1
33#endif
34
35#if !defined(SERIAL_USART_TX_PIN)
36# define SERIAL_USART_TX_PIN A9
37#endif
38
39#if !defined(SERIAL_USART_RX_PIN)
40# define SERIAL_USART_RX_PIN A10
41#endif
42
43#define SIGNAL_HANDSHAKE_RECEIVED 0x1
44
45void handle_transactions_slave(uint8_t sstd_index);
46static void receive_transaction_handshake(UARTDriver* uartp, uint16_t received_handshake);
47
48/*
49 * UART driver configuration structure. We use the blocking DMA enabled API and
50 * the rxchar callback to receive handshake tokens but only on the slave halve.
51 */
52// clang-format off
53static UARTConfig uart_config = {
54 .txend1_cb = NULL,
55 .txend2_cb = NULL,
56 .rxend_cb = NULL,
57 .rxchar_cb = NULL,
58 .rxerr_cb = NULL,
59 .timeout_cb = NULL,
60 .speed = (SERIAL_USART_SPEED),
61 .cr1 = (SERIAL_USART_CR1),
62 .cr2 = (SERIAL_USART_CR2),
63 .cr3 = (SERIAL_USART_CR3)
64};
65// clang-format on
66
67static SSTD_t* Transaction_table = NULL;
68static uint8_t Transaction_table_size = 0;
69static atomic_uint_least8_t handshake = 0xFF;
70static thread_reference_t tp_target = NULL;
71
72/*
73 * This callback is invoked when a character is received but the application
74 * was not ready to receive it, the character is passed as parameter.
75 * Receive transaction table index from initiator, which doubles as basic handshake token. */
76static void receive_transaction_handshake(UARTDriver* uartp, uint16_t received_handshake) {
77 /* Check if received handshake is not a valid transaction id.
78 * Please note that we can still catch a seemingly valid handshake
79 * i.e. a byte from a ongoing transfer which is in the allowed range.
80 * So this check mainly prevents any obviously wrong handshakes and
81 * subsequent wakeups of the receiving thread, which is a costly operation. */
82 if (received_handshake > Transaction_table_size) {
83 return;
84 }
85
86 handshake = (uint8_t)received_handshake;
87 chSysLockFromISR();
88 /* Wakeup receiving thread to start a transaction. */
89 chEvtSignalI(tp_target, (eventmask_t)SIGNAL_HANDSHAKE_RECEIVED);
90 chSysUnlockFromISR();
91}
92
93__attribute__((weak)) void usart_init(void) {
94#if defined(USE_GPIOV1)
95 palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_STM32_ALTERNATE_PUSHPULL);
96 palSetLineMode(SERIAL_USART_RX_PIN, PAL_MODE_INPUT);
97#else
98 palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE(SERIAL_USART_TX_PAL_MODE) | PAL_STM32_OTYPE_PUSHPULL | PAL_STM32_OSPEED_HIGHEST);
99 palSetLineMode(SERIAL_USART_RX_PIN, PAL_MODE_ALTERNATE(SERIAL_USART_RX_PAL_MODE) | PAL_STM32_OTYPE_PUSHPULL | PAL_STM32_OSPEED_HIGHEST);
100#endif
101}
102
103/*
104 * This thread runs on the slave half and reacts to transactions initiated from the master.
105 */
106static THD_WORKING_AREA(waSlaveThread, 1024);
107static THD_FUNCTION(SlaveThread, arg) {
108 (void)arg;
109 chRegSetThreadName("slave_usart_tx_rx");
110
111 while (true) {
112 /* We sleep as long as there is no handshake waiting for us. */
113 chEvtWaitAny((eventmask_t)SIGNAL_HANDSHAKE_RECEIVED);
114 handle_transactions_slave(handshake);
115 }
116}
117
118void soft_serial_target_init(SSTD_t* const sstd_table, int sstd_table_size) {
119 Transaction_table = sstd_table;
120 Transaction_table_size = (uint8_t)sstd_table_size;
121 usart_init();
122
123#if defined(USART_REMAP)
124 USART_REMAP;
125#endif
126
127 tp_target = chThdCreateStatic(waSlaveThread, sizeof(waSlaveThread), HIGHPRIO, SlaveThread, NULL);
128
129 // Start receiving handshake tokens on slave halve
130 uart_config.rxchar_cb = receive_transaction_handshake;
131 uartStart(&SERIAL_USART_DRIVER, &uart_config);
132}
133
134/**
135 * @brief React to transactions started by the master.
136 * This version uses duplex send and receive usart pheriphals and DMA backed transfers.
137 */
138void inline handle_transactions_slave(uint8_t sstd_index) {
139 size_t buffer_size = 0;
140 msg_t msg = 0;
141 SSTD_t* trans = &Transaction_table[sstd_index];
142
143 /* Send back the handshake which is XORed as a simple checksum,
144 to signal that the slave is ready to receive possible transaction buffers */
145 sstd_index ^= HANDSHAKE_MAGIC;
146 buffer_size = (size_t)sizeof(sstd_index);
147 msg = uartSendTimeout(&SERIAL_USART_DRIVER, &buffer_size, &sstd_index, TIME_MS2I(SERIAL_USART_TIMEOUT));
148
149 if (msg != MSG_OK) {
150 if (trans->status) {
151 *trans->status = TRANSACTION_NO_RESPONSE;
152 }
153 return;
154 }
155
156 /* Receive transaction buffer from the master. If this transaction requires it.*/
157 buffer_size = (size_t)trans->initiator2target_buffer_size;
158 if (buffer_size) {
159 msg = uartReceiveTimeout(&SERIAL_USART_DRIVER, &buffer_size, trans->initiator2target_buffer, TIME_MS2I(SERIAL_USART_TIMEOUT));
160 if (msg != MSG_OK) {
161 if (trans->status) {
162 *trans->status = TRANSACTION_NO_RESPONSE;
163 }
164 return;
165 }
166 }
167
168 /* Send transaction buffer to the master. If this transaction requires it. */
169 buffer_size = (size_t)trans->target2initiator_buffer_size;
170 if (buffer_size) {
171 msg = uartSendFullTimeout(&SERIAL_USART_DRIVER, &buffer_size, trans->target2initiator_buffer, TIME_MS2I(SERIAL_USART_TIMEOUT));
172 if (msg != MSG_OK) {
173 if (trans->status) {
174 *trans->status = TRANSACTION_NO_RESPONSE;
175 }
176 return;
177 }
178 }
179
180 if (trans->status) {
181 *trans->status = TRANSACTION_ACCEPTED;
182 }
183}
184
185void soft_serial_initiator_init(SSTD_t* const sstd_table, int sstd_table_size) {
186 Transaction_table = sstd_table;
187 Transaction_table_size = (uint8_t)sstd_table_size;
188 usart_init();
189
190#if defined(SERIAL_USART_PIN_SWAP)
191 uart_config.cr2 |= USART_CR2_SWAP; // master has swapped TX/RX pins
192#endif
193
194#if defined(USART_REMAP)
195 USART_REMAP;
196#endif
197
198 uartStart(&SERIAL_USART_DRIVER, &uart_config);
199}
200
201/**
202 * @brief Start transaction from the master to the slave.
203 * This version uses duplex send and receive usart pheriphals and DMA backed transfers.
204 *
205 * @param index Transaction Table index of the transaction to start.
206 * @return int TRANSACTION_NO_RESPONSE in case of Timeout.
207 * TRANSACTION_TYPE_ERROR in case of invalid transaction index.
208 * TRANSACTION_END in case of success.
209 */
210#if !defined(SERIAL_USE_MULTI_TRANSACTION)
211int soft_serial_transaction(void) {
212 uint8_t sstd_index = 0;
213#else
214int soft_serial_transaction(int index) {
215 uint8_t sstd_index = index;
216#endif
217
218 if (sstd_index > Transaction_table_size) {
219 return TRANSACTION_TYPE_ERROR;
220 }
221
222 SSTD_t* const trans = &Transaction_table[sstd_index];
223 msg_t msg = 0;
224 size_t buffer_size = (size_t)sizeof(sstd_index);
225
226 /* Send transaction table index to the slave, which doubles as basic handshake token. */
227 uartSendFullTimeout(&SERIAL_USART_DRIVER, &buffer_size, &sstd_index, TIME_MS2I(SERIAL_USART_TIMEOUT));
228
229 uint8_t sstd_index_shake = 0xFF;
230 buffer_size = (size_t)sizeof(sstd_index_shake);
231
232 /* Receive the handshake token from the slave. The token was XORed by the slave as a simple checksum.
233 If the tokens match, the master will start to send and receive possible transaction buffers. */
234 msg = uartReceiveTimeout(&SERIAL_USART_DRIVER, &buffer_size, &sstd_index_shake, TIME_MS2I(SERIAL_USART_TIMEOUT));
235 if (msg != MSG_OK || (sstd_index_shake != (sstd_index ^ HANDSHAKE_MAGIC))) {
236 dprintln("USART: Handshake Failed");
237 return TRANSACTION_NO_RESPONSE;
238 }
239
240 /* Send transaction buffer to the slave. If this transaction requires it. */
241 buffer_size = (size_t)trans->initiator2target_buffer_size;
242 if (buffer_size) {
243 msg = uartSendFullTimeout(&SERIAL_USART_DRIVER, &buffer_size, trans->initiator2target_buffer, TIME_MS2I(SERIAL_USART_TIMEOUT));
244 if (msg != MSG_OK) {
245 dprintln("USART: Send Failed");
246 return TRANSACTION_NO_RESPONSE;
247 }
248 }
249
250 /* Receive transaction buffer from the slave. If this transaction requires it. */
251 buffer_size = (size_t)trans->target2initiator_buffer_size;
252 if (buffer_size) {
253 msg = uartReceiveTimeout(&SERIAL_USART_DRIVER, &buffer_size, trans->target2initiator_buffer, TIME_MS2I(SERIAL_USART_TIMEOUT));
254 if (msg != MSG_OK) {
255 dprintln("USART: Receive Failed");
256 return TRANSACTION_NO_RESPONSE;
257 }
258 }
259
260 return TRANSACTION_END;
261}
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
deleted file mode 100644
index de3f40052..000000000
--- a/drivers/haptic/haptic.c
+++ /dev/null
@@ -1,355 +0,0 @@
1/* Copyright 2019 ishtob
2 * Driver for haptic feedback written for QMK
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17#include "haptic.h"
18#include "eeconfig.h"
19#include "progmem.h"
20#include "debug.h"
21#ifdef DRV2605L
22# include "DRV2605L.h"
23#endif
24#ifdef SOLENOID_ENABLE
25# include "solenoid.h"
26#endif
27
28haptic_config_t haptic_config;
29
30void haptic_init(void) {
31 debug_enable = 1; // Debug is ON!
32 if (!eeconfig_is_enabled()) {
33 eeconfig_init();
34 }
35 haptic_config.raw = eeconfig_read_haptic();
36#ifdef SOLENOID_ENABLE
37 solenoid_set_dwell(haptic_config.dwell);
38#endif
39 if ((haptic_config.raw == 0)
40#ifdef SOLENOID_ENABLE
41 || (haptic_config.dwell == 0)
42#endif
43 ) {
44 // this will be called, if the eeprom is not corrupt,
45 // but the previous firmware didn't have haptic enabled,
46 // or the previous firmware didn't have solenoid enabled,
47 // and the current one has solenoid enabled.
48 haptic_reset();
49 }
50#ifdef SOLENOID_ENABLE
51 solenoid_setup();
52 dprintf("Solenoid driver initialized\n");
53#endif
54#ifdef DRV2605L
55 DRV_init();
56 dprintf("DRV2605 driver initialized\n");
57#endif
58 eeconfig_debug_haptic();
59}
60
61void haptic_task(void) {
62#ifdef SOLENOID_ENABLE
63 solenoid_check();
64#endif
65}
66
67void eeconfig_debug_haptic(void) {
68 dprintf("haptic_config eprom\n");
69 dprintf("haptic_config.enable = %d\n", haptic_config.enable);
70 dprintf("haptic_config.mode = %d\n", haptic_config.mode);
71}
72
73void haptic_enable(void) {
74 haptic_config.enable = 1;
75 xprintf("haptic_config.enable = %u\n", haptic_config.enable);
76 eeconfig_update_haptic(haptic_config.raw);
77}
78
79void haptic_disable(void) {
80 haptic_config.enable = 0;
81 xprintf("haptic_config.enable = %u\n", haptic_config.enable);
82 eeconfig_update_haptic(haptic_config.raw);
83}
84
85void haptic_toggle(void) {
86 if (haptic_config.enable) {
87 haptic_disable();
88 } else {
89 haptic_enable();
90 }
91 eeconfig_update_haptic(haptic_config.raw);
92}
93
94void haptic_feedback_toggle(void) {
95 haptic_config.feedback++;
96 if (haptic_config.feedback >= HAPTIC_FEEDBACK_MAX) haptic_config.feedback = KEY_PRESS;
97 xprintf("haptic_config.feedback = %u\n", !haptic_config.feedback);
98 eeconfig_update_haptic(haptic_config.raw);
99}
100
101void haptic_buzz_toggle(void) {
102 bool buzz_stat = !haptic_config.buzz;
103 haptic_config.buzz = buzz_stat;
104 haptic_set_buzz(buzz_stat);
105}
106
107void haptic_mode_increase(void) {
108 uint8_t mode = haptic_config.mode + 1;
109#ifdef DRV2605L
110 if (haptic_config.mode >= drv_effect_max) {
111 mode = 1;
112 }
113#endif
114 haptic_set_mode(mode);
115}
116
117void haptic_mode_decrease(void) {
118 uint8_t mode = haptic_config.mode - 1;
119#ifdef DRV2605L
120 if (haptic_config.mode < 1) {
121 mode = (drv_effect_max - 1);
122 }
123#endif
124 haptic_set_mode(mode);
125}
126
127void haptic_dwell_increase(void) {
128#ifdef SOLENOID_ENABLE
129 int16_t next_dwell = ((int16_t)haptic_config.dwell) + SOLENOID_DWELL_STEP_SIZE;
130 if (haptic_config.dwell >= SOLENOID_MAX_DWELL) {
131 // if it's already at max, we wrap back to min
132 next_dwell = SOLENOID_MIN_DWELL;
133 } else if (next_dwell > SOLENOID_MAX_DWELL) {
134 // if we overshoot the max, then cap at max
135 next_dwell = SOLENOID_MAX_DWELL;
136 }
137 solenoid_set_dwell(next_dwell);
138#else
139 int16_t next_dwell = ((int16_t)haptic_config.dwell) + 1;
140#endif
141 haptic_set_dwell(next_dwell);
142}
143
144void haptic_dwell_decrease(void) {
145#ifdef SOLENOID_ENABLE
146 int16_t next_dwell = ((int16_t)haptic_config.dwell) - SOLENOID_DWELL_STEP_SIZE;
147 if (haptic_config.dwell <= SOLENOID_MIN_DWELL) {
148 // if it's already at min, we wrap to max
149 next_dwell = SOLENOID_MAX_DWELL;
150 } else if (next_dwell < SOLENOID_MIN_DWELL) {
151 // if we go below min, then we cap to min
152 next_dwell = SOLENOID_MIN_DWELL;
153 }
154 solenoid_set_dwell(next_dwell);
155#else
156 int16_t next_dwell = ((int16_t)haptic_config.dwell) - 1;
157#endif
158 haptic_set_dwell(next_dwell);
159}
160
161void haptic_reset(void) {
162 haptic_config.enable = true;
163 uint8_t feedback = HAPTIC_FEEDBACK_DEFAULT;
164 haptic_config.feedback = feedback;
165#ifdef DRV2605L
166 uint8_t mode = HAPTIC_MODE_DEFAULT;
167 haptic_config.mode = mode;
168#endif
169#ifdef SOLENOID_ENABLE
170 uint8_t dwell = SOLENOID_DEFAULT_DWELL;
171 haptic_config.dwell = dwell;
172 haptic_config.buzz = SOLENOID_DEFAULT_BUZZ;
173 solenoid_set_dwell(dwell);
174#else
175 // This is to trigger haptic_reset again, if solenoid is enabled in the future.
176 haptic_config.dwell = 0;
177 haptic_config.buzz = 0;
178#endif
179 eeconfig_update_haptic(haptic_config.raw);
180 xprintf("haptic_config.feedback = %u\n", haptic_config.feedback);
181 xprintf("haptic_config.mode = %u\n", haptic_config.mode);
182}
183
184void haptic_set_feedback(uint8_t feedback) {
185 haptic_config.feedback = feedback;
186 eeconfig_update_haptic(haptic_config.raw);
187 xprintf("haptic_config.feedback = %u\n", haptic_config.feedback);
188}
189
190void haptic_set_mode(uint8_t mode) {
191 haptic_config.mode = mode;
192 eeconfig_update_haptic(haptic_config.raw);
193 xprintf("haptic_config.mode = %u\n", haptic_config.mode);
194}
195
196void haptic_set_amplitude(uint8_t amp) {
197 haptic_config.amplitude = amp;
198 eeconfig_update_haptic(haptic_config.raw);
199 xprintf("haptic_config.amplitude = %u\n", haptic_config.amplitude);
200#ifdef DRV2605L
201 DRV_amplitude(amp);
202#endif
203}
204
205void haptic_set_buzz(uint8_t buzz) {
206 haptic_config.buzz = buzz;
207 eeconfig_update_haptic(haptic_config.raw);
208 xprintf("haptic_config.buzz = %u\n", haptic_config.buzz);
209}
210
211void haptic_set_dwell(uint8_t dwell) {
212 haptic_config.dwell = dwell;
213 eeconfig_update_haptic(haptic_config.raw);
214 xprintf("haptic_config.dwell = %u\n", haptic_config.dwell);
215}
216
217uint8_t haptic_get_mode(void) {
218 if (!haptic_config.enable) {
219 return false;
220 }
221 return haptic_config.mode;
222}
223
224uint8_t haptic_get_feedback(void) {
225 if (!haptic_config.enable) {
226 return false;
227 }
228 return haptic_config.feedback;
229}
230
231uint8_t haptic_get_dwell(void) {
232 if (!haptic_config.enable) {
233 return false;
234 }
235 return haptic_config.dwell;
236}
237
238void haptic_enable_continuous(void) {
239 haptic_config.cont = 1;
240 xprintf("haptic_config.cont = %u\n", haptic_config.cont);
241 eeconfig_update_haptic(haptic_config.raw);
242#ifdef DRV2605L
243 DRV_rtp_init();
244#endif
245}
246
247void haptic_disable_continuous(void) {
248 haptic_config.cont = 0;
249 xprintf("haptic_config.cont = %u\n", haptic_config.cont);
250 eeconfig_update_haptic(haptic_config.raw);
251#ifdef DRV2605L
252 DRV_write(DRV_MODE, 0x00);
253#endif
254}
255
256void haptic_toggle_continuous(void) {
257#ifdef DRV2605L
258 if (haptic_config.cont) {
259 haptic_disable_continuous();
260 } else {
261 haptic_enable_continuous();
262 }
263 eeconfig_update_haptic(haptic_config.raw);
264#endif
265}
266
267void haptic_cont_increase(void) {
268 uint8_t amp = haptic_config.amplitude + 10;
269 if (haptic_config.amplitude >= 120) {
270 amp = 120;
271 }
272 haptic_set_amplitude(amp);
273}
274
275void haptic_cont_decrease(void) {
276 uint8_t amp = haptic_config.amplitude - 10;
277 if (haptic_config.amplitude < 20) {
278 amp = 20;
279 }
280 haptic_set_amplitude(amp);
281}
282
283void haptic_play(void) {
284#ifdef DRV2605L
285 uint8_t play_eff = 0;
286 play_eff = haptic_config.mode;
287 DRV_pulse(play_eff);
288#endif
289#ifdef SOLENOID_ENABLE
290 solenoid_fire();
291#endif
292}
293
294bool process_haptic(uint16_t keycode, keyrecord_t *record) {
295 if (keycode == HPT_ON && record->event.pressed) {
296 haptic_enable();
297 }
298 if (keycode == HPT_OFF && record->event.pressed) {
299 haptic_disable();
300 }
301 if (keycode == HPT_TOG && record->event.pressed) {
302 haptic_toggle();
303 }
304 if (keycode == HPT_RST && record->event.pressed) {
305 haptic_reset();
306 }
307 if (keycode == HPT_FBK && record->event.pressed) {
308 haptic_feedback_toggle();
309 }
310 if (keycode == HPT_BUZ && record->event.pressed) {
311 haptic_buzz_toggle();
312 }
313 if (keycode == HPT_MODI && record->event.pressed) {
314 haptic_mode_increase();
315 }
316 if (keycode == HPT_MODD && record->event.pressed) {
317 haptic_mode_decrease();
318 }
319 if (keycode == HPT_DWLI && record->event.pressed) {
320 haptic_dwell_increase();
321 }
322 if (keycode == HPT_DWLD && record->event.pressed) {
323 haptic_dwell_decrease();
324 }
325 if (keycode == HPT_CONT && record->event.pressed) {
326 haptic_toggle_continuous();
327 }
328 if (keycode == HPT_CONI && record->event.pressed) {
329 haptic_cont_increase();
330 }
331 if (keycode == HPT_COND && record->event.pressed) {
332 haptic_cont_decrease();
333 }
334
335 if (haptic_config.enable) {
336 if (record->event.pressed) {
337 // keypress
338 if (haptic_config.feedback < 2) {
339 haptic_play();
340 }
341 } else {
342 // keyrelease
343 if (haptic_config.feedback > 0) {
344 haptic_play();
345 }
346 }
347 }
348 return true;
349}
350
351void haptic_shutdown(void) {
352#ifdef SOLENOID_ENABLE
353 solenoid_shutdown();
354#endif
355}
diff --git a/drivers/haptic/haptic.h b/drivers/haptic/haptic.h
deleted file mode 100644
index ba8e0d20b..000000000
--- a/drivers/haptic/haptic.h
+++ /dev/null
@@ -1,81 +0,0 @@
1/* Copyright 2019 ishtob
2 * Driver for haptic feedback written for QMK
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#pragma once
19#include <stdint.h>
20#include <stdbool.h>
21#include "quantum.h"
22#ifdef DRV2605L
23# include "DRV2605L.h"
24#endif
25
26#ifndef HAPTIC_FEEDBACK_DEFAULT
27# define HAPTIC_FEEDBACK_DEFAULT 0
28#endif
29#ifndef HAPTIC_MODE_DEFAULT
30# define HAPTIC_MODE_DEFAULT DRV_MODE_DEFAULT
31#endif
32
33/* EEPROM config settings */
34typedef union {
35 uint32_t raw;
36 struct {
37 bool enable : 1;
38 uint8_t feedback : 2;
39 uint8_t mode : 7;
40 bool buzz : 1;
41 uint8_t dwell : 7;
42 bool cont : 1;
43 uint8_t amplitude : 8;
44 uint8_t reserved : 5;
45 };
46} haptic_config_t;
47
48typedef enum HAPTIC_FEEDBACK {
49 KEY_PRESS,
50 KEY_PRESS_RELEASE,
51 KEY_RELEASE,
52 HAPTIC_FEEDBACK_MAX,
53} HAPTIC_FEEDBACK;
54
55bool process_haptic(uint16_t keycode, keyrecord_t *record);
56void haptic_init(void);
57void haptic_task(void);
58void eeconfig_debug_haptic(void);
59void haptic_enable(void);
60void haptic_disable(void);
61void haptic_toggle(void);
62void haptic_feedback_toggle(void);
63void haptic_mode_increase(void);
64void haptic_mode_decrease(void);
65void haptic_mode(uint8_t mode);
66void haptic_reset(void);
67void haptic_set_feedback(uint8_t feedback);
68void haptic_set_mode(uint8_t mode);
69void haptic_set_dwell(uint8_t dwell);
70void haptic_set_buzz(uint8_t buzz);
71void haptic_buzz_toggle(void);
72uint8_t haptic_get_mode(void);
73uint8_t haptic_get_feedback(void);
74void haptic_dwell_increase(void);
75void haptic_dwell_decrease(void);
76void haptic_toggle_continuous(void);
77void haptic_cont_increase(void);
78void haptic_cont_decrease(void);
79
80void haptic_play(void);
81void haptic_shutdown(void);
diff --git a/drivers/haptic/solenoid.c b/drivers/haptic/solenoid.c
index 3e61d5a17..25cf34465 100644
--- a/drivers/haptic/solenoid.c
+++ b/drivers/haptic/solenoid.c
@@ -18,6 +18,7 @@
18#include "timer.h" 18#include "timer.h"
19#include "solenoid.h" 19#include "solenoid.h"
20#include "haptic.h" 20#include "haptic.h"
21#include "gpio.h"
21 22
22bool solenoid_on = false; 23bool solenoid_on = false;
23bool solenoid_buzzing = false; 24bool solenoid_buzzing = false;
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/apa102/apa102.c b/drivers/led/apa102.c
index 7396dc3c5..7396dc3c5 100644
--- a/drivers/apa102/apa102.c
+++ b/drivers/led/apa102.c
diff --git a/drivers/apa102/apa102.h b/drivers/led/apa102.h
index 58cf020c1..58cf020c1 100644
--- a/drivers/apa102/apa102.h
+++ b/drivers/led/apa102.h
diff --git a/drivers/led/aw20216.c b/drivers/led/aw20216.c
new file mode 100644
index 000000000..c608c0ab4
--- /dev/null
+++ b/drivers/led/aw20216.c
@@ -0,0 +1,141 @@
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#define AW_PWM_REGISTER_COUNT 216
47
48#ifndef AW_SCALING_MAX
49# define AW_SCALING_MAX 150
50#endif
51
52#ifndef AW_GLOBAL_CURRENT_MAX
53# define AW_GLOBAL_CURRENT_MAX 150
54#endif
55
56#ifndef AW_SPI_DIVISOR
57# define AW_SPI_DIVISOR 4
58#endif
59
60uint8_t g_pwm_buffer[DRIVER_COUNT][AW_PWM_REGISTER_COUNT];
61bool g_pwm_buffer_update_required[DRIVER_COUNT] = {false};
62
63bool AW20216_write(pin_t cs_pin, uint8_t page, uint8_t reg, uint8_t* data, uint8_t len) {
64 static uint8_t s_spi_transfer_buffer[2] = {0};
65
66 if (!spi_start(cs_pin, false, 0, AW_SPI_DIVISOR)) {
67 spi_stop();
68 return false;
69 }
70
71 s_spi_transfer_buffer[0] = (AWINIC_ID | page | AW_WRITE);
72 s_spi_transfer_buffer[1] = reg;
73
74 if (spi_transmit(s_spi_transfer_buffer, 2) != SPI_STATUS_SUCCESS) {
75 spi_stop();
76 return false;
77 }
78
79 if (spi_transmit(data, len) != SPI_STATUS_SUCCESS) {
80 spi_stop();
81 return false;
82 }
83
84 spi_stop();
85 return true;
86}
87
88static inline bool AW20216_write_register(pin_t cs_pin, uint8_t page, uint8_t reg, uint8_t value) {
89 // Little wrapper so callers need not care about sending a buffer
90 return AW20216_write(cs_pin, page, reg, &value, 1);
91}
92
93static void AW20216_init_scaling(pin_t cs_pin) {
94 // Set constant current to the max, control brightness with PWM
95 for (uint8_t i = 0; i < AW_PWM_REGISTER_COUNT; i++) {
96 AW20216_write_register(cs_pin, AW_PAGE_SCALING, i, AW_SCALING_MAX);
97 }
98}
99
100static inline void AW20216_init_current_limit(pin_t cs_pin) {
101 // Push config
102 AW20216_write_register(cs_pin, AW_PAGE_FUNCTION, AW_REG_GLOBALCURRENT, AW_GLOBAL_CURRENT_MAX);
103}
104
105static inline void AW20216_soft_enable(pin_t cs_pin) {
106 // Push config
107 AW20216_write_register(cs_pin, AW_PAGE_FUNCTION, AW_REG_CONFIGURATION, AW_CONFIG_DEFAULT | AW_CHIPEN);
108}
109
110void AW20216_init(pin_t cs_pin, pin_t en_pin) {
111 setPinOutput(en_pin);
112 writePinHigh(en_pin);
113
114 // Drivers should start with all scaling and PWM registers as off
115 AW20216_init_current_limit(cs_pin);
116 AW20216_init_scaling(cs_pin);
117
118 AW20216_soft_enable(cs_pin);
119}
120
121void AW20216_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
122 aw_led led = g_aw_leds[index];
123
124 g_pwm_buffer[led.driver][led.r] = red;
125 g_pwm_buffer[led.driver][led.g] = green;
126 g_pwm_buffer[led.driver][led.b] = blue;
127 g_pwm_buffer_update_required[led.driver] = true;
128}
129
130void AW20216_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
131 for (uint8_t i = 0; i < DRIVER_LED_TOTAL; i++) {
132 AW20216_set_color(i, red, green, blue);
133 }
134}
135
136void AW20216_update_pwm_buffers(pin_t cs_pin, uint8_t index) {
137 if (g_pwm_buffer_update_required[index]) {
138 AW20216_write(cs_pin, AW_PAGE_PWM, 0, g_pwm_buffer[index], AW_PWM_REGISTER_COUNT);
139 }
140 g_pwm_buffer_update_required[index] = false;
141}
diff --git a/drivers/led/aw20216.h b/drivers/led/aw20216.h
new file mode 100644
index 000000000..97ac6dc5b
--- /dev/null
+++ b/drivers/led/aw20216.h
@@ -0,0 +1,253 @@
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#include "progmem.h"
22#include "gpio.h"
23
24typedef struct aw_led {
25 uint8_t driver : 2;
26 uint8_t r;
27 uint8_t g;
28 uint8_t b;
29} aw_led;
30
31extern const aw_led __flash g_aw_leds[DRIVER_LED_TOTAL];
32
33void AW20216_init(pin_t cs_pin, pin_t en_pin);
34void AW20216_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
35void AW20216_set_color_all(uint8_t red, uint8_t green, uint8_t blue);
36void AW20216_update_pwm_buffers(pin_t cs_pin, uint8_t index);
37
38#define CS1_SW1 0x00
39#define CS2_SW1 0x01
40#define CS3_SW1 0x02
41#define CS4_SW1 0x03
42#define CS5_SW1 0x04
43#define CS6_SW1 0x05
44#define CS7_SW1 0x06
45#define CS8_SW1 0x07
46#define CS9_SW1 0x08
47#define CS10_SW1 0x09
48#define CS11_SW1 0x0A
49#define CS12_SW1 0x0B
50#define CS13_SW1 0x0C
51#define CS14_SW1 0x0D
52#define CS15_SW1 0x0E
53#define CS16_SW1 0x0F
54#define CS17_SW1 0x10
55#define CS18_SW1 0x11
56#define CS1_SW2 0x12
57#define CS2_SW2 0x13
58#define CS3_SW2 0x14
59#define CS4_SW2 0x15
60#define CS5_SW2 0x16
61#define CS6_SW2 0x17
62#define CS7_SW2 0x18
63#define CS8_SW2 0x19
64#define CS9_SW2 0x1A
65#define CS10_SW2 0x1B
66#define CS11_SW2 0x1C
67#define CS12_SW2 0x1D
68#define CS13_SW2 0x1E
69#define CS14_SW2 0x1F
70#define CS15_SW2 0x20
71#define CS16_SW2 0x21
72#define CS17_SW2 0x22
73#define CS18_SW2 0x23
74#define CS1_SW3 0x24
75#define CS2_SW3 0x25
76#define CS3_SW3 0x26
77#define CS4_SW3 0x27
78#define CS5_SW3 0x28
79#define CS6_SW3 0x29
80#define CS7_SW3 0x2A
81#define CS8_SW3 0x2B
82#define CS9_SW3 0x2C
83#define CS10_SW3 0x2D
84#define CS11_SW3 0x2E
85#define CS12_SW3 0x2F
86#define CS13_SW3 0x30
87#define CS14_SW3 0x31
88#define CS15_SW3 0x32
89#define CS16_SW3 0x33
90#define CS17_SW3 0x34
91#define CS18_SW3 0x35
92#define CS1_SW4 0x36
93#define CS2_SW4 0x37
94#define CS3_SW4 0x38
95#define CS4_SW4 0x39
96#define CS5_SW4 0x3A
97#define CS6_SW4 0x3B
98#define CS7_SW4 0x3C
99#define CS8_SW4 0x3D
100#define CS9_SW4 0x3E
101#define CS10_SW4 0x3F
102#define CS11_SW4 0x40
103#define CS12_SW4 0x41
104#define CS13_SW4 0x42
105#define CS14_SW4 0x43
106#define CS15_SW4 0x44
107#define CS16_SW4 0x45
108#define CS17_SW4 0x46
109#define CS18_SW4 0x47
110#define CS1_SW5 0x48
111#define CS2_SW5 0x49
112#define CS3_SW5 0x4A
113#define CS4_SW5 0x4B
114#define CS5_SW5 0x4C
115#define CS6_SW5 0x4D
116#define CS7_SW5 0x4E
117#define CS8_SW5 0x4F
118#define CS9_SW5 0x50
119#define CS10_SW5 0x51
120#define CS11_SW5 0x52
121#define CS12_SW5 0x53
122#define CS13_SW5 0x54
123#define CS14_SW5 0x55
124#define CS15_SW5 0x56
125#define CS16_SW5 0x57
126#define CS17_SW5 0x58
127#define CS18_SW5 0x59
128#define CS1_SW6 0x5A
129#define CS2_SW6 0x5B
130#define CS3_SW6 0x5C
131#define CS4_SW6 0x5D
132#define CS5_SW6 0x5E
133#define CS6_SW6 0x5F
134#define CS7_SW6 0x60
135#define CS8_SW6 0x61
136#define CS9_SW6 0x62
137#define CS10_SW6 0x63
138#define CS11_SW6 0x64
139#define CS12_SW6 0x65
140#define CS13_SW6 0x66
141#define CS14_SW6 0x67
142#define CS15_SW6 0x68
143#define CS16_SW6 0x69
144#define CS17_SW6 0x6A
145#define CS18_SW6 0x6B
146#define CS1_SW7 0x6C
147#define CS2_SW7 0x6D
148#define CS3_SW7 0x6E
149#define CS4_SW7 0x6F
150#define CS5_SW7 0x70
151#define CS6_SW7 0x71
152#define CS7_SW7 0x72
153#define CS8_SW7 0x73
154#define CS9_SW7 0x74
155#define CS10_SW7 0x75
156#define CS11_SW7 0x76
157#define CS12_SW7 0x77
158#define CS13_SW7 0x78
159#define CS14_SW7 0x79
160#define CS15_SW7 0x7A
161#define CS16_SW7 0x7B
162#define CS17_SW7 0x7C
163#define CS18_SW7 0x7D
164#define CS1_SW8 0x7E
165#define CS2_SW8 0x7F
166#define CS3_SW8 0x80
167#define CS4_SW8 0x81
168#define CS5_SW8 0x82
169#define CS6_SW8 0x83
170#define CS7_SW8 0x84
171#define CS8_SW8 0x85
172#define CS9_SW8 0x86
173#define CS10_SW8 0x87
174#define CS11_SW8 0x88
175#define CS12_SW8 0x89
176#define CS13_SW8 0x8A
177#define CS14_SW8 0x8B
178#define CS15_SW8 0x8C
179#define CS16_SW8 0x8D
180#define CS17_SW8 0x8E
181#define CS18_SW8 0x8F
182#define CS1_SW9 0x90
183#define CS2_SW9 0x91
184#define CS3_SW9 0x92
185#define CS4_SW9 0x93
186#define CS5_SW9 0x94
187#define CS6_SW9 0x95
188#define CS7_SW9 0x96
189#define CS8_SW9 0x97
190#define CS9_SW9 0x98
191#define CS10_SW9 0x99
192#define CS11_SW9 0x9A
193#define CS12_SW9 0x9B
194#define CS13_SW9 0x9C
195#define CS14_SW9 0x9D
196#define CS15_SW9 0x9E
197#define CS16_SW9 0x9F
198#define CS17_SW9 0xA0
199#define CS18_SW9 0xA1
200#define CS1_SW10 0xA2
201#define CS2_SW10 0xA3
202#define CS3_SW10 0xA4
203#define CS4_SW10 0xA5
204#define CS5_SW10 0xA6
205#define CS6_SW10 0xA7
206#define CS7_SW10 0xA8
207#define CS8_SW10 0xA9
208#define CS9_SW10 0xAA
209#define CS10_SW10 0xAB
210#define CS11_SW10 0xAC
211#define CS12_SW10 0xAD
212#define CS13_SW10 0xAE
213#define CS14_SW10 0xAF
214#define CS15_SW10 0xB0
215#define CS16_SW10 0xB1
216#define CS17_SW10 0xB2
217#define CS18_SW10 0xB3
218#define CS1_SW11 0xB4
219#define CS2_SW11 0xB5
220#define CS3_SW11 0xB6
221#define CS4_SW11 0xB7
222#define CS5_SW11 0xB8
223#define CS6_SW11 0xB9
224#define CS7_SW11 0xBA
225#define CS8_SW11 0xBB
226#define CS9_SW11 0xBC
227#define CS10_SW11 0xBD
228#define CS11_SW11 0xBE
229#define CS12_SW11 0xBF
230#define CS13_SW11 0xC0
231#define CS14_SW11 0xC1
232#define CS15_SW11 0xC2
233#define CS16_SW11 0xC3
234#define CS17_SW11 0xC4
235#define CS18_SW11 0xC5
236#define CS1_SW12 0xC6
237#define CS2_SW12 0xC7
238#define CS3_SW12 0xC8
239#define CS4_SW12 0xC9
240#define CS5_SW12 0xCA
241#define CS6_SW12 0xCB
242#define CS7_SW12 0xCC
243#define CS8_SW12 0xCD
244#define CS9_SW12 0xCE
245#define CS10_SW12 0xCF
246#define CS11_SW12 0xD0
247#define CS12_SW12 0xD1
248#define CS13_SW12 0xD2
249#define CS14_SW12 0xD3
250#define CS15_SW12 0xD4
251#define CS16_SW12 0xD5
252#define CS17_SW12 0xD6
253#define CS18_SW12 0xD7
diff --git a/drivers/issi/is31fl3218.c b/drivers/led/issi/is31fl3218.c
index d43863ac4..d43863ac4 100644
--- a/drivers/issi/is31fl3218.c
+++ b/drivers/led/issi/is31fl3218.c
diff --git a/drivers/issi/is31fl3218.h b/drivers/led/issi/is31fl3218.h
index fa760da19..fa760da19 100644
--- a/drivers/issi/is31fl3218.h
+++ b/drivers/led/issi/is31fl3218.h
diff --git a/drivers/issi/is31fl3731-simple.c b/drivers/led/issi/is31fl3731-simple.c
index d295772f5..d295772f5 100644
--- a/drivers/issi/is31fl3731-simple.c
+++ b/drivers/led/issi/is31fl3731-simple.c
diff --git a/drivers/issi/is31fl3731-simple.h b/drivers/led/issi/is31fl3731-simple.h
index 9665d6ed3..ecde31eed 100644
--- a/drivers/issi/is31fl3731-simple.h
+++ b/drivers/led/issi/is31fl3731-simple.h
@@ -20,13 +20,14 @@
20 20
21#include <stdint.h> 21#include <stdint.h>
22#include <stdbool.h> 22#include <stdbool.h>
23#include "progmem.h"
23 24
24typedef struct is31_led { 25typedef struct is31_led {
25 uint8_t driver : 2; 26 uint8_t driver : 2;
26 uint8_t v; 27 uint8_t v;
27} __attribute__((packed)) is31_led; 28} __attribute__((packed)) is31_led;
28 29
29extern const is31_led g_is31_leds[DRIVER_LED_TOTAL]; 30extern const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL];
30 31
31void IS31FL3731_init(uint8_t addr); 32void IS31FL3731_init(uint8_t addr);
32void IS31FL3731_write_register(uint8_t addr, uint8_t reg, uint8_t data); 33void IS31FL3731_write_register(uint8_t addr, uint8_t reg, uint8_t data);
diff --git a/drivers/issi/is31fl3731.c b/drivers/led/issi/is31fl3731.c
index 110bdc1be..110bdc1be 100644
--- a/drivers/issi/is31fl3731.c
+++ b/drivers/led/issi/is31fl3731.c
diff --git a/drivers/issi/is31fl3731.h b/drivers/led/issi/is31fl3731.h
index 19e8e6251..803ea3ea1 100644
--- a/drivers/issi/is31fl3731.h
+++ b/drivers/led/issi/is31fl3731.h
@@ -19,6 +19,7 @@
19 19
20#include <stdint.h> 20#include <stdint.h>
21#include <stdbool.h> 21#include <stdbool.h>
22#include "progmem.h"
22 23
23typedef struct is31_led { 24typedef struct is31_led {
24 uint8_t driver : 2; 25 uint8_t driver : 2;
@@ -27,7 +28,7 @@ typedef struct is31_led {
27 uint8_t b; 28 uint8_t b;
28} __attribute__((packed)) is31_led; 29} __attribute__((packed)) is31_led;
29 30
30extern const is31_led g_is31_leds[DRIVER_LED_TOTAL]; 31extern const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL];
31 32
32void IS31FL3731_init(uint8_t addr); 33void IS31FL3731_init(uint8_t addr);
33void IS31FL3731_write_register(uint8_t addr, uint8_t reg, uint8_t data); 34void IS31FL3731_write_register(uint8_t addr, uint8_t reg, uint8_t data);
diff --git a/drivers/issi/is31fl3733.c b/drivers/led/issi/is31fl3733.c
index d99e5339c..d99e5339c 100644
--- a/drivers/issi/is31fl3733.c
+++ b/drivers/led/issi/is31fl3733.c
diff --git a/drivers/issi/is31fl3733.h b/drivers/led/issi/is31fl3733.h
index 603d505a1..64fd38eb1 100644
--- a/drivers/issi/is31fl3733.h
+++ b/drivers/led/issi/is31fl3733.h
@@ -20,6 +20,7 @@
20 20
21#include <stdint.h> 21#include <stdint.h>
22#include <stdbool.h> 22#include <stdbool.h>
23#include "progmem.h"
23 24
24typedef struct is31_led { 25typedef struct is31_led {
25 uint8_t driver : 2; 26 uint8_t driver : 2;
@@ -28,7 +29,7 @@ typedef struct is31_led {
28 uint8_t b; 29 uint8_t b;
29} __attribute__((packed)) is31_led; 30} __attribute__((packed)) is31_led;
30 31
31extern const is31_led g_is31_leds[DRIVER_LED_TOTAL]; 32extern const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL];
32 33
33void IS31FL3733_init(uint8_t addr, uint8_t sync); 34void IS31FL3733_init(uint8_t addr, uint8_t sync);
34bool IS31FL3733_write_register(uint8_t addr, uint8_t reg, uint8_t data); 35bool IS31FL3733_write_register(uint8_t addr, uint8_t reg, uint8_t data);
diff --git a/drivers/issi/is31fl3736.c b/drivers/led/issi/is31fl3736.c
index 7dece1b1e..7dece1b1e 100644
--- a/drivers/issi/is31fl3736.c
+++ b/drivers/led/issi/is31fl3736.c
diff --git a/drivers/issi/is31fl3736.h b/drivers/led/issi/is31fl3736.h
index e48e31c27..c956c87f7 100644
--- a/drivers/issi/is31fl3736.h
+++ b/drivers/led/issi/is31fl3736.h
@@ -18,6 +18,7 @@
18 18
19#include <stdint.h> 19#include <stdint.h>
20#include <stdbool.h> 20#include <stdbool.h>
21#include "progmem.h"
21 22
22// Simple interface option. 23// Simple interface option.
23// If these aren't defined, just define them to make it compile 24// If these aren't defined, just define them to make it compile
@@ -37,7 +38,7 @@ typedef struct is31_led {
37 uint8_t b; 38 uint8_t b;
38} __attribute__((packed)) is31_led; 39} __attribute__((packed)) is31_led;
39 40
40extern const is31_led g_is31_leds[DRIVER_LED_TOTAL]; 41extern const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL];
41 42
42void IS31FL3736_init(uint8_t addr); 43void IS31FL3736_init(uint8_t addr);
43void IS31FL3736_write_register(uint8_t addr, uint8_t reg, uint8_t data); 44void IS31FL3736_write_register(uint8_t addr, uint8_t reg, uint8_t data);
diff --git a/drivers/issi/is31fl3737.c b/drivers/led/issi/is31fl3737.c
index 8647c93cc..0bb4ddd42 100644
--- a/drivers/issi/is31fl3737.c
+++ b/drivers/led/issi/is31fl3737.c
@@ -65,11 +65,12 @@ uint8_t g_twi_transfer_buffer[20];
65// We could optimize this and take out the unused registers from these 65// We could optimize this and take out the unused registers from these
66// buffers and the transfers in IS31FL3737_write_pwm_buffer() but it's 66// buffers and the transfers in IS31FL3737_write_pwm_buffer() but it's
67// probably not worth the extra complexity. 67// probably not worth the extra complexity.
68
68uint8_t g_pwm_buffer[DRIVER_COUNT][192]; 69uint8_t g_pwm_buffer[DRIVER_COUNT][192];
69bool g_pwm_buffer_update_required = false; 70bool g_pwm_buffer_update_required[DRIVER_COUNT] = {false};
70 71
71uint8_t g_led_control_registers[DRIVER_COUNT][24] = {{0}}; 72uint8_t g_led_control_registers[DRIVER_COUNT][24] = {0};
72bool g_led_control_registers_update_required = false; 73bool g_led_control_registers_update_required[DRIVER_COUNT] = {false};
73 74
74void IS31FL3737_write_register(uint8_t addr, uint8_t reg, uint8_t data) { 75void IS31FL3737_write_register(uint8_t addr, uint8_t reg, uint8_t data) {
75 g_twi_transfer_buffer[0] = reg; 76 g_twi_transfer_buffer[0] = reg;
@@ -155,10 +156,10 @@ void IS31FL3737_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
155 if (index >= 0 && index < DRIVER_LED_TOTAL) { 156 if (index >= 0 && index < DRIVER_LED_TOTAL) {
156 is31_led led = g_is31_leds[index]; 157 is31_led led = g_is31_leds[index];
157 158
158 g_pwm_buffer[led.driver][led.r] = red; 159 g_pwm_buffer[led.driver][led.r] = red;
159 g_pwm_buffer[led.driver][led.g] = green; 160 g_pwm_buffer[led.driver][led.g] = green;
160 g_pwm_buffer[led.driver][led.b] = blue; 161 g_pwm_buffer[led.driver][led.b] = blue;
161 g_pwm_buffer_update_required = true; 162 g_pwm_buffer_update_required[led.driver] = true;
162 } 163 }
163} 164}
164 165
@@ -194,30 +195,28 @@ void IS31FL3737_set_led_control_register(uint8_t index, bool red, bool green, bo
194 g_led_control_registers[led.driver][control_register_b] &= ~(1 << bit_b); 195 g_led_control_registers[led.driver][control_register_b] &= ~(1 << bit_b);
195 } 196 }
196 197
197 g_led_control_registers_update_required = true; 198 g_led_control_registers_update_required[led.driver] = true;
198} 199}
199 200
200void IS31FL3737_update_pwm_buffers(uint8_t addr1, uint8_t addr2) { 201void IS31FL3737_update_pwm_buffers(uint8_t addr, uint8_t index) {
201 if (g_pwm_buffer_update_required) { 202 if (g_pwm_buffer_update_required[index]) {
202 // Firstly we need to unlock the command register and select PG1 203 // Firstly we need to unlock the command register and select PG1
203 IS31FL3737_write_register(addr1, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5); 204 IS31FL3737_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
204 IS31FL3737_write_register(addr1, ISSI_COMMANDREGISTER, ISSI_PAGE_PWM); 205 IS31FL3737_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_PWM);
205 206
206 IS31FL3737_write_pwm_buffer(addr1, g_pwm_buffer[0]); 207 IS31FL3737_write_pwm_buffer(addr, g_pwm_buffer[index]);
207 // IS31FL3737_write_pwm_buffer(addr2, g_pwm_buffer[1]);
208 } 208 }
209 g_pwm_buffer_update_required = false; 209 g_pwm_buffer_update_required[index] = false;
210} 210}
211 211
212void IS31FL3737_update_led_control_registers(uint8_t addr1, uint8_t addr2) { 212void IS31FL3737_update_led_control_registers(uint8_t addr, uint8_t index) {
213 if (g_led_control_registers_update_required) { 213 if (g_led_control_registers_update_required[index]) {
214 // Firstly we need to unlock the command register and select PG0 214 // Firstly we need to unlock the command register and select PG0
215 IS31FL3737_write_register(addr1, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5); 215 IS31FL3737_write_register(addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5);
216 IS31FL3737_write_register(addr1, ISSI_COMMANDREGISTER, ISSI_PAGE_LEDCONTROL); 216 IS31FL3737_write_register(addr, ISSI_COMMANDREGISTER, ISSI_PAGE_LEDCONTROL);
217 for (int i = 0; i < 24; i++) { 217 for (int i = 0; i < 24; i++) {
218 IS31FL3737_write_register(addr1, i, g_led_control_registers[0][i]); 218 IS31FL3737_write_register(addr, i, g_led_control_registers[index][i]);
219 // IS31FL3737_write_register(addr2, i, g_led_control_registers[1][i]);
220 } 219 }
221 g_led_control_registers_update_required = false;
222 } 220 }
221 g_led_control_registers_update_required[index] = false;
223} 222}
diff --git a/drivers/issi/is31fl3737.h b/drivers/led/issi/is31fl3737.h
index a1d228177..06886e9c9 100644
--- a/drivers/issi/is31fl3737.h
+++ b/drivers/led/issi/is31fl3737.h
@@ -20,6 +20,7 @@
20 20
21#include <stdint.h> 21#include <stdint.h>
22#include <stdbool.h> 22#include <stdbool.h>
23#include "progmem.h"
23 24
24typedef struct is31_led { 25typedef struct is31_led {
25 uint8_t driver : 2; 26 uint8_t driver : 2;
@@ -28,7 +29,7 @@ typedef struct is31_led {
28 uint8_t b; 29 uint8_t b;
29} __attribute__((packed)) is31_led; 30} __attribute__((packed)) is31_led;
30 31
31extern const is31_led g_is31_leds[DRIVER_LED_TOTAL]; 32extern const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL];
32 33
33void IS31FL3737_init(uint8_t addr); 34void IS31FL3737_init(uint8_t addr);
34void IS31FL3737_write_register(uint8_t addr, uint8_t reg, uint8_t data); 35void IS31FL3737_write_register(uint8_t addr, uint8_t reg, uint8_t data);
diff --git a/drivers/issi/is31fl3741.c b/drivers/led/issi/is31fl3741.c
index 1b533c9b6..1b533c9b6 100644
--- a/drivers/issi/is31fl3741.c
+++ b/drivers/led/issi/is31fl3741.c
diff --git a/drivers/issi/is31fl3741.h b/drivers/led/issi/is31fl3741.h
index 2df0c5b1a..cea6761ca 100644
--- a/drivers/issi/is31fl3741.h
+++ b/drivers/led/issi/is31fl3741.h
@@ -21,6 +21,7 @@
21 21
22#include <stdint.h> 22#include <stdint.h>
23#include <stdbool.h> 23#include <stdbool.h>
24#include "progmem.h"
24 25
25typedef struct is31_led { 26typedef struct is31_led {
26 uint32_t driver : 2; 27 uint32_t driver : 2;
@@ -29,7 +30,7 @@ typedef struct is31_led {
29 uint32_t b : 10; 30 uint32_t b : 10;
30} __attribute__((packed)) is31_led; 31} __attribute__((packed)) is31_led;
31 32
32extern const is31_led g_is31_leds[DRIVER_LED_TOTAL]; 33extern const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL];
33 34
34void IS31FL3741_init(uint8_t addr); 35void IS31FL3741_init(uint8_t addr);
35void IS31FL3741_write_register(uint8_t addr, uint8_t reg, uint8_t data); 36void IS31FL3741_write_register(uint8_t addr, uint8_t reg, uint8_t data);
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..9e3c64c19
--- /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)
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);