* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
- * Copyright 2003, 2004 Develer S.r.l. (http://www.develer.com/)
- * Copyright 2000 Bernardo Innocenti <bernie@codewiz.org>
+ * Copyright 2003, 2004, 2010 Develer S.r.l. (http://www.develer.com/)
+ * Copyright 2000 Bernie Innocenti <bernie@codewiz.org>
*
* -->
*
- * \brief AVR UART and SPI I/O driver
- *
- * Rationale for project_ks hardware.
- *
- * The serial 0 on the board_kf board is used to communicate with the
- * smart card, which has the TX and RX lines connected together. To
- * allow the smart card to drive the RX line of the CPU the CPU TX has
- * to be in a high impedance state.
- * Whenever a transmission is done and there is nothing more to send
- * the transmitter is turn off. The output pin is held in input with
- * pull-up enabled, to avoid capturing noise from the nearby RX line.
- *
- * The line on the KBus port must keep sending data, even when
- * there is nothing to transmit, because a burst data transfer
- * generates noise on the audio channels.
- * This is accomplished using the multiprocessor mode of the
- * ATmega64/128 serial.
- *
- * The receiver keeps the MPCM bit always on. When useful data
- * is trasmitted the address bit is set. The receiver hardware
- * consider the frame as address info and receive it.
- * When useless fill bytes are sent the address bit is cleared
- * and the receiver will ignore them, avoiding useless triggering
- * of RXC interrupt.
+ * \brief AVR UART and SPI I/O driver (Implementation)
*
* \version $Id$
- * \author Bernardo Innocenti <bernie@develer.com>
+ *
+ * \author Bernie Innocenti <bernie@codewiz.org>
* \author Stefano Fedrigo <aleph@develer.com>
+ * \author Luca Ottaviano <lottaviano@develer.com>
*/
-#include <drv/ser.h>
-#include <drv/ser_p.h>
+#include "hw/hw_ser.h" /* Required for bus macros overrides */
+#include <hw/hw_cpufreq.h> /* CPU_FREQ */
-#include <hw_ser.h> /* Required for bus macros overrides */
-#include <hw_cpu.h> /* CLOCK_FREQ */
-#include <appconfig.h>
+#include "cfg/cfg_ser.h"
#include <cfg/macros.h> /* DIV_ROUND */
#include <cfg/debug.h>
+#include <cfg/cfg_arch.h> // ARCH_NIGHTTEST
+
+#include <drv/ser.h>
+#include <drv/ser_p.h>
#include <drv/timer.h>
-#include <mware/fifobuf.h>
+
+#include <struct/fifobuf.h>
#include <avr/io.h>
+
#if defined(__AVR_LIBC_VERSION__) && (__AVR_LIBC_VERSION__ >= 10400UL)
#include <avr/interrupt.h>
#else
#define BIT_RXEN1 RXEN1
#define BIT_TXEN1 TXEN1
#define BIT_UDRIE1 UDRIE1
+#elif CPU_AVR_ATMEGA328P
+ #define BIT_RXCIE0 RXCIE0
+ #define BIT_RXEN0 RXEN0
+ #define BIT_TXEN0 TXEN0
+ #define BIT_UDRIE0 UDRIE0
+
+ #define BIT_RXCIE1 RXCIE0
+ #define BIT_RXEN1 RXEN0
+ #define BIT_TXEN1 TXEN0
+ #define BIT_UDRIE1 UDRIE0
#else
#define BIT_RXCIE0 RXCIE
#define BIT_RXEN0 RXEN
#define SPI_SCK_BIT PB1
#define SPI_MOSI_BIT PB2
#define SPI_MISO_BIT PB3
-#elif CPU_AVR_ATMEGA8
+// TODO: these bits are the same as ATMEGA8 but the defines in avr-gcc are different.
+// They should be the same!
+#elif CPU_AVR_ATMEGA328P
+ #define SPI_PORT PORTB
+ #define SPI_DDR DDRB
+ #define SPI_SS_BIT PORTB2
+ #define SPI_SCK_BIT PORTB5
+ #define SPI_MOSI_BIT PORTB3
+ #define SPI_MISO_BIT PORTB4
+#elif CPU_AVR_ATMEGA8 || CPU_AVR_ATMEGA168
#define SPI_PORT PORTB
#define SPI_DDR DDRB
#define SPI_SS_BIT PB2
/* USART register definitions */
#if CPU_AVR_ATMEGA64 || CPU_AVR_ATMEGA128 || CPU_AVR_ATMEGA1281
#define AVR_HAS_UART1 1
+#elif CPU_AVR_ATMEGA328P
+ #define AVR_HAS_UART1 0
+ #define USART0_UDRE_vect USART_UDRE_vect
+ #define USART0_RX_vect USART_RX_vect
+ #define SIG_UART0_TRANS SIG_UART_TRANS
#elif CPU_AVR_ATMEGA8
#define AVR_HAS_UART1 0
#define UCSR0A UCSRA
#define UDR0 UDR
#define UBRR0L UBRRL
#define UBRR0H UBRRH
+/* TODO: The following SIGs are old style interrupts, must be refactored */
#define SIG_UART0_DATA SIG_UART_DATA
#define SIG_UART0_RECV SIG_UART_RECV
#define SIG_UART0_TRANS SIG_UART_TRANS
#define UDR0 UDR
#define UCSR0A USR
#define UBRR0L UBRR
+/* TODO: The following SIGs are old style interrupts, must be refactored */
#define SIG_UART0_DATA SIG_UART_DATA
#define SIG_UART0_RECV SIG_UART_RECV
#define SIG_UART0_TRANS SIG_UART_TRANS
#endif
-/**
- * \def CONFIG_SER_STROBE
- *
- * This is a debug facility that can be used to
- * monitor SER interrupt activity on an external pin.
- *
- * To use strobes, redefine the macros SER_STROBE_ON,
- * SER_STROBE_OFF and SER_STROBE_INIT and set
- * CONFIG_SER_STROBE to 1.
- */
-#if !defined(CONFIG_SER_STROBE) || !CONFIG_SER_STROBE
- #define SER_STROBE_ON do {/*nop*/} while(0)
- #define SER_STROBE_OFF do {/*nop*/} while(0)
- #define SER_STROBE_INIT do {/*nop*/} while(0)
-#endif
-
-
/* From the high-level serial driver */
-extern struct Serial ser_handles[SER_CNT];
+extern struct Serial *ser_handles[SER_CNT];
/* TX and RX buffers */
static unsigned char uart0_txbuffer[CONFIG_UART0_TXBUFSIZE];
};
-/*
- * These are to trick GCC into *not* using absolute addressing mode
- * when accessing ser_handles, which is very expensive.
- *
- * Accessing through these pointers generates much shorter
- * (and hopefully faster) code.
- */
-struct Serial *ser_uart0 = &ser_handles[SER_UART0];
-#if AVR_HAS_UART1
-struct Serial *ser_uart1 = &ser_handles[SER_UART1];
-#endif
-struct Serial *ser_spi = &ser_handles[SER_SPI];
-
-
/*
* Callbacks
static void uart0_setbaudrate(UNUSED_ARG(struct SerialHardware *, _hw), unsigned long rate)
{
/* Compute baud-rate period */
- uint16_t period = DIV_ROUND(CLOCK_FREQ / 16UL, rate) - 1;
+ uint16_t period = DIV_ROUND(CPU_FREQ / 16UL, rate) - 1;
#if !CPU_AVR_ATMEGA103
UBRR0H = (period) >> 8;
static void uart1_setbaudrate(UNUSED_ARG(struct SerialHardware *, _hw), unsigned long rate)
{
/* Compute baud-rate period */
- uint16_t period = DIV_ROUND(CLOCK_FREQ / 16UL, rate) - 1;
+ uint16_t period = DIV_ROUND(CPU_FREQ / 16UL, rate) - 1;
UBRR1H = (period) >> 8;
UBRR1L = (period);
* - as input but tied high forever!
* This driver set the pin as output.
*/
- #warning SPI SS pin set as output for proper operation, check schematics for possible conflicts.
+ #warning FIXME:SPI SS pin set as output for proper operation, check schematics for possible conflicts.
ATOMIC(SPI_DDR |= BV(SPI_SS_BIT));
ATOMIC(SPI_DDR &= ~BV(SPI_MISO_BIT));
{
struct AvrSerial *hw = (struct AvrSerial *)_hw;
- cpuflags_t flags;
+ cpu_flags_t flags;
IRQ_SAVE_DISABLE(flags);
/* Send data only if the SPI is not already transmitting */
- if (!hw->sending && !fifo_isempty(&ser_spi->txfifo))
+ if (!hw->sending && !fifo_isempty(&ser_handles[SER_SPI]->txfifo))
{
hw->sending = true;
- SPDR = fifo_pop(&ser_spi->txfifo);
+ SPDR = fifo_pop(&ser_handles[SER_SPI]->txfifo);
}
IRQ_RESTORE(flags);
#if CONFIG_SER_HWHANDSHAKE
/// This interrupt is triggered when the CTS line goes high
-SIGNAL(SIG_CTS)
+DECLARE_ISR(SIG_CTS)
{
// Re-enable UDR empty interrupt and TX, then disable CTS interrupt
- UCSR0B = BV(RXCIE) | BV(UDRIE) | BV(RXEN) | BV(TXEN);
+ UCSR0B = BV(BIT_RXCIE0) | BV(BIT_UDRIE0) | BV(BIT_RXEN0) | BV(BIT_TXEN0);
EIMSK &= ~EIMSKF_CTS;
}
/**
* Serial 0 TX interrupt handler
*/
-SIGNAL(USART0_UDRE_vect)
+DECLARE_ISR(USART0_UDRE_vect)
{
SER_STROBE_ON;
- struct FIFOBuffer * const txfifo = &ser_uart0->txfifo;
+ struct FIFOBuffer * const txfifo = &ser_handles[SER_UART0]->txfifo;
if (fifo_isempty(txfifo))
{
{
// Disable rx interrupt and tx, enable CTS interrupt
// UNTESTED
- UCSR0B = BV(RXCIE) | BV(RXEN) | BV(TXEN);
+ UCSR0B = BV(BIT_RXCIE0) | BV(BIT_RXEN0) | BV(BIT_TXEN0);
EIFR |= EIMSKF_CTS;
EIMSK |= EIMSKF_CTS;
}
* otherwise we'd stop the serial port with some data
* still pending in the buffer.
*/
-SIGNAL(SIG_UART0_TRANS)
+DECLARE_ISR(SIG_UART0_TRANS)
{
SER_STROBE_ON;
- struct FIFOBuffer * const txfifo = &ser_uart0->txfifo;
+ struct FIFOBuffer * const txfifo = &ser_handles[SER_UART0]->txfifo;
if (fifo_isempty(txfifo))
{
SER_UART0_BUS_TXOFF;
UARTDescs[SER_UART0].sending = false;
}
else
- UCSR0B = BV(RXCIE) | BV(UDRIE) | BV(RXEN) | BV(TXEN);
+ UCSR0B = BV(BIT_RXCIE0) | BV(BIT_UDRIE0) | BV(BIT_RXEN0) | BV(BIT_TXEN0);
SER_STROBE_OFF;
}
/**
* Serial 1 TX interrupt handler
*/
-SIGNAL(USART1_UDRE_vect)
+DECLARE_ISR(USART1_UDRE_vect)
{
SER_STROBE_ON;
- struct FIFOBuffer * const txfifo = &ser_uart1->txfifo;
+ struct FIFOBuffer * const txfifo = &ser_handles[SER_UART1]->txfifo;
if (fifo_isempty(txfifo))
{
{
// Disable rx interrupt and tx, enable CTS interrupt
// UNTESTED
- UCSR1B = BV(RXCIE) | BV(RXEN) | BV(TXEN);
+ UCSR1B = BV(BIT_RXCIE1) | BV(BIT_RXEN1) | BV(BIT_TXEN1);
EIFR |= EIMSKF_CTS;
EIMSK |= EIMSKF_CTS;
}
*
* \sa port 0 TX complete handler.
*/
-SIGNAL(SIG_UART1_TRANS)
+DECLARE_ISR(USART1_TX_vect)
{
SER_STROBE_ON;
- struct FIFOBuffer * const txfifo = &ser_uart1->txfifo;
+ struct FIFOBuffer * const txfifo = &ser_handles[SER_UART1]->txfifo;
if (fifo_isempty(txfifo))
{
SER_UART1_BUS_TXOFF;
UARTDescs[SER_UART1].sending = false;
}
else
- UCSR1B = BV(RXCIE) | BV(UDRIE) | BV(RXEN) | BV(TXEN);
+ UCSR1B = BV(BIT_RXCIE1) | BV(BIT_UDRIE1) | BV(BIT_RXEN1) | BV(BIT_TXEN1);
SER_STROBE_OFF;
}
* RXCIE is cleared. Unfortunately the RXC flag is read-only
* and can't be cleared by code.
*/
-SIGNAL(USART0_RX_vect)
+DECLARE_ISR(USART0_RX_vect)
{
SER_STROBE_ON;
//IRQ_ENABLE;
/* Should be read before UDR */
- ser_uart0->status |= UCSR0A & (SERRF_RXSROVERRUN | SERRF_FRAMEERROR);
+ ser_handles[SER_UART0]->status |= UCSR0A & (SERRF_RXSROVERRUN | SERRF_FRAMEERROR);
/* To clear the RXC flag we must _always_ read the UDR even when we're
* not going to accept the incoming data, otherwise a new interrupt
* will occur once the handler terminates.
*/
char c = UDR0;
- struct FIFOBuffer * const rxfifo = &ser_uart0->rxfifo;
+ struct FIFOBuffer * const rxfifo = &ser_handles[SER_UART0]->rxfifo;
if (fifo_isfull(rxfifo))
- ser_uart0->status |= SERRF_RXFIFOOVERRUN;
+ ser_handles[SER_UART0]->status |= SERRF_RXFIFOOVERRUN;
else
{
fifo_push(rxfifo, c);
* is heavily loaded, because an interrupt could be retriggered
* when executing the handler prologue before RXCIE is disabled.
*
- * \see SIGNAL(USART1_RX_vect)
+ * \see DECLARE_ISR(USART1_RX_vect)
*/
-SIGNAL(USART1_RX_vect)
+DECLARE_ISR(USART1_RX_vect)
{
SER_STROBE_ON;
//IRQ_ENABLE;
/* Should be read before UDR */
- ser_uart1->status |= UCSR1A & (SERRF_RXSROVERRUN | SERRF_FRAMEERROR);
+ ser_handles[SER_UART1]->status |= UCSR1A & (SERRF_RXSROVERRUN | SERRF_FRAMEERROR);
/* To avoid an IRQ storm, we must _always_ read the UDR even when we're
* not going to accept the incoming data
*/
char c = UDR1;
- struct FIFOBuffer * const rxfifo = &ser_uart1->rxfifo;
+ struct FIFOBuffer * const rxfifo = &ser_handles[SER_UART1]->rxfifo;
//ASSERT_VALID_FIFO(rxfifo);
if (UNLIKELY(fifo_isfull(rxfifo)))
- ser_uart1->status |= SERRF_RXFIFOOVERRUN;
+ ser_handles[SER_UART1]->status |= SERRF_RXFIFOOVERRUN;
else
{
fifo_push(rxfifo, c);
/**
* SPI interrupt handler
*/
-SIGNAL(SIG_SPI)
+DECLARE_ISR(SIG_SPI)
{
SER_STROBE_ON;
/* Read incoming byte. */
- if (!fifo_isfull(&ser_spi->rxfifo))
- fifo_push(&ser_spi->rxfifo, SPDR);
+ if (!fifo_isfull(&ser_handles[SER_SPI]->rxfifo))
+ fifo_push(&ser_handles[SER_SPI]->rxfifo, SPDR);
/*
* FIXME
else
- ser_spi->status |= SERRF_RXFIFOOVERRUN;
+ ser_handles[SER_SPI]->status |= SERRF_RXFIFOOVERRUN;
*/
/* Send */
- if (!fifo_isempty(&ser_spi->txfifo))
- SPDR = fifo_pop(&ser_spi->txfifo);
+ if (!fifo_isempty(&ser_handles[SER_SPI]->txfifo))
+ SPDR = fifo_pop(&ser_handles[SER_SPI]->txfifo);
else
UARTDescs[SER_SPI].sending = false;