1. 程式人生 > >C語言三劍客--一個簡單的命令直譯器

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等,這裡分享一下,不喜勿噴。