@ -9,40 +9,85 @@
# include <avr/io.h>
# include <avr/io.h>
# include <avr/interrupt.h>
# include <avr/interrupt.h>
# include <util/delay.h>
# include <util/delay.h>
# include <stddef.h>
# include <stdbool.h>
# include <stdbool.h>
# include "serial.h"
# include "serial.h"
//#include <pro_micro.h>
# ifdef USE_SERIAL
# 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)
# define _delay_sub_us(x) __builtin_avr_delay_cycles(x)
// Serial pulse period in microseconds.
// Serial pulse period in microseconds.
# define TID_SEND_ADJUST 14
# define SELECT_SERIAL_SPEED 1
# define SELECT_SERIAL_SPEED 1
# if SELECT_SERIAL_SPEED == 0
# if SELECT_SERIAL_SPEED == 0
// Very High speed
// Very High speed
# define SERIAL_DELAY 4 // micro sec
# define SERIAL_DELAY 4 // micro sec
# define READ_WRITE_START_ADJUST 30 // cycles
# define READ_WRITE_START_ADJUST 3 3 // cycles
# define READ_WRITE_WIDTH_ADJUST 10 // cycles
# define READ_WRITE_WIDTH_ADJUST 3 // cycles
# elif SELECT_SERIAL_SPEED == 1
# elif SELECT_SERIAL_SPEED == 1
// High speed
// High speed
# define SERIAL_DELAY 6 // micro sec
# define SERIAL_DELAY 6 // micro sec
# define READ_WRITE_START_ADJUST 23 // cycles
# define READ_WRITE_START_ADJUST 30 // cycles
# define READ_WRITE_WIDTH_ADJUST 10 // cycles
# define READ_WRITE_WIDTH_ADJUST 3 // cycles
# elif SELECT_SERIAL_SPEED == 2
# elif SELECT_SERIAL_SPEED == 2
// Middle speed
// Middle speed
# define SERIAL_DELAY 12 // micro sec
# define SERIAL_DELAY 12 // micro sec
# define READ_WRITE_START_ADJUST 25 // cycles
# define READ_WRITE_START_ADJUST 30 // cycles
# define READ_WRITE_WIDTH_ADJUST 10 // cycles
# define READ_WRITE_WIDTH_ADJUST 3 // cycles
# elif SELECT_SERIAL_SPEED == 3
# elif SELECT_SERIAL_SPEED == 3
// Low speed
// Low speed
# define SERIAL_DELAY 24 // micro sec
# define SERIAL_DELAY 24 // micro sec
# define READ_WRITE_START_ADJUST 25 // cycles
# define READ_WRITE_START_ADJUST 30 // cycles
# define READ_WRITE_WIDTH_ADJUST 10 // cycles
# define READ_WRITE_WIDTH_ADJUST 3 // cycles
# elif SELECT_SERIAL_SPEED == 4
# elif SELECT_SERIAL_SPEED == 4
// Very Low speed
// Very Low speed
# define SERIAL_DELAY 50 // micro sec
# define SERIAL_DELAY 50 // micro sec
# define READ_WRITE_START_ADJUST 25 // cycles
# define READ_WRITE_START_ADJUST 30 // cycles
# define READ_WRITE_WIDTH_ADJUST 10 // cycles
# define READ_WRITE_WIDTH_ADJUST 3 // cycles
# else
# else
# error Illegal Serial Speed
# error Illegal Serial Speed
# endif
# endif
@ -51,14 +96,15 @@
# define SERIAL_DELAY_HALF1 (SERIAL_DELAY / 2)
# define SERIAL_DELAY_HALF1 (SERIAL_DELAY / 2)
# define SERIAL_DELAY_HALF2 (SERIAL_DELAY - SERIAL_DELAY / 2)
# define SERIAL_DELAY_HALF2 (SERIAL_DELAY - SERIAL_DELAY / 2)
# define SLAVE_INT_WIDTH 1
# define SLAVE_INT_WIDTH_US 1
# define SLAVE_INT_RESPONSE_TIME SERIAL_DELAY
# ifndef SERIAL_USE_MULTI_TRANSACTION
# define SLAVE_INT_RESPONSE_TIME SERIAL_DELAY
uint8_t volatile serial_slave_buffer [ SERIAL_SLAVE_BUFFER_LENGTH ] = { 0 } ;
# else
uint8_t volatile serial_master_buffer [ SERIAL_MASTER_BUFFER_LENGTH ] = { 0 } ;
# define SLAVE_INT_ACK_WIDTH_UNIT 2
# define SLAVE_INT_ACK_WIDTH 4
# endif
# define SLAVE_DATA_CORRUPT (1<<0)
static SSTD_t * Transaction_table = NULL ;
volatile uint8_t status = 0 ;
inline static
inline static
void serial_delay ( void ) {
void serial_delay ( void ) {
@ -75,12 +121,14 @@ void serial_delay_half2(void) {
_delay_us ( SERIAL_DELAY_HALF2 ) ;
_delay_us ( SERIAL_DELAY_HALF2 ) ;
}
}
inline static void serial_output ( void ) ALWAYS_INLINE ;
inline static
inline static
void serial_output ( void ) {
void serial_output ( void ) {
SERIAL_PIN_DDR | = SERIAL_PIN_MASK ;
SERIAL_PIN_DDR | = SERIAL_PIN_MASK ;
}
}
// make the serial pin an input with pull-up resistor
// make the serial pin an input with pull-up resistor
inline static void serial_input_with_pullup ( void ) ALWAYS_INLINE ;
inline static
inline static
void serial_input_with_pullup ( void ) {
void serial_input_with_pullup ( void ) {
SERIAL_PIN_DDR & = ~ SERIAL_PIN_MASK ;
SERIAL_PIN_DDR & = ~ SERIAL_PIN_MASK ;
@ -92,50 +140,58 @@ uint8_t serial_read_pin(void) {
return ! ! ( SERIAL_PIN_INPUT & SERIAL_PIN_MASK ) ;
return ! ! ( SERIAL_PIN_INPUT & SERIAL_PIN_MASK ) ;
}
}
inline static void serial_low ( void ) ALWAYS_INLINE ;
inline static
inline static
void serial_low ( void ) {
void serial_low ( void ) {
SERIAL_PIN_PORT & = ~ SERIAL_PIN_MASK ;
SERIAL_PIN_PORT & = ~ SERIAL_PIN_MASK ;
}
}
inline static void serial_high ( void ) ALWAYS_INLINE ;
inline static
inline static
void serial_high ( void ) {
void serial_high ( void ) {
SERIAL_PIN_PORT | = SERIAL_PIN_MASK ;
SERIAL_PIN_PORT | = SERIAL_PIN_MASK ;
}
}
void serial_master_init ( void ) {
void soft_serial_initiator_init ( SSTD_t * sstd_table )
serial_output ( ) ;
{
serial_high ( ) ;
Transaction_table = sstd_table ;
serial_output ( ) ;
serial_high ( ) ;
}
}
void serial_slave_init ( void ) {
void soft_serial_target_init ( SSTD_t * sstd_table )
serial_input_with_pullup ( ) ;
{
Transaction_table = sstd_table ;
serial_input_with_pullup ( ) ;
# if SERIAL_PIN_MASK == _BV(PD0)
# if SERIAL_PIN_MASK == _BV(PD0)
// Enable INT0
// Enable INT0
EIMSK | = _BV ( INT0 ) ;
EIMSK | = _BV ( INT0 ) ;
// Trigger on falling edge of INT0
// Trigger on falling edge of INT0
EICRA & = ~ ( _BV ( ISC00 ) | _BV ( ISC01 ) ) ;
EICRA & = ~ ( _BV ( ISC00 ) | _BV ( ISC01 ) ) ;
# elif SERIAL_PIN_MASK == _BV(PD2)
# elif SERIAL_PIN_MASK == _BV(PD2)
// Enable INT2
// Enable INT2
EIMSK | = _BV ( INT2 ) ;
EIMSK | = _BV ( INT2 ) ;
// Trigger on falling edge of INT2
// Trigger on falling edge of INT2
EICRA & = ~ ( _BV ( ISC20 ) | _BV ( ISC21 ) ) ;
EICRA & = ~ ( _BV ( ISC20 ) | _BV ( ISC21 ) ) ;
# else
# else
# error unknown SERIAL_PIN_MASK value
# error unknown SERIAL_PIN_MASK value
# endif
# endif
}
}
// Used by the sender to synchronize timing with the reciver.
// Used by the sender to synchronize timing with the reciver.
static void sync_recv ( void ) NO_INLINE ;
static
static
void sync_recv ( void ) {
void sync_recv ( void ) {
for ( int i = 0 ; i < SERIAL_DELAY * 5 & & serial_read_pin ( ) ; i + + ) {
for ( u int8_ t i = 0 ; i < SERIAL_DELAY * 5 & & serial_read_pin ( ) ; i + + ) {
}
}
// This shouldn't hang if the slave disconnects because the
// This shouldn't hang if the target disconnects because the
// serial line will float to high if the slave does disconnect.
// serial line will float to high if the target does disconnect.
while ( ! serial_read_pin ( ) ) ;
while ( ! serial_read_pin ( ) ) ;
}
}
// Used by the reciver to send a synchronization signal to the sender.
// Used by the reciver to send a synchronization signal to the sender.
static void sync_send ( void ) NO_INLINE ;
static
static
void sync_send ( void ) {
void sync_send ( void ) {
serial_low ( ) ;
serial_low ( ) ;
@ -144,152 +200,245 @@ void sync_send(void) {
}
}
// Reads a byte from the serial line
// Reads a byte from the serial line
static
static uint8_t serial_read_chunk ( uint8_t * pterrcount , uint8_t bit ) NO_INLINE ;
uint8_t serial_read_byte ( void ) {
static uint8_t serial_read_chunk ( uint8_t * pterrcount , uint8_t bit ) {
uint8_t byte = 0 ;
uint8_t byte , i , p , pb ;
_delay_sub_us ( READ_WRITE_START_ADJUST ) ;
_delay_sub_us ( READ_WRITE_START_ADJUST ) ;
for ( uint8_t i = 0 ; i < 8 ; + + i ) {
for ( i = 0 , byte = 0 , p = 0 ; i < bit ; i + + ) {
serial_delay_half1 ( ) ; // read the middle of pulses
serial_delay_half1 ( ) ; // read the middle of pulses
byte = ( byte < < 1 ) | serial_read_pin ( ) ;
if ( serial_read_pin ( ) ) {
_delay_sub_us ( READ_WRITE_WIDTH_ADJUST ) ;
byte = ( byte < < 1 ) | 1 ; p ^ = 1 ;
serial_delay_half2 ( ) ;
} 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 ;
return byte ;
}
}
// Sends a byte with MSB ordering
// Sends a byte with MSB ordering
static
void serial_write_chunk ( uint8_t data , uint8_t bit ) NO_INLINE ;
void serial_write_byte ( uint8_t data ) {
void serial_write_chunk ( uint8_t data , uint8_t bit ) {
uint8_t b = 1 < < 7 ;
uint8_t b , p ;
while ( b ) {
for ( p = 0 , b = 1 < < ( bit - 1 ) ; b ; b > > = 1 ) {
if ( data & b ) {
if ( data & b ) {
serial_high ( ) ;
serial_high ( ) ; p ^ = 1 ;
} else {
} else {
serial_low ( ) ;
serial_low ( ) ; p ^ = 0 ;
}
serial_delay ( ) ;
}
}
b > > = 1 ;
/* send parity bit */
if ( p & 1 ) { serial_high ( ) ; }
else { serial_low ( ) ; }
serial_delay ( ) ;
serial_delay ( ) ;
}
serial_low ( ) ; // sync_send() / senc_recv() need raise edge
}
// interrupt handle to be used by the slave device
serial_low ( ) ; // sync_send() / senc_recv() need raise edge
ISR ( SERIAL_PIN_INTERRUPT ) {
}
serial_output ( ) ;
// slave send phase
static void serial_send_packet ( uint8_t * buffer , uint8_t size ) NO_INLINE ;
uint8_t checksum = 0 ;
static
for ( int i = 0 ; i < SERIAL_SLAVE_BUFFER_LENGTH ; + + i ) {
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 ( ) ;
sync_send ( ) ;
serial_write_byte ( serial_slave_buffer [ i ] ) ;
serial_write_chunk ( data , 8 ) ;
checksum + = serial_slave_buffer [ i ] ;
}
}
sync_send ( ) ;
}
serial_write_byte ( checksum ) ;
static uint8_t serial_recive_packet ( uint8_t * buffer , uint8_t size ) NO_INLINE ;
// slave switch to input
static
sync_send ( ) ; //0
uint8_t serial_recive_packet ( uint8_t * buffer , uint8_t size ) {
serial_delay_half1 ( ) ; //1
uint8_t pecount = 0 ;
serial_low ( ) ; //2
for ( uint8_t i = 0 ; i < size ; + + i ) {
serial_input_with_pullup ( ) ; //2
uint8_t data ;
serial_delay_half1 ( ) ; //3
// slave recive phase
uint8_t checksum_computed = 0 ;
for ( int i = 0 ; i < SERIAL_MASTER_BUFFER_LENGTH ; + + i ) {
sync_recv ( ) ;
sync_recv ( ) ;
serial_master_buffer[ i ] = serial_read_byte ( ) ;
data = serial_read_chunk ( & pecount , 8 ) ;
checksum_computed + = serial_master_ buffer[ i ] ;
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 ( ) ;
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 ) {
serial_high ( ) ; // response step1 low->high
status | = SLAVE_DATA_CORRUPT ;
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 {
} else {
status & = ~ SLAVE_DATA_CORRUPT ;
* trans - > status = TRANSACTION_ACCEPTED ;
}
}
sync_recv ( ) ; //weit master output to high
sync_recv ( ) ; //weit initiator output to high
}
inline
bool serial_slave_DATA_CORRUPT ( void ) {
return status & SLAVE_DATA_CORRUPT ;
}
}
// 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:
// Returns:
// 0 => no error
// TRANSACTION_END
// 1 => slave did not respond
// TRANSACTION_NO_RESPONSE
// 2 => checksum error
// TRANSACTION_DATA_ERROR
int serial_update_buffers ( void ) {
// this code is very time dependent, so we need to disable interrupts
// 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 ( ) ;
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_output ( ) ;
serial_low ( ) ;
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 ( ) ;
serial_input_with_pullup ( ) ;
_delay_us ( SLAVE_INT_RESPONSE_TIME ) ;
_delay_us ( SLAVE_INT_RESPONSE_TIME ) ;
// check if the slave is present
// check if the target is present
if ( serial_read_pin ( ) ) {
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_output ( ) ;
serial_high ( ) ;
serial_high ( ) ;
* trans - > status = TRANSACTION_NO_RESPONSE ;
sei ( ) ;
sei ( ) ;
return 1 ;
return TRANSACTION_NO_RESPONSE ;
}
}
// master recive phase
# else
// if the slave is present syncronize with it
// 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 ;
// wait for the target response (step1 low->high)
// receive data from the slave
serial_input_with_pullup ( ) ;
for ( int i = 0 ; i < SERIAL_SLAVE_BUFFER_LENGTH ; + + i ) {
while ( ! serial_read_pin ( ) ) {
sync_recv ( ) ;
_delay_sub_us ( 2 ) ;
serial_slave_buffer [ i ] = serial_read_byte ( ) ;
checksum_computed + = serial_slave_buffer [ i ] ;
}
}
sync_recv ( ) ;
uint8_t checksum_received = serial_read_byte ( ) ;
if ( checksum_computed ! = checksum_received ) {
// check if the target is present (step2 high->low)
serial_output ( ) ;
for ( int i = 0 ; serial_read_pin ( ) ; i + + ) {
serial_high ( ) ;
if ( i > SLAVE_INT_ACK_WIDTH + 1 ) {
sei ( ) ;
// slave failed to pull the line low, assume not present
return 2 ;
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
// initiator recive phase
sync_recv ( ) ; //0
// if the target is present syncronize with it
serial_delay ( ) ; //1
if ( trans - > target2initiator_buffer_size > 0 ) {
serial_low ( ) ; //3
if ( ! serial_recive_packet ( ( uint8_t * ) trans - > target2initiator_buffer ,
serial_output ( ) ; // 3
trans - > target2initiator_buffer_size ) ) {
serial_delay_half1 ( ) ; //4
serial_output ( ) ;
serial_high ( ) ;
// master send phase
* trans - > status = TRANSACTION_DATA_ERROR ;
uint8_t checksum = 0 ;
sei ( ) ;
return TRANSACTION_DATA_ERROR ;
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 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
// always, release the line when not in use
sync_send ( ) ;
sync_send ( ) ;
* trans - > status = TRANSACTION_END ;
sei ( ) ;
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
# endif