qmk

Unnamed repository; edit this file 'description' to name the repository.
git clone git://git.z3bra.org/qmk.git
Log | Files | Refs | Submodules | LICENSE

commit feb5e4aaebb78842c090230f68ea9de80a5c17e6
parent 90d8ed6c448a7b09e0a459181f889e3153d960ee
Author: MakotoKurauchi <pluis@me.com>
Date:   Sat, 11 Aug 2018 00:22:09 +0900

Keyboard: Helix serial improvements (#3608)

* add change_reciver2sender()/change_sender2reciver()

This is a change to improve readability.

* txled, rxled off in matrix_init()

* add serial_send_packet() / serial_recive_packet()

This is a change to reduce object size.

* add serial_low() at ISR() top

* add __attribute__((always_inline)) to some functions

* modify serial_send_packet()/serial_recive_packet()

A little, object size reduction.
A little, speedup.

* add debug code to helix/serial.c

* Adjust sampling timing of serial signal being received

* add split_scomm.c/split_scomm.h and change serial.c/serial.h

serial.c was divided into 2 layers, split_scom.c and serial.c.
The upper layer split_scomm.c is called from matrix.c.
The lower layer serial.c accesses the hardware.

* add split_scomm.c/split_scomm.h into helix/rev1

* reduce object size helix/rev2/matrix.c

* remove checksum check, add parity check

* force occur parity error for test

* parity test ok. remove test code

* change some comment & add skip code when buffer_size == 0

* serial.c: multiple types of transaction support

Add 4 bits transaction-type field at packet top.
Select Transaction Descriptor Table entry by transaction-type.

* helix serial master-slave transaction optimize

Using multi-type transaction feature of serial.c, communication contents between master slaves were optimized.

* add debug code for retry

* add comment into each config.h

* fix ISR status drop

* add a debug macro 'debug_retry_chg()'

* reduce led_test size

* remove debug code from helix/serial.c and etc.

* helix:five_rows change TAPPING_TERM value 140

* Improved compatibility with let's split of serial.c. Finish helix/serial.c improvement.

- The difference with the original let's split's serial.c
  - It's high-speed about 4 times.
  - Stable bi-directional data transfer. (Helix need master to slave transfer)
  - serial.h was divided 2 files, serial_config.h and sereial.h
  - With multiple types of transaction support, communication contents can be optimized. (NEW flexible API)

- USE OLD Simple APIs (compatible with let's split serial.c)
  - files :
    - serial_config.h -- hardware configuration (need include by config.h)
    - serial.c/serial.h -- serial communication

- USE NEW flexible APIs. (Support multi-type transaction function.)
  serial.c was divided into 2 layers, split_scom.c and serial.c.
  The upper layer split_scomm.c is called from matrix.c.
  The lower layer serial.c accesses the hardware.
  - files
    - split_scomm.c -- communication buffer is defined in here. call by matrix.c.
    - split_scomm.h -- buffer size is defined in here. include by matrix.c, split_util.c
    - serial_config.h -- hardware configuration (need include by config.h)
      To use the NEW API, specify #define SERIAL_USE_MULTI_TRANSACTION
    - serial.c/serial.h -- serial communication lower layer

- NEW APIs for serial.c / serial.h (The lower layer)
  // Soft Serial Transaction Descriptor
  typedef struct _SSTD_t  {
      uint8_t *status;
      uint8_t initiator2target_buffer_size;
      uint8_t *initiator2target_buffer;
      uint8_t target2initiator_buffer_size;
      uint8_t *target2initiator_buffer;
  } SSTD_t;

  // initiator is transaction start side
  void soft_serial_initiator_init(SSTD_t *sstd_table);
  // target is interrupt accept side
  void soft_serial_target_init(SSTD_t *sstd_table);
  int soft_serial_transaction(int sstd_index);
  int soft_serial_get_and_clean_target_status(int sstd_index);

- NEW APIs for split_scomm.c / split_scomm.h (The upper layer)
   move from old serial.c the following buffer and functions
     serial_slave_buffer[]
     serial_master_buffer[]
     void serial_master_init(void)
     void serial_slave_init(void)
     int serial_update_buffers(void)
   define SERIAL_xxxxx_BUFFER_LENGTH move from serial_config.h to split_scomm.h

Diffstat:
keyboards/helix/rev1/keymaps/OLED_sample/serial_config.h | 2+-
keyboards/helix/rev1/rules.mk | 4++--
keyboards/helix/rev1/serial_config.h | 2+-
keyboards/helix/rev2/keymaps/default/config.h | 3+++
keyboards/helix/rev2/keymaps/edvorakjp/config.h | 3+++
keyboards/helix/rev2/keymaps/five_rows/config.h | 6++++++
keyboards/helix/rev2/keymaps/five_rows_jis/config.h | 3+++
keyboards/helix/rev2/keymaps/froggy/config.h | 3+++
keyboards/helix/rev2/keymaps/led_test/config.h | 3+++
keyboards/helix/rev2/matrix.c | 58++++++++++++++++++++++++++++++++++------------------------
keyboards/helix/rev2/rules.mk | 7++++---
keyboards/helix/rev2/serial_config.h | 3+--
keyboards/helix/rev2/serial_config_simpleapi.h | 8++++++++
keyboards/helix/rev2/split_scomm.c | 73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
keyboards/helix/rev2/split_scomm.h | 24++++++++++++++++++++++++
keyboards/helix/rev2/split_util.c | 2+-
keyboards/helix/rules.mk | 9++++++---
keyboards/helix/serial.c | 411++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
keyboards/helix/serial.h | 63++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
19 files changed, 514 insertions(+), 173 deletions(-)

diff --git a/keyboards/helix/rev1/keymaps/OLED_sample/serial_config.h b/keyboards/helix/rev1/keymaps/OLED_sample/serial_config.h @@ -9,7 +9,7 @@ #define SERIAL_PIN_INTERRUPT INT2_vect #define SERIAL_SLAVE_BUFFER_LENGTH MATRIX_ROWS/2 -#define SERIAL_MASTER_BUFFER_LENGTH 1 +#define SERIAL_MASTER_BUFFER_LENGTH 0 //// #error rev1/keymaps/OLED_sample serial config diff --git a/keyboards/helix/rev1/rules.mk b/keyboards/helix/rev1/rules.mk @@ -1,4 +1,4 @@ -SRC += rev1/matrix.c \ - rev1/split_util.c +SRC += rev1/matrix.c +SRC += rev1/split_util.c BACKLIGHT_ENABLE = no diff --git a/keyboards/helix/rev1/serial_config.h b/keyboards/helix/rev1/serial_config.h @@ -9,7 +9,7 @@ #define SERIAL_PIN_INTERRUPT INT0_vect #define SERIAL_SLAVE_BUFFER_LENGTH MATRIX_ROWS/2 -#define SERIAL_MASTER_BUFFER_LENGTH 1 +#define SERIAL_MASTER_BUFFER_LENGTH 0 /// #error rev1 serial config diff --git a/keyboards/helix/rev2/keymaps/default/config.h b/keyboards/helix/rev2/keymaps/default/config.h @@ -21,6 +21,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. #ifndef CONFIG_USER_H #define CONFIG_USER_H +// if you need more program area, try uncomment follow line +//#include "serial_config_simpleapi.h" + // place overrides here #endif /* CONFIG_USER_H */ diff --git a/keyboards/helix/rev2/keymaps/edvorakjp/config.h b/keyboards/helix/rev2/keymaps/edvorakjp/config.h @@ -1,6 +1,9 @@ #ifndef CONFIG_USER_H #define CONFIG_USER_H +// if you need more program area, try uncomment follow line +//#include "serial_config_simpleapi.h" + #undef TAPPING_FORCE_HOLD #undef TAPPING_TERM #define TAPPING_TERM 120 diff --git a/keyboards/helix/rev2/keymaps/five_rows/config.h b/keyboards/helix/rev2/keymaps/five_rows/config.h @@ -21,6 +21,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. #ifndef CONFIG_USER_H #define CONFIG_USER_H +// if you need more program area, try uncomment follow line +//#include "serial_config_simpleapi.h" + +#undef TAPPING_TERM +#define TAPPING_TERM 140 + // place overrides here #endif /* CONFIG_USER_H */ diff --git a/keyboards/helix/rev2/keymaps/five_rows_jis/config.h b/keyboards/helix/rev2/keymaps/five_rows_jis/config.h @@ -23,6 +23,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. // place overrides here +// if you need more program area, try uncomment follow line +//#include "serial_config_simpleapi.h" + #ifdef MOUSEKEY_ENABLE #undef MOUSEKEY_INTERVAL #define MOUSEKEY_INTERVAL 0 diff --git a/keyboards/helix/rev2/keymaps/froggy/config.h b/keyboards/helix/rev2/keymaps/froggy/config.h @@ -21,6 +21,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. #ifndef CONFIG_USER_H #define CONFIG_USER_H +// if you need more program area, try uncomment follow line +//#include "serial_config_simpleapi.h" + #undef TAPPING_TERM #define TAPPING_TERM 200 #define ONESHOT_TAP_TOGGLE 5 /* Tapping this number of times holds the key until tapped this number of times again. */ diff --git a/keyboards/helix/rev2/keymaps/led_test/config.h b/keyboards/helix/rev2/keymaps/led_test/config.h @@ -21,6 +21,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. #ifndef CONFIG_USER_H #define CONFIG_USER_H +// if you need more program area, try uncomment follow line +#include "serial_config_simpleapi.h" + // place overrides here #endif /* CONFIG_USER_H */ diff --git a/keyboards/helix/rev2/matrix.c b/keyboards/helix/rev2/matrix.c @@ -20,6 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include <stdint.h> #include <stdbool.h> +#include <string.h> #include <avr/io.h> #include <avr/wdt.h> #include <avr/interrupt.h> @@ -34,7 +35,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. #ifdef USE_MATRIX_I2C # include "i2c.h" #else // USE_SERIAL -# include "serial.h" +# include "split_scomm.h" #endif #ifndef DEBOUNCE @@ -102,6 +103,8 @@ void matrix_init(void) init_cols(); TX_RX_LED_INIT; + TXLED0; + RXLED0; // initialize matrix state: all keys off for (uint8_t i=0; i < MATRIX_ROWS; i++) { @@ -178,17 +181,20 @@ i2c_error: // the cable is disconnceted, or something else went wrong #else // USE_SERIAL -int serial_transaction(void) { +int serial_transaction(int master_changed) { int slaveOffset = (isLeftHand) ? (ROWS_PER_HAND) : 0; +#ifdef SERIAL_USE_MULTI_TRANSACTION + int ret=serial_update_buffers(master_changed); +#else int ret=serial_update_buffers(); +#endif if (ret ) { - if(ret==2)RXLED1; + if(ret==2) RXLED1; return 1; } -RXLED0; - for (int i = 0; i < ROWS_PER_HAND; ++i) { - matrix[slaveOffset+i] = serial_slave_buffer[i]; - } + RXLED0; + memcpy(&matrix[slaveOffset], + (void *)serial_slave_buffer, sizeof(serial_slave_buffer)); return 0; } #endif @@ -199,19 +205,9 @@ uint8_t matrix_scan(void) matrix_master_scan(); }else{ matrix_slave_scan(); - -// if(serial_slave_DATA_CORRUPT()){ -// TXLED0; - int offset = (isLeftHand) ? ROWS_PER_HAND : 0; - - for (int i = 0; i < ROWS_PER_HAND; ++i) { - matrix[offset+i] = serial_master_buffer[i]; - } - -// }else{ -// TXLED1; -// } - + int offset = (isLeftHand) ? ROWS_PER_HAND : 0; + memcpy(&matrix[offset], + (void *)serial_master_buffer, sizeof(serial_master_buffer)); matrix_scan_quantum(); } return 1; @@ -221,6 +217,7 @@ uint8_t matrix_scan(void) uint8_t matrix_master_scan(void) { int ret = _matrix_scan(); + int mchanged = 1; #ifndef KEYBOARD_helix_rev1 int offset = (isLeftHand) ? 0 : ROWS_PER_HAND; @@ -231,16 +228,19 @@ uint8_t matrix_master_scan(void) { // i2c_slave_buffer[i] = matrix[offset+i]; // } #else // USE_SERIAL - for (int i = 0; i < ROWS_PER_HAND; ++i) { - serial_master_buffer[i] = matrix[offset+i]; - } + #ifdef SERIAL_USE_MULTI_TRANSACTION + mchanged = memcmp((void *)serial_master_buffer, + &matrix[offset], sizeof(serial_master_buffer)); + #endif + memcpy((void *)serial_master_buffer, + &matrix[offset], sizeof(serial_master_buffer)); #endif #endif #ifdef USE_MATRIX_I2C if( i2c_transaction() ) { #else // USE_SERIAL - if( serial_transaction() ) { + if( serial_transaction(mchanged) ) { #endif // turn on the indicator led when halves are disconnected TXLED1; @@ -274,9 +274,19 @@ void matrix_slave_scan(void) { i2c_slave_buffer[i] = matrix[offset+i]; } #else // USE_SERIAL + #ifdef SERIAL_USE_MULTI_TRANSACTION + int change = 0; + #endif for (int i = 0; i < ROWS_PER_HAND; ++i) { + #ifdef SERIAL_USE_MULTI_TRANSACTION + if( serial_slave_buffer[i] != matrix[offset+i] ) + change = 1; + #endif serial_slave_buffer[i] = matrix[offset+i]; } + #ifdef SERIAL_USE_MULTI_TRANSACTION + slave_buffer_change_count += change; + #endif #endif } diff --git a/keyboards/helix/rev2/rules.mk b/keyboards/helix/rev2/rules.mk @@ -1,3 +1,4 @@ -SRC += rev2/matrix.c \ - rev2/split_util.c \ - ws2812.c +SRC += rev2/matrix.c +SRC += rev2/split_util.c +SRC += rev2/split_scomm.c +SRC += ws2812.c diff --git a/keyboards/helix/rev2/serial_config.h b/keyboards/helix/rev2/serial_config.h @@ -8,8 +8,7 @@ #define SERIAL_PIN_MASK _BV(PD2) #define SERIAL_PIN_INTERRUPT INT2_vect -#define SERIAL_SLAVE_BUFFER_LENGTH MATRIX_ROWS/2 -#define SERIAL_MASTER_BUFFER_LENGTH MATRIX_ROWS/2 +#define SERIAL_USE_MULTI_TRANSACTION //// #error rev2 serial config diff --git a/keyboards/helix/rev2/serial_config_simpleapi.h b/keyboards/helix/rev2/serial_config_simpleapi.h @@ -0,0 +1,8 @@ +#ifndef SERIAL_CONFIG_SIMPLEAPI_H +#define SERIAL_CONFIG_SIMPLEAPI_H + +#undef SERIAL_USE_MULTI_TRANSACTION +#define SERIAL_SLAVE_BUFFER_LENGTH MATRIX_ROWS/2 +#define SERIAL_MASTER_BUFFER_LENGTH MATRIX_ROWS/2 + +#endif // SERIAL_CONFIG_SIMPLEAPI_H diff --git a/keyboards/helix/rev2/split_scomm.c b/keyboards/helix/rev2/split_scomm.c @@ -0,0 +1,73 @@ +#ifdef USE_SERIAL +#ifdef SERIAL_USE_MULTI_TRANSACTION +/* --- USE flexible API (using multi-type transaction function) --- */ + +#include <stdbool.h> +#include <stdint.h> +#include <stddef.h> +#include <split_scomm.h> +#include "serial.h" +#ifdef SERIAL_DEBUG_MODE +#include <avr/io.h> +#endif + +uint8_t volatile serial_slave_buffer[SERIAL_SLAVE_BUFFER_LENGTH] = {0}; +uint8_t volatile serial_master_buffer[SERIAL_MASTER_BUFFER_LENGTH] = {0}; +uint8_t volatile status_com = 0; +uint8_t volatile status1 = 0; +uint8_t slave_buffer_change_count = 0; +uint8_t s_change_old = 0xff; + +SSTD_t transactions[] = { +#define GET_SLAVE_STATUS 0 + /* master buffer not changed, only recive slave_buffer_change_count */ + { (uint8_t *)&status_com, + 0, NULL, + sizeof(slave_buffer_change_count), &slave_buffer_change_count, + }, +#define PUT_MASTER_GET_SLAVE_STATUS 1 + /* master buffer changed need send, and recive slave_buffer_change_count */ + { (uint8_t *)&status_com, + sizeof(serial_master_buffer), (uint8_t *)serial_master_buffer, + sizeof(slave_buffer_change_count), &slave_buffer_change_count, + }, +#define GET_SLAVE_BUFFER 2 + /* recive serial_slave_buffer */ + { (uint8_t *)&status1, + 0, NULL, + sizeof(serial_slave_buffer), (uint8_t *)serial_slave_buffer + } +}; + +void serial_master_init(void) +{ + soft_serial_initiator_init(transactions); +} + +void serial_slave_init(void) +{ + soft_serial_target_init(transactions); +} + +// 0 => no error +// 1 => slave did not respond +// 2 => checksum error +int serial_update_buffers(int master_update) +{ + int status; + static int need_retry = 0; + if( s_change_old != slave_buffer_change_count ) { + status = soft_serial_transaction(GET_SLAVE_BUFFER); + if( status == TRANSACTION_END ) + s_change_old = slave_buffer_change_count; + } + if( !master_update && !need_retry) + status = soft_serial_transaction(GET_SLAVE_STATUS); + else + status = soft_serial_transaction(PUT_MASTER_GET_SLAVE_STATUS); + need_retry = ( status == TRANSACTION_END ) ? 0 : 1; + return status; +} + +#endif // SERIAL_USE_MULTI_TRANSACTION +#endif /* USE_SERIAL */ diff --git a/keyboards/helix/rev2/split_scomm.h b/keyboards/helix/rev2/split_scomm.h @@ -0,0 +1,24 @@ +#ifndef SPLIT_COMM_H +#define SPLIT_COMM_H + +#ifndef SERIAL_USE_MULTI_TRANSACTION +/* --- USE Simple API (OLD API, compatible with let's split serial.c) --- */ +#include "serial.h" + +#else +/* --- USE flexible API (using multi-type transaction function) --- */ +// Buffers for master - slave communication +#define SERIAL_SLAVE_BUFFER_LENGTH MATRIX_ROWS/2 +#define SERIAL_MASTER_BUFFER_LENGTH MATRIX_ROWS/2 + +extern volatile uint8_t serial_slave_buffer[SERIAL_SLAVE_BUFFER_LENGTH]; +extern volatile uint8_t serial_master_buffer[SERIAL_MASTER_BUFFER_LENGTH]; +extern uint8_t slave_buffer_change_count; + +void serial_master_init(void); +void serial_slave_init(void); +int serial_update_buffers(int master_changed); + +#endif + +#endif /* SPLIT_COMM_H */ diff --git a/keyboards/helix/rev2/split_util.c b/keyboards/helix/rev2/split_util.c @@ -11,7 +11,7 @@ #ifdef USE_MATRIX_I2C # include "i2c.h" #else -# include "serial.h" +# include "split_scomm.h" #endif volatile bool isLeftHand = true; diff --git a/keyboards/helix/rules.mk b/keyboards/helix/rules.mk @@ -1,6 +1,9 @@ -SRC += i2c.c \ - serial.c \ - ssd1306.c +SRC += i2c.c +SRC += serial.c +SRC += ssd1306.c + +# if firmware size over limit, try this option +# CFLAGS += -flto # MCU name #MCU = at90usb1287 diff --git a/keyboards/helix/serial.c b/keyboards/helix/serial.c @@ -9,40 +9,85 @@ #include <avr/io.h> #include <avr/interrupt.h> #include <util/delay.h> +#include <stddef.h> #include <stdbool.h> #include "serial.h" +//#include <pro_micro.h> #ifdef USE_SERIAL +#ifndef SERIAL_USE_MULTI_TRANSACTION +/* --- USE Simple API (OLD API, compatible with let's split serial.c) */ + #if SERIAL_SLAVE_BUFFER_LENGTH > 0 + uint8_t volatile serial_slave_buffer[SERIAL_SLAVE_BUFFER_LENGTH] = {0}; + #endif + #if SERIAL_MASTER_BUFFER_LENGTH > 0 + uint8_t volatile serial_master_buffer[SERIAL_MASTER_BUFFER_LENGTH] = {0}; + #endif + uint8_t volatile status0 = 0; + +SSTD_t transactions[] = { + { (uint8_t *)&status0, + #if SERIAL_MASTER_BUFFER_LENGTH > 0 + sizeof(serial_master_buffer), (uint8_t *)serial_master_buffer, + #else + 0, (uint8_t *)NULL, + #endif + #if SERIAL_SLAVE_BUFFER_LENGTH > 0 + sizeof(serial_slave_buffer), (uint8_t *)serial_slave_buffer + #else + 0, (uint8_t *)NULL, + #endif + } +}; + +void serial_master_init(void) +{ soft_serial_initiator_init(transactions); } + +void serial_slave_init(void) +{ soft_serial_target_init(transactions); } + +// 0 => no error +// 1 => slave did not respond +// 2 => checksum error +int serial_update_buffers() +{ return soft_serial_transaction(); } + +#endif // Simple API (OLD API, compatible with let's split serial.c) + +#define ALWAYS_INLINE __attribute__((always_inline)) +#define NO_INLINE __attribute__((noinline)) #define _delay_sub_us(x) __builtin_avr_delay_cycles(x) // Serial pulse period in microseconds. +#define TID_SEND_ADJUST 14 + #define SELECT_SERIAL_SPEED 1 #if SELECT_SERIAL_SPEED == 0 // Very High speed #define SERIAL_DELAY 4 // micro sec - #define READ_WRITE_START_ADJUST 30 // cycles - #define READ_WRITE_WIDTH_ADJUST 10 // cycles + #define READ_WRITE_START_ADJUST 33 // cycles + #define READ_WRITE_WIDTH_ADJUST 3 // cycles #elif SELECT_SERIAL_SPEED == 1 // High speed #define SERIAL_DELAY 6 // micro sec - #define READ_WRITE_START_ADJUST 23 // cycles - #define READ_WRITE_WIDTH_ADJUST 10 // cycles + #define READ_WRITE_START_ADJUST 30 // cycles + #define READ_WRITE_WIDTH_ADJUST 3 // cycles #elif SELECT_SERIAL_SPEED == 2 // Middle speed #define SERIAL_DELAY 12 // micro sec - #define READ_WRITE_START_ADJUST 25 // cycles - #define READ_WRITE_WIDTH_ADJUST 10 // cycles + #define READ_WRITE_START_ADJUST 30 // cycles + #define READ_WRITE_WIDTH_ADJUST 3 // cycles #elif SELECT_SERIAL_SPEED == 3 // Low speed #define SERIAL_DELAY 24 // micro sec - #define READ_WRITE_START_ADJUST 25 // cycles - #define READ_WRITE_WIDTH_ADJUST 10 // cycles + #define READ_WRITE_START_ADJUST 30 // cycles + #define READ_WRITE_WIDTH_ADJUST 3 // cycles #elif SELECT_SERIAL_SPEED == 4 // Very Low speed #define SERIAL_DELAY 50 // micro sec - #define READ_WRITE_START_ADJUST 25 // cycles - #define READ_WRITE_WIDTH_ADJUST 10 // cycles + #define READ_WRITE_START_ADJUST 30 // cycles + #define READ_WRITE_WIDTH_ADJUST 3 // cycles #else #error Illegal Serial Speed #endif @@ -51,14 +96,15 @@ #define SERIAL_DELAY_HALF1 (SERIAL_DELAY/2) #define SERIAL_DELAY_HALF2 (SERIAL_DELAY - SERIAL_DELAY/2) -#define SLAVE_INT_WIDTH 1 -#define SLAVE_INT_RESPONSE_TIME SERIAL_DELAY - -uint8_t volatile serial_slave_buffer[SERIAL_SLAVE_BUFFER_LENGTH] = {0}; -uint8_t volatile serial_master_buffer[SERIAL_MASTER_BUFFER_LENGTH] = {0}; +#define SLAVE_INT_WIDTH_US 1 +#ifndef SERIAL_USE_MULTI_TRANSACTION + #define SLAVE_INT_RESPONSE_TIME SERIAL_DELAY +#else + #define SLAVE_INT_ACK_WIDTH_UNIT 2 + #define SLAVE_INT_ACK_WIDTH 4 +#endif -#define SLAVE_DATA_CORRUPT (1<<0) -volatile uint8_t status = 0; +static SSTD_t *Transaction_table = NULL; inline static void serial_delay(void) { @@ -75,12 +121,14 @@ void serial_delay_half2(void) { _delay_us(SERIAL_DELAY_HALF2); } +inline static void serial_output(void) ALWAYS_INLINE; inline static void serial_output(void) { SERIAL_PIN_DDR |= SERIAL_PIN_MASK; } // make the serial pin an input with pull-up resistor +inline static void serial_input_with_pullup(void) ALWAYS_INLINE; inline static void serial_input_with_pullup(void) { SERIAL_PIN_DDR &= ~SERIAL_PIN_MASK; @@ -92,50 +140,58 @@ uint8_t serial_read_pin(void) { return !!(SERIAL_PIN_INPUT & SERIAL_PIN_MASK); } +inline static void serial_low(void) ALWAYS_INLINE; inline static void serial_low(void) { SERIAL_PIN_PORT &= ~SERIAL_PIN_MASK; } +inline static void serial_high(void) ALWAYS_INLINE; inline static void serial_high(void) { SERIAL_PIN_PORT |= SERIAL_PIN_MASK; } -void serial_master_init(void) { - serial_output(); - serial_high(); +void soft_serial_initiator_init(SSTD_t *sstd_table) +{ + Transaction_table = sstd_table; + serial_output(); + serial_high(); } -void serial_slave_init(void) { - serial_input_with_pullup(); +void soft_serial_target_init(SSTD_t *sstd_table) +{ + Transaction_table = sstd_table; + serial_input_with_pullup(); #if SERIAL_PIN_MASK == _BV(PD0) - // Enable INT0 - EIMSK |= _BV(INT0); - // Trigger on falling edge of INT0 - EICRA &= ~(_BV(ISC00) | _BV(ISC01)); + // Enable INT0 + EIMSK |= _BV(INT0); + // Trigger on falling edge of INT0 + EICRA &= ~(_BV(ISC00) | _BV(ISC01)); #elif SERIAL_PIN_MASK == _BV(PD2) - // Enable INT2 - EIMSK |= _BV(INT2); - // Trigger on falling edge of INT2 - EICRA &= ~(_BV(ISC20) | _BV(ISC21)); + // Enable INT2 + EIMSK |= _BV(INT2); + // Trigger on falling edge of INT2 + EICRA &= ~(_BV(ISC20) | _BV(ISC21)); #else #error unknown SERIAL_PIN_MASK value #endif } // Used by the sender to synchronize timing with the reciver. +static void sync_recv(void) NO_INLINE; static void sync_recv(void) { - for (int i = 0; i < SERIAL_DELAY*5 && serial_read_pin(); i++ ) { + for (uint8_t i = 0; i < SERIAL_DELAY*5 && serial_read_pin(); i++ ) { } - // This shouldn't hang if the slave disconnects because the - // serial line will float to high if the slave does disconnect. + // This shouldn't hang if the target disconnects because the + // serial line will float to high if the target does disconnect. while (!serial_read_pin()); } // Used by the reciver to send a synchronization signal to the sender. +static void sync_send(void)NO_INLINE; static void sync_send(void) { serial_low(); @@ -144,152 +200,245 @@ void sync_send(void) { } // Reads a byte from the serial line -static -uint8_t serial_read_byte(void) { - uint8_t byte = 0; +static uint8_t serial_read_chunk(uint8_t *pterrcount, uint8_t bit) NO_INLINE; +static uint8_t serial_read_chunk(uint8_t *pterrcount, uint8_t bit) { + uint8_t byte, i, p, pb; + _delay_sub_us(READ_WRITE_START_ADJUST); - for ( uint8_t i = 0; i < 8; ++i) { - serial_delay_half1(); // read the middle of pulses - byte = (byte << 1) | serial_read_pin(); - _delay_sub_us(READ_WRITE_WIDTH_ADJUST); - serial_delay_half2(); + for( i = 0, byte = 0, p = 0; i < bit; i++ ) { + serial_delay_half1(); // read the middle of pulses + if( serial_read_pin() ) { + byte = (byte << 1) | 1; p ^= 1; + } else { + byte = (byte << 1) | 0; p ^= 0; + } + _delay_sub_us(READ_WRITE_WIDTH_ADJUST); + serial_delay_half2(); } + /* recive parity bit */ + serial_delay_half1(); // read the middle of pulses + pb = serial_read_pin(); + _delay_sub_us(READ_WRITE_WIDTH_ADJUST); + serial_delay_half2(); + + *pterrcount += (p != pb)? 1 : 0; + return byte; } // Sends a byte with MSB ordering -static -void serial_write_byte(uint8_t data) { - uint8_t b = 1<<7; - while( b ) { - if(data & b) { - serial_high(); - } else { - serial_low(); +void serial_write_chunk(uint8_t data, uint8_t bit) NO_INLINE; +void serial_write_chunk(uint8_t data, uint8_t bit) { + uint8_t b, p; + for( p = 0, b = 1<<(bit-1); b ; b >>= 1) { + if(data & b) { + serial_high(); p ^= 1; + } else { + serial_low(); p ^= 0; + } + serial_delay(); } - b >>= 1; + /* send parity bit */ + if(p & 1) { serial_high(); } + else { serial_low(); } serial_delay(); - } - serial_low(); // sync_send() / senc_recv() need raise edge -} -// interrupt handle to be used by the slave device -ISR(SERIAL_PIN_INTERRUPT) { - serial_output(); + serial_low(); // sync_send() / senc_recv() need raise edge +} - // slave send phase - uint8_t checksum = 0; - for (int i = 0; i < SERIAL_SLAVE_BUFFER_LENGTH; ++i) { +static void serial_send_packet(uint8_t *buffer, uint8_t size) NO_INLINE; +static +void serial_send_packet(uint8_t *buffer, uint8_t size) { + for (uint8_t i = 0; i < size; ++i) { + uint8_t data; + data = buffer[i]; sync_send(); - serial_write_byte(serial_slave_buffer[i]); - checksum += serial_slave_buffer[i]; + serial_write_chunk(data,8); } - sync_send(); - serial_write_byte(checksum); - - // slave switch to input - sync_send(); //0 - serial_delay_half1(); //1 - serial_low(); //2 - serial_input_with_pullup(); //2 - serial_delay_half1(); //3 - - // slave recive phase - uint8_t checksum_computed = 0; - for (int i = 0; i < SERIAL_MASTER_BUFFER_LENGTH; ++i) { +} + +static uint8_t serial_recive_packet(uint8_t *buffer, uint8_t size) NO_INLINE; +static +uint8_t serial_recive_packet(uint8_t *buffer, uint8_t size) { + uint8_t pecount = 0; + for (uint8_t i = 0; i < size; ++i) { + uint8_t data; sync_recv(); - serial_master_buffer[i] = serial_read_byte(); - checksum_computed += serial_master_buffer[i]; + data = serial_read_chunk(&pecount, 8); + buffer[i] = data; } + return pecount == 0; +} + +inline static +void change_sender2reciver(void) { + sync_send(); //0 + serial_delay_half1(); //1 + serial_low(); //2 + serial_input_with_pullup(); //2 + serial_delay_half1(); //3 +} + +inline static +void change_reciver2sender(void) { + sync_recv(); //0 + serial_delay(); //1 + serial_low(); //3 + serial_output(); //3 + serial_delay_half1(); //4 +} + +// interrupt handle to be used by the target device +ISR(SERIAL_PIN_INTERRUPT) { + +#ifndef SERIAL_USE_MULTI_TRANSACTION + serial_low(); + serial_output(); + SSTD_t *trans = Transaction_table; +#else + // recive transaction table index + uint8_t tid; + uint8_t pecount = 0; sync_recv(); - uint8_t checksum_received = serial_read_byte(); + tid = serial_read_chunk(&pecount,4); + if(pecount> 0) + return; + serial_delay_half1(); - if ( checksum_computed != checksum_received ) { - status |= SLAVE_DATA_CORRUPT; + serial_high(); // response step1 low->high + serial_output(); + _delay_sub_us(SLAVE_INT_ACK_WIDTH_UNIT*SLAVE_INT_ACK_WIDTH); + SSTD_t *trans = &Transaction_table[tid]; + serial_low(); // response step2 ack high->low +#endif + + // target send phase + if( trans->target2initiator_buffer_size > 0 ) + serial_send_packet((uint8_t *)trans->target2initiator_buffer, + trans->target2initiator_buffer_size); + // target switch to input + change_sender2reciver(); + + // target recive phase + if( trans->initiator2target_buffer_size > 0 ) { + if (serial_recive_packet((uint8_t *)trans->initiator2target_buffer, + trans->initiator2target_buffer_size) ) { + *trans->status = TRANSACTION_ACCEPTED; + } else { + *trans->status = TRANSACTION_DATA_ERROR; + } } else { - status &= ~SLAVE_DATA_CORRUPT; + *trans->status = TRANSACTION_ACCEPTED; } - sync_recv(); //weit master output to high -} - -inline -bool serial_slave_DATA_CORRUPT(void) { - return status & SLAVE_DATA_CORRUPT; + sync_recv(); //weit initiator output to high } -// Copies the serial_slave_buffer to the master and sends the -// serial_master_buffer to the slave. +///////// +// start transaction by initiator +// +// int soft_serial_transaction(int sstd_index) // // Returns: -// 0 => no error -// 1 => slave did not respond -// 2 => checksum error -int serial_update_buffers(void) { - // this code is very time dependent, so we need to disable interrupts +// TRANSACTION_END +// TRANSACTION_NO_RESPONSE +// TRANSACTION_DATA_ERROR +// this code is very time dependent, so we need to disable interrupts +#ifndef SERIAL_USE_MULTI_TRANSACTION +int soft_serial_transaction(void) { + SSTD_t *trans = Transaction_table; +#else +int soft_serial_transaction(int sstd_index) { + SSTD_t *trans = &Transaction_table[sstd_index]; +#endif cli(); - // signal to the slave that we want to start a transaction + // signal to the target that we want to start a transaction serial_output(); serial_low(); - _delay_us(SLAVE_INT_WIDTH); + _delay_us(SLAVE_INT_WIDTH_US); - // wait for the slaves response +#ifndef SERIAL_USE_MULTI_TRANSACTION + // wait for the target response serial_input_with_pullup(); _delay_us(SLAVE_INT_RESPONSE_TIME); - // check if the slave is present + // check if the target is present if (serial_read_pin()) { - // slave failed to pull the line low, assume not present + // target failed to pull the line low, assume not present serial_output(); serial_high(); + *trans->status = TRANSACTION_NO_RESPONSE; sei(); - return 1; + return TRANSACTION_NO_RESPONSE; } - // master recive phase - // if the slave is present syncronize with it +#else + // send transaction table index + sync_send(); + _delay_sub_us(TID_SEND_ADJUST); + serial_write_chunk(sstd_index, 4); + serial_delay_half1(); - uint8_t checksum_computed = 0; - // receive data from the slave - for (int i = 0; i < SERIAL_SLAVE_BUFFER_LENGTH; ++i) { - sync_recv(); - serial_slave_buffer[i] = serial_read_byte(); - checksum_computed += serial_slave_buffer[i]; + // wait for the target response (step1 low->high) + serial_input_with_pullup(); + while( !serial_read_pin() ) { + _delay_sub_us(2); } - sync_recv(); - uint8_t checksum_received = serial_read_byte(); - if (checksum_computed != checksum_received) { - serial_output(); - serial_high(); - sei(); - return 2; + // check if the target is present (step2 high->low) + for( int i = 0; serial_read_pin(); i++ ) { + if (i > SLAVE_INT_ACK_WIDTH + 1) { + // slave failed to pull the line low, assume not present + serial_output(); + serial_high(); + *trans->status = TRANSACTION_NO_RESPONSE; + sei(); + return TRANSACTION_NO_RESPONSE; + } + _delay_sub_us(SLAVE_INT_ACK_WIDTH_UNIT); } +#endif - // master switch to output - sync_recv(); //0 - serial_delay(); //1 - serial_low(); //3 - serial_output(); // 3 - serial_delay_half1(); //4 - - // master send phase - uint8_t checksum = 0; - - for (int i = 0; i < SERIAL_MASTER_BUFFER_LENGTH; ++i) { - sync_send(); - serial_write_byte(serial_master_buffer[i]); - checksum += serial_master_buffer[i]; + // initiator recive phase + // if the target is present syncronize with it + if( trans->target2initiator_buffer_size > 0 ) { + if (!serial_recive_packet((uint8_t *)trans->target2initiator_buffer, + trans->target2initiator_buffer_size) ) { + serial_output(); + serial_high(); + *trans->status = TRANSACTION_DATA_ERROR; + sei(); + return TRANSACTION_DATA_ERROR; + } + } + + // initiator switch to output + change_reciver2sender(); + + // initiator send phase + if( trans->initiator2target_buffer_size > 0 ) { + serial_send_packet((uint8_t *)trans->initiator2target_buffer, + trans->initiator2target_buffer_size); } - sync_send(); - serial_write_byte(checksum); // always, release the line when not in use sync_send(); + *trans->status = TRANSACTION_END; sei(); - return 0; + return TRANSACTION_END; } +#ifdef SERIAL_USE_MULTI_TRANSACTION +int soft_serial_get_and_clean_status(int sstd_index) { + SSTD_t *trans = &Transaction_table[sstd_index]; + cli(); + int retval = *trans->status; + *trans->status = 0;; + sei(); + return retval; +} +#endif + #endif diff --git a/keyboards/helix/serial.h b/keyboards/helix/serial.h @@ -3,25 +3,78 @@ #include <stdbool.h> -// //////////////////////////////////////////// +// ///////////////////////////////////////////////////////////////// // Need Soft Serial defines in serial_config.h -// //////////////////////////////////////////// +// ///////////////////////////////////////////////////////////////// // ex. // #define SERIAL_PIN_DDR DDRD // #define SERIAL_PIN_PORT PORTD // #define SERIAL_PIN_INPUT PIND // #define SERIAL_PIN_MASK _BV(PD?) ?=0,2 // #define SERIAL_PIN_INTERRUPT INT?_vect ?=0,2 +// +// //// USE Simple API (OLD API, compatible with let's split serial.c) +// ex. // #define SERIAL_SLAVE_BUFFER_LENGTH MATRIX_ROWS/2 -// #define SERIAL_MASTER_BUFFER_LENGTH MATRIX_ROWS/2 +// #define SERIAL_MASTER_BUFFER_LENGTH 1 +// +// //// USE flexible API (using multi-type transaction function) +// #define SERIAL_USE_MULTI_TRANSACTION +// +// ///////////////////////////////////////////////////////////////// + -// Buffers for master - slave communication +#ifndef SERIAL_USE_MULTI_TRANSACTION +/* --- USE Simple API (OLD API, compatible with let's split serial.c) */ +#if SERIAL_SLAVE_BUFFER_LENGTH > 0 extern volatile uint8_t serial_slave_buffer[SERIAL_SLAVE_BUFFER_LENGTH]; +#endif +#if SERIAL_MASTER_BUFFER_LENGTH > 0 extern volatile uint8_t serial_master_buffer[SERIAL_MASTER_BUFFER_LENGTH]; +#endif void serial_master_init(void); void serial_slave_init(void); int serial_update_buffers(void); -bool serial_slave_data_corrupt(void); + +#endif // USE Simple API + +// Soft Serial Transaction Descriptor +typedef struct _SSTD_t { + uint8_t *status; + uint8_t initiator2target_buffer_size; + uint8_t *initiator2target_buffer; + uint8_t target2initiator_buffer_size; + uint8_t *target2initiator_buffer; +} SSTD_t; + +// initiator is transaction start side +void soft_serial_initiator_init(SSTD_t *sstd_table); +// target is interrupt accept side +void soft_serial_target_init(SSTD_t *sstd_table); + +// initiator resullt +#define TRANSACTION_END 0 +#define TRANSACTION_NO_RESPONSE 0x1 +#define TRANSACTION_DATA_ERROR 0x2 +#ifndef SERIAL_USE_MULTI_TRANSACTION +int soft_serial_transaction(void); +#else +int soft_serial_transaction(int sstd_index); +#endif + +// target status +// *SSTD_t.status has +// initiator: +// TRANSACTION_END +// or TRANSACTION_NO_RESPONSE +// or TRANSACTION_DATA_ERROR +// target: +// TRANSACTION_DATA_ERROR +// or TRANSACTION_ACCEPTED +#define TRANSACTION_ACCEPTED 0x4 +#ifdef SERIAL_USE_MULTI_TRANSACTION +int soft_serial_get_and_clean_status(int sstd_index); +#endif #endif /* SOFT_SERIAL_H */