打印

ms2程序分析

[复制链接]
7929|40
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
lldwsw|  楼主 | 2007-6-19 19:44 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
ms2
http://file.21ic.com/MCU/实用的单片机系统第二版.rar
ms3
http://file.21ic.comMCU实用单片机系统第三版(带SI).rar

一:下载MS2.RAR压缩包解压缩后可以看到如下界面:
第一项为MS2软件包,第二项为SourceInsight3.0,它是一款非常优秀的C语言编辑器,目前各个大公司,比如华为等都用它,手机行业几乎无一例外。它的作用是代替Keil的编辑环境,因为Keil的编辑环境太难看了,并且使用也不方便,用它来看程序,查找函数特别容易,以下是它的界面:

相关帖子

沙发
lldwsw|  楼主 | 2007-6-19 19:46 | 只看该作者

continue

使用特权

评论回复
板凳
lldwsw|  楼主 | 2007-6-19 19:47 | 只看该作者

continue

各位可以看到,它里面不同的关键字的颜色都是不同的,看上去很舒服,最主要的还是它的查找功能,比如要看MSTimerStart的函数原型,但不知道他在那个地方,双击函数名,右击后出现属性,点“Jump to Definition”,就可以看到函数原型,这个功能Keil下也有,还有一个Keil下没有的,就是这个函数被谁调用了,双击函数名,右击后出现属性,点“Jump to Caller”。
具体请参考SourceInsight使用说明,也就是第四项,第三项是周立功的DP-51电路图,因为MS2的设计本身是不倚赖外设的,所以没有加外部接口,直接用串口来演示,如下图:

使用特权

评论回复
地板
lldwsw|  楼主 | 2007-6-19 19:48 | 只看该作者

continue

大家可以看到liweifeng, test1,test2,test3在不停的打印,这是四个伪任务,按一定的时候间隔打印,我们将在后面分析。当然按键等都可以在处理字符串栏中输入,在接收窗口显示。

以上是学习MS2的必要条件,有一个感性认识。

假设朋友你看了Source Insight的说明(以下简称SI,有些朋友用的是3.5版本的也可以,3.5版本兼容3.0版本),并且安装了SI,还破密了(里面有序列号),那么我们接下来就开始分析程序:

进入如下界面:

使用特权

评论回复
5
lldwsw|  楼主 | 2007-6-19 19:49 | 只看该作者

continue

双击红框中的项目文件,出现如下界面:

使用特权

评论回复
6
lldwsw|  楼主 | 2007-6-19 19:52 | 只看该作者

continue

首先,大家应该对照着SI的说明书用熟SI先。功能都在鼠标右击里面。


接下来我们来分析程序,在boot.c文件中找到main函数,它是入口,第一步肯定要先看它。
里面很简单,就是几个函数,
1:init_process();  初始化函数,大家可以进入它的原型,双击函数名,右击点“Jump to definition”,就出来一个界面

使用特权

评论回复
7
lldwsw|  楼主 | 2007-6-19 19:54 | 只看该作者

end

这些函数大家应该很熟悉的
1:端口初始化,
2:中断初始化,
3:等待外设,尤其是一些继电器类需要延时的
4:一个条件编译,因为飞利浦的芯片支持6clock和12clock,一般MCU都是12clock,采用6clock速度可以提高一倍,条件编译就是说假设定义过SPEEDUP,比如有#define SPEEDUP,那么就编译SETBIT(CKCON, 0),若没有#define SPEEDUP,则编译RESETBIT(CKCON, 0),条件编译可能大家理解不了,可以先不管。需要说的是,往往在一套软件中,有不同的应用,比如LCD屏有两种,就可以采用条件编译,是选择A,还是选择B编译。SETBIT和RESET是一个宏定义,是为了简化编程用的,它的原型大家可以查看
#define SETBIT(A,B)    (A |= 1 << (B))              /*A=Register, B=Bitnumber (7..0)*/
#define RESETBIT(A,B)  (A &= ~(1 << (B)))         /*A=Register, B=Bitnumber (7..0)*/
#define GETBIT(A,B)       ((A >> B) & 0x01)

5:中断优先级初始化,目前设置的是串口的优先级是最高的,其次是系统时钟Timer2
6:串口初始化,6clock是115200bps,12clcok是57600bps。
7:界面初始化。
8:定时器2初始化,它做为系统时钟,时间间隔是20mS,也可以设置,MS2中特意采用T2的16bit自动重载模式,它虽然受开关中断影响有瞬时误差,但没有积累误差,它是MS2中非常关键的一个,整个系统必须依赖它来运行,这类似于操作系统里的节拍的概念,因为按键检测,软件时钟,软件虚拟定时器,例行程序都靠它来实现的。任何优先级高于T2的中断时间不能大于系统节拍,不然有可能引起系统崩溃,这一点切记。

9:开中断,系统运行。

初始化完后,回到main函数,初始化了四个伪任务,如下
    MSTimerStart(100, test0);
    MSTimerStart(110, test1);
    MSTimerStart(120, test2);
    MSTimerStart(130, test3);

按第一个分析:
参数一是时间,单位为20mS,也就是系统节拍,值为100,就是指100* 20mS = 2S,test0叫回调函数,也就是说这个MSTimerStart注册了一个函数test0,它在2S之后运行。这是本人学手机里面软件定时器的用法做的,非常好用,举个例子:
设置一个闹钟,10S后响,软件如下:
/*闹钟响铃 函数*/
void naozhong(void)
{
    Beep;
}

main()
{

    按键后:
MSTimerStart(10000 / 20, naozhong);

}

也就是说MSTimerStart函数把闹钟这个函数naozhong放到了系统中,注册登记了,到了需要的时间,系统自动的调用这个函数,不需要我们关心了。这个也是系统节拍引入后得到的一个好处。等一下我们分析软件虚拟定时器是怎么做的,先还是谈以上四个函数,第一个函数延迟100个节拍,第二个延时110个节拍,第三个延时120个节拍,第四个延时130个节拍,它们对应的函数是test0,test1,test2,test3。效果就是上面第三张图片,那我们接下来看test0:
void test0(void)
{
    uprintf("liweifeng ");
    MSTimerStart(100, test0);
}
先打印了一句话“liweifeng ”,我记得好像是一个叫李卫峰的过来,给他演示的时候改的。之后又把自己注册到系统中,2S后又重新运行自身,这个叫自我循环,中间的间隔时间自己设置,这有点类似于操作系统中的任务的概念,独立自我循环,但毕竟不是真的任务,所以本人把它叫做伪任务。

接下来分析一下这个软件虚拟定时器是怎么设计的,先简单说一下原理:
设一个数组
static MSTimer idata MSTimerArray[MSTIMER_NUMBER];
类型是MSTimer类型,MSTimer的类型原型如下:
typedef struct 
{
    word  delay;
    void (*CallBack) ( void);
}MSTimer;

也就是说,定义了一个二维数组,其中一个是时间,即多少个节拍,另外一个是函数指针,存放函数指针用的,当然这儿有点技巧,若不懂,先承认它,或者去看看书。

MSTimerStart原型如下:
byte MSTimerStart(word Delay, void ( *CallBack ) ( void ))
{
    byte i;
    for(i = 0; i < MSTIMER_NUMBER; i++)
    {
        if(!GETBIT(MSTimerIDRegister, i))
        {
            SETBIT(MSTimerIDRegister, i);
            MSTimerArray.delay = Delay;
            MSTimerArray.CallBack = CallBack;
            return(i);
        }
    }
    
    ERRprintf("MSStartTimer ");
}
定义了一个静态变量MSTimerIDRegister,它用于标识那个软件定时器被使用了,先查找,若有空的,就在它对应的数组里把两个参数 时间 和 函数指针放到数组里,OK。

再看软件虚拟定时器的服务器,原型如下
void MSTimer_server(void)
{
    byte i = 0;
    byte MSTimerIDRegisterMap;
    MSTimerIDRegisterMap = MSTimerIDRegister;
    
    while(MSTimerIDRegisterMap)
    {
        if((MSTimerIDRegisterMap & 0x01) == 1)
        {
            if(!(--MSTimerArray.delay))
            {
                RESETBIT(MSTimerIDRegister, i);
                (*(MSTimerArray.CallBack))();
            }
        }
        MSTimerIDRegisterMap = MSTimerIDRegisterMap >> 1;
        i++;
    }
}

每次系统节拍到了之后,都会去查询有没有软件定时器在工作,若有,每一路都检查,减去1,之后再检查,若减到为0了,说明时间到了,就去调用被注册的函数,就是上面说到的test0,test1之类的。其实很简单。需要说明的是这个被调函数test0是在系统中断中执行的,所以test0的费用不能太高,在MS3中引入了另外一种被调方式,可以在boot.c的大循环中处理,就没有这个费用问题了。

接下来就是系统的一个核心内容,整个系统关联的关键,消息机制。
    while(TRUE)
    {
        switch(msg_queue_out())                        
        {
            case MSG_KEY:    
                mmi_key_process(g_MsgReturnValue);
                break;
            case MSG_UART:                        
                uart_process();
                break;
            case MSG_TEST:
                //special for test
                break;
            case MSG_NULL:
                break;
            default:
                break;
        }
    }

不停的读取消息,若是MSG_KEY,则处理按键消息,若是MSG_UART,就处理串口程序之类的,反正是什么消息,处理什么事情,就这么简单,那么消息怎么来呢,等一下说,先谈一下消息机制。
消息机制在当前的编程中是最基本的,它是连接两个事件的纽带,可以把一个系统模块化,结构分得很清晰,所以合理的消息机制很关键,以前很多人在大循环体系中采用做标记也算是一种消息。
大凡的消息机制采用指针方式,有头(point),尾(tail),循环的,(MS3采用这种方式)本来也想采用这个,后来想着MS2的对象是初学者,取消了,用了一个比较笨的方法:
1:入消息
void msg_queue_in(MSGTYPE msgType, MSGPOINT msgPoint)    
{
    if(MsgPoolPoint == MSG_STACK_DEPTH)                        
    {
        ERRprintf("msg_queue_in ");
        return;
    }
    bEA = EA;            
    EA = 0;            
    MsgPool[MsgPoolPoint][0] = msgType;
    MsgPool[MsgPoolPoint++][1] = msgPoint;
    EA = bEA;
}
第一个参数是消息类型,第二个是消息值,里面的处理方法一样,也是定义了一个二维数组,把这两个数据存起来,之后开关中断注意点就行了。

2:取消息,有三种,FIFO,FILO,PRIORITY(消息优先级)就讲一种,FIFO的吧,三种消息也是条件编译的,便于用户选择,
byte msg_queue_out(void)
{
    if(MsgPoolPoint == 0)
    {
        return(MSG_NULL);
    }
    bEA = EA;
    EA = 0;                    
    g_MsgReturnValue = MsgPool[0][1];                
    msgType = MsgPool[0][0];
    if(--MsgPoolPoint)            
    {
        for(i = 0; i < MsgPoolPoint; i++)
        {
            MsgPool[0] = MsgPool[i + 1][0];
            MsgPool[1] = MsgPool[i + 1][1];
        }
    }
    EA = bEA;
    return(msgType);
}
就是把消息值保存到MsgReturnValue的共用变量里,之后数组向上拷贝,函数返回消息类型,也很简单,一看就懂。

在单片机里,采用消息还带来一个非常大的好处,有些时候函数套的级数比较深,也就是说函数调函数,调的比较多,尤其是界面之类的编程,那很占用栈的资源,MCU51的资源本来就不多,很容易引起内存不足,我们可以采用消息,把一个很长的调用分成好几块,通过消息把它们连起来,这样就可以降低内存,效果很好。

刚才讲解了boot.c  main函数这个主线,还有一个主线要讲,那就是系统节拍,在system.c里面,初始化后,每20mS MCU自动中断进入
static void Timer2Server(void) interrupt 5 /*不要带指定寄存器,否则将产生移位指令出错*/
{
    ET2 = 0;                            /*close interrupt*/
    TF2 = 0;                            /*clear interrupt flag*/

    if(++RTCCounter == 50)
    {
        RTCCounter = 0;
        rtc_soft_routine();            /*定时器例行程序*/
    }
    
    key_check();                    /*按键检测*/

    if(MSTimerIDRegister > 0)        /*软件虚拟定时器*/
    {
        MSTimer_server();
    }
        
    
    routine_process();                /*运行例行任务程序*/

    ET2 = 1;
}

上面注析很清楚了,软件虚拟定时器已经讲过,接下来讲讲key_check();本人自认为这一块写的不错,看原型
void key_check(void)            /*5*5=25mS*/
{
    byte KeyWord;
    
    if(!g_bKeyEnable)            /*是否开按键*/
        return;
/*采用P1口,4×4扫描,16个按键,先输出高四位为0,读取低四位数据,再输出低四位为0,读取高四位,把读取的两部分合并*/
    P1 = 0x0F;
    KeyWord = P1 & 0x0F;
    P1 = 0xF0;
    KeyWord = KeyWord |( P1 & 0xF0);            
检测按键,若是0xFF,则无按键
    if(KeyWord==0xFF)
    {
        KeyCounter=0;
        if(KeyIntervalSafeguard)    
        {
            KeyIntervalSafeguard--;
        }
        return;
    }
若有按键,静态计数器加一
    KeyCounter++;
若计数器累加到KEY_SHORT_INTERVAL,,抛出一个按键消息
    if(KeyCounter==KEY_SHORT_INTERVAL)
    {    
        if(!KeyIntervalSafeguard)
        {
            msg_queue_in(MSG_KEY,KeyMap(KeyWord));
        }
        KeyIntervalSafeguard=3;
    }

若计数器累加到KEY_ LONG _INTERVAL,,类似于PC按键抛出消息        
    if(KeyCounter==KEY_LONG_INTERVAL)
    {
        msg_queue_in(MSG_KEY,KeyMap(KeyWord));
        KeyCounter-=4;
    }
}

说明:一般的按键处理采用中断,检测到一个按键后采用delay函数延时20mS消抖动后再读取,这样做的一个坏处是白白浪费了系统20mS,MS2采用系统节拍来处理,它的间隔就是20mS,采用静态计数器,当有按键的时候,累加一次,达到要求就抛出消息,这样把按键处理独立化了,并且采用P1口,按键很容易达到16个,上面的代码跟MS2中有点区别,MS2中因为需要超过16个按键,所以增加了一个GPIO口T0,分析的时候就去掉了。这样的按键处理,在没有按键的时候也没增加多少费用,一检测到没有按键就退出了。需要提一下的是KeyIntervalSafeguard,这个可能大家不容易明白,它是为了保证按键的可靠性加入的,有这样一种情况会导致按键误触发,就是按键氧化,接触不良,在100mS内可能导致多次按键事件,这是不允许的,所以加入这个变量,效果非常好。

提到消息机制,我们可以看下面的程序
static void UartInterruptServer(void) interrupt 4
{
    ES = 0;
    RI = 0;

    msg_queue_in(MSG_KEY,SBUF);
    
    ES=1;
}

这一段就是把串口当作按键来处理了,上位机发送一个串口数据过来,当作按键处理,这是消息的灵活处。

好了,该讲的架构都讲完了,还有不清楚的再补充了,MS2相比MS3,一些细节点还没有处理好,在MS3中采用了循环指针的消息机制,并且是16bit,避免一些临界态问题,软件定时器增加了一个模式,可以在大循环里处理,还有就是软硬件分开了,把硬件独立出来了。
看完MS2,请看MS3,技巧性的稍多一些,学了一些uCOS的概念。

SI使用说明在MS2.rar内有包含。

请各位多提一些意见,便于本人的提高,当然不要怕争论,有争论才有民主,有争论才有真理!



使用特权

评论回复
8
ocon| | 2007-6-19 20:09 | 只看该作者

写得很好。

学习

使用特权

评论回复
9
lldwsw|  楼主 | 2007-6-19 20:17 | 只看该作者

花了整整一下午。

明后两天本人出差,可能不能及时回复,请原谅

使用特权

评论回复
10
yewuyi| | 2007-6-19 20:27 | 只看该作者

为什么非要用那个编辑器?

俺认为用IAR,MPLAB,KEIL等自带的编辑器就可以了,何必非这个编辑器?

如果程序写得自己都找不到的话,我看给他什么编辑器都是没用的把。

使用特权

评论回复
11
yewuyi| | 2007-6-19 20:29 | 只看该作者

另外一个问题,你那东西俺怎么下载不了?

呵呵,不妨发给俺瞻仰瞻仰……


不管东西好不好,肯与大家共享自己的知识总是要被表扬一下的拉……

使用特权

评论回复
12
fsaok| | 2007-6-19 20:34 | 只看该作者

.

一句话,高!佩服

使用特权

评论回复
13
su_tech| | 2007-6-19 20:40 | 只看该作者

楼主辛苦了,来杯清茶

楼主辛苦了,就是不知道MS3是什么?链接有错

使用特权

评论回复
14
lldwsw|  楼主 | 2007-6-19 20:42 | 只看该作者

回yewuyi

我以前也跟你一样,直接在keil下编程,后来用了SI后感觉差太多了,还有最关键的,不能老想着自己做的东西,更多的时候是看懂别人的代码,移植到自己的程序中,或者直接在别人的代码上再次开发,现在不能什么东西都是自己从头做起的,看别人的代码也是非常重要的。当然就51来说,keil等或许够了,毕竟代码量不大,但就算如此还是SI方便,用过SI的人,基本上没有再用keil的编辑器了。还有一个大家画PCB大部分都用protel,但用过powerpcb的,很少回去用protel了,目前深圳这边手机公司基本上都用powerpcb,反正一句话,能有好的东西,尽量用,VC下有一个插件叫Visual Assist,应用很广泛,但也有很多人不知道。个人喜好吧

思维是需要扩展的,要吃着碗里,看着锅里,不能老原地踏步。

下载不了是21ic的问题,我是用LeechFTP上的,也可以下。

使用特权

评论回复
15
su_tech| | 2007-6-19 20:42 | 只看该作者

呵呵,跟贴的不少!再顶

使用特权

评论回复
16
hq_y| | 2007-6-19 20:44 | 只看该作者

能下载啊,使用flashget很轻松的下载了

使用特权

评论回复
17
lldwsw|  楼主 | 2007-6-19 20:44 | 只看该作者

ms2,ms3只是名字

ms2的下载地址为:
http://file.21ic.com/MCU/实用的单片机系统第二版.rar
ms3的下载地址为:
http://file.21ic.comMCU实用单片机系统第三版(带SI).rar 

这是本人的疏忽,谢谢指正

使用特权

评论回复
18
su_tech| | 2007-6-19 20:47 | 只看该作者

呵呵,客气!学习中

呵呵,客气!MS3好像还是下不了!

使用特权

评论回复
19
lldwsw|  楼主 | 2007-6-19 20:51 | 只看该作者

好像地址的反斜杠不对

http://file.21ic.com/MCU/实用单片机系统第三版(带SI).rar

MCU前后的反斜杠不对,上面的地址正确

使用特权

评论回复
20
孤独泪| | 2007-6-19 21:43 | 只看该作者

定,学习了

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

96

主题

859

帖子

50

粉丝