1.简介
RTX51 Tiny是一个实时操作系统(RTOS),允许创建应用程序,同时执行多个功能或任务。这在嵌入式应用程序中是经常需要的。虽然可以在没有RTOS的情况下创建实时程序(通过在循环中执行一个或多个函数或任务),但是像RTX51 Tiny这样的RTOS可以解决许多调度、维护和计时问题。实时操作系统(real-time operating system, RTOS)可以灵活地调度CPU、内存等系统资源,并提供任务之间的通信。RTX51 Tiny是一个功能强大的RTOS,易于使用,并可以与所有8051衍生品一起配合使用。
RTX51的基本程序是用标准C结构编写,用Keil C51 C编译器编译的。C语言的补充允许我们可以轻松地声明任务函数,而不需要复杂的堆栈和变量框架配置。RTX51微型程序只需要包含一个特殊的头文件(#include <rtx51tny.h>),并将RTX51微型库连接到我们的程序中。
2.运行原理
因为RTX51 Tiny使用和管理目标系统的资源,所以RTX51 Tiny的许在多方面可以项目的基础上进行配置。
1.时钟中断
RTX51 Tiny使用标准的8051 Timer 0(模式1)来产生周期性中断。这个中断是RTX51 Tiny Timer Tick。为RTX51 Tiny库例程指定的超时和间隔值是使用RTX51 Tiny Timer Tick来测量的。默认情况下,RTX51 Timer Tick中断每10,000个机器周期发生一次。因此,对于运行在12MHz的标准8051,定时器的周期是0.01秒或频率为100Hz (12MHz / 12 / 10,000)。可以在CONF_TNY.A51中修改相应的配置文件。也可以将我们自己的代码附加到RTX51定时器中断CONF_TNY.A51配置文件中以实现自定义。
2.任务
RTX51 Tiny基本上是一个任务切换器。要创建RTX51 Tiny程序,必须创建一个具有一个或多个任务函数的应用程序。接下来将对此进行详细的讲解,但有以下基本概念:
任务是在C编程语言中使用Keil C51编译器支持的新关键字定义的。 RTX51 Tiny将每个任务保持在一个状态(运行、就绪、等待、删除或超时)。 一次只能有一个任务处于“运行状态”。 许多任务可能处于就绪、等待、删除或超时状态。 Idle Task总是准备好在所有定义的任务都被阻塞的情况下运行。
3.任务状态
运行(RUNNING)
当前正在运行的任务处于“running State”状态。一次只能有一个任务处于此状态。os_running_task_id返回当前执行任务的任务号。
就绪(READY)
准备运行的任务处于ready状态。一旦Running任务完成处理,RTX51 Tiny选择并启动下一个Ready任务。可以通过使用os_set_ready或isr_set_ready函数设置就绪标志,使任务立即就绪(也就是使任务正在等待超时或信号)。
等待(WAITING)
等待事件发生的任务处于等待状态。事件发生后,任务切换到READY状态。os_wait功能用于将任务置于等待状态。
删除(DELETED)
未启动或已删除的任务处于“已删除状态”。os_delete_task例程将已启动的任务(使用os_create_task)置于DELETED State。
超时(TIME-OUT)
被Round-Robin Time-Out中断的任务将处于“Time-Out”状态。这个状态相当于轮询程序的READY状态
4.事件
实时操作系统中的事件可以用来控制程序中任务的执行。任务可以等待事件,也可以为其他任务设置事件标志。
os_wait函数允许任务等待一个或多个事件,但还有以下细节
Timeout是任务可以等待的常见事件。超时仅仅是时钟的次数。当一个任务等待超时时,其他任务可能会执行。一旦指定的计时器数过去,任务就可以继续执行。
Interval可以说是Timeout的变体。间隔类似于超时,不同之处在于指定的时钟周期数与任务上次调用os_wait函数的时间有关。Interval可用于生成一个任务,该任务按照常规的同步计划(比如每秒钟运行一次)运行,而不管调用os_wait函数的时间间隔是多少。如果已经过了指定的时钟节拍数(因为上次调用了os_wait函数),则立即重新启动任务——不执行其他任务。
Signal是任务间通信的一种简单形式。任务可以等待另一个任务发送信号(使用os_send_signal和isr_send_signal函数)。
每个任务都有一个Ready标志,可以由其他任务设置(使用os_set_ready和isr_set_ready函数)。一个正在等待超时、间隔或信号的任务可以通过设置其ready标志来启动。
每个事件都有一个由RTX51 Tiny维护的相关事件标志。以下事件选择器可以与os_wait函数一起使用,以指定要等待的内容:
事件选择器 | 简述 | K_IVL | 等待指定的时间Interval | K_SIG | 等待一个Signal | K_TMO | 等待指定的Time-out |
当os_wait函数返回时,发生的事件由返回值指定:
返回值 | 简述 | RDY_EVENT | 任务被设成READY标志 | SIG_EVENT | 接收到一个Signal | TMO_EVENT | Time-out完成或Interval已过去 |
os_wait函数可以等待以下事件组合:
K_SIG | K_TMO: os_wait将延迟任务,直到向其发送信号或经过指定的时钟周期。 K_SIG | K_IVL: os_wait将延迟任务,直到向其发送信号或经过指定的时间间隔。
其中还要非常注意K_IVL和K_TMO事件选择器不能组合使用。
5.任务调度
任务调度器将处理器分配给一个任务。RTX51微型调度器使用以下规则来决定运行哪个任务:
当出现以下情况时,当前任务中断:
1.任务调用os_switch_task,另一个任务准备好运行了。 2.任务调用os_wait函数,而指定的事件没有发生。 3.任务的执行时间超过了定义的轮询时间片。
当出现以下情况时,另一个任务将运行:
1.没有其他任务正在运行。 2.待启动的任务处于“READY”或“TIME-OUT”状态。
6.轮询任务切换
RTX51 Tiny可以被配置为使用轮循多任务(或任务切换)。Round-Robin允许多个任务的准并行执行。任务实际上并不是并发执行的,而是按时间划分的(可用的CPU时间被划分为多个时间片,RTX51 Tiny为每个任务分配了一个时间片)。因为时间片很短(只有几毫秒),所以看起来好像任务是同时执行的。
任务在它们的时间片期间执行(除非任务的时间片被放弃)。然后,RTX51 Tiny切换到下一个准备运行的任务。一个时间片的持续时间可以由RTX51 Tiny Configuration定义。
下面的例子展示了一个简单的RTX51 Tiny程序,它使用了轮询多任务处理。这个程序中的两个任务是计数器循环。RTX51 Tiny开始执行task 0,即名为job0的函数。这个函数创建另一个名为job1的任务。在它的时间片执行job0之后,RTX51 Tiny切换到job1。在为它的时间片执行job1之后,RTX51 Tiny切换回job0。这个过程是无限重复的。
#include <rtx51tny.h>
int count0;
int count1;
void job0 (void) _task_0
{
os_create(1); /*标志task1已就绪*/
while(1)
{
count0++;
}
}
void job1 (void) _task_1
{
while(1)
{
count1++;
}
}
我们可以使用os_wait函数或os_switch_task函数来允许RTX51 Tiny切换到另一个任务,而不是等待任务的时间片过期。os_wait函数会暂停当前任务(将当前任务状态变为WAITING状态),直到发生指定的事件(任务状态变为READY状态)。在此期间,可以运行任意数量的其他任务。
7.联合任务切换
如果禁用Round-Robin多任务,则必须设计和实现任务,使它们能够协同工作。具体来说,必须在每个任务中调用os_wait函数或os_switch_task函数。这些功能信号RTX51微小切换到另一个任务。 os_wait和os_switch_task的区别在于,os_wait允许您的任务等待某个事件,而os_switch_task会立即切换到另一个就绪的任务。
8.空闲任务
当没有任务准备运行时,RTX51 Tiny会执行一个空闲任务。空闲任务只是一个无尽的循环。例如:
JMP $
一些8051兼容的设备提供空闲模式,通过暂停程序执行直到中断发生来降低功耗。在这种模式下,所有外设包括中断系统仍然继续运行。
RTX51 Tiny允许你在空闲任务中启动空闲模式(当没有其他任务准备好执行时)。当RTX51小定时器滴答中断(或任何其他中断)发生时,微控制器恢复程序执行。Idle Task执行的代码可以在CONF_TNY.A51配置文件中启用和配置。
9.堆栈管理
RTX51 Tiny仅使用8051的内部内存(IDATA)为每个任务维护一个堆栈。当一个任务正在运行时,它会被赋予尽可能多的堆栈空间。当任务切换发生时,前一个任务堆栈会收缩并重新定位,当前任务的堆栈则会扩展并重新定位。
下图演示了带有三个独立任务的示例应用程序的内部内存布局
STACK符号表示堆栈的起始地址。在这个例子中,位于堆栈下面的对象包括全局变量、寄存器和可位寻址内存。剩余的内存用于任务堆栈。内存的顶部可以在配置中指定。
3.具体配置
RTX51 Tiny必须为创建的嵌入式应用程序配置。所有的配置设置都可以在CONF_TNY中找到。A51文件位于\KEIL软件安装目录\C51\RTXTINY\文件夹中。CONF_TNY中的配置选项A51允许:
*指定定时器中断寄存器。 *指定定时器间隔(以8051机器周期为单位)。 *指定在定时器中断中执行的用户代码。 *指定轮询超时时间。 *启用/禁用轮询任务切换功能。 *指定应用程序包含长时间中断。 *指定是否使用代码分体。 *定义RTX51微型堆栈的顶部。 *指定所需的最小堆栈空间。 *指定发生堆栈错误时要执行的代码。 *定义空闲任务操作。
CONF_TNY的默认配置。A51包含在RTX51微型库中。但是,要保证应用程序使用的配置,必须复制CONF_TNY.A51文件到我们的项目文件夹,并将其添加到我们的项目。如果我们的项目中没有包含配置文件(CONF_TNY.A51),则会自动包含库中的默认配置文件。对存储在库中的配置文件的后续更改可能会对应用程序的操作产生不利影响。
1.硬件定时器
下面的EQUates指定如何配置RTX51微型硬件定时器
INT_REGBANK指定RTX51 Tiny Timer Interrupt使用的寄存器组。默认设置为1(对于寄存器bank1)
INT_CLOCK指定定时器产生中断之前的周期数。取值范围为1000 ~ 65535。小数目产生更快的中断。这个数字用于计算计时器的重新加载值(65536-INT_CLOCK)。默认设置为10000。
HW_TIMER_CODE是一个宏,指令代码在RTX51小定时器中断结束时执行。这个宏的默认设置是从中断(RETI)返回。
2.轮询任务切换
系统默认开启轮询任务切换功能。通过以下等效参数,可以配置轮询任务切换时间或完全禁用轮询。
TIMESHARING指定在轮询任务切换前,每个任务运行的RTX51 Tiny timer数。取值为0时,禁用轮询任务切换。默认设置为5个计时滴答。
3.中断
通常,中断服务程序(isr)被设计为快速执行。在某些情况下,我们的isr可能会执行很长一段时间。如果一个高优先级的ISR执行时间超过RTX51定时器间隔,RTX51定时器中断可能被(我的ISR)中断,并可能被(随后的RTX51定时器中断)重新输入。
如果我们使用长时间执行的高优先级中断,我们应该考虑减少在ISR中执行的工作量,将RTX51 Timer Tick速率更改为一个较慢的速率,或者使用以下配置选项。
LONG_USR_INTR指定应用程序是否有执行时间超过定时器间隔的中断(RTX51 Tiny Timer Tick Interrupt除外)。当这个配置选项设置为1时,RTX51 Tiny包含了保护RTX51 Tiny Timer Tick Interrupt免于重入的代码。对于快速中断,默认设置为0。
4.代码分体
下面的配置选项允许您指定您的RTX51微型应用程序是否使用代码分体。
CODE_BANKING指定应用程序是否使用代码分体。如果使用代码分体,则该选项必须设置为1;如果不使用代码分体,则该选项必须设置为0。对于不进行代码分体业务,默认设置为0。
5.堆栈
堆栈配置有几个选项可用。下面的EQUates定义了用于堆栈区域的内部RAM的大小和堆栈上的最小空闲空间。宏允许您指定在CPU堆栈上没有足够的空闲堆栈时执行的代码。
RAMTOP指定片上堆栈的顶部地址。除非有位于堆栈上方的IDATA变量,否则不应更改此参数。默认设置为0xFF。
FREE_STACK指定堆栈上可用的最小字节数。当切换到一个任务时,如果RTX51 Tiny检测到少于指定的值,则执行宏STACK_ERROR。值0禁用堆栈检查。默认设置为20字节。
STACK_ERROR是一个宏,它指定在发生堆栈错误(少于FREE_STACK可用字节)时执行的指令。默认的宏禁用中断并进入一个无限循环。例如:
STACK_ERROR MACRO
CLR EA;禁用中断
SJMP $;如果堆栈耗尽则进入无限循环
ENDM
6.空闲任务
当没有任务准备好运行RTX51 Tiny执行一个空闲任务。Idle Task是一个简单的循环,它什么都不做——它只等待RTX51的一个小定时器Tick Interrupt来切换到一个准备好的任务。下面的EQUates允许我们配置RTX51 Tiny Idle Task的不同方面
CPU_IDLE是一个宏,它指定要在Idle Task中执行的代码。默认指令设置PCON寄存器中的Idle Mode位(在大多数8051设备上可用)。这通过停止程序执行直到中断发生来节省电力。例如:
CPU_IDLE MACRO
ORL PCON,#1;把CPU设成IDLE
ENDM
CPU_IDLE_CODE指定CPU_IDLE宏是否在Idle Task中执行。默认设置为0,因此CPU_IDLE宏不包含在Idle Task中。
7.优化建议*
我们可以做以下几件事来优化我们的RTX51程序。
1.如果可能,就关闭轮询任务切换。Round-Robin需要13个字节的堆栈空间来存储任务上下文和所有寄存器。如果任务切换是通过调用RTX51 Tiny库例程(如os_wait或os_switch_task)触发的,则不需要这个空间。
2.使用os_wait例程而不是依赖Round-Robin超时切换任务。这提高了系统响应时间和任务响应时间。
3.避免设置定时器滴答中断速率过快。将计时器的超时设置为一个较小的值会增加每秒的滴答数,但会减少任务可用的时间(因为计时器中断需要100-200个周期来执行)。将超时值设置得足够高,以最小化计时器中断处理程序的影响。
4.为了最小化RTX51 Tiny的内存需求,可以按顺序从0开始为任务编号
4.具体使用
说到具体使用就是编程了。通常编程都会三步走,首先编写RTX51程序。编译和执行程序。测试和调试程序。
RTX51 Tiny只需要使用一个包含文件:RTX51TNY.H。所有库例程和常量都定义在这个头文件中。您可以将它包含在我们的RTX51源文件中,如下所示:
#include <rtx51tny.h>
编写RTX51程序的时候有些事情必须要知道
1.确保包含rtx51ny . h头文件 2.不能创建C主函数,RTX51 Tiny有自己的主要功能。 3.程序必须包含至少一个任务函数。 4.RTX51小程序必须启用中断(EA=1)。如果禁用关键部分的中断,得小心。
5.程序必须调用至少一个RTX51 Tiny库例程(如os_wait)。否则,链接器将不包括RTX51微型库。 6.Task 0是程序中第一个执行的函数。必须从任务0调用os_create_task函数来运行其他任务。 7.任务函数永远不能退出或返回。任务必须重复使用while(1)或类似的结构。使用os_delete_task函数停止正在运行的任务。 8.必须在μVision或链接器命令行中指定RTX51 Tiny。
关于创建任务,实时或多任务应用程序由一个或多个执行特定操作的任务组成。RTX51 Tiny最大支持16个任务。 任务是简单的C函数,它有一个void返回类型和一个void参数列表,并使用_task_ function属性声明。例如
void func (void) _task_ task_id
func是任务函数的名称 task_id任务ID号,取值范围为0 ~ 15
关于创建任务的注意事项
1.所有的任务都应该实现为无限循环。任务不应该返回。 2.任务不能返回函数值。它们必须有一个void返回类型。 3.参数不能传递给任务。任务必须有一个void参数列表。 4.必须为每个任务分配一个唯一的、非循环的任务ID。 5.为了最小化RTX51 Tiny的内存需求,应按顺序从0开始为任务编号。
下面有一个交通灯的实例,这是一个控制交通灯的程序。 交通灯是定时的,让车辆在特定的时间内通过。有一个行人过街按钮,让行人通过。指示灯已连接到Port 2。我们可以使用dScope来查看实际情况。
这个程序用了RTX51 Tiny。程序初始化操作代码在TRAFFIC.C中。我们可以寻找初始化任务,看看一切从哪里开始。
串行I/O是中断和信号驱动的。完整地看看SERIAL.C封装的模块(或许可以在自己的程序中使用这个)。
/******************************************************************************/
/* */
/* TRAFFIC.C: Traffic Light Controller using the C-51 COMPILER */
/* */
/******************************************************************************/
char code menu[] =
"\n"
"+***** TRAFFIC LIGHT CONTROLLER using C51 and RTX-51 tiny *****+\n"
"| This program is a simple Traffic Light Controller. Between |\n"
"| start time and end time the system controls a traffic light |\n"
"| with pedestrian self-service. Outside of this time range |\n"
"| the yellow caution lamp is blinking. |\n"
"+ command -+ syntax -----+ function ---------------------------+\n"
"| Display | D | display times |\n"
"| Time | T hh:mm:ss | set clock time |\n"
"| Start | S hh:mm:ss | set start time |\n"
"| End | E hh:mm:ss | set end time |\n"
"+----------+-------------+-------------------------------------+\n";
#include <REG932.H> /* special function registers 8052 */
#include <rtx51tny.h> /* RTX-51 tiny functions & defines */
#include <stdio.h> /* standard I/O .h-file */
#include <ctype.h> /* character functions */
#include <string.h> /* string and memory functions */
#include "traffic.h" /* project specific header file */
struct time ctime = { 12, 0, 0 }; /* storage for clock time values */
struct time start = { 7, 30, 0 }; /* storage for start time values */
struct time end = { 18, 30, 0 }; /* storage for end time values */
sbit red = P2^2; /* I/O Pin: red lamp output */
sbit yellow = P2^1; /* I/O Pin: yellow lamp output */
sbit green = P2^0; /* I/O Pin: green lamp output */
sbit stop = P2^3; /* I/O Pin: stop lamp output */
sbit walk = P2^4; /* I/O Pin: walk lamp output */
sbit key = P2^5; /* I/O Pin: self-service key input */
char idata inline[16]; /* storage for command input line */
/******************************************************************************/
/* Task 0 'init': Initialize */
/******************************************************************************/
void init (void) _task_ INIT { /* program execution starts here */
serial_init (); /* initialize the serial interface */
/* configure I/O port */
P2M1 = 0x1F; /* P2.0-P.2.4 output, P2.5-P2.7 input */
os_create_task (CLOCK); /* start clock task */
os_create_task (COMMAND); /* start command task */
os_create_task (LIGHTS); /* start lights task */
os_create_task (KEYREAD); /* start keyread task */
os_delete_task (INIT); /* stop init task (no longer needed) */
}
bit display_time = 0; /* flag: signal cmd state display_time */
/******************************************************************************/
/* Task 2 'clock' */
/******************************************************************************/
void clock (void) _task_ CLOCK {
while (1) { /* clock is an endless loop */
if (++ctime.sec == 60) { /* calculate the second */
ctime.sec = 0;
if (++ctime.min == 60) { /* calculate the minute */
ctime.min = 0;
if (++ctime.hour == 24) { /* calculate the hour */
ctime.hour = 0;
}
}
}
if (display_time) { /* if command_status == display_time */
os_send_signal (COMMAND); /* signal to task command: time changed */
}
os_wait (K_IVL, 100, 0); /* wait interval: 1 second */
}
}
struct time rtime; /* temporary storage for entry time */
/******************************************************************************/
/* readtime: convert line input to time values & store in rtime */
/******************************************************************************/
bit readtime (char idata *buffer) {
unsigned char args; /* number of arguments */
rtime.sec = 0; /* preset second */
args = sscanf (buffer, "%bd:%bd:%bd", /* scan input line for */
&rtime.hour, /* hour, minute and second */
&rtime.min,
&rtime.sec);
if (rtime.hour > 23 || rtime.min > 59 || /* check for valid inputs */
rtime.sec > 59 || args < 2 || args == EOF) {
printf ("\n*** ERROR: INVALID TIME FORMAT\n");
return (0);
}
return (1);
}
#define ESC 0x1B /* ESCAPE character code */
static bit escape; /* flag: mark ESCAPE character entered */
/******************************************************************************/
/* Task 6 'get_escape': check if ESC (escape character) was entered */
/******************************************************************************/
void get_escape (void) _task_ GET_ESC {
while (1) { /* endless loop */
if (_getkey () == ESC) escape = 1; /* set flag if ESC entered */
if (escape) { /* if escape flag send signal */
os_send_signal (COMMAND); /* to task 'command' */
}
}
}
/******************************************************************************/
/* Task 1 'command': command processor */
/******************************************************************************/
void command (void) _task_ COMMAND {
unsigned char i;
printf (menu); /* display command menu */
while (1) { /* endless loop */
printf ("\nCommand: "); /* display prompt */
getline (&inline, sizeof (inline)); /* get command line input */
for (i = 0; inline[i] != 0; i++) { /* convert to uppercase */
inline[i] = toupper(inline[i]);
}
for (i = 0; inline[i] == ' '; i++); /* skip blanks */
switch (inline[i]) { /* proceed to command function */
case 'D': /* Display Time Command */
printf ("Start Time: %02bd:%02bd:%02bd "
"End Time: %02bd:%02bd:%02bd\n",
start.hour, start.min, start.sec,
end.hour, end.min, end.sec);
printf (" type ESC to abort\r");
os_create_task (GET_ESC); /* ESC check in display loop */
escape = 0; /* clear escape flag */
display_time = 1; /* set display time flag */
os_clear_signal (COMMAND); /* clear pending signals */
while (!escape) { /* while no ESC entered */
printf ("Clock Time: %02bd:%02bd:%02bd\r", /* display time */
ctime.hour, ctime.min, ctime.sec);
os_wait (K_SIG, 0, 0); /* wait for time change or ESC */
}
os_delete_task (GET_ESC); /* ESC check not longer needed */
display_time = 0; /* clear display time flag */
printf ("\n\n");
break;
case 'T': /* Set Time Command */
if (readtime (&inline[i+1])) { /* read time input and */
ctime.hour = rtime.hour; /* store in 'ctime' */
ctime.min = rtime.min;
ctime.sec = rtime.sec;
}
break;
case 'E': /* Set End Time Command */
if (readtime (&inline[i+1])) { /* read time input and */
end.hour = rtime.hour; /* store in 'end' */
end.min = rtime.min;
end.sec = rtime.sec;
}
break;
case 'S': /* Set Start Time Command */
if (readtime (&inline[i+1])) { /* read time input and */
start.hour = rtime.hour; /* store in 'start' */
start.min = rtime.min;
start.sec = rtime.sec;
}
break;
default: /* Error Handling */
printf (menu); /* display command menu */
break;
}
}
}
/******************************************************************************/
/* signalon: check if clock time is between start and end */
/******************************************************************************/
static bit signalon (void) {
if (memcmp (&start, &end, sizeof (struct time)) < 0) {
if (memcmp (&start, &ctime, sizeof (struct time)) < 0 &&
memcmp (&ctime, &end, sizeof (struct time)) < 0) return (1);
}
else {
if (memcmp (&end, &ctime, sizeof (start)) > 0 &&
memcmp (&ctime, &start, sizeof (start)) > 0) return (1);
}
return (0); /* signal off, blinking on */
}
/******************************************************************************/
/* Task 3 'blinking': runs if current time is outside start & end time */
/******************************************************************************/
void blinking (void) _task_ BLINKING { /* blink yellow light */
red = 0; /* all lights off */
yellow = 0;
green = 0;
stop = 0;
walk = 0;
while (1) { /* endless loop */
yellow = 1; /* yellow light on */
os_wait (K_TMO, 150, 0); /* wait for timeout: 150 ticks */
yellow = 0; /* yellow light off */
os_wait (K_TMO, 150, 0); /* wait for timeout: 150 ticks */
if (signalon ()) { /* if blinking time over */
os_create_task (LIGHTS); /* start lights */
os_delete_task (BLINKING); /* and stop blinking */
}
}
}
/******************************************************************************/
/* Task 4 'lights': executes if current time is between start & end time */
/******************************************************************************/
void lights (void) _task_ LIGHTS { /* traffic light operation */
red = 1; /* red & stop lights on */
yellow = 0;
green = 0;
stop = 1;
walk = 0;
while (1) { /* endless loop */
os_wait (K_TMO, 150, 0); /* wait for timeout: 150 ticks */
if (!signalon ()) { /* if traffic signal time over */
os_create_task (BLINKING); /* start blinking */
os_delete_task (LIGHTS); /* stop lights */
}
yellow = 1;
os_wait (K_TMO, 150, 0); /* wait for timeout: 150 ticks */
red = 0; /* green light for cars */
yellow = 0;
green = 1;
os_clear_signal (LIGHTS);
os_wait (K_TMO, 200, 0); /* wait for timeout: 200 ticks */
os_wait (K_TMO + K_SIG, 250, 0); /* wait for timeout & signal */
yellow = 1;
green = 0;
os_wait (K_TMO, 150, 0); /* wait for timeout: 150 ticks */
red = 1; /* red light for cars */
yellow = 0;
os_wait (K_TMO, 150, 0); /* wait for timeout: 150 ticks */
stop = 0; /* green light for walkers */
walk = 1;
os_wait (K_TMO, 250, 0); /* wait for timeout: 250 ticks */
os_wait (K_TMO, 250, 0); /* wait for timeout: 250 ticks */
stop = 1; /* red light for walkers */
walk = 0;
}
}
/******************************************************************************/
/* Task 5 'keyread': process key stroke from pedestrian push button */
/******************************************************************************/
void keyread (void) _task_ KEYREAD {
while (1) { /* endless loop */
if (key) { /* if key pressed */
os_send_signal (LIGHTS); /* send signal to task lights */
}
os_wait (K_TMO, 2, 0); /* wait for timeout: 2 ticks */
}
}
/******************************************************************************/
/* */
/* SERIAL.C: Interrupt Controlled Serial Interface for RTX-51 tiny */
/* */
/******************************************************************************/
#include <REG932.H> /* special function register 8052 */
#include <rtx51tny.h> /* RTX-51 tiny functions & defines */
#define OLEN 8 /* size of serial transmission buffer */
unsigned char ostart; /* transmission buffer start index */
unsigned char oend; /* transmission buffer end index */
idata char outbuf[OLEN]; /* storage for transmission buffer */
unsigned char otask = 0xff; /* task number of output task */
#define ILEN 8 /* size of serial receiving buffer */
unsigned char istart; /* receiving buffer start index */
unsigned char iend; /* receiving buffer end index */
idata char inbuf[ILEN]; /* storage for receiving buffer */
unsigned char itask = 0xff; /* task number of output task */
#define CTRL_Q 0x11 /* Control+Q character code */
#define CTRL_S 0x13 /* Control+S character code */
static bit sendfull; /* flag: marks transmit buffer full */
static bit sendactive; /* flag: marks transmitter active */
static bit sendstop; /* flag: marks XOFF character */
/******************************************************************************/
/* putbuf: write a character to SBUF or transmission buffer */
/******************************************************************************/
static void putbuf (char c) {
if (!sendfull) { /* transmit only if buffer not full */
ES = 0; /* disable serial interrupt */
if (!sendactive && !sendstop) { /* if transmitter not active: */
sendactive = 1; /* transfer the first character direct */
SBUF = c; /* to SBUF to start transmission */
}
else { /* otherwize: */
outbuf[oend++ & (OLEN-1)] = c; /* transfer char to transmission buffer */
if (((oend ^ ostart) & (OLEN-1)) == 0) sendfull = 1;
} /* set flag if buffer is full */
ES = 1; /* enable serial interrupt */
}
}
/******************************************************************************/
/* putchar: interrupt controlled putchar function */
/******************************************************************************/
char putchar (char c) {
if (c == '\n') { /* expand new line character: */
while (sendfull) { /* wait for transmission buffer empty */
otask = os_running_task_id (); /* set output task number */
os_wait (K_SIG, 0, 0); /* RTX-51 call: wait for signal */
otask = 0xff; /* clear output task number */
}
putbuf (0x0D); /* send CR before LF for <new line> */
}
while (sendfull) { /* wait for transmission buffer empty */
otask = os_running_task_id (); /* set output task number */
os_wait (K_SIG, 0, 0); /* RTX-51 call: wait for signal */
otask = 0xff; /* clear output task number */
}
putbuf (c); /* send character */
return (c); /* return character: ANSI requirement */
}
/******************************************************************************/
/* _getkey: interrupt controlled _getkey */
/******************************************************************************/
char _getkey (void) {
while (iend == istart) {
itask = os_running_task_id (); /* set input task number */
os_wait (K_SIG, 0, 0); /* RTX-51 call: wait for signal */
itask = 0xff; /* clear input task number */
}
return (inbuf[istart++ & (ILEN-1)]);
}
/******************************************************************************/
/* serial: serial receiver / transmitter interrupt */
/******************************************************************************/
void serial (void) interrupt 4 using 2 { /* use registerbank 2 for interrupt */
unsigned char c;
bit start_trans = 0;
if (RI) { /* if receiver interrupt */
c = SBUF; /* read character */
RI = 0; /* clear interrupt request flag */
switch (c) { /* process character */
case CTRL_S:
sendstop = 1; /* if Control+S stop transmission */
break;
case CTRL_Q:
start_trans = sendstop; /* if Control+Q start transmission */
sendstop = 0;
break;
default: /* read all other characters into inbuf */
if (istart + ILEN != iend) {
inbuf[iend++ & (ILEN-1)] = c;
}
/* if task waiting: signal ready */
if (itask != 0xFF) isr_send_signal (itask);
break;
}
}
if (TI || start_trans) { /* if transmitter interrupt */
TI = 0; /* clear interrupt request flag */
if (ostart != oend) { /* if characters in buffer and */
if (!sendstop) { /* if not Control+S received */
SBUF = outbuf[ostart++ & (OLEN-1)]; /* transmit character */
sendfull = 0; /* clear 'sendfull' flag */
/* if task waiting: signal ready */
if (otask != 0xFF) isr_send_signal (otask);
}
}
else sendactive = 0; /* if all transmitted clear 'sendactive' */
}
}
/******************************************************************************/
/* serial_init: initialize serial interface */
/******************************************************************************/
void serial_init (void) {
P1M1 = 0xFE; // Configure P1.0 (TxD) as Output
SCON = 0x52; /* initialize UART */
BRGR0 = 0xF0; /* 9600 baud, 8 bit, no parity, 1 stop bit */
BRGR1 = 0x02;
BRGCON = 0x03;
}
/******************************************************************************/
/* */
/* GETLINE.C: Line Edited Character Input */
/* */
/******************************************************************************/
#include <stdio.h>
#define CNTLQ 0x11
#define CNTLS 0x13
#define DEL 0x7F
#define BACKSPACE 0x08
#define CR 0x0D
#define LF 0x0A
/***************/
/* Line Editor */
/***************/
void getline (char idata *line, unsigned char n) {
unsigned char cnt = 0;
char c;
do {
if ((c = _getkey ()) == CR) c = LF; /* read character */
if (c == BACKSPACE || c == DEL) { /* process backspace */
if (cnt != 0) {
cnt--; /* decrement count */
line--; /* and line pointer */
putchar (0x08); /* echo backspace */
putchar (' ');
putchar (0x08);
}
}
else if (c != CNTLQ && c != CNTLS) { /* ignore Control S/Q */
putchar (*line = c); /* echo and store character */
line++; /* increment line pointer */
cnt++; /* and count */
}
} while (cnt < n - 1 && c != LF); /* check limit and line feed */
*line = 0; /* mark end of string */
}
|