⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 linux串口通信示例子.txt

📁 linux下的串口通信程序详细示例 详细解释了更种串口通信所需的系统调用
💻 TXT
📖 第 1 页 / 共 2 页
字号:
最好的侦错你程式码的方法是建构另一台 Linux box, 并把两台电脑用 null-modem 缆线连接. 用 miniterm以传送字元到你的 Linux box. Miniterm 很容易编译而它会把所有输入到键盘的字元透过序列埠传送. 只有这个宣告定义会被检查 #define MODEMDEVICE "/dev/ttyS0". 如果是 COM1 设定为 ttyS0, 如果是 COM2 设定为 ttyS1 等等.. 先前的测试是必要的, 所有的 字元都将以 raw 方式 (不经任何处理) 直接传送. 测试是否连接正确, 在两台电脑上都启动 miniterm 然後随便在键盘上乱按. 在其中一台上输入的字元应该会显示在另一台电脑上反之亦同. 但输入的字元不会回应到与之相连的萤幕上.  
    要自制 null-modem 的电缆, 你必需要把 TxD (传送) 及 RxD (接收) 两线对调.

    当然也可以只用一台电脑来作相同的测试, 只要电脑上有两个未使用的序列埠.

    当然你也就要执行两个 miniterm 来当虚拟控制台. 如果你是藉由拔去滑鼠来取得另一个序列埠, 记得要把 /dev/mouse 装置重新导向, 如果它存在的话. 如果你使用多埠的序列埠控制卡, 请确定它已设定正确. 当我在我的电脑上测试时也曾经因为设定错误而出过槌. 当我连到另一台电脑, 通讯埠开始传送字元. 就因为刚好这不是完整的非同步式传输, 所以可在同一台电脑上执行两个程式. 

连接埠设定 

    /dev/ttyS* 装置会被当成连接到你的 Linux box 的终端机, 并且在启动後就设定好了. 这个观念在你写 raw 装置的通讯程式时必需记住. 也就是说这个连接埠被设定为回应所有自这个装置送出的字元, 而用在资料传输时通常这种要改变这种工作模式.  

    所有的参数可以由一个小程式简单的完成. 设定参数被放在一个结构体内 struct termios, 他的定义档在 :  

#define NCCS 19 
struct termios { 
        tcflag_t c_iflag;               /* 输入模式旗标 */ 
        tcflag_t c_oflag;               /* 输出模式旗标 */ 
        tcflag_t c_cflag;               /* 控制模式旗标 */ 
        tcflag_t c_lflag;               /* 区域模式旗标 */ 
        cc_t c_line;                    /* 行控制 (line discipline) */ 
        cc_t c_cc[NCCS];                /* 控制特性 */ 
}; 

    这个档案也包含所有的旗标定义. 输入模式旗标在 c_iflag 掌管所有的输入处理, 这就意谓著由装置上传来的字元在还没用 read 功能读取前可以先处理过. 同理 c_oflag 掌管所有的输出处理. c_cflag 包含连接埠的设定, 如 鲍率, 每字元多少位元, 停止位元, 等等.. 区域模式旗标放在 c_lflag 用来侦测字元是否回应, 而讯号会送到你的程式, 等等.. 最後 c_cc 阵列定义了档案终了的控制字元, 停止, 等等.. 预设的控制字元值放在 . 有关旗标的细节摆在使用手册 termios(3). termios 结构体内的 c_line 行控制 (line discipline) 元素, 不能在 POSIX 相容的系统下使用译者注:这里所说的 line discipline 虽然我翻成 行控制但还是很难说出那是舍. 如果想知道请看看 kernel :( . 

序列装置的输入观念  

    有三个输入的观念要说明. 按照所要写的应用程式选用适合的观念. 尽量避免使用回圈来读取单一的字元再组成字串. 我曾这样做过, 会掉字元, 且对 read 而言不会显示任何错误.  

标准输入程序 

    这是终端机的标准处理程序, 但用来与其他 dl 型式的以行为单位的输入通讯也很有用, 也就是 read 会传回一整行完整的输入资料. 行预设的终止字元是 NL (ASCII LF), 档案结束符, 或行终止字元. 预设环境下, CR (是 DOS/Windows 预设的行终止符) 不会终止一行的叙述.  

    标准的输入处理程序还可以处理 清除, 删除字, 重印字元, 及转换 CR 为 NL 等等功能..  

非标准输入程序 

    非标准输入程序可以用在需要每次读取固定数量字元的情况, 并允许使用字元输入时间的计时器. 这种模式可以用在读取固定字元数量的应用程式, 或者所连接的装置会突然送出大量字元的状况.  

非同步式输入 

    以上所叙述的两种模式都可以用在非同步与同步的传输模式. 预设是在同步的模式下工作, 也就是在尚未读取完之前, read 的状态会被阻断. 而非同步模式下 read 的状态会直接返回并送出讯号到所叫用的程式直到完成工作. 这个讯号可以由讯号的处理程式 handler...来接收.  

等待来自多个讯号来源的输入 

    这并不是一个不一样的输入模式. 如果你要透过序列埠连接并处理多个装置的话, 它是满有用的. 在我的应用程式中我必需在几乎同一时间内, 透过 TCP/IP socket 及序列埠处理来自其他电脑的输入讯号. 下面这个□例程式将等待来自两个不同输入源的讯号. 如果其中一个信号源出现, 他就会被处理, 而程式会继续等待新的输入讯号.  

    以下这个方法看起来相当覆杂, 但请记住 Linux 是一个多工的作业系统. select 这个系统呼叫并不会在等待输入讯号时把 CPU 负载加重, 而如果你用回圈方式来等待输入讯号将使得其它同时执行的行程被拖慢.  

程序范例 

    所有的范例来源自 miniterm.c. The type ahead 暂存器被限制在 255 个字元, 就跟标准输入程序的最大字串长度相同 ( 或 ).  

    参考程式码中的注解它会解释不同输入模式的使用. 我希望这些程式码都能被了解. 标准输入程序的程式范例的注解写得最好, 其它的范例都只在不同於其它范例的地方做注解.  

    叙述不是很完整, 但可以激励你对这范例做实验, 以延生出合於你所需应用程式的最佳解.  

    别忘记要把序列埠的权限设定正确 (也就是: chmod a+rw /dev/ttyS1)!  

标准输入程序  

#include  
#include  
#include  
#include  
#include  

/* 鲍率设定被定义在 , 这在  被引入 */ 
#define BAUDRATE B38400             
/* 定义正确的序列埠 */ 
#define MODEMDEVICE "/dev/ttyS1" 
#define _POSIX_SOURCE 1 /* POSIX 系统相容 */ 

#define FALSE 0 
#define TRUE 1 

volatile int STOP=FALSE;  

main() 
{ 
  int fd,c, res; 
  struct termios oldtio,newtio; 
  char buf[255]; 
/*  
  开启数据机装置以读取并写入而不以控制 tty 的模式 
  因为我们不想程式在送出 CTRL-C 後就被杀掉. 
*/ 
 fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY );  
 if (fd <0) {perror(MODEMDEVICE); exit(-1); } 

 tcgetattr(fd,&oldtio); /* 储存目前的序列埠设定 */ 
 bzero(&newtio, sizeof(newtio)); /* 清除结构体以放入新的序列埠设定值 */ 

/*  
  BAUDRATE: 设定 bps 的速度. 你也可以用 cfsetispeed 及 cfsetospeed 来设定. 
  CRTSCTS : 输出资料的硬体流量控制 (只能在具完整线路的缆线下工作 
            参考 Serial-HOWTO 第七节) 
  CS8     : 8n1 (8 位元, 不做同位元检查,1 个终止位元) 
  CLOCAL  : 本地连线, 不具数据机控制功能 
  CREAD   : 致能接收字元 
*/ 
 newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD; 
  
/* 
  IGNPAR  : 忽略经同位元检查後, 错误的位元组 
  ICRNL   : 比 CR 对应成 NL (否则当输入讯号有 CR 时不会终止输入) 
            在不然把装置设定成 raw 模式(没有其它的输入处理) 
*/ 
 newtio.c_iflag = IGNPAR | ICRNL; 
  
/* 
 Raw 模式输出. 
*/ 
 newtio.c_oflag = 0; 
  
/* 
  ICANON  : 致能标准输入, 使所有回应机能停用, 并不送出信号以叫用程式 
*/ 
 newtio.c_lflag = ICANON; 
  
/*  
  初始化所有的控制特性 
  预设值可以在 /usr/include/termios.h 找到, 在注解中也有, 
  但我们在这不需要看它们 
*/ 
 newtio.c_cc[VINTR]    = 0;     /* Ctrl-c */  
 newtio.c_cc[VQUIT]    = 0;     /* Ctrl-\ */ 
 newtio.c_cc[VERASE]   = 0;     /* del */ 
 newtio.c_cc[VKILL]    = 0;     /* @ */ 
 newtio.c_cc[VEOF]     = 4;     /* Ctrl-d */ 
 newtio.c_cc[VTIME]    = 0;     /* 不使用分割字元组的计时器 */ 
 newtio.c_cc[VMIN]     = 1;     /* 在读取到 1 个字元前先停止 */ 
 newtio.c_cc[VSWTC]    = 0;     /* '\0' */ 
 newtio.c_cc[VSTART]   = 0;     /* Ctrl-q */  
 newtio.c_cc[VSTOP]    = 0;     /* Ctrl-s */ 
 newtio.c_cc[VSUSP]    = 0;     /* Ctrl-z */ 
 newtio.c_cc[VEOL]     = 0;     /* '\0' */ 
 newtio.c_cc[VREPRINT] = 0;     /* Ctrl-r */ 
 newtio.c_cc[VDISCARD] = 0;     /* Ctrl-u */ 
 newtio.c_cc[VWERASE]  = 0;     /* Ctrl-w */ 
 newtio.c_cc[VLNEXT]   = 0;     /* Ctrl-v */ 
 newtio.c_cc[VEOL2]    = 0;     /* '\0' */ 

/*  
  现在清除数据机线并启动序列埠的设定 
*/ 
 tcflush(fd, TCIFLUSH); 
 tcsetattr(fd,TCSANOW,&newtio); 

/* 
  终端机设定完成, 现在处理输入讯号 
  在这个□例, 在一行的开始处输入 'z' 会退出此程式. 
*/ 
 while (STOP==FALSE) {     /* 回圈会在我们发出终止的讯号後跳出 */ 
 /* 即使输入超过 255 个字元, 读取的程式段还是会一直等到行终结符出现才停止. 
    如果读到的字元组低於正确存在的字元组, 则所剩的字元会在下一次读取时取得. 
    res 用来存放真正读到的字元组个数 */ 
    res = read(fd,buf,255);  
    buf[res]=0;             /* 设定字串终止字元, 所以我们能用 printf */ 
    printf(":%s:%d\n", buf, res); 
    if (buf[0]=='z') STOP=TRUE; 
 } 
 /* 回存旧的序列埠设定值 */ 
 tcsetattr(fd,TCSANOW,&oldtio); 
} 



3.2 非标准输入程序  
在非标准的输入程序模式下, 输入的资料不会被组合成一行而输入後的处理功能 (清除, 杀掉, 删除, 等等.) 都不能使用. 这个模式有两个功能控制参数: c_cc[VTIME] 设定字元输入时间计时器, 及 c_cc[VMIN] 设定满足读取功能的最低字元接收个数.  

如果 MIN > 0 且 TIME = 0, MIN 设定为满足读取功能的最低字元接收个数. 由於 TIME 是 零, 所以计时器将不被使用.  

如果 MIN = 0 且 TIME > 0, TIME 将被当做逾时设定值. 满足读取功能的情况为读取到单一字元, 或者超过 TIME 所定义的时间 (t = TIME *0.1 s). 如果超过 TIME 所定义的时间, 则不会传回任何字元.  

如果 MIN > 0 且 TIME > 0, TIME 将被当做一个分割字元组的计时器. 满足读取功能的条件为 接收到  MIN 个数的字元, 或两个字元的间隔时间超过 TIME 所定义的值. 计时器会在每读到一个字元後重新计时, 且只会在第一个字元收到後才会启动.  

如果 MIN = 0 且 TIME = 0, 读取功能就马上被满足. 目前所存在的字元组个数, 或者 将回传的字元组个数. 根据  Antonino (参考 贡献) 所说, 你可以用 fcntl(fd, F_SETFL, FNDELAY); 在读取前得到相同的结果.  

藉由修改 newtio.c_cc[VTIME] 及 newtio.c_cc[VMIN] 上述的模式就可以测试了.  


#include  
#include  

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -