Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Helix serial.c master to slave communication bugfix & speed up #12

Merged
merged 13 commits into from
Jun 28, 2018
149 changes: 102 additions & 47 deletions keyboards/helix/serial.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,45 @@

#ifdef USE_SERIAL

// Serial pulse period in microseconds. Its probably a bad idea to lower this
// value.
#define SERIAL_DELAY 30
#define _delay_sub_us(x) __builtin_avr_delay_cycles(x)

// Serial pulse period in microseconds.
#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
#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
#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
#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
#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
#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 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};
Expand All @@ -28,17 +64,25 @@ 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) {
SERIAL_PIN_DDR |= SERIAL_PIN_MASK;
}

// make the serial pin an input with pull-up resistor
inline static
void serial_input(void) {
void serial_input_with_pullup(void) {
SERIAL_PIN_DDR &= ~SERIAL_PIN_MASK;
SERIAL_PIN_PORT |= SERIAL_PIN_MASK;
}
Expand All @@ -64,7 +108,7 @@ void serial_master_init(void) {
}

void serial_slave_init(void) {
serial_input();
serial_input_with_pullup();

#ifndef USE_SERIAL_PD2
// Enable INT0
Expand All @@ -79,92 +123,92 @@ void serial_slave_init(void) {
#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) {
serial_input();
for (int 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.
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) {
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();
_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();
serial_delay();
_delay_us(1);
_delay_sub_us(READ_WRITE_WIDTH_ADJUST);
serial_delay_half2();
}

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)) {
uint8_t b = 1<<7;
while( b ) {
if(data & b) {
serial_high();
} else {
serial_low();
}
b >>= 1;
serial_delay();
}
serial_low(); // sync_send() / senc_recv() need raise edge
}

// interrupt handle to be used by the slave device
ISR(SERIAL_PIN_INTERRUPT) {
sync_send();
serial_output();

// slave send phase
uint8_t checksum = 0;
for (int i = 0; i < SERIAL_SLAVE_BUFFER_LENGTH; ++i) {
serial_write_byte(serial_slave_buffer[i]);
sync_send();
serial_write_byte(serial_slave_buffer[i]);
checksum += serial_slave_buffer[i];
}
serial_write_byte(checksum);
sync_send();
serial_write_byte(checksum);

// wait for the sync to finish sending
serial_delay();

// read the middle of pulses
_delay_us(SERIAL_DELAY/2);
// 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) {
sync_recv();
serial_master_buffer[i] = serial_read_byte();
sync_send();
checksum_computed += serial_master_buffer[i];
}
sync_recv();
uint8_t checksum_received = serial_read_byte();
sync_send();

serial_input(); // end transaction

if ( checksum_computed != checksum_received ) {
status |= SLAVE_DATA_CORRUPT;
} else {
status &= ~SLAVE_DATA_CORRUPT;
}

sync_recv(); //weit master output to high
}

inline
Expand All @@ -178,58 +222,69 @@ bool serial_slave_DATA_CORRUPT(void) {
// 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
cli();

// signal to the slave that we want to start a transaction
serial_output();
serial_low();
_delay_us(1);
_delay_us(SLAVE_INT_WIDTH);

// wait for the slaves response
serial_input();
serial_high();
_delay_us(SERIAL_DELAY);
serial_input_with_pullup();
_delay_us(SLAVE_INT_RESPONSE_TIME);

// check if the slave is present
if (serial_read_pin()) {
// slave failed to pull the line low, assume not present
serial_output();
serial_high();
sei();
return 1;
}

// master recive phase
// 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();
serial_slave_buffer[i] = serial_read_byte();
checksum_computed += serial_slave_buffer[i];
}
uint8_t checksum_received = serial_read_byte();
sync_recv();
uint8_t checksum_received = serial_read_byte();

if (checksum_computed != checksum_received) {
serial_output();
serial_high();
sei();
return 2;
}

// 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;
// send data to the slave

for (int i = 0; i < SERIAL_MASTER_BUFFER_LENGTH; ++i) {
sync_send();
serial_write_byte(serial_master_buffer[i]);
sync_recv();
checksum += serial_master_buffer[i];
}
sync_send();
serial_write_byte(checksum);
sync_recv();

// always, release the line when not in use
serial_output();
serial_high();
sync_send();

sei();
return 0;
Expand Down