调试系统环境:Ubuntu 17.04 + Clion 17.1
今天在调试Ubuntu系统通过串口发送16进制数据时,我发现了一个很神奇的问题现象。
先看看下面的代码:
//char *data = "hello world dhs!";
char data[9] = {0x11,0x22,0x33,0x11,0x22,0x33,0x11,0x22,0x33};
//data[0] = 0xAA;
//int datalen = strlen(data);
//send data
//while (1)
{
for (int i = 0; i < 100; i++)
{
SendLen = PortSend(fd, data, 9);
if (SendLen > 0) {
printf("No %d send %d data.\n", i, SendLen);
} else {
printf("Error: send failed.\n");
}
sleep(1);
}
}
然后在Clion 中进行编译,发现编译很完美,并能够通过 xgcom(一款Linux下的带GUI的串口调试助手)接收到16进制数据。
再看看以下代码:
//char *data = "hello world dhs!";
char data[2] = {0x11,0x22,0x33,0x11,0x22,0x33,0x11,0x22,0x88};
//data[0] = 0xAA;
//int datalen = strlen(data);
//send data
//while (1)
{
for (int i = 0; i < 100; i++)
{
SendLen = PortSend(fd, data, 2);
if (SendLen > 0) {
printf("No %d send %d data.\n", i, SendLen);
} else {
printf("Error: send failed.\n");
}
sleep(1);
}
}
然后再Clion中编译源代码时,就发现报错了,错误信息如下:
/home/dhs/桌面/serial_dhs/serail (cpp)/serial.cpp:305:66: error: narrowing conversion of ‘136’ from ‘int’ to ‘char’ inside { } [-Wnarrowing]
char data[9] = {0x11,0x22,0x33,0x11,0x22,0x33,0x11,0x22,0x88};
^
CMakeFiles/serail.dir/build.make:62: recipe for target 'CMakeFiles/serail.dir/serial.cpp.o' failed
make[3]: *** [CMakeFiles/serail.dir/serial.cpp.o] Error 1
CMakeFiles/Makefile2:67: recipe for target 'CMakeFiles/serail.dir/all' failed
make[2]: *** [CMakeFiles/serail.dir/all] Error 2
CMakeFiles/Makefile2:79: recipe for target 'CMakeFiles/serail.dir/rule' failed
make[1]: *** [CMakeFiles/serail.dir/rule] Error 2
Makefile:118: recipe for target 'serail' failed
make: *** [serail] Error 2
经过我多次测试,终于发现了一个很神奇的错误现象。
当发送的16进制数据包中有数据大于255时,便会编译报错。
因此,我认为在串口发送16进制数据时可能使用了int数据类型作转换,也就是说在同类的程序中也很有可能会发生这种错误。
所以,我在此将其提出,一是想给自己一个警示,同时也是希望能够解决它的数据传输问题。(问题是我需要收发16进制数据形式的大小超过255。)
最后,我把整个的代码贴出来如下(主要是在网上找改的程序):
#include <stdio.h> // printf
#include <fcntl.h> // open
#include <string.h> // bzero
#include <stdlib.h> // exit
#include <sys/times.h> // times
#include <sys/types.h> // pid_t
#include <termios.h> //termios, tcgetattr(), tcsetattr()
#include <unistd.h>
#include <sys/ioctl.h> // ioctl
#define TTY_DEV "/dev/ttyUSB" //端口路径
#define TIMEOUT_SEC(buflen,baud) (buflen*20/baud+2) //接收超时
#define TIMEOUT_USEC 0
//串口结构
typedef struct{
char prompt; //prompt after reciving data
int baudrate; //baudrate
char databit; //data bits, 5, 6, 7, 8
char debug; //debug mode, 0: none, 1: debug
char echo; //echo mode, 0: none, 1: echo
char fctl; //flow control, 0: none, 1: hardware, 2: software
char tty; //tty: 0, 1, 2, 3, 4, 5, 6, 7
char parity; //parity 0: none, 1: odd, 2: even
char stopbit; //stop bits, 1, 2
const int reserved; //reserved, must be zero
}portinfo_t;
typedef portinfo_t *pportinfo_t;
/**
* 打开串口,返回文件描述符
* pportinfo: 待设置的串口信息
*/
int PortOpen(pportinfo_t pportinfo);
/**
* 设置串口
* fdcom: 串口文件描述符, pportinfo: 待设置的串口信息
*/
int PortSet(int fdcom, const pportinfo_t pportinfo);
/**
* 关闭串口
* fdcom:串口文件描述符
*/
void PortClose(int fdcom);
/**
* 发送数据
* fdcom:串口描述符, data:待发送数据, datalen:数据长度
* 返回实际发送长度
*/
int PortSend(int fdcom, char *data, int datalen);
/**
* 接收数据
* fdcom:串口描述符, data:接收缓冲区, datalen:接收长度, baudrate:波特率
* 返回实际读入的长度
*/
int PortRecv(int fdcom, char *data, int datalen, int baudrate);
/*******************************************
* 获得端口名称
********************************************/
char *get_ptty(pportinfo_t pportinfo)
{
char *ptty;
switch(pportinfo->tty){
case '0':{
ptty = TTY_DEV"0";
}break;
case '1':{
ptty = TTY_DEV"1";
}break;
case '2':{
ptty = TTY_DEV"2";
}break;
}
return(ptty);
}
/*******************************************
* 波特率转换函数(请确认是否正确)
********************************************/
int convbaud(unsigned long int baudrate)
{
switch(baudrate){
case 2400:
return B2400;
case 4800:
return B4800;
case 9600:
return B9600;
case 19200:
return B19200;
case 38400:
return B38400;
case 57600:
return B57600;
case 115200:
return B115200;
default:
return B9600;
}
}
/*******************************************
* Setup comm attr
* fdcom: 串口文件描述符,pportinfo: 待设置的端口信息(请确认)
*
********************************************/
int PortSet(int fdcom, const pportinfo_t pportinfo)
{
struct termios termios_old, termios_new;
int baudrate, tmp;
char databit, stopbit, parity, fctl;
bzero(&termios_old, sizeof(termios_old));
bzero(&termios_new, sizeof(termios_new));
cfmakeraw(&termios_new);
tcgetattr(fdcom, &termios_old); //get the serial port attributions
/*------------设置端口属性----------------*/
//baudrates
baudrate = convbaud(pportinfo->baudrate);
cfsetispeed(&termios_new, baudrate); //填入串口输入端的波特率
cfsetospeed(&termios_new, baudrate); //填入串口输出端的波特率
termios_new.c_cflag |= CLOCAL; //控制模式,保证程序不会成为端口的占有者
termios_new.c_cflag |= CREAD; //控制模式,使能端口读取输入的数据
// 控制模式:flow control
fctl = pportinfo-> fctl;
switch(fctl){
case '0':{
termios_new.c_cflag &= ~CRTSCTS; //no flow control
}break;
case '1':{
termios_new.c_cflag |= CRTSCTS; //hardware flow control
}break;
case '2':{
termios_new.c_iflag |= IXON | IXOFF |IXANY; //software flow control
}break;
}
//控制模式,data bits
termios_new.c_cflag &= ~CSIZE; //控制模式,屏蔽字符大小位
databit = pportinfo -> databit;
switch(databit){
case '5':
termios_new.c_cflag |= CS5;
case '6':
termios_new.c_cflag |= CS6;
case '7':
termios_new.c_cflag |= CS7;
default:
termios_new.c_cflag |= CS8;
}
//控制模式 parity check
parity = pportinfo -> parity;
switch(parity){
case '0':{
termios_new.c_cflag &= ~PARENB; //no parity check
}break;
case '1':{
termios_new.c_cflag |= PARENB; //odd check
termios_new.c_cflag &= ~PARODD;
}break;
case '2':{
termios_new.c_cflag |= PARENB; //even check
termios_new.c_cflag |= PARODD;
}break;
}
//控制模式,stop bits
stopbit = pportinfo -> stopbit;
if(stopbit == '2'){
termios_new.c_cflag |= CSTOPB; //2 stop bits
}
else{
termios_new.c_cflag &= ~CSTOPB; //1 stop bits
}
//other attributions default
termios_new.c_oflag &= ~OPOST; //输出模式, 原始数据输出
termios_new.c_cc[VMIN] = 0; //控制字符, 所要读取字符的最小数量
termios_new.c_cc[VTIME] = 1; //控制字符, 读取第一个字符的等待时间 unit: (1/10)second
tcflush(fdcom, TCIFLUSH); //溢出的数据可以接收,但不读
tmp = tcsetattr(fdcom, TCSANOW, &termios_new); //设置新属性,TCSANOW:所有改变立即生效
//tcgetattr(fdcom, &termios_old);
return(tmp);
}
/*******************************************
* Open serial port
* tty: 端口号 ttyS0, ttyS1, ....
* 返回值为串口文件描述符
********************************************/
int PortOpen(pportinfo_t pportinfo)
{
int fdcom; //串口文件描述符
char *ptty;
ptty = get_ptty(pportinfo);
//fdcom = open(ptty, O_RDWR | O_NOCTTY | O_NONBLOCK | O_NDELAY);
fdcom = open(ptty, O_RDWR | O_NOCTTY | O_NONBLOCK);
return (fdcom);
}
/*******************************************
* Close serial port
********************************************/
void PortClose(int fdcom)
{
close(fdcom);
}
/********************************************
* send data
* fdcom: 串口描述符,data: 待发送数据,datalen: 数据长度
* 返回实际发送长度
*********************************************/
int PortSend(int fdcom, char *data, int datalen)
{
int len = 0;
len = write(fdcom, data, datalen); //实际写入的长度
if(len == datalen){
return (len);
}
else{
tcflush(fdcom, TCOFLUSH);
return -1;
}
}
/*******************************************
* receive data
* 返回实际读入的字节数
*
********************************************/
int PortRecv(int fdcom, char *data, int datalen, int baudrate)
{
int readlen, fs_sel;
fd_set fs_read;
struct timeval tv_timeout;
FD_ZERO(&fs_read);
FD_SET(fdcom, &fs_read);
tv_timeout.tv_sec = TIMEOUT_SEC(datalen, baudrate);
tv_timeout.tv_usec = TIMEOUT_USEC;
fs_sel = select(fdcom + 1, &fs_read, NULL, NULL, &tv_timeout);
if(fs_sel){
readlen = read(fdcom, data, datalen);
return(readlen);
}
else{
return(-1);
}
//return (readlen);
}
//*************************Test*********************************
int main(int argc, char *argv[]) {
int fd = -1, SendLen = 0, RecvLen = 0;
struct termios termios_cur;
char RecvBuf[10] = {0};
portinfo_t portinfo = {
'0', // print prompt after receiving
9600, // baudrate: 9600
'8', // databit: 8
'0', // debug: off
'1', // echo: on
'1', // flow control: hardware
'0', // default tty: COM1
'0', // parity: none
'1', // stopbit: 1
0 // reserved
};
/*
if(argc != 2){
printf("Usage: <type 0 -- send 1 -- receive>\n");
printf(" eg:");
printf(" MyPort 0");
exit(-1);
}
*/
fd = PortOpen(&portinfo);
if (fd < 0) {
printf("Error: open serial port error.\n");
exit(1);
}
PortSet(fd, &portinfo);
//char *data = "hello world dhs!";
char data[9] = {0x11,0x22,0x33,0x11,0x22,0x33,0x11,0x22,0x88};
//data[0] = 0xAA;
int datalen = strlen(data);
//send data
//while (1)
{
for (int i = 0; i < 100; i++)
{
SendLen = PortSend(fd, data, 9);
if (SendLen > 0) {
printf("No %d send %d data.\n", i, SendLen);
} else {
printf("Error: send failed.\n");
}
sleep(1);
}
}
PortClose(fd);
/**
for(;;)
{
RecvLen = PortRecv(fd, RecvBuf, 10, portinfo.baudrate);
if(RecvLen>0){
for(i=0; i<RecvLen; i++){
printf("Receive data No %d is %x.\n", i, RecvBuf[i]);
}
printf("Total frame length is %d.\n", RecvLen);
}
else{
printf("E
rror: receive error.\n");
}
sleep(2);
}
*/
return 0;
}