嵌入式命令行操作(可移植性)
本文先贴出代码,参考u-boot命令行
/*
****************************************************************************
* File : cmd.c
*
* Creat on : Otc 08, 2018
* Author : Flinn
*
* @beirf :
*
* @Note(s) :
*
****************************************************************************
*/
#include "HAL_Uart.h"
#include "cmd.h"
#define VER_MAJ 0
#define VER_MIN 1
typedef enum {
FALSE = 0,
TRUE = 1
} bool;
#ifndef NULL
#define NULL ((void *)0)
#endif
typedef unsigned char U8;
typedef unsigned short U16;
typedef unsigned int U32;
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
static cmd_tbl_t * cli_cmd_start;
static cmd_tbl_t * cli_cmd_end ;
int atoi_r (const char *str);
static void outc (const char c)
{
UART_OUT(c);
}
static void outs(const char *str)
{
while(*str++)
{
outc(*str);
}
}
static void do_version (cmd_tbl_t *cmdtp)
{
printf("version : %d.%d \r\n", VER_MAJ,VER_MIN);
}
REGISTER_CMD(
ver,
1,
do_version,
"print monitor version"
);
static char *version_history[] = {
"ver Date description \r\n",
"0.1 Mar 1, 2018 initial version with cli \r\n"
};
static void do_history(void)
{
U8 i;
for(i = 0; i < ARRAY_SIZE(version_history); i++)
{
printf("%s", version_history[i]);
}
}
REGISTER_CMD(
his,
1,
do_history,
"print history informaton"
);
void do_help(void)
{
cmd_tbl_t *cmdtp;
int len = cli_cmd_end - cli_cmd_start;
for (cmdtp = cli_cmd_start; cmdtp != cli_cmd_start + len; cmdtp++)
{
printf("%-8s %s \r\n",cmdtp->name, cmdtp->usage);
}
}
REGISTER_CMD(
help,
1,
do_help,
"print help information "
);
#define DEF_PROMPT "cmd:>"
void show_prompt()
{
printf("\r\n%s:>",DEF_PROMPT);
}
/****************************************************************************/
static U32 hex2int(U8 ch)
{
U8 val;
if((ch >= 'A') && (ch <= 'F'))
val = ch - 'A' + 10;
else if((ch >= 'a') && (ch <= 'f'))
val = ch - 'a' + 10;
else if((ch >= '0') && (ch <= '9'))
val = ch - '0';
else
val = 16;
return val;
}
static U32 hexs_to_int(const char *exp)
{
U8 ch;
U32 result = 0;
U32 digital;
while('\0' != (ch = *exp++))
{
digital = hex2int(ch);
if(digital != 16) // fixme
{
result = result * 16 + digital;
}
else
{
break;
}
}
return result;
}
U32 _TOU32(const char *exp)
{
char first = *exp;
char second = *exp + 1;
U32 result;
if(('0' == first) && ('1' == second)) //??? while not 'x'/'X'
{
result = hexs_to_int(exp + 2); // skip prefix
}
else if('-' == first) // s32
{
return atoi_r(exp);
}
else
{
result = (U32)atoi_r(exp);
}
return result;
}
int isspace (int ch)
{
if ((ch == ' ') || (ch == '\t')) /* \n ??? */
return 1;
else
return 0;
}
int isalnum (int ch)
{
/* ASCII only */
if (((ch >= '0') && (ch <= '9')) ||
((ch >= 'A') && (ch <= 'Z')) ||
((ch >= 'a') && (ch <= 'z')))
return 1;
else
return 0;
}
int isdigit (int ch)
{
/* ASCII only */
if ((ch >= '0') && (ch <= '9'))
return 1;
else
return 0;
}
int str_cmp(const char * s1, const char * s2)
{
while (*s1 == *s2)
{
if (*s1 == '\0')
return 0;
s1++;
s2++;
}
return *s1 - *s2;
}
static bool check_cmd(const char *name, cmd_tbl_t **cmd)
{
bool found = FALSE;
printf("cmd : %s \r\n", name);
cmd_tbl_t *cmdtp;
int len = cli_cmd_end - cli_cmd_start;
for (cmdtp = cli_cmd_start; cmdtp != cli_cmd_start + len; cmdtp++)
{
if(0 == str_cmp(name, cmdtp->name))
{
*cmd = cmdtp;
found = TRUE;
break;
}
}
return found;
}
int parse_line (char *line, char *argv[])
{
int nargs = 0;
printf ("parse_line: \"%s\"\r\n", line);
while (nargs < MAX_ARGS_CNT) {
/* skip any white space */
while ((*line == ' ') || (*line == '\t')) {
++line;
}
if (*line == '\0') { /* end of line, no more args */
argv[nargs] = NULL;
return (nargs);
}
argv[nargs++] = line; /* begin of argument string */
/* find end of string */
while (*line && (*line != ' ') && (*line != '\t')) {
++line;
}
if (*line == '\0') { /* end of line, no more args */
argv[nargs] = NULL;
return (nargs);
}
*line++ = '\0'; /* terminate current arg */
}
printf ("** Too many args (max. %d) **\r\n", MAX_ARGS_CNT);
return (nargs);
}
#define MAX_ARGS_CNT 10
#define MAX_ARGS_LENGTH 32
#define MAX_CMDLINE_LEN 64
void run_command(const char *cmdBuf)
{
cmd_tbl_t *cmd;
char *argv[MAX_ARGS_CNT + 1];
int argc = 0;
char destLine[MAX_CMDLINE_LEN];
const char *src = cmdBuf;
char *dest = destLine;
while(*src != '\0')
{
*dest++ = *src++;
}
*dest = '\0';
dest = destLine;
if(*dest == '\0') // invalid
return;
argc = parse_line(dest, argv);
printf("\r\ncheck the token argv %s argc : %d \r\n",(char *)argv[0], argc);
if(TRUE == check_cmd((const char *)argv[0], &cmd))
{
printf("got! cmd : %s argc : %d \r\n", cmd->name, cmd->maxargs);
if(argc != cmd->maxargs )
{
printf("\nargs not matched ! \r\n");
return;
}
// excute the handler
switch(argc)
{
case 0:
cmd->cmd();
break;
case 1:
cmd->cmd(_TOU32(argv[1]));
break;
case 2:
cmd->cmd(_TOU32(argv[1]), _TOU32(argv[2]));
break;
default:
break;
}
}
else
{
printf("Unrecognized cmd , check %s with 'help' \r\n", argv[0]);
}
}
static char erase_seq[] = "\b \b"; /* erase sequence */
static char tab_seq[] = " "; /* used to expand TABs */
char console_buffer[CONFIG_SYS_CBSIZE];
static int index = 0; /* buffer index */
void cmdline_run(void)
{
char *p = &console_buffer[index];
char * p_buf = p;
char c;
int count = UART_Rx(&c, 1);
if(1 == count){
switch (c)
{
case '\r': /* Enter */
case '\n':
*p = '\0';
printf ("\r\n");
run_command(console_buffer);
show_prompt();
index = 0; // next cmd
break;
case '\0': /* nul */
return;
case 0x03: /* ^C - break */
p_buf[0] = '\0'; /* discard input */
return ;
case 0x08: /* ^H - backspace */
case 0x7F: /* DEL - backspace */
//p=delete_char(p_buf, p, &col, &n, plen);
printf("%s",erase_seq);
p--; /* update the index */
index--;
break;
default:
if (index < CONFIG_SYS_CBSIZE-2)
{
if (c == '\t') /* expand TABs */
outs (tab_seq);
else
outc (c);
*p++ = c;
++index;
}
}
}
}
void cmdline_init(void)
{
extern unsigned char Load$$CMDREG$$Base[];
extern unsigned char Load$$CMDREG$$Limit[];
unsigned long cmd_addr = (unsigned long)Load$$CMDREG$$Base;
unsigned long cmd_end = (unsigned long)Load$$CMDREG$$Limit;
//CLI_OUT("cmd addr:%08x, limit:%08x\r\n", cmd_addr, cmd_end);
cli_cmd_start =(cmd_tbl_t *)cmd_addr;
cli_cmd_end = (cmd_tbl_t *)cmd_end;
}
// TODO : if stdlib.h(atoi(),...) is available , remove follows
unsigned long strtoul_r (char *str, char **ptr, int base)
{
unsigned long rvalue = 0;
int neg = 0;
int c;
/* Validate parameters */
if ((str != NULL) && (base >= 0) && (base <= 36))
{
/* Skip leading white spaces */
while (isspace(*str))
{
++str;
}
/* Check for notations */
switch (str[0])
{
case '0':
if (base == 0)
{
if ((str[1] == 'x') || (str[1] == 'X'))
{
base = 16;
str += 2;
}
else
{
base = 8;
str++;
}
}
break;
case '-':
neg = 1;
str++;
break;
case '+':
str++;
break;
default:
break;
}
if (base == 0)
base = 10;
while (isalnum(c = *str))
{
/* Convert char to num in 0..36 */
if ((c -= ('a' - 10)) < 10) /* 'a'..'z' */
if ((c += ('a' - 'A')) < 10) /* 'A'..'Z' */
c += ('A' - '0' - 10); /* '0'..'9' */
/* check c against base */
if (c >= base)
break;
if (neg)
rvalue = (rvalue * base) - c;
else
rvalue = (rvalue * base) + c;
++str;
}
}
/* Upon exit, 'str' points to the character at which valid info */
/* STOPS. No chars including and beyond 'str' are used. */
if (ptr != NULL)
*ptr = str;
return rvalue;
}
int atoi_r (const char *str)
{
char *s = (char *)str;
return ((int)strtoul_r(s, NULL, 10));
}
头文件
/*
****************************************************************************
* File : cmd.h
*
* Creat on : Otc 08, 2018
* Author : Flinn
*
* @beirf :
*
* @Note(s) :
*
****************************************************************************
*/
#ifndef _CMD_H
#define _CMD_H
#include <stdarg.h>
typedef void (*CLI_handler)();
struct cmd_tbl_s {
char *name; /* Command Name */
int maxargs; /* maximum number of arguments */
CLI_handler cmd; /* Implementation function */
char *usage; /* Usage message (short) */
};
typedef struct cmd_tbl_s cmd_tbl_t;
#define REGISTER_CMD(name,maxargs,handler,usage) \
const cmd_tbl_t strcmd_##name __attribute__ ((section ("cmd"))) = {#name, maxargs, handler, usage}
#define CONFIG_SYS_CBSIZE 256 /* buffer size of console */
#define MAX_ARGS_CNT 10
#define MAX_ARGS_LENGTH 32
void cmdline_run(void);
void cmdline_init(void);
#endif /* _CMD_H */
运行效果:
平台stm32 和arm9, msp430等等:
以上多余打印是调试时解析bug用的,可以去除。