目录
2. 注册串口设备rt_hw_serial_register
串口驱动框架抽象了不同串口设备的共性,实现了IO设备管理层需要的设备操作方法。
1. 串口设备控制块
rtthread通过serial.c和serial.h两个文件进行串口设备的管理。通过serial.h中的结构体rt_serial_device进行串口设备的定义,串口设备继承自设备基类rt_device,rt_device继承自rt_object基类,继承关系如下
串口设备通过结构体的定义实现了对rt_device设备基类的继承,结构体的私有成员据定了串口设备的相关操作。
/* components/drivers/include/drivers/serial.h */
struct rt_serial_device
{
struct rt_device parent;
const struct rt_uart_ops *ops;
struct serial_configure config;
void *serial_rx;
void *serial_tx;
};
typedef struct rt_serial_device rt_serial_t;
- parent:基类设备对象
- ops:串口设备操作,需要驱动层实现
- config:串口配置
- serial_rx:记录接收软件FIFO首地址
- serial_tx:记录发送软件FIFO首地址
2. 注册串口设备rt_hw_serial_register
设置设备操作方法,将设备注册到IO设备管理框架中
/* components/drivers/serial/serial.c */
/*
* serial register
*/
rt_err_t rt_hw_serial_register(struct rt_serial_device *serial,
const char *name,
rt_uint32_t flag,
void *data)
{
rt_err_t ret;
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;
#ifdef RT_USING_DEVICE_OPS
device->ops = &serial_ops;
#else
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;
#endif
device->user_data = data;
/* register a character device */
ret = rt_device_register(device, name, flag);
#if defined(RT_USING_POSIX)
/* set fops */
device->fops = &_serial_fops;
#endif
return ret;
}
完成的主要工作:
- 设置设备类型为
RT_Device_Class_Char
,即字符设备 - 设置设备操作方法,包括
init
、open
、close
、read
、write
、control
- 设置用户数据
- 将设备注册到设备管理框架
3. 串口设备操作方法
3.1 rt_serial_init
IO设备管理层对串口设备调用rt_device_init
时,实际调用的就是这个函数
/* components/drivers/serial/serial.c */
/*
* This function initializes serial device.
*/
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;
/* initialize rx/tx */
serial->serial_rx = RT_NULL;
serial->serial_tx = RT_NULL;
/* apply configuration */
if (serial->ops->configure)
result = serial->ops->configure(serial, &serial->config);
return result;
}
完成的主要工作:
- 初始化
serial_rx
和serial_tx
为RT_NULL
- 调用串口设备
configure
方法,configure
方法需要驱动层实现
3.2 rt_serial_control
IO设备管理层对串口设备调用rt_device_control
时,实际调用的就是这个函数
/* components/drivers/serial/serial.c */
static rt_err_t rt_serial_control(struct rt_device *dev,
int cmd,
void *args)
{
rt_err_t ret = RT_EOK;
struct rt_serial_device *serial;
RT_ASSERT(dev != 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:
if (args)
{
struct serial_configure *pconfig = (struct serial_configure *) args;
if (pconfig->bufsz != serial->config.bufsz && serial->parent.ref_count)
{
/*can not change buffer size*/
return RT_EBUSY;
}
/* set serial configure */
serial->config = *pconfig;
if (serial->parent.ref_count)
{
/* serial device has been opened, to configure it */
serial->ops->configure(serial, (struct serial_configure *) args);
}
}
break;
#ifdef RT_USING_POSIX_TERMIOS
case TCGETA:
{
struct termios *tio = (struct termios*)args;
if (tio == RT_NULL) return -RT_EINVAL;
tio->c_iflag = 0;
tio->c_oflag = 0;
tio->c_lflag = 0;
/* update oflag for console device */
if (rt_console_get_device() == dev)
tio->c_oflag = OPOST | ONLCR;
/* set cflag */
tio->c_cflag = 0;
if (serial->config.data_bits == DATA_BITS_5)
tio->c_cflag = CS5;
else if (serial->config.data_bits == DATA_BITS_6)
tio->c_cflag = CS6;
else if (serial->config.data_bits == DATA_BITS_7)
tio->c_cflag = CS7;
else if (serial->config.data_bits == DATA_BITS_8)
tio->c_cflag = CS8;
if (serial->config.stop_bits == STOP_BITS_2)
tio->c_cflag |= CSTOPB;
if (serial->config.parity == PARITY_EVEN)
tio->c_cflag |= PARENB;
else if (serial->config.parity == PARITY_ODD)
tio->c_cflag |= (PARODD | PARENB);
cfsetospeed(tio, _get_speed(serial->config.baud_rate));
}
break;
case TCSETAW:
case TCSETAF:
case TCSETA:
{
int baudrate;
struct serial_configure config;
struct termios *tio = (struct termios*)args;
if (tio == RT_NULL) return -RT_EINVAL;
config = serial->config;
baudrate = _get_baudrate(cfgetospeed(tio));
config.baud_rate = baudrate;
switch (tio->c_cflag & CSIZE)
{
case CS5:
config.data_bits = DATA_BITS_5;
break;
case CS6:
config.data_bits = DATA_BITS_6;
break;
case CS7:
config.data_bits = DATA_BITS_7;
break;
default:
config.data_bits = DATA_BITS_8;
break;
}
if (tio->c_cflag & CSTOPB) config.stop_bits = STOP_BITS_2;
else config.stop_bits = STOP_BITS_1;
if (tio->c_cflag & PARENB)
{
if (tio->c_cflag & PARODD) config.parity = PARITY_ODD;
else config.parity = PARITY_EVEN;
}
else config.parity = PARITY_NONE;
serial->ops->configure(serial, &config);
}
break;
case TCFLSH:
{
int queue = (int)args;
_tc_flush(serial, queue);
}
break;
case TCXONC:
break;
#endif
#ifdef RT_USING_POSIX
case FIONREAD:
{
rt_size_t recved = 0;
rt_base_t level;
level = rt_hw_interrupt_disable();
recved = _serial_fifo_calc_recved_len(serial);
rt_hw_interrupt_enable(level);
*(rt_size_t *)args = recved;
}
break;
#endif
default :
/* control device */
ret = serial->ops->control(serial, cmd, args);
break;
}
return ret;
}
完成的主要工作:
- 根据不同的命令执行不同的处理,如果不是预定义的命令,则调用串口设备
control
方法,control
需要驱动层实现
3.3 rt_serial_open
IO设备管理层对串口设备调用rt_device_open
时,实际调用的就是这个函数。主要完成对serial_rx
和serial_tx
的初始化。
/* components/drivers/serial/serial.c */
static rt_err_t rt_serial_open(struct rt_device *dev, rt_uint16_t oflag)
{
rt_uint16_t stream_flag = 0;
struct rt_serial_device *serial;
RT_ASSERT(dev != RT_NULL);
serial = (struct rt_serial_device *)dev;
LOG_D("open serial device: 0x%08x with open flag: 0x%04x",
dev, oflag);
/* check device flag with the open flag */
if ((oflag & RT_DEVICE_FLAG_DMA_RX) && !(dev->flag & RT_DEVICE_FLAG_DMA_RX))
return -RT_EIO;
if ((oflag & RT_DEVICE_FLAG_DMA_TX) && !(dev->flag & RT_DEVICE_FLAG_DMA_TX))
return -RT_EIO;
if ((oflag & RT_DEVICE_FLAG_INT_RX) && !(dev->flag & RT_DEVICE_FLAG_INT_RX))
return -RT_EIO;
if ((oflag & RT_DEVICE_FLAG_INT_TX) && !(dev->flag & RT_DEVICE_FLAG_INT_TX))
return -RT_EIO;
/* keep steam flag */
if ((oflag & RT_DEVICE_FLAG_STREAM) || (dev->open_flag & RT_DEVICE_FLAG_STREAM))
stream_flag = RT_DEVICE_FLAG_STREAM;
/* get open flags */
dev->open_flag = oflag & 0xff;
/* initialize the Rx/Tx structure according to open flag */
if (serial->serial_rx == RT_NULL)
{
if (oflag & RT_DEVICE_FLAG_INT_RX)
{
struct rt_serial_rx_fifo* rx_fifo;
rx_fifo = (struct rt_serial_rx_fifo*) rt_malloc (sizeof(struct rt_serial_rx_fifo) +
serial->config.bufsz);
RT_ASSERT(rx_fifo != RT_NULL);
rx_fifo->buffer = (rt_uint8_t*) (rx_fifo + 1);
rt_memset(rx_fifo->buffer, 0, serial->config.bufsz);
rx_fifo->put_index = 0;
rx_fifo->get_index = 0;
rx_fifo->is_full = RT_FALSE;
serial->serial_rx = rx_fifo;
dev->open_flag |= RT_DEVICE_FLAG_INT_RX;
/* configure low level device */
serial->ops->control(serial, RT_DEVICE_CTRL_SET_INT, (void *)RT_DEVICE_FLAG_INT_RX);
}
#ifdef RT_SERIAL_USING_DMA
else if (oflag & RT_DEVICE_FLAG_DMA_RX)
{
if (serial->config.bufsz == 0) {
struct rt_serial_rx_dma* rx_dma;
rx_dma = (struct rt_serial_rx_dma*) rt_malloc (sizeof(struct rt_serial_rx_dma));
RT_ASSERT(rx_dma != RT_NULL);
rx_dma->activated = RT_FALSE;
serial->serial_rx = rx_dma;
} else {
struct rt_serial_rx_fifo* rx_fifo;
rx_fifo = (struct rt_serial_rx_fifo*) rt_malloc (sizeof(struct rt_serial_rx_fifo) +
serial->config.bufsz);
RT_ASSERT(rx_fifo != RT_NULL);
rx_fifo->buffer = (rt_uint8_t*) (rx_fifo + 1);
rt_memset(rx_fifo->buffer, 0, serial->config.bufsz);
rx_fifo->put_index = 0;
rx_fifo->get_index = 0;
rx_fifo->is_full = RT_FALSE;
serial->serial_rx = rx_fifo;
/* configure fifo address and length to low level device */
serial->ops->control(serial, RT_DEVICE_CTRL_CONFIG, (void *) RT_DEVICE_FLAG_DMA_RX);
}
dev->open_flag |= RT_DEVICE_FLAG_DMA_RX;
}
#endif /* RT_SERIAL_USING_DMA */
else
{
serial->serial_rx = RT_NULL;
}
}
else
{
if (oflag & RT_DEVICE_FLAG_INT_RX)
dev->open_flag |= RT_DEVICE_FLAG_INT_RX;
#ifdef RT_SERIAL_USING_DMA
else if (oflag & RT_DEVICE_FLAG_DMA_RX)
dev->open_flag |= RT_DEVICE_FLAG_DMA_RX;
#endif /* RT_SERIAL_USING_DMA */
}
if (serial->serial_tx == RT_NULL)
{
if (oflag & RT_DEVICE_FLAG_INT_TX)
{
struct rt_serial_tx_fifo *tx_fifo;
tx_fifo = (struct rt_serial_tx_fifo*) rt_malloc(sizeof(struct rt_serial_tx_fifo));
RT_ASSERT(tx_fifo != RT_NULL);
rt_completion_init(&(tx_fifo->completion));
serial->serial_tx = tx_fifo;
dev->open_flag |= RT_DEVICE_FLAG_INT_TX;
/* configure low level device */
serial->ops->control(serial, RT_DEVICE_CTRL_SET_INT, (void *)RT_DEVICE_FLAG_INT_TX);
}
#ifdef RT_SERIAL_USING_DMA
else if (oflag & RT_DEVICE_FLAG_DMA_TX)
{
struct rt_serial_tx_dma* tx_dma;
tx_dma = (struct rt_serial_tx_dma*) rt_malloc (sizeof(struct rt_serial_tx_dma));
RT_ASSERT(tx_dma != RT_NULL);
tx_dma->activated = RT_FALSE;
rt_data_queue_init(&(tx_dma->data_queue), 8, 4, RT_NULL);
serial->serial_tx = tx_dma;
dev->open_flag |= RT_DEVICE_FLAG_DMA_TX;
/* configure low level device */
serial->ops->control(serial, RT_DEVICE_CTRL_CONFIG, (void *)RT_DEVICE_FLAG_DMA_TX);
}
#endif /* RT_SERIAL_USING_DMA */
else
{
serial->serial_tx = RT_NULL;
}
}
else
{
if (oflag & RT_DEVICE_FLAG_INT_TX)
dev->open_flag |= RT_DEVICE_FLAG_INT_TX;
#ifdef RT_SERIAL_USING_DMA
else if (oflag & RT_DEVICE_FLAG_DMA_TX)
dev->open_flag |= RT_DEVICE_FLAG_DMA_TX;
#endif /* RT_SERIAL_USING_DMA */
}
/* set stream flag */
dev->open_flag |= stream_flag;
return RT_EOK;
}
完成的主要工作:
- 如果
serial_rx
未设置- 如果要使用
RT_DEVICE_FLAG_INT_RX
中断接收,则从内存堆中分配接收软件FIFO,将FIFO首地址赋给serial_rx
,设置设备RT_DEVICE_FLAG_INT_RX
中断接收标志,调用串口设备control
方法来处理RT_DEVICE_CTRL_SET_INT
命令 - 如果要使用
RT_DEVICE_FLAG_DMA_RX
DMA接收,则从内存堆中分配接收软件FIFO,将FIFO首地址赋给serial_rx
,设置设备RT_DEVICE_FLAG_DMA_RX
DMA接收标志,调用串口设备control
方法来处理RT_DEVICE_CTRL_CONFIG
命令 - 否则
serial_rx
设为RT_NULL
- 如果要使用
- 如果
serial_rx
已设置,则只设置设备的RT_DEVICE_FLAG_INT_RX
中断接收标志或RT_DEVICE_FLAG_DMA_RX
DMA接收标志 - 对
serial_tx
重复上面类似的操作
3.4 rt_serial_close
完成与rt_serial_open
相反的操作,主要是释放软件FIFO空间,清除设备中断收发或者DMA收发标志,将serial_rx
和serial_tx
置为RT_NULL
,调用串口设备control
方法
3.5 rt_serial_read
/* components/drivers/serial/serial.c */
static rt_size_t rt_serial_read(struct rt_device *dev,
rt_off_t pos,
void *buffer,
rt_size_t size)
{
struct rt_serial_device *serial;
RT_ASSERT(dev != RT_NULL);
if (size == 0) return 0;
serial = (struct rt_serial_device *)dev;
if (dev->open_flag & RT_DEVICE_FLAG_INT_RX)
{
return _serial_int_rx(serial, (rt_uint8_t *)buffer, size);
}
#ifdef RT_SERIAL_USING_DMA
else if (dev->open_flag & RT_DEVICE_FLAG_DMA_RX)
{
return _serial_dma_rx(serial, (rt_uint8_t *)buffer, size);
}
#endif /* RT_SERIAL_USING_DMA */
return _serial_poll_rx(serial, (rt_uint8_t *)buffer, size);
}
完成的主要工作:
- 根据设备打开标志进行中断接收,或DMA接收,或轮询接收
3.6 rt_serial_write
/* components/drivers/serial/serial.c */
static rt_size_t rt_serial_write(struct rt_device *dev,
rt_off_t pos,
const void *buffer,
rt_size_t size)
{
struct rt_serial_device *serial;
RT_ASSERT(dev != RT_NULL);
if (size == 0) return 0;
serial = (struct rt_serial_device *)dev;
if (dev->open_flag & RT_DEVICE_FLAG_INT_TX)
{
return _serial_int_tx(serial, (const rt_uint8_t *)buffer, size);
}
#ifdef RT_SERIAL_USING_DMA
else if (dev->open_flag & RT_DEVICE_FLAG_DMA_TX)
{
return _serial_dma_tx(serial, (const rt_uint8_t *)buffer, size);
}
#endif /* RT_SERIAL_USING_DMA */
else
{
return _serial_poll_tx(serial, (const rt_uint8_t *)buffer, size);
}
}
完成的主要工作:
- 根据设备打开标志进行中断发送,或DMA发送,或轮询发送