Rt-thread 中有一个完整的finsh(shell )系统,使用串口做命令行输入输出.但是想要用这个炫酷的工具就必须要上rtthread系统,或者花大力气将其移植出来.于是我就自己写了一个类似于这样的插件.只需要把一对.c/.h文件加入到你的工程,就可以实现这个简易版的shell.


  git: https://github.com/KimAlittleStar/ExternFunc

ExternFunc.c

 #include "stdio.h"
#include "string.h"
#include "ExternFunc.h"
#include "stm32f4xx_hal.h" #define MATCH_CASE_ENABLE 0 //函数调用名称大小写是否敏感 1表示敏感 0 表示不敏感 void show(int i);
void showcircle(char ch,int r); static int ExternFunc_Find(char* funcname);
static void ExternFunc_list(void);
static void ExternFunc_SocReset(void);
static unsigned char matchString(const char* str1,const char* str2); const CALLFUNCTIONTABLE functable[] =
{
EXPOTRFUNC(LIST,ExternFunc_list, ,函数列表),
EXPOTRFUNC(RST,ExternFunc_SocReset,,芯片软件复位),
EXPOTRFUNC(circle,showcircle,%c %d,串口显示一个圆),
EXPOTRFUNC(九九乘法表,show,%d,%d乘法表)
};
//EXPOTRFUNC( 函数别名命令行调用的名字 |真正的函数名 | 函数传参的格式字符串 |这个函数的简介)
void simplefunction(char* str,unsigned int sum,float dee,char ch)
{ printf("接收到的字符串是:%s,\n\
接收到的字符是: %c \n\
接受到的数字是 %d\n\
接收到的小数是 %f __ \n ",str,ch,sum,dee);
} void showcircle(char ch,int r)
{
for(int i = ; i<=(*r); i++)
{
for(int j = ; j<(*r); j++)
{
if(((i-r)*(i-r)+(j-r)*(j-r))<=(r*r))
printf("%c ",ch);
else
printf("%c ",' ');
}
printf("\n");
}
} void show(int i)
{
for(int qq = ;qq<= i;qq++)
{
for(int j = ;j<=qq;j++)
{
printf("%dx%d=%2d ",j,qq,j*qq);
}
printf("\n");
}
}
//以上是示例的测试函数 //以下是真正的实现函数 //找到对应函数的 函数指针 返回数组号
// 输入: "circle * 16" return 2
static int ExternFunc_Find(char* funcname)
{
int size = sizeof(functable)/sizeof(functable[]);
for(int i = ; i<size; i++)
{
if(matchString(funcname,functable[i].FuncName) == )
return i;
}
return -;
} //因为需要兼容字符串,所以需要二维数组 最多可以传参字符串长度为 (100-1)*4
static void* args[][] = {}; //外部调用函数,传入字符串自动找到对应函数 并执行.(不会打印返回值)
void ExternFunc_excute(char* str)
{
char* ptemp;
char ch;
ptemp = strstr(str," ");
if(ptemp == NULL)
{
ptemp = str+strlen(str);
ch = *ptemp;
}
else
{
ch = '\0';
*ptemp = '\0';
ptemp++;
} int loc = ExternFunc_Find(str); //寻找函数
if(loc == -)
{
printf("%s are not find\n the function list :\n",str);
ExternFunc_list();
return ;
} if(ch != '\0')
*ptemp = ch;
int success = sscanf(ptemp,functable[loc].fmt,&args[][],&args[][],&args[][],&args[][],&args[][],&args[][]); //为兼容 可以输入字符串而做出的妥协
int i = ;
ptemp = (char*)functable[loc].fmt;
for(i = ;i<;i++)
{
if((ptemp=strstr(ptemp,"%")) !=NULL)
{ if(*(++ptemp) == 's')
args[i][] = &args[i][];
else
args[i][] = args[i][];
}else break;
}
if(i!= success)
{
printf("Err: 函数%s 参数应该为%d个,但只有%d\n",functable[loc].FuncName,i,success);
return ;
}
//调用真正的函数
functable[loc].func(args[][],args[][],args[][],args[][],args[][],args[][],args[][]);
} void ExternFunc_list(void)
{
static char isfirstPrint = ; int size = sizeof(functable)/sizeof(functable[]);
printf("QuickComplet:");
for(int i = ;i<size;i++)
{
printf("\"%s\"",functable[i].FuncName);
if(i != (size-))
printf(",");
}
printf("\n\n*---------------------------------------------------------\n");
for(int i = ; i<size; i++)
{
printf(" | %s(%s);%30s\n",functable[i].FuncName,functable[i].fmt,functable[i].Introduction);
if(i != size-)
printf(" |--------------------------------------------------------\n");
}
printf("*---------------------------------------------------------\n");
} static void ExternFunc_SocReset(void)
{ __set_FAULTMASK();//关闭所有中断
NVIC_SystemReset();//复位
} static unsigned char matchString(const char* str1,const char* str2)
{
char* ptemp1 = (char*) str1;
char* ptemp2 = (char*) str2;
while(*ptemp1 != '\0' || *ptemp2 != '\0')
{
#if MATCH_CASE_ENABLE==0
if(((*ptemp1-*ptemp2) == ('A'-'a') || (*ptemp1-*ptemp2) == ('a'-'A'))&&
(*ptemp1>= 'A' && *ptemp1<= 'z' && *ptemp2>= 'A' && *ptemp2<= 'z'))
{
ptemp1++;
ptemp2++;
}else if(*ptemp1 != *ptemp2) return ;
#else
if(*ptemp1 != *ptemp2) return ;
#endif else
{
ptemp1++;
ptemp2++;
}
}
if(*ptemp1 == '\0'&& *ptemp2 == '\0')
return ;
else
return 0xFF;
}

ExternFunc.h

 #ifndef EXTERNFUNC_H_INCLUDED
#define EXTERNFUNC_H_INCLUDED #include "stdio.h"
#include "string.h" typedef struct
{
const char * FuncName;
void *( (*func)(void* args,...));
const char * fmt;
const char * Introduction;
} CALLFUNCTIONTABLE; #define EXPOTRFUNC(NAME,FUNC,FTM,INTRO) {#NAME,(void *(*)(void* args,...))FUNC,#FTM,#INTRO} extern const CALLFUNCTIONTABLE functable[]; void ExternFunc_excute(char* str); #endif // EXTERNFUNC_H_INCLUDED

  里面内置了两个函数  一个LIST函数RST

    LIST 指令是打印出当前所有的可以调用的函数信息

    RST 是复位单片机(Cortex M0.3.4.7 系列可用)   主要原理:禁止所有中断 ,人为触发复位中断.

    其他函数可以删除.

原理:

  函数传参的值以第一个参数的地址基准,依次向后偏移,可变参数的值获取方式也是这么做的.那么我们只需要把相应的值摆列排放到指定位置即可.

实现的关键在于一个函数指针:

    void *( (*func)(void* args,...));

  强制转换成上述指针函数后可以传入不定参数,以适应不同函数,不同个传参值 ,不同的传参类型. 

  我们知道函数指针是指向函数的,里面存放的是函数的跳转地址.那么使用函数指针就是把某个函数的跳转地址存进来,等到 PC寄存器读取到之后,自动跳转到指定函数.上面这个函数指针指向一个什么函数呢?指向返回值为空类型指针 , 传参为 (void * args ,...) 格式的函数.而 printf 的函数原型 int printf(const char* str , ... ) ; 这个" ... " 代表可变参数.所以我们才可以同时 printf 多个值,同时 还带有 "... "的函数有哪些呢: sprintf ,scanf ,sscanf. 可能 sscanf 和 sprintf 大家见得不多.这里给大家示例一下怎么使用.大家就知道是什么功能了

 char buff [];
char *string = "hello world";
int a = ;
sprintf(buff,"a = %d string = %s",a,string); printf("%s",buff); //buff: "a = 10 string = hello world" char ssbuff[] = "%d %s";
int b;
char sstring[];
sscanf(ssbuff,"10 HelloWorld",&b,sstring);
printf(" b = %d sstring = %s",b ,sstring); //b = 10 sstring = "HelloWorld"

实现原理参考文末跳转链接

  根据这个原理.当我们把函数 void show(int a ,int b); 强制转换为 void* show(void* a,...) 只要我们在 调用show() 的时候 只传送两个参数即可.如果你传入了其他参数,那么会有影响嘛,会有,会影响到函数里面的其他的局部变量的值.但是我们做好局部变量值的初始化的话,就没关系啦,

  怎么做到 对应的函数 传对应的值 ? 在functable 中需要输入 %c %d 就是对于输入格式的限定.同时在sscanf 中会返回转换一个int 表示成功转换数; 例如%d %c 正常的话会返回2 那么我只要检测 他返回的和我检测到的%号 的个数不匹配.那么我就报错,个数不匹配.

  通过 使用 sscanf 将字符串转化为对应格式.灌入指定的函数中,就可以执行响应函数啦.

  不过这么做有风险吗? 有 ,因为是强制类型转换,对于类型检查不严格(就没有类型检查) .可能会出现奇奇怪怪的现象.  同时 因为 float 是使用2进制的科学计数法在内存中存储. long long 类型和 double 是64 位宽,而我们的void* 是32位宽.所以 double 类型 long long 类型 float 类型 带有这三类的传参函数 和自定义的struct类型  参数值都会不正常.

printf 原理:http://www.cnblogs.com/ThatsMyTiger/p/6924462.html

单片机实现简易版shell的方法和原理的更多相关文章

  1. 实现简易版shell

    操作系统小组作业,实现一个简易shell,shell实现了下列命令 exit------退出终端命令 clr-------清屏命令 time-----时间命令 myshell----欢迎命令 quit ...

  2. js实现简易版validate

    需求分析 项目中需要根据选择不同的类型,显示不同的表单,采用的方法是css隐藏显示不需要的表单,但是这个表单字段都是必填的,尝试把不同的表单放在不同的form里,提交时根据不同的类型调用miniui自 ...

  3. MVC5+EF6 简易版CMS(非接口) 第四章:使用业务层方法,以及关联表解决方案

    目录 简易版CMS后台管理系统开发流程 MVC5+EF6 简易版CMS(非接口) 第一章:新建项目 MVC5+EF6 简易版CMS(非接口) 第二章:建数据模型 MVC5+EF6 简易版CMS(非接口 ...

  4. .NET Core的文件系统[5]:扩展文件系统构建一个简易版“云盘”

    FileProvider构建了一个抽象文件系统,作为它的两个具体实现,PhysicalFileProvider和EmbeddedFileProvider则分别为我们构建了一个物理文件系统和程序集内嵌文 ...

  5. 简易版自定义BaseServlet

    这几天在学Java Web,一直在思考Servlet重用的问题,就用java的反射机制实现自定义的简易版BaseServlet; 该方式有点像struts2 利用映射获取前端的参数.有兴趣的同学可以自 ...

  6. 简易版的TimSort排序算法

    欢迎探讨,如有错误敬请指正 如需转载,请注明出处http://www.cnblogs.com/nullzx/ 1. 简易版本TimSort排序算法原理与实现 TimSort排序算法是Python和Ja ...

  7. MVC5+EF6 简易版CMS(非接口) 第三章:数据存储和业务处理

    目录 简易版CMS后台管理系统开发流程 MVC5+EF6 简易版CMS(非接口) 第一章:新建项目 MVC5+EF6 简易版CMS(非接口) 第二章:建数据模型 MVC5+EF6 简易版CMS(非接口 ...

  8. MVC5+EF6 简易版CMS(非接口) 第二章:建数据模型

    目录 简易版CMS后台管理系统开发流程 MVC5+EF6 简易版CMS(非接口) 第一章:新建项目 MVC5+EF6 简易版CMS(非接口) 第二章:建数据模型 MVC5+EF6 简易版CMS(非接口 ...

  9. MVC5+EF6 简易版CMS(非接口) 第一章:新建项目

    目录 简易版CMS后台管理系统开发流程 MVC5+EF6 简易版CMS(非接口) 第一章:新建项目 MVC5+EF6 简易版CMS(非接口) 第二章:建数据模型 MVC5+EF6 简易版CMS(非接口 ...

随机推荐

  1. JVM知识(二):类加载器原理

    我们知道我们编写的java代码,会经过编译器编译成字节码(class文件),再把字节码文件装载到JVM中,最后映射到各个内存区域中,我们的程序就可以在内存中运行了.那么问题来了,这些字节码文件是怎么装 ...

  2. .NET 2.0 参考源码索引

    http://www.projky.com/dotnet/2.0/Microsoft/CSharp/csharpcodeprovider.cs.htmlhttp://www.projky.com/do ...

  3. Linux 挂载

    千万不要挂载到 根目录下 也不要用 umount -fl  会死的 fdisk -l 看 能挂载的是哪个盘 格式化 mkfs.ext4 /dev/vde 创建一个文件 mkdir /testmnt 卸 ...

  4. asp.net 一般处理程序实现网站验证码

    使用VerifyCode.ashx一般处理程序生成验证码,实现如下: using System; using System.Drawing; using System.Web; using Syste ...

  5. Python学习---IO的异步[twisted模块]

    安装twisted模块 Linux: pip3 install twisted Window: a. http://www.lfd.uci.edu/~gohlke/pythonlibs/#twiste ...

  6. CSS学习摘要-数值和单位及颜色

    在CSS中,值的类型有很多种,一些很常见,一些你却几乎没怎么遇到过.我们不会在这篇文档中面面俱到地描述他们,而只是这些对于掌握CSS可能最有用处的这些.本文将会涉及如下CSS的值: 数值: 长度值,用 ...

  7. Memorize and recite an important historical speech

    Memorize and recite an important historical speech memorize['memәraiz]v.[亦作memorise] 记住, 记忆 historic ...

  8. "字符串"经过strip 之后还是字符串, 而"字符串"经过split 分开后,就变成了一个列表["x","xx","xxx"]

    "字符串"经过strip 之后还是字符串, 而"字符串"经过split 分开后,就变成了一个列表["x","xx",&q ...

  9. python邮件处理

    SMTP SMTP(Simple Mail Transfer Protocol)即简单邮件传输协议,它是一组用于由源地址到目的地址传送邮件的规则,由它来控制信件的中转方式.Python对SMTP支持有 ...

  10. 基于 Webpack 4 搭建 Vue 开发环境

    自从工作之后,就已经很久没有写过博客了.时间被分割得比较碎,积累了一段时间的学习成果,才写下了这篇博客. 之前有写过 Webpack4 的文章,但是都比较偏入门,唯一的一篇实战篇 -- 基于Webpa ...