Ubuntu 17.04編譯串口發送16進制數據包報了個神奇的錯誤

調試系統環境: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;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章