基于网上网友的代码,自己在单片机上实现, 特此记录分享之。

基于https://blog.csdn.net/yyx112358/article/details/78877523

//使用KEIL C51实现的简单合作式多任务操作系统内核

#include <regx52.H>
#include <INTRINS.H> typedef unsigned char u8;
typedef unsigned int u16; sbit LED1 = P2 ^ 0;
sbit LED2 = P2 ^ 1;
sbit LED3_idle = P2 ^ 3; //两个宏定义是为了保护现场,不被定时中断打乱。
//主要用于需要一次性运行完毕的代码中。
#define OPEN_SYS_ISR() {EA=1;TR2=1;}
#define CLOSE_SYS_ISR() {EA=0;TR2=0;TF2=0;} #define OS_TASK_STACK_SIZE (2+13+2*3)//存放断点2B,中断函数可能压栈13B,子程序每嵌套一层2B #define OS_TASK_NUM 2 typedef struct OS_TASK_ST
{
u8 delay; //当前延时剩余时间
u8 stack[OS_TASK_STACK_SIZE]; //私有堆栈
u8 sp; //私有堆栈指针
} OS_TASK; //任务工作块。 data OS_TASK os_task[OS_TASK_NUM]; //必须定义为data(因堆栈只能在data区)
data u8 os_idle_stack[15]; void os_switch(void);
void os_idle(void);
//void os_update_time(void);
void os_load(u8 id, void(*func));
void os_delay(u8 id, u8 delay);
void LED_Driver();
void os_task_0(void);
void sys_init(void);
void delay(u16 i); /*******************************************************************************
* 函 数 名 : Timer2 Init
* 函数功能 : 定时器0初始化,用于系统时钟
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void Timer2_init()
{
RCAP2H = (0xFFFF - 10000) / 256;
RCAP2L = (0xFFFF - 10000) % 256; //12MHz晶振下定10ms,自动重装
TH2 = RCAP2H;
TL2 = RCAP2L; //定时器2赋初值
T2CON = 0; //配置定时器2控制寄存器,这里其实不用配置,T2CON上电默认就是0,这里赋值只是为了演示这个寄存器的配置
T2MOD = 0; //配置定时器2工作模式寄存器,这里其实不用配置,T2MOD上电默认就是0,这里赋值只是为了演示这个寄存器的配置 IE = 0xA0; //1010 0000开总中断,开外定时器2中断,可按位操作:EA=1; ET2=1;
TR2 = 1; //启动定时器2
} void LED_Driver()
{
LED1 = ~LED1;
} void os_idle(void)
{
while (1)
{
LED3_idle = ~LED3_idle;
os_switch();
}
} /*
*任务调度,转向当前延时时间到且优先级最高(id较小)任务
而在一般的应用中,我们往往需要一个软件延时。例如:按键去抖、周期性采样等等。
所以,这就要求有一个软件定时器功能。因此,修改调度器如下:
首先定义任务控制器数据结构,加入一个延时记录:
*/
void os_switch(void)//任务切换
{
u8 i = OS_TASK_NUM;
do
{
i--;
if (os_task[i].delay == 0)//如果有任务延时时间到,则跳转至相应任务
SP = os_task[i].sp;
}
while (i); //否则不改变SP,继续执行os_idle()
} /*
* 更新任务延时表
* 注:应定时更新,最好放入定时器中断 void os_update_time(void)
{
u8 i = OS_TASK_NUM;
do
{
i--;
if(os_task[i].delay)
os_task[i].delay--;
}
while(i);
}
*/ //修改任务工作块并跳转入os_idle()进行任务切
void os_delay(u8 id, u8 delay)
{
TR2 = 0;//关中断
{
os_task[id].delay = delay; //延时设定
os_task[id].sp = SP; //保存SP
SP = os_idle_stack + 1; //SP指向os_idle_stack[1]
//os_delay()结束后跳转os_idle()
}
TR2 = 1;
} void os_load(u8 id, void(*func))
{
os_task[id].sp = os_task[id].stack + 1; //私有堆栈指针指向私有堆栈
os_task[id].stack[0] = (u16)func & 0xFF;//私有堆栈栈底存放任务函数入口
os_task[id].stack[1] = (u16)func >> 8;
} void os_task_0(void)
{
#define OS_CUR_ID (0) //static u8 i=0; //KEIL一般分配临时变量在RAM不在堆栈
//因此为了防止任务之间改写凡是作用域跨越os_delay()应作为static while (1)
{
LED1 = ~ LED1;
os_delay(OS_CUR_ID, 1);
} #undef OS_CUR_ID
} void os_task_1(void)
{
#define OS_CUR_ID (1) //static u8 i=0; //KEIL一般分配临时变量在RAM不在堆栈
//因此为了防止任务之间改写凡是作用域跨越os_delay()应作为static while (1)
{
//LED_Driver();
LED2 = ~ LED2;
os_delay(OS_CUR_ID, 1);
} #undef OS_CUR_ID
} /*******************************************************************************
* 函 数 名 : delay
* 函数功能 : 延时函数,i=1时,大约延时10us
*******************************************************************************/
void delay(u16 i)
{
while (i--);
} void sys_init()
{
u8 i;
for (i=0; i<4; i++)
{
LED_Driver();
delay(10000);
}
} void main()
{
//…初始化外设
sys_init(); //…初始化os所用定时器 Timer2_init(); //…初始化其它任务控制器
os_load(0, os_task_0);
os_load(1, os_task_1); os_idle_stack[0] = (u16)os_idle & 0xFF;
os_idle_stack[1] = (u16)os_idle >> 8;
SP = os_task[0].sp; //运行任务0 return; //跳转,永不返回。
} //12MHz晶振下定10ms,自动重装
void timer2() interrupt 5
{
u8 i = OS_TASK_NUM;
//EA=0;
//ET2=0;
//TF2=0;
//!!!注意!!!定时器2必须由软件对溢出标志位清零,TF2=0;硬件不能清零,
//这里与定时器0和定时器1不同!!!
CLOSE_SYS_ISR(); do
{
i--;
if (os_task[i].delay)
os_task[i].delay--;
}
while (i); OPEN_SYS_ISR();
}

使用KEIL C51实现的简单合作式多任务操作系统内核(单片机实现版本)的更多相关文章

  1. 编写和运行简单的"Hello World"操作系统内核

    通常编写一个操作系统内核是一项浩大的工程.但我今天的目标是制作一个简单的内核,用比较方便的方法在虚拟机上验证它能够被grub装载和运行,并且可通过gdb进行调试,为接下去的工作创造一个基础环境. 首先 ...

  2. Keil C51编译及连接技术

    主要介绍Keil C51的预处理方法如宏定义.常用的预处理指令及文件包含指令,C51编译库的选择及代码优化原理,C51与汇编混合编程的方法与实现以及超过64KB空间的地址分页方法的C51实现. 教学目 ...

  3. keil c51笔记

    第一章 Keil C51开发系统基本知识 第一节 系统概述 Keil C51是美国Keil Software公司出品的51系列兼容单片机C语言软件开发系统,与汇编相比,C语言在功能上.结构性.可读性. ...

  4. Keil C51程序调试过程

    用Keil C51编写程序时,经常需要调试,如果不是经常用这个软件的话,很容易忘记这些调试步骤,现在举一个例子“验证延迟函数delay()使其延迟的时间为500ms”说明. 源程序写完后,就可以调试了 ...

  5. Keil C51软件的使用

    进入 Keil C51 后,屏幕如下图所示.几秒钟后出现编辑界 启动Keil C51时的屏幕 进入Keil C51后的编辑界面 简单程序的调试:学习程序设计语言.学习某种程序软件,最好的方法是直接操作 ...

  6. Keil C51总线外设操作问题的深入分析

    阅读了<单片机与嵌入式系统应用>2005年第10期杂志<经验交流>栏目的一篇文章<Keil C51对同一端口的连续读取方法>(原文)后,笔者认为该文并未就此问题进行 ...

  7. KEIL C51 中嵌入汇编以及C51与A51间的相互调用

    如何在 KEIL C51(v6.21) 中调用汇编函数的一个示例 有关c51调用汇编的方法已经有很多帖子讲到,但是一般只讲要点,很少有对整个过程作详细描述,对于初学者是不够的,这里笔者通过一个简单例子 ...

  8. Hash查找法在Keil C51中的实现

    摘要:散列(hash)是一种重要的存储方法,也是一种常见的查找方法.它是指在记录的存储位置和它的关键字之间建立一个确定的对应关系.本文以射频卡门禁控制器为例,说明用射频卡卡号作为关键字,用Hash查找 ...

  9. KEIL C51高级编程

    第一节 绝对地址访问C51提供了三种访问绝对地址的方法: 1. 绝对宏:在程序中,用“#include”即可使用其中定义的宏来访问绝对地址,包括:CBYTE.XBYTE.PWORD.DBYTE.CWO ...

随机推荐

  1. IDEA使用Maven的第一个测试

    创建完成后,点击这个按钮.进行配置. 选择第二个就行了. 然后选择这个去配置tomcat.

  2. HTTP请求流程基础知识

    HTTP协议解析: HTTP即超文本传输协议,是一种详细规定了浏览器和万维网服务器之间互相通信的规则,它是万维网交换信息的基础,它允许将HTML文档从WEB服务器传输到WEB浏览器. URL(统一资源 ...

  3. HDU - 6621 K-th Closest Distance 主席树+二分答案

    K-th Closest Distance 主席树第二波~ 题意 给你\(n\)个数\(m\)个询问,问\(i\in [l,r]\)计算每一个\(|a_{i}-p|\)求出第\(k\)小 题目要求强制 ...

  4. Activity 启动模式总结

    Activity 启动模式: 1. standard: 默认启动模式,每次启动一个Activity都会重新创建一个实例: 2. singleTop: 栈顶复用模式,新Activity位于任务栈的栈顶, ...

  5. 用C#编写ActiveX控件,开发浏览器控件,注册ActiveX 控件

    用C#编写ActiveX控件,开发浏览器控件,注册ActiveX 控件用C#编写ActiveX控件 开发浏览器控件这是本控件开发完成后的一个简单应用.我们可以利用它以本地文件夹为单位来批量更新服务器的 ...

  6. gym 101810 M. Greedy Pirate (LCA)

    题目:https://codeforc.es/gym/101810/problem/M 题意:给 你一颗树,下面有m次查询,求u->v的最大值是多少,输入两点之间都会有两条边,正边有正权,反边有 ...

  7. CSS基础知识复习

    1. CSS优先级 标签内部属性 style定义的CSS > 文档内定义的css > 引用外部CSS文件 2. CSS选择器类型 . 标签选择器 . 类选择器(使用.做标识) . ID选择 ...

  8. (转)堆和栈的概念和区别 HeapOutOfMemory和StackOverflow解释

    转:https://blog.csdn.net/pt666/article/details/70876410 https://blog.csdn.net/guohan_solft/article/de ...

  9. bzoj 1233: [Usaco2009Open]干草堆tower 【想法题】

    首先这题的$n^3$的DP是比较好想的 $f[i][j]$表示用前$i$包干草 且最顶层为第$j+1$包到第$i$包 所能达到的最大高度 然而数据范围还是太大了 因此我们需要去想一想有没有什么单调性 ...

  10. java并发编程笔记(八)——死锁

    java并发编程笔记(八)--死锁 死锁发生的必要条件 互斥条件 进程对分配到的资源进行排他性的使用,即在一段时间内只能由一个进程使用,如果有其他进程在请求,只能等待. 请求和保持条件 进程已经保持了 ...