先看效果图
这个图是在裸机下跑的,移植了u-boot的command, 新的u-boot改成cli(命令行接口)
使用起来非常方便:
static int do_version (struct cmd_tbl_s *cmd_tbl_t, int argc, int type, char * const argv[])
{
puts("version : V1.0 \r\n");
return 0;
}
REGISTER_CMD(
ver,
1,
ARG_TYPE_NONE,
do_version,
"print monitor version"
);
在任意文件里面都可以添加命令。
REGISTER_CMD宏即
#define REGISTER_CMD(name,maxargs,type,handler,usage) \
const cmd_tbl_t cmd_##name __attribute__ ((section (".cli_cmd"))) = {#name, maxargs, type, handler, usage}
也即REGISTER_CMD宏定义的命令最终都会链接到“.cli_cmd”段里面,在链接脚本里面:
SECTIONS
{
. = 0x02023400;
.text : {
src/head.o
* (.text)
}
.data : {
* (.data)
}
__cli_cmd_start = .;
.cli_cmd : {
*(.cli_cmd)
}
__cli_cmd_end = .;
bss_start = .;
.bss : {
* (.bss)
}
bss_end = .;
}
可以看到.cli_cmd字段。
上面简单描述了效果即简单解析。
目录结构
.
├── basic.lds
├── common
├── HAL
│ ├── uart.c
│ └── uart.h
├── include
│ ├── system_clock.h
│ └── tiny4412.h
├── Makefile
├── src
│ ├── command.c
│ ├── command.h
│ ├── head.S
│ ├── main.c
│ └── system_clock.c
└── tools
├── bl2.bin
├── E4412_N.bl1.bin
├── E4412_tzsw.bin
├── Makefile
├── mkbl2
├── sd_fuse.sh
└── V310-EVT1-mkbl2.c
其中basic.lds就是链接脚本,内容在上面以及贴出来了。
Makefile
CC = /home/flinn/tools/4.5.1/bin/arm-none-linux-gnueabi-gcc
LD = /home/flinn/tools/4.5.1/bin/arm-none-linux-gnueabi-ld
AR = /home/flinn/tools/4.5.1/bin/arm-none-linux-gnueabi-ar
OBJCOPY = /home/flinn/tools/4.5.1/bin/arm-none-linux-gnueabi-objcopy
OBJDUMP = /home/flinn/tools/4.5.1/bin/arm-none-linux-gnueabi-objdump
CFLAGS := -Wall -O2 -nostdlib -fno-builtin \
-I./include \
-I./HAL \
-I./common \
-I./src \
-lgcc -L/home/flinn/tools/4.5.1/arm-none-linux-gnueabi/lib
OBJS := src/head.o src/main.o src/system_clock.o HAL/uart.o src/command.o
VER = 1.0
TARGET = tiny4412_$(VER).bin
$(TARGET): $(OBJS)
${LD} -Tbasic.lds -o $(TARGET)_elf $^
${OBJCOPY} -O binary -S $(TARGET)_elf $@
${OBJDUMP} -D -m arm $(TARGET)_elf > $(TARGET).dis
%.o:%.c
${CC} $(CFLAGS) -c -o $@ $<
%.o:%.S
${CC} $(CFLAGS) -c -o $@ $<
clean:
rm -f $(TARGET) $(TARGET)_elf $(TARGET).dis *.o *.bin $(OBJS)
main
main很简单:
include "tiny4412.h"
#include "system_clock.h"
#include "uart.h"
#include "command.h"
int main(void)
{
int i = 0;
system_clock_init();
uart_init();
command_init();
puts("\r\nstart...\r\n");
while (1)
{
command_loop();
}
return 0;
}
其中需要初始化串口。
command_init()和command_loop()两个实现都在src/command.c里面
以上代码没有实现itoa,strlen, printf , vsprintf等标准库函数,并运行在tiny4412裸机上,对于其他平台,比如fpga,stm32, msp430, 51等平台,一般IDE支持标准库函数,移植起来更方便。熟悉u-boot移植的人来说,cli接口是必不可少的调试神器。
所有代码由以下方式获取:
git clone git@github.com:fanglinn/tiny4412.git
最后还是补充一下:
如果你想使用以上框架,需要做如下几点:
1.在Makefile里面修改你的编译器gcc路径
2.实现uart底层读写函数,必须实现putc, puts, getc, tstc四个函数,分别表示发送字符,发送字符串, 获
取一个字符, 监测是否有数据来
3.插入SD卡,使用sudo fdisk -l来识别,比如/dev/sdc
4.进入tools目录,执行sudo ./sd_fuse /dev/sdc 即可
最后还是贴上代码,以便自己后面使用:
src/command.c
#include "command.h"
#include "uart.h"
char console_buffer[CONFIG_SYS_CBSIZE + 1]; /* console I/O buffer */
static const char erase_seq[] = "\b \b"; /* erase sequence */
static const char tab_seq[] = " "; /* used to expand TABs */
static cmd_tbl_t * CLI_cmd_start;
static cmd_tbl_t * CLI_cmd_end ;
static int do_version (struct cmd_tbl_s *cmd_tbl_t, int argc, int type, char * const argv[])
{
puts("version : V1.0 \r\n");
return 0;
}
REGISTER_CMD(
ver,
1,
ARG_TYPE_NONE,
do_version,
"print monitor version"
);
int do_help(struct cmd_tbl_s *cmd, int argc, int type, char * const argv[])
{
cmd_tbl_t *cmdtp;
int len = CLI_cmd_end - CLI_cmd_start;
for (cmdtp = CLI_cmd_start; cmdtp != CLI_cmd_start + len; cmdtp++)
{
puts(cmdtp->name);
puts("\t");
puts(cmdtp->usage);
puts("\r\n");
}
return 0;
}
REGISTER_CMD(
help,
1,
ARG_TYPE_NONE,
do_help,
"print help information"
);
char * strcpy(char * dest,const char *src)
{
char *tmp = dest;
while ((*dest++ = *src++) != '\0')
/* nothing */;
return tmp;
}
unsigned int strlen(const char * s)
{
const char *sc;
for (sc = s; *sc != '\0'; ++sc)
/* nothing */;
return sc - s;
}
int strcmp(const char * cs,const char * ct)
{
register signed char __res;
while (1) {
if ((__res = *cs - *ct++) != 0 || !*cs++)
break;
}
return __res;
}
static char * delete_char (char *buffer, char *p, int *colp, int *np, int plen)
{
char *s;
if (*np == 0) {
return (p);
}
if (*(--p) == '\t') { /* will retype the whole line */
while (*colp > plen) {
puts (erase_seq);
(*colp)--;
}
for (s=buffer; s<p; ++s) {
if (*s == '\t') {
puts (tab_seq+((*colp) & 07));
*colp += 8 - ((*colp) & 07);
} else {
++(*colp);
putc (*s);
}
}
} else {
puts (erase_seq);
(*colp)--;
}
(*np)--;
return (p);
}
int readline_into_buffer(const char *const prompt, char *buffer, int timeout)
{
char *p = buffer;
char * p_buf = p;
int n = 0; /* buffer index */
int plen = 0; /* prompt length */
int col; /* output column cnt */
char c;
/* print prompt */
if (prompt) {
plen = strlen (prompt);
puts (prompt);
}
col = plen;
for (;;)
{
c = getc();
/*
* Special character handling
*/
switch (c){
case '\r': /* Enter */
case '\n':
*p = '\0';
puts ("\r\n");
return p - p_buf;
case '\0': /* nul */
continue;
case 0x03: /* ^C - break */
p_buf[0] = '\0'; /* discard input */
return -1;
case 0x15: /* ^U - erase line */
while (col > plen) {
puts (erase_seq);
--col;
}
p = p_buf;
n = 0;
continue;
case 0x17: /* ^W - erase word */
p=delete_char(p_buf, p, &col, &n, plen);
while ((n > 0) && (*p != ' ')) {
p=delete_char(p_buf, p, &col, &n, plen);
}
continue;
case 0x08: /* ^H - backspace */
case 0x7F: /* DEL - backspace */
p=delete_char(p_buf, p, &col, &n, plen);
continue;
default:
/*
* Must be a normal character then
*/
if (n < CONFIG_SYS_CBSIZE-2) {
if (c == '\t') { /* expand TABs */
puts (tab_seq+(col&07));
col += 8 - (col&07);
} else {
char buf[2];
/*
* Echo input using puts() to force an
* LCD flush if we are using an LCD
*/
++col;
buf[0] = c;
buf[1] = '\0';
puts(buf);
}
*p++ = c;
++n;
} else { /* Buffer full */
putc ('\a');
}
}
}
}
int readline (const char *const prompt)
{
/*
* If console_buffer isn't 0-length the user will be prompted to modify
* it instead of entering it from scratch as desired.
*/
console_buffer[0] = '\0';
return readline_into_buffer(prompt, console_buffer, 0);
}
int parse_line (char *line, char *argv[])
{
int nargs = 0;
while (nargs < CONFIG_SYS_MAXARGS) {
/* skip any white space */
while (isblank(*line))
++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 && !isblank(*line))
++line;
if (*line == '\0') { /* end of line, no more args */
argv[nargs] = NULL;
return nargs;
}
*line++ = '\0'; /* terminate current arg */
}
puts ("** Too many args (max) **\n");
return (nargs);
}
static int find_cmd (const char *name, cmd_tbl_t **cmd)
{
int found = 0;
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 == strcmp(name, cmdtp->name))
{
*cmd = cmdtp;
found = 1;
break;
}
}
return found;
}
int cmd_usage(const cmd_tbl_t *cmdtp)
{
//printf("%s - %s\n\n", cmdtp->name, cmdtp->usage);
return 1;
}
static int cmd_call(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
int result;
result = (cmdtp->cmd)(cmdtp, flag, argc, argv);
if (result)
puts("Command failed");
return result;
}
enum command_ret_t cmd_process(int flag, int argc, char * const argv[],
int *repeatable, unsigned long *ticks)
{
enum command_ret_t rc = CMD_RET_SUCCESS;
cmd_tbl_t *cmdtp;
int found = 0;
/* Look up command in command table */
found = find_cmd(argv[0], &cmdtp);
if (found == 0) {
puts("Unknown command '");
puts(argv[0]);
puts("' - try 'help'\r\n");
return 1;
}
/* found - check max args */
if (argc > cmdtp->maxargs)
rc = CMD_RET_USAGE;
/* If OK so far, then do the command */
if (!rc) {
rc = cmd_call(cmdtp, flag, argc, argv);
}
if (rc == CMD_RET_USAGE)
rc = cmd_usage(cmdtp);
return rc;
}
static int builtin_run_command(const char *cmd, int flag)
{
char cmdbuf[CONFIG_SYS_CBSIZE]; /* working copy of cmd */
char *token; /* start of token in cmdbuf */
char *sep; /* end of token (separator) in cmdbuf */
char *str = cmdbuf;
char *argv[CONFIG_SYS_MAXARGS + 1]; /* NULL terminated */
int argc, inquotes;
int repeatable = 1;
int rc = 0;
if (!cmd || !*cmd) {
return -1; /* empty command */
}
if (strlen(cmd) >= CONFIG_SYS_CBSIZE) {
puts ("## Command too long!\r\n");
return -1;
}
strcpy (cmdbuf, cmd);
while (*str) {
/*
* Find separator, or string end
* Allow simple escape of ';' by writing "\;"
*/
for (inquotes = 0, sep = str; *sep; sep++) {
if ((*sep=='\'') &&
(*(sep-1) != '\\'))
inquotes=!inquotes;
if (!inquotes &&
(*sep == ';') && /* separator */
( sep != str) && /* past string start */
(*(sep-1) != '\\')) /* and NOT escaped */
break;
}
/*
* Limit the token to data between separators
*/
token = str;
if (*sep) {
str = sep + 1; /* start of command for next pass */
*sep = '\0';
}
else
str = sep; /* no more commands for next pass */
/* Extract arguments */
if ((argc = parse_line (token, argv)) == 0) {
rc = -1; /* no command at all */
continue;
}
if (cmd_process(flag, argc, argv, &repeatable, NULL))
rc = -1;
}
return rc ? rc : repeatable;
}
/*
* Run a command using the selected parser.
*
* @param cmd Command to run
* @param flag Execution flags (CMD_FLAG_...)
* @return 0 on success, or != 0 on error.
*/
int run_command(const char *cmd, int flag)
{
if (builtin_run_command(cmd, flag) == -1)
return 1;
return 0;
}
void command_loop(void)
{
static char lastcommand[CONFIG_SYS_CBSIZE] = { 0, };
int len;
int rc = 1;
int flag;
/*
* Main Loop for Monitor Command Processing
*/
for (;;) {
len = readline (CONFIG_SYS_PROMPT);
flag = 0; /* assume no special flags for now */
if (len > 0)
strcpy (lastcommand, console_buffer);
if (len == -1)
puts ("<INTERRUPT>\n");
else
rc = run_command(lastcommand, flag);
if (rc <= 0) {
/* invalid command or not repeatable, forget it */
lastcommand[0] = 0;
}
}
}
void command_init(void)
{
extern cmd_tbl_t __cli_cmd_start,__cli_cmd_end;
CLI_cmd_start = &__cli_cmd_start;
CLI_cmd_end = &__cli_cmd_end;
}
src/command.h
#ifndef _COMMAND_H
#define _COMMAND_H
#define ARG_TYPE_NONE 9 /* idle */
#define ARG_TYPE_S32 0
#define ARG_TYPE_U32 1
#define ARG_TYPE_BOOL 2
#define ARG_TYPE_STR 3
#define CONFIG_SYS_PROMPT "flinn@tiny4412 # "
#define CONFIG_SYS_CBSIZE 256 /* Console I/O Buffer Size */
#define CONFIG_SYS_PBSIZE 384 /* Print Buffer Size */
#define CONFIG_SYS_MAXARGS 16 /* max number of command args */
enum command_ret_t {
CMD_RET_SUCCESS, /* 0 = Success */
CMD_RET_FAILURE, /* 1 = Failure */
CMD_RET_USAGE = -1, /* Failure, please report 'usage' error */
};
struct cmd_tbl_s {
char *name; /* Command Name */
int maxargs; /* maximum number of arguments */
int type;
int (*cmd)(struct cmd_tbl_s *, int, int, char * const []);
char *usage; /* Usage message (short) */
};
typedef struct cmd_tbl_s cmd_tbl_t;
#define REGISTER_CMD(name,maxargs,type,handler,usage) \
const cmd_tbl_t cmd_##name __attribute__ ((section (".cli_cmd"))) = {#name, maxargs, type, handler, usage}
#ifndef NULL
#define NULL (void *)0
#endif
#define isblank(c) (c == ' ' || c == '\t')
void command_loop(void);
void command_init(void);
#endif /* _COMMAND_H */