C语言三剑客--一个简单的命令解释器

这是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等,这里分享一下,不喜勿喷。

猜你喜欢

转载自blog.csdn.net/Terrys0518/article/details/83924696