更新: 新增类似linux的重定向符号,支持xcmd_print到文件

This commit is contained in:
hqm 2022-08-12 22:42:58 +08:00 committed by qmfr
parent 840076a741
commit c23f3288e9
6 changed files with 274 additions and 125 deletions

View File

@ -1,40 +1,53 @@
# xcmd
#### 效果展示
![输入图片说明](https://images.gitee.com/uploads/images/2021/0922/220957_66faa768_1680380.gif "演示1.gif")
#### 介绍
xcmd是一款单片机命令行工具移植十分方便并且对flash与ram占用很小旨在为单片机提供一个能够快速搭建且占用资源很小的命令行工具可以大大加快单片机程序调试工作它有一下几个优点。
1. **移植十分简单**
2. **资源占用约8K rom 1K ram**
3. **支持历史记录**
2. **资源占用很小**
3. **支持历史记录**
4. **支持命令自动补全**
5. **支持注册快捷键**
5. **支持`xcmd_cmd_register()/xcmd_key_register`方法注册命令或按键**
5. **支持`XCMD_EXPORT_CMD()/XCMD_EXPORT_KEY()`方法直接导出命令或按键,不需要额外运行注册函数**
5. **支持注册快捷键**
6. **支持 `xcmd_cmd_register()/xcmd_key_register`方法注册命令或按键**
7. **支持 `XCMD_EXPORT_CMD()/XCMD_EXPORT_KEY()`方法直接导出命令或按键,不需要额外运行注册函数**
#### 支持的平台
1. linux
2. arduino
3. stm32
4. esp32
5. qemu-stm32
#### 测试过的串口软件
2. win/linux推荐putty
3. win推荐MobaXterm
5. linux端推荐cu
1. SecureCRT请设置成Xterm模式
4. Xshell
6. nc(支持性很差)
7. minicom(支持性很差)
4. linux端推荐cu
5. SecureCRT请设置成Xterm模式
6. Xshell
7. nc(支持性很差)
8. minicom(支持性很差)
#### 支持的扩展功能
1. 类linux风格的快捷键ctr+left、ctr+right、ctr+a、ctr+e、ctr+u、ctr+k、ctr+l快捷键
2. 支持字体、背景颜色显示
3. 支持类linux的文件操作Fatfs文件系统ls、cd、rm、df、mv、mkdir、touch、read、write
3. 支持类linux的文件操作Fatfs文件系统ls、cd、rm、df、mv、mkdir、touch、rd、wr、cat
4. 支持基于socket的udp server、udp client测试程序
#### 移植
- 移植十分简单可在example中查看各平台移植结果
1. 在初始化的时候提供字符输入输出函数即可
2. 从example中拷贝一个xcmd_confg.h到自己的项目
```C
/* 例如移植到Arduino平台 */
int cmd_get_char(uint8_t *ch)
@ -62,14 +75,17 @@ void setup() {
}
void loop() {
xcmd_task();
}
```
#### 配置
配置xcmd_config.h
**注意** 如果不知道如何修改连接脚本建议不要使能ENABLE_XCMD_EXPORT。不使能ENABLE_XCMD_EXPORT的情况下有更好的兼容性移植更加简单
```C
#define XCMD_LINE_MAX_LENGTH (128) /* 命令行支持的最大字符数 */
#define XCMD_HISTORY_MAX_NUM (16) /* 支持的历史记录个数, 这个参数对内存的影响很大建议arduino设置为00为不支持 */
@ -78,8 +94,11 @@ void loop() {
// #define XCMD_DEFAULT_PROMPT_CLOLR TX_GREEN /* 提示符颜色 */
// #define ENABLE_XCMD_EXPORT /* 需要修改lds启用后支持XCMD_EXPORT_CMD和XCMD_EXPORT_KEY方法导出命令 */
```
#### 使用说明
##### xcmd_register()方法注册自定义命令
```C
static int cmd_echo(int argc, char* argv[])
{
@ -100,7 +119,9 @@ void test_cmd_init(void)
xcmd_cmd_register(cmds, sizeof(cmds)/sizeof(xcmd_t));
}
```
##### xcmd_key_register() 方法注册自定义快捷键
```C
int cmd_ctr_a(void* pv)
{
@ -117,7 +138,9 @@ void test_keys_init(void)
xcmd_key_register(user_keys, sizeof(user_keys)/sizeof(xcmd_key_t));
}
```
##### XCMD_EXPORT_CMD()方法注册自定义命令需要使能宏ENABLE_XCMD_EXPORT并修改lds见`example/stm32/qemu/demos/main.ld`
##### XCMD_EXPORT_CMD()方法注册自定义命令需要使能宏ENABLE_XCMD_EXPORT并修改lds`example/stm32/qemu/demos/main.ld`
```C
static int cmd_echo(int argc, char* argv[])
{
@ -129,7 +152,9 @@ static int cmd_echo(int argc, char* argv[])
}
XCMD_EXPORT_CMD(echo, cmd_echo, "print anything")
```
##### XCMD_EXPORT_KEY() 方法注册自定义快捷键, 需要使能宏ENABLE_XCMD_EXPORT并修改lds见`example/stm32/qemu/demos/main.ld`
##### XCMD_EXPORT_KEY() 方法注册自定义快捷键, 需要使能宏ENABLE_XCMD_EXPORT并修改lds`example/stm32/qemu/demos/main.ld`
```C
int cmd_ctr_a(void* pv)
{
@ -137,5 +162,7 @@ int cmd_ctr_a(void* pv)
}
XCMD_EXPORT_KEY(KEY_CTR_A, cmd_ctr_a, "ctr+a")
```
#### 其他
流程图需要使用drawio来打开可以使用vscode drawio的插件来打开

View File

@ -170,8 +170,8 @@
/* Number of volumes (logical drives) to be used. (1-10) */
#define FF_STR_VOLUME_ID 0
#define FF_VOLUME_STRS "RAM","NAND","CF","SD","SD2","USB","USB2","USB3"
#define FF_STR_VOLUME_ID 1
// #define FF_VOLUME_STRS "RAM","NAND" //,"CF","SD","SD2","USB","USB2","USB3"
/* FF_STR_VOLUME_ID switches support for volume ID in arbitrary strings.
/ When FF_STR_VOLUME_ID is set to 1 or 2, arbitrary strings can be used as drive
/ number in the path name. FF_VOLUME_STRS defines the volume ID strings for each
@ -194,7 +194,7 @@
#define FF_MIN_SS 512
#define FF_MAX_SS 512
#define FF_MAX_SS 4096
/* This set of options configures the range of sector size to be supported. (512,
/ 1024, 2048 or 4096) Always set both 512 for most systems, generic memory card and
/ harddisk, but a larger value may be required for on-board flash memory and some

View File

@ -11,21 +11,29 @@
static DiskIo_t * g_Disks[FF_VOLUMES] = {0};
int f_disk_regist(DiskIo_t * disk, int id)
#if FF_STR_VOLUME_ID >= 1
#ifndef FF_VOLUME_STRS
const char* VolumeStr[FF_VOLUMES] = {0};
#else
static char * VolumeStr[FF_VOLUMES] = {FF_VOLUME_STRS};
#endif
#endif
int f_disk_regist(DiskIo_t * disk, const char* volume_name, int id)
{
if(!disk)
if(!disk || !volume_name)
{
return -1;
}
if(id<0)
{
for(int i=0; i<FF_VOLUMES; i++)
{
if(!g_Disks[id])
if(!g_Disks[i])
{
g_Disks[i] = disk;
return i;
id = i;
goto ok;
}
}
}
@ -34,10 +42,39 @@ int f_disk_regist(DiskIo_t * disk, int id)
if((id < FF_VOLUMES) && !g_Disks[id])
{
g_Disks[id] = disk;
return id;
goto ok;
}
}
return -1;
ok:
#if FF_STR_VOLUME_ID >= 1
#ifndef FF_VOLUME_STRS
#if FF_STR_VOLUME_ID == 1
snprintf(disk->disk_path, MAX_VOL_NAME_LEN, "%s:", volume_name);
#endif
#if FF_STR_VOLUME_ID == 2
snprintf(disk->disk_path, MAX_VOL_NAME_LEN, "/%s/", volume_name);
#endif
VolumeStr[id] = volume_name;
#else
#if FF_STR_VOLUME_ID == 1
snprintf(disk->disk_path, MAX_VOL_NAME_LEN, "%s:", VolumeStr[id]);
#endif
#if FF_STR_VOLUME_ID == 2
snprintf(disk->disk_path, MAX_VOL_NAME_LEN, "/%s/", VolumeStr[id]);
#endif
#endif
#else
snprintf(disk->disk_path, MAX_VOL_NAME_LEN, "%d:", id);
#endif
return id;
}
DiskIo_t * f_disk_get(int id)
@ -48,3 +85,12 @@ DiskIo_t * f_disk_get(int id)
}
return NULL;
}
char * f_disk_path(int id)
{
if(id < FF_VOLUMES)
{
return g_Disks[id]->disk_path;
}
return NULL;
}

View File

@ -14,9 +14,11 @@
#include "diskio.h"
#include <stdio.h>
#define MAX_VOL_NAME_LEN (16)
typedef struct __DiskIo
{
char * disk_name;
char disk_path[MAX_VOL_NAME_LEN];
DSTATUS (*disk_status)(void);
DSTATUS (*disk_initialize)(void);
DRESULT (*disk_read)(BYTE *buff, LBA_t sector, UINT count);
@ -24,7 +26,7 @@ typedef struct __DiskIo
DRESULT (*disk_ioctl) (BYTE cmd, void *buff);
}DiskIo_t;
int f_disk_regist(DiskIo_t * disk, int id);
int f_disk_regist(DiskIo_t * disk, const char* volume_name, int id);
DiskIo_t * f_disk_get(int id);
char * f_disk_path(int id);
#endif

View File

@ -9,6 +9,7 @@
#include "fs_cmds.h"
#include "xcmd.h"
#include "ff.h"
#include "fatfs_port.h"
#define RESAULT_TO_STR(r) resault_to_str_map[r]
static char *resault_to_str_map[] =
@ -41,13 +42,13 @@ static FILINFO fno;
#define HELP_DF ("Show Disk Info. Usage: df")
#define HELP_LS ("List information about the FILEs. Usage: ls")
#define HELP_CD ("Change the shell working directory. Usage: cd path")
#define HELP_READ ("Read FILE. Usage: read path")
#define HELP_CAT ("Read FILE. Usage: cat path")
#define HELP_RM ("Delete FILE. Usage: rm [-r:dir] path")
#define HELP_MV ("Move FILE. Usage: mv path")
#define HELP_SYNC ("Sync Usage: sync")
#define HELP_MKDIR ("Make DIR. Usage: mkdir path")
#define HELP_TOUCH ("Create empty FILE. Usage: touch path")
#define HELP_WRITE ("Write str to FILE. Usage: Write [-a:append] path str")
#define HELP_WRITE ("Write str to FILE. Usage: wr string (>|>>) filename")
static FRESULT scan_files(
char *path /* Start node to be scanned (***also used as work area***) */
@ -152,9 +153,10 @@ static FRESULT df(char* path, DWORD* totle_byte, DWORD* free_byte)
#if FF_MAX_SS != FF_MIN_SS
*totle_byte = tot_sect * fs->ssize;
*free_byte = fre_sect * fs->ssize;
#endif
#else
*totle_byte = tot_sect * FF_MAX_SS;
*free_byte = fre_sect * FF_MAX_SS;
#endif
}
return res;
}
@ -163,21 +165,23 @@ static int cmd_df(int argc, char* argv[])
{
FRESULT res;
DWORD fre_bytes, tot_bytes;
res = df("0:", &tot_bytes, &fre_bytes);
if (res != FR_OK)
char * disk_path;
for(int i=0; i<FF_VOLUMES; i++)
{
xcmd_print("0: Failure:%s\r\n", RESAULT_TO_STR(res));
return -1;
disk_path = f_disk_path(i);
if(disk_path)
{
res = df(disk_path, &tot_bytes, &fre_bytes);
if (res != FR_OK)
{
xcmd_print("%s Failure:%s\r\n", disk_path, RESAULT_TO_STR(res));
}
else
{
xcmd_print("%s %lu/%lu KiB.\r\n", disk_path, fre_bytes/1024, tot_bytes / 1024);
}
}
}
xcmd_print("0:/ %lu/%lu KiB.\r\n", fre_bytes/1024, tot_bytes / 1024);
res = df("1:", &tot_bytes, &fre_bytes);
if (res != FR_OK)
{
xcmd_print("1: Failure:%s\r\n", RESAULT_TO_STR(res));
return -1;
}
xcmd_print("1:/ %lu/%lu KiB.\r\n", fre_bytes/1024, tot_bytes / 1024);
return 0;
}
@ -220,50 +224,6 @@ static int cmd_cd(int argc, char *argv[])
return 0;
}
static int cmd_read(int argc, char *argv[])
{
if (argc >= 2)
{
FRESULT res;
res = f_open(&fp, argv[1], FA_READ);
if (res != FR_OK)
{
xcmd_print("Failure:%s\r\n", RESAULT_TO_STR(res));
return -1;
}
else
{
char buf[128];
UINT br;
while (1)
{
res = f_read(&fp, buf, 128, &br);
if (res != FR_OK)
{
xcmd_print("Failure:%s\r\n", RESAULT_TO_STR(res));
f_close(&fp);
return -1;
}
if (br == 0)
{
break;
}
for(UINT i=0; i<br; i++)
{
xcmd_print("%c", buf[i]);
}
}
}
}
else
{
xcmd_print("%s\r\n", HELP_READ);
return -1;
}
f_close(&fp);
return 0;
}
static int cmd_rm(int argc, char *argv[])
{
FRESULT res;
@ -385,70 +345,125 @@ static int cmd_touch(int argc, char *argv[])
return 0;
}
static int cmd_write(int argc, char *argv[])
static int cmd_cat(int argc, char *argv[])
{
uint8_t append_flag = 0;
uint8_t param_num = 3;
/* 查找可选参数 */
if (strcmp(argv[1], "-a") == 0)
if (argc >= 2)
{
append_flag = 1;
param_num = 4;
}
FRESULT res = FR_INVALID_PARAMETER;
char *str = NULL;
if (argc >= param_num)
{
if (append_flag)
{
res = f_open(&fp, argv[2], FA_WRITE | FA_OPEN_APPEND);
str = argv[3];
}
else
{
res = f_open(&fp, argv[1], FA_WRITE | FA_CREATE_ALWAYS);
str = argv[2];
}
FRESULT res;
res = f_open(&fp, argv[1], FA_READ);
if (res != FR_OK)
{
xcmd_print("Failure:%s", RESAULT_TO_STR(res));
xcmd_print("Failure:%s\r\n", RESAULT_TO_STR(res));
return -1;
}
else
{
char buf[128];
UINT br;
res = f_write(&fp, str, strlen(str), &br);
res = f_write(&fp, "\n", 1, &br);
f_close(&fp);
if (res != FR_OK)
while (1)
{
xcmd_print("Failure:%s", RESAULT_TO_STR(res));
return -1;
res = f_read(&fp, buf, 128, &br);
if (res != FR_OK)
{
xcmd_print("Failure:%s\r\n", RESAULT_TO_STR(res));
f_close(&fp);
return -1;
}
if (br == 0)
{
break;
}
for(UINT i=0; i<br; i++)
{
xcmd_print("%c", buf[i]);
}
}
}
}
else
{
xcmd_print("%s\r\n", HELP_WRITE);
xcmd_print("%s\r\n", HELP_CAT);
return -1;
}
f_close(&fp);
return 0;
}
ssize_t file_open(char *name, int is_write, int is_append)
{
FRESULT res;
BYTE mode = 0;
if(is_write)
{
if (is_append)
{
mode = FA_WRITE | FA_OPEN_APPEND;
}
else
{
mode = FA_WRITE | FA_CREATE_ALWAYS;
}
}
else
{
mode = FA_READ;
}
res = f_open(&fp, name, mode);
if (res != FR_OK)
{
return -1;
}
return (ssize_t)&fp;
}
void file_close(ssize_t fd)
{
if(fd != -1)
f_close((FIL*)fd);
}
int file_read(ssize_t fd, char *buf, int buflen)
{
FRESULT res;
UINT br;
if(fd != -1)
{
res = f_read((FIL*)fd, buf, buflen, &br);
if (res != FR_OK)
{
return -1;
}
return 0;
}
return -1;
}
int file_write(ssize_t fd, const char *str)
{
FRESULT res = FR_INVALID_PARAMETER;
if(fd != -1)
{
UINT br;
res = f_write((FIL*)fd, str, strlen(str), &br);
if (res != FR_OK)
{
return -1;
}
return 0;
}
return -1;
}
XCMD_EXPORT_CMD(ls, cmd_ls, HELP_LS)
XCMD_EXPORT_CMD(df, cmd_df, HELP_DF)
XCMD_EXPORT_CMD(cd, cmd_cd, HELP_CD)
XCMD_EXPORT_CMD(read, cmd_read, HELP_READ)
XCMD_EXPORT_CMD(cat, cmd_cat, HELP_CAT)
XCMD_EXPORT_CMD(rm, cmd_rm, HELP_RM)
XCMD_EXPORT_CMD(mv, cmd_mv, HELP_MV)
XCMD_EXPORT_CMD(sync, cmd_sync, HELP_SYNC)
XCMD_EXPORT_CMD(mkdir, cmd_mkdir, HELP_MKDIR)
XCMD_EXPORT_CMD(touch, cmd_touch, HELP_TOUCH)
XCMD_EXPORT_CMD(write, cmd_write, HELP_WRITE)
static xcmd_t cmds[] =
{
@ -456,13 +471,12 @@ static xcmd_t cmds[] =
{"ls", cmd_ls, HELP_LS, NULL},
{"df", cmd_df, HELP_DF, NULL},
{"cd", cmd_cd, HELP_CD, NULL},
{"read", cmd_read, HELP_READ, NULL},
{"cat", cmd_cat, HELP_CAT, NULL},
{"rm", cmd_rm, HELP_RM, NULL},
{"mv", cmd_mv, HELP_MV, NULL},
{"sync", cmd_sync, HELP_SYNC, NULL},
{"mkdir", cmd_mkdir, HELP_MKDIR, NULL},
{"touch", cmd_touch, HELP_TOUCH, NULL},
{"write", cmd_write, HELP_WRITE, NULL},
{"touch", cmd_touch, HELP_TOUCH, NULL}
#endif
};

View File

@ -20,7 +20,9 @@ typedef struct __history
struct
{
struct
{
{
ssize_t write_fd;
ssize_t read_fd;
int (*get_c)(uint8_t*);
int (*put_c)(uint8_t);
}io;
@ -65,6 +67,31 @@ struct
uint8_t _initOK;
} g_xcmder = {0};
ssize_t file_open(char *name, int is_write, int is_append) __attribute__((weak));
void file_close(ssize_t fd) __attribute__((weak));
int file_write(ssize_t fd, const char *str) __attribute__((weak));
int file_read(ssize_t fd, char *buf, int buflen) __attribute__((weak));
void file_close(ssize_t fd)
{
}
ssize_t file_open(char *name, int is_write, int is_append)
{
return -1;
}
int file_write(ssize_t fd, const char *str)
{
return -1;
}
int file_read(ssize_t fd, char *buf, int buflen)
{
return -1;
}
static char *xcmd_strpbrk(char*s, const char *delim) //返回s1中第一个满足条件的字符的指针, 并且保留""号内的源格式
{
uint8_t flag = 0;
@ -159,6 +186,27 @@ static int xcmd_get_param(char* msg, char*delim, char* get[], int max_num)
return ret;
}
static int xcmd_redirected(int argc, char*argv[])
{
for(int i=1; i<(argc-1); i++)
{
if(argv[i][0] == '>')
{
argc = i;
if(argv[i][1] == '>')
{
g_xcmder.io.write_fd = file_open(argv[i+1], 1, 1);
}
else
{
g_xcmder.io.write_fd = file_open(argv[i+1], 1, 0);
}
break;
}
}
return argc;
}
static int xcmd_cmd_match(int argc, char*argv[])
{
uint8_t flag = 0;
@ -178,7 +226,10 @@ static int xcmd_cmd_match(int argc, char*argv[])
break;
}
}
argc = xcmd_redirected(argc, argv);
ret = p->func(argc, argv);
file_close(g_xcmder.io.write_fd);
g_xcmder.io.write_fd = -1;
break;
}
}
@ -310,6 +361,13 @@ static void xcmd_parser(uint8_t byte)
void xcmd_put_str(const char *str)
{
if(g_xcmder.io.write_fd != -1)
{
if(file_write(g_xcmder.io.write_fd, str) != -1)
{
return;
}
}
for(uint16_t i=0; str[i]; i++)
{
g_xcmder.io.put_c(str[i]);
@ -648,6 +706,8 @@ void xcmd_init( int (*get_c)(uint8_t*), int (*put_c)(uint8_t))
{
g_xcmder.io.get_c = get_c;
g_xcmder.io.put_c = put_c;
g_xcmder.io.read_fd = -1;
g_xcmder.io.write_fd = -1;
g_xcmder.parser.prompt = XCMD_DEFAULT_PROMPT;
g_xcmder.parser.byte_num = 0;