由于项目需要 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 *
待续。。。。。。。。。。。。。。
阅读(2247) | 评论(0) | 转发(0) |