From 7fddcb0e355a572504181d3e04943acebd5e4fae Mon Sep 17 00:00:00 2001 From: "dzzxzz@gmail.com" Date: Fri, 1 Jun 2012 06:52:34 +0000 Subject: add serial device driver framework and implement the example in MB9BF506R branch git-svn-id: https://rt-thread.googlecode.com/svn/trunk@2145 bbd45198-f89e-11dd-88c7-29a3b14d5316 --- components/drivers/include/drivers/serial.h | 107 +++++++++ components/drivers/include/rtdevice.h | 4 + components/drivers/serial/SConscript | 8 + components/drivers/serial/serial.c | 361 ++++++++++++++++++++++++++++ 4 files changed, 480 insertions(+) create mode 100644 components/drivers/include/drivers/serial.h create mode 100644 components/drivers/serial/SConscript create mode 100644 components/drivers/serial/serial.c (limited to 'components/drivers') diff --git a/components/drivers/include/drivers/serial.h b/components/drivers/include/drivers/serial.h new file mode 100644 index 0000000000..12ecb0bb9a --- /dev/null +++ b/components/drivers/include/drivers/serial.h @@ -0,0 +1,107 @@ +/* + * File : serial.h + * This file is part of RT-Thread RTOS + * COPYRIGHT (C) 2006 - 2012, RT-Thread Development Team + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rt-thread.org/license/LICENSE + * + * Change Logs: + * Date Author Notes + * 2012-05-15 lgnq first version. + * 2012-05-28 bernard chage interfaces + */ + +#ifndef __SERIAL_H__ +#define __SERIAL_H__ + +#include + +#define BAUD_RATE_9600 9600 +#define BAUD_RATE_115200 115200 + +#define DATA_BITS_5 5 +#define DATA_BITS_6 6 +#define DATA_BITS_7 7 +#define DATA_BITS_8 8 +#define DATA_BITS_9 9 + +#define STOP_BITS_1 1 +#define STOP_BITS_2 2 +#define STOP_BITS_3 3 +#define STOP_BITS_4 4 + +#define PARITY_NONE 0 +#define PARITY_ODD 1 +#define PARITY_EVEN 2 + +#define BIT_ORDER_LSB 0 +#define BIT_ORDER_MSB 1 + +#define NRZ_NORMAL 0 /* Non Return to Zero : normal mode */ +#define NRZ_INVERTED 1 /* Non Return to Zero : inverted mode */ + +#define UART_RX_BUFFER_SIZE 64 +#define UART_TX_BUFFER_SIZE 64 +#define SERIAL_RBUFFER_SIZE 64 + +#define RT_DEVICE_CTRL_CONFIG 0x03 /* configure device */ +#define RT_DEVICE_CTRL_SET_INT 0x10 /* enable receive irq */ +#define RT_DEVICE_CTRL_CLR_INT 0x11 /* disable receive irq */ +#define RT_DEVICE_CTRL_GET_INT 0x12 + +#define RT_SERIAL_RX_INT 0x01 +#define RT_SERIAL_TX_INT 0x02 + +#define RT_SERIAL_ERR_OVERRUN 0x01 +#define RT_SERIAL_ERR_FRAMING 0x02 +#define RT_SERIAL_ERR_PARITY 0x03 + +struct serial_ringbuffer +{ + rt_uint8_t buffer[SERIAL_RBUFFER_SIZE]; + rt_uint16_t put_index, get_index; +}; + +struct serial_configure +{ + rt_uint32_t baud_rate; + rt_uint32_t data_bits :4; + rt_uint32_t stop_bits :2; + rt_uint32_t parity :2; + rt_uint32_t bit_order :1; + rt_uint32_t invert :1; + rt_uint32_t reserved :20; +}; + +struct rt_serial_device +{ + struct rt_device parent; + + const struct rt_uart_ops *ops; + struct serial_configure config; + + /* rx structure */ + struct serial_ringbuffer *int_rx; + /* tx structure */ + struct serial_ringbuffer *int_tx; +}; +typedef struct rt_serial_device rt_serial_t; + +/** + * uart operators + */ +struct rt_uart_ops +{ + rt_err_t (*configure)(struct rt_serial_device *serial, struct serial_configure *cfg); + rt_err_t (*control)(struct rt_serial_device *serial, int cmd, void *arg); + + int (*putc)(struct rt_serial_device *serial, char c); + int (*getc)(struct rt_serial_device *serial); +}; + +void rt_hw_serial_isr(struct rt_serial_device *serial); +rt_err_t rt_hw_serial_register(struct rt_serial_device *serial, const char *name, rt_uint32_t flag, void *data); + +#endif diff --git a/components/drivers/include/rtdevice.h b/components/drivers/include/rtdevice.h index 259f935279..2b4b7b9ec4 100644 --- a/components/drivers/include/rtdevice.h +++ b/components/drivers/include/rtdevice.h @@ -63,4 +63,8 @@ rt_size_t rt_ringbuffer_emptry_size(struct rt_ringbuffer* rb); #include "drivers/usb_host.h" #endif +#ifdef RT_USING_SERIAL +#include "drivers/serial.h" +#endif + #endif diff --git a/components/drivers/serial/SConscript b/components/drivers/serial/SConscript new file mode 100644 index 0000000000..5e66156c82 --- /dev/null +++ b/components/drivers/serial/SConscript @@ -0,0 +1,8 @@ +from building import * + +cwd = GetCurrentDir() +src = Glob('*.c') +CPPPATH = [cwd + '/../include'] +group = DefineGroup('DeviceDrivers', src, depend = ['RT_USING_SERIAL'], CPPPATH = CPPPATH) + +Return('group') diff --git a/components/drivers/serial/serial.c b/components/drivers/serial/serial.c new file mode 100644 index 0000000000..b1f733006c --- /dev/null +++ b/components/drivers/serial/serial.c @@ -0,0 +1,361 @@ +/* + * File : serial.c + * This file is part of RT-Thread RTOS + * COPYRIGHT (C) 2006, RT-Thread Development Team + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rt-thread.org/license/LICENSE + * + * Change Logs: + * Date Author Notes + * 2006-03-13 bernard first version + * 2012-05-15 lgnq modified according bernard's implementation. + * 2012-05-28 bernard code cleanup + */ + +#include +#include +#include + +rt_inline void serial_ringbuffer_init(struct serial_ringbuffer* rbuffer) +{ + rt_memset(rbuffer->buffer, 0, sizeof(rbuffer->buffer)); + rbuffer->put_index = 0; + rbuffer->get_index = 0; +} + +rt_inline void serial_ringbuffer_putc(struct serial_ringbuffer* rbuffer, char ch) +{ + rt_base_t level; + + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + + rbuffer->buffer[rbuffer->put_index] = ch; + rbuffer->put_index = (rbuffer->put_index + 1) & (SERIAL_RBUFFER_SIZE - 1); + + /* if the next position is read index, discard this 'read char' */ + if (rbuffer->put_index == rbuffer->get_index) + { + rbuffer->get_index = (rbuffer->get_index + 1) & (SERIAL_RBUFFER_SIZE - 1); + } + + /* enable interrupt */ + rt_hw_interrupt_enable(level); +} + +rt_inline int serial_ringbuffer_putchar(struct serial_ringbuffer* rbuffer, char ch) +{ + rt_base_t level; + rt_uint16_t next_index; + + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + + next_index = (rbuffer->put_index + 1) & (SERIAL_RBUFFER_SIZE - 1); + if (next_index != rbuffer->get_index) + { + rbuffer->buffer[rbuffer->put_index] = ch; + rbuffer->put_index = next_index; + } + else + { + /* enable interrupt */ + rt_hw_interrupt_enable(level); + return -1; + } + + /* enable interrupt */ + rt_hw_interrupt_enable(level); + return 1; +} + +rt_inline int serial_ringbuffer_getc(struct serial_ringbuffer* rbuffer) +{ + int ch; + rt_base_t level; + + ch = -1; + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + if (rbuffer->get_index != rbuffer->put_index) + { + ch = rbuffer->buffer[rbuffer->get_index]; + rbuffer->get_index = (rbuffer->get_index + 1) & (SERIAL_RBUFFER_SIZE - 1); + } + /* enable interrupt */ + rt_hw_interrupt_enable(level); + return ch; +} + +rt_inline rt_uint32_t serial_ringbuffer_size(struct serial_ringbuffer* rbuffer) +{ + rt_uint32_t size; + rt_base_t level; + + level = rt_hw_interrupt_disable(); + size = (rbuffer->put_index - rbuffer->get_index) & (SERIAL_RBUFFER_SIZE - 1); + rt_hw_interrupt_enable(level); + + return size; +} + +/* RT-Thread Device Interface */ + +/* + * This function initializes serial + */ +static rt_err_t rt_serial_init(struct rt_device *dev) +{ + rt_err_t result = RT_EOK; + struct rt_serial_device *serial; + + RT_ASSERT(dev != RT_NULL); + serial = (struct rt_serial_device*) dev; + + if (!(dev->flag & RT_DEVICE_FLAG_ACTIVATED)) + { + /* apply configuration */ + if (serial->ops->configure) + result = serial->ops->configure(serial, &serial->config); + + if (result != RT_EOK) + return result; + + if (dev->flag & RT_DEVICE_FLAG_INT_RX) + serial_ringbuffer_init(serial->int_rx); + + if (dev->flag & RT_DEVICE_FLAG_INT_TX) + serial_ringbuffer_init(serial->int_tx); + + /* set activated */ + dev->flag |= RT_DEVICE_FLAG_ACTIVATED; + } + + return result; +} + +static rt_err_t rt_serial_open(struct rt_device *dev, rt_uint16_t oflag) +{ + struct rt_serial_device *serial; + rt_uint32_t int_flags = 0; + + RT_ASSERT(dev != RT_NULL); + serial = (struct rt_serial_device*) dev; + + if (dev->flag & RT_DEVICE_FLAG_INT_RX) + int_flags = RT_SERIAL_RX_INT; + if (dev->flag & RT_DEVICE_FLAG_INT_TX) + int_flags |= RT_SERIAL_TX_INT; + + if (int_flags) + { + serial->ops->control(serial, RT_DEVICE_CTRL_SET_INT, (void*)int_flags); + } + + return RT_EOK; +} + +static rt_err_t rt_serial_close(struct rt_device *dev) +{ + struct rt_serial_device *serial; + rt_uint32_t int_flags = 0; + + RT_ASSERT(dev != RT_NULL); + serial = (struct rt_serial_device*) dev; + + if (dev->flag & RT_DEVICE_FLAG_INT_RX) + int_flags = RT_SERIAL_RX_INT; + if (dev->flag & RT_DEVICE_FLAG_INT_TX) + int_flags |= RT_SERIAL_TX_INT; + + if (int_flags) + { + serial->ops->control(serial, RT_DEVICE_CTRL_CLR_INT, (void*)int_flags); + } + + return RT_EOK; +} + +static rt_size_t rt_serial_read(struct rt_device *dev, rt_off_t pos, void *buffer, rt_size_t size) +{ + rt_uint8_t *ptr; + rt_uint32_t read_nbytes; + struct rt_serial_device *serial; + + RT_ASSERT(dev != RT_NULL); + serial = (struct rt_serial_device*) dev; + + ptr = (rt_uint8_t*)buffer; + + if (dev->flag & RT_DEVICE_FLAG_INT_RX) + { + /* interrupt mode Rx */ + while (size) + { + int ch; + + ch = serial_ringbuffer_getc(serial->int_rx); + if (ch == -1) break; + + *ptr = ch & 0xff; + ptr ++; + size --; + } + } + else + { + /* polling mode */ + while ((rt_uint32_t)ptr - (rt_uint32_t)buffer < size) + { + *ptr = serial->ops->getc(serial); + ptr ++; + } + } + + read_nbytes = (rt_uint32_t)ptr - (rt_uint32_t)buffer; + /* set error code */ + if (read_nbytes == 0) + { + rt_set_errno(-RT_EEMPTY); + } + + return read_nbytes; +} + +static rt_size_t rt_serial_write(struct rt_device *dev, rt_off_t pos, + const void *buffer, rt_size_t size) +{ + rt_uint8_t *ptr; + rt_size_t write_nbytes = 0; + struct rt_serial_device *serial; + + RT_ASSERT(dev != RT_NULL); + serial = (struct rt_serial_device*) dev; + + ptr = (rt_uint8_t*)buffer; + + if (dev->flag & RT_DEVICE_FLAG_INT_TX) + { + /* warning: data will be discarded if buffer is full */ + while (size) + { + if (serial_ringbuffer_putchar(serial->int_tx, *ptr) != -1) + { + ptr ++; + size --; + } + else break; + } + } + else + { + /* polling mode */ + while (size) + { + /* + * to be polite with serial console add a line feed + * to the carriage return character + */ + if (*ptr == '\n' && (dev->flag & RT_DEVICE_FLAG_STREAM)) + { + serial->ops->putc(serial, '\r'); + } + + serial->ops->putc(serial, *ptr); + + ++ptr; + --size; + } + } + + write_nbytes = (rt_uint32_t)ptr - (rt_uint32_t)buffer; + if (write_nbytes == 0) + { + rt_set_errno(-RT_EFULL); + } + + return write_nbytes; +} + +static rt_err_t rt_serial_control(struct rt_device *dev, rt_uint8_t cmd, void *args) +{ + struct rt_serial_device *serial; + + RT_ASSERT(serial != RT_NULL); + serial = (struct rt_serial_device*) dev; + + switch (cmd) + { + case RT_DEVICE_CTRL_SUSPEND: + /* suspend device */ + dev->flag |= RT_DEVICE_FLAG_SUSPENDED; + break; + + case RT_DEVICE_CTRL_RESUME: + /* resume device */ + dev->flag &= ~RT_DEVICE_FLAG_SUSPENDED; + break; + + case RT_DEVICE_CTRL_CONFIG: + /* configure device */ + serial->ops->configure(serial, (struct serial_configure *)args); + break; + } + + return RT_EOK; +} + +/* + * serial register + */ +rt_err_t rt_hw_serial_register(struct rt_serial_device *serial, const char *name, rt_uint32_t flag, void *data) +{ + struct rt_device *device; + RT_ASSERT(serial != RT_NULL); + + device = &(serial->parent); + + device->type = RT_Device_Class_Char; + device->rx_indicate = RT_NULL; + device->tx_complete = RT_NULL; + + device->init = rt_serial_init; + device->open = rt_serial_open; + device->close = rt_serial_close; + device->read = rt_serial_read; + device->write = rt_serial_write; + device->control = rt_serial_control; + device->user_data = data; + + /* register a character device */ + return rt_device_register(device, name, flag); +} + +/* ISR for serial interrupt */ +void rt_hw_serial_isr(struct rt_serial_device *serial) +{ + int ch = -1; + + /* interrupt mode receive */ + RT_ASSERT(serial->parent.flag & RT_DEVICE_FLAG_INT_RX); + + while (1) + { + ch = serial->ops->getc(serial); + if (ch == -1) break; + + serial_ringbuffer_putc(serial->int_rx, ch); + } + + /* invoke callback */ + if (serial->parent.rx_indicate != RT_NULL) + { + rt_size_t rx_length; + + /* get rx length */ + rx_length = serial_ringbuffer_size(serial->int_rx); + serial->parent.rx_indicate(&serial->parent, rx_length); + } +} -- cgit v1.3.1