Linux/Android 串口总结
文章目录
前言
简单总结了下 Linux/Android 串口相关操作
参考资料:
《Linux 设备驱动开发详解》
《Linux 设备驱动程序》
《Linux 内核完全注释》
《Unix 环境高级编程》
协议相关
话不多说了,嵌入式常用串口为三根线: GND/RX/TX, 接线时需要交叉连接。
即:
GNC <> GND
RX <> TX
TX <==> RX
串口配置如下:
抓取波形图如下:
解释如下:
软件相关
Kernel
终端概念
汇总:
tty 核心概览
上面说的有点绕,甚至还有点逻辑不清,线路规程吧,举个简单例子,以在终端输入命令来说,键盘硬件输出的都是按下的键码,
但是在终端上,最终会转化为回车,换行等各种操作,这个将键码转换为回车换行的就是一种线程规程。
结构体
串口核心结构体
串口驱动编写流程
1. uart_register_driver(): 注册串口驱动 uart_driver
///////////////////////////////////////////////////////////////////////////////
// 封装接口: 不需要我们做什么
1.1 alloc_tty_driver(): 分配 tty_driver 结构体
1.2 设置 tty_driver 结构体
1.3 初始化各个串口端口
1.4 tty_register_driver(): 注册 tty_driver 驱动
///////////////////////////////////////////////////////////////////////////////
2. 硬件及相关结构体初始化
3. uart_add_one_port(): 注册 uart_port 结构体
4. uart_remove_one_port(): 注销 uart_port 结构体
5. uart_unregister_driver(): 注销 uart_driver 结构体
参考例子
代码:Samsung.c (drivers\tty\serial)
平台:S3C24xx
初始化流程:
// Samsung.c (drivers\tty\serial)
module_init(s3c24xx_serial_modinit);
static int __init s3c24xx_serial_modinit//(void)
{
int ret;
ret = uart_register_driver(&s3c24xx_uart_drv);
// // 结构体中有名字什么的,只起到领头作用
// static struct uart_driver s3c24xx_uart_drv = {
// .owner = THIS_MODULE,
// .driver_name = "s3c2410_serial",
// .nr = CONFIG_SERIAL_SAMSUNG_UARTS,
// .cons = S3C24XX_SERIAL_CONSOLE,
// .dev_name = S3C24XX_SERIAL_NAME,
// .major = S3C24XX_SERIAL_MAJOR,
// .minor = S3C24XX_SERIAL_MINOR,
// };
int uart_register_driver//(struct uart_driver *drv)
{
struct tty_driver *normal;
int i, retval;
BUG_ON(drv->state);
/*
* Maybe we should be using a slab cache for this, especially if
* we have a large number of ports to handle.
*/
// 0. 分配 uart_state 结构体的内存空间,后面串口操作全部会根据此结构快速中转
drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL);
if (!drv->state)
goto out;
// 1. 分配 tty_driver
normal = alloc_tty_driver(drv->nr);
if (!normal)
goto out_kfree;
// 2. 设置 tty_driver
drv->tty_driver = normal;
normal->driver_name = drv->driver_name;
normal->name = drv->dev_name;
normal->major = drv->major;
normal->minor_start = drv->minor;
normal->type = TTY_DRIVER_TYPE_SERIAL;
normal->subtype = SERIAL_TYPE_NORMAL;
normal->init_termios = tty_std_termios;
// 默认的链路设置
struct ktermios tty_std_termios = { /* for the benefit of tty drivers */
.c_iflag = ICRNL | IXON,
.c_oflag = OPOST | ONLCR,
.c_cflag = B38400 | CS8 | CREAD | HUPCL,
.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK |
ECHOCTL | ECHOKE | IEXTEN,
.c_cc = INIT_C_CC,
.c_ispeed = 38400,
.c_ospeed = 38400
};
normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
normal->init_termios.c_ispeed = normal->init_termios.c_ospeed = 9600;
normal->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
normal->driver_state = drv;
tty_set_operations(normal, &uart_ops);
////////////////////////////////////////////////////////////
//【二级操作函数】
static const struct tty_operations uart_ops = {
.open = uart_open,
.close = uart_close,
.write = uart_write,
.put_char = uart_put_char,
.flush_chars = uart_flush_chars,
.write_room = uart_write_room,
.chars_in_buffer= uart_chars_in_buffer,
.flush_buffer = uart_flush_buffer,
.ioctl = uart_ioctl,
.throttle = uart_throttle,
.unthrottle = uart_unthrottle,
.send_xchar = uart_send_xchar,
.set_termios = uart_set_termios,
.set_ldisc = uart_set_ldisc,
.stop = uart_stop,
.start = uart_start,
.hangup = uart_hangup,
.break_ctl = uart_break_ctl,
.wait_until_sent= uart_wait_until_sent,
#ifdef CONFIG_PROC_FS
.proc_fops = &uart_proc_fops,
#endif
.tiocmget = uart_tiocmget,
.tiocmset = uart_tiocmset,
.get_icount = uart_get_icount,
#ifdef CONFIG_CONSOLE_POLL
.poll_init = uart_poll_init,
.poll_get_char = uart_poll_get_char,
.poll_put_char = uart_poll_put_char,
#endif
};
/*
* Initialise the UART state(s).
*/
// 3. 初始化各个串口端口
for (i = 0; i < drv->nr; i++) {
struct uart_state *state = drv->state + i;
struct tty_port *port = &state->port;
tty_port_init(port);
void tty_port_init//(struct tty_port *port)
{
memset(port, 0, sizeof(*port));
init_waitqueue_head(&port->open_wait);
init_waitqueue_head(&port->close_wait);
init_waitqueue_head(&port->delta_msr_wait);
mutex_init(&port->mutex);
mutex_init(&port->buf_mutex);
spin_lock_init(&port->lock);
port->close_delay = (50 * HZ) / 100;
port->closing_wait = (3000 * HZ) / 100;
kref_init(&port->kref);
}
port->ops = &uart_port_ops;
static const struct tty_port_operations uart_port_ops = {
.activate = uart_port_activate,
.shutdown = uart_port_shutdown,
.carrier_raised = uart_carrier_raised,
.dtr_rts = uart_dtr_rts,
};
port->close_delay = HZ / 2; /* .5 seconds */
port->closing_wait = 30 * HZ;/* 30 seconds */
}
// 4. 注册 tty_driver 驱动
retval = tty_register_driver(normal);
/////////////////////////////////////////////////////////////////////////////
// 字符设备
alloc_chrdev_region(&dev, driver->minor_start, driver->num, driver->name);
register_chrdev_region(dev, driver->num, driver->name);
cdev_init(&driver->cdev, &tty_fops);
////////////////////////////////////////////////////////////////
// 字符设备操作函数【一级操作函数】
static const struct file_operations tty_fops = {
.llseek = no_llseek,
.read = tty_read,
.write = tty_write,
.poll = tty_poll,
.unlocked_ioctl = tty_ioctl,
.compat_ioctl = tty_compat_ioctl,
.open = tty_open,
.release = tty_release,
.fasync = tty_fasync,
};
cdev_add(&driver->cdev, dev, driver->num);
tty_register_device(driver, i, NULL);
device_create(tty_class, device, dev, NULL, name);
if (retval >= 0)
return retval;
put_tty_driver(normal);
out_kfree:
kfree(drv->state);
out:
return -ENOMEM;
}
if (ret < 0) {
printk(KERN_ERR "failed to register UART driver\n");
return -1;
}
return platform_driver_register(&samsung_serial_driver);
// 调用对应的 probe 函数
s3c24xx_serial_probe()
// 1. 获得串口端口设置
ourport = &s3c24xx_serial_ports[probe_index];
static struct s3c24xx_uart_port s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS] = {
[0] = {
.port = {
.lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[0].port.lock),
.iotype = UPIO_MEM,
.uartclk = 0,
.fifosize = 16,
.ops = &s3c24xx_serial_ops,
.flags = UPF_BOOT_AUTOCONF,
.line = 0,
}
},
[1] = {
.port = {
.lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[1].port.lock),
.iotype = UPIO_MEM,
.uartclk = 0,
.fifosize = 16,
.ops = &s3c24xx_serial_ops,//【三级操作函数】
static struct uart_ops s3c24xx_serial_ops = {
.pm = s3c24xx_serial_pm,
.tx_empty = s3c24xx_serial_tx_empty,
.get_mctrl = s3c24xx_serial_get_mctrl,
.set_mctrl = s3c24xx_serial_set_mctrl,
.stop_tx = s3c24xx_serial_stop_tx,
.start_tx = s3c24xx_serial_start_tx,
.stop_rx = s3c24xx_serial_stop_rx,
.enable_ms = s3c24xx_serial_enable_ms,
.break_ctl = s3c24xx_serial_break_ctl,
.startup = s3c24xx_serial_startup,
.shutdown = s3c24xx_serial_shutdown,
.set_termios = s3c24xx_serial_set_termios,
.type = s3c24xx_serial_type,
.release_port = s3c24xx_serial_release_port,
.request_port = s3c24xx_serial_request_port,
.config_port = s3c24xx_serial_config_port,
.verify_port = s3c24xx_serial_verify_port,
};
.flags = UPF_BOOT_AUTOCONF,
.line = 1,
}
},
。。。
};
ourport->drv_data = s3c24xx_get_driver_data(pdev);
// 2. 初始化串口端口
s3c24xx_serial_init_port(ourport, pdev);
// 获得资源,初始化 fifo、获得中断号、获得时钟等
// 3. 注册串口端口,建立 uart_driver 和 uart_port 之是的联系
uart_add_one_port(&s3c24xx_uart_drv, &ourport->port);
uart_configure_port(drv, state, uport);
// 注册串口设备
tty_dev = tty_register_device(drv->tty_driver, uport->line, uport->dev);
// 4. 属性文件与cpu设置
device_create_file(&pdev->dev, &dev_attr_clock_source);
s3c24xx_serial_cpufreq_register(ourport);
}
杂项流程汇总
框架
流程汇总
// 打开串口,会注册串口处理中断
APP: open()
-----------------------------------------------------------------
tty_open
// 初始化 tty 设备
tty_init_dev()
// 分析一个 tty 结构体
alloc_tty_struct();
initialize_tty_struct()
tty_ldisc_init(tty);
// 获得下面终端设置的 N_TTY 默认的操作函数集,即 tty_ldisc_N_TTY
struct tty_ldisc *ld = tty_ldisc_get(N_TTY);
tty_ldisc_assign(tty, ld);
tty->ldisc = ld;
retval = tty_ldisc_setup(tty, tty->link);
retval = tty_ldisc_open(tty, ld);
// 调用线程规程的 open 函数,即 tty_ldisc_N_TTY.ops.open
ret = ld->ops->open(tty);
tty_buffer_init(tty);
// 这个是在 tty_flip_buffer_push() 中调用的
INIT_WORK(&tty->buf.work, flush_to_ldisc);
tty->ops->open(tty, filp);
// 在初始化阶段设置成了 tty_operations 的,即 uart_ops
uart_open
// 设置 uart_state 结构体,这是在 uart_register_driver() 中分配的空间
struct uart_state *state = drv->state + line;
tty->driver_data = state;
uart_startup(tty, state, 0);
uart_port_startup(tty, state, init_hw);
// 调用驱动里面设置的 uart_ops 操作函数集
uport->ops->startup(uport);
s3c24xx_serial_startup
// 1. 使能串口接收功能
rx_enabled(port) = 1;
// 2. 为数据接收注册中断处理程序
request_irq(ourport->rx_irq, s3c24xx_serial_rx_chars, 0,
s3c24xx_serial_portname(port), ourport);
// 3. 使能串口发送功能
tx_enabled(port) = 1;
// 4. 为数据发送注册中断处理程序
request_irq(ourport->tx_irq, s3c24xx_serial_tx_chars, 0,
s3c24xx_serial_portname(port), ourport);
// 设置波特率
uart_change_speed(tty, state, NULL);
uport->ops->set_termios(uport, termios, old_termios);
// 关于 N_TTY 的来历
start_kernel(void)
console_init();
/* Setup the default TTY line discipline. */
tty_ldisc_begin();
/* Setup the default TTY line discipline. */
(void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);
// struct tty_ldisc_ops tty_ldisc_N_TTY = {
// .magic = TTY_LDISC_MAGIC,
// .name = "n_tty",
// .open = n_tty_open,
// .close = n_tty_close,
// .flush_buffer = n_tty_flush_buffer,
// .chars_in_buffer = n_tty_chars_in_buffer,
// .read = n_tty_read,
// .write = n_tty_write,
// .ioctl = n_tty_ioctl,
// .set_termios = n_tty_set_termios,
// .poll = n_tty_poll,
// .receive_buf = n_tty_receive_buf,
// .write_wakeup = n_tty_write_wakeup
// };
tty_ldiscs[disc] = new_ldisc;
new_ldisc->num = disc;
new_ldisc->refcount = 0;
// 串口发送
APP: write()
-----------------------------------------------------------------
tty_write
do_tty_write(ld->ops->write, tty, file, buf, count);
ret = write(tty, file, tty->write_buf, size);
/////////////////////////////////////////////////////////////////////////////////////////////////
// 即调用对应的 ld->ops->write 函数,这个函数在 open() 中设置为 tty_ldisc_N_TTY
n_tty_write(struct tty_struct *tty, struct file *file, const unsigned char *buf, size_t nr)
while (1) {
process_output_block(tty, b, nr);
// 查看可用缓冲空间
tty_write_room(tty);
if (tty->ops->write_room)
return tty->ops->write_room(tty);
/////////////////////////////////////////////////////////////////////////////////
// 调用串口注册函数中设置的 uart_ops->uart_write,他是 tty_operations 类型的
i = tty->ops->write(tty, buf, i);
uart_write
// 获得 uart_state 结构体,这是在 open 时设置的
struct uart_state *state = tty->driver_data;
uart_start(tty);
__uart_start(tty);
port->ops->start_tx(port);
///////////////////////////////////////////////////////////////////
// 调用对应串口驱动程序中写的操作函数
s3c24xx_serial_start_tx
if (!tx_enabled(port)) {
if (port->flags & UPF_CONS_FLOW)
s3c24xx_serial_rx_disable(port);
if (s3c24xx_serial_has_interrupt_mask(port))
__clear_bit(S3C64XX_UINTM_TXD,
portaddrl(port, S3C64XX_UINTM));
else
// 仅仅是使能中断来**发送处理函数
enable_irq(ourport->tx_irq);
// 使能串口发送功能
tx_enabled(port) = 1;
}
}
// 串口发送中断
static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id)
{
struct s3c24xx_uart_port *ourport = id;
struct uart_port *port = &ourport->port;
struct circ_buf *xmit = &port->state->xmit;
int count = 256;
// 1. 判断 x_char 是否为 0 ,不为 0 ,则发判断 0
if (port->x_char) {
wr_regb(port, S3C2410_UTXH, port->x_char);
port->icount.tx++;
port->x_char = 0;
goto out;
}
/* if there isn't anything more to transmit, or the uart is now
* stopped, disable the uart and exit
*/
// 2. 如果发送缓冲空或者驱动被设置为停止发送的状态,则取消发送
if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
s3c24xx_serial_stop_tx(port);
goto out;
}
/* try and drain the buffer... */
// 3. 循环发送,循环条件:发送缓冲区不为空
while (!uart_circ_empty(xmit) && count-- > 0) {
// 3.1 发送 fifo 如果满,退出发送
if (rd_regl(port, S3C2410_UFSTAT) & ourport->info->tx_fifofull)
break;
// 3.2 将要发送的字符写入发送寄存器
wr_regb(port, S3C2410_UTXH, xmit->buf[xmit->tail]);
// 3.3 修改循环缓冲的尾部位置
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
port->icount.tx++;//更新发送的统计量
}
// 4. 如果发送缓冲中的剩余数据量 uart_circ_chars_pending < 256 则唤醒之前阻塞的发送进程 uart_write_wakeup
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(port);
// 5. 如果发送缓冲空,则关闭发送使能
if (uart_circ_empty(xmit))
s3c24xx_serial_stop_tx(port);
out:
return IRQ_HANDLED;//函数出口,表示中断已经处理
}
// 串口接收
APP: read()
-----------------------------------------------------------------
tty_read
if (ld->ops->read)
i = (ld->ops->read)(tty, file, buf, count);
/////////////////////////////////////////////////////////////////////////////////////////
// 即调用对应的 ld->ops->read 函数,这个函数在 open() 中设置为 tty_ldisc_N_TTY
n_tty_read(struct tty_struct *tty, struct file *file, unsigned char __user *buf, size_t nr)
add_wait_queue(&tty->read_wait, &wait);
while (nr) {
set_current_state(TASK_INTERRUPTIBLE);
// 如果没有数据可读,调度其他程序
if (!input_available_p(tty, 0)) {
timeout = schedule_timeout(timeout);
}
// 如果有数据,则读从 tty_struct->read_buf 中读数据, 驱动有数据时,就会往这里写
uncopied = copy_from_read_buf(tty, &b, &nr);
uncopied += copy_from_read_buf(tty, &b, &nr);
}
remove_wait_queue(&tty->read_wait, &wait);
__set_current_state(TASK_RUNNING);
// 设置剩余空间
n_tty_set_room(tty);
// 串口接收中断
static irqreturn_t s3c24xx_serial_rx_chars(int irq, void *dev_id)
{
struct s3c24xx_uart_port *ourport = dev_id;
struct uart_port *port = &ourport->port;
struct tty_struct *tty = port->state->port.tty;
unsigned int ufcon, ch, flag, ufstat, uerstat;
int max_count = 64;
while (max_count-- > 0) {
// 1. 读取 UFCON 寄存器
ufcon = rd_regl(port, S3C2410_UFCON);
// 2. 读取 UFSTAT 寄存器
ufstat = rd_regl(port, S3C2410_UFSTAT);
// 3. 如果接收 fifo 里的数据量为 0,则退出处理
if (s3c24xx_serial_rx_fifocnt(ourport, ufstat) == 0)
break;
// 4. 读取 UERSTAT 寄存器
uerstat = rd_regl(port, S3C2410_UERSTAT);
// 5. 从 URXH 寄存器中取出接收到的字符
ch = rd_regb(port, S3C2410_URXH);
// 6. 进行流控处理
if (port->flags & UPF_CONS_FLOW) {
int txe = s3c24xx_serial_txempty_nofifo(port);
if (rx_enabled(port)) {
if (!txe) {
rx_enabled(port) = 0;
continue;
}
} else {
if (txe) {
ufcon |= S3C2410_UFCON_RESETRX;
wr_regl(port, S3C2410_UFCON, ufcon);
rx_enabled(port) = 1;
goto out;
}
continue;
}
}
/* insert the character into the buffer */
flag = TTY_NORMAL;
port->icount.rx++;
// 7. 根据 UERSTAT 寄存器的值,记录具体的错误类型
if (unlikely(uerstat & S3C2410_UERSTAT_ANY)) {
dbg("rxerr: port ch=0x%02x, rxs=0x%08x\n",
ch, uerstat);
/* check for break */
if (uerstat & S3C2410_UERSTAT_BREAK) {
dbg("break!\n");
port->icount.brk++;
if (uart_handle_break(port))
goto ignore_char;
}
if (uerstat & S3C2410_UERSTAT_FRAME)
port->icount.frame++;
if (uerstat & S3C2410_UERSTAT_OVERRUN)
port->icount.overrun++;
uerstat &= port->read_status_mask;
if (uerstat & S3C2410_UERSTAT_BREAK)
flag = TTY_BREAK;
else if (uerstat & S3C2410_UERSTAT_PARITY)
flag = TTY_PARITY;
else if (uerstat & (S3C2410_UERSTAT_FRAME |
S3C2410_UERSTAT_OVERRUN))
flag = TTY_FRAME;
}
// 8. 如果收到的是 sysrq 字符,进行特殊处理: uart_handle_sysrq_char()
if (uart_handle_sysrq_char(port, ch))
goto ignore_char;
// 9. 把接收到的字符送进串口驱动的 read_buf: uart_insert_char()
uart_insert_char(port, uerstat, S3C2410_UERSTAT_OVERRUN, ch, flag);
ignore_char:
continue;
}
// 10. 把串口驱动接收到的数据送进线路规程的 read_buf , tty_flip_buffer_push()
tty_flip_buffer_push(tty);
out:
return IRQ_HANDLED;
}
// 控制台初始化
start_kernel
console_init();
/* Setup the default TTY line discipline. */
tty_ldisc_begin();
/* Setup the default TTY line discipline.
设置 N_TTY 的默认操作函数为 tty_ldisc_N_TTY*/
(void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);
// 设置 tty_ldiscs[] 数组对应项,这个数组保存各个 ldisc 类型的操作函数集
tty_ldiscs[disc] = new_ldisc;
/*
* set up the console device so that later boot sequences can
* inform about problems etc..
*/
call = __con_initcall_start;
while (call < __con_initcall_end) {
(*call)();
call++;
}
Linux
用户空间终端编程,整体框架如下图:
工作模式:
1. 规范模式:以行为处理单位
2. 非规范模式:输入字符不组成行
相关结构体
终端 I/O 函数摘要
特殊字符
操作示例
// 打开串口
/* O_RDWR 读、写打开。
* O_NOCTTY 如果p a t h n a m e指的是终端设备,则不将此设备分配作为此进程的控制终端。
* O_NONBLOCK 如果p a t h n a m e指的是一个F I F O、一个块特殊文件或一个字符特殊文件,则此选择项为此文件的本次打开操作和后续的I / O操作设置非阻塞方式。
*
* O_RDONLY 只读打开。
O_WRONLY 只写打开。
O_APPEND 每次写时都加到文件的尾端。
O_CREAT 若此文件不存在则创建它。使用此选择项时,需同时说明第三个参数mode,用其说明该新文件的存取许可权位。
O_EXCL 如果同时指定了O_CREAT,而文件已经存在,则出错。这可测试一个文件是否存在,如果不存在则创建此文件成为一个原子操作。
O_TRUNC 如果此文件存在,而且为只读或只写成功打开,则将其长度截短为0。
O_SYNC 使每次w r i t e都等到物理I / O操作完成。
O_NOCTTY:告诉Unix这个程序不想成为“控制终端”控制的程序,不说明这个标志的话,任何输入都会影响你的程序。
O_NDELAY:告诉Unix这个程序不关心DCD信号线状态,即其他端口是否运行,不说明这个标志的话,该程序就会在DCD信号线为低电平时停止。
* */
m_nFd = open( "/dev/ttyS0", O_RDWR | O_NOCTTY | O_NONBLOCK); //support poll behavior
// disable echo on serial lines
if ( isatty( m_nFd ) )
{
struct termios ios;
tcgetattr( m_nFd, &ios );
DEBUG__("iDasSerialPortService:ComManager:ComManager","开始设置波特率等参数 set to 115200bps, 8-N-1");// Set 8bit data, No parity, stop 1 bit (8N1):
ios.c_cflag &= ~PARENB;//no parity
ios.c_cflag &= ~CSTOPB;//stop 1 bit
ios.c_cflag &= ~CSIZE;//Bit mask for data bits
ios.c_cflag = B115200 | CS8 | CLOCAL | CREAD;//baud rate |8 bit data | Local line ,don't change ower of port| enable receiver
ios.c_iflag &= ~(INLCR | ICRNL | IXON | IXOFF | IXANY);
ios.c_oflag &= ~OPOST; /*raw output*/
ios.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); /*raw input*/
tcsetattr( m_nFd, TCSANOW, &ios );
}
读/写/关闭串口用标准 Linux 接口:
ret = read(m_nFd, buff, sizeof(buff) );
ret = write(m_nFd, start_buffer, len);
关闭串口:
Android
基于安卓 4.0 平台,通过 jni 往下使用标准 Linux 接口调用串口,相关文件下附
整体流程是这样的:
在 SerialHelper.java 类中
调用 SerialPort.java 类初始化时
调用 Jni 文件通过 open() 打开串口,
然后返回文件描述符 fd
然后就可以通过 SerialHelper.java 类中标准的读写接口往文件描述符里写数据了
注意需要添加相关权限。
Jni 文件
/*
* Copyright 2009-2011 Cedric Priscal
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <termios.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <jni.h>
#include "SerialPort.h"
#include "android/log.h"
static const char *TAG="serial_port";
#define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO, TAG, fmt, ##args)
#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args)
#define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args)
static speed_t getBaudrate(jint baudrate)
{
switch(baudrate) {
case 0: return B0;
case 50: return B50;
case 75: return B75;
case 110: return B110;
case 134: return B134;
case 150: return B150;
case 200: return B200;
case 300: return B300;
case 600: return B600;
case 1200: return B1200;
case 1800: return B1800;
case 2400: return B2400;
case 4800: return B4800;
case 9600: return B9600;
case 19200: return B19200;
case 38400: return B38400;
case 57600: return B57600;
case 115200: return B115200;
case 230400: return B230400;
case 460800: return B460800;
case 500000: return B500000;
case 576000: return B576000;
case 921600: return B921600;
case 1000000: return B1000000;
case 1152000: return B1152000;
case 1500000: return B1500000;
case 2000000: return B2000000;
case 2500000: return B2500000;
case 3000000: return B3000000;
case 3500000: return B3500000;
case 4000000: return B4000000;
default: return -1;
}
}
/*
* Class: android_serialport_SerialPort
* Method: open
* Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor;
*/
JNIEXPORT jobject JNICALL Java_android_1serialport_1api_SerialPort_open
(JNIEnv *env, jclass thiz, jstring path, jint baudrate, jint flags)
{
int fd;
speed_t speed;
jobject mFileDescriptor;
/* Check arguments */
{
speed = getBaudrate(baudrate);
if (speed == -1) {
/* TODO: throw an exception */
LOGE("Invalid baudrate");
return NULL;
}
}
/* Opening device */
{
jboolean iscopy;
const char *path_utf = (*env)->GetStringUTFChars(env, path, &iscopy);
LOGD("Opening serial port %s with flags 0x%x", path_utf, O_RDWR | flags);
fd = open(path_utf, O_RDWR | flags);
LOGD("open() fd = %d", fd);
(*env)->ReleaseStringUTFChars(env, path, path_utf);
if (fd == -1)
{
/* Throw an exception */
LOGE("Cannot open port");
/* TODO: throw an exception */
return NULL;
}
}
/* Configure device */
{
struct termios cfg;
LOGD("Configuring serial port");
if (tcgetattr(fd, &cfg))
{
LOGE("tcgetattr() failed");
close(fd);
/* TODO: throw an exception */
return NULL;
}
cfmakeraw(&cfg);
cfsetispeed(&cfg, speed);
cfsetospeed(&cfg, speed);
if (tcsetattr(fd, TCSANOW, &cfg))
{
LOGE("tcsetattr() failed");
close(fd);
/* TODO: throw an exception */
return NULL;
}
}
/* Create a corresponding file descriptor */
{
jclass cFileDescriptor = (*env)->FindClass(env, "java/io/FileDescriptor");
jmethodID iFileDescriptor = (*env)->GetMethodID(env, cFileDescriptor, "<init>", "()V");
jfieldID descriptorID = (*env)->GetFieldID(env, cFileDescriptor, "descriptor", "I");
mFileDescriptor = (*env)->NewObject(env, cFileDescriptor, iFileDescriptor);
(*env)->SetIntField(env, mFileDescriptor, descriptorID, (jint)fd);
}
return mFileDescriptor;
}
/*
* Class: cedric_serial_SerialPort
* Method: close
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_android_1serialport_1api_SerialPort_close
(JNIEnv *env, jobject thiz)
{
jclass SerialPortClass = (*env)->GetObjectClass(env, thiz);
jclass FileDescriptorClass = (*env)->FindClass(env, "java/io/FileDescriptor");
jfieldID mFdID = (*env)->GetFieldID(env, SerialPortClass, "mFd", "Ljava/io/FileDescriptor;");
jfieldID descriptorID = (*env)->GetFieldID(env, FileDescriptorClass, "descriptor", "I");
jobject mFd = (*env)->GetObjectField(env, thiz, mFdID);
jint descriptor = (*env)->GetIntField(env, mFd, descriptorID);
LOGD("close(fd = %d)", descriptor);
close(descriptor);
}
Java 串口类
/*
* Copyright 2009 Cedric Priscal
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android_serialport_api;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import android.util.Log;
public class SerialPort {
private static final String TAG = "SerialPort";
/*
* Do not remove or rename the field mFd: it is used by native method close();
*/
private FileDescriptor mFd;
private FileInputStream mFileInputStream;
private FileOutputStream mFileOutputStream;
public SerialPort(File device, int baudrate, int flags) throws SecurityException, IOException {
/* Check access permission */
if (!device.canRead() || !device.canWrite()) {
try {
/* Missing read/write permission, trying to chmod the file */
Process su;
su = Runtime.getRuntime().exec("/system/bin/su");
String cmd = "chmod 666 " + device.getAbsolutePath() + "\n"
+ "exit\n";
su.getOutputStream().write(cmd.getBytes());
if ((su.waitFor() != 0) || !device.canRead()
|| !device.canWrite()) {
throw new SecurityException();
}
} catch (Exception e) {
e.printStackTrace();
throw new SecurityException();
}
}
mFd = open(device.getAbsolutePath(), baudrate, flags);
if (mFd == null) {
Log.e(TAG, "native open returns null");
throw new IOException();
}
mFileInputStream = new FileInputStream(mFd);
mFileOutputStream = new FileOutputStream(mFd);
}
// Getters and setters
public InputStream getInputStream() {
return mFileInputStream;
}
public OutputStream getOutputStream() {
return mFileOutputStream;
}
// JNI
private native static FileDescriptor open(String path, int baudrate, int flags);
public native void close();
static {
System.loadLibrary("serial_port");
}
}
Java 辅助类
package com.bjw.ComAssistant;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.InvalidParameterException;
import com.bjw.bean.ComBean;
import android_serialport_api.SerialPort;
/**
* @author benjaminwan
*串口辅助工具类
*/
public abstract class SerialHelper{
private SerialPort mSerialPort;
private OutputStream mOutputStream;
private InputStream mInputStream;
private ReadThread mReadThread;
private SendThread mSendThread;
private String sPort="/dev/s3c2410_serial0";
private int iBaudRate=9600;
private boolean _isOpen=false;
private byte[] _bLoopData=new byte[]{0x30};
private int iDelay=500;
//----------------------------------------------------
public SerialHelper(String sPort,int iBaudRate){
this.sPort = sPort;
this.iBaudRate=iBaudRate;
}
public SerialHelper(){
this("/dev/s3c2410_serial0",9600);
}
public SerialHelper(String sPort){
this(sPort,9600);
}
public SerialHelper(String sPort,String sBaudRate){
this(sPort,Integer.parseInt(sBaudRate));
}
//----------------------------------------------------
public void open() throws SecurityException, IOException,InvalidParameterException{
mSerialPort = new SerialPort(new File(sPort), iBaudRate, 0);
mOutputStream = mSerialPort.getOutputStream();
mInputStream = mSerialPort.getInputStream();
mReadThread = new ReadThread();
mReadThread.start();
mSendThread = new SendThread();
mSendThread.setSuspendFlag();
mSendThread.start();
_isOpen=true;
}
//----------------------------------------------------
public void close(){
if (mReadThread != null)
mReadThread.interrupt();
if (mSerialPort != null) {
mSerialPort.close();
mSerialPort = null;
}
_isOpen=false;
}
//----------------------------------------------------
public void send(byte[] bOutArray){
try
{
mOutputStream.write(bOutArray);
} catch (IOException e)
{
e.printStackTrace();
}
}
//----------------------------------------------------
public void sendHex(String sHex){
byte[] bOutArray = MyFunc.HexToByteArr(sHex);
send(bOutArray);
}
//----------------------------------------------------
public void sendTxt(String sTxt){
byte[] bOutArray =sTxt.getBytes();
send(bOutArray);
}
//----------------------------------------------------
private class ReadThread extends Thread {
@Override
public void run() {
super.run();
while(!isInterrupted()) {
try
{
if (mInputStream == null) return;
byte[] buffer=new byte[512];
int size = mInputStream.read(buffer);
if (size > 0){
ComBean ComRecData = new ComBean(sPort,buffer,size);
onDataReceived(ComRecData);
}
try
{
Thread.sleep(50);//延时50ms
} catch (InterruptedException e)
{
e.printStackTrace();
}
} catch (Throwable e)
{
e.printStackTrace();
return;
}
}
}
}
//----------------------------------------------------
private class SendThread extends Thread{
public boolean suspendFlag = true;// 控制线程的执行
@Override
public void run() {
super.run();
while(!isInterrupted()) {
synchronized (this)
{
while (suspendFlag)
{
try
{
wait();
} catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
send(getbLoopData());
try
{
Thread.sleep(iDelay);
} catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
//线程暂停
public void setSuspendFlag() {
this.suspendFlag = true;
}
//唤醒线程
public synchronized void setResume() {
this.suspendFlag = false;
notify();
}
}
//----------------------------------------------------
public int getBaudRate()
{
return iBaudRate;
}
public boolean setBaudRate(int iBaud)
{
if (_isOpen)
{
return false;
} else
{
iBaudRate = iBaud;
return true;
}
}
public boolean setBaudRate(String sBaud)
{
int iBaud = Integer.parseInt(sBaud);
return setBaudRate(iBaud);
}
//----------------------------------------------------
public String getPort()
{
return sPort;
}
public boolean setPort(String sPort)
{
if (_isOpen)
{
return false;
} else
{
this.sPort = sPort;
return true;
}
}
//----------------------------------------------------
public boolean isOpen()
{
return _isOpen;
}
//----------------------------------------------------
public byte[] getbLoopData()
{
return _bLoopData;
}
//----------------------------------------------------
public void setbLoopData(byte[] bLoopData)
{
this._bLoopData = bLoopData;
}
//----------------------------------------------------
public void setTxtLoopData(String sTxt){
this._bLoopData = sTxt.getBytes();
}
//----------------------------------------------------
public void setHexLoopData(String sHex){
this._bLoopData = MyFunc.HexToByteArr(sHex);
}
//----------------------------------------------------
public int getiDelay()
{
return iDelay;
}
//----------------------------------------------------
public void setiDelay(int iDelay)
{
this.iDelay = iDelay;
}
//----------------------------------------------------
public void startSend()
{
if (mSendThread != null)
{
mSendThread.setResume();
}
}
//----------------------------------------------------
public void stopSend()
{
if (mSendThread != null)
{
mSendThread.setSuspendFlag();
}
}
//----------------------------------------------------
protected abstract void onDataReceived(ComBean ComRecData);
}
相关修改
有一个项目上使用串口,收发数据量比较大,通过跳过平台自带的串口数据缓冲解决,核心就是驱动中置位如下标志
对应具体项目代码为:
项目串口使用比较频繁,而内核使用 kworker 来上报数据,会有延迟产生,故而可以通过添加标志 UPF_LOW_LATENCY
uart->port.flags = UPF_BOOT_AUTOCONF;
在这一行解决。