diff --git a/README.md b/README.md index d7550c4..fe3d468 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # letter shell 3.x -![version](https://img.shields.io/badge/version-3.1.0-brightgreen.svg) +![version](https://img.shields.io/badge/version-3.1.1-brightgreen.svg) ![standard](https://img.shields.io/badge/standard-c99-brightgreen.svg) -![build](https://img.shields.io/badge/build-2021.05.24-brightgreen.svg) +![build](https://img.shields.io/badge/build-2021.06.27-brightgreen.svg) ![license](https://img.shields.io/badge/license-MIT-brightgreen.svg) 一个功能强大的嵌入式shell @@ -165,6 +165,7 @@ | SHELL_PARAMETER_MAX_NUMBER | shell命令参数最大数量 | | SHELL_HISTORY_MAX_NUMBER | 历史命令记录数量 | | SHELL_DOUBLE_CLICK_TIME | 双击间隔(ms) | + | SHELL_QUICK_HELP | 快速帮助 | | SHELL_MAX_NUMBER | 管理的最大shell数量 | | SHELL_GET_TICK() | 获取系统时间(ms) | | SHELL_USING_LOCK | 是否使用锁 | @@ -283,7 +284,7 @@ letter shell采取一个静态数组对定义的多个shell进行管理,shell ### 执行未导出函数 -letter shell支持通过函数地址直接执行函数,可以方便执行那些没有导出,但是有临时需要使用的函数,使用命令`exec [addr] [args]`执行,使用此功能需要开启`SHELL_EXEC_UNDEF_FUNC`宏,注意,由于直接操作函数地址执行,如果给进的地址有误,可能引起程序崩溃 +letter shell支持通过函数地址直接执行函数,可以方便执行那些没有导出,但是又临时需要使用的函数,使用命令`exec [addr] [args]`执行,使用此功能需要开启`SHELL_EXEC_UNDEF_FUNC`宏,注意,由于直接操作函数地址执行,如果给进的地址有误,可能引起程序崩溃 函数的地址可以通过编译生成的文件查找,比如说对于keil,可以在`.map`文件中查找到每个函数的地址,对于keil,`.map`文件中的地址需要偏移一个字节,才可以成功执行,比如说`shellClear`函数地址为`0x08028620`,则通过`exec`执行应为`exec 0x08028621` @@ -297,7 +298,7 @@ letter shell 3.x将可执行的函数命令定义,用户定义,按键定义 letter shell 支持使用命令导出方式和命令表方式进行命令的添加,定义,通过宏```SHELL_USING_CMD_EXPORT```控制 -命令导出方式支持keil,IAR(未测试)以及GCC +命令导出方式支持keil,IAR以及GCC 1. 命令导出方式 diff --git a/demo/x86-gcc/shell_cfg.h b/demo/x86-gcc/shell_cfg.h index 974f5bf..78a1860 100644 --- a/demo/x86-gcc/shell_cfg.h +++ b/demo/x86-gcc/shell_cfg.h @@ -13,6 +13,7 @@ #define __SHELL_CFG_H__ #include "stdlib.h" +unsigned int userGetTick(); /** * @brief 是否使用默认shell任务while循环,使能宏`SHELL_USING_TASK`后此宏有意义 @@ -101,6 +102,12 @@ */ #define SHELL_DOUBLE_CLICK_TIME 200 +/** + * @brief 快速帮助 + * 作用于双击tab的场景,当使能此宏时,双击tab不会对命令进行help补全,而是直接显示对应命令的帮助信息 + */ +#define SHELL_QUICK_HELP 1 + /** * @brief 管理的最大shell数量 */ @@ -124,7 +131,7 @@ * 定义此宏为获取系统Tick,如`HAL_GetTick()` * @note 此宏不定义时无法使用双击tab补全命令help,无法使用shell超时锁定 */ -#define SHELL_GET_TICK() 0 +#define SHELL_GET_TICK() userGetTick() /** * @brief 使用锁 diff --git a/demo/x86-gcc/shell_port.c b/demo/x86-gcc/shell_port.c index 8b5e50f..f882298 100644 --- a/demo/x86-gcc/shell_port.c +++ b/demo/x86-gcc/shell_port.c @@ -18,6 +18,7 @@ #include #include #include +#include Shell shell; char shellBuffer[512]; @@ -28,6 +29,18 @@ Log log = { .level = LOG_DEBUG }; +/** + * @brief 获取系统tick + * + * @return unsigned int 系统tick + */ +unsigned int userGetTick() +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return (tv.tv_sec * 1000 + tv.tv_usec / 1000) & 0x7FFFFFFF; +} + /** * @brief 日志写函数实现 * @@ -73,8 +86,8 @@ unsigned short userShellRead(char *data, unsigned short len) { *data++ = getchar(); } - system("stty icanon"); - system("stty echo"); + // system("stty icanon"); + // system("stty echo"); return len; } @@ -142,8 +155,8 @@ void userShellInit(void) log.write = terminalLogWrite; logRegister(&log, &shell); - logDebug("hello world"); - logHexDump(LOG_ALL_OBJ, LOG_DEBUG, (void *)&shell, sizeof(shell)); + // logDebug("hello world"); + // logHexDump(LOG_ALL_OBJ, LOG_DEBUG, (void *)&shell, sizeof(shell)); } diff --git a/extensions/shell_enhance/shell_passthrough.c b/extensions/shell_enhance/shell_passthrough.c index f951c58..fec78a5 100644 --- a/extensions/shell_enhance/shell_passthrough.c +++ b/extensions/shell_enhance/shell_passthrough.c @@ -40,20 +40,21 @@ unsigned int shellPassthrough(Shell *shell, const char *prompt, ShellPassthrough { if (data == '\r' || data == '\n') { - if (shell->parser.length == 0) + shellWriteString(shell, "\r\n"); + if (shell->parser.length != 0) { - continue; + shell->parser.buffer[shell->parser.length] = 0; + handler(shell->parser.buffer, shell->parser.length); + shell->parser.length = 0; + shell->parser.cursor = 0; } - shellWriteString(shell, "\r\n"); - shell->parser.buffer[shell->parser.length] = 0; - handler(shell->parser.buffer, shell->parser.length); - shell->parser.length = 0; - shell->parser.cursor = 0; shellWriteString(shell, prompt); } else if (data == SHELL_PASSTHROUGH_EXIT_KEY) { shellWriteString(shell, "\r\n"); + shell->parser.length = 0; + shell->parser.cursor = 0; return -1; } else diff --git a/src/shell.c b/src/shell.c index a5daebc..507e8e1 100644 --- a/src/shell.c +++ b/src/shell.c @@ -158,6 +158,7 @@ ShellCommand* shellSeekCommand(Shell *shell, const char *cmd, ShellCommand *base, unsigned short compareLength); +static void shellWriteCommandHelp(Shell *shell, char *cmd); /** * @brief shell 初始化 @@ -168,18 +169,21 @@ void shellInit(Shell *shell, char *buffer, unsigned short size) { shell->parser.length = 0; shell->parser.cursor = 0; - shell->history.offset = 0; - shell->history.number = 0; - shell->history.record = 0; shell->info.user = NULL; shell->status.isChecked = 1; shell->parser.buffer = buffer; shell->parser.bufferSize = size / (SHELL_HISTORY_MAX_NUMBER + 1); + +#if SHELL_HISTORY_MAX_NUMBER > 0 + shell->history.offset = 0; + shell->history.number = 0; + shell->history.record = 0; for (short i = 0; i < SHELL_HISTORY_MAX_NUMBER; i++) { shell->history.item[i] = buffer + shell->parser.bufferSize * (i + 1); } +#endif /** SHELL_HISTORY_MAX_NUMBER > 0 */ #if SHELL_USING_CMD_EXPORT == 1 #if defined(__CC_ARM) || (defined(__ARMCC_VERSION) && __ARMCC_VERSION >= 6000000) @@ -1271,6 +1275,7 @@ static void shellWriteReturnValue(Shell *shell, int value) } +#if SHELL_HISTORY_MAX_NUMBER > 0 /** * @brief shell历史记录添加 * @@ -1350,6 +1355,7 @@ static void shellHistory(Shell *shell, signed char dir) } } +#endif /** SHELL_HISTORY_MAX_NUMBER > 0 */ /** @@ -1382,7 +1388,9 @@ void shellExec(Shell *shell) if (shell->status.isChecked) { + #if SHELL_HISTORY_MAX_NUMBER > 0 shellHistoryAdd(shell); + #endif /** SHELL_HISTORY_MAX_NUMBER > 0 */ shellParserParam(shell); shell->parser.length = shell->parser.cursor = 0; if (shell->parser.paramCount == 0) @@ -1411,6 +1419,7 @@ void shellExec(Shell *shell) } +#if SHELL_HISTORY_MAX_NUMBER > 0 /** * @brief shell上方向键输入 * @@ -1433,6 +1442,7 @@ void shellDown(Shell *shell) shellHistory(shell, -1); } SHELL_EXPORT_KEY(SHELL_CMD_PERMISSION(0), 0x1B5B4200, shellDown, down); +#endif /** SHELL_HISTORY_MAX_NUMBER > 0 */ /** @@ -1543,6 +1553,12 @@ void shellTab(Shell *shell) && shell->status.tabFlag && SHELL_GET_TICK() - shell->info.activeTime < SHELL_DOUBLE_CLICK_TIME) { + #if SHELL_QUICK_HELP == 1 + shellWriteString(shell, "\r\n"); + shellWriteCommandHelp(shell, shell->parser.buffer); + shellWritePrompt(shell, 1); + shellWriteString(shell, shell->parser.buffer); + #else shellClearCommandLine(shell); for (short i = shell->parser.length; i >= 0; i--) { @@ -1553,6 +1569,7 @@ void shellTab(Shell *shell) shell->parser.length += 5; shell->parser.cursor = shell->parser.length; shellWriteString(shell, shell->parser.buffer); + #endif } else { @@ -1614,6 +1631,31 @@ SHELL_EXPORT_KEY(SHELL_CMD_PERMISSION(0)|SHELL_CMD_ENABLE_UNCHECKED, 0x0D0A0000, shellEnter, enter); #endif +/** + * @brief shell 写命令帮助信息 + * + * @param shell shell对象 + * @param cmd 命令字符串 + */ +static void shellWriteCommandHelp(Shell *shell, char *cmd) +{ + ShellCommand *command = shellSeekCommand(shell, + cmd, + shell->commandList.base, + 0); + if (command) + { + shellWriteString(shell, shellText[SHELL_TEXT_HELP_HEADER]); + shellWriteString(shell, shellGetCommandName(command)); + shellWriteString(shell, "\r\n"); + shellWriteString(shell, shellGetCommandDesc(command)); + shellWriteString(shell, "\r\n"); + } + else + { + shellWriteString(shell, shellText[SHELL_TEXT_CMD_NOT_FOUND]); + } +} /** * @brief shell help @@ -1631,22 +1673,7 @@ void shellHelp(int argc, char *argv[]) } else if (argc > 1) { - ShellCommand *command = shellSeekCommand(shell, - argv[1], - shell->commandList.base, - 0); - if (command) - { - shellWriteString(shell, shellText[SHELL_TEXT_HELP_HEADER]); - shellWriteString(shell, shellGetCommandName(command)); - shellWriteString(shell, "\r\n"); - shellWriteString(shell, shellGetCommandDesc(command)); - shellWriteString(shell, "\r\n"); - } - else - { - shellWriteString(shell, shellText[SHELL_TEXT_CMD_NOT_FOUND]); - } + shellWriteCommandHelp(shell, argv[1]); } } SHELL_EXPORT_CMD( diff --git a/src/shell.h b/src/shell.h index 5f2aa56..f05a5e3 100644 --- a/src/shell.h +++ b/src/shell.h @@ -14,7 +14,7 @@ #include "shell_cfg.h" -#define SHELL_VERSION "3.1.0" /**< 版本号 */ +#define SHELL_VERSION "3.1.1" /**< 版本号 */ /** @@ -341,6 +341,7 @@ typedef struct shell_def unsigned short paramCount; /**< 参数数量 */ int keyValue; /**< 输入按键键值 */ } parser; +#if SHELL_HISTORY_MAX_NUMBER > 0 struct { char *item[SHELL_HISTORY_MAX_NUMBER]; /**< 历史记录 */ @@ -348,6 +349,7 @@ typedef struct shell_def unsigned short record; /**< 当前记录位置 */ signed short offset; /**< 当前历史记录偏移 */ } history; +#endif /** SHELL_HISTORY_MAX_NUMBER > 0 */ struct { void *base; /**< 命令表基址 */ diff --git a/src/shell_cfg.h b/src/shell_cfg.h index 37cabaf..55e1393 100644 --- a/src/shell_cfg.h +++ b/src/shell_cfg.h @@ -100,6 +100,13 @@ */ #define SHELL_DOUBLE_CLICK_TIME 200 +/** + * @brief 快速帮助 + * 作用于双击tab的场景,当使能此宏时,双击tab不会对命令进行help补全,而是直接显示对应命令的帮助信息 + */ +#define SHELL_QUICK_HELP 1 + + /** * @brief 管理的最大shell数量 */