Rt-thread 中有一個完整的finsh(shell )系統,使用串口做命令行輸入輸出.但是想要用這個炫酷的工具就必須要上rtthread系統,或者花大力氣將其移植出來.於是我就自己寫了一個類似於這樣的插件.只需要把一對.c/.h文件加入到你的工程,就可以實現這個簡易版的shell.
git: https://github.com/KimAlittleStar/ExternFunc
ExternFunc.c

1 #include "stdio.h"
2 #include "string.h"
3 #include "ExternFunc.h"
4 #include "stm32f4xx_hal.h"
5
6 #define MATCH_CASE_ENABLE 0 //函數調用名稱大小寫是否敏感 1表示敏感 0 表示不敏感
7
8 void show(int i); 9 void showcircle(char ch,int r); 10
11 static int ExternFunc_Find(char* funcname); 12 static void ExternFunc_list(void); 13 static void ExternFunc_SocReset(void); 14 static unsigned char matchString(const char* str1,const char* str2); 15
16 const CALLFUNCTIONTABLE functable[] =
17 { 18 EXPOTRFUNC(LIST,ExternFunc_list, ,函數列表), 19 EXPOTRFUNC(RST,ExternFunc_SocReset,,芯片軟件復位), 20 EXPOTRFUNC(circle,showcircle,%c %d,串口顯示一個圓), 21 EXPOTRFUNC(九九乘法表,show,%d,%d乘法表) 22 }; 23 //EXPOTRFUNC( 函數別名命令行調用的名字 |真正的函數名 | 函數傳參的格式字符串 |這個函數的簡介)
24 void simplefunction(char* str,unsigned int sum,float dee,char ch) 25 { 26
27 printf("接收到的字符串是:%s,\n\
28 接收到的字符是: %c \n\ 29 接受到的數字是 %d\n\ 30 接收到的小數是 %f __ \n ",str,ch,sum,dee);
31 } 32
33 void showcircle(char ch,int r) 34 { 35 for(int i = 1; i<=(2*r); i++) 36 { 37 for(int j = 1; j<(2*r); j++) 38 { 39 if(((i-r)*(i-r)+(j-r)*(j-r))<=(r*r)) 40 printf("%c ",ch); 41 else
42 printf("%c ",' '); 43 } 44 printf("\n"); 45 } 46 } 47
48 void show(int i) 49 { 50 for(int qq = 1;qq<= i;qq++) 51 { 52 for(int j = 1;j<=qq;j++) 53 { 54 printf("%dx%d=%2d ",j,qq,j*qq); 55 } 56 printf("\n"); 57 } 58 } 59 //以上是示例的測試函數 60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85 //以下是真正的實現函數 86
87 //找到對應函數的 函數指針 返回數組號 88 // 輸入: "circle * 16" return 2
89 static int ExternFunc_Find(char* funcname) 90 { 91 int size = sizeof(functable)/sizeof(functable[0]); 92 for(int i = 0; i<size; i++) 93 { 94 if(matchString(funcname,functable[i].FuncName) == 0) 95 return i; 96 } 97 return -1; 98 } 99
100
101 //因為需要兼容字符串,所以需要二維數組 最多可以傳參字符串長度為 (100-1)*4
102 static void* args[7][100] = {0}; 103
104 //外部調用函數,傳入字符串自動找到對應函數 並執行.(不會打印返回值)
105 void ExternFunc_excute(char* str) 106 { 107 char* ptemp; 108 char ch; 109 ptemp = strstr(str," "); 110 if(ptemp == NULL) 111 { 112 ptemp = str+strlen(str); 113 ch = *ptemp; 114 } 115 else
116 { 117 ch = '\0'; 118 *ptemp = '\0'; 119 ptemp++; 120 } 121
122
123
124
125 int loc = ExternFunc_Find(str); //尋找函數
126 if(loc == -1) 127 { 128 printf("%s are not find\n the function list :\n",str); 129 ExternFunc_list(); 130 return ; 131 } 132
133 if(ch != '\0') 134 *ptemp = ch; 135 int success = sscanf(ptemp,functable[loc].fmt,&args[0][1],&args[1][1],&args[2][1],&args[3][1],&args[4][1],&args[5][1]); 136
137 //為兼容 可以輸入字符串而做出的妥協
138 int i = 0; 139 ptemp = (char*)functable[loc].fmt; 140 for(i = 0;i<7;i++) 141 { 142 if((ptemp=strstr(ptemp,"%")) !=NULL) 143 { 144
145 if(*(++ptemp) == 's') 146 args[i][0] = &args[i][1]; 147 else
148 args[i][0] = args[i][1]; 149 }else break; 150 } 151 if(i!= success) 152 { 153 printf("Err: 函數%s 參數應該為%d個,但只有%d\n",functable[loc].FuncName,i,success); 154 return ; 155 } 156 //調用真正的函數
157 functable[loc].func(args[0][0],args[1][0],args[2][0],args[3][0],args[4][0],args[5][0],args[6][0]); 158 } 159
160
161 void ExternFunc_list(void) 162 { 163 static char isfirstPrint = 0; 164
165 int size = sizeof(functable)/sizeof(functable[0]); 166 printf("QuickComplet:"); 167 for(int i = 0;i<size;i++) 168 { 169 printf("\"%s\"",functable[i].FuncName); 170 if(i != (size-1)) 171 printf(","); 172 } 173 printf("\n\n*---------------------------------------------------------\n"); 174 for(int i = 0; i<size; i++) 175 { 176 printf(" | %s(%s);%30s\n",functable[i].FuncName,functable[i].fmt,functable[i].Introduction); 177 if(i != size-1) 178 printf(" |--------------------------------------------------------\n"); 179 } 180 printf("*---------------------------------------------------------\n"); 181 } 182
183
184
185
186 static void ExternFunc_SocReset(void) 187 { 188
189 __set_FAULTMASK(1);//關閉所有中斷
190 NVIC_SystemReset();//復位
191 } 192
193
194 static unsigned char matchString(const char* str1,const char* str2) 195 { 196 char* ptemp1 = (char*) str1; 197 char* ptemp2 = (char*) str2; 198 while(*ptemp1 != '\0' || *ptemp2 != '\0') 199 { 200 #if MATCH_CASE_ENABLE==0
201 if(((*ptemp1-*ptemp2) == ('A'-'a') || (*ptemp1-*ptemp2) == ('a'-'A'))&&
202 (*ptemp1>= 'A' && *ptemp1<= 'z' && *ptemp2>= 'A' && *ptemp2<= 'z')) 203 { 204 ptemp1++; 205 ptemp2++; 206 }else if(*ptemp1 != *ptemp2) return 1; 207 #else
208 if(*ptemp1 != *ptemp2) return 1; 209 #endif
210
211 else
212 { 213 ptemp1++; 214 ptemp2++; 215 } 216 } 217 if(*ptemp1 == '\0'&& *ptemp2 == '\0') 218 return 0; 219 else
220 return 0xFF; 221 }
ExternFunc.h

1 #ifndef EXTERNFUNC_H_INCLUDED 2 #define EXTERNFUNC_H_INCLUDED
3
4 #include "stdio.h"
5 #include "string.h"
6
7 typedef struct
8 { 9 const char * FuncName; 10 void *( (*func)(void* args,...)); 11 const char * fmt; 12 const char * Introduction; 13 } CALLFUNCTIONTABLE; 14
15 #define EXPOTRFUNC(NAME,FUNC,FTM,INTRO) {#NAME,(void *(*)(void* args,...))FUNC,#FTM,#INTRO}
16
17 extern const CALLFUNCTIONTABLE functable[]; 18
19 void ExternFunc_excute(char* str); 20
21
22 #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 大家見得不多.這里給大家示例一下怎么使用.大家就知道是什么功能了
1 char buff [100]; 2 char *string = "hello world"; 3 int a = 10; 4 sprintf(buff,"a = %d string = %s",a,string); 5
6 printf("%s",buff); //buff: "a = 10 string = hello world"
7
8 char ssbuff[100] = "%d %s"; 9 int b; 10 char sstring[50]; 11 sscanf(ssbuff,"10 HelloWorld",&b,sstring); 12 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