OO思想已经出现了20多年,各种成功的实践告诉我们它是软件开发的必然趋势。面向对象编程(OOP)的一个关键原则之一就是封装(encapsulation),把暴露的数据封装起来,尽可能的让对象管理它们自己的状态,因为过多的依存性会造就紧耦合(highly coupled)系统,使得任何一点小小改动都可能造成许多无法预料的结果。而数据隐藏/封装机制是一个控制对象数据和状态强而有力的方法,它对外部世界隐藏其内部细节,这就意味着每一个对象都应该尽可能少的了解系统的其他部分或者被其他部分所了解,这样一来一旦发生了变化,需要了解这一个变化的对象会比较少,因此变化也就相对容易地进行。 C++之父Bjarne Stroustrup也曾说过:“你使用一个语言特征是因为你需要它,而不是因为它存在”。设计ucClass(uCosII封装类)的初衷是由于过去很多的实现都是在C++中完成,深切感受到面向对象分析、设计乃至编程的独特魅力。回想微软的MFC1为win32程序设计所带来的便利、DriverWork2在PC硬件的驱动开发的高效,促使完成ucClass的设计(这里头没有什么高深的学问),其实更重要的一点是它能改进现有的设计模式,能让今后的uCosII相关开发及维护变得更加轻松和高效! 对于OO我也就几年的开发经验,没有做过什么“巨项”,对于ARM也是接触时间不长,不过有一个概念一直影响着我,“ARM不就是复杂一点的单片机吗”,我想也不应该难到那里去。于是有空就玩玩,对于很多做这行做开发的对C++这样的面向对象编程语言大多不甚了解,要想用OO思想来武装自己又谈何容易啊,这就是一种挑战习惯、挑战思维方式的行为,等于放弃过去曾经为你有效解决难题完成工作的开发模式。在此我也没有什么灵丹妙药可改变这些处境,唯一的建议就是坚持及不断实践、只有这样的思想基础才能提高成功的机会。 下面简单设计一个ucClass的demo程序(当然会可能隐藏了一些bug or error),目标板使用的是菲利普LPC2100芯片(周立功的EasyARM 2100开发板),有些头文件这里就不贴了,旨在看看主程序的结构及设计过程,就让一些软件的大虾们见笑了。 若有机会的我还准备写几个基于OO之上的在嵌入式开发的应用demo…… 设计需求: 使用ucClass设计一个demo程序完成下列任务: 任务编号 任务指责和功能描述 物理资源占用 1 每间隔1秒蜂鸣器短促发声两次,每10秒间隔使用信号量通知任务2,随后自身进入挂起等待状态。 蜂鸣器及IO端口 2 无限等待由任务1触发的信号量,当有信号时引发1秒蜂鸣器长鸣,其后恢复任务1,自身再次进入信号量的无限等待状态中。 蜂鸣器及IO端口 3 孤立任务,简单的一秒间隔led1闪烁。 Led1及IO端口 4 孤立任务,通过检查key1按键状态控制led4的二值状态(亮/灭)。 key1,led4及IO端口 // main.cpp文件,ucClass测试主程序 // exdata 2004-8-18 #include "config.h" // 一些硬件相关的声明 #include "GPIO.H" // GPIO类头文件 #include "ucClass.H" // uCosII的收集类库头文件 /* // 在浏览demo程序时要注意以下几点: // 1,带参数的对象构造 // 2,静态函数调用 // 3,带参数的派生类构造 // 4,虚拟函数重载 // 5,特别注意一些头文件(不在此文档中)包含写法及原有C函数声明方法,关注extern "C" // 6,不拘泥于传统的设计思想(模式) // 7,比较不使用class时候又是如何处理的?分析每个对象背后隐藏了什么? */ /************************** 全局变量 *****************************/ // 定义几个task对象,这是实例化对象,隐藏并实现了任务栈的构造 CTask t1(128); // 参数是任务堆栈分配大小(在ARM中以32bit为单位) CTask t2(64); CTask t3(64); CSem t2WaitEvent; // 定义一个信号量对象用于任务t1、t2通信 CGPIO buzz("P0.7",1); // 定义一个GPIO对象,输出模式,用于控制蜂鸣器 /* // 前置声明几个任务运行函数,这个与C模式下没有什么区别。 // 还保留这种处理方法仅仅为求兼顾一些使用uCosII习惯,建议参考CMyTask的 // 设计及t4的定义,使用子类设计实现具体任务的处理方法要比C模式的的全局 // 孤立启动函数更具有设计上及维护上的优点,当做一个中大型项目时候效果尤 // 为明显。 */ void t1Run(void *p); void t2Run(void *p); void t3Run(void *p); /*******************************************************************/ /* // t1任务运行函数。 // 功能:每间隔1秒蜂鸣器短促发声两次,每10秒间隔使用信号量t2WaitEvent通 // 知任务t2,随后自身进入挂起等待状态。 */ void t1Run(void *p) { TargetInit(); // 初始化硬件目标板 INT32U i = 0; while(1) { CuCos::TimeDlyHMSM(0,0,1,0); if(++i == 10) { i = 0; t2WaitEvent.Post(); t1.Suspend(); } // 蜂鸣器短促发声两次 buzz.Clear(); CuCos::TimeDlyHMSM(0,0,0,50); buzz.Set(); CuCos::TimeDlyHMSM(0,0,0,50); buzz.Clear(); CuCos::TimeDlyHMSM(0,0,0,50); buzz.Set(); } p = p; } /* // t2任务运行函数。 // 功能:无限等待信号量t2WaitEvent,当有信号时引发1秒蜂鸣器长鸣,其后恢 // 复t1任务,任务t2再次自身进入信号量t2WaitEvent的无限等待状态。 */ void t2Run(void *p) { INT8U err = 0; while(1) { t2WaitEvent.Pend(0,&err); buzz.Clear(); CuCos::TimeDlyHMSM(0,0,1,0); buzz.Set(); CuCos::TimeDlyHMSM(0,0,0,50); t1.Resume(); } p = p; } /* // t3任务运行函数。 // 功能:简单的一秒间隔led1闪烁。 */ void t3Run(void *p) { static CGPIO led1("P0.22",1); while(1) { led1.Set(); CuCos::TimeDlyHMSM(0,0,1,0); led1.Clear(); CuCos::TimeDlyHMSM(0,0,1,0); } p = p; } /* // 为求原汁原味体现一个task对象的封装,下面设计一个子类实现一个特定任务 // 的处理,在此我们可以认为一个任务就是一个子系统,通过一个子类表现一个 // 特有任务的具体属性和行为,注意这种设计方法与传统的面向对象编程模式是 // 有区别的。子系统是一个有自主能力主体,例如在MyTask子系统中我们有两 // 个IO资源m_key1和m_led4分别对应着电路板上的按键Key1和发光二极管Led4, // 通过检查key1按键状态控制led4的二值状态(亮/灭)。重载基类了Run函数为 // 求体现类的封装特性及表现一个任务的内聚能力,它是一个强制的运行接口, // 当任务启动时便能自动地从重载的Run函数开始运行(具体调用的中转过程在 // 基类中已经实现)。 */ class CMyTask : public CTask // 注意这里的派生关系!!! { public: CMyTask(INT32U StkSize) : CTask(StkSize),m_key1("P0.16",0),m_led4("P0.25",1,0) { // 注意带参数的构造处理方法!!! } protected: virtual void Run(void *p); // 需要重载Run()函数实现任务执行 private: // 这是任务用到的一些资源定义,当然添加子系统的其他的属性和方法也是允许的 CGPIO m_key1; CGPIO m_led4; }; /* // t4任务运行函数。 // 通过函数重载实现重写Run函数,该函数也就是任务运行函数。 // 功能:通过检查key1按键状态控制led4的二值状态(亮/灭)。 */ void CMyTask::Run(void *p) // Run函数将会被uCosII系统自动运行! { while(1) { if(m_key1.GetStatus() == 0) // 判断按键是否按下 { if(m_led4.GetStatus() == 0) { m_led4.Set(); } else { m_led4.Clear(); } while(m_key1.GetStatus() == 0) { CuCos::TimeDly(20); // 等待按键释放 } } else { CuCos::TimeDly(10); } } p = p; } /* // 注意一般应该把上面的CMyTask定义及实现另行写在其他的文件中!把它写在 // 这里仅仅为求说明方便。 */ /*-------------------------------------------------------------------------------------------------*/ CMyTask t4(128); //注意任务对象t4是由CMyTask实例化的对象 // 看看这个简洁的main函数,这时候您想到了什么? int main() { CuCos::Init(); // uCosII的系统函数,用于初始化uCosII,等效于OSInit() t2WaitEvent.Create(); // 创建t2WaitEvent的信号量 // 创建任务(参数指定任务函数启动地址、任务函数参数和优先级) t1.Create(t1Run,NULL,1); t2.Create(t2Run,NULL,2); t3.Create(t3Run,NULL,3); t4.Create(NULL,4); // 创建任务,注意Create函数的重载 CuCos::Start(); // 等效于OSStart() return 0; }
|