STC设计与RTX51--RTX51操作系统

知不足而奋进 望远山而前行


目录

文章目录

前言

内容

操作系统

我们认知的操作系统

最小的操作系统

RTX51系统

RTX51 Real-Time Kernel

RTX51 Tiny环境搭建

库函数与RTX51

RTX51的延时问题

K_TMO与K_IVL的区别

K_SIG信号等待

总结


前言

  • 理解操作系统功能
  • 学会使用RTX51操作系统

内容

操作系统

我们认知的操作系统

我们认知的操作系统更多的是通过PC机或者手机来进行理解的,例如Window系统,Android系统。

由于这些商业系统属于高度集成化后的产物,我们会发现操作系统这个概念很复杂、很宽泛,无法理解和去实现。

目前大家已知的操作系统功能大致如下:

  1. 进程管理:管理程序的执行,包括进程的创建、终止、调度和通信等。
  2. 内存管理:负责管理内存的分配和回收,保证每个程序都能获得足够的内存空间。
  3. 文件系统管理:为应用程序提供文件的读写和管理功能,包括文件的创建、打开、关闭、删除等。
  4. 设备管理:管理计算机系统中的各种设备,包括输入输出设备、网络设备、存储设备等。
  5. 安全管理:保护系统的安全性,防止病毒、黑客等对系统进行攻击和破坏。
  6. 用户界面:提供用户与计算机系统进行交互的方式,包括图形用户界面和命令行界面等。
  7. 网络通信:管理计算机系统的网络通信,包括数据传输、协议处理、连接管理等。
  8. 错误处理:监测和处理各种错误和异常情况,保证系统的稳定性和可靠性。

操作系统是计算机系统中的核心软件,为用户和应用程序提供各种服务和支持,使得计算机系统能够高效、稳定地运行。

以上是我们认知中的商业操作系统的功能概述,每一块单独拿出来,都是一个复杂的内容,而且这些最终这些还要集合到一起去。

最小的操作系统

其实操作系统的复杂程度和硬件功能的集成度相关,内存、存储、CPU性能、是否有网络设备、是否有输入输出设备等等这些条件。

由于集成度不同,通用性也就不同,但是作为一个操作系统,必然会提供以下功能:

  • 任务管理
  • 任务调度
  • 任务间通信

所有操作系统必须提供这三种特定功能,有的只是叫法不同。

任务管理、任务调度和任务通讯是单片机最小操作系统中的核心功能,下面分别介绍一下:

  1. 任务管理:最小操作系统通常支持多任务,任务管理就是对多任务的管理。它包括任务的创建、销毁和切换。任务创建时需要分配任务所需要的资源,例如堆栈、全局变量等;任务销毁时需要释放这些资源。任务切换时需要保存任务的上下文并切换到下一个任务的上下文。
  2. 任务调度:任务调度是指按照一定的规则从就绪队列中选择一个任务并将处理器分配给它执行。最小操作系统通常采用时间片轮转的方式进行任务调度,每个任务被分配一个时间片,当时间片用完后,处理器被分配给下一个任务。
  3. 任务通讯:任务通讯是指多个任务之间进行信息交换。最小操作系统中通常采用消息队列的方式进行任务通讯,一个任务发送消息,另一个任务接收消息。任务通讯还可以采用信号量、邮箱等方式实现。

需要注意的是,最小操作系统的功能非常简单,只能满足一些基本的需求,例如任务管理、任务调度和任务通讯。如果需要更加复杂的功能,例如文件系统、网络协议栈等,则需要使用更加完善的操作系统。

RTX51系统

RTX51是Keil公司推出的用于8051系列单片机的实时操作系统(RTOS)。它提供了任务管理、任务调度、任务通讯、定时器、信号量、邮箱等实时操作系统的基本功能,并且与Keil公司的C51编译器紧密集成,能够方便地进行开发和调试。

RTX51支持多任务并发执行,通过任务管理器实现任务的创建、删除、挂起、恢复、优先级调整等功能。任务调度器能够根据任务的优先级、时间片轮转等算法进行任务调度,保证系统中各个任务都能得到适当的执行机会。任务通讯机制包括消息队列、信号量、邮箱等,能够方便地实现任务之间的数据共享和同步。

除了任务管理、任务调度、任务通讯等基本功能之外,RTX51还提供了定时器、中断服务程序等功能,能够方便地实现定时器、PWM、ADC等应用。同时,RTX51还提供了对Flash和EEPROM的编程支持,能够方便地实现在线升级等功能。

总之,RTX51是一款成熟、可靠、易用的实时操作系统,能够大大简化单片机的开发过程,提高开发效率和可靠性。

RTX51 包含两个版本:

  • RTX51 Tiny
  • RTX51 Full

RTX51 Tiny是一个非常小型的实时操作系统,具有基本的任务调度功能,包括任务优先级和时间片轮转等。RTX51 Tiny适用于基于51系列单片机的应用程序,特别是对于小型和简单的应用程序,因为它不需要太多的RAM和ROM资源。

注意:

RTX51采用的是Timer0实现的任务切换,因此不可以使用timer0,包括其中断函数也不可以声明!

否则系统不可用!

RTX51 Full则是一个功能更为强大的实时操作系统,它不仅支持基本的任务调度功能,还提供了更多的RTOS特性,例如信号量、邮箱、消息队列、事件标志和互斥量等,使得它更加适合于需要更高级RTOS特性的应用程序。

RTX51 Tiny是一个轻量级的RTOS,适用于简单的应用程序,而RTX51 Full则提供了更多的RTOS特性,适用于更为复杂的应用程序。

RTX51 Real-Time Kernel

RTX51 is a real-time kernel for the 8051 family of microcontrollers that is designed to solve two problems common to embedded programs.

  1. Multitasking: several operations must execute simultaneously.
  2. Real-time control: operations must execute within a defined period of time.

RTX51 Tiny环境搭建

  1. 新建一个项目
  2. 打开keil安装目录,来到C51\RtxTiny2\SourceCode目录,拷贝Conf_tny.A51RTX51TNY.LIB到项目中。

  1. 在项目中添加一个Group,名称自己定义,我们在这里定义为OS,将添加的两个文件加入到group中

  1. 打开配置,来到Target中,将Operating system修改为RTX-51 Tny

  1. 新建好main.c, 代码如下
#include "RTX51TNY.H"
#include "STC8H.H"

// P5.3 闪烁
void sys_init() {
	P5M1 &= ~0x08; P5M0 &= ~0x08;
}

// 这里函数名可随意, 建议不要使用start, 会和I2C.h里的Start冲突
void main_start() _task_ 0 {
	sys_init();
	// 创建任务 1
	os_create_task(1);
	// 结束任务 0
	os_delete_task(0);
}

void task_0() _task_ 1 {
	while(1) {
		P53 = 1;
		os_wait1(K_TMO);
		
		P53 = 0;
		os_wait1(K_TMO);
	}
}
  • 不再有main函数
  • 代码入口为标记为 _task_ 0的函数。
  • _task_标记的函数,表示这个是独立的任务,多个task可以同时执行。

库函数与RTX51

开发过程中,会将库函数环境和OS环境进行整合,整合的过程需要用到两者特性,并且不产生冲突。

  1. 新建项目
  2. 在项目目录中新建User目录、Lib目录、OS目录。
  • User目录存放main.c等和业务相关的文件
  • Lib目录存放库函数目录
  • OS目录存放操作系统文件
  • Driver目录存放硬件驱动
  1. 在项目中添加Group,依次对应新建的几个目录,将对应的文件加入到各自的group中。
  2. keil中配置include path,将以上几个目录配置进去。
  3. 修改config.h文件,内容如下:

#ifndef		__CONFIG_H
#define		__CONFIG_H

//========================================================================
//                               主时钟定义
//========================================================================

#define MAIN_Fosc		24000000L	//定义主时钟
//#define MAIN_Fosc		22118400L	//定义主时钟
//#define MAIN_Fosc		12000000L	//定义主时钟
//#define MAIN_Fosc		11059200L	//定义主时钟
//#define MAIN_Fosc		 5529600L	//定义主时钟

#define	NULL	0

//========================================================================
//                                头文件
//========================================================================

#include "STC8H.H"
#include "RTX51TNY.H"
#include <intrins.h>
#include <stdlib.h>
#include <stdio.h>

//========================================================================
//                               类型定义
//========================================================================

typedef unsigned char   u8;     //  8 bits 
typedef unsigned int    u16;    // 16 bits 
typedef unsigned long   u32;    // 32 bits 

typedef signed char     int8;   //  8 bits 
typedef signed int      int16;  // 16 bits 
typedef signed long     int32;  // 32 bits 

typedef unsigned char   uint8;  //  8 bits 
typedef unsigned int    uint16; // 16 bits 
typedef unsigned long   uint32; // 32 bits 

//===================================================

#define	TRUE	1
#define	FALSE	0

//===================================================

#define	Priority_0			0	//中断优先级为 0 级(最低级)
#define	Priority_1			1	//中断优先级为 1 级(较低级)
#define	Priority_2			2	//中断优先级为 2 级(较高级)
#define	Priority_3			3	//中断优先级为 3 级(最高级)

#define ENABLE		1
#define DISABLE		0

#define SUCCESS		0
#define FAIL		-1


//===================================================

#define	I2C_Mode_Master			1
#define	I2C_Mode_Slave			0

#define	PIE			0x20	//1: 比较结果由0变1, 产生上升沿中断
#define	NIE			0x10	//1: 比较结果由1变0, 产生下降沿中断

#define	PWMA	128
#define	PWMB	129

#define	FALLING_EDGE		1		//产生下降沿中断
#define	RISING_EDGE			2		//产生上升沿中断
//===================================================


//========================================================================
//                             外部函数和变量声明
//========================================================================

#endif
    • include加入OS的支持

RTX51的延时问题

示例代码

#include "config.h"
#include "GPIO.h"
#include <stdio.h>
#include "delay.h"

#define LED_SW		P45
#define LED1			P27
#define LED2			P26

void GPIO_config(void) {
    GPIO_InitTypeDef	GPIO_InitStructure;		//结构定义
    GPIO_InitStructure.Pin  = GPIO_Pin_5;		//指定要初始化的IO,
    GPIO_InitStructure.Mode = GPIO_PullUp;	//指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
    GPIO_Inilize(GPIO_P4, &GPIO_InitStructure);//初始化

    GPIO_InitStructure.Pin  = GPIO_Pin_7;		//指定要初始化的IO,
    GPIO_InitStructure.Mode = GPIO_PullUp;	//指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
    GPIO_Inilize(GPIO_P2, &GPIO_InitStructure);//初始化

    GPIO_InitStructure.Pin  = GPIO_Pin_3;		//指定要初始化的IO,
    GPIO_InitStructure.Mode = GPIO_PullUp;	//指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
    GPIO_Inilize(GPIO_P5, &GPIO_InitStructure);//初始化
}

void sys_init() {
    GPIO_config();
    EA = 1;
}

// 这里函数名可随意, 建议不要使用start, 会和I2C.h里的Start冲突
void main_start() _task_ 0 {
    sys_init();
		LED_SW = 0;

    // 创建任务 1
    os_create_task(1);
    // 创建任务 2
    os_create_task(2);

    // 结束任务 0
    os_delete_task(0);
}

void task_1() _task_ 1 {
    P53 = 1;
    while(1) {
        P53 = !P53;
        //os_wait2(K_TMO, 200);
		delay_ms(250);
        delay_ms(250);
        delay_ms(250);
        delay_ms(250);
    }
}

void task_2() _task_ 2 {
    LED1 = 0;
    while(1) {
        LED1 = !LED1;
        os_wait2(K_TMO, 200);
    }
}

两个任务,一个延时会卡住cpu执行,一个不会。

我们可以适当调节延时时间,通过逻辑分析仪来分析。delay的延时会影响到系统的延时。

我们在非必要的情况下,尽量少使用卡住cpu的延时操作。或者在任务执行过程中,任务中的延时要求不需要那么高。否则两者会影响。

K_TMO与K_IVL的区别

  调用os_wait() / os_wait2()指定K_TMO / K_IVL参数都能让任务进入waiting状态,然后等待一段时间后恢复到ready状态,K_TMOK_IVl的区别如下:

  1、计算的起点:K_TMO是以当前调用wait / wait2的时间为起点,K_IVL是以上一次任务结束为起点。

  2、是否包含任务本身执行时间:K_TMO不包含,K_IVl包含。

  通过一个时序图说明情况,如下图,有3个任务,分别是task_0/1/2,假设3个任务的自身执行时间1ms

    • task_0没有调用任何阻塞API。
    • task_1使用K_TMO参数等待3ms超时。
    • task_2使用K_IVL等待3ms间隔。

  并假设调度器先按照task_0、task_1、task_2的顺序调度任务:

task_0先执行1ms,SysTick=1

task_1执行1ms,然后等待3ms超时,SysTick=2

task_2执行1ms,然后等待3ms间隔,SysTick=3

task_0执行2ms,此时只有task_0可执行,故连续执行2ms,SysTick=5

task_2执行1ms,注意,由于K_IVL包含任务自身执行时间,离上一次task_2结束已经过去2ms,所以接下来要执行task_2,SysTick=6

task_1执行1ms,task_1等待的时间已经到了并且超时,所以执行task_1,SysTick=7

task_0执行1ms,SysTick=8

task_2执行1ms,离上一次task_2结束已经过去2ms,接下来执行task_2,SysTick=9

……

  能够观察到,K_TMO是超时的意思,它能够保证一定会等到足够的时间,但是不一定准确,时间轴上可能是不连续的,比如上图而言,task_1只在SysTick=11的时候准确等待了3ms,而前面SysTick=6的时候等待了4ms。

  K_IVL保证间隔的时间是准确的,在时间轴上是连续的。对于code = x(ms),wait() / wait2() = y(ms),调用API后,K_TMO将在 >=(x+y) 的时间执行完成,K_IVL将在 y 时间执行完成。

官网文档:Documentation – Arm Developer

K_SIG信号等待

通过os_wait实现信号等待

void task_2() _task_ 2 {
	while(1) {
		printf("task 2 start wait\r\n");
		os_wait1(K_SIG);
		printf("task 2 end wait\r\n");
	}
}

以上是一个任务在执行,通过os_wait1(K_SIG)进行信号等待,直到有信号来才能执行下一句。

我们可以通过在一些事件中来发送信号:

os_send_signal(2);


总结

  • 如果想从现在起等待一段时间,用K_TMO
  • 如果想做周期性动作,用K_IVL
  • 22
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

薛慕昭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值