嵌入式操作系统是一个纯软件系统,作为嵌入式系统的软件层,对于想从事嵌入式开发的人员来说,是必须弄清楚的。然而,对于像我这种不是计算机专业背景的开发人员来说,并且一直都在玩51单片机,总想自己写一个简单的操作系统,就算不能够实现强大的功能,但基本的功能还是可以实现的。想借此学习过程对操作系统有一个大体的了解。想想快毕业了,想利用最近的1个月时间多学点知识,我参考的书籍是:北航出版社出版的陈旭武编著的《轻松自编小型嵌入式操作系统》,感觉这本书不错,适合入门,推荐一下。下面是我的学习笔记吧。
一、嵌入式操作系统内核
内核相当于人的大脑,小型嵌入式操作系统的内核一般具有以下基本功能:
(1)任务管理功能
(2)时间管理功能
(3)中断服务处理功能
(4)事件管理功能
(5)内存管理功能
其中任务管理、时间管理、中断服务处理是内核中最基本的,不可裁剪。具有这三个基本功能就可以实现基本的任务。
二、嵌入式操作系统基本功能组建
(1)调度器:就是我们学嵌入式经常说的调度算法了
(2)系统时钟
(3)应用任务
(4)操作系统服务功能的API调用函数集
三、实现一个简单的3任务调度系统
硬件环境:
我的硬件环境是我自己焊的单片机板子,有基本的LED和数码管、蜂鸣器。单片机型号为:STC89C52RD
软件环境:
win7+Keil 51(已破解,不受2Kb代码限制)
下面介绍开始构建步骤:
1.Keil51新建工程就不说了,自己新建一个工程。选定芯片型号后会弹出一个对话框询问要不要在工程中加入启动代码文件,选择否。
2. 新建一个空白文件,保存为后缀为.c的文件,然后添加到该工程中。
3. 下面对工程选项进行设置,玩51的都知道,修改频率,修改为输出hex文件
4.右键main.c文件,选择Option for file ‘main.c’中的属性中勾选Generate Assembler SRC File 和 Assemble SRC File。主要是是编译后生成SRC汇编程序文件,好分析,可以在C文件中程序代码中直接嵌入汇编代码。
5. 根据选择的编译模式,将相应的库文件加入到工程中,库文件在keil安装目录\C51\LIB\C51S.lib
这里就不说具体的代码怎么编写了,代码中有注释。也可以去参考我推荐的这本书。直接上代码:
main.c
/****************************************************************************** ** File Name: main.c ** Discreption: ** Date: 2013/5/17 16:30 ** Author: Li Xiaoming (Juven) ** Blog: http://www.cnblogs.com/lixiaoming90 ******************************************************************************/ #include <reg52.h> typedef unsigned char uint8; typedef unsigned int uint16; sbit led_red = P0^0; sbit led_green = P0^1; sbit buzzer = P2^6; uint8 state; /* 存放系统当前正在运行的任务代号 */ uint8 Task0Stk[10]; /* 定义任务0的任务堆栈区 */ uint8 Task1Stk[10]; /* 定义任务1的任务堆栈区 */ uint8 Task2Stk[10]; /* 定义任务2的任务堆栈区 */ uint8 StkTop[3]; /* 保存任务堆栈区的栈顶地址 */ void Task0(); /* 任务0声明 */ void Task1(); /* 任务1声明 */ void Task2(); /* 任务2声明 */ void Task0() { while(1){ led_red = 0; } } void Task1() { while(1){ led_green = 0; } } void Task2() { while(1){ buzzer = 0; } } //============================================================================= //函数:main() //描述:系统初始化操作 (1)任务函数入口地址存放在任务栈区 // (2)保存任务栈顶地址 // (3)SP取的任务栈顶地址,通过main()返回作用使PC获得任务 // 任务入口地址,任务开始运行 //参数:无 //返回:无 //日期:2013/5/17 16:18 //============================================================================= void main() { //任务函数入口地址存放在栈区中 Task0Stk[1] = (uint16)Task0; /* 低8位 */ Task0Stk[2] = (uint16)Task0 >> 8; /* 高8位 */ Task1Stk[1] = (uint16)Task1; /* 低8位 */ Task1Stk[2] = (uint16)Task1 >> 8; /* 高8位 */ Task2Stk[1] = (uint16)Task2; /* 低8位 */ Task2Stk[2] = (uint16)Task2 >> 8; /* 高8位 */ //保存任务栈区的栈顶地址 StkTop[0] = Task0Stk; /* 取得任务0栈区的首地址 */ StkTop[0] += 2; StkTop[1] = Task1Stk; /* 取得任务1栈区的首地址 */ StkTop[1] += 2; StkTop[2] = Task2Stk; /* 取得任务2栈区的首地址 */ StkTop[2] += 2; //启动任务0 SP = StkTop[1]; }
将改程序编译后下载到单片机上,运行,灯亮,目标实现!!!!!
通过简单的学习,终于对操作系统有点了解了,这只是学习的开始,下面进行自己设计任务调度器.
下面是多加了任务调度器和延时函数,可以看到LED灯轮流点亮,蜂鸣器按一定频率响。源代码如下:
/****************************************************************************** ** File Name: main.c ** Discreption: ** Date: 2013/5/17 16:30 ** Author: Li Xiaoming (Juven) ** Blog: http://www.cnblogs.com/lixiaoming90 ******************************************************************************/ #include <reg52.h> typedef unsigned char uint8; typedef unsigned int uint16; sbit led_red = P1^0; sbit led_green = P1^1; sbit buzzer = P2^1; uint8 state; /* 存放系统当前正在运行的任务代号 */ uint8 Task0Stk[10]; /* 定义任务0的任务堆栈区 */ uint8 Task1Stk[10]; /* 定义任务1的任务堆栈区 */ uint8 Task2Stk[10]; /* 定义任务2的任务堆栈区 */ uint8 StkTop[3]; /* 保存任务堆栈区的栈顶地址 */ void Task0(); /* 任务0声明 */ void Task1(); /* 任务1声明 */ void Task2(); /* 任务2声明 */ void Scheduler(uint8 N); /* 任务调度器 */ void DelayNms(uint8 z); /* 毫秒级延时函数 */ void Task0() { while(1){ led_red = ~led_red; DelayNms(1000); Scheduler(1); } } void Task1() { while(1){ led_green = ~led_green; DelayNms(1000); Scheduler(2); } } void Task2() { while(1){ buzzer = ~buzzer; DelayNms(1000); Scheduler(0); } } //============================================================================= //函数:main() //描述:系统初始化操作 (1)任务函数入口地址存放在任务栈区 // (2)保存任务栈顶地址 // (3)SP取的任务栈顶地址,通过main()返回作用使PC获得任务 // 任务入口地址,任务开始运行 //参数:无 //返回:无 //日期:2013/5/17 16:18 //============================================================================= void main() { //任务函数入口地址存放在栈区中 Task0Stk[1] = (uint16)Task0; /* 低8位 */ Task0Stk[2] = (uint16)Task0 >> 8; /* 高8位 */ Task1Stk[1] = (uint16)Task1; /* 低8位 */ Task1Stk[2] = (uint16)Task1 >> 8; /* 高8位 */ Task2Stk[1] = (uint16)Task2; /* 低8位 */ Task2Stk[2] = (uint16)Task2 >> 8; /* 高8位 */ //保存任务栈区的栈顶地址 StkTop[0] = Task0Stk; /* 取得任务0栈区的首地址 */ StkTop[0] += 2; StkTop[1] = Task1Stk; /* 取得任务1栈区的首地址 */ StkTop[1] += 2; StkTop[2] = Task2Stk; /* 取得任务2栈区的首地址 */ StkTop[2] += 2; //启动任务0 SP = StkTop[0]; } //============================================================================= //函数:Scheduler //描述:调度器 (1)保存当前任务的栈顶地址 // (2)把要运行的任务号赋给state变量 // (3)堆栈指针取的要运行的任务的栈顶地址 //参数:要运行的任务号 //返回:无 //日期:2013/5/17 18:50 //============================================================================= void Scheduler(uint8 N) { StkTop[state] = SP; state = N; SP = StkTop[state]; } //============================================================================= //函数:DelayNms(uint z) //描述:12M晶振时Nms延时函数 //参数:延时参数 //返回:无 //日期:2013/5/17 18:50 //============================================================================= void DelayNms(uint8 z) { uint8 x,y; for(x=z;x>0;x--) { for(y=0;y<120;y++); } }