友善之臂comtest.c串口編譯程序詳解 但願對你們有幫助

友善comtest.c串口編譯程序詳解 但願對你們有幫助

說明 :armcomtest 是友善之臂爲了方便測試而開發的linux 下的簡易實用串口終端程 
序,它使用標準的系統調用,和硬件無關。通常Linux 系統系統啓動後,串口 0,1,2對應的設 
備名分別爲 /dev/ttySAC0,1,2,3  
  
測試串口2 須要藉助另外一臺帶有串口的PC ,鏈接好 COM2 和另外一臺PC的串口,並設置該PC的超級終端爲波特率115200 , 
無流控制,其餘默認。 
在命令行下輸入: 
# armcomtest  –d /dev/ttySAC1 - o   
這時若是輸入字符會在另外一臺PC的超級終端出現,反之亦然。 
若是要測試串口3,則須要鏈接擴展小板的COM3 ,並在命令行輸入: 
# armcomtest  –d /dev/ttySAC2  - o   
# include <stdio.h>
# include <stdlib.h>
# include <termio.h>
# include <unistd.h>
# include <fcntl.h>
# include <getopt.h>
# include <time.h>
# include <errno.h>
# include <string.h>

static void Error(const char *Msg)
{
    fprintf (stderr, "%s\n", Msg);
    fprintf (stderr, "strerror() is %s\n", strerror(errno));
    exit(1);
}
static void Warning(const char *Msg)
{
     fprintf (stderr, "Warning: %s\n", Msg);
}


static int SerialSpeed(const char *SpeedString) //波特率選擇
{
    int SpeedNumber = atoi(SpeedString);
#   define TestSpeed(Speed) if (SpeedNumber == Speed) return B##Speed
    TestSpeed(1200);
    TestSpeed(2400);
    TestSpeed(4800);
    TestSpeed(9600);
    TestSpeed(19200);
    TestSpeed(38400);
    TestSpeed(57600);
    TestSpeed(115200);
    TestSpeed(230400);
    Error("Bad speed");
    return -1;
}

static void PrintUsage(void)  //打印消息
{

   fprintf(stderr, "comtest - interactive program of comm port\n");
   fprintf(stderr, "press [ESC] 3 times to quit\n\n");

   fprintf(stderr, "Usage: comtest [-d device] [-t tty] [-s speed] [-7] [-c] [-x] [-o] [-h]\n");
   fprintf(stderr, "         -7 7 bit\n");
   fprintf(stderr, "         -x hex mode\n");
   fprintf(stderr, "         -o output to stdout too\n");
   fprintf(stderr, "         -c stdout output use color\n");
   fprintf(stderr, "         -h print this help\n");
   exit(-1);
}

static inline void WaitFdWriteable(int Fd)
/*
相似於一種宏定義
這種宏定義在形式上相似於一個函數,
但在使用它時,僅僅只是作預處理器符號表中的簡單替換,
所以它不能進行參數有效性的檢測,
也就不能享受C++編譯器嚴格類型檢查的好處,
另外它的返回值也不能被強制轉換爲可轉換的合適的類型,
這樣,它的使用就存在着一系列的隱患和侷限性。
*/
{
    fd_set WriteSetFD;
    FD_ZERO(&WriteSetFD);
    FD_SET(Fd, &WriteSetFD);
    if (select(Fd + 1, NULL, &WriteSetFD, NULL, NULL) < 0) {
      Error(strerror(errno));
    } //select用來監控
    
}

int main(int argc, char **argv)
{
    int CommFd, TtyFd;

    struct termios TtyAttr;
    struct termios BackupTtyAttr;

    int DeviceSpeed = B115200;
    int TtySpeed = B115200;
    int ByteBits = CS8;
    const char *DeviceName = "/dev/ttyS0";
    const char *TtyName = "/dev/tty";
    int OutputHex = 0;
    int OutputToStdout = 0;
    int UseColor = 0;

    opterr = 0;
    for (;;) {
        int c = getopt(argc, argv, "d:s:t:7xoch");
        /*
        函數說明 getopt()用來分析命令行參數。
        參數argc和argv是由main()傳遞的參數個數和內容。
        參數optstring 則表明欲處理的選項字符串。
        此函數會返回在argv 中下一個的選項字母,
        此字母會對應參數optstring 中的字母。
        若是選項字符串裏的字母后接着冒號「:」,
        則表示還有相關的參數,全域變量optarg 
        即會指向此額外參數。若是getopt()
        找不到符合的參數則會印出錯信息,
        並將全域變量optopt設爲「?」字符,
        若是不但願getopt()印出錯信息,
        則只要將全域變量opterr設爲0便可。        
        */
        if (c == -1)
            break;
        switch(c) {
        case 'd':
            DeviceName = optarg;
            break;
        case 't':
            TtyName = optarg;
            break;
        case 's':
        if (optarg[0] == 'd') {
        DeviceSpeed = SerialSpeed(optarg + 1);
        } else if (optarg[0] == 't') {
        TtySpeed = SerialSpeed(optarg + 1);
        } else
                TtySpeed = DeviceSpeed = SerialSpeed(optarg);
            break;
    case 'o':
        OutputToStdout = 1;
        break;
    case '7':
        ByteBits = CS7;
        break;
        case 'x':
            OutputHex = 1;
            break;
    case 'c':
        UseColor = 1;
        break;
        case '?':
        case 'h':
        default:
        PrintUsage();
        }
    }
    if (optind != argc)
        PrintUsage();//打印消息

    CommFd = open(DeviceName, O_RDWR, 0);
    if (CommFd < 0)
    Error("Unable to open device");
    if (fcntl(CommFd, F_SETFL, O_NONBLOCK) < 0)
         Error("Unable set to NONBLOCK mode");
    /*
    F_SETFL 設置文件描述詞狀態旗標,
    參數arg爲新旗標,但只容許O_APPEND、
    O_NONBLOCK和O_ASYNC位的改變,其餘位的改變將不受影響。
    
    O_NONBLOCK 若是路徑名指向 FIFO/塊文件/字符文件,則把文件的打開和後繼 I/O
    */


    memset(&TtyAttr, 0, sizeof(struct termios));
    /*
    函數原型  void *memset(void *s, int ch, unsigned n);
    
    將s所指向的某一塊內存中的每一個字節的內容所有設置爲ch指定的ASCII值,
    塊的大小由第三個參數指定,這個函數一般爲新申請的內存作初始化工做, 
    其返回值爲指向S的指針。

    將TtyAttr 所指向的內存區域的值所有設置爲0 的Ascii值
    

    */
    
    //設置串口的一些參數
    TtyAttr.c_iflag = IGNPAR;    //IGNPAR   忽略奇偶校驗錯誤

    //c_iflag:輸入模式標誌,控制終端輸入方式,具體參數如表6.3所示。

    TtyAttr.c_cflag = DeviceSpeed | HUPCL | ByteBits | CREAD | CLOCAL;
    
    //CREAD 使用接收器  HUPCL  關閉設備時掛起  CLOCAL  忽略調制解調器線路狀態
    //c_cflag:控制模式標誌,指定終端硬件控制信息,具體參數如表6.5所示。

    TtyAttr.c_cc[VMIN] = 1;  //VMIN 非規範模式讀取時的最小字符數

    
    //c_cc[NCCS]:控制字符,用於保存終端驅動程序中的特殊字符,
    //如輸入結束符等。c_cc中定義瞭如表6.7所示的控制字符。



    if (tcsetattr(CommFd, TCSANOW, &TtyAttr) < 0)
        Warning("Unable to set comm port");
    /*
    爲了使程序控制終端參數,
    在標準庫中POSIX須要幾個函數,
    最重要的兩個函數爲tcsetattr和tcgetattr。
    tcgetattr取回一個如圖3-34中顯示的數據結構的一份拷貝,
    該結構爲termios,它包含用來改變特殊字符、
    設置模式和修改終端其餘特性的全部信息。
    程序能夠檢查當前的設置,
    當須要時對這些設置進行修改,
    而後調用tcsetattr把這個結構寫回終端任務中。
    
    
    頭文件
    <termios.h>

    <unistd.h>

    函數形式
    int tcgetattr(int fd, struct termios *termios_p);

    int tcsetattr(int fd, int optional_actions, const struct termios *termios_p);

    */


    TtyFd = open(TtyName, O_RDWR | O_NDELAY, 0);  //ndelay 自我解釋應該是延時的意思
    if (TtyFd < 0)
    Error("Unable to open tty");

    TtyAttr.c_cflag = TtySpeed | HUPCL | ByteBits | CREAD | CLOCAL;
    if (tcgetattr(TtyFd, &BackupTtyAttr) < 0)
    Error("Unable to get tty");
    //BackupTtyAttr 用於存儲得到的終端參數信息

    if (tcsetattr(TtyFd, TCSANOW, &TtyAttr) < 0)
    //使用tcsetattr函數將修改後的終端參數設置到標準輸入中
    Error("Unable to set tty");


    for (;;) {
    unsigned char Char = 0;
    fd_set ReadSetFD;

    void OutputStdChar(FILE *File) 
    {
        char Buffer[10];
        int Len = sprintf(Buffer, OutputHex ? "%.2X  " : "%c", Char);
        fwrite(Buffer, 1, Len, File);
    }
/*
    int select(int maxfd,fd_set *rdset,fd_set *wrset,fd_set *exset,struct timeval *timeout); 
    參數maxfd是須要監視的最大的文件描述符值+1;rdset,wrset,exset分別對應於須要檢測的可讀文件描述符的集合,
    可寫文件描述符的集 合及異常文件描述符的集合。struct timeval結構用於描述一段時間長度,若是在這個時間內,
    須要監視的描述符沒有事件發生則函數返回,返回值爲0。 
    fd_set(它比較重要因此先介紹一下)是一組文件描述字(fd)的集合,
    它用一位來表示一個fd(下面會仔細介紹),對於fd_set類型經過下面四個宏來操做: 
    FD_ZERO(fd_set *fdset);將指定的文件描述符集清空,
    在對文件描述符集合進行設置前,必須對其進行初始化,若是不清空,
    因爲在系統分配內存空間後,一般並不做清空處理,因此結果是不可知的。 
    FD_SET(fd_set *fdset);用於在文件描述符集合中增長一個新的文件描述符。 
    FD_CLR(fd_set *fdset);用於在文件描述符集合中刪除一個文件描述符。 
    FD_ISSET(int fd,fd_set *fdset);用於測試指定的文件描述符是否在該集合中。        
    過去,一個fd_set一般只能包含<32的fd(文件描述字),
    由於fd_set其實只用了一個32位矢量來表示fd;如今,
    UNIX系統一般會在頭文件<sys/select.h>中定義常量FD_SETSIZE,
    它是數據類型fd_set的描述字數量,其值一般是1024,這樣就能表示<1024的fd。
    根據fd_set的位矢量實現,咱們能夠從新理解操做fd_set的四個宏: 
    fd_set set;
    FD_ZERO(&set);     
    FD_SET(0, &set);   
    FD_CLR(4, &set);     
    FD_ISSET(5, &set);   
*/
    FD_ZERO(&ReadSetFD);

    FD_SET(CommFd, &ReadSetFD);
    FD_SET( TtyFd, &ReadSetFD);
#    define max(x,y) ( ((x) >= (y)) ? (x) : (y) )
    if (select(max(CommFd, TtyFd) + 1, &ReadSetFD, NULL, NULL, NULL) < 0) {
        Error(strerror(errno));
    //檢測可讀的文件的文件描述符的集合
    }
#    undef max

    if (FD_ISSET(CommFd, &ReadSetFD)) {
    /*
    int FD_ISSET(int fd,fd_set *fdset)   
    宏說明:在調用select()函數後,用FD_ISSET來檢測fdset中文件fd有無發生變化 
    */
        while (read(CommFd, &Char, 1) == 1) {

        WaitFdWriteable(TtyFd);
        //監控TtyFd這個文件描述符中是否有數據發生變化
        //這個函數在上面有定義
        
        // ssize_t write (int fd,const void * buf,size_t count);
        //write()會把參數buf所指的內存寫入count個字節到參數fd所指的文件內。
        //固然,文件讀寫位置也會隨之移動。
        if (write(TtyFd, &Char, 1) < 0) {
              Error(strerror(errno));
        }
        //若是用到了-O的屬性  OutputTostdout 就會變成1就會輸出下面的信息
        if (OutputToStdout) {
            if (UseColor)
            fwrite("\x1b[01;34m", 1, 8, stdout);
            OutputStdChar(stdout);
            if (UseColor)
            fwrite("\x1b[00m", 1, 8, stdout);
            fflush(stdout);
        }
        }
    }

    if (FD_ISSET(TtyFd, &ReadSetFD)) {
        while (read(TtyFd, &Char, 1) == 1) {
               static int EscKeyCount = 0;
        WaitFdWriteable(CommFd);
               if (write(CommFd, &Char, 1) < 0) {
              Error(strerror(errno));
        }
        if (OutputToStdout) {
            if (UseColor)
            fwrite("\x1b[01;31m", 1, 8, stderr);
            OutputStdChar(stderr);
            if (UseColor)
            fwrite("\x1b[00m", 1, 8, stderr);
            fflush(stderr);
            }

              if (Char == '\x1b') {
                    EscKeyCount ++;
                    if (EscKeyCount >= 3)
                        goto ExitLabel;
                       // 跳出到退出label
                } else
                    EscKeyCount = 0;
        } 
        }

    }

ExitLabel:
    if (tcsetattr(TtyFd, TCSANOW, &BackupTtyAttr) < 0)
    Error("Unable to set tty");

    return 0;



另外一種解析
做者: kissazi2  
出處: http://www.cnblogs.com/kissazi2/  
本文版權歸做者全部,歡迎轉載,但未經做者贊成必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接,不然保留追究法律責任的權利。
/*************************************************************************** ** 文件: comtest.c ** 描述: **     以串口通信的測試程序 ** **------------------------------------------------------------------------------------------------------ ********************************************************************************************************/ #ifdef HAVE_CONFIG_H #include <config.h> #endif #include <stdio.h> /*標準輸入輸出定義*/ #include <stdlib.h> /*標準函數庫定義*/ #include <termio.h> /*PPSIX 終端控制定義*/ #include <unistd.h>/*Unix 標準函數定義,使用exit()*/ #include <fcntl.h>/*文件控制定義*/ #include <getopt.h>/*參數提取定義*/ #include <time.h> #include <errno.h>/*錯誤號定義*/ #include <string.h> #include <assert.h> int OutputHex = 0;//是否以十六進制發送。OutputHex爲1時,以十六進制發送;爲0,以字符串方式發送 int OutputToStdout = 0;//是否將消息一樣發送一份到標準輸出。爲1時,發送;爲0,不發送 int UseColor = 0;       //是否使用顏色。爲1時,使用顏色;爲0,不使用顏色。 struct termios BackupTtyAttr;//終端屬性的備份 int IsWrite=0; static void Error(const char *Msg) {     fprintf (stderr, "%s\n", Msg);     fprintf (stderr, "strerror() is %s\n", strerror(errno));     exit(EXIT_FAILURE); } static void Warning(const char *Msg) {      fprintf (stderr, "Warning: %s\n", Msg); } static int SerialSpeed(const char *SpeedString) {     int SpeedNumber = atoi(SpeedString); #   define TestSpeed(Speed) if (SpeedNumber == Speed) return B##Speed     TestSpeed(1200);     TestSpeed(2400);     TestSpeed(4800);     TestSpeed(9600);     TestSpeed(19200);     TestSpeed(38400);     TestSpeed(57600);     TestSpeed(115200);     TestSpeed(230400);     Error("Bad speed");     return -1; } /** *@brief  打印錯誤信息 */ static void PrintUsage(void) {    fprintf(stderr, "comtest - interactive program of comm port\n");    fprintf(stderr, "press [ESC] 3 times to quit\n\n");    fprintf(stderr, "Usage: comtest [-d device] [-t tty] [-s speed] [-7] [-c] [-x] [-o] [-h]\n");    fprintf(stderr, "         -7 7 bit\n");    fprintf(stderr, "         -x hex mode\n");    fprintf(stderr, "         -o output to stdout too\n");    fprintf(stderr, "         -c stdout output use color\n");    fprintf(stderr, "         -h print this help\n");    exit(-1); } /******************************************************************************************************* ** 函數: WaitFdWriteable,  等待文件可寫 **------------------------------------------------------------------------------------------------------ ** 參數: Fd 文件描述符 ** 返回: void ** 函數說明:使用inline標識符,防止由於函數頻繁的調用佔用大量的棧空間 ** 日期: 2013.06.19 ********************************************************************************************************/ static inline void WaitFdWriteable(int Fd) {     fd_set WriteSetFD;    //定義可寫的設備集合     FD_ZERO(&WriteSetFD);//將可寫的設備集合清空     FD_SET(Fd, &WriteSetFD);//將fd添加到可寫的設備集合中     //select函數原型:    int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);     //值得說明的是:int maxfdp是一個整數值,是指須要測試的文件描述符的數目,測試的描述符範圍從0到nfds-1.即全部文件描述符的最大值加1,不能錯!          if (select(Fd + 1, NULL, &WriteSetFD, NULL, NULL) < 0) {//判斷是否有可寫的設備,若是沒有就一直阻塞,這裏沒有設置超時,若是沒有可寫的,會一直阻塞下去     //select函數中readfds、errorfds描述符集合都爲空,表示不進行測試         Error(strerror(errno));     } } void setAttrByArgvs(){ } /******************************************************************************************************* ** 函數: OutputStdChar,  打開一個串口 **------------------------------------------------------------------------------------------------------ ** 參數: FILE *File 文件描述符         int OutputHex,是否以十六進制發送。OutputHex爲1時,以十六進制發送;爲0,以字符串方式發送         unsigned char aCharToSend將要發送的字符 ** 返回: void ** ** 日期: 2013.06.19 ********************************************************************************************************/ void OutputStdChar(FILE *File,int OutputHex,unsigned char aCharToSend) {//向設備寫數據      char Buffer[10];      int Len = sprintf(Buffer, OutputHex ? "%.2X  " : "%c", aCharToSend);//%.2X表示輸出01,02樣式的十六進制數           // int Len = sprintf(Buffer,  0x01);//%.2X表示輸出01,02樣式的十六進制數      fwrite(Buffer, 1, Len, File); } /******************************************************************************************************* ** 函數: SetFD,  設置文件描述符 **------------------------------------------------------------------------------------------------------ ** 參數:int argc          char **argv         int * CommFd 串口文件描述符         int * TtyFd 終端文件描述符 ** 返回: void **函數說明: **本函數經過對main函數的argc、argv進行參數的提取,實現對串口文件描述符(CommFd)和終   端文件描述符TtyFd的設置 ** 日期: 2013.06.19 ********************************************************************************************************/ void SetFD(int argc, char **argv,int * CommFd,int * TtyFd){     struct termios TtyAttr;         //終端屬性     int DeviceSpeed = B2400;     //串口波特率     int TtySpeed = B2400;         //終端波特率     int ByteBits = CS8;             //數據位:8位     const char *DeviceName = "/dev/ttySAC1";//串口設備名     const char *TtyName = "/dev/tty";        //終端設備名,防止重要的信息被用戶重定向     opterr = 0;     printf("init........\n");     //經過Argc和Argv設置必要的參數     for (;;) {         int c = getopt(argc, argv, "d:s:t:7xoch");//利用getopt將argv參數一個個提取出來         if (c == -1)             break;         switch(c) {         case 'd'://設置串口的名稱             DeviceName = optarg;             break;         case 't'://設置終端的名稱             TtyName = optarg;             break;         case 's'://設置比特率             if (optarg[0] == 'd') {//設置串口的波特率                 DeviceSpeed = SerialSpeed(optarg + 1);             } else if (optarg[0] == 't') {//設置終端的波特率                 TtySpeed = SerialSpeed(optarg + 1);              } else                 TtySpeed = DeviceSpeed = SerialSpeed(optarg);//若是沒有帶d或t,直接將兩個設備的波特率設置成相同的             break;         case 'o'://設置同時將消息向標準輸出(stdout)輸出             OutputToStdout = 1;         break;         case '7'://設置數據位爲7位             ByteBits = CS7;          break;         case 'x':             OutputHex = 1;//以十六進制輸出              printf("OutputHex = 1\n");             break;         case 'c'://使用顏色標記              UseColor = 1;              break;                 case '?':                 case 'h':                 default:                 PrintUsage();//輸出main參數的說明         }     }//end of for(;;)          printf("end of getopt\n");     if (optind != argc)//判斷參數是否符合要求         PrintUsage();  //輸出main參數的說明     *CommFd = open(DeviceName, O_RDWR, 0);//以讀寫的方式打開      if (*CommFd < 0)         Error("Unable to open device");//不能打開設備     if (fcntl(*CommFd, F_SETFL, O_NONBLOCK) < 0)//設置文件訪問模式爲非阻塞         Error("Unable set to NONBLOCK mode");  //不能使用NONBLOCK模式     memset(&TtyAttr, 0, sizeof(struct termios));     TtyAttr.c_iflag = IGNPAR;//忽略輸入行中的停止狀態     TtyAttr.c_cflag = DeviceSpeed | HUPCL | ByteBits | CREAD | CLOCAL;//DeviceSpeed:     //HUPCL:關閉時掛斷調制解調器;CREAD:啓用字符接收器;CLOCAL:忽略全部調制解調器的狀態行     TtyAttr.c_cc[VMIN] = 1;//設置MIN值,read調用將一直等待,直到有MIN個字符可讀的時候才返回,返回是讀取的字符數量。到達文件結尾的時候返回0     if (tcsetattr(*CommFd, TCSANOW, &TtyAttr) < 0)//當即對屬性進行修改,不等當前輸出完成         Warning("Unable to set comm port");     *TtyFd = open(TtyName, O_RDWR | O_NDELAY, 0);//只有在CTEAT模式下,才須要第三個參數,這邊的第三個參數是沒有做用的。     if (*TtyFd < 0)         Error("Unable to open tty");     TtyAttr.c_cflag = TtySpeed | HUPCL | ByteBits | CREAD | CLOCAL;     if (tcgetattr(*TtyFd, &BackupTtyAttr) < 0)//將當前Tty的屬性備份在BackupTtyAttr,以便在程序退出時還原Tty的設置         Error("Unable to get tty");     if (tcsetattr(*TtyFd, TCSANOW, &TtyAttr) < 0)         Error("Unable to set tty"); } /******************************************************************************************************* ** 函數: OutputCharUseColor 輸出帶顏色的字符 **------------------------------------------------------------------------------------------------------ ** 參數:char * colorCode 顏色代碼         unsigned char aChar 要輸出的字符 ** 返回: void ** 日期: 2013.06.19 ********************************************************************************************************/ void OutputCharUseColor(char * colorCode,unsigned char aChar){     if (OutputToStdout) {    //同時向標準輸出寫消息         if (UseColor)             fwrite(colorCode, 1, 8, stdout);         OutputStdChar(stdout,OutputHex,aChar);         if (UseColor)             fwrite("\x1b[00m", 1, 8, stdout);         fflush(stdout);//將stdout緩衝區中的數據當即輸出,即在屏幕上顯示     } } int uart_pthread(int argc, char **argv) {     printf("into main\n");     int SendBufferIndex=0;             char * SendBuffer=(char *)malloc(sizeof(char)*20);//發送緩存     /**     TtyFD是爲了防止與用戶交互的信息被重定向,而沒有在屏幕上顯示。使用TtyFd能夠直接將     不想被重定向的信息直接向用戶終端(屏幕)輸出。     **/     int CommFd, TtyFd;             //串口、終端描述符     SetFD( argc, argv,&CommFd,&TtyFd);     for (;;) {         unsigned char aCharToSend = 0;         fd_set ReadSetFD;        //可讀設備集合         FD_ZERO(&ReadSetFD);    //清空可讀設備集合         FD_SET(CommFd, &ReadSetFD);//將串口加入可讀設備集合         FD_SET( TtyFd, &ReadSetFD);//將終端加入可讀設備集合         # define max(x,y) ( ((x) >= (y)) ? (x) : (y) )//最大值函數,返回兩個數中較大的數         if (select(max(CommFd, TtyFd) + 1, &ReadSetFD, NULL, NULL, NULL) < 0) {//同時測試串口和終端是否可讀              Error(strerror(errno));         }         # undef max         if (FD_ISSET(CommFd, &ReadSetFD)) {//判斷串口是否可讀              while (read(CommFd, &aCharToSend, 1) == 1) {//從串口中讀取一個char型,放在aCharToSend                 WaitFdWriteable(TtyFd);//會一直阻塞在這裏,直到終端設備可寫                 if (write(TtyFd, &aCharToSend, 1) < 0) {//向屏幕輸出收到的字符                     Error(strerror(errno));                //若是寫入錯誤,輸出錯誤信息                 }                 OutputCharUseColor("\x1b[01;34m",aCharToSend);              }         }         // printf("------INTO    if (FD_ISSET(TtyFd, &ReadSetFD)) ");         if (FD_ISSET(TtyFd, &ReadSetFD)) {               //判斷終端是否可讀                      while (read(TtyFd, &aCharToSend, 1) == 1) {//從終端中讀取一個值                     // fprintf(stderr,"\n------SendBufferIndex:%d ",SendBufferIndex);                     SendBuffer[SendBufferIndex++]=aCharToSend;                     // fprintf(stderr, "\nRead From Tty");                     static int EscKeyCount = 0;            //按下Esc的次數                     OutputCharUseColor("\x1b[01;31m",aCharToSend);                     if (aCharToSend == '\r') {//監測是否按下回車                         SendBuffer[SendBufferIndex-1]='\0';                         IsWrite=1;                          fprintf(stderr, "\x1b[01;34m you have enter :%s\n \x1b[00m",SendBuffer);                         // fprintf(stderr,"\n---11---IsWrite==%d ",IsWrite);                     }                     if(SendBufferIndex==19){                                 SendBuffer[SendBufferIndex]='\0';                         // fprintf(stderr, "you have enter :%s\n",SendBuffer);                         IsWrite=1;                     }                     if(IsWrite==1){                         // fprintf(stderr,"\n------WaitFdWriteable ");                         WaitFdWriteable(CommFd);                              if (write(CommFd, SendBuffer, SendBufferIndex) < 0) {                                 Error(strerror(errno));                         }                         IsWrite=0;                         SendBufferIndex=0;                     }                     if (aCharToSend == '\x1b') {//監測是否按下Esc                         fprintf(stderr, "EscKeyPressed\x1b\n");                         EscKeyCount ++;                         if (EscKeyCount >= 3)                         goto ExitLabel;                     }else{                         EscKeyCount = 0;                     }                     // fprintf(stderr,"\n---22---IsWrite==%d ",IsWrite);             }                       }     }//end of for (;;) ExitLabel:     free(SendBuffer);     if (tcsetattr(TtyFd, TCSANOW, &BackupTtyAttr) < 0)//恢復以前終端的設置         Error("Unable to set tty");     return 0; }