这是C语言三剑客书(三本书,不记得是哪一个了)的一个简单的C语言实现的的命令解释器,加在单片机软件工程里,用来调试,还是比较灵活方便的。
自制命令如下
1. 返回芯片 的ID值
&GetID
12345
2. 设置比例调节器的值为 12
& 12 SetP
......
实现如下:
1. 创建栈,数值栈 和 字符栈
static int stack[STKSIZE];
static char cstack[CSTKSIZE];
static int sp;
static int csp;
/*
name: push
description:将数据num压栈
author: Terry
time: 2012/4/29
*/
void push(int num)
{
if(sp == STKSIZE)
{
Debug("Stack Full!\n");
sp = 0;
return;
}
stack[sp++] = num;
}
/*
name: pop
description:数据弹出,由函数返回值返回
author: Terry
time: 2012/4/29
*/
int pop(void)
{
if(sp == 0)
{
Debug("Stack empty!!!\n");
return 0;
}
return (stack[--sp]);
}
/*
name: pushc
description:字符c压栈
author: Terry
time: 2012/4/29
*/
void pushc(char c)
{
if(csp == CSTKSIZE)
{
Debug("CStack Full!\n");
csp = 0;
return;
}
cstack[csp++] = c;
}
/*
name: popc
description:字符弹出,由返回值返回
author: Terry
time: 2012/4/29
*/
char popc()
{
if(csp == 0)
{
Debug("CStack empty!!!\r\n");
return 0;
}
return (cstack[--csp]);
}
/*返回数值栈指针*/
int getsp()
{
return sp;
}
/*返回字符栈指针*/
int getcsp()
{
return csp;
}
/*
name: popcstr
description:弹出一个字符串 x:字符串长度, *p接受字符串的地址
author: Terry
time: 2012/4/29
*/
int popcstr(int x , char *p)
{
int i;
for(i = 0;i < x; i++)
{
p[i] = popc();
if(p[i] == 0 )
break;
}
p[i] = 0;
return i;
}
2. 命令解释器
巧妙构建结构体,命令列表
typedef struct dtable
{
union
{
struct
{
unsigned char len;
char words[7];
}bytes;
char bits[8];
}id;
void (*fun)();
}table;
const table dictionary[] =
{
{5,'G','e','t','I','D',0,0,getid},
{5,'S','e','t','P',0,0,0,setup},
{7,'g','e','t','i','n','f','o',getinfo},
{6,'T','e','s','t','s','h',0,Testsh},
};
命令调用函数
void GetID(void)
{
uint32_t cpuid[3];
cpuid[0] = *(uint32_t *)(0x1ffff7e8); //STM32 全球统一的ID号
cpuid[1] = *(uint32_t *)(0x1ffff7ec);
cpuid[2] = *(uint32_t *)(0x1ffff7f0);
printf("%x %x %x \r\n",cpuid[0],cpuid[1],cpuid[2]);
}
/*其它三个函数略*/
命令解释器主程序
/*初始化*/
void CLI_Init()
{
sp = 0;
csp = 0;
numdic = (int)(sizeof(dictionary)/sizeof(dictionary[0])); //计算命令个数
}
/*
name: trypush
description:将非命令输入尝试压栈,转为数据或者字符串
author: Terry
time: 2012/4/29
*/
int trypush(char *str)
{
int i;
int val = 0;
if(isalpha(str[0])) //如果首字符是字符
{
for(i = strlen(str) -1; i >= 0; i--)
pushc(str[i]);
push((int)(strlen(str)));
}
else
{
for(i = 0; i< (int)strlen(str); i++)
{
if(isdigit(str[i])) //如果是数字
val = val * 10 +str[i] - '0';
else
{
printf("!!%s error???",str);
return 1;
}
}
push(val);
}
return 0;
}
/*
name: decipher
description:命令解释器
author: Terry
time: 2012/4/29
*/
void decipher(void const * argument)
{
char *tok;
int8_t err;
struct dtable cmds;
unsigned char match;
unsigned char i;
printf("welcome use the CLI\r\n");
printf("version 1.0\r\n");
CLI_Init();
while(1)
{
HAL_UART_Receive_DMA(&huart1,rx_buffer,STRLENTH); //DMA的方式接收数据,一旦有空闲中断发生,则结束数据接收
if(Rec_flg.recv_end_flag ==1) //recv_end_flag 串口数据成功接收
{
memcpy(str,rx_buffer,Rec_flg.rx_len);
Rec_flg.recv_end_flag = 0;
Rec_flg.rx_len=0;
printf("\r\n$");
tok = strtok((char *)str," ");
if(tok)
{
do
{
cmds.id.bytes.len = (unsigned char)strlen(tok);
for(i = 0; i < 7; i++)
if(i < cmds.id.bytes.len)
cmds.id.bytes.words[i] = (tok[i]);
else
cmds.id.bytes.words[i] = 0;
match = 0;
for(i = 0; i < numdic && !match; i++)
{
if(memcmp(dictionary[i].id.bits ,cmds.id.bits ,8) == 0)
{ match = 1;
(*dictionary[i].fun)();
}
}
err = 0;
if(!match)
{
if(trypush(tok))
err = 1;
}
tok = strtok(NULL," ");
}while(tok && !err); //strtok 只能在一个任务中使用,涉及到全局变量
}
}
osDelay(100);
}
}
/*需要头文件
#include "string.h"
#include "stdio.h"
#include "ctype.h"
*/
这个方法本人数次尝试,功能非常简单,比较实用,适合给系统留个小后门,方便现场修改和优化系统参数。当然还有其它方式,比如ModBus RTU等,这里分享一下,不喜勿喷。