Chinaunix首页 | 论坛 | 博客
  • 博客访问: 231178
  • 博文数量: 59
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 493
  • 用 户 组: 普通用户
  • 注册时间: 2014-04-26 17:46
个人简介

做技术要:鹰击长空,鱼翔浅底。

文章分类

全部博文(59)

文章存档

2017年(1)

2016年(3)

2015年(27)

2014年(28)

分类: LINUX

2014-04-27 23:33:18

   由于项目需要 Modbus走RS485物理层,首先看一下RS485电路:


电路很简单RS232 TTL电平经SP3458芯片转换成RS485的差分信号,软件控制上以U8为例,发送的时候GPIO0_19拉高,接收时GPIO0_19拉低即可。
由于我们在RS485上走Modbus 协议,由于ModBus是一个主从协议的特点,要求所有节点在空闲模式下都为接收状态,也就是GPIO0_19要拉低。我们可以在端口发送时将GPIO0_19拉高可写,写完在拉低可读。下面将引出这篇文章的核心---如何确定串口发送完成?
由于我们的开发框架式ARM+LINUX,
一般的串口操作是用 open函数打开一个串口获取文件操作符fd,对其进行波特率等设置,写时用write函数将要发送的字符串内容写进,然后通过write函数的返回值来确定我们写操作是否成功。读我们用的是read函数可以将驱动层串口缓存中的值读出来。这些在ModBus走RS232物理层完全可行的,因为RS232是全双工的。但是在R485半双工的情况下我们要明确一个时间点---什么时候我们的串口发送完成?
m_nFdModbus = Open(SERIAL4, O_RDWR | O_NONBLOCK)
//配置串口
 struct termios _opt;
    bzero(&_opt, sizeof(_opt)); //清除结构体以放入新的序列设定值
    Tcgetattr(m_nFdModbus, &_opt);
    _opt.c_cc[VMIN] = 1;
    _opt.c_iflag = 0;
    _opt.c_oflag = 0;
    _opt.c_lflag = 0;
    _opt.c_cflag = B19200 | CS8 | CLOCAL | CREAD;
    if (Tcsetattr(m_nFdModbus, TCSANOW, &_opt))
    {
        perror("tcsetattr   error");
        assert(false);
        exit(1);
    }

 int _ret = read(m_nFdModbus, _cRead, M_read_len);
 int _nRet = write(m_nFdModbus,pCmd_->szTxBuffer ,_nSendLen);

  一开始的时候我就在网上搜索,发现也有人遇到类似的问题:linux 串口的write,只是写到系统缓存里面,有没有一种方法可以检测到串口发送完成。。。。
 对,确实是这个问题?一开始不是很懂看着前辈们指点尝试了select,当我对select了解实现后,发现不是这不一回事?select方法满足不了发送的这种情况?
  这里介绍一下我理解的select,强调一点select面向的是文件集合,即一堆文件吧:
    int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout); 
1)    maxfdp:是一个文件描述符是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1 ,例如有三个描述符:    
     fd1=1;fd2=2;fd3=3,=>maxfdp=fd3+1=4
2)readfds,writefds,errorfds这三个都是struct  fd_set 类型的指针。
struct fd_set可以理解为一个集合,这个集合中存放的是文件描述符(file descriptor),即文件句柄,这可以是我们所说的普通意义的文件,当然Unix下任何设备、管道、FIFO等都是文件形式,全部包括在内,所以毫无疑问一个socket就是一个文件,socket句柄就是一个文件描述符。
fd_set集合可以通过一些宏由人为来操作,
比如清空集合-----FD_ZERO(fd_set *);
将一个给定的文件描述符加入集合之中----FD_SET(int ,fd_set *);
将一个给定的文件描述符从集合中删除FD_CLR(int ,fd_set*);
检查集合中指定的文件描述符是否可以读写FD_ISSET(int ,fd_set* );

再回头说说这几个参数:a)readfds 文件可读集合的结构体指针
                                         b)
writefds文件可写集合的结构体指针

                                          c)errorfds 文件异常集合的结构体指针

3)struct timeval是一个大家常用的结构,用来代表时间值,有两个成员,一个是秒数,另一个是毫秒数。他的作用是超时处理,即规定的时间没有想要的事件发生我就不给你们玩了我要退出,
4)文件无变化返回0(超时了亲),有变化返回一个正值
select错误返回负值;
main() 
int sock; FILE *fp; 
struct fd_set fds; 
struct timeval timeout={3,0}; //select等待3秒,3秒轮询,要非阻塞就置0 
char buffer[256]={0}; //256字节的接收缓冲区 
/* 假定已经建立UDP连接,具体过程不写,TCP也同理,主机ip和port都已经给定,要写的文件已经打开
sock=socket(...); 
bind(...); 
fp=fopen(...); */ 
while(1) 

FD_ZERO(&fds); //每次循环都要清空集合,否则不能检测描述符变化 
FD_SET(sock,&fds); //添加描述符 
FD_SET(fp,&fds); //同上 
maxfdp=sock>fp?sock+1:fp+1; //描述符最大值加1 
switch(select(maxfdp,&fds,&fds,NULL,&timeout)) //select使用
 

  case -1: exit(-1);break; //select错误,退出程序 
  case 0:break; //再次轮询 
  default: 
  if(FD_ISSET(sock,&fds)) //测试sock是否可读,即是否网络上有数据 

recvfrom(sock,buffer,256,.....);//接受网络数据 
if(FD_ISSET(fp,&fds)) //测试文件是否可写 
fwrite(fp,buffer...);//写入文件 
buffer清空; 
}// end if break; 
}// end switch 
}//end while 
}//end main

好了,这个也是参考:http://blog.csdn.net/zi_jin/article/details/4214359 加上了点自己的理解! 
介绍这么多,其实就想说这种方法对我们不适合?因为我一开始看别人这样说可以,不说这个了,就当是沿途的小插曲。
继续咱们的正题:
 我们要明确一个时间点---什么时候我们的串口发送完成?
由于我之前写过一点驱动,然后就自然想到去驱动中看看有没有此方法,硬着头皮看了一天多的内核源码中关于/device/tty/seri这部分,
中间产考了韦东山书中的串口驱动移植这一块,
在:struct tty_operations {
       int (*open)(struct tty_struct * tty, struct file * filp);
       void (*close)(struct tty_struct * tty, struct file * filp);
       int (*write)(struct tty_struct * tty,
                    const unsigned char *buf, int count);
       void (*put_char)(struct tty_struct *tty, unsigned char ch);
       void (*flush_chars)(struct tty_struct *tty);
       int (*write_room)(struct tty_struct *tty);
       int (*chars_in_buffer)(struct tty_struct *tty);
       int (*ioctl)(struct tty_struct *tty, struct file * file,
                  unsigned int cmd, unsigned long arg);
       long (*compat_ioctl)(struct tty_struct *tty, struct file * file,
                          unsigned int cmd, unsigned long arg);
       void (*set_termios)(struct tty_struct *tty, struct ktermios * old);
       void (*throttle)(struct tty_struct * tty);
       void (*unthrottle)(struct tty_struct * tty);
       void (*stop)(struct tty_struct *tty);
       void (*start)(struct tty_struct *tty);
       void (*hangup)(struct tty_struct *tty);
       void (*break_ctl)(struct tty_struct *tty, int state);
       void (*flush_buffer)(struct tty_struct *tty);
       void (*set_ldisc)(struct tty_struct *tty);
       void (*wait_until_sent)(struct tty_struct *tty, int timeout);
       void (*send_xchar)(struct tty_struct *tty, char ch);
       int (*read_proc)(char *page, char **start, off_t off,
                       int count, int *eof, void *

待续。。。。。。。。。。。。。。
阅读(5112) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~