@ -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