发一个裸机下的界面编写方式.,,不用结构体,,设计简单,思路清晰,没有死循环,,,,使用
发一个代码.也是本人在长期的写程序中慢慢摸索出来的,不敢说是独创..也算是原创.呵呵,,这个代码编译不通过,,因为我只把最重要的两个文件从工程中拿了出来,,其它的就不用了,,
大体说明一下.
每个界面有一个函数.
每个界面有一个初始化函数.
每个界面有一个标记STS
点击此处下载 ourdev_512719.rar(文件大小:2K) (原文件名:code.rar)
....
不多说了,,
个人在多个项目中使用过,稳定性很好.
没有使用那些乱七八糟的定时器,中断之类的东西,,, 这方法确实思路清晰,但程序大时候,占用程序空间相对较大,很多的switch
曾经用过.深有感触 是啊,,一个界面一个SWITCH,
反过来想,,
无论用什么方法写,都是在一个界面里来操作按键,怎么着也得去做按键的对应处理,,, MARK GUI 呵呵……恭喜LZ也摸索出这种模式了哈。这种工程模式我已经用很久了!不过我不使用switch,因为
局限性很大,每个状态机通常只能有一条执行线路。我使用if结构,这样每个状态机可以有多个并发
的执行线路。我注意到,你的代码中还没有使用调度器。其实配合这种状态机系统,可以用很简单的
合作式调度器来控制状态机的启动状态。有兴趣可以参考我的一些代码。
同时,配合这种编程方式,我正在酝酿一种可视化集成开发环境。哎……可惜一直没有时间实施…… 【4楼】 Gorgon Meducer 傻孩子
"我注意到,你的代码中还没有使用调度器。其实配合这种状态机系统,可以用很简单的
合作式调度器来控制状态机的启动状态。有兴趣可以参考我的一些代码。
"
Gorgon Meducer 傻孩子
您曾写的哪个代码有用到过这种方式的?很想学习下. 4楼,能否发一个按LZ所说方式、基于合作式调度器的例子? 顶一个 好贴,再顶顶! 以下是我的裸机界面,可以参考下
void (*Thrd)(void);
char InitFlag=1;
//时间设置
void TimeSet(void)
{
if(InitFlag == 1)//需要初始化界面
{
InitFlag=0;
}
if(Key == KEY_NONE)return ;
switch(Key)
{
case KEY_EXIT:
Thrd = ProcMainKey;//返回主界面
InitFlag = 1;//并需要刷新界面
break;
default:
break;
}
}
//日期设置
void DateSet(void)
{
if(InitFlag == 1)//需要初始化界面
{
InitFlag=0;
}
if(Key == KEY_NONE)return ;
switch(Key)
{
case KEY_EXIT:
Thrd = ProcMainKey;//返回主界面
InitFlag = 1;//并需要刷新界面
break;
default:
break;
}
}
//按键提示设置
void KeyToneSet(void)
{
if(InitFlag == 1)//需要初始化界面
{
InitFlag=0;
}
if(Key == KEY_NONE)return ;
switch(Key)
{
case KEY_EXIT:
Thrd = ProcMainKey;//返回主界面
InitFlag = 1;//并需要刷新界面
break;
default:
break;
}
}
void ProcMainKey(void)
{
if(InitFlag == 1)//需要初始化界面
{
InitFlag=0;
}
if(Key == KEY_NONE)return ;
switch(Key)
{
KEY1:
Thrd = TimeSet;//将“TimeSet”扔到while(1)里面循环
InitFlag = 1;
break;
KEY2:
Thrd = DateSet;//将“DateSet”扔到while(1)里面循环
InitFlag = 1;
break;
KEY3:
Thrd = KeyToneSet;//将“KeyToneSet”扔到while(1)里面循环
InitFlag = 1;
break;
default:
break;
}
}
void main(void)
{
Thrd = ProcMainKey;
while(1)
{
Thrd();
}
} 标记下,会火 MARK一下,谢谢。 排队学习 :
请把你的代码上传来看下,谢谢! MARK 关注下 很好,顶 Gorgon Meducer 傻孩子
可否把您的相关代码贴上来学习一下,谢谢。 不错,学习经验。 值得参考啊,谢谢! 真正有价值的怎能不标。。。。。个。。记。 傻孩子,都在等你! 灵活运用C语言的 结构体 和 指针,可以达到 事半功倍 的效果~ 记号 顶一个 偶用函数指针表.... 记号 mark mark,等傻孩子 等傻孩子的方案 我这里有两个调度器,一个是过去用的简单版本,一个是后来的改进版本,但是原理都基本一样。
这里我只说一下原理:
所有的C函数都可以看作是状态机,哪怕是一个最简单的函数,也可以看成是一个只有一个状态的
状态机。因此,最简单的状态机原型可以是大家常用的:
void FSMExample(void);
为了方便控制状态机的启动和关闭,我修改了最基本的原型:
BOOL FSMExample(void);
这就是最简单的双态状态机:
当函数返回TRUE,表明该状态机仍然“希望”处于运行状态;
当函数返回FALSE,表明该状态机已经完成,希望终止。
根据这一规定,如果通过函数指针把所有的状态机连接起来,就可以形成下面的简单调度器:
typedef struct Process
{
BOOL (*Proc)(void); //返回False,自动关闭任务
volatile BOOL IfProcAlive;
}PROCESS;
typedef BOOL (*PROC_FUNCTION)(void);
void Process_Task(void)
{
static uint8 n = 0;
if (ProcPCB.IfProcAlive) //处理进程
{
ProcPCB.IfProcAlive = (*ProcPCB.Proc)();
}
n ++;
if (n >= g_cCOSPROCCounter)
{
n = 0;
//g_cScheduleTest = MIN(g_cScheduleTest + 1,254);
}
}
这个调度器很简单,就是检测一个注册了的顶层状态机其Alive状态是否为TRUE,
如果为TRUE,就调用;同时在调用后将状态机的返回值重新赋给ALive属性,这样
状态机就可以通过在函数中返回TRUE或者FALSE来控制自己的运行状态。
同时,利用这一特性,状态机里面也可以调用其他子状态机,只需要在某一个状态
中调用子状态机函数并检测返回值是否为FALSE就可以知道子状态机的运行情况。
下面是一个状态机内部的例子:
#define FSM_ACTION_FLAG s_tbState.Bits
#define FSM_STOP_ALL_ACTIONS s_tbState.Value = 0;
#define FSM_START FSM_ACTION_FLAG.BIT0
#define FSM_STATE_A FSM_ACTION_FLAG.BIT1
...
BOOL FSMExampleB(void)
{
...
}
BOOL FSMExampleA(void)
{
static ES_BYTE s_tbState = {0};
if (0 == s_tbState.Value)
{
FSM_START = TRUE; //自动开始标志……
}
if (FSM_START)
{
//一些初始化操作
....
FSM_START = FALSE;
FSM_STATE_B = TRUE;
}
if (FSM_STATE_B)
{
if (!FSMExampleB()) //调用子状态机
{
//子状态机完成
...
FSM_STATE_B = FALSE;
FSM_STATE_C = TRUE; //同时激活两个状态,比如,其中一个
FSM_STATE_D = TRUE; //用于检测某个超时计数器
}
}
if (FSM_STATE_C)
{
//等待某些出发条件,比如等待某一个引脚电平为低
if (等待某个触法条件)
{
//条件触法了,比如监测到某个引脚电平为低了
... //一些操作
FSM_STATE_C = FALSE;
FSM_STATE_E = TRUE;
}
}
if (FSM_STATE_D)
{
if (检测是否超时)
{
//发生了超时
... //可以设置一些标志
FSM_STOP_ALL_ACTIONS; //复位状态机
return FALSE; //关闭状态机
}
}
if (FSM_STATE_E)
{
//假设这个状态是一个正常的终止态
...
FSM_STOP_ALL_ACTIONS;
return FALSE;
}
return TRUE;
}
使用if结构的好处是,每一个子状态都可以独立被激活,也就是可以并发激活,
对于一些明显是互斥的状态,就可以用if (){}else if (){}结构来提高效率;
同时,对于一些拥有优先级的状态,也可以用 if ... else if结构来组织。
使用这种结构,实际上可以把每一个函数看成一个 进程, 而每一个内部if结构
看成一个线程。这也符合“进程含有资源”,“线程通常不含有资源”的描述。
有兴趣的人可以发现,我的很多代码都是这种结构的。 期待傻孩子更详细的论述! 之前我也用这种状态机的方法来编写,,,但是我后来发现使用一些简单的时间片调度系统,更方便。 老版本简易调度器
点击此处下载 ourdev_513080.rar(文件大小:8K) (原文件名:FSM_EASY.rar)
新版本调度器
点击此处下载 ourdev_513081.rar(文件大小:28K) (原文件名:FSM_V2.rar) 好东西 mark 顶~ 不能不顶 MARK MARK 看来我这块砖扔的还行..../emotion/em025.gif./emotion/em025.gif./emotion/em025.gif./emotion/em025.gif 好帖马克之 jh mark 记号,有时间仔细看看。 参考了Gorgon Meducer 傻孩子的程序,,收获不小..
我自己改了下自己的界面程序写法.
去掉了状态变量标志.
没有改掉的是SWITCH这块,,虽然不如IF的效率高,但是直观一些,,也一直没想出来别的方法,直观但效率也要高的.
//////
以下程序仅讨论人机界面UI处理.
现在的方式是.
每个界面用一个函数来表示.
每个函数里就是处理按键.用的SWITCH方式.关于按键的产生,我在我另一个帖子里也贴出了程序.
用的函数指针来进行界面的切换.
定了了三个宏
#define UI_GOTO(index) {pUI=(index);}
#define UI_GOTO_CHILD(index) {pUI=(index); KeyDir=GOTO_CHILD;}
#define UI_GOTO_FATHER(index) {pUI=(index); KeyDir=GOTO_FATHER;}
大家不要省砖,,,俺家要盖房子.
点击此处打开 ourdev_513809.txt(文件大小:5K) (原文件名:xpUI example.txt)
再上传我的按键处理程序
可以响应多种方式,,,KEY_DOWN KEY_HOLD KEY_BURST KEY_UPKEY_DOUBLE
用按键属性值返回.与SWITCH组合非常方便直观
点击此处下载 ourdev_513811.txt(文件大小:1K) (原文件名:key.h.txt)
点击此处下载 ourdev_513812.txt(文件大小:6K) (原文件名:key.c.txt) 不错 有空慢慢看 学习一下。 顶楼主,加盖一层楼! Mark MK 标记。 mark mark mark,学习一下! 标记! 不错!学习了 看了半天,我没看懂状态机,我的多层界面采用索引
//定义按键功能指针
void (*KeyFuncPtr)(void);
/*定义类型*/
typedef struct
{unsigned int KeyStateIndex; //当前的状态索引号
void (*CurrentOperate)(void); //当前状态应该执行的功能操作
}StateTab;
/*-------------------------------------------------------------*/
StateTab KeyTab=
{
{1,(*main_main)},
{11,(*main_0)}, //参数设置
{12,(*main_0)}, //数据监视
{13,(*main_0)}, //关于
{14,(*main_14)}, //手动发送
{15,(*main_14_set)}, //设置为自动发送
{151,(*main_main)},//
{141,(*hand_send)},
{111,(*main_11)}, //参数设置子菜单 主站号码
{112,(*main_11)}, //温度限定 //5
{113,(*main_11)}, //过压限定
{114,(*main_12)}, //过流限定
{115,(*main_12)}, //欠压限定
{116,(*main_12)}, //功率限定
{117,(*main_13)}, //全部默认 //10
{118,(*main_13)}, //数据存储
{119,(*main_13)}, //自动发送频率设定
{131,(*about)}, //关于子菜单
{1111,(*set_number)}, //设置主站号码
{1121,(*main_121)}, //温度限定子 //14
{1122,(*main_121)}, //wenduxianding
{11211,(*set_temph)},//高温限定
{11221,(*set_templ)},//低位限定
{1131,(*set_ovv)}, //过压限定设置
{1141,(*set_ovi)}, //过流设置 //19
{1151,(*set_qy)}, //欠压设置
{1161,(*set_ovw)}, //功率限定设置
{1171,(*set_default)},//默认设置
{1181,(*set_store)},//数据存储 23
{121,(*main_21)}, //数据监视gsm
{122,(*main_21)}, //电能
{123,(*main_21)}, //开关量
{124,(*main_22)}, // 温度 27
{1221,(*main_221)},//三相电压电流
{1222,(*main_221)},//三功率 29
{1223,(*main_221)},//有功功率
{1224,(*main_224)},//无功功率
{1231,(*rd_switch)},//开关量读取函数 31
{1211,(*main_211)},//gsm子--at
{1212,(*main_211)},//init
{1213,(*main_211)},// 发信息测试 34
{12111,(*test_at)},//at
{12121,(*test_init)}, //init
{12131,(*test_sms)},//sms
{12211,(*axiang)},
{12221,(*abcxiang)}, //41
{12231,(*abcxiang2)},
{12241,(*abcxiang3)},
{1241,(*wendu)},//41
{1191,(*set_auto)}, //自动发送频率设定
};
/*-------------------------------------------------------------*/
void Menu_Change(unsigned char key)
{
unsigned char i=0;
unsigned int temp;
unsigned char cengxian="";
// 第一层 第二层 第三层 第四层 第五层
//unsigned char status[]={0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x2d,0x20,0x2d,0x20,0x2d,0x20,0x2d,0x20};//
unsigned char status_s[]=" - - - - ";
//第一层菜单偏移为8第二为10 第三为12第四为14第五为16
if(zidongesc)
{zidongesc=0;
key=Esc;
}
switch(key)
{
caseUP: //向上的键
{
temp=Menu_Index-1;
for(i=0;i<MENU_SIZE;i++)
{if(temp==KeyTab.KeyStateIndex) {Menu_Index=temp;tab_num=i;break;}
}
break;
}
case Enter: //回车键
{
temp=Menu_Index*10+1;
for(i=0;i<MENU_SIZE;i++)
{if(temp==KeyTab.KeyStateIndex) {Menu_Index=temp;tab_num=i;break;}
}
break;
}
caseDown: //向下的键
{
temp=Menu_Index+1;
for(i=0;i<MENU_SIZE;i++)
{if(temp==KeyTab.KeyStateIndex) {Menu_Index=temp;tab_num=i;break;}
}
break;
}
case Esc: //退出键
{
temp=Menu_Index/10;
for(i=0;i<MENU_SIZE;i++)
{if(temp==KeyTab.KeyStateIndex) {Menu_Index=temp;tab_num=i;break;}
}
break;
}
}
KeyFuncPtr=KeyTab.CurrentOperate;
(*KeyFuncPtr)(); //执行当前的按键操作
if(key)
OSWait(K_TMO,30);
if(key||caidanxiang)
{
if(Menu_Index<=10) {cengxian=Menu_Index;cengxian=10;cengxian=10;cengxian=10;cengxian=10;}
if((Menu_Index>10)&&(Menu_Index<100)) {cengxian=Menu_Index/10;cengxian=Menu_Index%10;cengxian=10;cengxian=10;cengxian=10;}
if((Menu_Index>100)&&(Menu_Index<1000)) {cengxian=Menu_Index/100;cengxian=(Menu_Index%100)/10;cengxian=Menu_Index%10;cengxian=10;cengxian=10;}
if((Menu_Index>1000)&&(Menu_Index<10000)) {cengxian=Menu_Index/1000;cengxian=(Menu_Index%1000)/100;cengxian=((Menu_Index%1000)%100)/10;cengxian=Menu_Index%10;cengxian=10;}
if(Menu_Index>10000) {cengxian=Menu_Index/10000;cengxian=(Menu_Index%10000)/1000;cengxian=((Menu_Index%10000)%1000)/100;cengxian=(((Menu_Index%10000)%1000)%100)/10;cengxian=Menu_Index%10;}
status_s=cengxian+48;
status_s=cengxian+48;
status_s=cengxian+48;
status_s=cengxian+48;
status_s=cengxian+48;
display_str(3,0,status_s);
caidanxiang=0;//实现首次进入餐单就刷新一次
if(!zhizhen)
{ switch ((Menu_Index%10)-1)
{
case 0:display_str(0,0,"->");display_str(1,0,"--");display_str(2,0,"--");break;
case 1:display_str(0,0,"--");display_str(1,0,"->");display_str(2,0,"--");break;
case 2:display_str(0,0,"--");display_str(1,0,"--");display_str(2,0,"->");break;
case 3:display_str(0,0,"->");display_str(1,0,"--");display_str(2,0,"--");break;
case 4:display_str(0,0,"--");display_str(1,0,"->");display_str(2,0,"--");break;
case 5:display_str(0,0,"--");display_str(1,0,"--");display_str(2,0,"->");break;
case 6:display_str(0,0,"->");display_str(1,0,"--");display_str(2,0,"--");break;
case 7:display_str(0,0,"--");display_str(1,0,"->");display_str(2,0,"--");break;
case 8:display_str(0,0,"--");display_str(1,0,"--");display_str(2,0,"->");break;
}
}
}
} 不错!学习了 MARK 学习,好好学习! 也贴一个界面处理的函数,
根据screen_status数值来跑 。
void get_screen_status(void)
{
get_key_value();//获取键值
/*********************运行画面响应**********************/
if(screen_status == 0x00)
{
//时间显示刷新
time_show();
if(key_Press_flag)//有键按下
{
if(key_value == Enter_key_value)//确认按下
{
chn_disp(Menu_tab); //菜单 显示
Cursor_menu = 1; //菜单光标=1,
Highlight_Cursor_menu();//菜单光标显示
screen_status = 0x01; //屏幕状态改为:菜单
}
key_value = 0; //键盘数据清除,等下下一次的操作
key_Press_flag = 0;
}
asm("nop");
}
/*********************运行响应结束*********************/
/*********************菜单画面响应**********************/
if(screen_status == 0x01)
{
if(key_Press_flag)//有键按下
{
//菜单光标处理,转移到其它画面
Cursor_menu_TAB_show();
if(key_value == Back_key_value)//返回按下
{
Highlight_Clear(); //删除所有光标
chn_disp(run_tab); //运行画面 显示
Cursor_menu = 1; //菜单光标=1,
screen_status = 0x00; //屏幕状态改为:运行
}
key_value = 0; //键盘数据清除,等下下一次的操作
key_Press_flag = 0;
}
}
/*********************菜单响应结束*********************/
/*********************瞬时值画面响应**********************/
if(screen_status == 0x02)
{
Now_value_View_show();
if(key_Press_flag)//有键按下
{
if(key_value == Back_key_value)//返回按下
{
Highlight_Clear();
chn_disp(Menu_tab); //菜单画面 显示
Highlight_Cursor_menu(); //菜单光标显示
screen_status = 0x01; //屏幕状态改为:菜单
}
Cursor_now_TAB_show(); //瞬时值光标处理
key_value = 0; //键盘数据清除,等下下一次的操作
key_Press_flag = 0;
}
}
/*********************瞬时值响应结束*********************/
/*********************历史值画面响应**********************/
if(screen_status == 0x03)
{
History_value_View_show();
if(key_Press_flag)//有键按下
{
if(key_value == Back_key_value)//返回按下
{
Highlight_Clear();
chn_disp(Menu_tab); //菜单画面 显示
Highlight_Cursor_menu(); //菜单光标显示
screen_status = 0x01; //屏幕状态改为:菜单
}
Cursor_History_TAB_show();
key_value = 0; //键盘数据清除,等下下一次的操作
key_Press_flag = 0;
}
}
/*********************历史值响应结束*********************/
/*********************历史值查询画面响应**********************/
if(screen_status == 0x06)
{
if(key_Press_flag)//有键按下
{
if(key_value == Back_key_value)//返回按下
{
chn_disp(History_tab);
Highlight_Cursor_History();
screen_status = 0x03;
}
key_value = 0; //键盘数据清除,等下下一次的操作
key_Press_flag = 0;
}
}
/*********************历史值查询响应结束*********************/
/*********************设置画面响应**********************/
if(screen_status == 0x04)
{
time_set_show(); //参数修改 显示
if(key_Press_flag)//有键按下
{
Cursor_Set_TAB_show(); //修改光标
if(key_value == Back_key_value)//返回按下
{
Highlight_Clear();
chn_disp(Menu_tab); //菜单画面 显示
Highlight_Cursor_menu(); //菜单光标显示
screen_status = 0x01; //屏幕状态改为:菜单
}
key_value = 0; //键盘数据清除,等下下一次的操作
key_Press_flag = 0;
}
}
/*********************设置响应结束*********************/
/*********************帮助画面响应**********************/
if(screen_status == 0x05)
{
if(key_Press_flag)//有键按下
{
if(key_value == Back_key_value)//返回按下
{
chn_disp(Menu_tab); //菜单画面 显示
Highlight_Cursor_menu(); //菜单光标显示
screen_status = 0x01; //屏幕状态改为:菜单
}
key_value = 0; //键盘数据清除,等下下一次的操作
key_Press_flag = 0;
}
}
/*********************帮助响应结束*********************/
} 火了
mark 此贴火了。 mark mark.学习 marl MARK 程序当然得这么写喽,看网上人家写的GSM发短信程序,发一个while一下,发一个while一下,别的活还要不要干啦
SWITCH与IF各有千秋, mark mark 先mark 慢慢学习 mark 慢慢看,不过跟写个结构体,然后用函数指针有多大的区别? mark 越简单越灵活,呵呵, LZ的例子看得不是很明白阿,哪位大大能说得具体的吗,,我太菜了,,,或是举个简单的点例子阿??? mark 学习了 学习了…… 谢谢学习 很不错 标志下,准备学了 不mark不行了 MARK GUI mark~~ mark~~ 好东西 好贴,mark! mark mark.非常好的东西啊 很不错,MARK MARK 慢慢学习 顶 学习一下···· 我也mark一下 MARK 思路不错。 mark.......... 学习一下