From 0375780817109b6ab5cd4f36ccf80650b2fe77d5 Mon Sep 17 00:00:00 2001 From: bernie Date: Sun, 23 May 2004 18:10:11 +0000 Subject: [PATCH] Import drv/ modules. git-svn-id: https://src.develer.com/svnoss/bertos/trunk@4 38d2e660-2303-0410-9eaa-f027e97ec537 --- drv/buzzer.c | 207 +++++++++++++++++++++ drv/buzzer.h | 31 ++++ drv/kdebug.c | 224 ++++++++++++++++++++++ drv/kdebug.h | 89 +++++++++ drv/ser.c | 370 +++++++++++++++++++++++++++++++++++++ drv/ser.h | 196 ++++++++++++++++++++ drv/ser_avr.c | 472 +++++++++++++++++++++++++++++++++++++++++++++++ drv/ser_dsp56k.c | 228 +++++++++++++++++++++++ drv/ser_i196.c | 95 ++++++++++ drv/ser_p.h | 52 ++++++ 10 files changed, 1964 insertions(+) create mode 100755 drv/buzzer.c create mode 100755 drv/buzzer.h create mode 100755 drv/kdebug.c create mode 100755 drv/kdebug.h create mode 100755 drv/ser.c create mode 100755 drv/ser.h create mode 100755 drv/ser_avr.c create mode 100755 drv/ser_dsp56k.c create mode 100755 drv/ser_i196.c create mode 100755 drv/ser_p.h diff --git a/drv/buzzer.c b/drv/buzzer.c new file mode 100755 index 00000000..cb722398 --- /dev/null +++ b/drv/buzzer.c @@ -0,0 +1,207 @@ +/*! + * \file + * + * + * \version $Id$ + * + * \author Bernardo Innocenti + * + * \brief Buzzer driver + */ + +/* + * $Log$ + * Revision 1.1 2004/05/23 18:10:11 bernie + * Import drv/ modules. + * + * Revision 1.5 2004/04/04 17:44:51 aleph + * Move event.h from mware to kern dir + * + * Revision 1.4 2004/03/24 15:03:45 bernie + * Use explicit include paths; clean Doxygen comments + * + * Revision 1.3 2004/03/03 18:27:44 bernie + * Fixed race conds with IRQ when fiddling with I/O ports + * + * Revision 1.2 2003/12/18 18:15:24 aleph + * Use new IRQ disable/enable reentrant macros + * + * Revision 1.1 2003/12/13 23:53:37 aleph + * Add buzzer driver + * + */ + +#include "hw.h" +#include "kdebug.h" +#include "timer.h" +#include "buzzer.h" +#include + + +#if (ARCH & ARCH_EMUL) + + int Emul_IsBuzzerOn(void); + void Emul_BuzzerOn(void); + void Emul_BuzzerOff(void); + void Emul_BuzzerInit(void); + +# define IS_BUZZER_ON (Emul_IsBuzzerOn()) +# define BUZZER_ON (Emul_BuzzerOn()) +# define BUZZER_OFF (Emul_BuzzerOff()) +# define BUZZER_INIT (Emul_BuzzerInit()) + +#elif defined(__AVR__) + +# define IS_BUZZER_ON (PORTG & BV(PORTG0)) + + /** + * Buzzer manipulation macros + * + * \note Some PORTG functions are being used from + * interrupt code, so we must be careful to + * avoid race conditions. + */ +# define BUZZER_ON \ + do { \ + cpuflags_t _flags; \ + DISABLE_IRQSAVE(_flags); \ + PORTG |= BV(PORTG0); \ + ENABLE_IRQRESTORE(_flags); \ + } while (0) + +# define BUZZER_OFF \ + do { \ + cpuflags_t _flags; \ + DISABLE_IRQSAVE(_flags); \ + PORTG &= ~BV(PORTG0); \ + ENABLE_IRQRESTORE(_flags); \ + } while (0) + +# define BUZZER_INIT \ + do { \ + cpuflags_t _flags; \ + DISABLE_IRQSAVE(_flags); \ + PORTG &= ~BV(PORTG0); \ + DDRG |= BV(PORTG0); \ + ENABLE_IRQRESTORE(_flags); \ + } while (0) + +#elif defined(__IAR_SYSTEMS_ICC) || defined(__IAR_SYSTEMS_ICC__) /* 80C196 */ + +# define IS_BUZZER_ON (cpld->Buzzer & 1) +# define BUZZER_ON (cpld->Buzzer = 1) +# define BUZZER_OFF (cpld->Buzzer = 0) +# define BUZZER_INIT (cpld->Buzzer = 0) + +#endif /* ARCH, __AVR__, __IAR_SYSTEM_ICC */ + + +/* Local vars */ +static Timer *buz_timer; +static bool buz_timer_running; +static time_t buz_repeat_interval; +static time_t buz_repeat_duration; + + +/*! + * Turn off buzzer, called by software timer + */ +static void buz_softint(void) +{ + if (IS_BUZZER_ON) + { + BUZZER_OFF; + if (buz_repeat_interval) + { + /* Wait for interval time */ + buz_timer->delay = buz_repeat_interval; + timer_add(buz_timer); + } + else + buz_timer_running = false; + } + else if (buz_repeat_interval) + { + /* Wait for beep time */ + BUZZER_ON; + buz_timer->delay = buz_repeat_duration; + timer_add(buz_timer); + } + else + buz_timer_running = false; +} + + +/*! + * Beep for the specified ms time + */ +void buz_beep(time_t time) +{ + cpuflags_t flags; + + /* Remove the software interrupt if it was already queued */ + DISABLE_IRQSAVE(flags); + if (buz_timer_running) + timer_abort(buz_timer); + + /* Turn on buzzer */ + BUZZER_ON; + + /* Add software interrupt to turn the buzzer off later */ + buz_timer_running = true; + buz_timer->delay = time; + timer_add(buz_timer); + + ENABLE_IRQRESTORE(flags); +} + + +/*! + * Start buzzer repetition + */ +void buz_repeat_start(time_t duration, time_t interval) +{ + buz_repeat_interval = interval; + buz_repeat_duration = duration; + buz_beep(duration); +} + + +/*! + * Stop buzzer repetition + */ +void buz_repeat_stop(void) +{ + cpuflags_t flags; + DISABLE_IRQSAVE(flags); + + /* Remove the software interrupt if it was already queued */ + if (buz_timer_running) + { + timer_abort(buz_timer); + buz_timer_running = false; + } + + buz_repeat_interval = 0; + BUZZER_OFF; + + ENABLE_IRQRESTORE(flags); +} + + +/*! + * Initialize buzzer + */ +void buz_init(void) +{ + BUZZER_INIT; + + /* Inizializza software interrupt */ + buz_timer = timer_new(); + ASSERT(buz_timer != NULL); + INITEVENT_INT(&buz_timer->expire, (Hook)buz_softint, 0); +} diff --git a/drv/buzzer.h b/drv/buzzer.h new file mode 100755 index 00000000..646c467b --- /dev/null +++ b/drv/buzzer.h @@ -0,0 +1,31 @@ +/*! + * \file + * Copyright (C) 1999,2003 Bernardo Innocenti + * Copyright (C) 2003 Develer S.r.l. (http://www.develer.com/) + * All Rights Reserved. + * + * \version $Id$ + * + * \author Bernardo Innocenti + * + * \brief Buzzer driver + */ + +/* + * $Log$ + * Revision 1.1 2004/05/23 18:10:11 bernie + * Import drv/ modules. + * + * Revision 1.1 2003/12/13 23:53:37 aleph + * Add buzzer driver + * + */ +#ifndef BUZZER_H +#define BUZZER_H + +extern void buz_init(void); +extern void buz_beep(time_t time); +extern void buz_repeat_start(time_t duration, time_t interval); +extern void buz_repeat_stop(void); + +#endif /* BUZZER_H */ diff --git a/drv/kdebug.c b/drv/kdebug.c new file mode 100755 index 00000000..44d211ff --- /dev/null +++ b/drv/kdebug.c @@ -0,0 +1,224 @@ +/* + * \file + * + * + * \brief General pourpose debug functions. + * + * \version $Id$ + * + * \author Bernardo Innocenti + * \author Stefano Fedrigo + */ + +/* + */ + +#include "kdebug.h" +#include "hw.h" +#include "config.h" + +#include /* for _formatted_write() */ + +#ifdef _DEBUG + +#if defined(_EMUL) + #include + #define KDBG_WAIT_READY() do {/*nop*/} while(0) + #define KDBG_WRITE_CHAR(c) putchar((c)) + #define KDBG_MASK_IRQ(old) do {/*nop*/} while(0) + #define KDBG_RESTORE_IRQ() do {/*nop*/} while(0) +#elif defined(__I196__) + #include "Util196.h" + #define KDBG_WAIT_READY() do {} while (!(SP_STAT & (SPSF_TX_EMPTY | SPSF_TX_INT))) + #define KDBG_WRITE_CHAR(c) do { SBUF = (c); } while(0) + #define KDBG_MASK_IRQ(old) \ + do { \ + (old) = INT_MASK1 & INT1F_TI; \ + INT_MASK1 &= ~INT1F_TI; \ + } while(0) + #define KDBG_RESTORE_IRQ(old) do { INT_MASK1 |= (old); } +#elif defined(__AVR__) + #include + #if CONFIG_KDEBUG_PORT == 0 + #ifndef __AVR_ATmega103__ + #define UCR UCSR0B + #define UDR UDR0 + #define USR UCSR0A + #endif + #define KDBG_WAIT_READY() do { loop_until_bit_is_set(USR, UDRE); } while(0) + #define KDBG_WRITE_CHAR(c) do { UCR |= BV(TXEN); UDR = (c); } while(0) + #define KDBG_MASK_IRQ(old) do { (old) = UCR & BV(TXCIE); cbi(UCR, TXCIE); } while(0) + #define KDBG_RESTORE_IRQ(old) do { UCR |= (old); } while(0) + #elif CONFIG_KDEBUG_PORT == 1 + #define KDBG_WAIT_READY() do { loop_until_bit_is_set(UCSR1A, UDRE); } while(0) + #define KDBG_WRITE_CHAR(c) do { UCSR1B |= BV(TXEN); UDR1 = (c); } while(0) + #define KDBG_MASK_IRQ(old) do { (old) = UCSR1B & BV(TXCIE); cbi(UCSR1B, TXCIE); } while(0) + #define KDBG_RESTORE_IRQ(old) do { UCSR1B |= (old); } while(0) + #else + #error CONFIG_KDEBUG_PORT should be either 0 or 1 + #endif +#else + #error Unknown architecture +#endif + + +void kdbg_init(void) +{ +#if defined(__I196__) + + /* Set serial port for 19200bps 8N1 */ + INT_MASK1 &= ~(INT1F_TI | INT1F_RI); + SP_CON = SPCF_RECEIVE_ENABLE | SPCF_MODE1; + ioc1_img |= IOC1F_TXD_SEL | IOC1F_EXTINT_SRC; + IOC1 = ioc1_img; + BAUD_RATE = 0x33; + BAUD_RATE = 0x80; + +#elif defined(__AVR__) + + /* Compute the baud rate */ + uint16_t period = (((CLOCK_FREQ / 16UL) + (CONFIG_KDEBUG_BAUDRATE / 2)) / CONFIG_KDEBUG_BAUDRATE) - 1; + + #ifdef __AVR_ATmega64__ + #if CONFIG_KDEBUG_PORT == 0 + + /* Set the baud rate */ + UBRR0H = (uint8_t)(period>>8); + UBRR0L = (uint8_t)period; + + #elif CONFIG_KDEBUG_PORT == 1 + + UBRR1H = (uint8_t)(period>>8); + UBRR1L = (uint8_t)period; + + #else + #error CONFIG_KDEBUG_PORT should be either 0 or 1 + #endif + #elif defined (__AVR_ATmega103__) + + /* Set the baud rate */ + UBRR = (uint8_t)period; + + #else + #error Unknown arch + #endif + +#endif /* !__I196__ && !__AVR__ */ + + kputs("\n\n*** DBG START ***\n"); +} + + +/*! + * Output one character to the debug console + */ +static void kputchar(char c, UNUSED(void *unused)) +{ + /* Poll while serial buffer is still busy */ + KDBG_WAIT_READY(); + + /* Send '\n' as '\r\n' for dumb terminals */ + if (c == '\n') + { + KDBG_WRITE_CHAR('\r'); + KDBG_WAIT_READY(); + } + + KDBG_WRITE_CHAR(c); +} + + +void PGM_FUNC(kprintf)(const char * PGM_ATTR fmt, ...) +{ + va_list ap; + + /* Mask serial TX intr */ + unsigned char irqsave; + KDBG_MASK_IRQ(irqsave); + + va_start(ap, fmt); + PGM_FUNC(_formatted_write)(fmt, kputchar, 0, ap); + va_end(ap); + + /* Restore serial TX intr */ + KDBG_RESTORE_IRQ(irqsave); +} + + +void PGM_FUNC(kputs)(const char * PGM_ATTR str) +{ + char c; + + /* Mask serial TX intr */ + unsigned char irqsave; + KDBG_MASK_IRQ(irqsave); + + while ((c = PGM_READ_CHAR(str++))) + kputchar(c, 0); + + KDBG_RESTORE_IRQ(irqsave); +} + + +int PGM_FUNC(__assert)(const char * PGM_ATTR cond, const char *file, int line) +{ + PGM_FUNC(kputs)(file); + PGM_FUNC(kprintf)(PSTR(":%d: Assertion failed: \""), line); + PGM_FUNC(kputs)(cond); + PGM_FUNC(kputs)(PSTR("\"\n")); + return 1; +} + + +int PGM_FUNC(__invalid_ptr)(void *value, const char * PGM_ATTR name, const char * PGM_ATTR file, int line) +{ + PGM_FUNC(kputs)(file); + PGM_FUNC(kprintf)(PSTR(":%d: Invalid pointer: "), line); + PGM_FUNC(kputs)(name); + PGM_FUNC(kprintf)(PSTR(" = 0x%x\n"), value); + return 1; +} + + +void __init_wall(long *wall, int size) +{ + while(size--) + *wall++ = WALL_VALUE; +} + + +int __check_wall(long *wall, int size, const char *name, const char *file, int line) +{ + int i, fail = 0; + + for (i = 0; i < size; i++) + { + if (wall[i] != WALL_VALUE) + { + kprintf("%s:%d: Wall broken: %s[%d] (0x%p) = %ld\n", + file, line, name, i, wall + i, wall[i]); + fail = 1; + } + } + + return fail; +} + + +/*! + * Dump binary data in hex + */ +void kdump(const void *_buf, size_t len) +{ + const unsigned char *buf = (const unsigned char *)_buf; + + while (len--) + kprintf("%02X", *buf++); + kputs("\n"); +} + +#endif /* _DEBUG */ diff --git a/drv/kdebug.h b/drv/kdebug.h new file mode 100755 index 00000000..3777d3ef --- /dev/null +++ b/drv/kdebug.h @@ -0,0 +1,89 @@ +/* + * \file + * + * + * \brief Definition of handy debug macros. These macros are no-ops + * when the preprocessor symbol _DEBUG isn't defined. + * + * \version $Id$ + * + * \author Bernardo Innocenti + */ + +/* + * $Log$ + * Revision 1.1 2004/05/23 18:10:11 bernie + * Import drv/ modules. + * + */ +#ifndef KDEBUG_H +#define KDEBUG_H + +#include "compiler.h" + +/* Avoid name clashes with Win32 headers */ +#ifdef ASSERT +#undef ASSERT +#endif + +#if defined(_DEBUG) + void kdbg_init(void); + void kdump(const void *buf, size_t len); + + #ifdef __AVR__ + + #include + void kputs_P(const char *PROGMEM str); + void kprintf_P(const char *PROGMEM fmt, ...) FORMAT(__printf__, 1, 2); + int __assert_P(const char *PROGMEM cond, const char *PROGMEM file, int line); + int __invalid_ptr_P(void *p, const char *PROGMEM name, const char *PROGMEM file, int line); + #define kputs(str) kputs_P(PSTR(str)) + #define kprintf(fmt, ...) kprintf_P(PSTR(fmt) ,## __VA_ARGS__) + #define __assert(cond, file, line) __assert_P(PSTR(cond), PSTR(file), (line)) + #define __invalid_ptr(p, name, file, line) __invalid_ptr_P((p), PSTR(name), PSTR(file), (line)) + + #else + void kputs(const char *str); + void kprintf(const char * fmt, ...) FORMAT(__printf__, 1, 2); + int __assert(const char *cond, const char *file, int line); + int __invalid_ptr(void *p, const char *name, const char *file, int line); + #endif + + void __init_wall(long *wall, int size); + int __check_wall(long *wall, int size, const char *name, const char *file, int line); + + #define THIS_FILE __FILE__ + #define ASSERT(x) ((x) ? 0 : __assert(#x, THIS_FILE, __LINE__)) + #define ASSERT_VALID_PTR(p) ((p >= 0x200) ? 0 : __invalid_ptr(p, #p, THIS_FILE, __LINE__)) + #define ASSERT_VALID_PTR_OR_NULL(p) (((p == NULL) || (p >= 0x200)) ? 0 : __invalid_ptr(p, #p, THIS_FILE, __LINE__)) + #define TRACE kprintf("%s()\n", __FUNCTION__) + #define TRACEMSG(msg,...) kprintf("%s(): " msg "\n", __FUNCTION__, ## __VA_ARGS__) + #define DB(x) x + + /* Walls to detect data corruption */ + #define WALL_SIZE 8 + #define WALL_VALUE (long)0xABADCAFEL + #define DECLARE_WALL(name,size) static long name[(size) / sizeof(long)]; + #define INIT_WALL(name) __init_wall((name), countof(name)) + #define CHECK_WALL(name) __check_wall((name), countof(name), #name, THIS_FILE, __LINE__) + +#else /* !_DEBUG */ + + #define ASSERT(x) + #define ASSERT_VALID_PTR(p) + #define ASSERT_VALID_PTR_OR_NULL(p) + #define TRACE do {} while(0) + #define TRACEMSG(x,...) do {} while(0) + #define DB(x) + + #define DECLARE_WALL(name, size) + #define INIT_WALL(name) + #define CHECK_WALL(name) + +#endif /* !_DEBUG */ + +#endif /* KDEBUG_H */ diff --git a/drv/ser.c b/drv/ser.c new file mode 100755 index 00000000..96c93df5 --- /dev/null +++ b/drv/ser.c @@ -0,0 +1,370 @@ +/*! + * \file + * + * + * \brief Buffered serial I/O driver + * + * The serial rx interrupt buffers incoming data in a software FIFO + * to decouple the higher level protocols from the line speed. + * Outgoing data is buffered as well for better performance. + * This driver is not optimized for best performance, but it + * has proved to be fast enough to handle transfer rates up to + * 38400bps on a 16MHz 80196. + * + * MODULE CONFIGURATION + * \li \c CONFIG_SER_HWHANDSHAKE define this preprocessor symbol to enable + * support for RTS/CTS handshake. Support is incomplete/untested + * for 80196. + * \li \c CONFIG_SER_TXTIMEOUT - Enable software serial transmission timeouts + * + * + * \version $Id$ + * \author Bernardo Innocenti + */ + +/* + * $Log$ + * Revision 1.1 2004/05/23 18:10:11 bernie + * Import drv/ modules. + * + * Revision 1.5 2004/05/14 12:47:26 rasky + * Importato nuovo supporto seriale per AVR da Stefano + * + * Revision 1.4 2004/04/29 16:40:23 rasky + * Durante i busy loop della seriale, chiama la proc_switch() per cambiare processo + * + * Revision 1.3 2004/04/21 17:38:24 rasky + * New application + * + * Revision 1.16 2004/04/03 18:30:49 aleph + * Move timeout defines in config, private define in .c + * + * Revision 1.15 2004/03/29 17:01:02 aleph + * Add function to set serial parity, fix it when ser_open is used + * + * Revision 1.14 2004/03/29 16:19:33 aleph + * Add ser_cleanup function; Various code improvements + * + * Revision 1.13 2004/03/24 15:48:53 bernie + * Remove Copyright messages from Doxygen output + */ + +#include +#include +#include "ser.h" +#include "ser_p.h" +#include "hw.h" + +#ifdef CONFIG_KERNEL + #include +#endif +#if defined(CONFIG_SER_TXTIMEOUT) || defined(CONFIG_SER_RXTIMEOUT) + #include +#endif + + +/* Serial configuration parameters */ +#define SER_CTSDELAY 70 /*!< CTS line retry interval (ms) */ +#define SER_TXPOLLDELAY 2 /*!< Transmit buffer full retry interval (ms) */ +#define SER_RXPOLLDELAY 2 /*!< Receive buffer empty retry interval (ms) */ + + +struct Serial ser_handles[SER_CNT]; + + +/*! + * Inserisce il carattere c nel buffer di trasmissione. + * Questa funzione mette il processo chiamante in attesa + * quando il buffer e' pieno. + * + * \return EOF in caso di errore o timeout, altrimenti + * il carattere inviato. + */ +int ser_putchar(int c, struct Serial *port) +{ + if (fifo_isfull_locked(&port->txfifo)) + { +#ifdef CONFIG_SER_TXTIMEOUT + time_t start_time = timer_gettick(); +#endif + + /* Attende finche' il buffer e' pieno... */ + do + { +#ifdef CONFIG_KERN_SCHED + /* Give up timeslice to other processes. */ + proc_switch(); +#endif +#ifdef CONFIG_SER_TXTIMEOUT + if (timer_gettick() - start_time >= port->txtimeout) + { + port->status |= SERRF_TXTIMEOUT; + return EOF; + } +#endif /* CONFIG_SER_TXTIMEOUT */ + } + while (fifo_isfull_locked(&port->txfifo)); + } + + fifo_push(&port->txfifo, (unsigned char)c); + + /* (re)trigger tx interrupt */ + port->hw->table->enabletxirq(port->hw); + + /* Avoid returning signed estended char */ + return (int)((unsigned char)c); +} + + +/*! + * Preleva un carattere dal buffer di ricezione. + * Questa funzione mette il processo chiamante in attesa + * quando il buffer e' vuoto. L'attesa ha un timeout + * di ser_rxtimeout millisecondi. + * + * \return EOF in caso di errore o timeout, altrimenti + * il carattere ricevuto. + */ +int ser_getchar(struct Serial *port) +{ + int result; + + if (fifo_isempty_locked(&port->rxfifo)) + { +#ifdef CONFIG_SER_RXTIMEOUT + time_t start_time = timer_gettick(); +#endif + /* Wait while buffer is empty */ + do + { +#ifdef CONFIG_KERN_SCHED + /* Give up timeslice to other processes. */ + proc_switch(); +#endif +#ifdef CONFIG_SER_RXTIMEOUT + if (timer_gettick() - start_time >= port->rxtimeout) + { + port->status |= SERRF_RXTIMEOUT; + return EOF; + } +#endif /* CONFIG_SER_RXTIMEOUT */ + } + while (fifo_isempty_locked(&port->rxfifo)); + } + + /* + * Get a byte from the FIFO (avoiding sign-extension), + * re-enable RTS, then return result. + */ + result = (int)(unsigned char)fifo_pop(&port->rxfifo); + return port->status ? EOF : result; +} + + +/*! + * Preleva un carattere dal buffer di ricezione. + * Se il buffer e' vuoto, ser_getchar_nowait() ritorna + * immediatamente EOF. + */ +int ser_getchar_nowait(struct Serial *port) +{ + if (fifo_isempty_locked(&port->rxfifo)) + return EOF; + + /* NOTE: the double cast prevents unwanted sign extension */ + return (int)(unsigned char)fifo_pop(&port->rxfifo); +} + + +/*! + * Read a line long at most as size and puts it + * in buf. + * \return number of chars read or EOF in case + * of error. + */ +int ser_gets(struct Serial *port, char *buf, int size) +{ + return ser_gets_echo(port, buf, size, false); +} + + +/*! + * Read a line long at most as size and puts it + * in buf, with optional echo. + * \return number of chars read or EOF in case + * of error. + */ +int ser_gets_echo(struct Serial *port, char *buf, int size, bool echo) +{ + int i = 0; + int c; + + for (;;) + { + if ((c = ser_getchar(port)) == EOF) + return -1; + /* FIXME */ + if (c == '\r' || c == '\n' || i >= size-1) + { + buf[i] = '\0'; + if (echo) + ser_print(port, "\r\n"); + break; + } + buf[i++] = c; + if (echo) + ser_putchar(c, port); + } + + return i; +} + + +/*! + * Read at most size bytes and puts them + * in buf. + * \return number of bytes read or EOF in case + * of error. + */ +int ser_read(struct Serial *port, char *buf, size_t size) +{ + size_t i = 0; + int c; + + while (i < size) + { + if ((c = ser_getchar(port)) == EOF) + return EOF; + buf[i++] = c; + } + + return i; +} + + +/*! + * Write a string to serial. + * \return 0 if OK, EOF in case of error. + */ +int ser_print(struct Serial *port, const char *s) +{ + while (*s) + { + if (ser_putchar(*s++, port) == EOF) + return EOF; + } + return 0; +} + + +/*! + * \brief Write a buffer to serial. + * + * \return 0 if OK, EOF in case of error. + */ +int ser_write(struct Serial *port, const void *buf, size_t len) +{ + while (len--) + { + if (ser_putchar(*((const char *)buf)++, port) == EOF) + return EOF; + } + return 0; +} + + +/*! + * Formatted write + */ +int ser_printf(struct Serial *port, const char *format, ...) +{ + va_list ap; + int len; + + ser_setstatus(port, 0); + va_start(ap, format); + len = _formatted_write(format, (void (*)(char, void *))ser_putchar, port, ap); + va_end(ap); + + return len; +} + +#if defined(CONFIG_SER_RXTIMEOUT) || defined(CONFIG_SER_TXTIMEOUT) +void ser_settimeouts(struct Serial *port, time_t rxtimeout, time_t txtimeout) +{ + port->rxtimeout = rxtimeout; + port->txtimeout = txtimeout; +} +#endif /* defined(CONFIG_SER_RXTIMEOUT) || defined(CONFIG_SER_TXTIMEOUT) */ + + +void ser_setbaudrate(struct Serial *port, unsigned long rate) +{ + port->hw->table->setbaudrate(port->hw, rate); +} + + +void ser_setparity(struct Serial *port, int parity) +{ + port->hw->table->setparity(port->hw, parity); +} + + +/*! + * Flush both the RX and TX buffers. + */ +void ser_purge(struct Serial *ser) +{ + fifo_flush(&ser->rxfifo); + fifo_flush(&ser->txfifo); +} + + +/*! + * Initialize serial + */ +struct Serial *ser_open(unsigned int unit) +{ + struct Serial *port; + + ASSERT(unit < countof(ser_handles)); + + port = &ser_handles[unit]; + + ASSERT(!port->is_open); + + port->unit = unit; + port->is_open = true; + + /* Initialize circular buffer */ + fifo_init(&port->rxfifo, port->rxbuffer, sizeof(port->rxbuffer)); + fifo_init(&port->txfifo, port->txbuffer, sizeof(port->txbuffer)); + + port->hw = ser_hw_getdesc(unit); + port->hw->table->init(port->hw, port); + + /* Set default values */ +#if defined(CONFIG_SER_RXTIMEOUT) || defined(CONFIG_SER_TXTIMEOUT) + ser_settimeouts(port, CONFIG_SER_RXTIMEOUT, CONFIG_SER_TXTIMEOUT); +#endif + ser_setbaudrate(port, CONFIG_SER_DEFBAUDRATE); + + return port; +} + + +/*! + * Clean up serial port, disabling the associated hardware. + */ +void ser_close(struct Serial *port) +{ + ASSERT(port->is_open); + + port->is_open = false; + port->hw->table->cleanup(port->hw); + port->hw = NULL; +} diff --git a/drv/ser.h b/drv/ser.h new file mode 100755 index 00000000..ad643e35 --- /dev/null +++ b/drv/ser.h @@ -0,0 +1,196 @@ +/*! + * \file + * + * + * \brief High level serial I/O API + * + * \version $Id$ + * \author Bernardo Innocenti + */ + +/* + * $Log$ + * Revision 1.1 2004/05/23 18:10:11 bernie + * Import drv/ modules. + * + * Revision 1.2 2004/04/21 17:38:24 rasky + * New application + * + * Revision 1.16 2004/04/03 18:30:49 aleph + * Move timeout defines in config, private define in .c + * + * Revision 1.15 2004/03/29 17:01:02 aleph + * Add function to set serial parity, fix it when ser_open is used + * + * Revision 1.14 2004/03/29 16:19:33 aleph + * Add ser_cleanup function; Various code improvements + * + * Revision 1.13 2004/03/24 15:22:27 aleph + * Removed subdirs -I, fix header inclusion, move config.h in board_kf, kctrl + * + * Revision 1.12 2004/03/17 17:30:30 bernie + * Add GCC format checks to printf()-like functions. + * + * Revision 1.11 2004/03/16 23:06:42 aleph + * Doc fix + * + * Revision 1.10 2004/03/12 18:46:53 bernie + * ser_read(): New function. + * + * Revision 1.9 2004/03/11 18:11:51 bernie + * Cosmetic fixes + */ +#ifndef SER_H +#define SER_H + +#include "compiler.h" +#include +#include "config.h" + +/*! + * \name Serial Error/status flags + * + * Some of these flags map directly to the flags + * in AVR UART Status Register(USR). + * \todo flags of DSP56k aren't mapped to these flags. Luckily + * these flags doesn't collide with the DSP56k ones, + * which are from 0x0100 to 0x8000 + */ +/*\{*/ +#if defined(__AVR__) + /* Software errors */ + #define SERRF_RXFIFOOVERRUN BV(0) /*!< Rx FIFO buffer overrun */ + #define SERRF_RXTIMEOUT BV(5) /*!< Receive timeout */ + #define SERRF_TXTIMEOUT BV(6) /*!< Transmit timeout */ + + /* Hardware errors */ + #define SERRF_RXSROVERRUN BV(3) /*!< Rx shift register overrun */ + #define SERRF_FRAMEERROR BV(4) /*!< Stop bit missing */ + #define SERRF_PARITYERROR BV(7) /*!< Parity error */ +#elif defined(__m56800__) + /* 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 */ + #define SERRF_PARITYERROR BV(8) /*!< Parity error */ + #define SERRF_FRAMEERROR BV(9) /*!< Stop bit missing */ + #define SERRF_NOISEERROR BV(10) /*!< Noise error */ + #define SERRF_RXSROVERRUN BV(11) /*!< Rx shift register overrun */ +#else + #error unknown architecture +#endif +/*\}*/ + +/*! \name Parity settings for ser_setparity() */ +/*\{*/ +#define SER_PARITY_NONE 0 +#define SER_PARITY_EVEN 2 +#define SER_PARITY_ODD 3 +/*\}*/ + +/*! + * \name Serial hw numbers + * + * \{ + */ +enum +{ + #if defined(__AVR_ATmega64__) + SER_UART0, + SER_UART1, + SER_SPI, + + #elif defined(__AVR_ATmega103__) + SER_UART0, + SER_SPI, + + #elif defined (__m56800__) + SER_UART0, + SER_UART1, + + #else + #error unknown architecture + #endif + + SER_CNT /**< Number of serial ports */ +}; +/* @} */ + + +struct SerialHardware; + +/*! Human-readable serial error descriptions */ +extern const char * const serial_errors[8]; + +/*! Serial handle structure */ +struct Serial +{ + /*! Physical port number */ + unsigned int unit; + + bool is_open; + + /*! + * \name FIFO transmit and receive buffers. + * + * Declared volatile because handled asinchronously by interrupts. + * + * \{ + */ + volatile FIFOBuffer txfifo; + volatile FIFOBuffer rxfifo; + unsigned char txbuffer[CONFIG_SER_TXBUFSIZE]; + unsigned char rxbuffer[CONFIG_SER_RXBUFSIZE]; + /* \} */ + +#ifdef CONFIG_SER_RXTIMEOUT + time_t rxtimeout; +#endif +#ifdef CONFIG_SER_TXTIMEOUT + time_t txtimeout; +#endif + + /*! Holds the flags defined above. Will be 0 when no errors have occurred. */ + REGISTER uint16_t status; + + /*! Low-level interface to hardware. */ + struct SerialHardware* hw; + +}; + + +/* Function prototypes */ +extern int ser_putchar(int c, struct Serial *port); +extern int ser_getchar(struct Serial *port); +extern int ser_getchar_nowait(struct Serial *port); +extern int ser_write(struct Serial *port, const void *buf, size_t len); +extern int ser_read(struct Serial *port, char *buf, size_t size); +extern int ser_print(struct Serial *port, const char *s); +extern int ser_gets(struct Serial *port, char *buf, int size); +extern int ser_gets_echo(struct Serial *port, char *buf, int size, bool echo); +extern int ser_printf(struct Serial *port, const char *format, ...) FORMAT(__printf__, 2, 3); +extern void ser_setbaudrate(struct Serial *port, unsigned long rate); +extern void ser_setparity(struct Serial *port, int parity); +extern void ser_purge(struct Serial *port); +extern struct Serial *ser_open(unsigned int unit); +extern void ser_close(struct Serial *port); +#if defined(CONFIG_SER_RXTIMEOUT) || defined(CONFIG_SER_TXTIMEOUT) + extern void ser_settimeouts(struct Serial *port, time_t rxtimeout, time_t txtimeout); +#endif + +/** + * @name Additional functions implemented as macros + * + * @{ + */ +#define ser_getstatus(h) ((h)->status) +#define ser_setstatus(h, x) ((h)->status = (x)) +/* @} */ + +#endif /* SER_H */ diff --git a/drv/ser_avr.c b/drv/ser_avr.c new file mode 100755 index 00000000..c3bab672 --- /dev/null +++ b/drv/ser_avr.c @@ -0,0 +1,472 @@ +/** + * \file + * + * + * \version $Id$ + * + * \author Bernardo Innocenti + * + * \brief AVR UART and SPI I/O driver + */ + +/* + * $Log$ + * Revision 1.1 2004/05/23 18:10:11 bernie + * Import drv/ modules. + * + * Revision 1.30 2004/05/19 17:06:11 bernie + * Serial TX fill mode + * + * Revision 1.29 2004/05/16 19:16:46 aleph + * Serial always transmitting, first try + * + * Revision 1.28 2004/05/14 12:09:00 aleph + * Fix TX port pull-ups + * + * Revision 1.27 2004/05/08 13:56:02 aleph + * Adapt avr serial driver to new design + * + * Revision 1.25 2004/04/28 13:42:16 aleph + * Serial port fixes + * + * Revision 1.24 2004/04/08 14:17:27 bernie + * Change serial to disable TX when not sending data + * + * Revision 1.23 2004/04/03 20:39:41 aleph + * Remove strobe + * + * Revision 1.22 2004/03/29 17:01:02 aleph + * Add function to set serial parity, fix it when ser_open is used + */ + +#include "ser.h" +#include "ser_p.h" +#include "kdebug.h" +#include "config.h" +#include "hw.h" +#include + +extern struct Serial ser_handles[SER_CNT]; + +struct AvrSerial +{ + struct SerialHardware hw; + struct Serial* serial; +}; + + +/* Hardware handshake */ +#define RTS_ON +#define RTS_OFF +#define IS_CTS_ON true +#define IS_CTS_OFF false + + +/* SPI port and pin configuration */ +#define SPI_PORT PORTB +#define SPI_DDR DDRB +#define SPI_SCK_BIT PORTB1 +#define SPI_MOSI_BIT PORTB2 +#define SPI_MISO_BIT PORTB3 + + +#ifdef __AVR_ATmega103__ + /* Macro for ATmega103 compatibility */ + #define UCSR0B UCR + #define UDR0 UDR + #define UCSR0A USR + #define UBRR0L UBRR +#else + #define UCR UCSR0B + #define UDR UDR0 + #define USR UCSR0A +#endif + + +/* Transmission fill byte */ +#define SER_FILL_BYTE 0xAA + + +static void uart0_enabletxirq(UNUSED(struct SerialHardware *ctx)) +{ +#ifdef CONFIG_SER_TXFILL + UCSR0B = BV(RXCIE) | BV(UDRIE) | BV(RXEN) | BV(TXEN) | BV(UCSZ2); +#else + UCSR0B = BV(RXCIE) | BV(UDRIE) | BV(RXEN) | BV(TXEN); +#endif +} + +static void uart0_init(struct SerialHardware *_hw, struct Serial *ser) +{ + struct AvrSerial *hw = (struct AvrSerial *)_hw; + hw->serial = ser; + + /* Set TX port as input with pull-up enabled to avoid + * noise on the remote RX when TX is disabled */ + cpuflags_t flags; + DISABLE_IRQSAVE(flags); + DDRE &= ~BV(PORTE1); + PORTE |= BV(PORTE1); + ENABLE_IRQRESTORE(flags); + + /* TODO: explain why TX is disabled whenever possible */ +#ifdef CONFIG_SER_TXFILL + /*! + * Set multiprocessor mode and 9 bit data frame. + * The receiver keep MPCM bit always on. When useful data + * is trasmitted the ninth bit is set. Receiver consider the + * frame as address info and receive it. + * When useless fill bytes are sent the ninth bit is cleared + * and the receiver will ignore them, avoiding useless triggering + * of RXC interrupt. + */ + UCSR0A = BV(MPCM); + UCSR0B = BV(RXCIE) | BV(RXEN) | BV(UCSZ2); +#else + UCSR0B = BV(RXCIE) | BV(RXEN); +#endif + + RTS_ON; +} + +static void uart0_cleanup(UNUSED(struct SerialHardware *ctx)) +{ + UCSR0B = 0; +} + +static void uart0_setbaudrate(UNUSED(struct SerialHardware *ctx), unsigned long rate) +{ + // Compute baud-rate period + uint16_t period = (((CLOCK_FREQ / 16UL) + (rate / 2)) / rate) - 1; + +#ifndef __AVR_ATmega103__ + UBRR0H = (period) >> 8; +#endif + UBRR0L = (period); +} + + +#ifndef __AVR_ATmega103__ + +static void uart1_enabletxirq(UNUSED(struct SerialHardware *ctx)) +{ + UCSR1B = BV(RXCIE) | BV(UDRIE) | BV(RXEN) | BV(TXEN); +} + +static void uart1_init(struct SerialHardware *_hw, struct Serial *ser) +{ + struct AvrSerial *hw = (struct AvrSerial *)_hw; + hw->serial = ser; + + /* Set TX port as input with pull-up enabled to avoid + * noise on the remote RX when TX is disabled */ + cpuflags_t flags; + DISABLE_IRQSAVE(flags); + DDRD &= ~BV(PORTD3); + PORTD |= BV(PORTD3); + ENABLE_IRQRESTORE(flags); + + /* TODO: explain why TX is disabled whenever possible */ + UCSR1B = BV(RXCIE) | BV(RXEN); + + RTS_ON; +} + +static void uart1_cleanup(UNUSED(struct SerialHardware *ctx)) +{ + UCSR1B = 0; +} + +static void uart1_setbaudrate(UNUSED(struct SerialHardware *ctx), unsigned long rate) +{ + // Compute baud-rate period + uint16_t period = (((CLOCK_FREQ / 16UL) + (rate / 2)) / rate) - 1; + + UBRR1H = (period) >> 8; + UBRR1L = (period); +} + +static void uart0_setparity(UNUSED(struct SerialHardware *ctx), int parity) +{ + UCSR0C |= (parity) << UPM0; +} + +static void uart1_setparity(UNUSED(struct SerialHardware *ctx), int parity) +{ + UCSR1C |= (parity) << UPM0; +} + +#endif /* !__AVR_ATmega103__ */ + + +static void spi_init(struct SerialHardware *_hw, struct Serial *ser) +{ + struct AvrSerial *hw = (struct AvrSerial *)_hw; + hw->serial = ser; + + /* MOSI and SCK out, MISO in */ + SPI_DDR |= BV(SPI_MOSI_BIT) | BV(SPI_SCK_BIT); + SPI_DDR &= ~BV(SPI_MISO_BIT); + /* Enable SPI, IRQ on, Master, CPU_CLOCK/16 */ + SPCR = BV(SPE) | BV(SPIE) | BV(MSTR) | BV(SPR0); +} + +static void spi_cleanup(UNUSED(struct SerialHardware *ctx)) +{ + SPCR = 0; + /* Set all pins as inputs */ + SPI_DDR &= ~(BV(SPI_MISO_BIT) | BV(SPI_MOSI_BIT) | BV(SPI_SCK_BIT)); +} + + + +#if defined(CONFIG_SER_HW_HANDSHAKE) + +//! This interrupt is triggered when the CTS line goes high +SIGNAL(SIG_CTS) +{ + // Re-enable UDR empty interrupt and TX, then disable CTS interrupt + UCR = BV(RXCIE) | BV(UDRIE) | BV(RXEN) | BV(TXEN); + cbi(EIMSK, EIMSKB_CTS); +} + +#endif // CONFIG_SER_HW_HANDSHAKE + + +/*! + * Serial 0 TX interrupt handler + */ +#ifdef __AVR_ATmega103__ +SIGNAL(SIG_UART_DATA) +#else +SIGNAL(SIG_UART0_DATA) +#endif +{ + if (fifo_isempty(&ser_handles[SER_UART0].txfifo)) + { +#ifdef CONFIG_SER_TXFILL + /* + * To avoid audio interference: always transmit useless char. + * Send the byte with the ninth bit cleared, the receiver in MCPM mode + * will ignore it. + */ + UCSR0B &= ~BV(TXB8); + UDR0 = SER_FILL_BYTE; +#else + /* Disable UDR empty interrupt and transmitter */ + UCR = BV(RXCIE) | BV(RXEN); +#endif + } +#if defined(CONFIG_SER_HWHANDSHAKE) + else if (IS_CTS_OFF) + { + // disable rx interrupt and tx, enable CTS interrupt + UCR = BV(RXCIE) | BV(RXEN); + sbi(EIFR, EIMSKB_CTS); + sbi(EIMSK, EIMSKB_CTS); + } +#endif // CONFIG_SER_HWHANDSHAKE + else + { +#ifdef CONFIG_SER_TXFILL + /* Send with ninth bit set. Receiver in MCPM mode will receive it */ + UCSR0B |= BV(TXB8); +#endif + UDR = fifo_pop(&ser_handles[SER_UART0].txfifo); + } +} + +/*! + * Serial 1 TX interrupt handler + */ +#ifndef __AVR_ATmega103__ +SIGNAL(SIG_UART1_DATA) +{ + if (fifo_isempty(&ser_handles[SER_UART1].txfifo)) + { + /* Disable UDR empty interrupt and transmitter */ + UCSR1B = BV(RXCIE) | BV(RXEN); + } +#if defined(CONFIG_SER_HWHANDSHAKE) + else if (IS_CTS_OFF) + { + // disable rx interrupt and tx, enable CTS interrupt + UCSR1B = BV(RXCIE) | BV(RXEN); + sbi(EIFR, EIMSKB_CTS); + sbi(EIMSK, EIMSKB_CTS); + } +#endif // CONFIG_SER_HWHANDSHAKE + else + UDR1 = fifo_pop(&ser_handles[SER_UART1].txfifo); +} +#endif /* !__AVR_ATmega103__ */ + + +/*! + * Serial 0 RX complete interrupt handler + */ +#ifdef __AVR_ATmega103__ +SIGNAL(SIG_UART_RECV) +#else +SIGNAL(SIG_UART0_RECV) +#endif +{ + /* Should be read before UDR */ + ser_handles[SER_UART0].status |= USR & (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 = UDR; + + if (fifo_isfull(&ser_handles[SER_UART0].rxfifo)) + ser_handles[SER_UART0].status |= SERRF_RXFIFOOVERRUN; + else + { + fifo_push(&ser_handles[SER_UART0].rxfifo, c); +#if defined(CONFIG_SER_HW_HANDSHAKE) + if (fifo_isfull(&ser_handles[SER_UART0].rxfifo)) + RTS_OFF; +#endif + } +} + +/*! + * Serial 1 RX complete interrupt handler + */ +#ifndef __AVR_ATmega103__ +SIGNAL(SIG_UART1_RECV) +{ + /* Should be read before UDR */ + 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; + + if (fifo_isfull(&ser_handles[SER_UART1].rxfifo)) + ser_handles[SER_UART1].status |= SERRF_RXFIFOOVERRUN; + else + { + fifo_push(&ser_handles[SER_UART1].rxfifo, c); +#if defined(CONFIG_SER_HW_HANDSHAKE) + if (fifo_isfull(&ser_handles[SER_UART1].rxfifo)) + RTS_OFF; +#endif + } +} +#endif /* !__AVR_ATmega103__ */ + + +/* + * SPI Flag: true if we are transmitting/receiving with the SPI. + * + * This kludge 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 ser_spi_starttx()). We do this *only* if a transfer is + * not already started. + */ +static volatile bool spi_sending = false; + +static void spi_starttx(UNUSED(struct SerialHardware *ctx)) +{ + cpuflags_t flags; + + DISABLE_IRQSAVE(flags); + + /* Send data only if the SPI is not already transmitting */ + if (!spi_sending && !fifo_isempty(&ser_handles[SER_SPI].txfifo)) + { + SPDR = fifo_pop(&ser_handles[SER_SPI].txfifo); + spi_sending = true; + } + + ENABLE_IRQRESTORE(flags); +} + +/*! + * SPI interrupt handler + */ +SIGNAL(SIG_SPI) +{ + /* Read incoming byte. */ + if (!fifo_isfull(&ser_handles[SER_SPI].rxfifo)) + fifo_push(&ser_handles[SER_SPI].rxfifo, SPDR); + /* + * FIXME + else + ser_handles[SER_SPI].status |= SERRF_RXFIFOOVERRUN; + */ + + /* Send */ + if (!fifo_isempty(&ser_handles[SER_SPI].txfifo)) + SPDR = fifo_pop(&ser_handles[SER_SPI].txfifo); + else + spi_sending = false; +} + + +/* + +#pragma vector = UART_TXC_vect +__interrupt void UART_TXC_interrupt(void) +{ + UCSRB &= ~TXCIE; + ReceiveMode(); + UCSRB = RXCIE | RXEN | TXEN; //Abilito l'Interrupt in ricezione e RX e TX +} +*/ + + +static const struct SerialHardwareVT UART0_VT = +{ + .init = uart0_init, + .cleanup = uart0_cleanup, + .setbaudrate = uart0_setbaudrate, + .setparity = uart0_setparity, + .enabletxirq = uart0_enabletxirq, +}; + +static const struct SerialHardwareVT UART1_VT = +{ + .init = uart1_init, + .cleanup = uart1_cleanup, + .setbaudrate = uart1_setbaudrate, + .setparity = uart1_setparity, + .enabletxirq = uart1_enabletxirq, +}; + +static const struct SerialHardwareVT SPI_VT = +{ + .init = spi_init, + .cleanup = spi_cleanup, + .enabletxirq = spi_starttx, +}; + +static struct AvrSerial UARTDescs[SER_CNT] = +{ + { + .hw = { .table = &UART0_VT }, + }, + + { + .hw = { .table = &UART1_VT }, + }, + + { + .hw = { .table = &SPI_VT }, + }, +}; + +struct SerialHardware* ser_hw_getdesc(int unit) +{ + ASSERT(unit < SER_CNT); + return &UARTDescs[unit].hw; +} diff --git a/drv/ser_dsp56k.c b/drv/ser_dsp56k.c new file mode 100755 index 00000000..d2f13620 --- /dev/null +++ b/drv/ser_dsp56k.c @@ -0,0 +1,228 @@ +/** + * \file + * Copyright (C) 2003 Develer S.r.l. (http://www.develer.com/) + * All Rights Reserved. + * + * \version $Id$ + * + * \author Stefano Fedrigo + * + * \brief DSP5680x CPU specific serial I/O driver + */ + +#include +#include +#include +#include "ser.h" +#include "ser_p.h" + +// GPIO E is shared with SPI (in DSP56807). Pins 0&1 are TXD0 and RXD0. To use +// the serial, we need to disable the GPIO functions on them. +#define REG_GPIO_SERIAL REG_GPIO_E +#define REG_GPIO_SERIAL_MASK 0x3 + +// Check flag consistency +#if (SERRF_PARITYERROR != REG_SCI_SR_PF) || \ + (SERRF_RXSROVERRUN != REG_SCI_SR_OR) || \ + (SERRF_FRAMEERROR != REG_SCI_SR_FE) || \ + (SERRF_NOISEERROR != REG_SCI_SR_NF) + #error error flags do not match with register bits +#endif + +struct SCI +{ + struct SerialHardware hw; + struct Serial* serial; + volatile struct REG_SCI_STRUCT* regs; + uint16_t irq_tx; + uint16_t irq_rx; +}; + + +static inline void enable_tx_irq_bare(volatile struct REG_SCI_STRUCT* regs) +{ + regs->CR |= REG_SCI_CR_TEIE | REG_SCI_CR_TIIE; +} + +static inline void enable_rx_irq_bare(volatile struct REG_SCI_STRUCT* regs) +{ + regs->CR |= REG_SCI_CR_RIE | REG_SCI_CR_REIE; +} + +static inline void disable_tx_irq_bare(volatile struct REG_SCI_STRUCT* regs) +{ + regs->CR &= ~(REG_SCI_CR_TEIE | REG_SCI_CR_TIIE); +} + +static inline void disable_rx_irq_bare(volatile struct REG_SCI_STRUCT* regs) +{ + regs->CR &= ~(REG_SCI_CR_RIE | REG_SCI_CR_REIE); +} + +static inline void disable_tx_irq(struct SerialHardware* _hw) +{ + struct SCI* hw = (struct SCI*)_hw; + volatile struct REG_SCI_STRUCT* regs = hw->regs; + + disable_tx_irq_bare(regs); +} + +static inline void enable_tx_irq(struct SerialHardware* _hw) +{ + struct SCI* hw = (struct SCI*)_hw; + volatile struct REG_SCI_STRUCT* regs = hw->regs; + + enable_tx_irq_bare(regs); +} + +static inline void enable_rx_irq(struct SerialHardware* _hw) +{ + struct SCI* hw = (struct SCI*)_hw; + volatile struct REG_SCI_STRUCT* regs = hw->regs; + + enable_rx_irq_bare(regs); +} + +INLINE void tx_isr(struct SCI *hw) +{ + volatile struct REG_SCI_STRUCT* regs = hw->regs; + + if (fifo_isempty(&hw->serial->txfifo)) + disable_tx_irq_bare(regs); + else + { + // Clear transmitter flags before sending data + (void)regs->SR; + regs->DR = fifo_pop(&hw->serial->txfifo); + } +} + +INLINE void rx_isr(struct SCI *hw) +{ + volatile struct REG_SCI_STRUCT* regs = hw->regs; + + hw->serial->status |= regs->SR & (SERRF_PARITYERROR | + SERRF_RXSROVERRUN | + SERRF_FRAMEERROR | + SERRF_NOISEERROR); + + if (fifo_isfull(&hw->serial->rxfifo)) + hw->serial->status |= SERRF_RXFIFOOVERRUN; + else + fifo_push(&hw->serial->rxfifo, regs->DR); + + // Writing anything to the status register clear the + // error bits. + regs->SR = 0; +} + +static void init(struct SerialHardware* _hw, struct Serial* ser) +{ + struct SCI* hw = (struct SCI*)_hw; + volatile struct REG_SCI_STRUCT* regs = hw->regs; + + // Clear status register (IRQ/status flags) + (void)regs->SR; + regs->SR = 0; + + // Clear data register + (void)regs->DR; + + // Set priorities for both IRQs + irq_setpriority(hw->irq_tx, IRQ_PRIORITY_SCI_TX); + irq_setpriority(hw->irq_rx, IRQ_PRIORITY_SCI_RX); + + // Activate the RX error interrupts, and RX/TX transmissions + regs->CR = REG_SCI_CR_TE | REG_SCI_CR_RE; + enable_rx_irq_bare(regs); + + // Disable GPIO pins for TX and RX lines + REG_GPIO_SERIAL->PER |= REG_GPIO_SERIAL_MASK; + + hw->serial = ser; +} + +static void cleanup(struct SerialHardware* _hw) +{ + // TODO! + ASSERT(0); +} + +static void setbaudrate(struct SerialHardware* _hw, unsigned long rate) +{ + struct SCI* hw = (struct SCI*)_hw; + + // SCI has an internal 16x divider on the input clock, which comes + // from the IPbus (see the scheme in user manual, 12.7.3). We apply + // it to calculate the period to store in the register. + hw->regs->BR = (IPBUS_FREQ + rate * 8ul) / (rate * 16ul); +} + +static void setparity(struct SerialHardware* _hw, int parity) +{ + // ??? + ASSERT(0); +} + + +static const struct SerialHardwareVT SCI_VT = +{ + .init = init, + .cleanup = cleanup, + .setbaudrate = setbaudrate, + .setparity = setparity, + .enabletxirq = enable_tx_irq, +}; + +static struct SCI SCIDescs[2] = +{ + { + .hw = { .table = &SCI_VT }, + .regs = ®_SCI[0], + .irq_rx = IRQ_SCI0_RECEIVER_FULL, + .irq_tx = IRQ_SCI0_TRANSMITTER_READY, + }, + + { + .hw = { .table = &SCI_VT }, + .regs = ®_SCI[1], + .irq_rx = IRQ_SCI1_RECEIVER_FULL, + .irq_tx = IRQ_SCI1_TRANSMITTER_READY, + }, +}; + + + +void ser_hw_tx_isr_0(void); +void ser_hw_tx_isr_0(void) +{ +#pragma interrupt warn + tx_isr(&SCIDescs[0]); +} + +void ser_hw_rx_isr_0(void); +void ser_hw_rx_isr_0(void) +{ +#pragma interrupt warn + rx_isr(&SCIDescs[0]); +} + +void ser_hw_tx_isr_1(void); +void ser_hw_tx_isr_1(void) +{ +#pragma interrupt warn + tx_isr(&SCIDescs[1]); +} + +void ser_hw_rx_isr_1(void); +void ser_hw_rx_isr_1(void) +{ +#pragma interrupt warn + rx_isr(&SCIDescs[1]); +} + +struct SerialHardware* ser_hw_getdesc(int unit) +{ + ASSERT(unit < 2); + return &SCIDescs[unit].hw; +} diff --git a/drv/ser_i196.c b/drv/ser_i196.c new file mode 100755 index 00000000..7225e867 --- /dev/null +++ b/drv/ser_i196.c @@ -0,0 +1,95 @@ +/** + * \file + * Copyright (C) 2000 Bernardo Innocenti + * Copyright (C) 2003 Develer S.r.l. (http://www.develer.com/) + * All Rights Reserved. + * + * \version $Id$ + * + * \author Bernardo Innocenti + * + * \brief CPU specific serial I/O driver + */ + +/* + * $Log$ + * Revision 1.1 2004/05/23 18:10:11 bernie + * Import drv/ modules. + * + * Revision 1.1 2003/11/20 22:30:21 aleph + * Add serial driver + * + */ + +#include "hw.h" +#include "serhw.h" + +#define SER_HW_ENABLE_TX \ + DISABLE_INTS; \ + if (!ser_sending) \ + { \ + ser_sending = true; \ + (INT_PEND1 |= INT1F_TI) \ + } \ + ENABLE_INTS; + +static volatile bool ser_sending; + +// Serial TX intr +INTERRUPT(0x30) void TI_interrupt(void) +{ + if (CANT_SEND) + { + ser_sending = false; + return; + } + + /* Can we send two bytes at the same time? */ + if (SP_STAT & SPSF_TX_EMPTY) + { + SBUF = fifo_pop(&ser_txfifo); + + if (CANT_SEND) + { + ser_sending = false; + return; + } + } + + SBUF = fifo_pop(&ser_txfifo); +} + +INTERRUPT(0x32) void RI_interrupt(void) +{ + ser_status |= SP_STAT & + (SPSF_OVERRUN_ERROR | SPSF_PARITY_ERROR | SPSF_FRAMING_ERROR); + if (fifo_isfull(&ser_rxfifo)) + ser_status |= SERRF_RXFIFOOVERRUN; + else + fifo_push(&ser_rxfifo, SBUF); +} + +static void ser_setbaudrate(unsigned long rate) +{ + // Calcola il periodo per la generazione del baud rate richiesto + uint16_t baud = (uint16_t)(((CLOCK_FREQ / 16) / rate) - 1) | 0x8000; + BAUD_RATE = (uint8_t)baud; + BAUD_RATE = (uint8_t)(baud >> 8); +} + +static void ser_hw_init(void) +{ + // Inizializza la porta seriale + SP_CON = SPCF_RECEIVE_ENABLE | SPCF_MODE1; + ioc1_img |= IOC1F_TXD_SEL | IOC1F_EXTINT_SRC; + IOC1 = ioc1_img; + + // Svuota il buffer di ricezione + { + uint8_t dummy = SBUF; + } + + // Abilita gli interrupt + INT_MASK1 |= INT1F_TI | INT1F_RI; +} + diff --git a/drv/ser_p.h b/drv/ser_p.h new file mode 100755 index 00000000..14d21710 --- /dev/null +++ b/drv/ser_p.h @@ -0,0 +1,52 @@ +/** + * \file + * Copyright (C) 2003,2004 Develer S.r.l. (http://www.develer.com/) + * All Rights Reserved. + * + * \brief Hardware dependent serial driver (interface) + * + * \version $Id$ + * + * \author Stefano Fedrigo + * \author Giovanni Bajo + */ + +/* + * $Log$ + * Revision 1.1 2004/05/23 18:10:11 bernie + * Import drv/ modules. + * + * Revision 1.1 2004/05/14 12:47:26 rasky + * Importato nuovo supporto seriale per AVR da Stefano + * + * Revision 1.3 2004/05/08 13:59:08 aleph + * Fix header guard + * + * Revision 1.2 2004/05/08 13:58:36 aleph + * Add log comment + * + */ + +#ifndef _DRV_SER_P_H +#define _DRV_SER_P_H + +struct SerialHardware; +struct Serial; + +struct SerialHardwareVT +{ + void (*init)(struct SerialHardware* ctx, struct Serial* ser); + void (*cleanup)(struct SerialHardware* ctx); + void (*setbaudrate)(struct SerialHardware* ctx, unsigned long rate); + void (*setparity)(struct SerialHardware* ctx, int parity); + void (*enabletxirq)(struct SerialHardware* ctx); +}; + +struct SerialHardware +{ + const struct SerialHardwareVT* table; +}; + +struct SerialHardware* ser_hw_getdesc(int unit); + +#endif // _DRV_SER_P_H -- 2.25.1