Welcome 微信登录

首页 / 操作系统 / Linux / 嵌入式Linux 下 通用 console(控制台)的实现

前言:当我们使用嵌入式linux 进行开发时,kernel 跑起来之后,我们希望能通过串口(标准输入、输出),在应用程序正在运行的过程中,进行一些调试工作,例如,对CPU一些寄存进行调整,以观测调整以后的结果,并且,当我们无法把我们的应用程序放在后台运行,那么我们就需要实现一个基础的控制台。下文中的控制台,虽然简单,但完备的支持 上 下 左 右 backspace del 常用控制台操作,使用 上 下 键可以浏览已经输入过的命令(类似 doskey 这样的功能),支持 光标 左右移动 修改命令一般我们在 main 函数最后 都会做 while(TRUE) sleep(1000) 这样 阻塞住主线程,用这个控制台的实现,替换这个过程,则应用程序可增加控制台应用功能,各部分的具体实现如下: 调用代码(main.c):
  1. #include <stdio.h>   
  2. #include "app_console.h"   
  3.   
  4. int main(int argc, char *argv[])  
  5. {  
  6.     // 之前的应用代码   
  7.     ....  
  8.     ....  
  9.     ....  
  10.     App_Console_Start();      
  11.     return 0;  
  12. }  
控制台头文件(app_console.h) 
  1. #ifndef __APP_CONSOLE_H__   
  2. #define __APP_CONSOLE_H__   
  3.   
  4. #ifdef __cplusplus   
  5. extern "C"  
  6. {  
  7. #endif       
  8.   
  9. #include "type_def.h"   
  10.   
  11. void App_Console_Start();  
  12.   
  13.   
  14.   
  15. #ifdef __cplusplus   
  16. }  
  17. #endif   
  18.   
  19. #endif  
控制台C文件(app_console.c)
  1. #include "app_console.h"   
  2. #include "ctype.h"   
  3. #include "unistd.h"   
  4. #include "app_test.h"   
  5.   
  6. // 说明   
  7. // read write 使用的是 POSIX 的标准文件读写函数   
  8. // unistd.h 包含了 STDIN_FILENO 等文件描述符的定义   
  9. // ctype.h 包含了 isprint 函数的声明   
  10. // 经过仔细考虑,决定不支持 ESC 键,因为ESC 键的键值为 0x1b 与 上下左右的键值重复   
  11. // 但可以考虑按2下ESC清除本行输入   
  12. // 对不可打印字符的处理仅限于以下已经列出的宏定义   
  13.   
  14. // change:   
  15. // 放弃对 double ESC 的支持,因为可能出现按了 ESC 又按了 方向键 的情况   
  16. // 则用户输入编码为 "/x1b" "/x1b" "[" "A" (按了ESC 又按了上键)   
  17.   
  18. // change:   
  19. // 为了将应用与控制台应用剥离,则将 #define MAX_CMD_LEN 512 房到 app_test.h 中定义   
  20. // 二维数组作为参数进行传递时,需要明确第二个维度的大小,否则编译器无法正确定位地址   
  21.   
  22.   
  23.   
  24.   
  25. #define KEY_BACKSPACE    "/x08"    // back space    
  26. #define KEY_DEL          "/x7F"    // del    
  27. #define KEY_ENTER        "/x0A"    // 回车   
  28.   
  29. // 以下为 0x1b 开头的键值   
  30. //#define KEY_DOUBLEESC    "/x1B/x1B"// ESC   
  31. //#define KEY_ARROW_UP     "/x1B[A"  // 上   
  32. //#define KEY_ARROW_DOWN   "/x1B[B"  // 下   
  33. //#define KEY_ARROW_LEFT   "/x1B[D"  // 左   
  34. //#define KEY_ARROW_RIGHT  "/x1B[C"  // 右   
  35.   
  36. typedef enum   
  37. {  
  38.     WKS_WAIT,  
  39.     WKS_RECV1B,  
  40.     WKS_UDLR,  
  41. }T_WaitKeyState;  
  42.   
  43. #define MAX_CMD_HISTORY 32   
  44. #define MAX_PAR_COUNT 16   
  45.   
  46. static char szPrompt[] = {"TR_Console> "};  
  47.   
  48. static T_WaitKeyState waitKeyState = WKS_WAIT;  
  49. static char szCmdHistory[MAX_CMD_HISTORY][MAX_CMD_LEN];  
  50. static char szCmdNow[MAX_CMD_LEN] = {0};  
  51.   
  52. static UINT32 nCmdIndex = 0;  
  53. static UINT32 nCmdCursor = 0;  
  54. static UINT32 nCmdInputCount = 0;  
  55. static UINT32 nCmdInputCursor = 0;  
  56.   
  57. static void App_Console_ParseCmd(const char* pCmd);  
  58.   
  59. static UINT32 App_Console_ReadInput(char *input)  
  60. {  
  61.     U32    nRead=0;  
  62.     struct pollfd p;  
  63.     struct termio term,term_old;  
  64.   
  65.     /* Get control of the terminal */  
  66.     ioctl(STDIN_FILENO,TCGETA,(void *)&term);  
  67.     term_old = term;  
  68.     term.c_lflag &= ~ICANON;  
  69.     term.c_lflag &= ~ECHO;  
  70.     ioctl(STDIN_FILENO,TCSETAW,(void *)&term);  
  71.   
  72.     /* Get event we want to know */  
  73.     p.fd     = STDIN_FILENO;  
  74.     p.events = POLLIN;  
  75.   
  76.     /* If we receive one thing, get the byte now */  
  77.     if (poll(&p,1,-1)>0)  
  78.     {  
  79.         if (p.revents==POLLIN)  
  80.         {  
  81.             nRead=read(STDIN_FILENO,input,1);  
  82.         }  
  83.     }  
  84.     /* Purge the byte */  
  85.     /* tcflush(0,TCIOFLUSH); */  
  86.     /* Leave control */  
  87.     ioctl(STDIN_FILENO,TCSETAW,(void *)&term_old);  
  88.     return(nRead);  
  89. }  
  90.   
  91. static void App_Console_BSChar()  
  92. {  
  93.     char szTemp[3];  
  94.     szTemp[0] = "/b";  
  95.     szTemp[1] = " ";  
  96.     szTemp[2] = "/b";  
  97.     write(STDOUT_FILENO,szTemp,3);    
  98. }  
  99.   
  100. static void App_Console_OnPrintChar(char input)  
  101. {  
  102.     if(nCmdInputCount == MAX_CMD_LEN) return;  
  103.   
  104.     if(nCmdInputCursor < nCmdInputCount) // 光标不在末尾   
  105.     {  
  106.         char szTemp[MAX_CMD_LEN] = {0};  
  107.         char* pCmd = szCmdNow;  
  108.         int nBackCount = nCmdInputCount-nCmdInputCursor;  
  109.         szTemp[0] = input;  
  110.         memcpy(&szTemp[1],&pCmd[nCmdInputCursor],nBackCount);                                         
  111.         write(STDOUT_FILENO,szTemp,nBackCount+1);                                 
  112.         memcpy(&pCmd[nCmdInputCursor],&szTemp,nBackCount+1);  
  113.   
  114.         memset(szTemp,"/b",nBackCount);  
  115.         write(STDOUT_FILENO,szTemp,nBackCount);   
  116.     }  
  117.     else  
  118.     {  
  119.         write(STDOUT_FILENO,&input,1);  
  120.         szCmdNow[nCmdInputCount] = input;  
  121.     }  
  122.   
  123.     nCmdInputCursor++;  
  124.     nCmdInputCount++;  
  125. }  
  126.   
  127. static void App_Console_OnBackspace()  
  128. {  
  129.     if(nCmdInputCursor > 0)  
  130.     {  
  131.         if(nCmdInputCursor == nCmdInputCount) // 光标在末尾   
  132.             App_Console_BSChar();  
  133.         else// 光标不在末尾   
  134.         {  
  135.             char szTemp[MAX_CMD_LEN] = {0};  
  136.             char* pCmd = szCmdNow;  
  137.             int nBackCount = nCmdInputCount-nCmdInputCursor;  
  138.             szTemp[0] = "/b";  
  139.             memcpy(&szTemp[1],&pCmd[nCmdInputCursor],nBackCount);  
  140.             szTemp[nBackCount+1] = " ";                                   
  141.             write(STDOUT_FILENO,szTemp,nBackCount+2);                                 
  142.             memcpy(&pCmd[nCmdInputCursor-1],&szTemp[1],nBackCount);  
  143.   
  144.             memset(szTemp,"/b",nBackCount+1);  
  145.             write(STDOUT_FILENO,szTemp,nBackCount+1);                 
  146.         }  
  147.         nCmdInputCount --;  
  148.         nCmdInputCursor--;  
  149.     }  
  150. }  
  151.   
  152. static void App_Console_OnDel()  
  153. {  
  154.     if(nCmdInputCursor < nCmdInputCount) // 光标不在末尾   
  155.     {                             
  156.         char szTemp[MAX_CMD_LEN] = {0};  
  157.         char* pCmd = szCmdNow;  
  158.           
  159.         int nBackCount = nCmdInputCount-nCmdInputCursor-1;  
  160.         memcpy(szTemp,&pCmd[nCmdInputCursor+1],nBackCount);  
  161.         szTemp[nBackCount] = " ";  
  162.         write(STDOUT_FILENO,szTemp,nBackCount+1);                                 
  163.         memcpy(&pCmd[nCmdInputCursor],szTemp,nBackCount);  
  164.   
  165.         memset(szTemp,"/b",nBackCount+1);  
  166.         write(STDOUT_FILENO,szTemp,nBackCount+1);                 
  167.         nCmdInputCount--;                                 
  168.     }  
  169. }  
  170.   
  171. static void App_Console_OnDoubleEsc()  
  172. {  
  173.     if(nCmdInputCount > 0)  
  174.     {  
  175.         char* pCmd = szCmdNow;  
  176.   
  177.         // 将光标移动到最末尾   
  178.         while(nCmdInputCursor < nCmdInputCount)  
  179.         {  
  180.             write(STDOUT_FILENO,&pCmd[nCmdInputCursor],1);  
  181.             nCmdInputCursor++;                                    
  182.         }  
  183.   
  184.         // 清除所有输入的数据   
  185.         int i=0;  
  186.         for(i=0;i<nCmdInputCount;i++) App_Console_BSChar();  
  187.         nCmdInputCount = 0;  
  188.         nCmdInputCursor = 0;  
  189.     }  
  190. }  
  191.   
  192. static void App_Console_OnUp()  
  193. {     
  194.     if(nCmdCursor == 0) return;  
  195.     nCmdCursor --;  
  196.   
  197.     // 清除掉现在所有的输入   
  198.     App_Console_OnDoubleEsc();    
  199.       
  200.     char* pCmdHistory = &szCmdHistory[nCmdCursor][0];     
  201.     memcpy(szCmdNow,pCmdHistory,MAX_CMD_LEN);     
  202.     nCmdInputCount = strlen(szCmdNow);  
  203.     nCmdInputCursor= nCmdInputCount;  
  204.     write(STDOUT_FILENO,szCmdNow,nCmdInputCount);     
  205. }  
  206.   
  207. static void App_Console_OnDown()  
  208. {  
  209.     if(nCmdCursor >= (nCmdIndex-1)) return;  
  210.     nCmdCursor ++;  
  211.       
  212.     // 清除掉现在所有的输入   
  213.     App_Console_OnDoubleEsc();    
  214.       
  215.     char* pCmdHistory = &szCmdHistory[nCmdCursor][0];     
  216.     memcpy(szCmdNow,pCmdHistory,MAX_CMD_LEN);     
  217.     nCmdInputCount = strlen(szCmdNow);  
  218.     nCmdInputCursor= nCmdInputCount;  
  219.     write(STDOUT_FILENO,szCmdNow,nCmdInputCount);     
  220.       
  221. }  
  222.   
  223. static void App_Console_OnLeft()  
  224. {  
  225.     if(nCmdInputCursor > 0)  
  226.     {  
  227.         char c = "/b";  
  228.         write(STDOUT_FILENO,&c,1);  
  229.         nCmdInputCursor--;  
  230.     }  
  231. }  
  232.   
  233. static void App_Console_OnRight()  
  234. {  
  235.     if(nCmdInputCursor < nCmdInputCount)  
  236.     {  
  237.         char* pCmd = szCmdNow;  
  238.         char c = pCmd[nCmdInputCursor];  
  239.         write(STDOUT_FILENO,&c,1);  
  240.         nCmdInputCursor++;  
  241.     }  
  242. }  
  243.   
  244. static void App_Console_OnEnter()  
  245. {     
  246.     szCmdNow[nCmdInputCount] = "/0";  
  247.     char szTemp[] = {"/r/n"};  
  248.     write(STDOUT_FILENO,szTemp,strlen(szTemp));  
  249.     nCmdInputCount = 0;  
  250.     nCmdInputCursor = 0;  
  251.   
  252.     if(strlen(szCmdNow) == 0) return;  
  253.   
  254.     if(nCmdIndex == MAX_CMD_HISTORY) // 命令队列满了,移动   
  255.     {  
  256.         char szTempCmd[MAX_CMD_HISTORY][MAX_CMD_LEN];  
  257.         memcpy(szTempCmd,&szCmdHistory[1][0],MAX_CMD_LEN*(MAX_CMD_HISTORY-1));  
  258.         memcpy(szCmdHistory,szTempCmd,MAX_CMD_LEN*(MAX_CMD_HISTORY-1));  
  259.         nCmdIndex = MAX_CMD_HISTORY-1;  
  260.         nCmdCursor = nCmdIndex;  
  261.     }  
  262.       
  263.   
  264.     memcpy(szCmdHistory[nCmdIndex],szCmdNow,MAX_CMD_LEN);  
  265.       
  266.     nCmdIndex++;  
  267.     nCmdCursor = nCmdIndex;   
  268.   
  269.     // 解析命令   
  270.     App_Console_ParseCmd(szCmdNow);  
  271. }  
  272.   
  273. static void App_Console_CmdLoop()  
  274. {  
  275.     BOOL bGetEnter = FALSE;  
  276.     while(TRUE)  
  277.     {     
  278.         // 读取一个console输入   
  279.         UINT32 nReturn = 0;  
  280.         char input;  
  281.         nReturn = App_Console_ReadInput (&input);  
  282.         if(nReturn > 0)  
  283.         {             
  284.             switch(waitKeyState)  
  285.             {  
  286.                 case WKS_WAIT :  
  287.                     if(isprint(input))// 可打印字符   
  288.                         App_Console_OnPrintChar(input);  
  289.                     else  
  290.                     {  
  291.                         if(input == KEY_BACKSPACE)  
  292.                             App_Console_OnBackspace();  
  293.                         else if(input == KEY_DEL)  
  294.                             App_Console_OnDel();  
  295.                         else if(input == KEY_ENTER)  
  296.                         {  
  297.                             App_Console_OnEnter();  
  298.                             bGetEnter = TRUE;  
  299.                         }  
  300.                         else if(input == "/x1b")  
  301.                             waitKeyState = WKS_RECV1B;  
  302.                         else  
  303.                             waitKeyState = WKS_WAIT;  
  304.                     }                         
  305.                     break;  
  306.                 case WKS_RECV1B:  
  307.                     if(input == "/x1b") // 按了ESC 又按了方向键,或者是 ESC   
  308.                     {  
  309.                         //App_Console_OnDoubleEsc();   
  310.                         waitKeyState = WKS_RECV1B;  
  311.                     }  
  312.                     else   
  313.   
  314.                     if(input == "[") //可能为 上下左右 4个键   
  315.                         waitKeyState = WKS_UDLR;  
  316.                     else//下面的情况为 按了 ESC 键之后,按了其他的键的处理   
  317.                     {                         
  318.                         if(isprint(input)) App_Console_OnPrintChar(input);                            
  319.                         waitKeyState = WKS_WAIT;  
  320.                     }  
  321.                     break;  
  322.                 case WKS_UDLR:  
  323.                     if(input == "A"// 上   
  324.                         App_Console_OnUp();  
  325.                     else if(input == "B"// 下   
  326.                         App_Console_OnDown();  
  327.                     else if(input == "D"// 左   
  328.                         App_Console_OnLeft();  
  329.                     else if(input == "C"// 右   
  330.                         App_Console_OnRight();  
  331.                     else  
  332.                     {  
  333.                         if(isprint(input)) App_Console_OnPrintChar(input);  
  334.                     }  
  335.                     waitKeyState = WKS_WAIT;                      
  336.                     break;  
  337.                 default:  
  338.                     break;  
  339.             }  
  340.         }  
  341.   
  342.         if(bGetEnter)   
  343.         {             
  344.             break;  
  345.         }  
  346.     }  
  347. }  
  348.   
  349. void App_Console_Start()  
  350. {  
  351.     // 清空 sdtout 缓冲   
  352.     fflush(stdout);  
  353.     fflush(stdin);  
  354.   
  355.     char szTemp[] = {"/r/nStart TR Console.../r/n/r/n"};  
  356.     write(STDOUT_FILENO,szTemp,strlen(szTemp));  
  357.   
  358.     while(TRUE)  
  359.     {  
  360.         write(STDOUT_FILENO,szPrompt,strlen(szPrompt));  
  361.         App_Console_CmdLoop();  
  362.     }  
  363. }  
  364.   
  365. // 解析命令   
  366. static void App_Console_ParseCmd(const char* pCmd)  
  367. {  
  368.     int length = strlen(pCmd);   
  369.       
  370.     // 全部转小写   
  371.     int i=0;  
  372.     char szTempCmd[MAX_CMD_LEN];  
  373.     for (i=0; i < length; i++) szTempCmd[i] = tolower(pCmd[i]);   
  374.   
  375.   
  376.     // 将输入的命令各个 part 分开    
  377.     char szPart[MAX_PAR_COUNT][MAX_CMD_LEN];  
  378.     int nPartCount = 0;  
  379.     int nPartCursor = 0;  
  380.     int nCmdCursor = 0;  
  381.     memset(szPart,0,sizeof(szPart));  
  382.   
  383.     while(TRUE)  
  384.     {  
  385.         if(nCmdCursor == length)   
  386.         {  
  387.             if(nPartCursor > 0)  
  388.             {  
  389.                 nPartCount++;  
  390.                 nPartCursor = 0;  
  391.             }  
  392.             break;            
  393.         }  
  394.           
  395.         if( (szTempCmd[nCmdCursor] == ",") || (szTempCmd[nCmdCursor] == " ") ) // part 分割符   
  396.         {  
  397.             szPart[nPartCount][nPartCursor] = "/0";  
  398.               
  399.             nPartCount++;  
  400.             nPartCursor = 0;  
  401.         }  
  402.         else   
  403.         {  
  404.             szPart[nPartCount][nPartCursor] = szTempCmd[nCmdCursor];  
  405.             nPartCursor++;  
  406.         }  
  407.         nCmdCursor++;         
  408.     }  
  409.     App_Test_OnCmd(szPart,nPartCount);        
  410. }  
 命令实现头文件(app_test.h)view plaincopy to clipboardprint?
  1. #ifndef __APP_TEST_H__   
  2. #define __APP_TEST_H__   
  3.   
  4. #ifdef __cplusplus   
  5. extern "C"  
  6. {  
  7. #endif       
  8.   
  9. #include "type_def.h"   
  10.   
  11. #define MAX_CMD_LEN 512   
  12.   
  13. void App_Test_OnCmd(char szPart[][MAX_CMD_LEN],int nPartCount);  
  14.   
  15.   
  16.   
  17. #ifdef __cplusplus   
  18. }  
  19. #endif   
  20.   
  21. #endif