aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/avr/i2c_master.c19
-rw-r--r--drivers/avr/i2c_slave.c29
-rw-r--r--drivers/avr/i2c_slave.h13
-rw-r--r--drivers/avr/serial.c82
-rw-r--r--drivers/avr/serial.h62
-rw-r--r--drivers/awinic/aw20216.c166
-rw-r--r--drivers/awinic/aw20216.h251
-rw-r--r--drivers/chibios/serial.c52
-rw-r--r--drivers/chibios/serial.h62
-rw-r--r--drivers/chibios/serial_usart.c47
-rw-r--r--drivers/chibios/spi_master.c70
-rw-r--r--drivers/chibios/spi_master.h19
-rw-r--r--drivers/lcd/st7565.c496
-rw-r--r--drivers/lcd/st7565.h219
-rw-r--r--drivers/oled/oled_driver.c26
-rw-r--r--drivers/oled/oled_driver.h4
-rw-r--r--drivers/serial.h46
17 files changed, 1405 insertions, 258 deletions
diff --git a/drivers/avr/i2c_master.c b/drivers/avr/i2c_master.c
index b1e488529..2773e0077 100644
--- a/drivers/avr/i2c_master.c
+++ b/drivers/avr/i2c_master.c
@@ -28,8 +28,14 @@
28# define F_SCL 400000UL // SCL frequency 28# define F_SCL 400000UL // SCL frequency
29#endif 29#endif
30 30
31#ifndef I2C_START_RETRY_COUNT
32# define I2C_START_RETRY_COUNT 20
33#endif // I2C_START_RETRY_COUNT
34
31#define TWBR_val (((F_CPU / F_SCL) - 16) / 2) 35#define TWBR_val (((F_CPU / F_SCL) - 16) / 2)
32 36
37#define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
38
33void i2c_init(void) { 39void i2c_init(void) {
34 TWSR = 0; /* no prescaler */ 40 TWSR = 0; /* no prescaler */
35 TWBR = (uint8_t)TWBR_val; 41 TWBR = (uint8_t)TWBR_val;
@@ -47,7 +53,7 @@ void i2c_init(void) {
47#endif 53#endif
48} 54}
49 55
50i2c_status_t i2c_start(uint8_t address, uint16_t timeout) { 56static i2c_status_t i2c_start_impl(uint8_t address, uint16_t timeout) {
51 // reset TWI control register 57 // reset TWI control register
52 TWCR = 0; 58 TWCR = 0;
53 // transmit START condition 59 // transmit START condition
@@ -86,6 +92,17 @@ i2c_status_t i2c_start(uint8_t address, uint16_t timeout) {
86 return I2C_STATUS_SUCCESS; 92 return I2C_STATUS_SUCCESS;
87} 93}
88 94
95i2c_status_t i2c_start(uint8_t address, uint16_t timeout) {
96 // Retry i2c_start_impl a bunch times in case the remote side has interrupts disabled.
97 uint16_t timeout_timer = timer_read();
98 uint16_t time_slice = MAX(1, (timeout == (I2C_TIMEOUT_INFINITE)) ? 5 : (timeout / (I2C_START_RETRY_COUNT))); // if it's infinite, wait 1ms between attempts, otherwise split up the entire timeout into the number of retries
99 i2c_status_t status;
100 do {
101 status = i2c_start_impl(address, time_slice);
102 } while ((status < 0) && ((timeout == I2C_TIMEOUT_INFINITE) || (timer_elapsed(timeout_timer) < timeout)));
103 return status;
104}
105
89i2c_status_t i2c_write(uint8_t data, uint16_t timeout) { 106i2c_status_t i2c_write(uint8_t data, uint16_t timeout) {
90 // load data into data register 107 // load data into data register
91 TWDR = data; 108 TWDR = data;
diff --git a/drivers/avr/i2c_slave.c b/drivers/avr/i2c_slave.c
index 62a378165..2907f164c 100644
--- a/drivers/avr/i2c_slave.c
+++ b/drivers/avr/i2c_slave.c
@@ -17,6 +17,7 @@
17 * GitHub repository: https://github.com/g4lvanix/I2C-slave-lib 17 * GitHub repository: https://github.com/g4lvanix/I2C-slave-lib
18 */ 18 */
19 19
20#include <stddef.h>
20#include <avr/io.h> 21#include <avr/io.h>
21#include <util/twi.h> 22#include <util/twi.h>
22#include <avr/interrupt.h> 23#include <avr/interrupt.h>
@@ -24,6 +25,12 @@
24 25
25#include "i2c_slave.h" 26#include "i2c_slave.h"
26 27
28#if defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)
29# include "transactions.h"
30
31static volatile bool is_callback_executor = false;
32#endif // defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)
33
27volatile uint8_t i2c_slave_reg[I2C_SLAVE_REG_COUNT]; 34volatile uint8_t i2c_slave_reg[I2C_SLAVE_REG_COUNT];
28 35
29static volatile uint8_t buffer_address; 36static volatile uint8_t buffer_address;
@@ -48,11 +55,14 @@ ISR(TWI_vect) {
48 case TW_SR_SLA_ACK: 55 case TW_SR_SLA_ACK:
49 // The device is now a slave receiver 56 // The device is now a slave receiver
50 slave_has_register_set = false; 57 slave_has_register_set = false;
58#if defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)
59 is_callback_executor = false;
60#endif // defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)
51 break; 61 break;
52 62
53 case TW_SR_DATA_ACK: 63 case TW_SR_DATA_ACK:
54 // This device is a slave receiver and has received data 64 // This device is a slave receiver and has received data
55 // First byte is the location then the bytes will be writen in buffer with auto-incriment 65 // First byte is the location then the bytes will be writen in buffer with auto-increment
56 if (!slave_has_register_set) { 66 if (!slave_has_register_set) {
57 buffer_address = TWDR; 67 buffer_address = TWDR;
58 68
@@ -60,10 +70,25 @@ ISR(TWI_vect) {
60 ack = 0; 70 ack = 0;
61 buffer_address = 0; 71 buffer_address = 0;
62 } 72 }
63 slave_has_register_set = true; // address has been receaved now fill in buffer 73 slave_has_register_set = true; // address has been received now fill in buffer
74
75#if defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)
76 // Work out if we're attempting to execute a callback
77 is_callback_executor = buffer_address == split_transaction_table[I2C_EXECUTE_CALLBACK].initiator2target_offset;
78#endif // defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)
64 } else { 79 } else {
65 i2c_slave_reg[buffer_address] = TWDR; 80 i2c_slave_reg[buffer_address] = TWDR;
66 buffer_address++; 81 buffer_address++;
82
83#if defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)
84 // If we're intending to execute a transaction callback, do so, as we've just received the transaction ID
85 if (is_callback_executor) {
86 split_transaction_desc_t *trans = &split_transaction_table[split_shmem->transaction_id];
87 if (trans->slave_callback) {
88 trans->slave_callback(trans->initiator2target_buffer_size, split_trans_initiator2target_buffer(trans), trans->target2initiator_buffer_size, split_trans_target2initiator_buffer(trans));
89 }
90 }
91#endif // defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)
67 } 92 }
68 break; 93 break;
69 94
diff --git a/drivers/avr/i2c_slave.h b/drivers/avr/i2c_slave.h
index 1cd0625ef..a8647c9da 100644
--- a/drivers/avr/i2c_slave.h
+++ b/drivers/avr/i2c_slave.h
@@ -22,7 +22,18 @@
22 22
23#pragma once 23#pragma once
24 24
25#define I2C_SLAVE_REG_COUNT 30 25#ifndef I2C_SLAVE_REG_COUNT
26
27# if defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)
28# include "transport.h"
29# define I2C_SLAVE_REG_COUNT sizeof(split_shared_memory_t)
30# else // defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)
31# define I2C_SLAVE_REG_COUNT 30
32# endif // defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)
33
34#endif // I2C_SLAVE_REG_COUNT
35
36_Static_assert(I2C_SLAVE_REG_COUNT < 256, "I2C target registers must be single byte");
26 37
27extern volatile uint8_t i2c_slave_reg[I2C_SLAVE_REG_COUNT]; 38extern volatile uint8_t i2c_slave_reg[I2C_SLAVE_REG_COUNT];
28 39
diff --git a/drivers/avr/serial.c b/drivers/avr/serial.c
index 3647bee0d..9a7345a53 100644
--- a/drivers/avr/serial.c
+++ b/drivers/avr/serial.c
@@ -224,15 +224,8 @@
224# define SERIAL_DELAY_HALF2 (SERIAL_DELAY - SERIAL_DELAY / 2) 224# define SERIAL_DELAY_HALF2 (SERIAL_DELAY - SERIAL_DELAY / 2)
225 225
226# define SLAVE_INT_WIDTH_US 1 226# define SLAVE_INT_WIDTH_US 1
227# ifndef SERIAL_USE_MULTI_TRANSACTION 227# define SLAVE_INT_ACK_WIDTH_UNIT 2
228# define SLAVE_INT_RESPONSE_TIME SERIAL_DELAY 228# define SLAVE_INT_ACK_WIDTH 4
229# else
230# define SLAVE_INT_ACK_WIDTH_UNIT 2
231# define SLAVE_INT_ACK_WIDTH 4
232# endif
233
234static SSTD_t *Transaction_table = NULL;
235static uint8_t Transaction_table_size = 0;
236 229
237inline static void serial_delay(void) ALWAYS_INLINE; 230inline static void serial_delay(void) ALWAYS_INLINE;
238inline static void serial_delay(void) { _delay_us(SERIAL_DELAY); } 231inline static void serial_delay(void) { _delay_us(SERIAL_DELAY); }
@@ -259,16 +252,12 @@ inline static void serial_low(void) { writePinLow(SOFT_SERIAL_PIN); }
259inline static void serial_high(void) ALWAYS_INLINE; 252inline static void serial_high(void) ALWAYS_INLINE;
260inline static void serial_high(void) { writePinHigh(SOFT_SERIAL_PIN); } 253inline static void serial_high(void) { writePinHigh(SOFT_SERIAL_PIN); }
261 254
262void soft_serial_initiator_init(SSTD_t *sstd_table, int sstd_table_size) { 255void soft_serial_initiator_init(void) {
263 Transaction_table = sstd_table;
264 Transaction_table_size = (uint8_t)sstd_table_size;
265 serial_output(); 256 serial_output();
266 serial_high(); 257 serial_high();
267} 258}
268 259
269void soft_serial_target_init(SSTD_t *sstd_table, int sstd_table_size) { 260void soft_serial_target_init(void) {
270 Transaction_table = sstd_table;
271 Transaction_table_size = (uint8_t)sstd_table_size;
272 serial_input_with_pullup(); 261 serial_input_with_pullup();
273 262
274 // Enable INT0-INT7 263 // Enable INT0-INT7
@@ -395,19 +384,14 @@ static inline uint8_t nibble_bits_count(uint8_t bits) {
395 384
396// interrupt handle to be used by the target device 385// interrupt handle to be used by the target device
397ISR(SERIAL_PIN_INTERRUPT) { 386ISR(SERIAL_PIN_INTERRUPT) {
398# ifndef SERIAL_USE_MULTI_TRANSACTION
399 serial_low();
400 serial_output();
401 SSTD_t *trans = Transaction_table;
402# else
403 // recive transaction table index 387 // recive transaction table index
404 uint8_t tid, bits; 388 uint8_t tid, bits;
405 uint8_t pecount = 0; 389 uint8_t pecount = 0;
406 sync_recv(); 390 sync_recv();
407 bits = serial_read_chunk(&pecount, 7); 391 bits = serial_read_chunk(&pecount, 8);
408 tid = bits >> 3; 392 tid = bits >> 3;
409 bits = (bits & 7) != nibble_bits_count(tid); 393 bits = (bits & 7) != (nibble_bits_count(tid) & 7);
410 if (bits || pecount > 0 || tid > Transaction_table_size) { 394 if (bits || pecount > 0 || tid > NUM_TOTAL_TRANSACTIONS) {
411 return; 395 return;
412 } 396 }
413 serial_delay_half1(); 397 serial_delay_half1();
@@ -415,18 +399,22 @@ ISR(SERIAL_PIN_INTERRUPT) {
415 serial_high(); // response step1 low->high 399 serial_high(); // response step1 low->high
416 serial_output(); 400 serial_output();
417 _delay_sub_us(SLAVE_INT_ACK_WIDTH_UNIT * SLAVE_INT_ACK_WIDTH); 401 _delay_sub_us(SLAVE_INT_ACK_WIDTH_UNIT * SLAVE_INT_ACK_WIDTH);
418 SSTD_t *trans = &Transaction_table[tid]; 402 split_transaction_desc_t *trans = &split_transaction_table[tid];
419 serial_low(); // response step2 ack high->low 403 serial_low(); // response step2 ack high->low
420# endif 404
405 // If the transaction has a callback, we can execute it now
406 if (trans->slave_callback) {
407 trans->slave_callback(trans->initiator2target_buffer_size, split_trans_initiator2target_buffer(trans), trans->target2initiator_buffer_size, split_trans_target2initiator_buffer(trans));
408 }
421 409
422 // target send phase 410 // target send phase
423 if (trans->target2initiator_buffer_size > 0) serial_send_packet((uint8_t *)trans->target2initiator_buffer, trans->target2initiator_buffer_size); 411 if (trans->target2initiator_buffer_size > 0) serial_send_packet((uint8_t *)split_trans_target2initiator_buffer(trans), trans->target2initiator_buffer_size);
424 // target switch to input 412 // target switch to input
425 change_sender2reciver(); 413 change_sender2reciver();
426 414
427 // target recive phase 415 // target recive phase
428 if (trans->initiator2target_buffer_size > 0) { 416 if (trans->initiator2target_buffer_size > 0) {
429 if (serial_recive_packet((uint8_t *)trans->initiator2target_buffer, trans->initiator2target_buffer_size)) { 417 if (serial_recive_packet((uint8_t *)split_trans_initiator2target_buffer(trans), trans->initiator2target_buffer_size)) {
430 *trans->status = TRANSACTION_ACCEPTED; 418 *trans->status = TRANSACTION_ACCEPTED;
431 } else { 419 } else {
432 *trans->status = TRANSACTION_DATA_ERROR; 420 *trans->status = TRANSACTION_DATA_ERROR;
@@ -448,14 +436,12 @@ ISR(SERIAL_PIN_INTERRUPT) {
448// TRANSACTION_NO_RESPONSE 436// TRANSACTION_NO_RESPONSE
449// TRANSACTION_DATA_ERROR 437// TRANSACTION_DATA_ERROR
450// this code is very time dependent, so we need to disable interrupts 438// this code is very time dependent, so we need to disable interrupts
451# ifndef SERIAL_USE_MULTI_TRANSACTION
452int soft_serial_transaction(void) {
453 SSTD_t *trans = Transaction_table;
454# else
455int soft_serial_transaction(int sstd_index) { 439int soft_serial_transaction(int sstd_index) {
456 if (sstd_index > Transaction_table_size) return TRANSACTION_TYPE_ERROR; 440 if (sstd_index > NUM_TOTAL_TRANSACTIONS) return TRANSACTION_TYPE_ERROR;
457 SSTD_t *trans = &Transaction_table[sstd_index]; 441 split_transaction_desc_t *trans = &split_transaction_table[sstd_index];
458# endif 442
443 if (!trans->status) return TRANSACTION_TYPE_ERROR; // not registered
444
459 cli(); 445 cli();
460 446
461 // signal to the target that we want to start a transaction 447 // signal to the target that we want to start a transaction
@@ -463,27 +449,11 @@ int soft_serial_transaction(int sstd_index) {
463 serial_low(); 449 serial_low();
464 _delay_us(SLAVE_INT_WIDTH_US); 450 _delay_us(SLAVE_INT_WIDTH_US);
465 451
466# ifndef SERIAL_USE_MULTI_TRANSACTION
467 // wait for the target response
468 serial_input_with_pullup();
469 _delay_us(SLAVE_INT_RESPONSE_TIME);
470
471 // check if the target is present
472 if (serial_read_pin()) {
473 // target failed to pull the line low, assume not present
474 serial_output();
475 serial_high();
476 *trans->status = TRANSACTION_NO_RESPONSE;
477 sei();
478 return TRANSACTION_NO_RESPONSE;
479 }
480
481# else
482 // send transaction table index 452 // send transaction table index
483 int tid = (sstd_index << 3) | (7 & nibble_bits_count(sstd_index)); 453 int tid = (sstd_index << 3) | (7 & nibble_bits_count(sstd_index));
484 sync_send(); 454 sync_send();
485 _delay_sub_us(TID_SEND_ADJUST); 455 _delay_sub_us(TID_SEND_ADJUST);
486 serial_write_chunk(tid, 7); 456 serial_write_chunk(tid, 8);
487 serial_delay_half1(); 457 serial_delay_half1();
488 458
489 // wait for the target response (step1 low->high) 459 // wait for the target response (step1 low->high)
@@ -504,12 +474,11 @@ int soft_serial_transaction(int sstd_index) {
504 } 474 }
505 _delay_sub_us(SLAVE_INT_ACK_WIDTH_UNIT); 475 _delay_sub_us(SLAVE_INT_ACK_WIDTH_UNIT);
506 } 476 }
507# endif
508 477
509 // initiator recive phase 478 // initiator recive phase
510 // if the target is present syncronize with it 479 // if the target is present syncronize with it
511 if (trans->target2initiator_buffer_size > 0) { 480 if (trans->target2initiator_buffer_size > 0) {
512 if (!serial_recive_packet((uint8_t *)trans->target2initiator_buffer, trans->target2initiator_buffer_size)) { 481 if (!serial_recive_packet((uint8_t *)split_trans_target2initiator_buffer(trans), trans->target2initiator_buffer_size)) {
513 serial_output(); 482 serial_output();
514 serial_high(); 483 serial_high();
515 *trans->status = TRANSACTION_DATA_ERROR; 484 *trans->status = TRANSACTION_DATA_ERROR;
@@ -523,7 +492,7 @@ int soft_serial_transaction(int sstd_index) {
523 492
524 // initiator send phase 493 // initiator send phase
525 if (trans->initiator2target_buffer_size > 0) { 494 if (trans->initiator2target_buffer_size > 0) {
526 serial_send_packet((uint8_t *)trans->initiator2target_buffer, trans->initiator2target_buffer_size); 495 serial_send_packet((uint8_t *)split_trans_initiator2target_buffer(trans), trans->initiator2target_buffer_size);
527 } 496 }
528 497
529 // always, release the line when not in use 498 // always, release the line when not in use
@@ -534,9 +503,8 @@ int soft_serial_transaction(int sstd_index) {
534 return TRANSACTION_END; 503 return TRANSACTION_END;
535} 504}
536 505
537# ifdef SERIAL_USE_MULTI_TRANSACTION
538int soft_serial_get_and_clean_status(int sstd_index) { 506int soft_serial_get_and_clean_status(int sstd_index) {
539 SSTD_t *trans = &Transaction_table[sstd_index]; 507 split_transaction_desc_t *trans = &split_transaction_table[sstd_index];
540 cli(); 508 cli();
541 int retval = *trans->status; 509 int retval = *trans->status;
542 *trans->status = 0; 510 *trans->status = 0;
@@ -544,8 +512,6 @@ int soft_serial_get_and_clean_status(int sstd_index) {
544 sei(); 512 sei();
545 return retval; 513 return retval;
546} 514}
547# endif
548
549#endif 515#endif
550 516
551// Helix serial.c history 517// Helix serial.c history
diff --git a/drivers/avr/serial.h b/drivers/avr/serial.h
deleted file mode 100644
index 53e66cf90..000000000
--- a/drivers/avr/serial.h
+++ /dev/null
@@ -1,62 +0,0 @@
1#pragma once
2
3#include <stdbool.h>
4
5// /////////////////////////////////////////////////////////////////
6// Need Soft Serial defines in config.h
7// /////////////////////////////////////////////////////////////////
8// ex.
9// #define SOFT_SERIAL_PIN ?? // ?? = D0,D1,D2,D3,E6
10// OPTIONAL: #define SELECT_SOFT_SERIAL_SPEED ? // ? = 1,2,3,4,5
11// // 1: about 137kbps (default)
12// // 2: about 75kbps
13// // 3: about 39kbps
14// // 4: about 26kbps
15// // 5: about 20kbps
16//
17// //// USE simple API (using signle-type transaction function)
18// /* nothing */
19// //// USE flexible API (using multi-type transaction function)
20// #define SERIAL_USE_MULTI_TRANSACTION
21//
22// /////////////////////////////////////////////////////////////////
23
24// Soft Serial Transaction Descriptor
25typedef struct _SSTD_t {
26 uint8_t *status;
27 uint8_t initiator2target_buffer_size;
28 uint8_t *initiator2target_buffer;
29 uint8_t target2initiator_buffer_size;
30 uint8_t *target2initiator_buffer;
31} SSTD_t;
32#define TID_LIMIT(table) (sizeof(table) / sizeof(SSTD_t))
33
34// initiator is transaction start side
35void soft_serial_initiator_init(SSTD_t *sstd_table, int sstd_table_size);
36// target is interrupt accept side
37void soft_serial_target_init(SSTD_t *sstd_table, int sstd_table_size);
38
39// initiator resullt
40#define TRANSACTION_END 0
41#define TRANSACTION_NO_RESPONSE 0x1
42#define TRANSACTION_DATA_ERROR 0x2
43#define TRANSACTION_TYPE_ERROR 0x4
44#ifndef SERIAL_USE_MULTI_TRANSACTION
45int soft_serial_transaction(void);
46#else
47int soft_serial_transaction(int sstd_index);
48#endif
49
50// target status
51// *SSTD_t.status has
52// initiator:
53// TRANSACTION_END
54// or TRANSACTION_NO_RESPONSE
55// or TRANSACTION_DATA_ERROR
56// target:
57// TRANSACTION_DATA_ERROR
58// or TRANSACTION_ACCEPTED
59#define TRANSACTION_ACCEPTED 0x8
60#ifdef SERIAL_USE_MULTI_TRANSACTION
61int soft_serial_get_and_clean_status(int sstd_index);
62#endif
diff --git a/drivers/awinic/aw20216.c b/drivers/awinic/aw20216.c
new file mode 100644
index 000000000..236c42a3c
--- /dev/null
+++ b/drivers/awinic/aw20216.c
@@ -0,0 +1,166 @@
1/* Copyright 2021 Jasper Chan
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "aw20216.h"
18#include "spi_master.h"
19
20/* The AW20216 appears to be somewhat similar to the IS31FL743, although quite
21 * a few things are different, such as the command byte format and page ordering.
22 * The LED addresses start from 0x00 instead of 0x01.
23 */
24#define AWINIC_ID 0b1010 << 4
25
26#define AW_PAGE_FUNCTION 0x00 << 1 // PG0, Function registers
27#define AW_PAGE_PWM 0x01 << 1 // PG1, LED PWM control
28#define AW_PAGE_SCALING 0x02 << 1 // PG2, LED current scaling control
29#define AW_PAGE_PATCHOICE 0x03 << 1 // PG3, Pattern choice?
30#define AW_PAGE_PWMSCALING 0x04 << 1 // PG4, LED PWM + Scaling control?
31
32#define AW_WRITE 0
33#define AW_READ 1
34
35#define AW_REG_CONFIGURATION 0x00 // PG0
36#define AW_REG_GLOBALCURRENT 0x01 // PG0
37
38// Default value of AW_REG_CONFIGURATION
39// D7:D4 = 1011, SWSEL (SW1~SW12 active)
40// D3 = 0?, reserved (apparently this should be 1 but it doesn't seem to matter)
41// D2:D1 = 00, OSDE (open/short detection enable)
42// D0 = 0, CHIPEN (write 1 to enable LEDs when hardware enable pulled high)
43#define AW_CONFIG_DEFAULT 0b10110000
44#define AW_CHIPEN 1
45
46#ifndef AW_SCALING_MAX
47# define AW_SCALING_MAX 150
48#endif
49
50#ifndef AW_GLOBAL_CURRENT_MAX
51# define AW_GLOBAL_CURRENT_MAX 150
52#endif
53
54#ifndef DRIVER_1_CS
55# define DRIVER_1_CS B13
56#endif
57
58#ifndef DRIVER_1_EN
59# define DRIVER_1_EN C13
60#endif
61
62uint8_t g_spi_transfer_buffer[20] = {0};
63aw_led g_pwm_buffer[DRIVER_LED_TOTAL];
64bool g_pwm_buffer_update_required[DRIVER_LED_TOTAL];
65
66bool AW20216_write_register(pin_t slave_pin, uint8_t page, uint8_t reg, uint8_t data) {
67 // Do we need to call spi_stop() if this fails?
68 if (!spi_start(slave_pin, false, 0, 16)) {
69 return false;
70 }
71
72 g_spi_transfer_buffer[0] = (AWINIC_ID | page | AW_WRITE);
73 g_spi_transfer_buffer[1] = reg;
74 g_spi_transfer_buffer[2] = data;
75
76 if (spi_transmit(g_spi_transfer_buffer, 3) != SPI_STATUS_SUCCESS) {
77 spi_stop();
78 return false;
79 }
80 spi_stop();
81 return true;
82}
83
84bool AW20216_init_scaling(void) {
85 // Set constant current to the max, control brightness with PWM
86 aw_led led;
87 for (uint8_t i = 0; i < DRIVER_LED_TOTAL; i++) {
88 led = g_aw_leds[i];
89 if (led.driver == 0) {
90 AW20216_write_register(DRIVER_1_CS, AW_PAGE_SCALING, led.r, AW_SCALING_MAX);
91 AW20216_write_register(DRIVER_1_CS, AW_PAGE_SCALING, led.g, AW_SCALING_MAX);
92 AW20216_write_register(DRIVER_1_CS, AW_PAGE_SCALING, led.b, AW_SCALING_MAX);
93 }
94#ifdef DRIVER_2_CS
95 else if (led.driver == 1) {
96 AW20216_write_register(DRIVER_2_CS, AW_PAGE_SCALING, led.r, AW_SCALING_MAX);
97 AW20216_write_register(DRIVER_2_CS, AW_PAGE_SCALING, led.g, AW_SCALING_MAX);
98 AW20216_write_register(DRIVER_2_CS, AW_PAGE_SCALING, led.b, AW_SCALING_MAX);
99 }
100#endif
101 }
102 return true;
103}
104
105bool AW20216_soft_enable(void) {
106 AW20216_write_register(DRIVER_1_CS, AW_PAGE_FUNCTION, AW_REG_CONFIGURATION, AW_CONFIG_DEFAULT | AW_CHIPEN);
107#ifdef DRIVER_2_CS
108 AW20216_write_register(DRIVER_2_CS, AW_PAGE_FUNCTION, AW_REG_CONFIGURATION, AW_CONFIG_DEFAULT | AW_CHIPEN);
109#endif
110 return true;
111}
112
113void AW20216_update_pwm(int index, uint8_t red, uint8_t green, uint8_t blue) {
114 aw_led led = g_aw_leds[index];
115 if (led.driver == 0) {
116 AW20216_write_register(DRIVER_1_CS, AW_PAGE_PWM, led.r, red);
117 AW20216_write_register(DRIVER_1_CS, AW_PAGE_PWM, led.g, green);
118 AW20216_write_register(DRIVER_1_CS, AW_PAGE_PWM, led.b, blue);
119 }
120#ifdef DRIVER_2_CS
121 else if (led.driver == 1) {
122 AW20216_write_register(DRIVER_2_CS, AW_PAGE_PWM, led.r, red);
123 AW20216_write_register(DRIVER_2_CS, AW_PAGE_PWM, led.g, green);
124 AW20216_write_register(DRIVER_2_CS, AW_PAGE_PWM, led.b, blue);
125 }
126#endif
127 return;
128}
129
130void AW20216_init(void) {
131 // All LEDs should start with all scaling and PWM registers as off
132 setPinOutput(DRIVER_1_EN);
133 writePinHigh(DRIVER_1_EN);
134 AW20216_write_register(DRIVER_1_CS, AW_PAGE_FUNCTION, AW_REG_GLOBALCURRENT, AW_GLOBAL_CURRENT_MAX);
135#ifdef DRIVER_2_EN
136 setPinOutput(DRIVER_2_EN);
137 writePinHigh(DRIVER_2_EN);
138 AW20216_write_register(DRIVER_2_CS, AW_PAGE_FUNCTION, AW_REG_GLOBALCURRENT, AW_GLOBAL_CURRENT_MAX);
139#endif
140 AW20216_init_scaling();
141 AW20216_soft_enable();
142 return;
143}
144
145void AW20216_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {
146 g_pwm_buffer[index].r = red;
147 g_pwm_buffer[index].g = green;
148 g_pwm_buffer[index].b = blue;
149 g_pwm_buffer_update_required[index] = true;
150 return;
151}
152void AW20216_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {
153 for (uint8_t i = 0; i < DRIVER_LED_TOTAL; i++) {
154 AW20216_set_color(i, red, green, blue);
155 }
156 return;
157}
158void AW20216_update_pwm_buffers(void) {
159 for (uint8_t i = 0; i < DRIVER_LED_TOTAL; i++) {
160 if (g_pwm_buffer_update_required[i]) {
161 AW20216_update_pwm(i, g_pwm_buffer[i].r, g_pwm_buffer[i].g, g_pwm_buffer[i].b);
162 g_pwm_buffer_update_required[i] = false;
163 }
164 }
165 return;
166}
diff --git a/drivers/awinic/aw20216.h b/drivers/awinic/aw20216.h
new file mode 100644
index 000000000..9c6865cc8
--- /dev/null
+++ b/drivers/awinic/aw20216.h
@@ -0,0 +1,251 @@
1/* Copyright 2021 Jasper Chan (Gigahawk)
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#pragma once
18
19#include <stdint.h>
20#include <stdbool.h>
21
22typedef struct aw_led {
23 uint8_t driver : 2;
24 uint8_t r;
25 uint8_t g;
26 uint8_t b;
27} aw_led;
28
29extern const aw_led g_aw_leds[DRIVER_LED_TOTAL];
30
31void AW20216_init(void);
32void AW20216_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);
33void AW20216_set_color_all(uint8_t red, uint8_t green, uint8_t blue);
34void AW20216_update_pwm_buffers(void);
35
36#define CS1_SW1 0x00
37#define CS2_SW1 0x01
38#define CS3_SW1 0x02
39#define CS4_SW1 0x03
40#define CS5_SW1 0x04
41#define CS6_SW1 0x05
42#define CS7_SW1 0x06
43#define CS8_SW1 0x07
44#define CS9_SW1 0x08
45#define CS10_SW1 0x09
46#define CS11_SW1 0x0A
47#define CS12_SW1 0x0B
48#define CS13_SW1 0x0C
49#define CS14_SW1 0x0D
50#define CS15_SW1 0x0E
51#define CS16_SW1 0x0F
52#define CS17_SW1 0x10
53#define CS18_SW1 0x11
54#define CS1_SW2 0x12
55#define CS2_SW2 0x13
56#define CS3_SW2 0x14
57#define CS4_SW2 0x15
58#define CS5_SW2 0x16
59#define CS6_SW2 0x17
60#define CS7_SW2 0x18
61#define CS8_SW2 0x19
62#define CS9_SW2 0x1A
63#define CS10_SW2 0x1B
64#define CS11_SW2 0x1C
65#define CS12_SW2 0x1D
66#define CS13_SW2 0x1E
67#define CS14_SW2 0x1F
68#define CS15_SW2 0x20
69#define CS16_SW2 0x21
70#define CS17_SW2 0x22
71#define CS18_SW2 0x23
72#define CS1_SW3 0x24
73#define CS2_SW3 0x25
74#define CS3_SW3 0x26
75#define CS4_SW3 0x27
76#define CS5_SW3 0x28
77#define CS6_SW3 0x29
78#define CS7_SW3 0x2A
79#define CS8_SW3 0x2B
80#define CS9_SW3 0x2C
81#define CS10_SW3 0x2D
82#define CS11_SW3 0x2E
83#define CS12_SW3 0x2F
84#define CS13_SW3 0x30
85#define CS14_SW3 0x31
86#define CS15_SW3 0x32
87#define CS16_SW3 0x33
88#define CS17_SW3 0x34
89#define CS18_SW3 0x35
90#define CS1_SW4 0x36
91#define CS2_SW4 0x37
92#define CS3_SW4 0x38
93#define CS4_SW4 0x39
94#define CS5_SW4 0x3A
95#define CS6_SW4 0x3B
96#define CS7_SW4 0x3C
97#define CS8_SW4 0x3D
98#define CS9_SW4 0x3E
99#define CS10_SW4 0x3F
100#define CS11_SW4 0x40
101#define CS12_SW4 0x41
102#define CS13_SW4 0x42
103#define CS14_SW4 0x43
104#define CS15_SW4 0x44
105#define CS16_SW4 0x45
106#define CS17_SW4 0x46
107#define CS18_SW4 0x47
108#define CS1_SW5 0x48
109#define CS2_SW5 0x49
110#define CS3_SW5 0x4A
111#define CS4_SW5 0x4B
112#define CS5_SW5 0x4C
113#define CS6_SW5 0x4D
114#define CS7_SW5 0x4E
115#define CS8_SW5 0x4F
116#define CS9_SW5 0x50
117#define CS10_SW5 0x51
118#define CS11_SW5 0x52
119#define CS12_SW5 0x53
120#define CS13_SW5 0x54
121#define CS14_SW5 0x55
122#define CS15_SW5 0x56
123#define CS16_SW5 0x57
124#define CS17_SW5 0x58
125#define CS18_SW5 0x59
126#define CS1_SW6 0x5A
127#define CS2_SW6 0x5B
128#define CS3_SW6 0x5C
129#define CS4_SW6 0x5D
130#define CS5_SW6 0x5E
131#define CS6_SW6 0x5F
132#define CS7_SW6 0x60
133#define CS8_SW6 0x61
134#define CS9_SW6 0x62
135#define CS10_SW6 0x63
136#define CS11_SW6 0x64
137#define CS12_SW6 0x65
138#define CS13_SW6 0x66
139#define CS14_SW6 0x67
140#define CS15_SW6 0x68
141#define CS16_SW6 0x69
142#define CS17_SW6 0x6A
143#define CS18_SW6 0x6B
144#define CS1_SW7 0x6C
145#define CS2_SW7 0x6D
146#define CS3_SW7 0x6E
147#define CS4_SW7 0x6F
148#define CS5_SW7 0x70
149#define CS6_SW7 0x71
150#define CS7_SW7 0x72
151#define CS8_SW7 0x73
152#define CS9_SW7 0x74
153#define CS10_SW7 0x75
154#define CS11_SW7 0x76
155#define CS12_SW7 0x77
156#define CS13_SW7 0x78
157#define CS14_SW7 0x79
158#define CS15_SW7 0x7A
159#define CS16_SW7 0x7B
160#define CS17_SW7 0x7C
161#define CS18_SW7 0x7D
162#define CS1_SW8 0x7E
163#define CS2_SW8 0x7F
164#define CS3_SW8 0x80
165#define CS4_SW8 0x81
166#define CS5_SW8 0x82
167#define CS6_SW8 0x83
168#define CS7_SW8 0x84
169#define CS8_SW8 0x85
170#define CS9_SW8 0x86
171#define CS10_SW8 0x87
172#define CS11_SW8 0x88
173#define CS12_SW8 0x89
174#define CS13_SW8 0x8A
175#define CS14_SW8 0x8B
176#define CS15_SW8 0x8C
177#define CS16_SW8 0x8D
178#define CS17_SW8 0x8E
179#define CS18_SW8 0x8F
180#define CS1_SW9 0x90
181#define CS2_SW9 0x91
182#define CS3_SW9 0x92
183#define CS4_SW9 0x93
184#define CS5_SW9 0x94
185#define CS6_SW9 0x95
186#define CS7_SW9 0x96
187#define CS8_SW9 0x97
188#define CS9_SW9 0x98
189#define CS10_SW9 0x99
190#define CS11_SW9 0x9A
191#define CS12_SW9 0x9B
192#define CS13_SW9 0x9C
193#define CS14_SW9 0x9D
194#define CS15_SW9 0x9E
195#define CS16_SW9 0x9F
196#define CS17_SW9 0xA0
197#define CS18_SW9 0xA1
198#define CS1_SW10 0xA2
199#define CS2_SW10 0xA3
200#define CS3_SW10 0xA4
201#define CS4_SW10 0xA5
202#define CS5_SW10 0xA6
203#define CS6_SW10 0xA7
204#define CS7_SW10 0xA8
205#define CS8_SW10 0xA9
206#define CS9_SW10 0xAA
207#define CS10_SW10 0xAB
208#define CS11_SW10 0xAC
209#define CS12_SW10 0xAD
210#define CS13_SW10 0xAE
211#define CS14_SW10 0xAF
212#define CS15_SW10 0xB0
213#define CS16_SW10 0xB1
214#define CS17_SW10 0xB2
215#define CS18_SW10 0xB3
216#define CS1_SW11 0xB4
217#define CS2_SW11 0xB5
218#define CS3_SW11 0xB6
219#define CS4_SW11 0xB7
220#define CS5_SW11 0xB8
221#define CS6_SW11 0xB9
222#define CS7_SW11 0xBA
223#define CS8_SW11 0xBB
224#define CS9_SW11 0xBC
225#define CS10_SW11 0xBD
226#define CS11_SW11 0xBE
227#define CS12_SW11 0xBF
228#define CS13_SW11 0xC0
229#define CS14_SW11 0xC1
230#define CS15_SW11 0xC2
231#define CS16_SW11 0xC3
232#define CS17_SW11 0xC4
233#define CS18_SW11 0xC5
234#define CS1_SW12 0xC6
235#define CS2_SW12 0xC7
236#define CS3_SW12 0xC8
237#define CS4_SW12 0xC9
238#define CS5_SW12 0xCA
239#define CS6_SW12 0xCB
240#define CS7_SW12 0xCC
241#define CS8_SW12 0xCD
242#define CS9_SW12 0xCE
243#define CS10_SW12 0xCF
244#define CS11_SW12 0xD0
245#define CS12_SW12 0xD1
246#define CS13_SW12 0xD2
247#define CS14_SW12 0xD3
248#define CS15_SW12 0xD4
249#define CS16_SW12 0xD5
250#define CS17_SW12 0xD6
251#define CS18_SW12 0xD7
diff --git a/drivers/chibios/serial.c b/drivers/chibios/serial.c
index 54f7e1321..f54fbcee4 100644
--- a/drivers/chibios/serial.c
+++ b/drivers/chibios/serial.c
@@ -74,21 +74,12 @@ static THD_FUNCTION(Thread1, arg) {
74 } 74 }
75} 75}
76 76
77static SSTD_t *Transaction_table = NULL; 77void soft_serial_initiator_init(void) {
78static uint8_t Transaction_table_size = 0;
79
80void soft_serial_initiator_init(SSTD_t *sstd_table, int sstd_table_size) {
81 Transaction_table = sstd_table;
82 Transaction_table_size = (uint8_t)sstd_table_size;
83
84 serial_output(); 78 serial_output();
85 serial_high(); 79 serial_high();
86} 80}
87 81
88void soft_serial_target_init(SSTD_t *sstd_table, int sstd_table_size) { 82void soft_serial_target_init(void) {
89 Transaction_table = sstd_table;
90 Transaction_table_size = (uint8_t)sstd_table_size;
91
92 serial_input(); 83 serial_input();
93 84
94 palEnablePadEvent(PAL_PORT(SOFT_SERIAL_PIN), PAL_PAD(SOFT_SERIAL_PIN), PAL_EVENT_MODE_FALLING_EDGE); 85 palEnablePadEvent(PAL_PORT(SOFT_SERIAL_PIN), PAL_PAD(SOFT_SERIAL_PIN), PAL_EVENT_MODE_FALLING_EDGE);
@@ -154,16 +145,14 @@ void interrupt_handler(void *arg) {
154 uint8_t checksum_computed = 0; 145 uint8_t checksum_computed = 0;
155 int sstd_index = 0; 146 int sstd_index = 0;
156 147
157#ifdef SERIAL_USE_MULTI_TRANSACTION
158 sstd_index = serial_read_byte(); 148 sstd_index = serial_read_byte();
159 sync_send(); 149 sync_send();
160#endif
161 150
162 SSTD_t *trans = &Transaction_table[sstd_index]; 151 split_transaction_desc_t *trans = &split_transaction_table[sstd_index];
163 for (int i = 0; i < trans->initiator2target_buffer_size; ++i) { 152 for (int i = 0; i < trans->initiator2target_buffer_size; ++i) {
164 trans->initiator2target_buffer[i] = serial_read_byte(); 153 split_trans_initiator2target_buffer(trans)[i] = serial_read_byte();
165 sync_send(); 154 sync_send();
166 checksum_computed += trans->initiator2target_buffer[i]; 155 checksum_computed += split_trans_initiator2target_buffer(trans)[i];
167 } 156 }
168 checksum_computed ^= 7; 157 checksum_computed ^= 7;
169 uint8_t checksum_received = serial_read_byte(); 158 uint8_t checksum_received = serial_read_byte();
@@ -172,12 +161,17 @@ void interrupt_handler(void *arg) {
172 // wait for the sync to finish sending 161 // wait for the sync to finish sending
173 serial_delay(); 162 serial_delay();
174 163
164 // Allow any slave processing to occur
165 if (trans->slave_callback) {
166 trans->slave_callback(trans->initiator2target_buffer_size, split_trans_initiator2target_buffer(trans), trans->target2initiator_buffer_size, split_trans_target2initiator_buffer(trans));
167 }
168
175 uint8_t checksum = 0; 169 uint8_t checksum = 0;
176 for (int i = 0; i < trans->target2initiator_buffer_size; ++i) { 170 for (int i = 0; i < trans->target2initiator_buffer_size; ++i) {
177 serial_write_byte(trans->target2initiator_buffer[i]); 171 serial_write_byte(split_trans_target2initiator_buffer(trans)[i]);
178 sync_send(); 172 sync_send();
179 serial_delay_half(); 173 serial_delay_half();
180 checksum += trans->target2initiator_buffer[i]; 174 checksum += split_trans_target2initiator_buffer(trans)[i];
181 } 175 }
182 serial_write_byte(checksum ^ 7); 176 serial_write_byte(checksum ^ 7);
183 sync_send(); 177 sync_send();
@@ -206,15 +200,10 @@ void interrupt_handler(void *arg) {
206// TRANSACTION_NO_RESPONSE 200// TRANSACTION_NO_RESPONSE
207// TRANSACTION_DATA_ERROR 201// TRANSACTION_DATA_ERROR
208// this code is very time dependent, so we need to disable interrupts 202// this code is very time dependent, so we need to disable interrupts
209#ifndef SERIAL_USE_MULTI_TRANSACTION
210int soft_serial_transaction(void) {
211 int sstd_index = 0;
212#else
213int soft_serial_transaction(int sstd_index) { 203int soft_serial_transaction(int sstd_index) {
214#endif 204 if (sstd_index > NUM_TOTAL_TRANSACTIONS) return TRANSACTION_TYPE_ERROR;
215 205 split_transaction_desc_t *trans = &split_transaction_table[sstd_index];
216 if (sstd_index > Transaction_table_size) return TRANSACTION_TYPE_ERROR; 206 if (!trans->status) return TRANSACTION_TYPE_ERROR; // not registered
217 SSTD_t *trans = &Transaction_table[sstd_index];
218 207
219 // TODO: remove extra delay between transactions 208 // TODO: remove extra delay between transactions
220 serial_delay(); 209 serial_delay();
@@ -244,14 +233,13 @@ int soft_serial_transaction(int sstd_index) {
244 233
245 uint8_t checksum = 0; 234 uint8_t checksum = 0;
246 // send data to the slave 235 // send data to the slave
247#ifdef SERIAL_USE_MULTI_TRANSACTION
248 serial_write_byte(sstd_index); // first chunk is transaction id 236 serial_write_byte(sstd_index); // first chunk is transaction id
249 sync_recv(); 237 sync_recv();
250#endif 238
251 for (int i = 0; i < trans->initiator2target_buffer_size; ++i) { 239 for (int i = 0; i < trans->initiator2target_buffer_size; ++i) {
252 serial_write_byte(trans->initiator2target_buffer[i]); 240 serial_write_byte(split_trans_initiator2target_buffer(trans)[i]);
253 sync_recv(); 241 sync_recv();
254 checksum += trans->initiator2target_buffer[i]; 242 checksum += split_trans_initiator2target_buffer(trans)[i];
255 } 243 }
256 serial_write_byte(checksum ^ 7); 244 serial_write_byte(checksum ^ 7);
257 sync_recv(); 245 sync_recv();
@@ -262,9 +250,9 @@ int soft_serial_transaction(int sstd_index) {
262 // receive data from the slave 250 // receive data from the slave
263 uint8_t checksum_computed = 0; 251 uint8_t checksum_computed = 0;
264 for (int i = 0; i < trans->target2initiator_buffer_size; ++i) { 252 for (int i = 0; i < trans->target2initiator_buffer_size; ++i) {
265 trans->target2initiator_buffer[i] = serial_read_byte(); 253 split_trans_target2initiator_buffer(trans)[i] = serial_read_byte();
266 sync_recv(); 254 sync_recv();
267 checksum_computed += trans->target2initiator_buffer[i]; 255 checksum_computed += split_trans_target2initiator_buffer(trans)[i];
268 } 256 }
269 checksum_computed ^= 7; 257 checksum_computed ^= 7;
270 uint8_t checksum_received = serial_read_byte(); 258 uint8_t checksum_received = serial_read_byte();
diff --git a/drivers/chibios/serial.h b/drivers/chibios/serial.h
deleted file mode 100644
index 0c1857d52..000000000
--- a/drivers/chibios/serial.h
+++ /dev/null
@@ -1,62 +0,0 @@
1#pragma once
2
3#include <stdbool.h>
4
5// /////////////////////////////////////////////////////////////////
6// Need Soft Serial defines in config.h
7// /////////////////////////////////////////////////////////////////
8// ex.
9// #define SOFT_SERIAL_PIN ?? // ?? = D0,D1,D2,D3,E6
10// OPTIONAL: #define SELECT_SOFT_SERIAL_SPEED ? // ? = 1,2,3,4,5
11// // 1: about 137kbps (default)
12// // 2: about 75kbps
13// // 3: about 39kbps
14// // 4: about 26kbps
15// // 5: about 20kbps
16//
17// //// USE simple API (using signle-type transaction function)
18// /* nothing */
19// //// USE flexible API (using multi-type transaction function)
20// #define SERIAL_USE_MULTI_TRANSACTION
21//
22// /////////////////////////////////////////////////////////////////
23
24// Soft Serial Transaction Descriptor
25typedef struct _SSTD_t {
26 uint8_t *status;
27 uint8_t initiator2target_buffer_size;
28 uint8_t *initiator2target_buffer;
29 uint8_t target2initiator_buffer_size;
30 uint8_t *target2initiator_buffer;
31} SSTD_t;
32#define TID_LIMIT(table) (sizeof(table) / sizeof(SSTD_t))
33
34// initiator is transaction start side
35void soft_serial_initiator_init(SSTD_t *sstd_table, int sstd_table_size);
36// target is interrupt accept side
37void soft_serial_target_init(SSTD_t *sstd_table, int sstd_table_size);
38
39// initiator result
40#define TRANSACTION_END 0
41#define TRANSACTION_NO_RESPONSE 0x1
42#define TRANSACTION_DATA_ERROR 0x2
43#define TRANSACTION_TYPE_ERROR 0x4
44#ifndef SERIAL_USE_MULTI_TRANSACTION
45int soft_serial_transaction(void);
46#else
47int soft_serial_transaction(int sstd_index);
48#endif
49
50// target status
51// *SSTD_t.status has
52// initiator:
53// TRANSACTION_END
54// or TRANSACTION_NO_RESPONSE
55// or TRANSACTION_DATA_ERROR
56// target:
57// TRANSACTION_DATA_ERROR
58// or TRANSACTION_ACCEPTED
59#define TRANSACTION_ACCEPTED 0x8
60#ifdef SERIAL_USE_MULTI_TRANSACTION
61int soft_serial_get_and_clean_status(int sstd_index);
62#endif
diff --git a/drivers/chibios/serial_usart.c b/drivers/chibios/serial_usart.c
index cae29388c..9f180d2d7 100644
--- a/drivers/chibios/serial_usart.c
+++ b/drivers/chibios/serial_usart.c
@@ -113,37 +113,29 @@ void usart_slave_init(void) {
113 chThdCreateStatic(waSlaveThread, sizeof(waSlaveThread), HIGHPRIO, SlaveThread, NULL); 113 chThdCreateStatic(waSlaveThread, sizeof(waSlaveThread), HIGHPRIO, SlaveThread, NULL);
114} 114}
115 115
116static SSTD_t* Transaction_table = NULL; 116void soft_serial_initiator_init(void) { usart_master_init(); }
117static uint8_t Transaction_table_size = 0;
118 117
119void soft_serial_initiator_init(SSTD_t* sstd_table, int sstd_table_size) { 118void soft_serial_target_init(void) { usart_slave_init(); }
120 Transaction_table = sstd_table;
121 Transaction_table_size = (uint8_t)sstd_table_size;
122
123 usart_master_init();
124}
125
126void soft_serial_target_init(SSTD_t* sstd_table, int sstd_table_size) {
127 Transaction_table = sstd_table;
128 Transaction_table_size = (uint8_t)sstd_table_size;
129
130 usart_slave_init();
131}
132 119
133void handle_soft_serial_slave(void) { 120void handle_soft_serial_slave(void) {
134 uint8_t sstd_index = sdGet(&SERIAL_USART_DRIVER); // first chunk is always transaction id 121 uint8_t sstd_index = sdGet(&SERIAL_USART_DRIVER); // first chunk is always transaction id
135 SSTD_t* trans = &Transaction_table[sstd_index]; 122 split_transaction_desc_t* trans = &split_transaction_table[sstd_index];
136 123
137 // Always write back the sstd_index as part of a basic handshake 124 // Always write back the sstd_index as part of a basic handshake
138 sstd_index ^= HANDSHAKE_MAGIC; 125 sstd_index ^= HANDSHAKE_MAGIC;
139 sdWrite(&SERIAL_USART_DRIVER, &sstd_index, sizeof(sstd_index)); 126 sdWrite(&SERIAL_USART_DRIVER, &sstd_index, sizeof(sstd_index));
140 127
141 if (trans->initiator2target_buffer_size) { 128 if (trans->initiator2target_buffer_size) {
142 sdRead(&SERIAL_USART_DRIVER, trans->initiator2target_buffer, trans->initiator2target_buffer_size); 129 sdRead(&SERIAL_USART_DRIVER, split_trans_initiator2target_buffer(trans), trans->initiator2target_buffer_size);
130 }
131
132 // Allow any slave processing to occur
133 if (trans->slave_callback) {
134 trans->slave_callback(trans->initiator2target_buffer_size, split_trans_initiator2target_buffer(trans), trans->target2initiator_buffer_size, split_trans_target2initiator_buffer(trans));
143 } 135 }
144 136
145 if (trans->target2initiator_buffer_size) { 137 if (trans->target2initiator_buffer_size) {
146 sdWrite(&SERIAL_USART_DRIVER, trans->target2initiator_buffer, trans->target2initiator_buffer_size); 138 sdWrite(&SERIAL_USART_DRIVER, split_trans_target2initiator_buffer(trans), trans->target2initiator_buffer_size);
147 } 139 }
148 140
149 if (trans->status) { 141 if (trans->status) {
@@ -160,17 +152,14 @@ void handle_soft_serial_slave(void) {
160// TRANSACTION_END 152// TRANSACTION_END
161// TRANSACTION_NO_RESPONSE 153// TRANSACTION_NO_RESPONSE
162// TRANSACTION_DATA_ERROR 154// TRANSACTION_DATA_ERROR
163#ifndef SERIAL_USE_MULTI_TRANSACTION
164int soft_serial_transaction(void) {
165 uint8_t sstd_index = 0;
166#else
167int soft_serial_transaction(int index) { 155int soft_serial_transaction(int index) {
168 uint8_t sstd_index = index; 156 uint8_t sstd_index = index;
169#endif
170 157
171 if (sstd_index > Transaction_table_size) return TRANSACTION_TYPE_ERROR; 158 if (sstd_index > NUM_TOTAL_TRANSACTIONS) return TRANSACTION_TYPE_ERROR;
172 SSTD_t* trans = &Transaction_table[sstd_index]; 159 split_transaction_desc_t* trans = &split_transaction_table[sstd_index];
173 msg_t res = 0; 160 msg_t res = 0;
161
162 if (!trans->status) return TRANSACTION_TYPE_ERROR; // not registered
174 163
175 sdClear(&SERIAL_USART_DRIVER); 164 sdClear(&SERIAL_USART_DRIVER);
176 165
@@ -189,7 +178,7 @@ int soft_serial_transaction(int index) {
189 } 178 }
190 179
191 if (trans->initiator2target_buffer_size) { 180 if (trans->initiator2target_buffer_size) {
192 res = sdWriteTimeout(&SERIAL_USART_DRIVER, trans->initiator2target_buffer, trans->initiator2target_buffer_size, TIME_MS2I(SERIAL_USART_TIMEOUT)); 181 res = sdWriteTimeout(&SERIAL_USART_DRIVER, split_trans_initiator2target_buffer(trans), trans->initiator2target_buffer_size, TIME_MS2I(SERIAL_USART_TIMEOUT));
193 if (res < 0) { 182 if (res < 0) {
194 dprintf("serial::usart_transmit NO_RESPONSE\n"); 183 dprintf("serial::usart_transmit NO_RESPONSE\n");
195 return TRANSACTION_NO_RESPONSE; 184 return TRANSACTION_NO_RESPONSE;
@@ -197,7 +186,7 @@ int soft_serial_transaction(int index) {
197 } 186 }
198 187
199 if (trans->target2initiator_buffer_size) { 188 if (trans->target2initiator_buffer_size) {
200 res = sdReadTimeout(&SERIAL_USART_DRIVER, trans->target2initiator_buffer, trans->target2initiator_buffer_size, TIME_MS2I(SERIAL_USART_TIMEOUT)); 189 res = sdReadTimeout(&SERIAL_USART_DRIVER, split_trans_target2initiator_buffer(trans), trans->target2initiator_buffer_size, TIME_MS2I(SERIAL_USART_TIMEOUT));
201 if (res < 0) { 190 if (res < 0) {
202 dprintf("serial::usart_receive NO_RESPONSE\n"); 191 dprintf("serial::usart_receive NO_RESPONSE\n");
203 return TRANSACTION_NO_RESPONSE; 192 return TRANSACTION_NO_RESPONSE;
diff --git a/drivers/chibios/spi_master.c b/drivers/chibios/spi_master.c
index 4852a6eba..28ddcbb2b 100644
--- a/drivers/chibios/spi_master.c
+++ b/drivers/chibios/spi_master.c
@@ -18,8 +18,13 @@
18 18
19#include "timer.h" 19#include "timer.h"
20 20
21static pin_t currentSlavePin = NO_PIN; 21static pin_t currentSlavePin = NO_PIN;
22static SPIConfig spiConfig = {false, NULL, 0, 0, 0, 0}; 22
23#if defined(K20x) || defined(KL2x)
24static SPIConfig spiConfig = {NULL, 0, 0, 0};
25#else
26static SPIConfig spiConfig = {false, NULL, 0, 0, 0, 0};
27#endif
23 28
24__attribute__((weak)) void spi_init(void) { 29__attribute__((weak)) void spi_init(void) {
25 static bool is_initialised = false; 30 static bool is_initialised = false;
@@ -27,15 +32,15 @@ __attribute__((weak)) void spi_init(void) {
27 is_initialised = true; 32 is_initialised = true;
28 33
29 // Try releasing special pins for a short time 34 // Try releasing special pins for a short time
30 palSetPadMode(PAL_PORT(SPI_SCK_PIN), PAL_PAD(SPI_SCK_PIN), PAL_MODE_INPUT); 35 setPinInput(SPI_SCK_PIN);
31 palSetPadMode(PAL_PORT(SPI_MOSI_PIN), PAL_PAD(SPI_MOSI_PIN), PAL_MODE_INPUT); 36 setPinInput(SPI_MOSI_PIN);
32 palSetPadMode(PAL_PORT(SPI_MISO_PIN), PAL_PAD(SPI_MISO_PIN), PAL_MODE_INPUT); 37 setPinInput(SPI_MISO_PIN);
33 38
34 chThdSleepMilliseconds(10); 39 chThdSleepMilliseconds(10);
35#if defined(USE_GPIOV1) 40#if defined(USE_GPIOV1)
36 palSetPadMode(PAL_PORT(SPI_SCK_PIN), PAL_PAD(SPI_SCK_PIN), PAL_MODE_STM32_ALTERNATE_PUSHPULL); 41 palSetPadMode(PAL_PORT(SPI_SCK_PIN), PAL_PAD(SPI_SCK_PIN), SPI_SCK_PAL_MODE);
37 palSetPadMode(PAL_PORT(SPI_MOSI_PIN), PAL_PAD(SPI_MOSI_PIN), PAL_MODE_STM32_ALTERNATE_PUSHPULL); 42 palSetPadMode(PAL_PORT(SPI_MOSI_PIN), PAL_PAD(SPI_MOSI_PIN), SPI_MOSI_PAL_MODE);
38 palSetPadMode(PAL_PORT(SPI_MISO_PIN), PAL_PAD(SPI_MISO_PIN), PAL_MODE_STM32_ALTERNATE_PUSHPULL); 43 palSetPadMode(PAL_PORT(SPI_MISO_PIN), PAL_PAD(SPI_MISO_PIN), SPI_MISO_PAL_MODE);
39#else 44#else
40 palSetPadMode(PAL_PORT(SPI_SCK_PIN), PAL_PAD(SPI_SCK_PIN), PAL_MODE_ALTERNATE(SPI_SCK_PAL_MODE) | PAL_STM32_OTYPE_PUSHPULL | PAL_STM32_OSPEED_HIGHEST); 45 palSetPadMode(PAL_PORT(SPI_SCK_PIN), PAL_PAD(SPI_SCK_PIN), PAL_MODE_ALTERNATE(SPI_SCK_PAL_MODE) | PAL_STM32_OTYPE_PUSHPULL | PAL_STM32_OSPEED_HIGHEST);
41 palSetPadMode(PAL_PORT(SPI_MOSI_PIN), PAL_PAD(SPI_MOSI_PIN), PAL_MODE_ALTERNATE(SPI_MOSI_PAL_MODE) | PAL_STM32_OTYPE_PUSHPULL | PAL_STM32_OSPEED_HIGHEST); 46 palSetPadMode(PAL_PORT(SPI_MOSI_PIN), PAL_PAD(SPI_MOSI_PIN), PAL_MODE_ALTERNATE(SPI_MOSI_PAL_MODE) | PAL_STM32_OTYPE_PUSHPULL | PAL_STM32_OSPEED_HIGHEST);
@@ -58,6 +63,54 @@ bool spi_start(pin_t slavePin, bool lsbFirst, uint8_t mode, uint16_t divisor) {
58 return false; 63 return false;
59 } 64 }
60 65
66#if defined(K20x) || defined(KL2x)
67 spiConfig.tar0 = SPIx_CTARn_FMSZ(7) | SPIx_CTARn_ASC(1);
68
69 if (lsbFirst) {
70 spiConfig.tar0 |= SPIx_CTARn_LSBFE;
71 }
72
73 switch (mode) {
74 case 0:
75 break;
76 case 1:
77 spiConfig.tar0 |= SPIx_CTARn_CPHA;
78 break;
79 case 2:
80 spiConfig.tar0 |= SPIx_CTARn_CPOL;
81 break;
82 case 3:
83 spiConfig.tar0 |= SPIx_CTARn_CPHA | SPIx_CTARn_CPOL;
84 break;
85 }
86
87 switch (roundedDivisor) {
88 case 2:
89 spiConfig.tar0 |= SPIx_CTARn_BR(0);
90 break;
91 case 4:
92 spiConfig.tar0 |= SPIx_CTARn_BR(1);
93 break;
94 case 8:
95 spiConfig.tar0 |= SPIx_CTARn_BR(3);
96 break;
97 case 16:
98 spiConfig.tar0 |= SPIx_CTARn_BR(4);
99 break;
100 case 32:
101 spiConfig.tar0 |= SPIx_CTARn_BR(5);
102 break;
103 case 64:
104 spiConfig.tar0 |= SPIx_CTARn_BR(6);
105 break;
106 case 128:
107 spiConfig.tar0 |= SPIx_CTARn_BR(7);
108 break;
109 case 256:
110 spiConfig.tar0 |= SPIx_CTARn_BR(8);
111 break;
112 }
113#else
61 spiConfig.cr1 = 0; 114 spiConfig.cr1 = 0;
62 115
63 if (lsbFirst) { 116 if (lsbFirst) {
@@ -103,6 +156,7 @@ bool spi_start(pin_t slavePin, bool lsbFirst, uint8_t mode, uint16_t divisor) {
103 spiConfig.cr1 |= SPI_CR1_BR_2 | SPI_CR1_BR_1 | SPI_CR1_BR_0; 156 spiConfig.cr1 |= SPI_CR1_BR_2 | SPI_CR1_BR_1 | SPI_CR1_BR_0;
104 break; 157 break;
105 } 158 }
159#endif
106 160
107 currentSlavePin = slavePin; 161 currentSlavePin = slavePin;
108 spiConfig.ssport = PAL_PORT(slavePin); 162 spiConfig.ssport = PAL_PORT(slavePin);
diff --git a/drivers/chibios/spi_master.h b/drivers/chibios/spi_master.h
index e93580e31..b5a6ef143 100644
--- a/drivers/chibios/spi_master.h
+++ b/drivers/chibios/spi_master.h
@@ -21,6 +21,7 @@
21#include <stdbool.h> 21#include <stdbool.h>
22 22
23#include "gpio.h" 23#include "gpio.h"
24#include "chibios_config.h"
24 25
25#ifndef SPI_DRIVER 26#ifndef SPI_DRIVER
26# define SPI_DRIVER SPID2 27# define SPI_DRIVER SPID2
@@ -31,7 +32,11 @@
31#endif 32#endif
32 33
33#ifndef SPI_SCK_PAL_MODE 34#ifndef SPI_SCK_PAL_MODE
34# define SPI_SCK_PAL_MODE 5 35# if defined(USE_GPIOV1)
36# define SPI_SCK_PAL_MODE PAL_MODE_STM32_ALTERNATE_PUSHPULL
37# else
38# define SPI_SCK_PAL_MODE 5
39# endif
35#endif 40#endif
36 41
37#ifndef SPI_MOSI_PIN 42#ifndef SPI_MOSI_PIN
@@ -39,7 +44,11 @@
39#endif 44#endif
40 45
41#ifndef SPI_MOSI_PAL_MODE 46#ifndef SPI_MOSI_PAL_MODE
42# define SPI_MOSI_PAL_MODE 5 47# if defined(USE_GPIOV1)
48# define SPI_MOSI_PAL_MODE PAL_MODE_STM32_ALTERNATE_PUSHPULL
49# else
50# define SPI_MOSI_PAL_MODE 5
51# endif
43#endif 52#endif
44 53
45#ifndef SPI_MISO_PIN 54#ifndef SPI_MISO_PIN
@@ -47,7 +56,11 @@
47#endif 56#endif
48 57
49#ifndef SPI_MISO_PAL_MODE 58#ifndef SPI_MISO_PAL_MODE
50# define SPI_MISO_PAL_MODE 5 59# if defined(USE_GPIOV1)
60# define SPI_MISO_PAL_MODE PAL_MODE_STM32_ALTERNATE_PUSHPULL
61# else
62# define SPI_MISO_PAL_MODE 5
63# endif
51#endif 64#endif
52 65
53typedef int16_t spi_status_t; 66typedef int16_t spi_status_t;
diff --git a/drivers/lcd/st7565.c b/drivers/lcd/st7565.c
new file mode 100644
index 000000000..49b13c00f
--- /dev/null
+++ b/drivers/lcd/st7565.c
@@ -0,0 +1,496 @@
1/*
2Copyright 2021
3
4This program is free software: you can redistribute it and/or modify
5it under the terms of the GNU General Public License as published by
6the Free Software Foundation, either version 2 of the License, or
7(at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License
15along with this program. If not, see <http://www.gnu.org/licenses/>.
16*/
17
18#include "st7565.h"
19
20#include <string.h>
21
22#include "keyboard.h"
23#include "progmem.h"
24#include "timer.h"
25#include "wait.h"
26
27#include ST7565_FONT_H
28
29// Fundamental Commands
30#define CONTRAST 0x81
31#define DISPLAY_ALL_ON 0xA5
32#define DISPLAY_ALL_ON_RESUME 0xA4
33#define NORMAL_DISPLAY 0xA6
34#define INVERT_DISPLAY 0xA7
35#define DISPLAY_ON 0xAF
36#define DISPLAY_OFF 0xAE
37#define NOP 0xE3
38
39// Addressing Setting Commands
40#define PAM_SETCOLUMN_LSB 0x00
41#define PAM_SETCOLUMN_MSB 0x10
42#define PAM_PAGE_ADDR 0xB0 // 0xb0 -- 0xb7
43
44// Hardware Configuration Commands
45#define DISPLAY_START_LINE 0x40
46#define SEGMENT_REMAP 0xA0
47#define SEGMENT_REMAP_INV 0xA1
48#define COM_SCAN_INC 0xC0
49#define COM_SCAN_DEC 0xC8
50#define LCD_BIAS_7 0xA3
51#define LCD_BIAS_9 0xA2
52#define RESISTOR_RATIO 0x20
53#define POWER_CONTROL 0x28
54
55// Misc defines
56#ifndef ST7565_BLOCK_COUNT
57# define ST7565_BLOCK_COUNT (sizeof(ST7565_BLOCK_TYPE) * 8)
58#endif
59#ifndef ST7565_BLOCK_SIZE
60# define ST7565_BLOCK_SIZE (ST7565_MATRIX_SIZE / ST7565_BLOCK_COUNT)
61#endif
62
63#define ST7565_ALL_BLOCKS_MASK (((((ST7565_BLOCK_TYPE)1 << (ST7565_BLOCK_COUNT - 1)) - 1) << 1) | 1)
64
65#define HAS_FLAGS(bits, flags) ((bits & flags) == flags)
66
67// Display buffer's is the same as the display memory layout
68// this is so we don't end up with rounding errors with
69// parts of the display unusable or don't get cleared correctly
70// and also allows for drawing & inverting
71uint8_t st7565_buffer[ST7565_MATRIX_SIZE];
72uint8_t * st7565_cursor;
73ST7565_BLOCK_TYPE st7565_dirty = 0;
74bool st7565_initialized = false;
75bool st7565_active = false;
76bool st7565_inverted = false;
77display_rotation_t st7565_rotation = DISPLAY_ROTATION_0;
78#if ST7565_TIMEOUT > 0
79uint32_t st7565_timeout;
80#endif
81#if ST7565_UPDATE_INTERVAL > 0
82uint16_t st7565_update_timeout;
83#endif
84
85// Flips the rendering bits for a character at the current cursor position
86static void InvertCharacter(uint8_t *cursor) {
87 const uint8_t *end = cursor + ST7565_FONT_WIDTH;
88 while (cursor < end) {
89 *cursor = ~(*cursor);
90 cursor++;
91 }
92}
93
94bool st7565_init(display_rotation_t rotation) {
95 setPinOutput(ST7565_A0_PIN);
96 writePinHigh(ST7565_A0_PIN);
97 setPinOutput(ST7565_RST_PIN);
98 writePinHigh(ST7565_RST_PIN);
99
100 st7565_rotation = st7565_init_user(rotation);
101
102 spi_init();
103 spi_start(ST7565_SS_PIN, false, 0, ST7565_SPI_CLK_DIVISOR);
104
105 st7565_reset();
106
107 st7565_send_cmd(LCD_BIAS_7);
108 if (!HAS_FLAGS(st7565_rotation, DISPLAY_ROTATION_180)) {
109 st7565_send_cmd(SEGMENT_REMAP);
110 st7565_send_cmd(COM_SCAN_DEC);
111 } else {
112 st7565_send_cmd(SEGMENT_REMAP_INV);
113 st7565_send_cmd(COM_SCAN_INC);
114 }
115 st7565_send_cmd(DISPLAY_START_LINE | 0x00);
116 st7565_send_cmd(CONTRAST);
117 st7565_send_cmd(ST7565_CONTRAST);
118 st7565_send_cmd(RESISTOR_RATIO | 0x01);
119 st7565_send_cmd(POWER_CONTROL | 0x04);
120 wait_ms(50);
121 st7565_send_cmd(POWER_CONTROL | 0x06);
122 wait_ms(50);
123 st7565_send_cmd(POWER_CONTROL | 0x07);
124 wait_ms(10);
125 st7565_send_cmd(DISPLAY_ON);
126 st7565_send_cmd(DISPLAY_ALL_ON_RESUME);
127 st7565_send_cmd(NORMAL_DISPLAY);
128
129 spi_stop();
130
131#if ST7565_TIMEOUT > 0
132 st7565_timeout = timer_read32() + ST7565_TIMEOUT;
133#endif
134
135 st7565_clear();
136 st7565_initialized = true;
137 st7565_active = true;
138 return true;
139}
140
141__attribute__((weak)) display_rotation_t st7565_init_user(display_rotation_t rotation) { return rotation; }
142
143void st7565_clear(void) {
144 memset(st7565_buffer, 0, sizeof(st7565_buffer));
145 st7565_cursor = &st7565_buffer[0];
146 st7565_dirty = ST7565_ALL_BLOCKS_MASK;
147}
148
149uint8_t crot(uint8_t a, int8_t n) {
150 const uint8_t mask = 0x7;
151 n &= mask;
152 return a << n | a >> (-n & mask);
153}
154
155void st7565_render(void) {
156 if (!st7565_initialized) {
157 return;
158 }
159
160 // Do we have work to do?
161 st7565_dirty &= ST7565_ALL_BLOCKS_MASK;
162 if (!st7565_dirty) {
163 return;
164 }
165
166 // Find first dirty block
167 uint8_t update_start = 0;
168 while (!(st7565_dirty & ((ST7565_BLOCK_TYPE)1 << update_start))) {
169 ++update_start;
170 }
171
172 // Calculate commands to set memory addressing bounds.
173 uint8_t start_page = ST7565_BLOCK_SIZE * update_start / ST7565_DISPLAY_WIDTH;
174 uint8_t start_column = ST7565_BLOCK_SIZE * update_start % ST7565_DISPLAY_WIDTH;
175 // IC has 132 segment drivers, for panels with less width we need to offset the starting column
176 if (HAS_FLAGS(st7565_rotation, DISPLAY_ROTATION_180)) {
177 start_column += (132 - ST7565_DISPLAY_WIDTH);
178 }
179
180 spi_start(ST7565_SS_PIN, false, 0, ST7565_SPI_CLK_DIVISOR);
181
182 st7565_send_cmd(PAM_PAGE_ADDR | start_page);
183 st7565_send_cmd(PAM_SETCOLUMN_LSB | ((ST7565_COLUMN_OFFSET + start_column) & 0x0f));
184 st7565_send_cmd(PAM_SETCOLUMN_MSB | ((ST7565_COLUMN_OFFSET + start_column) >> 4 & 0x0f));
185
186 st7565_send_data(&st7565_buffer[ST7565_BLOCK_SIZE * update_start], ST7565_BLOCK_SIZE);
187
188 // Turn on display if it is off
189 st7565_on();
190
191 // Clear dirty flag
192 st7565_dirty &= ~((ST7565_BLOCK_TYPE)1 << update_start);
193}
194
195void st7565_set_cursor(uint8_t col, uint8_t line) {
196 uint16_t index = line * ST7565_DISPLAY_WIDTH + col * ST7565_FONT_WIDTH;
197
198 // Out of bounds?
199 if (index >= ST7565_MATRIX_SIZE) {
200 index = 0;
201 }
202
203 st7565_cursor = &st7565_buffer[index];
204}
205
206void st7565_advance_page(bool clearPageRemainder) {
207 uint16_t index = st7565_cursor - &st7565_buffer[0];
208 uint8_t remaining = ST7565_DISPLAY_WIDTH - (index % ST7565_DISPLAY_WIDTH);
209
210 if (clearPageRemainder) {
211 // Remaining Char count
212 remaining = remaining / ST7565_FONT_WIDTH;
213
214 // Write empty character until next line
215 while (remaining--) st7565_write_char(' ', false);
216 } else {
217 // Next page index out of bounds?
218 if (index + remaining >= ST7565_MATRIX_SIZE) {
219 index = 0;
220 remaining = 0;
221 }
222
223 st7565_cursor = &st7565_buffer[index + remaining];
224 }
225}
226
227void st7565_advance_char(void) {
228 uint16_t nextIndex = st7565_cursor - &st7565_buffer[0] + ST7565_FONT_WIDTH;
229 uint8_t remainingSpace = ST7565_DISPLAY_WIDTH - (nextIndex % ST7565_DISPLAY_WIDTH);
230
231 // Do we have enough space on the current line for the next character
232 if (remainingSpace < ST7565_FONT_WIDTH) {
233 nextIndex += remainingSpace;
234 }
235
236 // Did we go out of bounds
237 if (nextIndex >= ST7565_MATRIX_SIZE) {
238 nextIndex = 0;
239 }
240
241 // Update cursor position
242 st7565_cursor = &st7565_buffer[nextIndex];
243}
244
245// Main handler that writes character data to the display buffer
246void st7565_write_char(const char data, bool invert) {
247 // Advance to the next line if newline
248 if (data == '\n') {
249 // Old source wrote ' ' until end of line...
250 st7565_advance_page(true);
251 return;
252 }
253
254 if (data == '\r') {
255 st7565_advance_page(false);
256 return;
257 }
258
259 // copy the current render buffer to check for dirty after
260 static uint8_t st7565_temp_buffer[ST7565_FONT_WIDTH];
261 memcpy(&st7565_temp_buffer, st7565_cursor, ST7565_FONT_WIDTH);
262
263 _Static_assert(sizeof(font) >= ((ST7565_FONT_END + 1 - ST7565_FONT_START) * ST7565_FONT_WIDTH), "ST7565_FONT_END references outside array");
264
265 // set the reder buffer data
266 uint8_t cast_data = (uint8_t)data; // font based on unsigned type for index
267 if (cast_data < ST7565_FONT_START || cast_data > ST7565_FONT_END) {
268 memset(st7565_cursor, 0x00, ST7565_FONT_WIDTH);
269 } else {
270 const uint8_t *glyph = &font[(cast_data - ST7565_FONT_START) * ST7565_FONT_WIDTH];
271 memcpy_P(st7565_cursor, glyph, ST7565_FONT_WIDTH);
272 }
273
274 // Invert if needed
275 if (invert) {
276 InvertCharacter(st7565_cursor);
277 }
278
279 // Dirty check
280 if (memcmp(&st7565_temp_buffer, st7565_cursor, ST7565_FONT_WIDTH)) {
281 uint16_t index = st7565_cursor - &st7565_buffer[0];
282 st7565_dirty |= ((ST7565_BLOCK_TYPE)1 << (index / ST7565_BLOCK_SIZE));
283 // Edgecase check if the written data spans the 2 chunks
284 st7565_dirty |= ((ST7565_BLOCK_TYPE)1 << ((index + ST7565_FONT_WIDTH - 1) / ST7565_BLOCK_SIZE));
285 }
286
287 // Finally move to the next char
288 st7565_advance_char();
289}
290
291void st7565_write(const char *data, bool invert) {
292 const char *end = data + strlen(data);
293 while (data < end) {
294 st7565_write_char(*data, invert);
295 data++;
296 }
297}
298
299void st7565_write_ln(const char *data, bool invert) {
300 st7565_write(data, invert);
301 st7565_advance_page(true);
302}
303
304void st7565_pan(bool left) {
305 uint16_t i = 0;
306 for (uint16_t y = 0; y < ST7565_DISPLAY_HEIGHT / 8; y++) {
307 if (left) {
308 for (uint16_t x = 0; x < ST7565_DISPLAY_WIDTH - 1; x++) {
309 i = y * ST7565_DISPLAY_WIDTH + x;
310 st7565_buffer[i] = st7565_buffer[i + 1];
311 }
312 } else {
313 for (uint16_t x = ST7565_DISPLAY_WIDTH - 1; x > 0; x--) {
314 i = y * ST7565_DISPLAY_WIDTH + x;
315 st7565_buffer[i] = st7565_buffer[i - 1];
316 }
317 }
318 }
319 st7565_dirty = ST7565_ALL_BLOCKS_MASK;
320}
321
322display_buffer_reader_t st7565_read_raw(uint16_t start_index) {
323 if (start_index > ST7565_MATRIX_SIZE) start_index = ST7565_MATRIX_SIZE;
324 display_buffer_reader_t ret_reader;
325 ret_reader.current_element = &st7565_buffer[start_index];
326 ret_reader.remaining_element_count = ST7565_MATRIX_SIZE - start_index;
327 return ret_reader;
328}
329
330void st7565_write_raw_byte(const char data, uint16_t index) {
331 if (index > ST7565_MATRIX_SIZE) index = ST7565_MATRIX_SIZE;
332 if (st7565_buffer[index] == data) return;
333 st7565_buffer[index] = data;
334 st7565_dirty |= ((ST7565_BLOCK_TYPE)1 << (index / ST7565_BLOCK_SIZE));
335}
336
337void st7565_write_raw(const char *data, uint16_t size) {
338 uint16_t cursor_start_index = st7565_cursor - &st7565_buffer[0];
339 if ((size + cursor_start_index) > ST7565_MATRIX_SIZE) size = ST7565_MATRIX_SIZE - cursor_start_index;
340 for (uint16_t i = cursor_start_index; i < cursor_start_index + size; i++) {
341 uint8_t c = *data++;
342 if (st7565_buffer[i] == c) continue;
343 st7565_buffer[i] = c;
344 st7565_dirty |= ((ST7565_BLOCK_TYPE)1 << (i / ST7565_BLOCK_SIZE));
345 }
346}
347
348void st7565_write_pixel(uint8_t x, uint8_t y, bool on) {
349 if (x >= ST7565_DISPLAY_WIDTH) {
350 return;
351 }
352 uint16_t index = x + (y / 8) * ST7565_DISPLAY_WIDTH;
353 if (index >= ST7565_MATRIX_SIZE) {
354 return;
355 }
356 uint8_t data = st7565_buffer[index];
357 if (on) {
358 data |= (1 << (y % 8));
359 } else {
360 data &= ~(1 << (y % 8));
361 }
362 if (st7565_buffer[index] != data) {
363 st7565_buffer[index] = data;
364 st7565_dirty |= ((ST7565_BLOCK_TYPE)1 << (index / ST7565_BLOCK_SIZE));
365 }
366}
367
368#if defined(__AVR__)
369void st7565_write_P(const char *data, bool invert) {
370 uint8_t c = pgm_read_byte(data);
371 while (c != 0) {
372 st7565_write_char(c, invert);
373 c = pgm_read_byte(++data);
374 }
375}
376
377void st7565_write_ln_P(const char *data, bool invert) {
378 st7565_write_P(data, invert);
379 st7565_advance_page(true);
380}
381
382void st7565_write_raw_P(const char *data, uint16_t size) {
383 uint16_t cursor_start_index = st7565_cursor - &st7565_buffer[0];
384 if ((size + cursor_start_index) > ST7565_MATRIX_SIZE) size = ST7565_MATRIX_SIZE - cursor_start_index;
385 for (uint16_t i = cursor_start_index; i < cursor_start_index + size; i++) {
386 uint8_t c = pgm_read_byte(data++);
387 if (st7565_buffer[i] == c) continue;
388 st7565_buffer[i] = c;
389 st7565_dirty |= ((ST7565_BLOCK_TYPE)1 << (i / ST7565_BLOCK_SIZE));
390 }
391}
392#endif // defined(__AVR__)
393
394bool st7565_on(void) {
395 if (!st7565_initialized) {
396 return st7565_active;
397 }
398
399#if ST7565_TIMEOUT > 0
400 st7565_timeout = timer_read32() + ST7565_TIMEOUT;
401#endif
402
403 if (!st7565_active) {
404 spi_start(ST7565_SS_PIN, false, 0, ST7565_SPI_CLK_DIVISOR);
405 st7565_send_cmd(DISPLAY_ON);
406 spi_stop();
407 st7565_active = true;
408 st7565_on_user();
409 }
410 return st7565_active;
411}
412
413__attribute__((weak)) void st7565_on_user(void) {}
414
415bool st7565_off(void) {
416 if (!st7565_initialized) {
417 return !st7565_active;
418 }
419
420 if (st7565_active) {
421 spi_start(ST7565_SS_PIN, false, 0, ST7565_SPI_CLK_DIVISOR);
422 st7565_send_cmd(DISPLAY_OFF);
423 spi_stop();
424 st7565_active = false;
425 st7565_off_user();
426 }
427 return !st7565_active;
428}
429
430__attribute__((weak)) void st7565_off_user(void) {}
431
432bool st7565_is_on(void) { return st7565_active; }
433
434bool st7565_invert(bool invert) {
435 if (!st7565_initialized) {
436 return st7565_inverted;
437 }
438
439 if (invert != st7565_inverted) {
440 spi_start(ST7565_SS_PIN, false, 0, ST7565_SPI_CLK_DIVISOR);
441 st7565_send_cmd(invert ? INVERT_DISPLAY : NORMAL_DISPLAY);
442 spi_stop();
443 st7565_inverted = invert;
444 }
445 return st7565_inverted;
446}
447
448uint8_t st7565_max_chars(void) { return ST7565_DISPLAY_WIDTH / ST7565_FONT_WIDTH; }
449
450uint8_t st7565_max_lines(void) { return ST7565_DISPLAY_HEIGHT / ST7565_FONT_HEIGHT; }
451
452void st7565_task(void) {
453 if (!st7565_initialized) {
454 return;
455 }
456
457#if ST7565_UPDATE_INTERVAL > 0
458 if (timer_elapsed(st7565_update_timeout) >= ST7565_UPDATE_INTERVAL) {
459 st7565_update_timeout = timer_read();
460 st7565_set_cursor(0, 0);
461 st7565_task_user();
462 }
463#else
464 st7565_set_cursor(0, 0);
465 st7565_task_user();
466#endif
467
468 // Smart render system, no need to check for dirty
469 st7565_render();
470
471 // Display timeout check
472#if ST7565_TIMEOUT > 0
473 if (st7565_active && timer_expired32(timer_read32(), st7565_timeout)) {
474 st7565_off();
475 }
476#endif
477}
478
479__attribute__((weak)) void st7565_task_user(void) {}
480
481void st7565_reset(void) {
482 writePinLow(ST7565_RST_PIN);
483 wait_ms(20);
484 writePinHigh(ST7565_RST_PIN);
485 wait_ms(20);
486}
487
488spi_status_t st7565_send_cmd(uint8_t cmd) {
489 writePinLow(ST7565_A0_PIN);
490 return spi_write(cmd);
491}
492
493spi_status_t st7565_send_data(uint8_t *data, uint16_t length) {
494 writePinHigh(ST7565_A0_PIN);
495 return spi_transmit(data, length);
496}
diff --git a/drivers/lcd/st7565.h b/drivers/lcd/st7565.h
new file mode 100644
index 000000000..d453dbe6d
--- /dev/null
+++ b/drivers/lcd/st7565.h
@@ -0,0 +1,219 @@
1/*
2Copyright 2021
3
4This program is free software: you can redistribute it and/or modify
5it under the terms of the GNU General Public License as published by
6the Free Software Foundation, either version 2 of the License, or
7(at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License
15along with this program. If not, see <http://www.gnu.org/licenses/>.
16*/
17
18#pragma once
19
20#include <stdint.h>
21#include <stdbool.h>
22
23#include "spi_master.h"
24
25#ifndef ST7565_DISPLAY_WIDTH
26# define ST7565_DISPLAY_WIDTH 128
27#endif
28#ifndef ST7565_DISPLAY_HEIGHT
29# define ST7565_DISPLAY_HEIGHT 32
30#endif
31#ifndef ST7565_MATRIX_SIZE
32# define ST7565_MATRIX_SIZE (ST7565_DISPLAY_HEIGHT / 8 * ST7565_DISPLAY_WIDTH) // 1024 (compile time mathed)
33#endif
34#ifndef ST7565_BLOCK_TYPE
35# define ST7565_BLOCK_TYPE uint16_t
36#endif
37#ifndef ST7565_BLOCK_COUNT
38# define ST7565_BLOCK_COUNT (sizeof(ST7565_BLOCK_TYPE) * 8) // 32 (compile time mathed)
39#endif
40#ifndef ST7565_BLOCK_SIZE
41# define ST7565_BLOCK_SIZE (ST7565_MATRIX_SIZE / ST7565_BLOCK_COUNT) // 32 (compile time mathed)
42#endif
43
44// the column address corresponding to the first column in the display hardware
45#if !defined(ST7565_COLUMN_OFFSET)
46# define ST7565_COLUMN_OFFSET 0
47#endif
48
49// spi clock divisor
50#if !defined(ST7565_SPI_CLK_DIVISOR)
51# define ST7565_SPI_CLK_DIVISOR 4
52#endif
53
54// Custom font file to use
55#if !defined(ST7565_FONT_H)
56# define ST7565_FONT_H "glcdfont.c"
57#endif
58// unsigned char value of the first character in the font file
59#if !defined(ST7565_FONT_START)
60# define ST7565_FONT_START 0
61#endif
62// unsigned char value of the last character in the font file
63#if !defined(ST7565_FONT_END)
64# define ST7565_FONT_END 223
65#endif
66// Font render width
67#if !defined(ST7565_FONT_WIDTH)
68# define ST7565_FONT_WIDTH 6
69#endif
70// Font render height
71#if !defined(ST7565_FONT_HEIGHT)
72# define ST7565_FONT_HEIGHT 8
73#endif
74// Default contrast level
75#if !defined(ST7565_CONTRAST)
76# define ST7565_CONTRAST 32
77#endif
78
79#if !defined(ST7565_TIMEOUT)
80# if defined(ST7565_DISABLE_TIMEOUT)
81# define ST7565_TIMEOUT 0
82# else
83# define ST7565_TIMEOUT 60000
84# endif
85#endif
86
87#if !defined(ST7565_UPDATE_INTERVAL) && defined(SPLIT_KEYBOARD)
88# define ST7565_UPDATE_INTERVAL 50
89#endif
90
91typedef struct __attribute__((__packed__)) {
92 uint8_t *current_element;
93 uint16_t remaining_element_count;
94} display_buffer_reader_t;
95
96// Rotation enum values are flags
97typedef enum { DISPLAY_ROTATION_0, DISPLAY_ROTATION_180 } display_rotation_t;
98
99// Initialize the display, rotating the rendered output based on the define passed in.
100// Returns true if the display was initialized successfully
101bool st7565_init(display_rotation_t rotation);
102
103// Called at the start of st7565_init, weak function overridable by the user
104// rotation - the value passed into st7565_init
105// Return new display_rotation_t if you want to override default rotation
106display_rotation_t st7565_init_user(display_rotation_t rotation);
107
108// Clears the display buffer, resets cursor position to 0, and sets the buffer to dirty for rendering
109void st7565_clear(void);
110
111// Renders the dirty chunks of the buffer to display
112void st7565_render(void);
113
114// Moves cursor to character position indicated by column and line, wraps if out of bounds
115// Max column denoted by 'st7565_max_chars()' and max lines by 'st7565_max_lines()' functions
116void st7565_set_cursor(uint8_t col, uint8_t line);
117
118// Advances the cursor to the next page, writing ' ' if true
119// Wraps to the begining when out of bounds
120void st7565_advance_page(bool clearPageRemainder);
121
122// Moves the cursor forward 1 character length
123// Advance page if there is not enough room for the next character
124// Wraps to the begining when out of bounds
125void st7565_advance_char(void);
126
127// Writes a single character to the buffer at current cursor position
128// Advances the cursor while writing, inverts the pixels if true
129// Main handler that writes character data to the display buffer
130void st7565_write_char(const char data, bool invert);
131
132// Writes a string to the buffer at current cursor position
133// Advances the cursor while writing, inverts the pixels if true
134void st7565_write(const char *data, bool invert);
135
136// Writes a string to the buffer at current cursor position
137// Advances the cursor while writing, inverts the pixels if true
138// Advances the cursor to the next page, wiring ' ' to the remainder of the current page
139void st7565_write_ln(const char *data, bool invert);
140
141// Pans the buffer to the right (or left by passing true) by moving contents of the buffer
142// Useful for moving the screen in preparation for new drawing
143void st7565_pan(bool left);
144
145// Returns a pointer to the requested start index in the buffer plus remaining
146// buffer length as struct
147display_buffer_reader_t st7565_read_raw(uint16_t start_index);
148
149// Writes a string to the buffer at current cursor position
150void st7565_write_raw(const char *data, uint16_t size);
151
152// Writes a single byte into the buffer at the specified index
153void st7565_write_raw_byte(const char data, uint16_t index);
154
155// Sets a specific pixel on or off
156// Coordinates start at top-left and go right and down for positive x and y
157void st7565_write_pixel(uint8_t x, uint8_t y, bool on);
158
159#if defined(__AVR__)
160// Writes a PROGMEM string to the buffer at current cursor position
161// Advances the cursor while writing, inverts the pixels if true
162// Remapped to call 'void st7565_write(const char *data, bool invert);' on ARM
163void st7565_write_P(const char *data, bool invert);
164
165// Writes a PROGMEM string to the buffer at current cursor position
166// Advances the cursor while writing, inverts the pixels if true
167// Advances the cursor to the next page, wiring ' ' to the remainder of the current page
168// Remapped to call 'void st7565_write_ln(const char *data, bool invert);' on ARM
169void st7565_write_ln_P(const char *data, bool invert);
170
171// Writes a PROGMEM string to the buffer at current cursor position
172void st7565_write_raw_P(const char *data, uint16_t size);
173#else
174# define st7565_write_P(data, invert) st7565_write(data, invert)
175# define st7565_write_ln_P(data, invert) st7565_write_ln(data, invert)
176# define st7565_write_raw_P(data, size) st7565_write_raw(data, size)
177#endif // defined(__AVR__)
178
179// Can be used to manually turn on the screen if it is off
180// Returns true if the screen was on or turns on
181bool st7565_on(void);
182
183// Called when st7565_on() turns on the screen, weak function overridable by the user
184// Not called if the screen is already on
185void st7565_on_user(void);
186
187// Can be used to manually turn off the screen if it is on
188// Returns true if the screen was off or turns off
189bool st7565_off(void);
190
191// Called when st7565_off() turns off the screen, weak function overridable by the user
192// Not called if the screen is already off
193void st7565_off_user(void);
194
195// Returns true if the screen is currently on, false if it is
196// not
197bool st7565_is_on(void);
198
199// Basically it's st7565_render, but with timeout management and st7565_task_user calling!
200void st7565_task(void);
201
202// Called at the start of st7565_task, weak function overridable by the user
203void st7565_task_user(void);
204
205// Inverts the display
206// Returns true if the screen was or is inverted
207bool st7565_invert(bool invert);
208
209// Returns the maximum number of characters that will fit on a line
210uint8_t st7565_max_chars(void);
211
212// Returns the maximum number of lines that will fit on the display
213uint8_t st7565_max_lines(void);
214
215void st7565_reset(void);
216
217spi_status_t st7565_send_cmd(uint8_t cmd);
218
219spi_status_t st7565_send_data(uint8_t *data, uint16_t length);
diff --git a/drivers/oled/oled_driver.c b/drivers/oled/oled_driver.c
index 8e5ed5f07..7d4197890 100644
--- a/drivers/oled/oled_driver.c
+++ b/drivers/oled/oled_driver.c
@@ -34,6 +34,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
34#define DISPLAY_ALL_ON 0xA5 34#define DISPLAY_ALL_ON 0xA5
35#define DISPLAY_ALL_ON_RESUME 0xA4 35#define DISPLAY_ALL_ON_RESUME 0xA4
36#define NORMAL_DISPLAY 0xA6 36#define NORMAL_DISPLAY 0xA6
37#define INVERT_DISPLAY 0xA7
37#define DISPLAY_ON 0xAF 38#define DISPLAY_ON 0xAF
38#define DISPLAY_OFF 0xAE 39#define DISPLAY_OFF 0xAE
39#define NOP 0xE3 40#define NOP 0xE3
@@ -114,6 +115,7 @@ OLED_BLOCK_TYPE oled_dirty = 0;
114bool oled_initialized = false; 115bool oled_initialized = false;
115bool oled_active = false; 116bool oled_active = false;
116bool oled_scrolling = false; 117bool oled_scrolling = false;
118bool oled_inverted = false;
117uint8_t oled_brightness = OLED_BRIGHTNESS; 119uint8_t oled_brightness = OLED_BRIGHTNESS;
118oled_rotation_t oled_rotation = 0; 120oled_rotation_t oled_rotation = 0;
119uint8_t oled_rotation_width = 0; 121uint8_t oled_rotation_width = 0;
@@ -690,6 +692,30 @@ bool oled_scroll_off(void) {
690 return !oled_scrolling; 692 return !oled_scrolling;
691} 693}
692 694
695bool oled_invert(bool invert) {
696 if (!oled_initialized) {
697 return oled_inverted;
698 }
699
700 if (invert && !oled_inverted) {
701 static const uint8_t PROGMEM display_inverted[] = {I2C_CMD, INVERT_DISPLAY};
702 if (I2C_TRANSMIT_P(display_inverted) != I2C_STATUS_SUCCESS) {
703 print("oled_invert cmd failed\n");
704 return oled_inverted;
705 }
706 oled_inverted = true;
707 } else if (!invert && oled_inverted) {
708 static const uint8_t PROGMEM display_normal[] = {I2C_CMD, NORMAL_DISPLAY};
709 if (I2C_TRANSMIT_P(display_normal) != I2C_STATUS_SUCCESS) {
710 print("oled_invert cmd failed\n");
711 return oled_inverted;
712 }
713 oled_inverted = false;
714 }
715
716 return oled_inverted;
717}
718
693uint8_t oled_max_chars(void) { 719uint8_t oled_max_chars(void) {
694 if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_90)) { 720 if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_90)) {
695 return OLED_DISPLAY_WIDTH / OLED_FONT_WIDTH; 721 return OLED_DISPLAY_WIDTH / OLED_FONT_WIDTH;
diff --git a/drivers/oled/oled_driver.h b/drivers/oled/oled_driver.h
index a6b85f37e..fc68f0ec9 100644
--- a/drivers/oled/oled_driver.h
+++ b/drivers/oled/oled_driver.h
@@ -313,6 +313,10 @@ bool oled_scroll_left(void);
313// Returns true if the screen was not scrolling or stops scrolling 313// Returns true if the screen was not scrolling or stops scrolling
314bool oled_scroll_off(void); 314bool oled_scroll_off(void);
315 315
316// Inverts the display
317// Returns true if the screen was or is inverted
318bool oled_invert(bool invert);
319
316// Returns the maximum number of characters that will fit on a line 320// Returns the maximum number of characters that will fit on a line
317uint8_t oled_max_chars(void); 321uint8_t oled_max_chars(void);
318 322
diff --git a/drivers/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);