实用单片机系统MS3.21程序分析

王绍伟

实用单片机系统是基于MCU8051硬件平台下开发的一款操作平台,它不是一个操作系统,而是一
个操作平台,主要借鉴了操作系统、手机的一些概念,比如消息机制、系统时钟、软件定时器、
平台等概念。
实用单片机系统的核心理念是:在一个标准化的硬件基础上(如8051,avr,arm等)扩展一个
标准化的软件平台,把常规项目常用的一些功能如串口通讯、串口调试、系统定时器、软件定
时器、按键界面处理等通过消息机制组织起来,形成一个完整的系统。当一个特定的项目需要
增加或者删除一项具体的功能时,只需要在平台上增加或者去掉相应的功能即可,这样项目不
需要每次重新构思架构,也不需要从零开始,并且原有的系统通过各个项目沉淀后,更加稳定
可靠,这就是平台的概念,它不是各个子函数的集合。
相对于现在的很多人把RTOS操作系统应用于MCU来说,往往只为了实现任务的调度转换而不考虑
功能的实用、易用性,此外因其较高的资源占用性导致其不适合在MCU类低资源的嵌入式平台应
用,MS系统相对于这些RTOS来说,首先还是保留了编程者的常规前后台思维,但又加了一些
RTOS的优点,如软件定时器实现的时间片任务系统,类似RTOS的任务,其次为编程者实现了整
个程序的框架和一些常用的函数及接口功能如按键、串口、时钟等,让编程者把精力放在跟项
目相关的地方,甚至不需要关心所用MCU的寄存器配置,再次就是代码非常简单,容易学习,尤
其是建议大家采用SourceInsight查看程序,远比keil编辑器的功能强,它是C语言下最好的编
辑器。而MS3.21版本,建议大家直接在Keil的软件仿真器下运行学习。
MS资料可以从以下网址下载:http://www.study-bbs.com/thread-46471-1-1.html
读者有什么疑问也可以在这个版面提问,作者将尽力解释。目前MS3.21版本增加了一个GUI操作框架,
相比目前已有的GUI更加简单易懂,利用一个函数指针代替了复杂的状态机,每一个界面由一个界
面建立函数和一个执行函数构成即可。
若有不足及错误之处,请读者指出,便于作者改进。作者联系QQ为:26033613
程序说明

一、 架构



文件主要分为三部分组成,
第一部分是宏定义,常用头文件,类型、寄存器定义部分,
common.h为全局的头文件,包括所有用到的头文件
type.h为类型定义文件,把如unsignes char 定义为U8,简化写作
REG52x2.h为MCU芯片的寄存器定义头文件
第二部分为硬件相关部分文件
mcu.c和mcu.h为MCU相关的函数,如初始化,中断入口等
mcu_ext.c和mcu_ext.h为MCU扩展外设的处理函数,如按键处理,LED灯显示,数码管等
第三部分就是剩下的,纯软件,可以脱离平台,便于移植(本部分不提xx.h)
Boot.c为程序的入口文件,里面的main为入口函数
Flash.c为flash检测、校验、初始化值部分,现在基本上没用上
Key.c为按键处理部分,本按键处理的方法比较特殊,主要利用系统时钟的节拍来实现消抖动,效果
好并且费用低,独立性强并且不影响主循环
Message.c为消息函数,消息机制的运行就靠它
Mmi.c为GUI文件,mmi为人机接口意思
Putchar.c本为系统文件,printf即调用此文件函数实现串口打印,但系统函数跟MS冲突,特修改兼
容.
Routine.c为系统时钟轮训时需要例行的一些函数,如软件RTC,按键等扫描
Rtc.c为实时时钟的缩写,本rtc是由MCU时钟分频后获得比较准确的时间,但不如专用芯片采用
32.768K的时钟来的精准,长时间使用会有一定误差
Timer.c为软件定时器,这个是MS的一个特色,尤其是利用软件定时器实现时间片任务,类似于RTOS
的任务,创易的LCM触摸彩屏评估板就是采用软件定时器处理触摸采集显示,简单易用,效果非常好.
Trace.c用于调试,目前单片机开发基本上不再用仿真器,那么调试手段非常重要,尤其是串口打印
信息这种trace方式最重要,这个主要用trace.h部分
Uart.c用于串口通讯及串口调试(trace).
Utility.c常用工具函数

二、 入口



Main函数在boot.c内,boot.c除了main函数外,就是系统初始化函数init_process,具体请看注释,
核心就是不停获取消息,处理对应的消息。

三、 系统时钟



系统时钟采用定时器二的16bit自动重入模式,时间间隔为5mS。


系统时钟的中断函数,5mS调用一次system_process例行程序。


说明如图,软件定时器例行程序及系统例行扫描程序都在系统时钟中执行。


系统扫描程序首先把系统时钟分为5拍,每一拍间隔为25mS,第一拍case 0执行按键扫描,LED灯显
示及软件时钟的例行程序,case 1~case 4都是执行数码管显示。需要说明一下的是,这部分硬件当
时选择的是Zlg的DP51评估板,8颗LED灯(等价于一个数码管)与4个数码管占用了P1口的P1.0~P1.4
脚位,作为片选,按键片选为P1.5,之后P1.6和P1.7利用CPLD内部编程组成8位移位寄存器,通过
5mS时分复用实现这些功能,具体硬件参考硬件电路图




四、 消息机制

Main函数中通过MSG_GET_MSG(&Msg);获取消息,Msg是一个U16类型的消息变量,可以理解为一
个一维二元数组:


其中((U8 *)(&u16))[0] 为消息类型,((U8 *)(&u16))[1]为消息参数,用这种宏定义的一个最
大的好处就是节约资源的同时,意思表达清楚,建议大家多用宏定义,这个算是一个技巧。之
后switch关键字对MSG_TYPE(Msg)分类处理即可。


消息机制还包括消息入函数,主要由
#define MSG_SEND_MSG(MsgType, Val) msg_send_msg(MsgType, Val)
#define MSG_SEND_DATA(u16) msg_put_in(u16)
两个构成,其中第一个为消息类型和消息参数两个单字节组成,第二个为直接的双字节作为一个参
数,没有消息类型,第二个主要用于软件定时器的函数中断外执行。把函数指针变成U16类型作为参
数的一个中转。
消息函数的原型只要是三个:


这个只是简单的队列的进和出,比较简单,但各个模块之间,都是基于消息来实现互通的,比如按
键等。建议中断事件采用保存数据之后,抛出消息来处理。消息机制是MS系统的一个核心,请读者
多加理解,比如当函数嵌套,多重函数调用比较深的时候,会占用很多的RAM资源,有一种比较好的
办法,就是在多重调用的中间结束并抛出一个消息,把剩下的部分再通过消息再进入,这样可以节
省资源。作者有一次在做一个CH375 U盘功能的时候,因为厂家给的是一个lib文件,本身U盘功能消
耗了大量内存,但这个U盘存储操作是在一个菜单的最里面层处理,这个时候作者在相应的菜单里面
抛出一个消息,之后把U盘存储函数接口放在消息处理那边,很好的解决了RAM资源不足的问题。

目前设定的消息队列深度为4,这个在message.h里面改:#define MSG_ARRAY_SIZE 4
假如消息比较多,或者出现的频率较高,需要把队列深度加大,但不建议设的太大,不然占用资源
太多,具体值由测试决定。

五、 按键处理

按键部分是MS的一个特色,主要利用两个节拍之间25mS的间隔当作消抖动时间,此外它是在系
统节拍中处理的,跟main主循环无关,可以认为完全独立,不影响主循环。按键处理提供了两
种按键模式,一种是类似PC机,当长按的时候,一直会有按键产生,还有一种类似工业机,长
按的时候是另外一种按键值。这个主要由条件编译决定,读者自己选择。
按键部分代码如下,标注比较清楚,需要强调的是,很多按键因为质量不好,触点容易氧化,
当一次按下的时候,会有好几次的按键过程,也就是说,会有导通,不导通。再导通的过程,
这个就相当于产生了两次按键,为了避免这个问题,专门引入了一个变量
KeySafeguardInterval,当检测获得了按键之后,把它设上一定的值作为门槛,这样保证一个
按键产生后,在它的一定时间内,是不允许产生第二个按键的,这样很好避免了触点氧化引起
的多重按键问题,实际应用效果良好。
一般按键的硬件获取,在资源充分的情况下,作者一般喜欢用P1口的8个GPIO,4*4交叉扫描。
P1口最常用按键获取


按键处理部分


六、 软件定时器及时间片任务系统

作者写软件定时器起源于手机系统,因为手机需要大量的定时器,比如闹钟,定时,动画,按
键音时间等等。而MCU一个定时器用于串口,这样最多也只有两个,不适合做动画等,于是考虑
基于系统时钟扩展出软件定时器。


软件定时器初始化有三个参数,第一个是执行模式参数,类型如下:


MSTIMERMODE_MSG为主循环中执行,当回调函数运行时间较长,一定要放到主循环中执行,软件定时
器先抛出一个软件定时器消息,之后把回调函数的函数指针作为消息抛到消息队列中,主循环获取
消息后,再获取函数指针,执行。


MSTIMERMODE_INT在系统节拍中执行,等价于在中断中执行,这个时候,回调函数运行时间一定要短,
不然影响了节拍,严重导致系统混乱。
第二个参数是延时时间,以系统节拍为基数,比如系统节拍是5mS,假如延时值为100,则延时500mS。
第三个参数为回调函数名,也就是时间后到需要调用的函数的指针,当时间超时时,执行回调函数。
软件定时器的服务例行程序如下:


软件定时器还派生出一个妙用就是时间片任务系统:


首先用宏定义把软件定时器定义为SCH_Add_Task,说穿了就是重新起了一个名字,便于理解记忆。之
后若需要把一个函数改成时间片任务,只需要在函数底部加入SCH_Add_Task函数即可,注意回调函
数名字是这个函数自身,其他两个参数跟软件定时器意义一样。


之后在需要执行的地方执行Task即可。


系统上电之后就运行Task任务

七、 界面

界面人机交互(MMI)是MS3.21版本才加的,主要之前一直没有合适的MMI架构,主要MCU不适合
太多的结构体,也不应该用过于复杂的状态机,在这个基础下,采用了一个函数指针作为界面
执行函数的入口来做,获得不错的效果。
在mmi.c里定义了一个函数指针变量 Function idata fMMIProc;把按键要执行的函数赋值给
fMMIProc。


当有按键消息的时候,KeyStore保存按键值并作为全局变量传递,之后执行fMMIProc函数。
我们设定了每一个界面都有一个建立界面函数及该界面下的按键处理函数,如下:


Setup函数主要完成界面的显示及一些状态的设定,proc函数主要完成该界面下的一些响应,主要是按键处理。


上图中,setup建立了idle状态下的界面,proc对按键进行相应,当选择不同的按键,执行对应的界
面菜单的setup函数,同时把proc函数赋值给fMMIProc这个按键入口函数指针变量。这样就可以灵活
组织,也不需要界面的状态机。
对于Idle界面来说,它的初始化是在mmi_init中完成的。


还有一种常用的,比如在某一个界面下的数值显示,以时间显示为例,除了setup及proc外,需要专
门一个显示函数


需要引入一个开关量bTimer,先判断是否为TRUE,若为TRUE,就继续执行,先清屏,之后显示时间,
其中Key_BackSpace是0x08,删除按键,主要MS在Keil的软件模拟器中运行,利用串口打印作为显
示界面,利用删除按键作为清屏功能,便于时间在原位置显示。
其他两个时间函数如下:


Setup及proc都需要对bTimer这个开关量赋值。当然,display_time_routine需要跟采集部分连接起
来,MS中时间是从RTC消息中出来的:


八、 调试及Keil模拟

MS定义了自己的串口调试显示函数,主要用于调试及串口通讯上


请尽量使用以上函数,虽然printf函数功能强大,但消耗RAM和ROM也比较多。在调试期间资源多的
时候,用printf函数比较方便,但注意printf函数的参数是U16类型的,不是U8类型,这个特别强
调。

Uprintf常用于打印字符串,不带参数变量,带参数变量的建议用printf
Uputbyte直接打印数据,原值输出
Uputchar打印asc码,比如0x31,输出是0x33,0x31,0x20,等价于3、1、空格
Uputarray(A,B)打印一个数组A,长度由B决定
MS调试学习建议采用直接在Keil的软件模拟器下运行,先需要设置,如下:


其次在Keil菜单下点击Debug选择Start/Stop Debug Session进入软件模拟器下:




先点运行,之后双击串口显示,Keil对汉字支持不好,有些时候出现乱码,可以通过双击串口显示
刷新解决。


输入按键同样也采用串口调试窗口,把串口接收到的数据作为按键消息处理即可:


重新编译的时候,必须要退出运行,退出Debug模式才行。

九、 作者自述

2001年毕业在中科院半导体所工作的时候,指导老师写了很多子函数,但都比较孤立,那个时
候作者做过单片机,汇编的,没用过C,当时就想,应该基于C语言下写一个MCU的平台,这样避
免每次重复写基础函数,也避免每次从零开始构思,很花时间。那个时候就有一些平台的概念
,但还很模糊。
2002年进入深圳一家手机公司,自感不会软件,只有硬件基础不足以长远发展,选择了驱动工
程师这个职位,基于Infineon手机平台开发手机,算是第一批手机开发人员,甚至可以说第一
款国内量产的彩屏手机就是作者调试出来的,因为当时这家公司是最早的一家手机设计公司。
因为有较强的硬件基础,在熟悉半个多月手机平台后,就可以开始调试手机驱动。因工作效率
较高,有比较充裕的时间让我把在所里的想法实现。当时国内单片机评估板较少,Zlg那个时候
推出一款DP51开发板,比较早的,性价比比较好,采购按例子学习了几天后觉得还是一个个子
函数的,没有太强的实用价值,那个时候因为接触了手机编程好几个月了,被手机平台的一些
概念吸引,尤其是系统时钟,软件定时器,平台这三个概念,于是就在这三个概念的基础上,
加上自己学过的VB编程里面的事件触发消息机制,这四个融合起来,就成了第一版的实用单片
机系统,当时的英文名字叫McuSystem,之后强化了串口调试功能。
McuSystem的一个很重要的目的,就是学习手机开发经验,尤其是平台这个概念,很多人不理解什么
叫平台,平台就是为了快速开发一个或几个功能而在一个成熟的大框架下开发,这个框架提供了尽
可能多的信息,工具,例子等,比如在手机平台上开发一个功能,甚至不需要看芯片的寄存器配置,
基本上跟硬件相关的,都封装打包提供了一系列的api函数,目的只有一个,快速出产品,出项目。
McuSystem出来后,基于它做了几个项目,客户的感觉也不错,很容易学会,建议把它推广开,
但当时的C语言功底还是比较糟糕,但还是这样公布了,在21ic上,令人惊喜的是,21ic收录了
它,这让我有进一步完善的动力。
McuSystem的第二版完成于2005年6月,那个时候C语言功底加强了,代码也简洁了很多,去掉了
第一版本系统相关性不强的东西。MS2因为简洁和简单,是一个比较成功的版本,特别适合初学
者学习。
McuSystem的第三版完成于2007年3月,这个时候对宏定义和指针应用比较成熟,使用了一些技
巧,适合中级单片机人员学习。第三版在2007年21ic侃单片机版竞选版主时发布,有比较激烈
的讨论。
之后,McuSystem就一直没有改进直到今天,因很多读者认为应该继续推进并加大宣传,特增加
了GUI部分,这个GUI无论好坏,起码是自己的心血积累,还是一如既往的,追求简单易用为根
本目的。
关于推广,因为精力有限,一直没有亲自写过文档,但看到读者对自己劳动成功的承认,感到
必须要亲自写一下文档,无文则不能行远,今年春节期间,专门花精力把MS3重新整理了一下,
写上了注析,同时写了这篇文档,虽然花了不少时间,但希望一劳永逸。
从MS3完成后,作者一直处于创业期,过的比较艰辛,但回报也不少,2008年3月因为一个实用
的电阻电容电感样品本推广的想法,创立了一个淘宝店,叫创易电子(http://52edk.taobao.com
,主要产品就是电阻、电容、电感样品本,把各个系列的电阻电
容电感整理成样品册,通过淘宝销售给内地的中小企业、高校、研究所研发调试用,让他们免
除采购样品的痛苦,这个简单、实用的产品,广泛获得客户的好评。
2008年12月想着让搞MCU51的人也能够自己DIY手机的想法,开发了第一款手机开发评估板,被
很多高校采购用于教学及毕业生就业培训,此外很多中小企业采用它开发项目,我们也基于它
开发了很多产品,最为出名的是2010年上海世博会的手持式门票检测,并上了新闻联播,此外
应客户要求做成的手机核心板,因为具有显示、声音、GPRS网络及JAVA开发等等功能,被认为
是第三代单片机,只要扩展RFID、条码、红外采集、GPS等就变成了各种应用的PDA,广泛应用
于公交售票,快递物流,水电抄表,追踪定位方面,这一块业务是作者当前的主要业务而为之
奋斗,因为这个手机开发评估板,独立成立了一家公司叫华禹物控(www.huayusoft.com)。
因为手机开发评估板客户群体较大,成立了一个500人的QQ群(15762255)及论坛
www.study-bbs.com),但QQ群的人数限制导致客户的流失,基于此,我们设计了一个MiQ,
类似QQ,这个MiQ可以跟我们的QQ群对接,并且还可以跟我们的主页和论坛对接,也可以跟别的
QQ群及论坛、主页连接,这样就可以实现上万人的MiQ群,彻底解决QQ群人数的限制问题,MiQ
群实现独立的运作,独立的生态系统,包括市场,人才,交流等。当人数超过数千人,就形成
了一个门户效应,此后华禹物控的所有产品都跟MiQ对接建立后台数据库,实现数据业务,同时
联合华控的客户一起为实现物联网打下基础。
其实所有的一切,都是基于简单、易用、实用角度出发,如何让我们的客户用的更简单,更实
用,那么我们就能成功。
作者:王绍伟
2011年2月15日
衷心感谢支持我的网友们!
302 Found

302 Found


nginx/1.8.0