2012-09-15 50 views
12

我閱讀後面的鏈接和其他來源,但沒有找到我的問題的答案。如何在C程序中通過串行終端讀取二進制數據?

Binary data over serial terminal

Data gets corrupted during transmission over the serial port

我通過串口與我的嵌入式設備的通信。默認情況下,嵌入式Linux使用此端口作爲終端。但我想通過端口傳輸二進制數據(服務數據包)。我的/ etc/inittab文件有一個 「格蒂」 電話: 控制檯::重生:/ sbin目錄/蓋蒂115200 ttyS0來

我也有串/ etc/passwd文件,其中 「admin」 用戶推出我的「CLI 「應用程序後登錄: 管理:8MT/Jtxcyg8AY:1000:0:管理員:/ tmp目錄:/ tmp目錄/ CLI

我的默認ttyS0來設置之前運行的程序是:

​​

所以,在我的cli程序中,我執行以下操作:

main() 
{ 
    ... 
    system("stty erase ^H); 
    system("stty -F /dev/ttyS0 -icrnl -ixon -ixoff -opost -isig -icanon -echo"); // enter in non-canonical (raw) mode 

    // What function do I need to use here to retrieve binary data (also symbols that > 0x7F) from /dev/ttyS0? 

    system("stty -F /dev/ttyS0 icrnl ixon ixoff opost isig icanon echo"); // go back to canonical mode 

    ... 

    exit(0); 
} 

我試圖讀取()函數(與unsigned char緩衝區)來獲取二進制數據,但未能接收到正確的數據。我也初步打開/ dev/ttyS0來獲取file_descriptor &使用read()函數。

我的程序發送3個字節:0xAA,0x02,0xFE。 但在系統日誌中,我總是看到該設備接收不正確的符號:0x98,0xE6,0x18。

這是怎麼回事?如何獲取正確的二進制數據?

我正在測試的整個代碼。

#include "cli.h" 
#include "glb_vars.h" 

/****************************************** 
*** Definitions 
******************************************/ 
#define APPLICATION_NAME "cli" 
#define SERIALPORT_IS_CONSOLE 

/****************************************** 
*** Constants 
******************************************/ 
const char dev_name[] = DEV_NAME; 
const char lineminstr[] = "\t--------------------------------------------------------\n"; 

/****************************************** 
*** Internal Function Declarations 
******************************************/ 
CLI_RETVAL cliInit(void); 
CLI_RETVAL cliClose(void); 
void cliWorkLoop(Term_callback_t **term); 

/****************************************** 
*** External Function Declarations 
******************************************/ 
extern void Vectors_init(Term_callback_t **vec); 
extern char** Menu_completion(const char * text, int start, int end); 


/****************************************************************************/ 
int file_descr, max_fd; 
struct termios tty, orig_tty; 
fd_set work_set; 

/****************************************************************************/ 
/*! 
* \brief Init cli 
* 
* \return success or failure 
* \retval CLI_SUCCESS, CLI_FAILURE 
* 
* \ingroup CLI 
*/ 
/****************************************************************************/ 
CLI_RETVAL cliInit(void) 
{ 
    long spd; 

    signal(SIGINT, SIG_IGN); 
    signal(SIGHUP, SIG_IGN); 
    signal(SIGTERM, SIG_IGN); 
    signal(SIGABRT, SIG_IGN); 
    signal(SIGQUIT, SIG_IGN); 
    signal(SIGILL, SIG_IGN); 

// system("stty -F /dev/ttyS0 -icrnl -ixon -ixoff -opost -isig -icanon -echo"); // enter in non-canonical mode 
// system("stty -a"); 
// sleep(1); 

#ifdef SERIALPORT_IS_CONSOLE 
    file_descr = STDIN_FILENO; 
    SYS_LOG_DEBUG("SERIALPORT IS CONSOLE"); 
#else 
    SYS_LOG_DEBUG("SERIALPORT IS NOT CONSOLE"); 
    file_descr = open("/dev/ttyS0", O_RDWR | O_ASYNC | O_NDELAY); 
    if (file_descr == -1) { 
     // Could not open the port 
     perror("unable to open /dev/ttyS0"); 
     exit(1); 
    } 
#endif 

    if(tcgetattr(file_descr, &tty) < 0) 
    { 
     perror("unable to get tty attributes"); 
     exit(1); 
    } 
    // backup tty, make it raw and apply changes 
    orig_tty = tty; 

    spd = B115200; 
    cfsetospeed(&tty, (speed_t)spd); 
    cfsetispeed(&tty, (speed_t)spd); 

    cfmakeraw(&tty); 

    tty.c_cc[VMIN] = 1; 
    tty.c_cc[VTIME] = 10; 

    tty.c_cflag &= ~CSTOPB; 
    tty.c_cflag &= ~CRTSCTS; /* no HW flow control? */ 
    tty.c_cflag |= CLOCAL | CREAD; 
    tcsetattr(file_descr, TCSANOW, &tty); 

// // update local mode flags 
// tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); 
//// // renew control mode flags 
//// tty.c_cflag &= ~(CBAUD | CSIZE | CSTOPB | PARENB | PARODD); 
//// tty.c_cflag |= (BAUD | DATABITS | STOPBITS | PARITYON | PARITY); 
// // select 'raw' output mode 
// tty.c_oflag &= ~OPOST; 
// // disable mapping for input mode 
// tty.c_iflag &= ~(INLCR | ICRNL); 
// 
// 
// if(tcsetattr(file_descr, TCSAFLUSH, &tty) < 0) 
// { 
//  perror("unable to set tty attributes"); 
//  exit(1); 
// } 
// 
    // Setup fd_set 
    FD_ZERO(&work_set); 
    FD_SET(file_descr, &work_set); 
    max_fd = file_descr + 1; 

    /* Readline lib init */ 
    // Define application name for readline library 
    rl_readline_name = APPLICATION_NAME; 
    // Update Pointer to alternative function to create matches. 
    rl_attempted_completion_function = Menu_completion; 
    // Start readline with reading /etc/inputrc file 
    using_history(); 
    stifle_history(CLI_MAX_HISTORY_SIZE); 


    // Some other initialization code 
    // ... 
    // ... 

    return CLI_SUCCESS; 
} 

/****************************************************************************/ 
/*! 
* \brief Close cli 
* 
* \return success or failure 
* \retval CLI_SUCCESS, CLI_FAILURE 
* 
* \ingroup CLI 
*/ 
/****************************************************************************/ 
CLI_RETVAL cliClose(void) 
{ 

// system("stty -F /dev/ttyS0 icrnl ixon ixoff opost isig icanon echo"); // enter in canonical mode 

    tcsetattr(file_descr, TCSANOW, &orig_tty); 
// if(tcsetattr(file_descr, TCSAFLUSH, &orig_tty) < 0) 
// { 
//  perror("unable to set orig_tty attributes"); 
//  exit(1); 
// } 
    close(file_descr); 

    return CLI_SUCCESS; 
} 


/****************************************************************************/ 
/*! 
* \brief Main cli processing loop 
* 
* \no return 
* 
* \ingroup CLI 
*/ 
/****************************************************************************/ 
void cliWorkLoop(Term_callback_t **term) 
{ 
    Term_callback_t *cur_term; 
    int8 *commandString; 
    uint8 ret = CLI_REFRESH, no_prompt; 

    char prompt_str[20]; 

    while (1) { 

     cur_term = *term; 
     global_cmd_compl_pointer = cur_term->cmd_list; 

     commandString = NULL; 
     sprintf(prompt_str, "%s:~> ", dev_name); 

     if(ret == CLI_REFRESH) { 
      CLEAR_SCR(); 
      if(cur_term->out != NULL) { 
       cur_term->out(term, commandString, &ret); 
       no_prompt = ret; 
      } 
      CURSOR_DOWN(); 
     } 

     int n; 
     struct timeval timeout; 
     uint8 tmpBuf[32]; 

     while (1) 
     { 
      // Setup Timeout 
      timeout.tv_sec = 60; 
      timeout.tv_usec = 0; 
      // Wait for new connections 
      n = select(max_fd, &work_set, NULL, NULL, &timeout); 
      if (n < 0) 
      { 
       perror("select #2 failed"); 
       break; 
      } 
      if (n > 0) 
      { 
       /* У нас есть ввод */ 
       if (FD_ISSET(file_descr, &work_set)) 
       { 
        if (read(file_descr, tmpBuf, 10) < 0) { 
         perror("cannot read"); 
         exit(1); 
        } 
        else 
        { 
         SYS_LOG_DEBUG("READ first 4 chars: 0x%X,0x%X,0x%X,0x%X", tmpBuf[0], tmpBuf[1], tmpBuf[2], tmpBuf[3]); 
        } 
       } 
       break; 
      } 
     } 
// 
// 
//  n = read(file_descr, tmpBuf, 5); 
//  if (n > 0) { 
//   unsigned char *p = tmpBuf; 
// 
//   while (n-- > 0) 
//    printf(" 0x%x", *p++); 
//   printf("\r\n"); 
//  } else { 
//   printf("failed to read: %d\r\n", n); 
//  } 
// 
// 
     exit(0); 
    } 

    CLEAR_SCR(); 
    return; 
} 


/****************************************************************************/ 
/*! 
* \brief Main cli function 
* 
* \param[in]  argc - argument number. 
* \param[in,out] argv - argument values entered by user. 
* 
* \return success or failure 
* \retval EXIT_SUCCESS, EXIT_FAILURE 
* 
* 
* \ingroup CLI 
*/ 
/****************************************************************************/ 
int main(int argc, char *argv[]) 
{ 
    Term_callback_t *term; 
    char logname[16]; 
    FILE *fp; 


    /* Set mask for file operation */ 
    umask(0); 

    system("stty erase ^H"); 
    openlog("cli", LOG_CONS, LOG_USER); 

    /* Write cli start log */ 
    syslog(LOG_NOTICE, "Console startup. Software version: %s", VERSION); 
    /* Find login name */ 
    strcpy(logname, "noname"); 
    if ((fp = popen("whoami", "r")) == NULL) 
    { 
     SYS_LOG_ERR("Can't open process for \"whoami\" command."); 
    } else 
    { 
     fgets(logname, 16, fp); 
     pclose(fp); 
    } 
    SYS_LOG_INFO("Console is entered by \"%s\".", logname); //getenv("USER") 

    /* Console initialization */ 
    if (cliInit() != CLI_SUCCESS) { 
     SYS_LOG_CRIT("CLI init failed"); 
     return EXIT_FAILURE; 
    } 

    Vectors_init(&term); 

    /* Console work loop */ 
    cliWorkLoop(&term); 
    cliClose(); 

    /* Exiting from cli */ 
    SYS_LOG_INFO("\"%s\" exited from console.", logname); 

    return EXIT_SUCCESS; 
} 
+0

您的串口設置爲規範輸入,即ASCII文本行。切換到*原始模式*或在兩端轉換二進制數據。查看'uuencode'和'uudecode'以通過ASCII介質傳輸二進制數據。這是以純文本電子郵件和USENET過帳發送二進制數據的傳統方式。發送二進制或「特殊」字符的另一種方法是使用「轉義」字符來前綴「特殊」字符,並將該字節標準化爲有效的ASCII字符。 BTW'-parenb -parodd cs8'將產生11位字符幀。使用8位數據字符時,「無奇偶校驗」是典型的。 – sawdust

+0

謝謝你的木屑!你是對的,第二個變種是一個更方便的使用方法。但我有一箇舊的軟件與以前的嵌入式設備進行通信,並不支持它。我們很可能不得不改變它來支持一個新的。即使如此,爲什麼在使用read()函數進入原始模式時(請參閱提及的代碼),我無法獲得特殊字符? – Bakir

回答

9
system("stty erase ^H); 
system("stty -F /dev/ttyS0 -icrnl -ixon -ixoff -opost -isig -icanon -echo"); // enter into non-canonical (raw) mode 

這將是不充分的代碼(和每POSIX約定編碼實踐差)把串行端口成原始或非規範模式。
C和Linux中最簡單的方法是使用函數cfmakeraw(),該函數在GNU libc和uClibc庫中均可用。使用手冊頁獲取有關termioscfmakeraw()修改的結構成員的詳細信息。
請注意cfmakeraw()將設置數據長度爲8位的原始模式下的串行端口和奇偶校驗位,對於10位的總字符幀(假設一個停止位)。

優選的方法是保留的termios 的副本 stucture(用於恢復對程序退出),並且僅修改所需的標誌位(不是寫的完整結構成員)。

,關於我的ARM SoC的工作

修訂

代碼是:

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

#define SERIALPORT_IS_CONSOLE 

main() 
{ 
    struct termios tty; 
    struct termios savetty; 
    speed_t  spd; 
    unsigned int sfd; 
    unsigned char buf[80]; 
    int  reqlen = 79; 
    int  rc; 
    int  rdlen; 
    int  pau = 0; 

#ifdef SERIALPORT_IS_CONSOLE 
    sfd = STDIN_FILENO; 
#else 
    sfd = open("/dev/ttyS1", O_RDWR | O_NOCTTY); 
#endif 
    if (sfd < 0) { 
     syslog(LOG_DEBUG, "failed to open: %d, %s", sfd, strerror(errno)); 
     exit (-1); 
    } 
    syslog(LOG_DEBUG, "opened sfd=%d for reading", sfd); 

    rc = tcgetattr(sfd, &tty); 
    if (rc < 0) { 
     syslog(LOG_DEBUG, "failed to get attr: %d, %s", rc, strerror(errno)); 
     exit (-2); 
    } 
    savetty = tty; /* preserve original settings for restoration */ 

    spd = B115200; 
    cfsetospeed(&tty, (speed_t)spd); 
    cfsetispeed(&tty, (speed_t)spd); 

    cfmakeraw(&tty); 

    tty.c_cc[VMIN] = 1; 
    tty.c_cc[VTIME] = 10; 

    tty.c_cflag &= ~CSTOPB; 
    tty.c_cflag &= ~CRTSCTS; /* no HW flow control? */ 
    tty.c_cflag |= CLOCAL | CREAD; 
    rc = tcsetattr(sfd, TCSANOW, &tty); 
    if (rc < 0) { 
     syslog(LOG_DEBUG, "failed to set attr: %d, %s", rc, strerror(errno)); 
     exit (-3); 
    } 

    do { 
     unsigned char *p = buf; 

     rdlen = read(sfd, buf, reqlen); 
     if (rdlen > 0) { 
      if (*p == '\r') 
       pau = 1; 
      syslog(LOG_DEBUG, "read: %d, 0x%x 0x%x 0x%x", \ 
        rdlen, *p, *(p + 1), *(p + 2)); 
     } else { 
      syslog(LOG_DEBUG, "failed to read: %d, %s", rdlen, strerror(errno)); 
     } 
    } while (!pau); 

    tcsetattr(sfd, TCSANOW, &savetty); 
    close(sfd); 
    exit (0); 
} 

編譯程序被加載在目標板上執行&。

從串口通信鏈路的主機側,文件seq.bin具有以下內容被髮送:

$ od -t x1 seq.bin 
0000000 aa 02 fe 
0000003 

然後「ABC」被鍵入的主機上(其在運行minicom終端仿真器程序) ,然後回車。 該程序終止於目標,然後系統日誌被檢查:

# tail /var/log/messages               
Sep xx xx:xx:42 atmel_soc user.info kernel: EXT3 FS on nvsram, internal journal 
Sep xx xx:xx:42 atmel_soc user.info kernel: EXT3-fs: mounted filesystem with or. 
Sep xx xx:xx:42 atmel_soc user.info kernel: kjournald starting. Commit intervas 
Sep xx xx:xx:18 atmel_soc auth.info login[431]: root login on 'ttyS0'   
Sep xx xx:xx:04 atmel_soc user.debug syslog: opened sfd=0 for reading   
Sep xx xx:xx:14 atmel_soc user.debug syslog: read: 3, 0xaa 0x2 0xfe    
Sep xx xx:xx:50 atmel_soc user.debug syslog: read: 1, 0x41 0x2 0xfe    
Sep xx xx:xx:51 atmel_soc user.debug syslog: read: 1, 0x42 0x2 0xfe    
Sep xx xx:xx:51 atmel_soc user.debug syslog: read: 1, 0x43 0x2 0xfe    
Sep xx xx:xx:52 atmel_soc user.debug syslog: read: 1, 0xd 0x2 0xfe    
# 

二進制數據已經被接收完整的。

請注意,由於這是原始模式並且鍵入的字符輸入相對較慢,因此read()會返回「部分」數據,用戶程序將負責將數據緩存/彙編爲完整的「消息」。如果消息的長度是固定的,則c_cc[VMIN]成員可以將設置爲消息長度。但是當幀同步丟失時,請注意消息幀問題和複雜情況!

+0

我已經使用這個功能,並得到了相同的結果:) – Bakir

+0

但我會再試一次。 – Bakir

+0

所以,我檢查了你的代碼。不幸的是我得到了相同的圖片。 – Bakir

相關問題