2012-10-18 01:41:37 +00:00
/*
Copyright 2012 Jun WAKO < wakojun @ gmail . com >
This software is licensed with a Modified BSD License .
All of this is supposed to be Free Software , Open Source , DFSG - free ,
GPL - compatible , and OK to use in both free and proprietary applications .
Additions and corrections to this file are welcome .
Redistribution and use in source and binary forms , with or without
modification , are permitted provided that the following conditions are met :
* Redistributions of source code must retain the above copyright
notice , this list of conditions and the following disclaimer .
* Redistributions in binary form must reproduce the above copyright
notice , this list of conditions and the following disclaimer in
the documentation and / or other materials provided with the
distribution .
* Neither the name of the copyright holders nor the names of
contributors may be used to endorse or promote products derived
from this software without specific prior written permission .
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS " AS IS "
AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR
CONSEQUENTIAL DAMAGES ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS
INTERRUPTION ) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY , WHETHER IN
CONTRACT , STRICT LIABILITY , OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE )
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE , EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE .
*/
# include <stdbool.h>
# include <avr/io.h>
# include <avr/interrupt.h>
2012-10-19 05:06:18 +00:00
# include <util/delay.h>
2012-10-18 01:41:37 +00:00
# include "serial.h"
2012-10-19 05:06:18 +00:00
/*
* Stupid Inefficient Busy - wait Software Serial
2013-02-24 23:40:15 +00:00
* which is still useful for negative logic signal like Sun protocol
* if it is not supported by hardware UART .
*
* TODO : delay is not accurate enough . Instruction cycle should be counted and inline assemby is needed .
2012-10-19 05:06:18 +00:00
*/
2013-02-24 23:40:15 +00:00
# define WAIT_US (1000000L / SERIAL_SOFT_BAUD)
# ifdef SERIAL_SOFT_LOGIC_NEGATIVE
# define SERIAL_SOFT_RXD_IN() !(SERIAL_SOFT_RXD_READ())
# define SERIAL_SOFT_TXD_ON() SERIAL_SOFT_TXD_LO()
# define SERIAL_SOFT_TXD_OFF() SERIAL_SOFT_TXD_HI()
# else
# define SERIAL_SOFT_RXD_IN() !!(SERIAL_SOFT_RXD_READ())
# define SERIAL_SOFT_TXD_ON() SERIAL_SOFT_TXD_HI()
# define SERIAL_SOFT_TXD_OFF() SERIAL_SOFT_TXD_LO()
# endif
# ifdef SERIAL_SOFT_PARITY_EVEN
# define SERIAL_SOFT_PARITY_VAL 0
# elif defined(SERIAL_SOFT_PARITY_ODD)
# define SERIAL_SOFT_PARITY_VAL 1
# endif
2012-10-18 01:41:37 +00:00
2013-02-23 05:32:34 +00:00
/* debug for signal timing, see debug pin with oscilloscope */
2013-02-24 23:40:15 +00:00
# define SERIAL_SOFT_DEBUG
2013-02-23 05:32:34 +00:00
# ifdef SERIAL_SOFT_DEBUG
# define SERIAL_SOFT_DEBUG_INIT() (DDRD |= 1<<7)
# define SERIAL_SOFT_DEBUG_TGL() (PORTD ^= 1<<7)
# else
# define SERIAL_SOFT_DEBUG_INIT()
# define SERIAL_SOFT_DEBUG_TGL()
# endif
2012-10-18 01:41:37 +00:00
void serial_init ( void )
{
2013-02-23 05:32:34 +00:00
SERIAL_SOFT_DEBUG_INIT ( ) ;
2013-02-24 23:40:15 +00:00
SERIAL_SOFT_RXD_INIT ( ) ;
SERIAL_SOFT_TXD_INIT ( ) ;
2012-10-18 01:41:37 +00:00
}
2012-10-21 13:12:36 +00:00
/* RX ring buffer */
2012-10-18 01:41:37 +00:00
# define RBUF_SIZE 8
static uint8_t rbuf [ RBUF_SIZE ] ;
static uint8_t rbuf_head = 0 ;
static uint8_t rbuf_tail = 0 ;
2013-02-22 06:48:35 +00:00
2012-10-18 01:41:37 +00:00
uint8_t serial_recv ( void )
{
uint8_t data = 0 ;
if ( rbuf_head = = rbuf_tail ) {
return 0 ;
}
data = rbuf [ rbuf_tail ] ;
rbuf_tail = ( rbuf_tail + 1 ) % RBUF_SIZE ;
return data ;
}
2013-02-22 10:38:06 +00:00
int16_t serial_recv2 ( void )
{
uint8_t data = 0 ;
if ( rbuf_head = = rbuf_tail ) {
return - 1 ;
}
data = rbuf [ rbuf_tail ] ;
rbuf_tail = ( rbuf_tail + 1 ) % RBUF_SIZE ;
return data ;
}
2012-10-21 13:12:36 +00:00
void serial_send ( uint8_t data )
{
/* signal state: IDLE: ON, START: OFF, STOP: ON, DATA0: OFF, DATA1: ON */
2013-02-24 23:40:15 +00:00
# ifdef SERIAL_SOFT_BIT_ORDER_MSB
2014-08-26 08:39:04 +00:00
# ifdef SERIAL_SOFT_DATA_7BIT
uint8_t mask = 0x40 ;
# else
2012-10-21 13:12:36 +00:00
uint8_t mask = 0x80 ;
2014-08-26 08:39:04 +00:00
# endif
2012-10-21 13:12:36 +00:00
# else
uint8_t mask = 0x01 ;
# endif
2013-02-24 16:02:14 +00:00
uint8_t parity = 0 ;
/* start bit */
2013-02-24 23:40:15 +00:00
SERIAL_SOFT_TXD_OFF ( ) ;
_delay_us ( WAIT_US ) ;
2013-02-24 16:02:14 +00:00
2014-08-26 08:39:04 +00:00
# ifdef SERIAL_SOFT_DATA_7BIT
while ( mask & 0x7F ) {
# else
while ( mask & 0xFF ) {
# endif
2013-02-24 16:02:14 +00:00
if ( data & mask ) {
2013-02-24 23:40:15 +00:00
SERIAL_SOFT_TXD_ON ( ) ;
2013-02-24 16:02:14 +00:00
parity ^ = 1 ;
} else {
2013-02-24 23:40:15 +00:00
SERIAL_SOFT_TXD_OFF ( ) ;
2013-02-24 16:02:14 +00:00
}
2013-02-24 23:40:15 +00:00
_delay_us ( WAIT_US ) ;
2012-10-21 13:12:36 +00:00
2013-02-24 23:40:15 +00:00
# ifdef SERIAL_SOFT_BIT_ORDER_MSB
2012-10-21 13:12:36 +00:00
mask > > = 1 ;
# else
mask < < = 1 ;
# endif
}
2013-02-24 23:40:15 +00:00
# if defined(SERIAL_SOFT_PARITY_EVEN) || defined(SERIAL_SOFT_PARITY_ODD)
2013-02-24 16:02:14 +00:00
/* to center of parity bit */
2013-02-24 23:40:15 +00:00
if ( parity ! = SERIAL_SOFT_PARITY_VAL ) {
SERIAL_SOFT_TXD_ON ( ) ;
2013-02-24 16:02:14 +00:00
} else {
2013-02-24 23:40:15 +00:00
SERIAL_SOFT_TXD_OFF ( ) ;
2013-02-24 16:02:14 +00:00
}
2013-02-24 23:40:15 +00:00
_delay_us ( WAIT_US ) ;
2013-02-24 16:02:14 +00:00
# endif
2012-10-21 13:12:36 +00:00
/* stop bit */
2013-02-24 23:40:15 +00:00
SERIAL_SOFT_TXD_ON ( ) ;
_delay_us ( WAIT_US ) ;
2012-10-21 13:12:36 +00:00
}
/* detect edge of start bit */
2013-02-24 23:40:15 +00:00
ISR ( SERIAL_SOFT_RXD_VECT )
2012-10-18 01:41:37 +00:00
{
2013-02-24 23:40:15 +00:00
SERIAL_SOFT_DEBUG_TGL ( ) ;
SERIAL_SOFT_RXD_INT_ENTER ( )
2012-10-19 05:06:18 +00:00
uint8_t data = 0 ;
2013-02-22 00:53:46 +00:00
2013-02-24 23:40:15 +00:00
# ifdef SERIAL_SOFT_BIT_ORDER_MSB
2014-08-26 08:39:04 +00:00
# ifdef SERIAL_SOFT_DATA_7BIT
uint8_t mask = 0x40 ;
# else
2012-10-21 13:12:36 +00:00
uint8_t mask = 0x80 ;
2014-08-26 08:39:04 +00:00
# endif
2012-10-19 05:06:18 +00:00
# else
2012-10-21 13:12:36 +00:00
uint8_t mask = 0x01 ;
2012-10-19 05:06:18 +00:00
# endif
2013-02-22 00:53:46 +00:00
uint8_t parity = 0 ;
2012-10-21 13:12:36 +00:00
/* to center of start bit */
2013-02-22 10:38:06 +00:00
_delay_us ( WAIT_US / 2 ) ;
2013-02-24 23:40:15 +00:00
SERIAL_SOFT_DEBUG_TGL ( ) ;
2012-10-19 05:06:18 +00:00
do {
2012-10-21 13:12:36 +00:00
/* to center of next bit */
2013-02-22 10:38:06 +00:00
_delay_us ( WAIT_US ) ;
2012-10-19 05:06:18 +00:00
2013-02-24 23:40:15 +00:00
SERIAL_SOFT_DEBUG_TGL ( ) ;
if ( SERIAL_SOFT_RXD_IN ( ) ) {
2012-10-21 13:12:36 +00:00
data | = mask ;
2013-02-22 00:53:46 +00:00
parity ^ = 1 ;
2012-10-19 05:06:18 +00:00
}
2013-02-24 23:40:15 +00:00
# ifdef SERIAL_SOFT_BIT_ORDER_MSB
2012-10-21 13:12:36 +00:00
mask > > = 1 ;
2012-10-19 05:06:18 +00:00
# else
2012-10-21 13:12:36 +00:00
mask < < = 1 ;
2012-10-19 05:06:18 +00:00
# endif
2014-08-26 08:39:04 +00:00
# ifdef SERIAL_SOFT_DATA_7BIT
} while ( mask & 0x7F ) ;
# else
} while ( mask & 0xFF ) ;
# endif
2013-02-22 00:53:46 +00:00
2013-02-24 23:40:15 +00:00
# if defined(SERIAL_SOFT_PARITY_EVEN) || defined(SERIAL_SOFT_PARITY_ODD)
2013-02-22 00:53:46 +00:00
/* to center of parity bit */
2013-02-22 10:38:06 +00:00
_delay_us ( WAIT_US ) ;
2013-02-24 23:40:15 +00:00
if ( SERIAL_SOFT_RXD_IN ( ) ) { parity ^ = 1 ; }
SERIAL_SOFT_DEBUG_TGL ( ) ;
2013-02-23 05:32:34 +00:00
# endif
2013-02-22 00:53:46 +00:00
2012-10-21 13:12:36 +00:00
/* to center of stop bit */
2013-02-22 10:38:06 +00:00
_delay_us ( WAIT_US ) ;
2012-10-19 05:06:18 +00:00
2012-10-18 01:41:37 +00:00
uint8_t next = ( rbuf_head + 1 ) % RBUF_SIZE ;
2013-02-24 23:40:15 +00:00
# if defined(SERIAL_SOFT_PARITY_EVEN) || defined(SERIAL_SOFT_PARITY_ODD)
if ( ( parity = = SERIAL_SOFT_PARITY_VAL ) & & next ! = rbuf_tail ) {
2013-02-23 05:32:34 +00:00
# else
if ( next ! = rbuf_tail ) {
# endif
2012-10-19 05:06:18 +00:00
rbuf [ rbuf_head ] = data ;
2012-10-18 01:41:37 +00:00
rbuf_head = next ;
}
2012-10-19 05:06:18 +00:00
2013-02-24 23:40:15 +00:00
SERIAL_SOFT_RXD_INT_EXIT ( ) ;
SERIAL_SOFT_DEBUG_TGL ( ) ;
2012-10-18 01:41:37 +00:00
}