#ifndef COM_H
#define COM_H

#include <stdint.h>
#include <stddef.h>
#include "check.h"

/**
 * \addtogroup COM Serial Communication
 *
 * \brief Serial communication with SPiCboards
 *
 * \image html spicboard3_com_small.jpg
 * The SPiCboard3 can perform serial communication using the three PINs
 * on its upper right corner (\c TX at \c PD1, \c GND and \c RX at \c PD0 ).
 * To establish a connection you have to connect its \c RX with \c TX of
 * another board, both \c GND pins and \c TX with \c RX, like shown on the image
 * above.
 *
 * For the transmission this library uses the transmission setting \c 8-E-1,
 * in which we send (in addition to the start bit) 8 data bits,
 * one even parity bit and one stop bit.
 *
 * Example: Transmission of the (decimal) value \c 23
 * \image html com.png
 * - \c PD0 is configured as input (\c RX)
 * - \c PD1 is configured as output (\c TX)
 * - The idle voltage is 5V, a logical high (this is a historic legacy from
 *   telegraphy, in which the line is held high to show that the line and
 *   transmitter are not damaged).
 * - the transmission begins with the start bit, which changes the level from
 *   logical high (idle) to logical low.
 * - the time interval \c T for transmitting one symbol is defined by the
 *   \c BAUD_RATE : \f$ T = \frac{1s}{BAUD\_RATE} \f$
 * - the payload byte is sent bitwise, starting with the least significant bit
 * - the parity bit ensures that an even number of logical highs has been sent
 *   (including parity - calculated using \c XOR ) - it can be used to detect
 *   transmission errors.
 * - the stopbit finishes the transmission and leaves the line in a logical high
 *
 * \note The libspicboard implementation uses the 16bit <b>timer 4</b>.
 *
 * \note For educational purposes the transmission is implemented completely
 *       in software - for more advanced serial communcation you can use
 *       <a href="https://en.wikipedia.org/wiki/Universal_asynchronous_receiver-transmitter">USART</a>,
 *       described in Section 25 of the <a href="Atmel-42397-8-bit-AVR-Microcontroller-ATmega328PB_Datasheet.pdf">ATMega328PB Datasheet</a>.
 *
 * \sa com.h
 *
 * @{
 * \file com.h
 * \version \$Rev: 15477 $
 */

#ifndef BAUD_RATE
/**
 * \def BAUD_RATE
 *
 * Default baud rate (symbols per second) is 1200
 */
#define BAUD_RATE      1200UL
#endif

#if defined(__BUILTIN_AVR_DELAY_CYCLES)
/**
 * \def sb_com_wait(FACTOR)
 *
 * Active wait loop for the transmission interval time \c T (determined by the baud rate) multiplied by \a FACTOR
 */
#define sb_com_wait(FACTOR)	__builtin_avr_delay_cycles((F_CPU*FACTOR)/BAUD_RATE)
#else
#error Compiler does not support __builtin_avr_delay_cycles, which is required for sb_com_wait()
#endif

// Error
/**
 * \brief Error states used in mask
 */
typedef enum {
	ERROR_NO_STOP_BIT = 1,      /**< no stop bit received */
	ERROR_PARITY = 2,           /**< parity bit wrong */
	ERROR_BUFFER_FULL = 4,      /**< receive buffer full */
	ERROR_INVALID_POINTER = 8,  /**< data pointer invalid */
} __attribute__ ((__packed__)) COM_ERROR_STATUS;

CHECK_ENUM_SIZE(COM_ERROR_STATUS, 1)

/**
 * \brief Send one or more bytes, plus start, parity and stop bit for each byte
 *
 * \param data  the byte array to send
 * \param len   length of the byte array
 */
void sb_com_sendArray(uint8_t * data, size_t len);

/**
 * \brief Send a single byte (8-bit) plus one start-, parity and stop bit
 *
 * \param data  the byte to send
 */
void sb_com_sendByte(uint8_t data);

/**
 * \brief Send a 16-bit value, split up into two byte transmissions
 *        plus start, parity and stop bit each
 *
 * \param data  the bytes to send
 */
void sb_com_sendDoubleByte(uint16_t data);

/**
 * \brief Receive one or more bytes with start, parity and stop bit each
 *
 * We try to sample in the middle of each symbol, therefore we have to wait
 * about \f$ 1.5T \f$ after receiving the start bit before reading the first
 * data bit. Every subsequent symbol is sampled after a \f$ T \f$ delay.
 *
 * \image html com_recv.png
 *
 * Transmission errors are recorded and returned. They should not be ignored,
 * because they usually indicate invalid data - there is no resend mechanism
 * implemented in this library!
 *
 * \param data  pointer to allocated space for the received byte array
 * \param len   bytes to receive (and length of the pre-allocated space)
 *
 * \retval 0    no error
 * \retval >0   bit mask describing the error:
 *              - 1. bit: no stop bit received (\c ERROR_NO_STOP_BIT )
 *              - 2. bit: parity bit wrong (\c ERROR_PARITY )
 *              - 3. bit: receive buffer full (\c ERROR_BUFFER_FULL )
 *              - 4. bit: data pointer invalid (\c ERROR_INVALID_POINTER )
 */
uint8_t sb_com_receiveArray(uint8_t * data, size_t len);

/**
 * \brief Receive a single byte (8-bit) plus start, parity and stop bit
 *
 * \param data  pointer to allocated space for the received byte
 *
 * \retval 0    no error
 * \retval >0   bit mask describing the error
 *              (see sb_com_receiveArray() for details)
 */
uint8_t sb_com_receiveByte(uint8_t *data);

/**
 * \brief Receive two bytes (16-bit) plus start, parity and stop bit each
 *
 * \param data  pointer to allocated space for the received two bytes
 *
 * \retval 0    no error
 * \retval >0   bit mask describing the error
 *              (see sb_com_receiveArray() for details)
 */
uint8_t sb_com_receiveDoubleByte(uint16_t *data);

/**
 * \brief Check if there is a new byte item waiting to be received
 *
 * This method provides information if there is already the next item <i>on the line</i> to be received.
 *
 * In other words, a positive answer ensures that a call to
 * sb_com_receiveByte() would return within the usual processing time
 * (without the need to wait for the other communication end point to begin transmission).
 *
 * \retval 1  there is one item to receive
 * \retval 0  currently no item to receive, would have to wait
 */
uint8_t sb_com_byteReadyForReceive();

/** @}*/

#endif
