2012-09-15 17 views
12

मैं पीछा लिंक और अन्य स्रोतों पढ़ते हैं, लेकिन मेरे सवाल के लिए इस सवाल का जवाब नहीं मिला।सी प्रोग्राम में सीरियल टर्मिनल पर बाइनरी डेटा कैसे पढ़ा जाए?

Binary data over serial terminal

Data gets corrupted during transmission over the serial port

मैं एक सीरियल पोर्ट के माध्यम से अपने एम्बेडेड उपकरण के साथ संवाद। डिफ़ॉल्ट रूप से, एम्बेडेड लिनक्स टर्मिनल के रूप में इस पोर्ट का उपयोग करता है। लेकिन मैं बंदरगाह के माध्यम से बाइनरी डेटा (सर्विस पैकेट) को स्थानांतरित करना चाहता हूं। सांत्वना :: respawn:: मेरी/etc/inittab फ़ाइल एक "गेट्टी" कॉल है/sbin/गेट्टी 115200 ttyS0

मैं भी/etc/पासवर्ड जहां "व्यवस्थापक" उपयोगकर्ता मेरी "CLI लांच तार के साथ फ़ाइल है "आवेदन के बाद लॉग में: व्यवस्थापक: 8MT/Jtxcyg8AY: 1000: 0: व्यवस्थापक:/tmp:/tmp/CLI

मेरे डिफ़ॉल्ट ttyS0 सेटिंग्स से पहले कार्यक्रम चल रहा है:

~ # stty -a 
speed 115200 baud;stty: standard input 
line = 0; 
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = ^J; 
eol2 = <undef>; swtch = <undef>; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; 
werase = ^W; lnext = ^V; flush = ^O; min = 1; time = 0; 
-parenb -parodd cs8 hupcl -cstopb cread -clocal -crtscts 
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon ixoff 
-iuclc -ixany -imaxbel -iutf8 
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0 
isig icanon -iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt 
-echoctl echoke 
~ # 

तो , मेरे क्ली कार्यक्रम में मैं निम्नलिखित करता हूं:

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); 
} 

मैंने बाइनरी डेटा प्राप्त करने के लिए पढ़ने() फ़ंक्शन (हस्ताक्षरित चार बफर के साथ) का प्रयास किया है, लेकिन सही डेटा प्राप्त करने में विफल रहा है। मैं भी प्रारंभिक खुला/dev/ttyS0 फिर file_descriptor & उपयोग पढ़ा() समारोह मिलता है।

मेरा प्रोग्राम 3 बाइट्स भेजता है: 0xAA, 0x02, 0xFE। 0x98, 0xE6, 0x18: लेकिन syslog में मैं हमेशा उस डिवाइस को देखने के गलत प्रतीकों प्राप्त करता है।

बात क्या है? सही द्विआधारी डेटा कैसे प्राप्त करें?

एक पूरा कोड जिसे मैं इस समय परीक्षण कर रहा हूं।

#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 टेक्स्ट की लाइनें। या तो * कच्चे मोड * पर स्विच करें या दोनों सिरों पर बाइनरी डेटा को बदलें। एएससीआईआई माध्यम पर बाइनरी डेटा के संचरण के लिए 'यूएनकोड' और 'यूडियोड' देखें। यह टेक्स्ट-केवल ई-मेल और यूएसनेट पोस्टिंग में बाइनरी डेटा भेजने का पारंपरिक तरीका है। बाइनरी या "विशेष" वर्ण भेजने का एक और तरीका "विशेष" चार को उपसर्ग करने और बाइट को वैध ASCII char को सामान्य करने के लिए "बचने" चरित्र का उपयोग करना है। बीटीडब्ल्यू '-पेरेनब-स्पेरोड सीएस 8' 11-बिट चरित्र फ्रेम का उत्पादन करेगा। 8-बिट डेटा वर्णों का उपयोग करते समय "कोई समानता" सामान्य नहीं है। – sawdust

+0

धन्यवाद भूरा! आप सही हैं, दूसरा संस्करण उपयोग करने के लिए एक और अधिक सुविधाजनक तरीका है। लेकिन मेरे पास एक पुराना सॉफ्टवेयर है जो पिछले एम्बेडेड डिवाइसों के साथ संवाद करता है और इसका समर्थन नहीं करता है। हर संभावना है कि हमें इसे एक नए समर्थन के लिए बदलना होगा। फिर भी, एक कच्चे मोड में जाने पर मुझे विशेष char क्यों नहीं मिल सकता है (कृपया निर्दिष्ट कोड देखें) एक read() func का उपयोग कर? – 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 सम्मेलनों प्रति गरीब कोडन अभ्यास) कच्चे या गैर विहित मोड में सीरियल पोर्ट डाल करने के लिए किया जाएगा।
सी और लिनक्स में सबसे आसान तरीका फ़ंक्शन cfmakeraw() का उपयोग करना है जो जीएनयू libc और uClibc पुस्तकालयों दोनों में उपलब्ध है। मैन पेज का उपयोग टर्मियो संरचना सदस्यों पर विवरण प्राप्त करने के लिए करें जो cfmakeraw() द्वारा संशोधित हैं।
सावधान रहें कि cfmakeraw() 10 बिट्स (एक स्टॉप बिट मानते हुए) के कुल चरित्र फ्रेम के लिए 8 बिट्स और समता की डेटा लंबाई के लिए कच्चे मोड में सीरियल पोर्ट सेट करेगा।

पसंदीदा विधि टर्मियो स्टैक्चर (प्रोग्राम निकास पर बहाली के लिए) की प्रतिलिपि और केवल आवश्यक ध्वज बिट्स (पूर्ण संरचना सदस्यों को लिखने के बजाय) को संशोधित करने की प्रतिलिपि रख रही है।

संशोधन

कोड है कि मेरे एआरएम 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 

फिर "एबीसी" मेजबान पर लिखा गया है (जो minicom टर्मिनल एमुलेटर प्रोग्राम चल रहा है) , एक कैरिज रिटर्न के बाद। कार्यक्रम लक्ष्य पर समाप्त हो जाता है, और syslog तो जांच की है:

# 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

 संबंधित मुद्दे

  • कोई संबंधित समस्या नहीं^_^