2012-09-15 28 views
12

Leí los enlaces seguidos y otras fuentes, pero no encontré respuesta para mi pregunta.¿Cómo se leen los datos binarios a través del terminal en serie en el programa C?

Binary data over serial terminal

Data gets corrupted during transmission over the serial port

me comunico con mi dispositivo integrado a través de un puerto serie. Por defecto, Linux incorporado usa este puerto como terminal. Pero también quiero transferir datos binarios (paquetes de servicio) a través del puerto. Mi archivo/etc/inittab tiene un "getty" llamada: consola :: respawn:/sbin/getty 115200 ttyS0

También tengo/etc/passwd con la cadena en la que el usuario "admin" lanzar mi "cli "aplicación después del registro en: de administración: 8 millones de toneladas/Jtxcyg8AY: 1000: 0: admin:/tmp:/

Mi configuración ttyS0 predeterminado CLI/tmp antes de ejecutar el programa es:

~ # 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 
~ # 

Así , en mi programa cli hago lo siguiente:

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

He intentado la función read() (con el búfer char sin signo) para obtener datos binarios, pero no recibí los datos correctos. También, de forma preliminar, abro/dev/ttyS0 de nuevo para obtener file_descriptor & use read() func.

Mi programa envía 3 bytes: 0xAA, 0x02, 0xFE. Pero en syslog siempre veo que el dispositivo recibe símbolos incorrectos: 0x98, 0xE6, 0x18.

¿Cuál es el problema? ¿Cómo obtener los datos binarios correctos?

Todo un código que estoy probando en este momento.

#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

Su puerto serie está configurado para la entrada canónica, es decir, líneas de texto ASCII. Cambie al * modo en bruto * o transforme los datos binarios en ambos extremos. Mire 'uuencode' y' uudecode' para la transmisión de datos binarios en un medio ASCII. Es la forma tradicional de enviar datos binarios en correos electrónicos de solo texto y publicaciones de USENET. Otro método para enviar caracteres binarios o "especiales" es usar un carácter de "escape" para prefijar el carácter "especial" y normalizar el byte a un carácter ASCII válido. BTW '-parenb -parodd cs8' producirá cuadros de caracteres de 11 bits. "Sin paridad" es típico cuando se utilizan caracteres de datos de 8 bits. – sawdust

+0

Gracias aserrín! Tiene razón, la segunda variante es un método más conveniente de usar. Pero tengo un software antiguo que se comunica con dispositivos integrados anteriores y no lo admite. Es muy probable que tengamos que cambiarlo para apoyar uno nuevo. Aún así, ¿por qué no puedo obtener un carácter especial cuando voy en modo raw (por favor, vea el código mencionado) usando un juego de lectura()? – Bakir

Respuesta

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

Esto sería insuficiente código (y malas prácticas de codificación por convenciones POSIX) para poner el puerto en serie en el modo de crudo o no canónica.
El método más fácil en C y Linux es usar la función cfmakeraw() que está disponible en las bibliotecas libc de GNU y uClibc. Utilice la página del manual para obtener los detalles sobre los miembros de la estructura termios que están modificados por cfmakeraw().
Tenga en cuenta que cfmakeraw() configurará el puerto serie en modo raw para una longitud de datos de 8 bits y no paridad, para un marco de caracteres total de 10 bits (suponiendo un bit de parada).

El método preferido es preservar una copia de la estructura termios (para la restauración en la salida del programa) y solo modificar los bits de bandera requeridos (en lugar de escribir los miembros de la estructura completa).

REVISIÓN

código que funciona en mi SoC ARM es:

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

El programa compilado se carga & ejecutado en el tablero de destino.

Desde el lado del host del enlace de comunicaciones en serie, el archivo seq.bin con el siguiente contenido se envía:

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

A continuación, "ABC" se escribe en el host (el que se ejecuta el programa emulador minicom terminal) , seguido de un retorno de carro. El programa termina en el objetivo, y se examina a continuación, el 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    
# 

Los datos binarios se ha recibido intacto.

Tenga en cuenta que dado que este es el modo raw y los caracteres ingresados ​​fueron ingresados ​​con relativa lentitud, el read() devuelve datos "parciales" y el programa de usuario sería responsable de almacenar/ensamblar los datos en "mensajes" completos. Si los mensajes son de longitud fija, entonces c_cc[VMIN] miembro podría ajustarse a la longitud del mensaje. ¡Pero tenga cuidado con los problemas y las complicaciones del encuadre de mensajes cuando se pierde la sincronización de fotogramas!

+0

He usado esta función y obtuve el mismo resultado :) – Bakir

+0

Pero lo intentaré una vez más. – Bakir

+0

Entonces, verifiqué tu código. Lamentablemente, obtuve la misma imagen. – Bakir

Cuestiones relacionadas