简单总结了下 Linux/Android 串口相关操作
参考资料:
《Linux 设备驱动开发详解》
《Linux 设备驱动程序》
《Linux 内核完全注释》
《Unix 环境高级编程》
话不多说了,嵌入式常用串口为三根线: GND/RX/TX, 接线时需要交叉连接。
即:
GNC <> GND
RX <> TX
TX <==> RX
串口配置如下:
抓取波形图如下:
解释如下:
汇总:
上面说的有点绕,甚至还有点逻辑不清,线路规程吧,举个简单例子,以在终端输入命令来说,键盘硬件输出的都是按下的键码,
但是在终端上,最终会转化为回车,换行等各种操作,这个将键码转换为回车换行的就是一种线程规程。
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++; }
用户空间终端编程,整体框架如下图:
工作模式:
1. 规范模式:以行为处理单位
2. 非规范模式:输入字符不组成行
// 打开串口
/* 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); 关闭串口:
基于安卓 4.0 平台,通过 jni 往下使用标准 Linux 接口调用串口,相关文件下附
整体流程是这样的:
在 SerialHelper.java 类中 调用 SerialPort.java 类初始化时 调用 Jni 文件通过 open() 打开串口, 然后返回文件描述符 fd 然后就可以通过 SerialHelper.java 类中标准的读写接口往文件描述符里写数据了
注意需要添加相关权限。
/* * 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); }
/* * 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"); } }
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; 在这一行解决。