readline中的键盘码^[[A,^[[B,^[[D,^[[C (ANSI控制码)

时间:2022-08-29 00:19:40
最近调试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. richard@richard-H81M-DS2:~/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