From 8e6b1e394127c3e1635dffd1aa424b4971ef8a4f Mon Sep 17 00:00:00 2001 From: aleph Date: Fri, 15 Oct 2010 19:59:58 +0000 Subject: [PATCH] sam3 port: add serial module. Copied from SAM7 architecture driver and improved to handle SAM3 as well. Mantained SAM7 compatibility, to allow future merge between SAM7 and SAM3 driver trees. git-svn-id: https://src.develer.com/svnoss/bertos/trunk@4436 38d2e660-2303-0410-9eaa-f027e97ec537 --- bertos/cpu/cortex-m3/drv/ser_cm3.h | 2 + bertos/cpu/cortex-m3/drv/ser_sam3.c | 961 ++++++++++++++++++++++++++++ bertos/cpu/cortex-m3/drv/ser_sam3.h | 81 +++ 3 files changed, 1044 insertions(+) create mode 100644 bertos/cpu/cortex-m3/drv/ser_sam3.c create mode 100644 bertos/cpu/cortex-m3/drv/ser_sam3.h diff --git a/bertos/cpu/cortex-m3/drv/ser_cm3.h b/bertos/cpu/cortex-m3/drv/ser_cm3.h index fc9edd42..184846bd 100644 --- a/bertos/cpu/cortex-m3/drv/ser_cm3.h +++ b/bertos/cpu/cortex-m3/drv/ser_cm3.h @@ -41,6 +41,8 @@ #include "ser_lm3s.h" #elif CPU_CM3_STM32 #include "ser_stm32.h" +#elif CPU_CM3_AT91SAM3 + #include "ser_sam3.h" /*#elif Add other Cortex-M3 CPUs here */ #else #error Unknown CPU diff --git a/bertos/cpu/cortex-m3/drv/ser_sam3.c b/bertos/cpu/cortex-m3/drv/ser_sam3.c new file mode 100644 index 00000000..be0f9642 --- /dev/null +++ b/bertos/cpu/cortex-m3/drv/ser_sam3.c @@ -0,0 +1,961 @@ +/** + * \file + * + * + * \brief ARM UART and SPI I/O driver + * + * + * \author Daniele Basile + */ + +#include "hw/hw_ser.h" /* Required for bus macros overrides */ +#include /* CPU_FREQ */ + +#include "cfg/cfg_ser.h" +#include + + +//#include +#include +#include + +#include + +#include +#include + +#include + + +#define SERIRQ_PRIORITY 4 ///< default priority for serial irqs. + +/** + * \name Overridable serial bus hooks + * + * These can be redefined in hw.h to implement + * special bus policies such as half-duplex, 485, etc. + * + * + * \code + * TXBEGIN TXCHAR TXEND TXOFF + * | __________|__________ | | + * | | | | | | | | | + * v v v v v v v v v + * ______ __ __ __ __ __ __ ________________ + * \/ \/ \/ \/ \/ \/ \/ + * ______/\__/\__/\__/\__/\__/\__/ + * + * \endcode + * + * \{ + */ + +#ifndef SER_UART0_BUS_TXINIT + /** + * Default TXINIT macro - invoked in uart0_init() + * + * - Disable GPIO on USART0 tx/rx pins + */ + #if CPU_ARM_AT91 && !CPU_ARM_SAM7S_LARGE && !CPU_ARM_SAM7X + #warning Check USART0 pins! + #endif + #define SER_UART0_BUS_TXINIT do { \ + PIOA_PDR = BV(RXD0) | BV(TXD0); \ + } while (0) +#endif + +#ifndef SER_UART0_BUS_TXBEGIN + /** + * Invoked before starting a transmission + */ + #define SER_UART0_BUS_TXBEGIN +#endif + +#ifndef SER_UART0_BUS_TXCHAR + /** + * Invoked to send one character. + */ + #define SER_UART0_BUS_TXCHAR(c) do { \ + US0_THR = (c); \ + } while (0) +#endif + +#ifndef SER_UART0_BUS_TXEND + /** + * Invoked as soon as the txfifo becomes empty + */ + #define SER_UART0_BUS_TXEND +#endif + +/* End USART0 macros */ + +#if !CPU_CM3_AT91SAM3U + + #ifndef SER_UART1_BUS_TXINIT + /** + * Default TXINIT macro - invoked in uart1_init() + * + * - Disable GPIO on USART1 tx/rx pins + */ + #if CPU_ARM_AT91 + #if !CPU_ARM_SAM7S_LARGE && !CPU_ARM_SAM7X + #warning Check USART1 pins! + #endif + #define SER_UART1_BUS_TXINIT do { \ + PIOA_PDR = BV(RXD1) | BV(TXD1); \ + } while (0) + #elif CPU_CM3_AT91SAM3 + #define SER_UART1_BUS_TXINIT do { \ + PIOB_PDR = BV(RXD1) | BV(TXD1); \ + } while (0) + #else + #error Unknown CPU + #endif + #endif + + #ifndef SER_UART1_BUS_TXBEGIN + /** + * Invoked before starting a transmission + */ + #define SER_UART1_BUS_TXBEGIN + #endif + + #ifndef SER_UART1_BUS_TXCHAR + /** + * Invoked to send one character. + */ + #define SER_UART1_BUS_TXCHAR(c) do { \ + US1_THR = (c); \ + } while (0) + #endif + + #ifndef SER_UART1_BUS_TXEND + /** + * Invoked as soon as the txfifo becomes empty + */ + #define SER_UART1_BUS_TXEND + #endif + +#endif + +/** +* \name Overridable SPI hooks +* +* These can be redefined in hw.h to implement +* special bus policies such as slave select pin handling, etc. +* +* \{ +*/ + +#ifndef SER_SPI0_BUS_TXINIT + /** + * Default TXINIT macro - invoked in spi_init() + * The default is no action. + */ + #define SER_SPI0_BUS_TXINIT do { \ + /* Disable PIO on SPI pins */ \ + PIOA_PDR = BV(SPI0_SPCK) | BV(SPI0_MOSI) | BV(SPI0_MISO); \ + } while (0) +#endif + +#ifndef SER_SPI0_BUS_TXCLOSE + /** + * Invoked after the last character has been transmitted. + * The default is no action. + */ + #define SER_SPI0_BUS_TXCLOSE do { \ + /* Enable PIO on SPI pins */ \ + PIOA_PER = BV(SPI0_SPCK) | BV(SPI0_MOSI) | BV(SPI0_MISO); \ + } while (0) +#endif + +#if CPU_ARM_SAM7X + + #ifndef SER_SPI1_BUS_TXINIT + /** + * Default TXINIT macro - invoked in spi_init() + * The default is no action. + */ + #define SER_SPI1_BUS_TXINIT do { \ + /* Disable PIO on SPI pins */ \ + PIOA_PDR = BV(SPI1_SPCK) | BV(SPI1_MOSI) | BV(SPI1_MISO); \ + /* SPI1 pins are on B peripheral function! */ \ + PIOA_BSR = BV(SPI1_SPCK) | BV(SPI1_MOSI) | BV(SPI1_MISO); \ + } while (0) + #endif + + #ifndef SER_SPI1_BUS_TXCLOSE + /** + * Invoked after the last character has been transmitted. + * The default is no action. + */ + #define SER_SPI1_BUS_TXCLOSE do { \ + /* Enable PIO on SPI pins */ \ + PIOA_PER = BV(SPI1_SPCK) | BV(SPI1_MOSI) | BV(SPI1_MISO); \ + } while (0) + #endif +#endif +/*\}*/ + + +/** + * \name Core dependent interrupt handling macros + * + * Atmel serial hardware is used on different CPU cores, + * i.e. SAM3 and SAM7. The user interface of the serial + * subsystem is identical but core interrupt controllers + * are different. + * + * \{ + */ +#if CPU_ARM_AT91 + +INLINE void sysirq_setHandler(sysirq_t irq, sysirq_handler_t handler) +{ + /* Set the vector. */ + AIC_SVR(irq) = uart0_irq_dispatcher; + + /* Initialize to level/edge sensitive with defined priority. */ +#if CPU_ARM_SAM7X + if (irq == SPI0_ID || irq == SPI1_ID) +#else + if (irq == SPI0_ID) +#endif + AIC_SMR(irq) = (AIC_SMR(irq) & ~AIC_SRCTYPE_MASK) | AIC_SRCTYPE_INT_EDGE_TRIGGERED; + else // USART/UART + AIC_SMR(irq) = (AIC_SMR(irq) & ~AIC_SRCTYPE_MASK) | AIC_SRCTYPE_INT_LEVEL_SENSITIVE; + + /* Enable IRQ */ + AIC_IECR = BV(irq); +} + +INLINE void sysirq_setPriority(sysirq_t irq, int prio) +{ + AIC_SMR(irq) = (AIC_SMR(irq) & ~AIC_PRIOR_MASK) | SERIRQ_PRIORITY; +} + +/** Inform hw that we have served the IRQ */ +#define SER_INT_ACK do { \ + AIC_EOICR = 0; \ +} while (0) + +#elif CPU_CM3_AT91SAM3 + +/** Inform hw that we have served the IRQ */ +#define SER_INT_ACK do { /* nop */ } while (0) + +#else + #error No interrupt handling macros defined for current architecture +#endif + +/*\}*/ + +/* From the high-level serial driver */ +extern struct Serial *ser_handles[SER_CNT]; + +/* TX and RX buffers */ +static unsigned char uart0_txbuffer[CONFIG_UART0_TXBUFSIZE]; +static unsigned char uart0_rxbuffer[CONFIG_UART0_RXBUFSIZE]; +#if !CPU_CM3_AT91SAM3U +static unsigned char uart1_txbuffer[CONFIG_UART1_TXBUFSIZE]; +static unsigned char uart1_rxbuffer[CONFIG_UART1_RXBUFSIZE]; +#endif +static unsigned char spi0_txbuffer[CONFIG_SPI0_TXBUFSIZE]; +static unsigned char spi0_rxbuffer[CONFIG_SPI0_RXBUFSIZE]; +#if CPU_ARM_SAM7X +static unsigned char spi1_txbuffer[CONFIG_SPI1_TXBUFSIZE]; +static unsigned char spi1_rxbuffer[CONFIG_SPI1_RXBUFSIZE]; +#endif + +/** + * Internal hardware state structure + * + * The \a sending variable is true while the transmission + * interrupt is retriggering itself. + * + * For the USARTs the \a sending flag is useful for taking specific + * actions before sending a burst of data, at the start of a trasmission + * but not before every char sent. + * + * For the SPI, this flag is necessary because the SPI sends and receives + * bytes at the same time and the SPI IRQ is unique for send/receive. + * The only way to start transmission is to write data in SPDR (this + * is done by spi_starttx()). We do this *only* if a transfer is + * not already started. + */ +struct ArmSerial +{ + struct SerialHardware hw; + volatile bool sending; +}; + +static ISR_PROTO(uart0_irq_dispatcher); +#if !CPU_CM3_AT91SAM3U +static ISR_PROTO(uart1_irq_dispatcher); +#endif +static ISR_PROTO(spi0_irq_handler); +#if CPU_ARM_SAM7X +static ISR_PROTO(spi1_irq_handler); +#endif +/* + * Callbacks for USART0 + */ +static void uart0_init( + UNUSED_ARG(struct SerialHardware *, _hw), + UNUSED_ARG(struct Serial *, ser)) +{ + US0_IDR = 0xFFFFFFFF; + PMC_PCER = BV(US0_ID); + + /* + * - Reset USART0 + * - Set serial param: mode Normal, 8bit data, 1bit stop, parity none + * - Enable both the receiver and the transmitter + * - Enable only the RX complete interrupt + */ + US0_CR = BV(US_RSTRX) | BV(US_RSTTX); + US0_MR = US_CHMODE_NORMAL | US_CHRL_8 | US_NBSTOP_1 | US_PAR_NO; + US0_CR = BV(US_RXEN) | BV(US_TXEN); + US0_IER = BV(US_RXRDY); + + SER_UART0_BUS_TXINIT; + + sysirq_setPriority(US0_ID, SERIRQ_PRIORITY); + sysirq_setHandler(US0_ID, uart0_irq_dispatcher); + + SER_STROBE_INIT; +} + +static void uart0_cleanup(UNUSED_ARG(struct SerialHardware *, _hw)) +{ + US0_CR = BV(US_RSTRX) | BV(US_RSTTX) | BV(US_RXDIS) | BV(US_TXDIS) | BV(US_RSTSTA); +} + +static void uart0_enabletxirq(struct SerialHardware *_hw) +{ + struct ArmSerial *hw = (struct ArmSerial *)_hw; + + /* + * WARNING: racy code here! The tx interrupt sets hw->sending to false + * when it runs with an empty fifo. The order of statements in the + * if-block matters. + */ + if (!hw->sending) + { + hw->sending = true; + /* + * - Enable the transmitter + * - Enable TX empty interrupt + */ + SER_UART0_BUS_TXBEGIN; + US0_IER = BV(US_TXEMPTY); + } +} + +static void uart0_setbaudrate(UNUSED_ARG(struct SerialHardware *, _hw), unsigned long rate) +{ + /* Compute baud-rate period */ + US0_BRGR = CPU_FREQ / (16 * rate); + //DB(kprintf("uart0_setbaudrate(rate=%lu): period=%d\n", rate, period);) +} + +static void uart0_setparity(UNUSED_ARG(struct SerialHardware *, _hw), int parity) +{ + US0_MR &= ~US_PAR_MASK; + /* Set UART parity */ + switch(parity) + { + case SER_PARITY_NONE: + { + /* Parity none. */ + US0_MR |= US_PAR_NO; + break; + } + case SER_PARITY_EVEN: + { + /* Even parity. */ + US0_MR |= US_PAR_EVEN; + break; + } + case SER_PARITY_ODD: + { + /* Odd parity. */ + US0_MR |= US_PAR_ODD; + break; + } + default: + ASSERT(0); + } +} +/* + * Callbacks for USART1 + */ +static void uart1_init( + UNUSED_ARG(struct SerialHardware *, _hw), + UNUSED_ARG(struct Serial *, ser)) +{ + US1_IDR = 0xFFFFFFFF; + PMC_PCER = BV(US1_ID); + + /* + * - Reset USART1 + * - Set serial param: mode Normal, 8bit data, 1bit stop, parity none + * - Enable both the receiver and the transmitter + * - Enable only the RX complete interrupt + */ + US1_CR = BV(US_RSTRX) | BV(US_RSTTX); + US1_MR = US_CHMODE_NORMAL | US_CHRL_8 | US_NBSTOP_1 | US_PAR_NO; + US1_CR = BV(US_RXEN) | BV(US_TXEN); + US1_IER = BV(US_RXRDY); + + SER_UART1_BUS_TXINIT; + + sysirq_setPriority(US1_ID, SERIRQ_PRIORITY); + sysirq_setHandler(US1_ID, uart1_irq_dispatcher); + + SER_STROBE_INIT; +} + +static void uart1_cleanup(UNUSED_ARG(struct SerialHardware *, _hw)) +{ + US1_CR = BV(US_RSTRX) | BV(US_RSTTX) | BV(US_RXDIS) | BV(US_TXDIS) | BV(US_RSTSTA); +} + +static void uart1_enabletxirq(struct SerialHardware *_hw) +{ + struct ArmSerial *hw = (struct ArmSerial *)_hw; + + /* + * WARNING: racy code here! The tx interrupt sets hw->sending to false + * when it runs with an empty fifo. The order of statements in the + * if-block matters. + */ + if (!hw->sending) + { + hw->sending = true; + /* + * - Enable the transmitter + * - Enable TX empty interrupt + */ + SER_UART1_BUS_TXBEGIN; + US1_IER = BV(US_TXEMPTY); + } +} + +static void uart1_setbaudrate(UNUSED_ARG(struct SerialHardware *, _hw), unsigned long rate) +{ + /* Compute baud-rate period */ + US1_BRGR = CPU_FREQ / (16 * rate); + //DB(kprintf("uart0_setbaudrate(rate=%lu): period=%d\n", rate, period);) +} + +static void uart1_setparity(UNUSED_ARG(struct SerialHardware *, _hw), int parity) +{ + US1_MR &= ~US_PAR_MASK; + /* Set UART parity */ + switch(parity) + { + case SER_PARITY_NONE: + { + /* Parity none. */ + US1_MR |= US_PAR_NO; + break; + } + case SER_PARITY_EVEN: + { + /* Even parity. */ + US1_MR |= US_PAR_EVEN; + break; + } + case SER_PARITY_ODD: + { + /* Odd parity. */ + US1_MR |= US_PAR_ODD; + break; + } + default: + ASSERT(0); + } +} + +/* SPI driver */ +static void spi0_init(UNUSED_ARG(struct SerialHardware *, _hw), UNUSED_ARG(struct Serial *, ser)) +{ + SER_SPI0_BUS_TXINIT; + + /* Reset device */ + SPI0_CR = BV(SPI_SWRST); + + /* + * Set SPI to master mode, fixed peripheral select, chip select directly connected to a peripheral device, + * SPI clock set to MCK, mode fault detection disabled, loopback disable, NPCS0 active, Delay between CS = 0 + */ + SPI0_MR = BV(SPI_MSTR) | BV(SPI_MODFDIS); + + /* + * Set SPI mode. + * At reset clock division factor is set to 0, that is + * *forbidden*. Set SPI clock to minimum to keep it valid. + */ + SPI0_CSR0 = BV(SPI_NCPHA) | (255 << SPI_SCBR_SHIFT); + + /* Disable all irqs */ + SPI0_IDR = 0xFFFFFFFF; + + sysirq_setPriority(SPI0_ID, SERIRQ_PRIORITY); + sysirq_setHandler(SPI0_ID, spi0_irq_handler); + PMC_PCER = BV(SPI0_ID); + + /* Enable interrupt on tx buffer empty */ + SPI0_IER = BV(SPI_TXEMPTY); + + /* Enable SPI */ + SPI0_CR = BV(SPI_SPIEN); + + SER_STROBE_INIT; +} + +static void spi0_cleanup(UNUSED_ARG(struct SerialHardware *, _hw)) +{ + /* Disable SPI */ + SPI0_CR = BV(SPI_SPIDIS); + + /* Disable all irqs */ + SPI0_IDR = 0xFFFFFFFF; + + SER_SPI0_BUS_TXCLOSE; +} + +static void spi0_starttx(struct SerialHardware *_hw) +{ + struct ArmSerial *hw = (struct ArmSerial *)_hw; + + cpu_flags_t flags; + IRQ_SAVE_DISABLE(flags); + + /* Send data only if the SPI is not already transmitting */ + if (!hw->sending && !fifo_isempty(&ser_handles[SER_SPI0]->txfifo)) + { + hw->sending = true; + SPI0_TDR = fifo_pop(&ser_handles[SER_SPI0]->txfifo); + } + + IRQ_RESTORE(flags); +} + +static void spi0_setbaudrate(UNUSED_ARG(struct SerialHardware *, _hw), unsigned long rate) +{ + SPI0_CSR0 &= ~SPI_SCBR; + + ASSERT((uint8_t)DIV_ROUND(CPU_FREQ, rate)); + SPI0_CSR0 |= DIV_ROUND(CPU_FREQ, rate) << SPI_SCBR_SHIFT; +} + +#if CPU_ARM_SAM7X +/* SPI driver */ +static void spi1_init(UNUSED_ARG(struct SerialHardware *, _hw), UNUSED_ARG(struct Serial *, ser)) +{ + SER_SPI1_BUS_TXINIT; + + /* Reset device */ + SPI1_CR = BV(SPI_SWRST); + + /* + * Set SPI to master mode, fixed peripheral select, chip select directly connected to a peripheral device, + * SPI clock set to MCK, mode fault detection disabled, loopback disable, NPCS0 active, Delay between CS = 0 + */ + SPI1_MR = BV(SPI_MSTR) | BV(SPI_MODFDIS); + + /* + * Set SPI mode. + * At reset clock division factor is set to 0, that is + * *forbidden*. Set SPI clock to minimum to keep it valid. + */ + SPI1_CSR0 = BV(SPI_NCPHA) | (255 << SPI_SCBR_SHIFT); + + /* Disable all SPI irqs */ + SPI1_IDR = 0xFFFFFFFF; + + sysirq_setPriority(SPI1_ID, SERIRQ_PRIORITY); + sysirq_setHandler(SPI1_ID, spi1_irq_dispatcher); + PMC_PCER = BV(SPI1_ID); + + /* Enable interrupt on tx buffer empty */ + SPI1_IER = BV(SPI_TXEMPTY); + + /* Enable SPI */ + SPI1_CR = BV(SPI_SPIEN); + + SER_STROBE_INIT; +} + +static void spi1_cleanup(UNUSED_ARG(struct SerialHardware *, _hw)) +{ + /* Disable SPI */ + SPI1_CR = BV(SPI_SPIDIS); + + /* Disable all irqs */ + SPI1_IDR = 0xFFFFFFFF; + + SER_SPI1_BUS_TXCLOSE; +} + +static void spi1_starttx(struct SerialHardware *_hw) +{ + struct ArmSerial *hw = (struct ArmSerial *)_hw; + + cpu_flags_t flags; + IRQ_SAVE_DISABLE(flags); + + /* Send data only if the SPI is not already transmitting */ + if (!hw->sending && !fifo_isempty(&ser_handles[SER_SPI1]->txfifo)) + { + hw->sending = true; + SPI1_TDR = fifo_pop(&ser_handles[SER_SPI1]->txfifo); + } + + IRQ_RESTORE(flags); +} + +static void spi1_setbaudrate(UNUSED_ARG(struct SerialHardware *, _hw), unsigned long rate) +{ + SPI1_CSR0 &= ~SPI_SCBR; + + ASSERT((uint8_t)DIV_ROUND(CPU_FREQ, rate)); + SPI1_CSR0 |= DIV_ROUND(CPU_FREQ, rate) << SPI_SCBR_SHIFT; +} +#endif + +static void spi_setparity(UNUSED_ARG(struct SerialHardware *, _hw), UNUSED_ARG(int, parity)) +{ + // nop +} + + +static bool tx_sending(struct SerialHardware* _hw) +{ + struct ArmSerial *hw = (struct ArmSerial *)_hw; + return hw->sending; +} + +// FIXME: move into compiler.h? Ditch? +#if COMPILER_C99 + #define C99INIT(name,val) .name = val +#elif defined(__GNUC__) + #define C99INIT(name,val) name: val +#else + #warning No designated initializers, double check your code + #define C99INIT(name,val) (val) +#endif + +/* + * High-level interface data structures + */ +static const struct SerialHardwareVT UART0_VT = +{ + C99INIT(init, uart0_init), + C99INIT(cleanup, uart0_cleanup), + C99INIT(setBaudrate, uart0_setbaudrate), + C99INIT(setParity, uart0_setparity), + C99INIT(txStart, uart0_enabletxirq), + C99INIT(txSending, tx_sending), +}; + +static const struct SerialHardwareVT UART1_VT = +{ + C99INIT(init, uart1_init), + C99INIT(cleanup, uart1_cleanup), + C99INIT(setBaudrate, uart1_setbaudrate), + C99INIT(setParity, uart1_setparity), + C99INIT(txStart, uart1_enabletxirq), + C99INIT(txSending, tx_sending), +}; + +static const struct SerialHardwareVT SPI0_VT = +{ + C99INIT(init, spi0_init), + C99INIT(cleanup, spi0_cleanup), + C99INIT(setBaudrate, spi0_setbaudrate), + C99INIT(setParity, spi_setparity), + C99INIT(txStart, spi0_starttx), + C99INIT(txSending, tx_sending), +}; +#if CPU_ARM_SAM7X +static const struct SerialHardwareVT SPI1_VT = +{ + C99INIT(init, spi1_init), + C99INIT(cleanup, spi1_cleanup), + C99INIT(setBaudrate, spi1_setbaudrate), + C99INIT(setParity, spi_setparity), + C99INIT(txStart, spi1_starttx), + C99INIT(txSending, tx_sending), +}; +#endif + +static struct ArmSerial UARTDescs[SER_CNT] = +{ + { + C99INIT(hw, /**/) { + C99INIT(table, &UART0_VT), + C99INIT(txbuffer, uart0_txbuffer), + C99INIT(rxbuffer, uart0_rxbuffer), + C99INIT(txbuffer_size, sizeof(uart0_txbuffer)), + C99INIT(rxbuffer_size, sizeof(uart0_rxbuffer)), + }, + C99INIT(sending, false), + }, + { + C99INIT(hw, /**/) { + C99INIT(table, &UART1_VT), + C99INIT(txbuffer, uart1_txbuffer), + C99INIT(rxbuffer, uart1_rxbuffer), + C99INIT(txbuffer_size, sizeof(uart1_txbuffer)), + C99INIT(rxbuffer_size, sizeof(uart1_rxbuffer)), + }, + C99INIT(sending, false), + }, + + { + C99INIT(hw, /**/) { + C99INIT(table, &SPI0_VT), + C99INIT(txbuffer, spi0_txbuffer), + C99INIT(rxbuffer, spi0_rxbuffer), + C99INIT(txbuffer_size, sizeof(spi0_txbuffer)), + C99INIT(rxbuffer_size, sizeof(spi0_rxbuffer)), + }, + C99INIT(sending, false), + }, + #if CPU_ARM_SAM7X + { + C99INIT(hw, /**/) { + C99INIT(table, &SPI1_VT), + C99INIT(txbuffer, spi1_txbuffer), + C99INIT(rxbuffer, spi1_rxbuffer), + C99INIT(txbuffer_size, sizeof(spi1_txbuffer)), + C99INIT(rxbuffer_size, sizeof(spi1_rxbuffer)), + }, + C99INIT(sending, false), + } + + #endif +}; + +struct SerialHardware *ser_hw_getdesc(int unit) +{ + ASSERT(unit < SER_CNT); + return &UARTDescs[unit].hw; +} + +/** + * Serial 0 TX interrupt handler + */ +INLINE void uart0_irq_tx(void) +{ + SER_STROBE_ON; + + struct FIFOBuffer * const txfifo = &ser_handles[SER_UART0]->txfifo; + + if (fifo_isempty(txfifo)) + { + /* + * - Disable the TX empty interrupts + */ + US0_IDR = BV(US_TXEMPTY); + SER_UART0_BUS_TXEND; + UARTDescs[SER_UART0].sending = false; + } + else + { + char c = fifo_pop(txfifo); + SER_UART0_BUS_TXCHAR(c); + } + + SER_STROBE_OFF; +} + +/** + * Serial 0 RX complete interrupt handler. + */ +INLINE void uart0_irq_rx(void) +{ + SER_STROBE_ON; + + /* Should be read before US_CRS */ + ser_handles[SER_UART0]->status |= US0_CSR & (SERRF_RXSROVERRUN | SERRF_FRAMEERROR); + US0_CR = BV(US_RSTSTA); + + char c = US0_RHR; + struct FIFOBuffer * const rxfifo = &ser_handles[SER_UART0]->rxfifo; + + if (fifo_isfull(rxfifo)) + ser_handles[SER_UART0]->status |= SERRF_RXFIFOOVERRUN; + else + fifo_push(rxfifo, c); + + SER_STROBE_OFF; +} + +/** + * Serial IRQ dispatcher for USART0. + */ +static DECLARE_ISR(uart0_irq_dispatcher) +{ + if (US0_CSR & BV(US_RXRDY)) + uart0_irq_rx(); + + if (US0_CSR & BV(US_TXEMPTY)) + uart0_irq_tx(); + + SER_INT_ACK; +} + +/** + * Serial 1 TX interrupt handler + */ +INLINE void uart1_irq_tx(void) +{ + SER_STROBE_ON; + + struct FIFOBuffer * const txfifo = &ser_handles[SER_UART1]->txfifo; + + if (fifo_isempty(txfifo)) + { + /* + * - Disable the TX empty interrupts + */ + US1_IDR = BV(US_TXEMPTY); + SER_UART1_BUS_TXEND; + UARTDescs[SER_UART1].sending = false; + } + else + { + char c = fifo_pop(txfifo); + SER_UART1_BUS_TXCHAR(c); + } + + SER_STROBE_OFF; +} + +/** + * Serial 1 RX complete interrupt handler. + */ +INLINE void uart1_irq_rx(void) +{ + SER_STROBE_ON; + + /* Should be read before US_CRS */ + ser_handles[SER_UART1]->status |= US1_CSR & (SERRF_RXSROVERRUN | SERRF_FRAMEERROR); + US1_CR = BV(US_RSTSTA); + + char c = US1_RHR; + struct FIFOBuffer * const rxfifo = &ser_handles[SER_UART1]->rxfifo; + + if (fifo_isfull(rxfifo)) + ser_handles[SER_UART1]->status |= SERRF_RXFIFOOVERRUN; + else + fifo_push(rxfifo, c); + + SER_STROBE_OFF; +} + +/** + * Serial IRQ dispatcher for USART1. + */ +static DECLARE_ISR(uart1_irq_dispatcher) +{ + if (US1_CSR & BV(US_RXRDY)) + uart1_irq_rx(); + + if (US1_CSR & BV(US_TXEMPTY)) + uart1_irq_tx(); + + SER_INT_ACK; +} + +/** + * SPI0 interrupt handler + */ +static DECLARE_ISR(spi0_irq_handler) +{ + SER_STROBE_ON; + + char c = SPI0_RDR; + /* Read incoming byte. */ + if (!fifo_isfull(&ser_handles[SER_SPI0]->rxfifo)) + fifo_push(&ser_handles[SER_SPI0]->rxfifo, c); + /* + * FIXME + else + ser_handles[SER_SPI0]->status |= SERRF_RXFIFOOVERRUN; + */ + + /* Send */ + if (!fifo_isempty(&ser_handles[SER_SPI0]->txfifo)) + SPI0_TDR = fifo_pop(&ser_handles[SER_SPI0]->txfifo); + else + UARTDescs[SER_SPI0].sending = false; + + SER_INT_ACK; + + SER_STROBE_OFF; +} + + +#if CPU_ARM_SAM7X +/** + * SPI1 interrupt handler + */ +static DECLARE_ISR(spi1_irq_handler) +{ + SER_STROBE_ON; + + char c = SPI1_RDR; + /* Read incoming byte. */ + if (!fifo_isfull(&ser_handles[SER_SPI1]->rxfifo)) + fifo_push(&ser_handles[SER_SPI1]->rxfifo, c); + /* + * FIXME + else + ser_handles[SER_SPI1]->status |= SERRF_RXFIFOOVERRUN; + */ + + /* Send */ + if (!fifo_isempty(&ser_handles[SER_SPI1]->txfifo)) + SPI1_TDR = fifo_pop(&ser_handles[SER_SPI1]->txfifo); + else + UARTDescs[SER_SPI1].sending = false; + + SER_INT_ACK; + + SER_STROBE_OFF; +} +#endif diff --git a/bertos/cpu/cortex-m3/drv/ser_sam3.h b/bertos/cpu/cortex-m3/drv/ser_sam3.h new file mode 100644 index 00000000..ed294245 --- /dev/null +++ b/bertos/cpu/cortex-m3/drv/ser_sam3.h @@ -0,0 +1,81 @@ +/** + * \file + * + * + * \brief High level serial I/O API + * + * \author Daniele Basile + */ + +#ifndef SER_SAM3_H +#define SER_SAM3_H + +#include /* BV() */ +#include /* uint32_t */ +#include /* CPU_* */ + +/** \name Serial Error/status flags. */ +/*\{*/ +typedef uint32_t serstatus_t; + +/* Software errors */ +#define SERRF_RXFIFOOVERRUN BV(0) /**< Rx FIFO buffer overrun */ +#define SERRF_RXTIMEOUT BV(1) /**< Receive timeout */ +#define SERRF_TXTIMEOUT BV(2) /**< Transmit timeout */ + +/* + * Hardware errors. + * These flags map directly to the ARM USART Channel Status Register (US_CSR). + */ +#define SERRF_RXSROVERRUN BV(5) /**< Rx shift register overrun */ +#define SERRF_FRAMEERROR BV(6) /**< Stop bit missing */ +#define SERRF_PARITYERROR BV(7) /**< Parity error */ +#define SERRF_NOISEERROR 0 /**< Unsupported */ +/*\}*/ + +/** + * \name Serial hw numbers + * + * \{ + */ +enum +{ + SER_UART0, +#if !CPU_CM3_AT91SAM3U + SER_UART1, +#endif + SER_SPI0, + SER_CNT /**< Number of serial ports */ +}; +/*\}*/ + +#endif /* SER_SAM3_H */ -- 2.25.1