diff options
Diffstat (limited to 'keyboards/crkbd/serial.c')
-rw-r--r-- | keyboards/crkbd/serial.c | 452 |
1 files changed, 329 insertions, 123 deletions
diff --git a/keyboards/crkbd/serial.c b/keyboards/crkbd/serial.c index e918ab6ee6..11ceff0b37 100644 --- a/keyboards/crkbd/serial.c +++ b/keyboards/crkbd/serial.c @@ -9,36 +9,128 @@ #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 -// Serial pulse period in microseconds. Its probably a bad idea to lower this -// value. -#define SERIAL_DELAY 24 +#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); } -uint8_t volatile serial_slave_buffer[SERIAL_SLAVE_BUFFER_LENGTH] = {0}; -uint8_t volatile serial_master_buffer[SERIAL_MASTER_BUFFER_LENGTH] = {0}; +void serial_slave_init(void) +{ soft_serial_target_init(transactions); } -#define SLAVE_DATA_CORRUPT (1<<0) -volatile uint8_t status = 0; +// 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 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 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 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 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 30 // cycles + #define READ_WRITE_WIDTH_ADJUST 3 // cycles +#else +#error Illegal Serial Speed +#endif + + +#define SERIAL_DELAY_HALF1 (SERIAL_DELAY/2) +#define SERIAL_DELAY_HALF2 (SERIAL_DELAY - SERIAL_DELAY/2) + +#define SLAVE_INT_WIDTH_US 1 +#ifndef SERIAL_USE_MULTI_TRANSACTION + #define SLAVE_INT_RESPONSE_TIME SERIAL_DELAY +#else + #define SLAVE_INT_ACK_WIDTH_UNIT 2 + #define SLAVE_INT_ACK_WIDTH 4 +#endif + +static SSTD_t *Transaction_table = NULL; inline static void serial_delay(void) { _delay_us(SERIAL_DELAY); } -void serial_delay_short(void) { - _delay_us(SERIAL_DELAY-1); + +inline static +void serial_delay_half1(void) { + _delay_us(SERIAL_DELAY_HALF1); +} + +inline static +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(void) { +void serial_input_with_pullup(void) { SERIAL_PIN_DDR &= ~SERIAL_PIN_MASK; SERIAL_PIN_PORT |= SERIAL_PIN_MASK; } @@ -48,191 +140,305 @@ 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(); - -#ifndef USE_SERIAL_PD2 - // Enable INT0 - EIMSK |= _BV(INT0); - // Trigger on falling edge of INT0 - EICRA &= ~(_BV(ISC00) | _BV(ISC01)); +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)); +#elif SERIAL_PIN_MASK == _BV(PD2) + // Enable INT2 + EIMSK |= _BV(INT2); + // Trigger on falling edge of INT2 + EICRA &= ~(_BV(ISC20) | _BV(ISC21)); #else - // Enable INT2 - EIMSK |= _BV(INT2); - // Trigger on falling edge of INT2 - EICRA &= ~(_BV(ISC20) | _BV(ISC21)); + #error unknown SERIAL_PIN_MASK value #endif } -// Used by the master to synchronize timing with the slave. +// Used by the sender to synchronize timing with the reciver. +static void sync_recv(void) NO_INLINE; static void sync_recv(void) { - serial_input(); - // This shouldn't hang if the slave disconnects because the - // serial line will float to high if the slave does disconnect. + for (uint8_t i = 0; i < SERIAL_DELAY*5 && serial_read_pin(); i++ ) { + } + // 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()); - //serial_delay(); - _delay_us(SERIAL_DELAY-5); } -// Used by the slave to send a synchronization signal to the master. +// 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_output(); - serial_low(); serial_delay(); - serial_high(); } // Reads a byte from the serial line -static -uint8_t serial_read_byte(void) { - uint8_t byte = 0; - serial_input(); - for ( uint8_t i = 0; i < 8; ++i) { - byte = (byte << 1) | serial_read_pin(); - serial_delay(); - _delay_us(1); +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( 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 = 8; - serial_output(); - while( b-- ) { - if(data & (1 << 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(); } + /* send parity bit */ + if(p & 1) { serial_high(); } + else { serial_low(); } serial_delay(); - } -} -// interrupt handle to be used by the slave device -ISR(SERIAL_PIN_INTERRUPT) { - sync_send(); + serial_low(); // sync_send() / senc_recv() need raise edge +} - uint8_t checksum = 0; - for (int i = 0; i < SERIAL_SLAVE_BUFFER_LENGTH; ++i) { - serial_write_byte(serial_slave_buffer[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(); - checksum += serial_slave_buffer[i]; + serial_write_chunk(data,8); } - serial_write_byte(checksum); - sync_send(); +} - // wait for the sync to finish sending - serial_delay(); +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(); + data = serial_read_chunk(&pecount, 8); + buffer[i] = data; + } + return pecount == 0; +} - // read the middle of pulses - _delay_us(SERIAL_DELAY/2); +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 +} - uint8_t checksum_computed = 0; - for (int i = 0; i < SERIAL_MASTER_BUFFER_LENGTH; ++i) { - serial_master_buffer[i] = serial_read_byte(); - sync_send(); - checksum_computed += serial_master_buffer[i]; - } - uint8_t checksum_received = serial_read_byte(); - sync_send(); +inline static +void change_reciver2sender(void) { + sync_recv(); //0 + serial_delay(); //1 + serial_low(); //3 + serial_output(); //3 + serial_delay_half1(); //4 +} - serial_input(); // end transaction +// interrupt handle to be used by the target device +ISR(SERIAL_PIN_INTERRUPT) { - if ( checksum_computed != checksum_received ) { - status |= SLAVE_DATA_CORRUPT; +#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(); + tid = serial_read_chunk(&pecount,4); + if(pecount> 0) + return; + serial_delay_half1(); + + 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; } -} -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 -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(1); + _delay_us(SLAVE_INT_WIDTH_US); - // wait for the slaves response - serial_input(); - serial_high(); - _delay_us(SERIAL_DELAY); +#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; } - // if the slave is present syncronize with it - sync_recv(); - - uint8_t checksum_computed = 0; - // receive data from the slave - for (int i = 0; i < SERIAL_SLAVE_BUFFER_LENGTH; ++i) { - serial_slave_buffer[i] = serial_read_byte(); - sync_recv(); - checksum_computed += serial_slave_buffer[i]; +#else + // send transaction table index + sync_send(); + _delay_sub_us(TID_SEND_ADJUST); + serial_write_chunk(sstd_index, 4); + serial_delay_half1(); + + // wait for the target response (step1 low->high) + serial_input_with_pullup(); + while( !serial_read_pin() ) { + _delay_sub_us(2); } - uint8_t checksum_received = serial_read_byte(); - sync_recv(); - if (checksum_computed != checksum_received) { - 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 - uint8_t checksum = 0; - // send data to the slave - for (int i = 0; i < SERIAL_MASTER_BUFFER_LENGTH; ++i) { - serial_write_byte(serial_master_buffer[i]); - sync_recv(); - 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); } - serial_write_byte(checksum); - sync_recv(); // always, release the line when not in use - serial_output(); - serial_high(); + 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 |