1. 程式人生 > >readline中的鍵盤碼^[[A,^[[B,^[[D,^[[C (ANSI控制碼)

readline中的鍵盤碼^[[A,^[[B,^[[D,^[[C (ANSI控制碼)

最近除錯u-boot的readline,對一些特殊按鍵鍵值有些疑惑,比如↑↓←→四個鍵,在linux的minicom敲下這幾個鍵,串列埠會輸出什麼資料。
其實不需要用串列埠測試,我們可以寫個小程式測試一下:

點選(此處)摺疊或開啟

  1. #include <stdio.h>
  2. #include <curses.h>
  3. int main(int argc, char **argv)
  4. {
  5.     char a;
  6.     char b[80];
  7.     int i;
  8.     printf("Please input a string
  9.     :\n");
  10. //    a = getc(
    stdin);
  11.     scanf("%s",b);    
  12. //    printf("The input a string is : 0x%x\n", a);
  13.     printf("The input char is:\n");
  14.     for(= 0; b[i] !='\0' && i < 80; i++ )
  15.         printf("0x%x ",b[i]);
  16.     printf("\n");
  17.     return 0;
  18. }

測試:

點選(此處)摺疊或開啟

  1. [email protected]:~/work/driver/test$ ./test
  2. Please input a string:
  3. ^[[A^[[B^[[D^[[C
  4. The input char is:
  5. 0x1b 0x5b 0x41 0x1b 0x5b 0x42 0x1b 0x5b 0x44 0x1b 0x5b 0x43
可以看到,當輸入↑鍵,我們得到3個位元組:0x1b 0x5b 0x41。
或者執行cat -v,然後輸入↑↓←→(其他鍵也可以F1~F12)。

參考知乎一位大俠的說法:

http://www.zhihu.com/question/21518507
這下就明白了。再聯絡cread_line的程式碼片段:

點選(此處)摺疊或開啟

  1. static int cread_line(const char *const prompt, char *buf,
     unsigned int *len,
  2.         int timeout)
  3. {
  4.     unsigned long num = 0;
  5.     unsigned long eol_num = 0;
  6.     unsigned long wlen;
  7.     char ichar;
  8.     int insert = 1;
  9.     int esc_len = 0;
  10.     char esc_save[8];
  11.     int init_len = strlen(buf);
  12.     int first = 1;
  13.     if (init_len)
  14.         cread_add_str(buf, init_len, 1, &num, &eol_num, buf, *len);
  15.     while (1) {
  16. #ifdef CONFIG_BOOT_RETRY_TIME
  17.         while (!tstc()) {    /* while no incoming data */
  18.             if (retry_time >= 0 && get_ticks() > endtime)
  19.                 return (-2);    /* timed out */
  20.             WATCHDOG_RESET();
  21.         }
  22. #endif
  23.         if (first && timeout) {
  24.             uint64_t etime = endtick(timeout);
  25.             while (!tstc()) {    /* while no incoming data */
  26.                 if (get_ticks() >= etime)
  27.                     return -2;    /* timed out */
  28.                 WATCHDOG_RESET();
  29.             }
  30.             first = 0;
  31.         }
  32.         ichar = getcmd_getch();
  33.         if ((ichar == '\n') || (ichar == '\r')) {
  34.             putc('\n');
  35.             break;
  36.         }
  37.         /*
  38.          * handle standard linux xterm esc sequences for arrow key, etc.
  39.          */
  40.         if (esc_len != 0) {
  41.             if (esc_len == 1) {
  42.                 if (ichar == '[') {
  43.                     esc_save[esc_len] = ichar;
  44.                     esc_len = 2;
  45.                 } else {
  46.                     cread_add_str(esc_save, esc_len, insert,
  47.                          &num, &eol_num, buf, *len);
  48.                     esc_len = 0;
  49.                 }
  50.                 continue;
  51.             }
  52.             switch (ichar) {
  53.             case 'D':    /* <- key */
  54.                 ichar = CTL_CH('b');
  55.                 esc_len = 0;
  56.                 break;
  57.             case 'C':    /* -> key */
  58.                 ichar = CTL_CH('f');
  59.                 esc_len = 0;
  60.                 break;    /* pass off to ^F handler */
  61.             case 'H':    /* Home key */
  62.                 ichar = CTL_CH('a');
  63.                 esc_len = 0;
  64.                 break;    /* pass off to ^A handler */
  65.             case 'A':    /* up arrow */
  66.                 ichar = CTL_CH('p');
  67.                 esc_len = 0;
  68.                 break;    /* pass off to ^P handler */
  69.             case 'B':    /* down arrow */
  70.                 ichar = CTL_CH('n');
  71.                 esc_len = 0;
  72.                 break;    /* pass off to ^N handler */
  73.             default:
  74.                 esc_save[esc_len++] = ichar;
  75.                 cread_add_str(esc_save, esc_len, insert,
  76.                      &num, &eol_num, buf, *len);
  77.                 esc_len = 0;
  78.                 continue;
  79.             }
  80.         }
  81.         switch (ichar) {
  82.         case 0x1b:
  83.             if (esc_len == 0) {
  84.                 esc_save[esc_len] = ichar;
  85.                 esc_len = 1;
  86.             } else {
  87.                 puts("impossible condition #876\n");
  88.                 esc_len = 0;
  89.             }
  90.             break;
  91.         case CTL_CH('a'):
  92.             BEGINNING_OF_LINE();
  93.             break;
  94.         case CTL_CH('c'):    /* ^C - break */
  95.             *buf = '\0';    /* discard input */
  96.             return (-1);
  97.         case CTL_CH('f'):
  98.             if (num < eol_num) {
  99.                 getcmd_putch(buf[num]);
  100.                 num++;
  101.             }
  102.             break;
  103.         case CTL_CH('b'):
  104.             if (num) {
  105.                 getcmd_putch(CTL_BACKSPACE);
  106.                 num--;
  107.             }
  108.             break;
  109.         case CTL_CH('d'):
  110.             if (num < eol_num) {
  111.                 wlen = eol_num - num - 1;
  112.                 if (wlen) {
  113.                     memmove(&buf[num], &buf[num+1], wlen);
  114.                     putnstr(buf + num, wlen);
  115.                 }
  116.                 getcmd_putch(' ');
  117.                 do {
  118.                     getcmd_putch(CTL_BACKSPACE);
  119.                 } while (wlen--);
  120.                 eol_num--;
  121.             }
  122.             break;
  123.         case CTL_CH('k'):
  124.             ERASE_TO_EOL();
  125.             break;
  126.         case CTL_CH('e'):
  127.             REFRESH_TO_EOL();
  128.             break;
  129.         case CTL_CH('o'):
  130.             insert = !insert;
  131.             break;
  132.         case CTL_CH('x'):
  133.         case CTL_CH('u'):
  134.             BEGINNING_OF_LINE();
  135.             ERASE_TO_EOL();
  136.             break;
  137.         case DEL:
  138.         case DEL7:
  139.         case 8:
  140.             if (num) {
  141.                 wlen = eol_num - num;
  142.                 num--;
  143.                 memmove(&buf[num], &buf[num+1], wlen);
  144.                 getcmd_putch(CTL_BACKSPACE);
  145.                 putnstr(buf + num, wlen);
  146.                 getcmd_putch(' ');
  147.                 do {
  148.                     getcmd_putch(CTL_BACKSPACE);
  149.                 } while (wlen--);
  150.                 eol_num--;
  151.             }
  152.             break;
  153.         case CTL_CH('p'):
  154.         case CTL_CH('n'):
  155.         {
  156.             char * hline;
  157.             esc_len = 0;
  158.             if (ichar == CTL_CH('p'))
  159.                 hline = hist_prev();
  160.             else
  161.                 hline = hist_next();
  162.             if (!hline) {
  163.                 getcmd_cbeep();
  164.                 continue;
  165.             }
  166.             /* nuke the current line */
  167.             /* first, go home */
  168.             BEGINNING_OF_LINE();
  169.             /* erase to end of line */
  170.             ERASE_TO_EOL();
  171.             /* copy new line into place and display */
  172.             strcpy(buf, hline);
  173.             eol_num = strlen(buf);
  174.             REFRESH_TO_EOL();
  175.             continue;
  176.         }
  177. #ifdef CONFIG_AUTO_COMPLETE
  178.         case '\t': {
  179.             int num2, col;
  180.             /* do not autocomplete when in the middle */
  181.             if (num < eol_num) {
  182.                 getcmd_cbeep();
  183.                 break;
  184.             }
  185.             buf[num] = '\0';
  186.             col = strlen(prompt) + eol_num;
  187.             num2 = num;
  188.             if (cmd_auto_complete(prompt, buf, &num2, &col)) {
  189.                 col = num2 - num;
  190.                 num += col;
  191.                 eol_num += col;
  192.             }
  193.             break;
  194.         }
  195. #endif
  196.         default:
  197.             cread_add_char(ichar, insert, &num, &eol_num, buf, *len);
  198.             break;
  199.         }
  200.     }
  201.     *len = eol_num;
  202.     buf[eol_num] = '\0';    /* lose the newline */
  203.     if (buf[0] && buf[0] != CREAD_HIST_CHAR)
  204.         cread_add_to_hist(buf);
  205.     hist_cur = hist_add_idx;
  206.     return 0;
  207. }

這些都是ANSI 控制碼,以ESC起始為標記,百度就知道了
在測試這個問題的時候,需要使用virtual box虛擬機器中的xp串列埠進行測試,百度半天也沒找到能完全說清楚的文章,下面介紹我個人的試驗結果和理解:
第一種是裸檔案,這種模式只能讓虛擬機器串列埠輸出資訊到宿主機(我的是ubuntu)的某個檔案中,開啟可以檢視虛擬機器串列埠發來的文字資訊,基本沒什麼用。

第二種是主機管道,這個模式可以讓主機和虛擬機器進行串列埠通訊,相當於建立一個模擬的串列埠通道,主機和虛擬機器互聯,這個模式非常有用,對於測試串列埠提供了很方便的測試條件,即使你電腦沒有真是串列埠也沒關係。再也不需要想辦法去破解windows那些收費的虛擬串列埠軟體了。

注意使用主機管道模式時,應在管道路徑前加上字首unix#,例如virtual box中設定了串列埠位置為/dev/xpserial,那麼minicom中應設定Serial Device 為  unix#/dev/xpserial

第三種是主機裝置,這個就相當於將真實主機繫結到虛擬機器了,這時候真串列埠只能給虛擬機器用,但是有延遲,有時候會丟資料,實際上個人認為如果只是簡單資料收發沒必要用這個模式,真串列埠給宿主機用就可以了。

F8:
ASCII 碼:^[[19~
十六進位制0x1b 0x5b 0x31 0x39 0x7e
第一個位元組0x1b是"^[",第二個位元組0x5b是'[',第3個位元組是0x31,第4位元組就得到'9', 同理,0x39得到'9',最後0x7e是'~'

\033\133\061\071\176
在securecrt做對映時可以使用 "\e[\061\071\176"

↑:
^[[A
^[是第一個位元組0x1b,第二個位元組0x5b是[,最後的0x41是A
0x1b 0x5b 0x41
\033\133\101

Shift + F8:
^[[19;2~
0x1b 0x5b 0x31 0x39 0x3b 0x32 0x7e
"\e[\061\071\073\062\176"

關於控制碼還可以參考:
http://bbs.csdn.net/topics/320015593