前言:
当我们使用嵌入式linux 进行开发时,kernel 跑起来之后,我们希望能通过串口(标准输入、输出),在应用程序正在运行的过程中,进行一些调试工作,例如,对CPU一些寄存进行调整,以观测调整以后的结果,并且,当我们无法把我们的应用程序放在后台运行,那么我们就需要实现一个基础的控制台。
下文中的控制台,虽然简单,但完备的支持 上 下 左 右 backspace del 常用控制台操作,使用 上 下 键可以浏览已经输入过的命令(类似 doskey 这样的功能),支持 光标 左右移动 修改命令
一般我们在 main 函数最后 都会做 while(TRUE) sleep(1000) 这样 阻塞住主线程,用这个控制台的实现,替换这个过程,则应用程序可增加控制台应用功能,各部分的具体实现如下:
调用代码(main.c):
#include
#include "app_console.h"
intmain(intargc,char*argv[])
{
// 之前的应用代码
....
....
....
App_Console_Start();
return0;
}
控制台头文件(app_console.h)
#ifndef __APP_CONSOLE_H__
#define __APP_CONSOLE_H__
#ifdef __cplusplus
extern"C"
{
#endif
#include "type_def.h"
voidApp_Console_Start();
#ifdef __cplusplus
}
#endif
#endif
控制台C文件(app_console.c)
#include "app_console.h"
#include "ctype.h"
#include "unistd.h"
#include "app_test.h"
// 说明
// read write 使用的是 POSIX 的标准文件读写函数
// unistd.h 包含了 STDIN_FILENO 等文件描述符的定义
// ctype.h 包含了 isprint 函数的声明
// 经过仔细考虑,决定不支持 ESC 键,因为ESC 键的键值为 0x1b 与 上下左右的键值重复
// 但可以考虑按2下ESC清除本行输入
// 对不可打印字符的处理仅限于以下已经列出的宏定义
// change:
// 放弃对 double ESC 的支持,因为可能出现按了 ESC 又按了 方向键 的情况
// 则用户输入编码为 '/x1b' '/x1b' '[' 'A' (按了ESC 又按了上键)
// change:
// 为了将应用与控制台应用剥离,则将 #define MAX_CMD_LEN 512 房到 app_test.h 中定义
// 二维数组作为参数进行传递时,需要明确第二个维度的大小,否则编译器无法正确定位地址
#define KEY_BACKSPACE '/x08' // back space
#define KEY_DEL '/x7F' // del
#define KEY_ENTER '/x0A' // 回车
// 以下为 0x1b 开头的键值
//#define KEY_DOUBLEESC "/x1B/x1B"// ESC
//#define KEY_ARROW_UP "/x1B[A" // 上
//#define KEY_ARROW_DOWN "/x1B[B" // 下
//#define KEY_ARROW_LEFT "/x1B[D" // 左
//#define KEY_ARROW_RIGHT "/x1B[C" // 右
typedefenum
{
WKS_WAIT,
WKS_RECV1B,
WKS_UDLR,
}T_WaitKeyState;
#define MAX_CMD_HISTORY 32
#define MAX_PAR_COUNT 16
staticcharszPrompt[] = {"TR_Console> "};
staticT_WaitKeyState waitKeyState = WKS_WAIT;
staticcharszCmdHistory[MAX_CMD_HISTORY][MAX_CMD_LEN];
staticcharszCmdNow[MAX_CMD_LEN] = {0};
staticUINT32nCmdIndex = 0;
staticUINT32nCmdCursor = 0;
staticUINT32nCmdInputCount = 0;
staticUINT32nCmdInputCursor = 0;
staticvoidApp_Console_ParseCmd(constchar* pCmd);
staticUINT32App_Console_ReadInput(char*input)
{
U32 nRead=0;
structpollfd p;
structtermio term,term_old;
/* Get control of the terminal */
ioctl(STDIN_FILENO,TCGETA,(void*)&term);
term_old = term;
term.c_lflag &= ~ICANON;
term.c_lflag &= ~ECHO;
ioctl(STDIN_FILENO,TCSETAW,(void*)&term);
/* Get event we want to know */
p.fd = STDIN_FILENO;
p.events = POLLIN;
/* If we receive one thing, get the byte now */
if(poll(&p,1,-1)>0)
{
if(p.revents==POLLIN)
{
nRead=read(STDIN_FILENO,input,1);
}
}
/* Purge the byte */
/* tcflush(0,TCIOFLUSH); */
/* Leave control */
ioctl(STDIN_FILENO,TCSETAW,(void*)&term_old);
return(nRead);
}
staticvoidApp_Console_BSChar()
{
charszTemp[3];
szTemp[0] ='/b';
szTemp[1] =' ';
szTemp[2] ='/b';
write(STDOUT_FILENO,szTemp,3);
}
staticvoidApp_Console_OnPrintChar(charinput)
{
if(nCmdInputCount == MAX_CMD_LEN)return;
if(nCmdInputCursor
{
charszTemp[MAX_CMD_LEN] = {0};
char* pCmd = szCmdNow;
intnBackCount = nCmdInputCount-nCmdInputCursor;
szTemp[0] = input;
memcpy(&szTemp[1],&pCmd[nCmdInputCursor],nBackCount);
write(STDOUT_FILENO,szTemp,nBackCount+1);
memcpy(&pCmd[nCmdInputCursor],&szTemp,nBackCount+1);
memset(szTemp,'/b',nBackCount);
write(STDOUT_FILENO,szTemp,nBackCount);
}
else
{
write(STDOUT_FILENO,&input,1);
szCmdNow[nCmdInputCount] = input;
}
nCmdInputCursor++;
nCmdInputCount++;
}
staticvoidApp_Console_OnBackspace()
{
if(nCmdInputCursor > 0)
{
if(nCmdInputCursor == nCmdInputCount)// 光标在末尾
App_Console_BSChar();
else// 光标不在末尾
{
charszTemp[MAX_CMD_LEN] = {0};
char* pCmd = szCmdNow;
intnBackCount = nCmdInputCount-nCmdInputCursor;
szTemp[0] ='/b';
memcpy(&szTemp[1],&pCmd[nCmdInputCursor],nBackCount);
szTemp[nBackCount+1] =' ';
write(STDOUT_FILENO,szTemp,nBackCount+2);
memcpy(&pCmd[nCmdInputCursor-1],&szTemp[1],nBackCount);
memset(szTemp,'/b',nBackCount+1);
write(STDOUT_FILENO,szTemp,nBackCount+1);
}
nCmdInputCount --;
nCmdInputCursor--;
}
}
staticvoidApp_Console_OnDel()
{
if(nCmdInputCursor
{
charszTemp[MAX_CMD_LEN] = {0};
char* pCmd = szCmdNow;
intnBackCount = nCmdInputCount-nCmdInputCursor-1;
memcpy(szTemp,&pCmd[nCmdInputCursor+1],nBackCount);
szTemp[nBackCount] =' ';
write(STDOUT_FILENO,szTemp,nBackCount+1);
memcpy(&pCmd[nCmdInputCursor],szTemp,nBackCount);
memset(szTemp,'/b',nBackCount+1);
write(STDOUT_FILENO,szTemp,nBackCount+1);
nCmdInputCount--;
}
}
staticvoidApp_Console_OnDoubleEsc()
{
if(nCmdInputCount > 0)
{
char* pCmd = szCmdNow;
// 将光标移动到最末尾
while(nCmdInputCursor
{
write(STDOUT_FILENO,&pCmd[nCmdInputCursor],1);
nCmdInputCursor++;
}
// 清除所有输入的数据
inti=0;
for(i=0;i
nCmdInputCount = 0;
nCmdInputCursor = 0;