入职新公司以来,我开发的几乎每个Linux程序都要用到串口。我开发第一个程序的时候,在网上阅读了大量的代码,并根据这些代码写了一个通用的初始化函数,以后的程序都调用这个函数进行初始化。在这期间,还发现了网上流传的代码存在的一个问题,详见《串口通信中的INPCK和ISTRIP标志位》。然而近期有人在使用我的程序时,还是发现了一个问题,所有流经我的程序的数据中的0x0D全部变成了0x0A.
网上一搜就知道了原因,原来是我初始化的时候没有关闭ICRNL标志位。我猜测Linux的串口架构沿用自Unix系统,那个年代串口主要用于远程登录和传输文本,设计上主要考虑的是方便人机交互;而现在在工业自动化领域,串口主要用来传输二进制数据,更希望数据准确传输。
这个问题也说明,网上找的代码,一定要自己研究明白再据为己有。现在有很多软件开发人员,随口就是去Github上搜代码,这样做未必是好事。
如下是我的初始化函数,已经修改了我上面提到的0x0D变成0x0A的问题,但不知道是否还隐藏着其他问题。供大家参考。
/*初始化串口*/
int init_serial(unsigned char com_sn, unsigned char spd, unsigned char data_bit,
unsigned char parity, unsigned char stop_bit)
{
int com_fd; /*串口fd*/
char com_device_name[16]; /*串口设备名*/
struct termios options;
sprintf(com_device_name, "/dev/ttyS%d", com_sn);
#ifdef RS485_NONBLOCK
com_fd = open(com_device_name, O_RDWR | O_NOCTTY | O_NDELAY);
#else
com_fd = open(com_device_name, O_RDWR | O_NOCTTY);
#endif
if (com_fd < 0)
{
printf("Open the serial port %d error!\n", com_sn);
control_led(LED_ERR, ON);
return -1;
}
tcgetattr(com_fd, &options);
/*设置串口波特率*/
speed_t speed = B0;
switch (spd)
{
case 0x96:
speed = B9600;
break;
case 0x38:
speed = B38400;
break;
}
cfsetispeed(&options, speed);
cfsetospeed(&options, speed);
/*
* Enable the receiver and set local mode
*/
options.c_cflag |= (CLOCAL | CREAD);
switch (parity)
{
case 'N':
options.c_cflag &= ~PARENB; //关闭校验
options.c_iflag &= ~INPCK;
break;
case 'E':
options.c_cflag |= PARENB; //使能校验
options.c_iflag |= INPCK;
options.c_cflag &= ~PARODD; //关闭奇校验,也就相当于打开了偶校验
break;
case 'O':
options.c_cflag |= PARENB; //使能校验
options.c_iflag |= INPCK;
options.c_cflag |= PARODD;
break;
default:
/*do nothing*/;
}
if (stop_bit == 1)
{
options.c_cflag &= ~CSTOPB; //关闭第二个校验位,也就相当于只打开一个校验位
}
options.c_cflag &= ~CSIZE;
if (data_bit == 8)
{
options.c_cflag |= CS8; //8位数据位
}
/*
* Disable hardware flow control
*/
options.c_cflag &= ~CRTSCTS;
/*
* Choosing raw input
*/
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
/*
* Disable software flow control
*/
options.c_iflag &= ~(IXON | IXOFF | IXANY | ICRNL);
/*
* Choosing raw output
*/
options.c_oflag &= ~OPOST;
options.c_cc[VMIN] = 0;
options.c_cc[VTIME] = 1;
tcsetattr(com_fd, TCSANOW, &options);
return com_fd;
}
没有评论:
发表评论