搜索

Google
 

星期日, 六月 10, 2007

rs422在linux跟我开了个玩笑

很久没有更新blog了,就跟抽烟一样,如果长时间不抽的话也就不想抽了,看来这是一个不好的现象,不过长时间的沉默也说明了自己这段时间在技术没有什么积累。罢了,今天的blog就记录这段时间来比较郁闷的事情。

这个项目的开发就像当年初中的长跑一样,前几圈劲头十足信心满怀,接下来的情况我想大部分人跟我一样,慢慢地感觉有点累,再后来压力越来越大,越来越感觉大脑缺氧呼吸困难,到后来已经身心疲累。人这一生从出生开始就背负了责任,要么为责任活着要么超越责任为理想活着,所以有人活得辛苦而无趣,有人轻松而超然。有点跑题了,还是说一下前几天遇到的技术问题。

客户方有一外部设备是N年前购买的,一直用于原来的系统中,这次需要在本项目中使用此外设,关于这个外设的文档早已缺失,唯一可以参考的就是一个windows下的动态链接库源代码(不过这也是最重要的参考资料,候大侠一本书上不是也说“源码面前了无秘密”嘛),于是参考此代码写了一个windows下的测试程序很顺利地测试通过,测试代码如下:

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int
main(int argc, char **argv)
{
HANDLE h_com;
unsigned char addr = 0x01;
unsigned char buf[10];
int i;
DCB dcb;
COMMTIMEOUTS timeouts;
DWORD n;

if (argc != 2) {
printf("%s <comdev>\n", argv[0]);
return -1;
}

h_com = CreateFile(argv[1], GENERIC_READ | GENERIC_WRITE,
0, NULL, OPEN_EXISTING, 0, NULL);
if (h_com == INVALID_HANDLE_VALUE) {
printf("Initialize COM device failed\n");
return -1;
}

GetCommState(h_com, &dcb);
dcb.BaudRate = 9600;
dcb.ByteSize = 8;
dcb.Parity = MARKPARITY;
dcb.StopBits = ONESTOPBIT;
SetCommState(h_com, &dcb);

GetCommTimeouts(h_com, &timeouts);
timeouts.ReadTotalTimeoutConstant = 10;
SetCommTimeouts(h_com, &timeouts);

WriteFile(h_com, &addr, sizeof(addr), &n, NULL);

Sleep(1);
dcb.Parity = SPACEPARITY;
SetCommState(h_com, &dcb);
memcpy(buf, "\x02\x85\x83\04", 4);
WriteFile(h_com, buf, 4, &n, NULL);

Sleep(2);
ReadFile(h_com, buf, sizeof(buf), &n, NULL);
printf("read %d bytes\n", n);

for (i = 0; i < n; ++i)
printf("%02x ", buf[i]);
printf("\n");

return 0;
}

由此代码可以看出通信过程大概是这样的:将串口设置为8M1发送地址,然后将串口设置更改为8S1发送请求数据,再然后接收数据,看起来还是很简单的。接下来很快地写好了linux下的测试程序,源代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <termios.h>

int
main(int argc, char **argv)
{
if (argc != 2) {
printf("Usage: %s <comdev>\n", argv[0]);
return -1;
}

int fd = open(argv[1], O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1) {
printf("Initialize COM device failed.\n");
return -1;
}

struct termios options;
if (tcgetattr(fd, &options) == -1)
return -1;

cfsetispeed(&options, B9600);
cfsetospeed(&options, B9600);

options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8 | CLOCAL | CREAD | CMSPAR | PARODD;
options.c_cflag &= CSTOPB;
options.c_iflag |= INPCK;
options.c_iflag &= ~(IXON | IXOFF | IXANY);
options.c_oflag &= ~OPOST;
options.c_lflag &= ~(ECHO | ECHOE | ICANON | ISIG);
tcflush(fd, TCIFLUSH);
printf("tcsetattr result: %d\n", tcsetattr(fd, TCSANOW, &options));

char addr = 0x01;
if (write(fd, &addr, 1) == -1)
goto failed;

usleep(1000);

options.c_cflag &= ~PARODD;
printf("tcsetattr result: %d\n", tcsetattr(fd, TCSADRAIN, &options));

usleep(1000);

unsigned char buf[10];
memcpy(buf, "\x02\x85\x83\x04", 4);

if (write(fd, buf, 4) == -1)
goto failed;

usleep(10000);

ssize_t read_n;
int i;
read_n = read(fd, buf, sizeof(buf));
printf("result: %d\n", read_n);
for (i = 0; i < read_n; ++i)
printf("%02X ", buf[i]);
printf("\n");

close(fd);
return 0;

failed:
close(fd);
return -1;
}

虽然Mark Parity和Space Parity在Posix中并未定义,但是在linux下通过CMSPAR仍然可以实现,tcsetattr的man pages可以看到对CMSPAR的描述(),可是反复尝试仍然有问题,后来一个完全不懂linux开发的同事问了我设置串口各行代码的意思,并通过tcsetattr的man pages,一个一个地组合这些标记,居然成功了!结果出人意料,且看如下代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <termios.h>

int
main(int argc, char **argv)
{
if (argc != 2) {
printf("Usage: %s <comdev>\n", argv[0]);
return -1;
}

int fd = open(argv[1], O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1) {
printf("Initialize COM device failed.\n");
return -1;
}

struct termios options;

memset(&options, 0, sizeof(options));

cfsetispeed(&options, B9600);
cfsetospeed(&options, B9600);

options.c_cflag = CS8 | CLOCAL | CREAD | CSTOPB | CMSPAR;
options.c_iflag = INPCK;
options.c_oflag = 0;
options.c_lflag = 0;
tcflush(fd, TCIFLUSH);
printf("tcsetattr result: %d\n", tcsetattr(fd, TCSANOW, &options));

char addr = 0x01;
if (write(fd, &addr, 1) == -1)
goto failed;

usleep(1000);

options.c_cflag |= PARENB | PARODD;
printf("tcsetattr result: %d\n", tcsetattr(fd, TCSADRAIN, &options));

usleep(1000);

unsigned char buf[10];
memcpy(buf, "\x02\x85\x83\x04", 4);

if (write(fd, buf, 4) == -1)
goto failed;

usleep(10000);

ssize_t read_n;
int i;
read_n = read(fd, buf, sizeof(buf));
printf("result: %d\n", read_n);
for (i = 0; i < read_n; ++i)
printf("%02X ", buf[i]);
printf("\n");

close(fd);
return 0;

failed:
close(fd);
return -1;
}

想不到抛开CMSPAR不算8M1看起来是8N2,而8S1则看起来是8O2。。。

没有评论: