最近做一个小项目,功能稍显复杂,由于以前做STM32的项目用了freeRTOS比较多,体验到了用操作系统的便捷性。因此用STC8H单片机时,不太愿意跑裸机了,于是乎就考虑在51单片机上运行一个小型操作系统。
51单片机可运行的操作系统可选性并不多,经典的可以用KEIL软件自带的tiny51系统,实际测试的时候,发现该系统比较简单、占用内存比较少,主要是基于时间片轮询的方案,当多个任务同时运用公共资源(比如都用串口输出),经常会出现输出内容不正确的情况,因此,暂时放弃了该操作系统。
思来想去,当年读大学的时候接触过一点点陈明计先生的smallRTOS操作系统,于是准备启用起来。本文的处理器采用的是STC8H1K08。
于是在网上开始查找smallRTOS代码。首先是源码,估计是网上用的并不多,操作系统的源码还真不好找,下面给出了百度网盘的下载地址:
链接:https://pan.baidu.com/s/1LnV6ty-uW7CoqvDQGmuo8g
提取码:zfdc
源码下载后,解压出来有三个文件夹,如下图所示:
CONFIG_H文件夹下面,是三个与操作系统配置相关的三个.h文件,其中系统的主要配置config.h就在该文件夹下。在自己新建KEIL工程的时候,这三个.h文件必须和KEIL的工程文件放在一起,否则会编译出错,这一点尤为重要。
OS文件夹存放的是操作系统的源码,如下图所示:
OS_CORE.C是操作系统的核心实现代码,OS_Q是消息队列的实现代码,OS_SEM是信号量实现代码。
TARGET_CPU是与51单片机底层相关的文件,包含一个.c文件和汇编文件,如下图所示:
下面新建自己的一个keil工程。其中新建keil工程就不复述了,本文只是记录一下如何将smallRTOS新添加到工程中。将smallROTS51的源码复制到新建的keil工程目录下,如下图所示:
smallrtos51目录为操作系统的源码,然后将源码中的CONFIG_H目录下的三个.h头文件剪切到keil工程目录下,如上图的红色圈,这一步非常重要,否则会编译错误。
然后在keil中新建一个smallrtos的分组,将os的相关文件添加到文件分组内
然后编写main函数,主函数主要就是初始化定时器0(该定时器用于操作系统的滴答信号)和启动操作系统,代码如下:
void main()
{
SysInit(); //端口及外设初始化
OSStart(); //启动操作系统
while(1);
}
SysInit函数是我自己编写的一个初始化本项目的 io端口和相关外设的(暂时只是IO口初始化和串口初始化),代码如下:
void SysInit(void) //系统初始化函数
{
P1M1 = 0x0e; // 0000 1110 配置P10,P14,P16,P17为推挽输出, P15为准双向口,P11,P12,P13为高阻输入
P1M0 = 0xd1; // 1101 0001
P1PU = 0x20; //使能P15上拉电阻 0--禁止 1--打开
P3M1 = 0xf4; // 1111 0100 配置P34~P37为开漏输出,并使能上拉电阻,实现按键输入; P33为推挽输出 驱动LED灯
P3M0 = 0xf8; // 1111 1000
P3PU = 0xff;
P5M1 = 0xff;
P5M0 = 0xff; //P5配置为开漏 并使能上拉电阻 P54为按键
P5PU = 0xf5;
/*************定时器0初始化--用于smallrtos滴答时钟*********************************/
/*************经测 不能添加定时器初始值 不能设定为自动重载*********************************/
AUXR |= 0x80; //定时器时钟1T模式 //计算后操作系统的滴答时钟周期为65536/24M=2.73ms
TMOD = (TMOD & 0XF0) | 0X01;
TL0 = 0x0;
TH0 = 0x0;
TR0 = 1;
ET0 = 1;
/*********************************************************************************/
/*************串口1初始化 9600bps@24.000MHz*********************************/
SCON = 0x50; //8位数据,可变波特率
AUXR |= 0x01; //串口1选择定时器2为波特率发生器
AUXR &= 0xFB; //定时器时钟12T模式
T2L = 0xCC; //设置定时初始值
T2H = 0xFF; //设置定时初始值
AUXR |= 0x10; //定时器2开始计时
ES = 1;
uart1_busy = 0;
/******************************************************/
EA = 0; //关闭总中断 在启动操作系统函数OSStart();前不能先打开中断
}
然后配置操作系统参数,打开config.h文件,第一步更改头文件,将系统默认的51单片机的reg52.h更改为stc单片机的头文件,如下所示:
第二步是添加系统的多任务函数定义,本应用定义了5个任务如下所示:
第三步是更改OS_MAX_TASKS为5,该变量在OS_CFG.H头文件中,更改为5,如下所示:
第四步编写5个任务函数,均在main.c文件中添加,本文5个任务代码均相同,代码如下:
/************************************************************/
void TaskA(void)
{
while (1)
{
Uart1_SendString("this is taskA running\r\n");
OSWait(K_TMO,183); //延时183*2.73ms=500ms
OSWait(K_TMO,183); //延时183*2.73ms=500ms
}
}
代码的功能是每隔1s钟窗口输出各自任务的运行状态。
然后编译程序,基本上是没问题的,如下所示是本文的编译结果。
有5个警告,均是定义了某些函数(这几个函数是我自己编写的,暂时还没有调用),不影响代码的正常运行。可以看到编译后代码所占用的内存空间是非常少的。
最后下载到硬件板子上,查看运行结果,如下所示:
基本上每隔1s打印一次数据,5个任务打印的内容是不影响的,如果用tiny51系统的话,打印的内容会经常出错。
下面给出本工程的全部代码,编译通过并且运行正常。
链接:https://pan.baidu.com/s/1ug_h3zNOgJIyy3TfS3w2qg
提取码:kvav
后续会进一步实验信号量和消息队列的内容。