*********************************************************************************** * 文 档 名 称 :LINUX 下 的 串 口 通 信 编 程 与 调 试 * 文 档 作 者 : 熊 益 铭 (xiongyiming@nari-china.com) * 创 建 日 期 :2005 年 8 月 2 日 * 修 订 日 期 :2005 年 8 月 3 日 * 版 权 所 有 (C) 南 瑞 集 团 通 信 系 统 分 公 司, 保 留 所 有 权 利 *********************************************************************************** 目 录 1 使 用 串 口 的 注 意 点... 1 2 打 开 串 口... 2 3 配 置 串 口... 2 3.1 波 特 率... 2 3.2 数 据 位... 3 3.3 校 验 位... 3 3.4 停 止 位... 3 4 读 串 口... 3 5 写 串 口... 3 6 关 闭 串 口... 3 7 读 写 的 阻 塞 和 非 阻 塞 操 作... 4 7.1 阻 塞 和 非 阻 塞 的 含 义... 4 7.2 如 何 配 置... 4 7.3 read 例 外 的 配 置... 4 8 举 例 说 明 各 种 情 况... 5 8.1 读 写 都 非 阻 塞 的 例 子... 5 8.2 写 阻 塞 而 读 不 阻 塞 的 例 子... 6 8.3 读 写 都 阻 塞 的 例 子... 6 8.4 一 个 特 别 的 情 况... 7 9 推 荐 的 read 操 作... 7 10 编 程 点 滴... 8 源 代 码 参 考 192.168.0.26 机 器 上 F:\linux\example_code\serialcom.c 本 文 档 中 灰 色 部 分 为 源 代 码 1 使 用 串 口 的 注 意 点 Linux: 虚 拟 机 也 可 以 使 用 串 口 在 linux 下, 只 有 用 root 用 户 才 能 对 串 口 进 行 操 作 首 先 可 以 用 linux 命 令 minicom 进 行 调 试, 测 试 串 口 硬 件 上 是 否 能 够 通 起 来 在 Linux 下 串 口 设 备 作 为 文 件 进 行 操 作 版 权 所 有 (C) 南 瑞 集 团 通 信 系 统 分 公 司 - 1 -
串 口 操 作 需 要 的 头 文 件 如 下 : #include <stdio.h> /* 标 准 输 入 输 出 定 义 */ #include <stdlib.h> /* 标 准 函 数 库 定 义 */ #include <unistd.h> /*Unix 标 准 函 数 定 义 */ #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> /* 文 件 控 制 定 义 */ #include <termios.h> /*POSIX 终 端 控 制 定 义 */ #include <errno.h> /* 错 误 号 定 义 */ #include <sys/ioctl.h> /* 当 调 用 了 ioctl 函 数 时 include*/ 2 打 开 串 口 在 linux 中 串 口 位 于 /dev 下 串 口 一 为 /dev/ttys0, 串 口 二 为 /dev/ttys1 打 开 串 口 是 通 过 使 用 标 准 的 文 件 打 开 函 数 操 作 : int fd; struct termios options; int brate; fd=open( /dev/ttys0,o_rdwr O_NOCTTY); 3 配 置 串 口 打 开 串 口 之 后, 得 到 串 口 的 termios 变 量, 然 后 通 过 操 作 它 来 进 行 串 口 的 各 种 属 性 的 配 置 struct termios options; tcgetattr(fd,&options); //get termios information bzero(&options,sizeof(struct termios)); 这 里 清 零 的 作 用 是 保 证 本 次 将 要 进 行 的 配 置 不 会 受 到 串 口 原 来 的 配 置 的 影 响 当 全 部 配 置 完 成 之 后, tcflush(fd,tciflush); //flush both input and output io tcsetattr(fd,tcsanow,&options); //set termios infomation 3.1 波 特 率 long baudrate=9600; int brate; switch(baudrate) { case 110: brate=b110;break; case 300: brate=b300;break; 版 权 所 有 (C) 南 瑞 集 团 通 信 系 统 分 公 司 - 2 -
case 600: brate=b600;break; case 1200:brate=B1200;break; case 2400:brate=B2400;break; case 4800:brate=B4800;break; case 9600:brate=B9600;break; case 19200:brate=B19200;break; case 38400:brate=B38400;break; case 57600:brate=B57600;break; default: brate=b115200;break; } cfsetispeed(&options,brate); // 设 置 波 特 率 的 新 方 法 cfsetospeed(&options,brate); 3.2 数 据 位 3.3 校 验 位 3.4 停 止 位 4 读 串 口 Ret = read(fd,buffer,nbytes); 5 写 串 口 ret = write(fd,msg,length); 6 关 闭 串 口 close(fb); 版 权 所 有 (C) 南 瑞 集 团 通 信 系 统 分 公 司 - 3 -
7 读 写 的 阻 塞 和 非 阻 塞 操 作 有 2 个 可 以 进 行 控 制 的 地 方 ( 同 时 控 制 read 和 write): 一 个 是 在 打 开 串 口 的 时 候,open 函 数 是 否 带 O_NDELAY 参 数 ; 第 二 是 可 以 在 打 开 串 口 之 后, 用 fcntl() 函 数 进 行 配 置 7.1 阻 塞 和 非 阻 塞 的 含 义 block 情 况 : 对 于 read,block 指 的 是 当 串 口 输 入 缓 冲 区 没 有 数 据 的 时 候,read 函 数 将 会 阻 塞 在 这 里, 一 直 到 串 口 输 入 缓 冲 区 中 有 数 据 可 读 取,read 读 到 了 需 要 的 字 节 数 之 后, 返 回 值 为 读 到 的 字 节 数, 然 后 程 序 才 会 继 续 run 下 去 对 于 write,block 指 的 是 当 串 口 输 出 缓 冲 区 满, 或 者 剩 下 的 空 间 小 于 将 要 写 入 的 字 节 数, 则 write 将 阻 塞, 一 直 到 串 口 输 出 缓 冲 区 中 剩 下 的 空 间 大 于 等 于 将 要 写 入 的 字 节 数, 执 行 写 入 操 作, 然 后 返 回 写 入 的 字 节 数, 然 后 程 序 才 会 继 续 run 下 去 no block 情 况 : 对 于 read,no block 指 的 是 当 串 口 输 入 缓 冲 区 没 有 数 据 的 时 候,read 函 数 立 即 返 回, 返 回 值 为 0 对 于 write,no block 指 的 是 当 串 口 输 出 缓 冲 区 满, 或 者 剩 下 的 空 间 小 于 将 要 写 入 的 字 节 数, 则 write 将 进 行 写 操 作, 写 入 当 前 串 口 输 出 缓 冲 区 剩 下 的 空 间 允 许 的 字 节 数, 然 后 返 回 写 入 的 字 节 数 7.2 如 何 配 置 Block 配 置 : fd=open(devname,o_rdwr O_NOCTTY); 或 者 fcntl(fd,f_setfl,0); no block 配 置 : fd=open(devname,o_rdwr O_NOCTTY O_NDELAY); 或 者 fcntl(fd,f_setfl, FNDELAY); 注 意,fcntl() 函 数 可 以 在 O_NDELAY 参 数 使 用 之 后 使 用, 它 会 覆 盖 open 时 候 的 配 置 7.3 read 例 外 的 配 置 可 能 有 些 时 候, 进 行 了 上 述 block 的 配 置 之 后, 会 发 现 read 函 数 还 是 没 有 block, 而 是 立 即 返 回, 这 是 因 为 read 函 数 不 仅 仅 受 上 面 两 个 因 素 影 响, 还 受 下 面 的 两 个 参 数 影 响 options.c_cc[vmin] 和 options.c_cc[vtime] 不 过 这 两 个 参 数 只 有 当 前 面 两 个 参 数 设 置 为 block 状 态 下 才 有 效, 否 则 无 效 这 2 个 参 数 的 默 认 值 为 0 VMIN 是 配 置 read 函 数 每 次 读 数 据 的 时 候 最 小 读 取 的 字 节 数 VTIME 是 配 置 read 函 数 当 没 有 读 到 数 据 的 时 候, 等 待 的 超 时 时 间 ( 单 位 为 10ms) 配 置 方 法 如 下 : 版 权 所 有 (C) 南 瑞 集 团 通 信 系 统 分 公 司 - 4 -
8 举 例 说 明 各 种 情 况 8.1 读 写 都 非 阻 塞 的 例 子 情 况 2: fd=open(devname,o_rdwr O_NOCTTY);//block fcntl(fd,f_setfl,fndelay);//no block length = strlen( serial ); ret = write(fd, serial, length);// length=6 write() no block, 正 常 写 完 后 返 回 值 为 6; 不 正 常 时 返 回 值 为 写 入 的 字 节 数 ; 没 有 写 入 的 时 候 返 回 值 为 -1 read() no block, 当 串 口 输 入 缓 冲 区 中 没 有 数 据 的 时 候, 返 回 0 情 况 4: fd=open(devname,o_rdwr O_NOCTTY O_NDELAY); //no block fcntl(fd,f_setfl,fndelay); //no block ret = write(fd, serial, length); write() no block, 正 常 写 完 后 返 回 值 为 6; 不 正 常 时 返 回 值 为 写 入 的 字 节 数 ; 没 有 写 入 的 时 候 返 回 值 为 -1 read() no block, 当 串 口 输 入 缓 冲 区 中 没 有 数 据 的 时 候, 返 回 0 情 况 5: fd=open(devname,o_rdwr O_NOCTTY O_NDELAY); //no block options.c_cc[vmin]=4; 版 权 所 有 (C) 南 瑞 集 团 通 信 系 统 分 公 司 - 5 -
fcntl(fd,f_setfl,fndelay); //no block ret = write(fd, serial, length); write() no block, 正 常 写 完 后 返 回 值 为 6; 不 正 常 时 返 回 值 为 写 入 的 字 节 数 ; 没 有 写 入 的 时 候 返 回 值 为 -1 read() no block, 当 串 口 输 入 缓 冲 区 中 没 有 数 据 的 时 候, 返 回 -1 可 以 看 到, 这 个 时 候 options.c_cc[vmin] 和 options.c_cc[vtime] 的 配 置 是 不 起 效 果 的 8.2 写 阻 塞 而 读 不 阻 塞 的 例 子 情 况 1: fd=open(devname,o_rdwr O_NOCTTY);//block fcntl(fd,f_setfl,0);//block ret = write(fd, serial, length); write() block, 直 到 写 完 后 返 回 值 为 6 read() no block, 当 串 口 输 入 缓 冲 区 中 没 有 数 据 的 时 候, 返 回 0 情 况 3: fd=open(devname,o_rdwr O_NOCTTY O_NDELAY); //no block fcntl(fd,f_setfl,0); //block ret = write(fd, serial, length); write() block, 直 到 写 完 后 返 回 值 为 6 read() no block, 当 串 口 输 入 缓 冲 区 中 没 有 数 据 的 时 候, 返 回 0 8.3 读 写 都 阻 塞 的 例 子 情 况 6: 版 权 所 有 (C) 南 瑞 集 团 通 信 系 统 分 公 司 - 6 -
fd=open(devname,o_rdwr O_NOCTTY O_NDELAY); options.c_cc[vmin]=4; fcntl(fd,f_setfl,0); ret = write(fd, serial, 6); write() block, 正 常 写 完 后 返 回 值 为 6; 不 正 常 时 返 回 值 为 写 入 的 字 节 数 ; 没 有 写 入 的 时 候 返 回 值 为 -1 read() block, 当 串 口 输 入 缓 冲 区 中 没 有 >=VMIN 个 字 节 数 据 的 时 候, 阻 塞 只 有 当 缓 冲 中 有 >=4 个 字 节 的 时 候,read 才 解 除 阻 塞 不 过 受 到 read() 中 第 3 个 参 数 控 制, 最 多 一 次 读 取 8 个 字 节 所 以 read 的 返 回 值 为 4~8 8.4 一 个 特 别 的 情 况 情 况 7: fd=open(devname,o_rdwr O_NOCTTY O_NDELAY); options.c_cc[vmin]=4; fcntl(fd,f_setfl,0); ret = write(fd, serial, 6); i = read(fd, buffer, 3); write() block, 正 常 写 完 后 返 回 值 为 6; 不 正 常 时 返 回 值 为 写 入 的 字 节 数 ; 没 有 写 入 的 时 候 返 回 值 为 -1 read() block, 当 串 口 输 入 缓 冲 区 中 没 有 >=VMIN 个 字 节 的 时 候, 阻 塞 只 有 当 缓 冲 中 有 >=4 个 字 节 的 时 候,read 才 解 除 阻 塞 不 过 又 由 于 受 到 read() 中 第 3 个 参 数 控 制, 最 多 一 次 读 取 3 个 字 节 所 以 当 串 口 输 入 缓 冲 区 中 有 3 个 字 节 时 候,read 阻 塞, 当 串 口 输 入 缓 冲 区 中 又 加 入 1 个 字 节 的 时 候,read 解 除 阻 塞, 返 回 前 面 3 个 字 节 数 据, 返 回 值 为 3 9 推 荐 的 read 操 作 Reading data from a port is a little trick. When you operate the port in raw data mode, each read(2) system call will return however many characters are actually available in the serial input buffers. 因 为 上 面 的 这 个 原 因, 所 以 推 荐 在 read 调 用 之 前, 先 用 ioctl 函 数 来 查 询 一 下 在 串 口 版 权 所 有 (C) 南 瑞 集 团 通 信 系 统 分 公 司 - 7 -
输 入 缓 冲 区 中 有 多 少 字 节 数 据 当 字 节 数 为 0 的 时 候, 不 进 行 read, 这 样 就 不 用 再 关 心 read 操 作 是 否 阻 塞 方 式 了 int nbytes; ioctl(fd,fionread,&nbytes);// 查 询 输 入 缓 冲 区 中 的 字 节 数 if(nbytes>0) { nbytes=read(fd,buffer,nbytes); buffer[nbytes]=0; printf(buffer); } return nbytes; 10 编 程 点 滴 Windows 环 境 下 回 车 CR 是 0d 0a, 而 linux 下 是 0a Linux windows \n 0a 0d 0a \r\n 0d 0a \r od 版 权 所 有 (C) 南 瑞 集 团 通 信 系 统 分 公 司 - 8 -