串口通信编程详解(Linux)

本文详细介绍了Linux环境下串口通信编程,涉及RS-232和RS-485的区别,串口操作的四个步骤,以及非阻塞I/O模式下的读写处理。通过设置串口参数,如波特率、数据位、停止位和校验位,并使用read和write函数进行数据交换。此外,还讨论了如何处理串口读取数据的不确定性和实现非阻塞读取的技巧,如使用select和treadn函数确保在一定时间内接收完整帧数据。
摘要由CSDN通过智能技术生成

rs232是三芯通信,即DB9的第2引脚RXD(接收)、第3引脚TXD(发送数据)、第5引脚DG(信号地)。

rs232是三芯通信,485是两芯通讯的,RS-232串口线 通常 是 DB9--DB9 的 串口通信线,9芯

RS-485数据线 是 双绞线或者屏蔽双绞线,232传输距离较近,485传输距离比较远,485是单工(向)通讯,232是双工(向)的。

通信编程都没有区别,都是按照RS232编程的,计算机没有485接口,需要用一个232转485的转换器就可以了。

串口的操作一般都通过四个步骤来完成:
1、打开串口
2、配置串口:对串口的波特率、数据位、停止位、校验码、等进行设置。
3、读写串口
4、关闭串口

       linux下编写终端程序时,有规范模式,非规范模式(原始模式特殊的非规范模式)之分。不用于终端,而是在串口这种使用情况下,一般设置为原始模式(非规范的一种特殊情况)。但用read()函数,希望从串口接收指定的数量的字符时,往往接收到的实际字符数,都与指定的不同。如本人用read()希望接收 10 bytes的数据,但实验后发现,分了几次才接收到,俩次接收2bytes ,两次接收3bytes。

查阅相关资料得知:

      一般地串口的读写模式有直接模式和缓存模式,在直接模式下,串口的读写都是单字节的,也就是说一次的read或write只能操作一个字节;但是大部份串口芯片都支持缓存模式,缓存模式一般同时支持中断聚合和超时机制,也就是说在有数据时,当缓存满或者超时时间到时,都会触发读或写中断。写的时候可以将要操作的数据先搬到缓存里,然后启动写操作,芯片会自动将一连串的数据写出,在读的时候类似,一次读到的是串口芯片缓存里的数据。串口设备的缓存一般有限,一次能read到的最大字节数就是缓存的容量。所以串口芯片的缓存容量决定了你一次能收到的字节数。

     本人用一个usb转232来充当串口接收时,发现一次可以接收8个bytes。对于具体一次传输多少字节也不去追究了,总之通讯过程中无法保证一次发送的数据肯定是一次接收的,所以必须写代码来一次一次的接收,直到接收满足预定的为止,当然在此过程中得使用select/poll来避免超时接收。

      即从通讯的角度来说,接受方必须自己解决如何识别一个祯的问题.(操作串口相当于操作物理层,OSI/ISO模型中的第一层,解决祯同步问题是第二层的任务,所以我们需要自己搭一个第二层。也就是说:我们需要通过定义通讯协议,规定数据的内容自己分析什么时候收完了一次需要的数据。因为通讯过程中无法保证一次发送的数据肯定是一次接收的)

       简单的串口编程,一般设置成阻塞模式,便可以了。但是在大多数应用场合,把串口设置成阻塞模式是很不实用的,如read()时,如果没有数据发来,这程序一直会阻塞在这里(除非用多线程)。因此一般把其设置为非阻塞模式。一般是需要用串口读取指定长度的数据,但是read函数实际读取的数据长度,往往会与指定的不同,所以必须自己编写一个读写N字节数据的函数:很快想到用个循环,但是循环中必须有 ‘即使一直没有收到指定长度的数据但在一定时间后也必须跳出循环’的机制,否则就与阻塞模式的没有区别了(也就是让函数一直等,等到指定长度数据接收为止)。参考下APUE的程序清单14-11的readn()函数,此函数看似很好,但是它不适合用于串口的读取,因为它一旦if(nread = read(fd, ptr, nleft) < 0) 就立刻会跳出循环,没有丝毫的时间上的容限,而串口的接收必然没有这么快,如若波特率为1200,是比较慢的。俩个字节传输的间隔,其都会被判断为错误而跳出。当然该函数对于读写文件是非常好用的。

ssize_t             /* Read "n" bytes from a descriptor  */
readn(int fd, void *ptr, size_t n)
{
size_t  nleft;
ssize_t  nread;

nleft = n;
while (nleft > 0) {
  if ((nread = read(fd, ptr, nleft)) < 0) {
   if (nleft == n)
    return(-1); /* error, return -1 */
   else
    break;      /* error, return amount read so far */
  } else if (nread == 0) {
   break;          /* EOF */
  }
  nleft -= nread;
  ptr   += nread;
}
return(n - nleft);      /* return >= 0 */
}

非阻塞I/O使我们的操作要么成功,要么立即返回错误,不被阻塞。
对于一个给定的描述符两种方法对其指定非阻塞I/O:
(1)调用open获得描述符,并指定O_NONBLOCK标志
(2)对已经打开的文件描述符,调用fcntl,打开O_NONBLOCK文件状态标志。
int flags,s为描述符

C/C++ code
flags = fcntl( s, F_GETFL, 0 ) )
fcntl( s, F_SETFL, flags | O_NONBLOCK )

再次参考下APUE的tread() 和treadn()函数,这组函数结合了select函数,使得在放弃之前,有了个时间来

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值