簡單總結了下 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; 在這一行解決。