2016-09-14 69 views
0

我試圖開發使用此處提供的示例的LIN總線主控器:隨機暫停

https://github.com/trainman419/linux-lin/tree/master/misc/tty_lin_master

本質上這送出LIN協議消息通過串行端口。

我將代碼稍微改了一點,以使其對低級別功能測試更簡單。我想看看一個LIN分析器是否能正確解碼一個非常原始的LIN消息,但我遇到了與串口有關的奇怪問題。我通過/ dev/ttymxc4(RS-232)接口發送了幾個連續的字符,但在數據包傳輸過程中,我看到在中間的某個地方隨機暫停。有趣的是,這種停頓從某個價值開始,我俘獲了8.6ms,但隨後逐漸縮小直到它消失......但是隨後它再次啓動。

從本質上講,如果你看一下主,我真的只是發送10個字符通過RS-232 ...

下面的代碼,如果任何人有任何想法:

/* 
* UART-LIN master implementation 
*/ 

#define USE_TERMIOS2 

#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <fcntl.h> 
#include <string.h> 
#include <stdint.h> 
#include <sys/ioctl.h> 
#include <unistd.h> 
#include <fcntl.h> 
#include <time.h> /* clock_nanosleep */ 
#include <getopt.h> 

#ifndef USE_TERMIOS2 
    #include <linux/serial.h> /* struct struct_serial */ 
    #include <termios.h> 
#else /*USE_TERMIOS2*/ 
    #include <asm/ioctls.h> 
    #include <asm/termbits.h> 
#endif /*USE_TERMIOS2*/ 

#include "lin_common.h" 

#define LIN_HDR_SIZE  2 

struct sllin_tty { 
    int tty_fd; 

#ifndef USE_TERMIOS2 
    struct termios tattr_orig; 
    struct termios tattr; 
    struct serial_struct sattr; 
#else /*USE_TERMIOS2*/ 
    struct termios2 tattr_orig; 
    struct termios2 tattr; 
#endif /*USE_TERMIOS2*/ 
}; 

struct sllin_tty sllin_tty_data; 

struct sllin sllin_data = { 
    .tty = &sllin_tty_data, 
}; 

/* ------------------------------------------------------------------------ */ 

#ifndef USE_TERMIOS2 

static int tty_set_baudrate(struct sllin_tty *tty, int baudrate) 
{ 
    /* Set "non-standard" baudrate in serial_struct struct */ 
    tty->sattr.flags &= (~ASYNC_SPD_MASK); 
    tty->sattr.flags |= (ASYNC_SPD_CUST); 
    tty->sattr.custom_divisor = (tty->sattr.baud_base + baudrate/2)/baudrate; 
    if (ioctl(tty->tty_fd, TIOCSSERIAL, &tty->sattr) < 0) 
    { 
     perror("ioctl TIOCSSERIAL"); 
     return -1; 
    } 

    return 0; 
} 

static int tty_flush(struct sllin_tty *tty, int queue_selector) 
{ 
    return tcflush(tty->tty_fd, queue_selector); 
} 

#else /*USE_TERMIOS2*/ 

static int tty_set_baudrate(struct sllin_tty *tty, int baudrate) 
{ 
    tty->tattr.c_ospeed = baudrate; 
    tty->tattr.c_ispeed = baudrate; 
    tty->tattr.c_cflag &= ~CBAUD; 
    tty->tattr.c_cflag |= BOTHER; 

    if(ioctl(tty->tty_fd, TCSETS2, &tty->tattr)) { 
     perror("ioctl TIOCSSERIAL"); 
     return -1; 
    } 

    return 0; 
} 

static int tty_flush(struct sllin_tty *tty, int queue_selector) 
{ 
    return ioctl(tty->tty_fd, TCFLSH, queue_selector); 
} 

#endif /*USE_TERMIOS2*/ 


static int tty_set_mode(struct sllin_tty *tty, int baudrate) 
{ 
    if(!isatty(tty->tty_fd)) { 
     fprintf(stderr, "Not a terminal.\n"); 
     return -1; 
    } 

    /* Flush input and output queues. */ 
    if (tty_flush(tty, TCIOFLUSH) != 0) { 
     perror("tcflush"); 
     return -1;; 
    } 

#ifndef USE_TERMIOS2 

    /* Save settings for later restoring */ 
    if (tcgetattr(tty->tty_fd, &tty->tattr_orig) < 0) { 
     perror("tcgetattr"); 
     return -1; 
    } 

    /* Save settings into global variables for later use */ 
    if (tcgetattr(tty->tty_fd, &tty->tattr) < 0) { 
     perror("tcgetattr"); 
     return -1; 
    } 

    /* Save settings into global variables for later use */ 
    if (ioctl(tty->tty_fd, TIOCGSERIAL, &tty->sattr) < 0) { 
     perror("ioctl TIOCGSERIAL"); 
    } 

#else /*USE_TERMIOS2*/ 

    /* Save settings for later restoring */ 
    if (ioctl(tty->tty_fd, TCGETS2, &tty->tattr_orig) < 0) { 
     perror("ioctl TCGETS2"); 
     return -1; 
    } 

    /* Save settings into global variables for later use */ 
    if (ioctl(tty->tty_fd, TCGETS2, &tty->tattr) < 0) { 
     perror("ioctl TCGETS2"); 
     return -1; 
    } 

#endif /*USE_TERMIOS2*/ 

    /* 8 data bits     */ 
    /* Enable receiver    */ 
    /* Ignore CD (local connection) */ 
    tty->tattr.c_cflag = CS8 | CREAD | CLOCAL; 
    tty->tattr.c_iflag = 0; 
    tty->tattr.c_oflag = NL0 | CR0 | TAB0 | BS0 | VT0 | FF0; 
    tty->tattr.c_lflag = 0; 

    tty->tattr.c_cc[VINTR] = '\0'; 
    tty->tattr.c_cc[VQUIT] = '\0'; 
    tty->tattr.c_cc[VERASE] = '\0'; 
    tty->tattr.c_cc[VKILL] = '\0'; 
    tty->tattr.c_cc[VEOF]  = '\0'; 
    tty->tattr.c_cc[VTIME] = '\0'; 
    tty->tattr.c_cc[VMIN]  = 1; 
    tty->tattr.c_cc[VSWTC] = '\0'; 
    tty->tattr.c_cc[VSTART] = '\0'; 
    tty->tattr.c_cc[VSTOP] = '\0'; 
    tty->tattr.c_cc[VSUSP] = '\0'; 
    tty->tattr.c_cc[VEOL]  = '\0'; 
    tty->tattr.c_cc[VREPRINT] = '\0'; 
    tty->tattr.c_cc[VDISCARD] = '\0'; 
    tty->tattr.c_cc[VWERASE] = '\0'; 
    tty->tattr.c_cc[VLNEXT] = '\0'; 
    tty->tattr.c_cc[VEOL2] = '\0'; 

#ifndef USE_TERMIOS2 
    /* Set TX, RX speed to 38400 -- this value allows 
     to use custom speed in struct struct_serial */ 
    cfsetispeed(&tty->tattr, B38400); 
    cfsetospeed(&tty->tattr, B38400); 

    if (tcsetattr(tty->tty_fd, TCSANOW, &tty->tattr) == -1) { 
     perror("tcsetattr()"); 
     return -1; 
    } 

#else /*USE_TERMIOS2*/ 

    /* Set new parameters with previous speed and left */ 
    /* tty_set_baudrate() to do the rest */ 
    if(ioctl(tty->tty_fd, TCSETS2, &tty->tattr)) { 
     perror("ioctl TIOCSSERIAL"); 
     return -1; 
    } 

#endif /*USE_TERMIOS2*/ 

    /* Set real speed */ 
    tty_set_baudrate(tty, baudrate); 

    return 0; 
} 

int sllin_open(struct sllin *sl, const char *dev_fname, int baudrate) 
{ 
    int fd; 

    sl->lin_baud = baudrate; 

    /* Calculate baudrate for sending LIN break */ 
    sl->lin_break_baud = (sl->lin_baud * 2)/3; 

    fd = open(dev_fname, O_RDWR); 
    if (fd < 0) { 
     perror("open()"); 
     return -1; 
    } 
    sl->tty->tty_fd = fd; 

    return tty_set_mode(sl->tty, sl->lin_baud); 
} 

int main() 
{ 
    struct sllin *sl = &sllin_data; 

    char *dev_fname = "/dev/ttymxc4"; 
    int lin_baudrate = 19200; 
    int lin_id = 1; 

    if (sllin_open(sl, dev_fname, lin_baudrate) < 0) { 
     fprintf (stderr, "sllin_open open failed\n"); 
     exit(EXIT_FAILURE); 
    } 

    fcntl(fileno(stdin), F_SETFL, O_NONBLOCK); 
    printf("Press enter to terminate.\n\n"); 


    while(1) { 
     char c; 

     tty_flush(sl->tty, TCIOFLUSH); 

     unsigned int buff[10] = {1,2,3,4,5,6,7,8,9,10}; 
     // debug 
     write(sl->tty->tty_fd, &buff[0], 1); 
     write(sl->tty->tty_fd, &buff[1], 1); 
     write(sl->tty->tty_fd, &buff[2], 1); 
     write(sl->tty->tty_fd, &buff[3], 1); 
     write(sl->tty->tty_fd, &buff[4], 1); 
     write(sl->tty->tty_fd, &buff[5], 1); 
     write(sl->tty->tty_fd, &buff[6], 1); 
     write(sl->tty->tty_fd, &buff[7], 1); 
     write(sl->tty->tty_fd, &buff[8], 1); 
     write(sl->tty->tty_fd, &buff[9], 1); 
     // debug 

     sleep(1); 

     if (read(fileno(stdin), &c, 1) > 0) 
      break; 
    } 

    return EXIT_SUCCESS; 
} 
+1

這可能與芯片的FIFO相關(https://en.wikipedia.org/wiki/16550_UART#The_16550_FIFO)? 10不是標準尺寸之一,可能會造成延誤。 –

+1

我正在使用Gateworks Ventana GW5100 http://www.gateworks.com/product/item/ventana-gw5100-network-processor – NoS89

回答

1

從本質上講,如果你看看主,我實際上只是通過RS-232發送10個字符...

問題是你的輸出方法。
而不是十個write()系統調用每個只有一個字節(這是非常低效的),只使用一個write()作爲十個字節的緩衝區。

假設這是在Linux下執行的,每個系統調用都會允許調度程序暫停你的進程(因此間隙)。
如果只使用一個系統調用,則設備驅動程序將盡可能快地傳輸數據(只有DMA或中斷延遲可能會導致xmit間隙)。

更換這一切

write(sl->tty->tty_fd, &buff[0], 1); 
    write(sl->tty->tty_fd, &buff[1], 1); 
    write(sl->tty->tty_fd, &buff[2], 1); 
    write(sl->tty->tty_fd, &buff[3], 1); 
    write(sl->tty->tty_fd, &buff[4], 1); 
    write(sl->tty->tty_fd, &buff[5], 1); 
    write(sl->tty->tty_fd, &buff[6], 1); 
    write(sl->tty->tty_fd, &buff[7], 1); 
    write(sl->tty->tty_fd, &buff[8], 1); 
    write(sl->tty->tty_fd, &buff[9], 1); 

與眼前這個

write(sl->tty->tty_fd, buff, 10); 

而且,更換寫的睡眠()tcdrain()讀取。

+1

在我最後的實現中,我將需要打破write()sys調用,因爲LIN協議在幀的開始處需要13個連續的0個字符,實質上,我這樣做的方式是將波特率更改爲12800以發送這些字符,然後將其更改回19200以用於其餘的標題。因爲在兩個字節之間會有一個停止字符,所以我不能簡單地以減少的波特率發送兩個0x00字節作爲13個連續0的替換字節。 我應該使用不同的系統調用嗎? (是的,這是在Linux下執行的) – NoS89

+1

我還應該補充一點,當我將波特率更改爲12800以便發送看起來像13個連續的0時,我只需發送一個單字節0x00,速度爲12800。對於從設備,這顯示爲19200波特的13位0。 – NoS89

+1

我只能回答發佈的問題。我無法閱讀您的想法或預測您的(不可理解的)*「最終實施」*。我解釋了你發佈的具體問題的解決方案,你甚至無法表達一絲感謝。 – sawdust