51操作系统学习笔记(二)

发布者:二进制心灵最新更新时间:2017-01-10 来源: eefocus关键字:操作系统 手机看文章 扫描二维码
随时随地手机看文章

在“51操作系统学习笔记(一)”里,已经掌握了并行多任务的实现,和利用修改sp内容,子程序结束调用ret来实现程序跳转。但程序没有涉及中断,任务中有中断,程序会失去控制。

这一单元,要学习带中断问题的多任务操作系统:"在51单片机下具有延时功能占先式内核的操作系统"

 

在网上找的这个程序比较简短,也符合本单元的学习任务。

不过有一个麻烦,这个程序用到了52扩展的一些功能,需要先学习89c52单片机的一些寄存器和计数器使用。

 

全部源代码如下:程序详细讲解,和源自于http://www.51hei.com/mcu/1325.html

//可以直接拷贝到keil中调试

 

 

#include

#define MAX_TASKS 5

typedef struct os_task_control_table {
unsigned char os_task_wait_tick;
unsigned char os_task_stack_top;
}TCB;

volatile unsigned char int_count;
volatile unsigned char os_en_cr_count;
#define enter_int() EA=0;int_count++;
#define os_enter_critical() EA=0;os_en_cr_count++;//关中断,关中断次数增加1
#define os_exit_critical() if(os_en_cr_count>=1){os_en_cr_count--;if(os_en_cr_count==0)EA=1;} //关闭中断
unsigned char code os_map_tbl[] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};//任务的状态字

volatile unsigned char os_task_int_tbl;//任务中断表
idata volatile TCB os_tcb[MAX_TASKS];//任务控制表数组
volatile unsigned char os_task_running_id;
volatile unsigned char os_task_rdy_tbl;//任务准备
unsigned char idata os_task_stack[MAX_TASKS][20];//任务堆栈,每个任务有20字节的堆栈

 

void os_init(void);
void os_task_create(unsigned char task_id ,unsigned int task_point,unsigned char stack_point);
void os_delay(unsigned char ticks);
void os_start(void);
void os_task_switch(void);
void exit_int(void);


//操作系统初始化
void os_init(void) {
EA = 0; //关闭中断
ET2 = 1;
T2CON = 0X00;
T2MOD = 0X00; //关计数器
RCAP2H = 0x0D8;
RCAP2L = 0x0F0;  //计数器2的初始化
os_task_rdy_tbl = 0; //
os_task_int_tbl = 0xff; //中断
int_count = 0; //中断的次数
os_en_cr_count = 0; //关闭中断的次数
}


//任务加载
void os_task_create(unsigned char task_id ,unsigned int task_point,unsigned char stack_point) {
os_enter_critical();//关中断,程序段中不能被打断
((unsigned char idata *)stack_point)[0] = task_point;//task_point是16位的,把任务的程序地址记录下来
((unsigned char idata *)stack_point)[1] = task_point>>8;//右移8位,高位地址在上面
os_tcb[task_id].os_task_stack_top = stack_point+14;//堆栈结构:有两字节程序地址,13字节的其他数据,所以栈顶部是加14(0-14)
os_task_rdy_tbl |= os_map_tbl[task_id];//任务的状态就绪
os_tcb[task_id].os_task_wait_tick = 0;//装载任务,任务的等待时间就是0
os_exit_critical(); //开中断
}

//系统延时
void os_delay(unsigned char ticks) {
os_enter_critical();//
os_tcb[os_task_running_id].os_task_wait_tick = ticks;
os_task_rdy_tbl &= ~os_map_tbl[os_task_running_id];
os_exit_critical();
os_task_switch(); //换下一个任务
}


//任务开始
void os_start(void) {
os_task_running_id = 0; //由任务0开始
os_tcb[os_task_running_id].os_task_stack_top -= 13; //堆栈顶跳到任务一的程序地址处的堆栈
EA = 1;      //开中断
SP = os_tcb[os_task_running_id].os_task_stack_top; //把任务一的程序地址给sp
TR2 = 1; //T2计数器打开,该计数器是52扩展的计数器
}   //os_start(void)执行完,就实现RET,sp的内容赋给pc,会接着执行任务一


//任务转换
void os_task_switch(void) {  
unsigned char i;
EA = 0;
os_tcb[os_task_running_id].os_task_stack_top = SP;
os_task_int_tbl &= ~os_map_tbl[os_task_running_id];
for(i=0; iif(os_task_rdy_tbl&os_map_tbl[i]) {
break;
}
}
os_task_running_id = i;
SP = os_tcb[os_task_running_id].os_task_stack_top;
if(os_task_int_tbl&os_map_tbl[os_task_running_id]) {
__asm POP 7 //Keil51的中断处理会先让这13个寄存器入栈,所以这里要出栈
__asm POP 6

__asm POP 5
__asm POP 4
__asm POP 3
__asm POP 2
__asm POP 1
__asm POP 0
__asm POP PSW
__asm POP DPL  //13字节堆栈
__asm POP DPH
__asm POP B
__asm POP ACC
}
EA = 1;
__asm RETI //从中断服务返回主程序
}


//中断结束
void exit_int(void) {  
unsigned char i;
SP -= 2;

if(--int_count == 0) 
  { //如果中断数=0
    os_tcb[os_task_running_id].os_task_stack_top = SP;
    os_task_int_tbl |= os_map_tbl[os_task_running_id];
    for(i=0; i      {                     //顺序检查任务是否准备好
         if(os_task_rdy_tbl&os_map_tbl[i]) 
           { 
             break;
           }
      }
   os_task_running_id = i;          //把就绪的任务号给任务运行号码
   SP = os_tcb[os_task_running_id].os_task_stack_top;     //堆栈指向运行任务
   if(os_task_int_tbl&os_map_tbl[os_task_running_id]) 
    {
      __asm POP 7
      __asm POP 6 //恢复任务寄存器
      __asm POP 5
      __asm POP 4
       __asm POP 3
      __asm POP 2
    __asm POP 1
   __asm POP 0
      __asm POP PSW
   __asm POP DPL
   __asm POP DPH
   __asm POP B
     __asm POP ACC
   }
 EA = 1;
 __asm RETI
 }

//如果中断数还不为0
__asm POP 7
__asm POP 6 //恢复任务寄存器
__asm POP 5
__asm POP 4
__asm POP 3
__asm POP 2
__asm POP 1
__asm POP 0
__asm POP PSW
__asm POP DPL
__asm POP DPH
__asm POP B
__asm POP ACC
EA=1;
__asm RETI    //计数器2中断服务程序结束,返回主程序
}


//滴答中断程序,计数器2的中断
void timer2_isr(void) interrupt 5 {  
unsigned char i;
TF2=0;
enter_int();
for(i=0; iif(os_tcb[i].os_task_wait_tick) {
os_tcb[i].os_task_wait_tick--;
if(os_tcb[i].os_task_wait_tick == 0) {
os_task_rdy_tbl |= os_map_tbl[i];
}
}
}
exit_int(); //
}


//下面是任务实现代码
void task_0(void) {          //空循环
while(1) {

}
}

sbit seg2 = P2^5;
sbit seg3 = P2^6;
sbit seg4 = P2^7;

void delay_ms(unsigned int xms){ //只是程序延时
unsigned int x,y;
for(x=xms; x>0; x--)
for(y=248; y>0; y--);
}

unsigned char code table[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0x40,0};

void task_1(void) {          //提取task_2的等待时间,显示在数码管上
unsigned char gw,sw,bw;
while(1) {
bw = os_tcb[2].os_task_wait_tick/100;
sw = os_tcb[2].os_task_wait_tick%100/10;
gw = os_tcb[2].os_task_wait_tick%10;
P0 = table[bw];
seg2=0;
delay_ms(3);
seg2=1;
P0 = table[sw];
seg3=0;
delay_ms(3);
seg3=1;
P0 = table[gw];
seg4=0;
delay_ms(3);
seg4=1;
}
}

void task_2(void) {
unsigned char i;
while(1) {
i++;
P3 = 0x01<<(i%8);
os_delay(200);
}
}

void task_3(void) {
unsigned char i;
while(1) {
i++;
//P2 = 0x01<<(i%8);
os_delay(7);
}
}

void task_4(void) {
unsigned char i;
while(1) {
i++;
P1 = 0x01<<(i%8);
  os_delay(10);
}
}

 


void main(void) {
os_init();
os_task_create(4,(unsigned int)&task_0,(unsigned char)os_task_stack[4]);
os_task_create(3,(unsigned int)&task_1,(unsigned char)os_task_stack[3]);
os_task_create(2,(unsigned int)&task_2,(unsigned char)os_task_stack[2]);
os_task_create(1,(unsigned int)&task_3,(unsigned char)os_task_stack[1]);
os_task_create(0,(unsigned int)&task_4,(unsigned char)os_task_stack[0]);
os_start();
}


关键字:操作系统 引用地址:51操作系统学习笔记(二)

上一篇:STC89C58串口接收GPS信号
下一篇:单片机秒表计时器

推荐阅读最新更新时间:2024-03-16 15:29

51单片机的315M接收头接收汇编程序(从机)
;防盗器子机程序 FSDY EQU P1.7 ;发射电源端,高电平为输出 FSKZ EQU P1.6 ;发射控制端,低电平有效 FSDD EQU P1.5 ;发射控制D端,高电平控制有效 FSCC EQU P1.4 ;发射控制C端,高电平控制有效 FSBB EQU P1.3 ;发射控制B端,高电平控制有效 CGDY EQU P1.2 ;传感器电源端,高电平为输出 SPK1 EQU P1.1 ;报警喇叭,高电平为输出,暂时不用 DYAD EQU P1.0 ;电源AD输入端
[单片机]
C51单片机外部中断的触发方式
1. 电平触发方式 ○ CPU采样外部中断引脚电平 ○ 低电平,则硬件置1(IE=1) ○ 高电平,则硬件清0(IE=0) ○ 外部中断引脚的低电平要一直保持,直到CPU响应外部中断 ○ CPU响应中断后,会硬件将IE清0(IE=0),但是不会取消掉外部中断引脚的低电平,因此需要硬件加软件清除低电平(p133) 2. 下降沿触发方式 ○ CPU采样外部中断的引脚电平 ○ 如果上一个机器周期电平为高,下一个机器周期电平为低,则硬件置1(IE=1) ○ 等待CPU响应中断 ○ CPU响应中断,硬件清0(IE=0)
[单片机]
C<font color='red'>51</font>单片机外部中断的触发方式
#C51串口通讯3-#一串数据#中断即时解析用户自定义协议
前言 1.上一章(中断定时+超时接收)的逻辑,适用于协议较为复杂的情况,即中断判断接收结束,数据放置缓冲区,主函数中解析数据。 2.实际开发中,用户自定义协议的帧头是可预见的,包括数据长度等。 3.本章测试使用中断即时解析自定义协议 提示:以下是本篇文章正文内容,下面案例可供参考 一、场景 示例一串数据:固定帧头区(AA 66 AA )+数据类型(01/02)+数据区+和校验+异或校验 其中,01LED,02蜂鸣器 ,数据区为工作时间,低位在前高位在后 二、编程实现 1.知识点 a.“状态机”的算法思想 状态机是有限状态自动机的简称,是现实事物运行规则抽象而成的一个数学模型。 包括:State(状态)、Event(事件)、
[单片机]
#C<font color='red'>51</font>串口通讯3-#一串数据#中断即时解析用户自定义协议
51单片机串口通信篇
基本介绍 单片机通信是指单片机和单片机 或者 单片机和计算机的相互通信,一般比较多都是单片机(下位机)和计算机(上位机)作为通信。 串行通信其实用的还是挺多的,就比如我们在调试程序时,可以通过串行口来调试程序。像什么蓝牙模块、语音模块等等也挺多是用串行通信。 一般通信方式为两种:并行通信 和 串行通信。 通信制式 一共为3种: 单双工: 一个只能发送,一个只能接收。(相当于两个人,一个只能说话,一个只能听) 半双工:都可以发送和接收,但是同一时刻不能发送和接收同时进行。相当于一般的对讲机 你说话的时候 你是接收不了对方说的话的。 全双工:就是可以同时发送或者接收。相当于现在我们的手机打电话一样。可以两个人互骂哈哈
[单片机]
<font color='red'>51</font>单片机串口通信篇
51mcu模拟ps2键盘发送数据程序
这个是程序是来自我以前开发的一个项目,其中里面的的void ps_send(uchar x) 这个函数,便可实现向电脑发送按键的数据 你只要对照 ps2 扫描码的表填入适当的参数即可 #include STC89C51.H #include keyboard.h #include INTRINS.H #define nop() _nop_() uchar *receive_buf;//接收来自计算机的数据 //uchar key; unsigned char key_buf ; /******延时子程序********/ void delay_ms(uchar a) {uchar i; uchar j; for(j=0;j
[单片机]
51单片机+1602+DS18B20的温度报警程序
51单片机做的温度显示,温度显示在LCD1602液晶屏上。 然后按键可以调整温度阈值,温度高于或低于所设温度,蜂鸣器就会响…… 单片机源程序如下: /************************************************************************************** * 基于单片机的实时温度报警系统设计 * (1)DS18b20采集到的温度可以实时地在数码管或者在液晶上显示出来; (2)用三个独立键盘设置阀值,包括设置键,加键
[单片机]
<font color='red'>51</font>单片机+1602+DS18B20的温度报警程序
C51单片机的IO口介绍(下)
上一篇文章我们对C51的IO简单的介绍了一下,现在我们来简单了解C51的IO的结构 这里我们学习一下比较常见的单片机的IO模式,分别是准双向,开漏输出和推挽输出 (这里要提一嘴,我们用的STC88C52RC是没有推挽输出功能的,STC公司后续推出的一些机型才有推挽输出功能) 接下来我们来看看这几种模式 1.准双向 看看下面的原理图,机内信号通过一个非门取反后送入一个MOS管,MOS管负责控制这个IO的高低电平,配合内部上拉电阻完成高低电平的输出 为了方便我们分析,我们把这个MOS管看成一只NPN三极管。区别是三极管靠电流导通,MOS管靠电压导通 结合前面我们讲过的知识,对于一只NPN三极管,基极b电压比集电极c电压
[单片机]
C<font color='red'>51</font>单片机的IO口介绍(下)
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved