20.2.5 串行通信及命令获取子函数

在SERIAL_CommunI2Cation.c程序文件中,主要用于处理串行接口的中断数据通信。其中包含了基本的串行口初始化、字符输入输出以及串行中断处理等。程序中预定义了一些变量,示例如下。


include<reg52.h>//头文件

include<stdio.h>

include<rtx51tny.h>

define uchar unsigned char//宏定义

define OUTBUF_LEN 8//串行口发送缓冲区的长度

define INBUF_LEN 8//串行口接收缓冲区的长度

define CTRL_Q 0x11

define CTRL_S 0x13

bit Full;//发送缓冲区满标志位

bit Active;//发送激活标志位

bit Stop;//发送停止标志位

uchar bufout_start;//串行口发送缓冲区的起点

uchar bufout_end;//串行口发送缓冲区的终点

idata

char outbuf[OUTBUF_LEN];//串行口发送缓冲区

uchar out_task=0xff;//串行口输出任务的任务号

uchar bufin_start;//串行口接收缓冲区的起点

uchar bufin_end;//串行口接收缓冲区的终点

idata char inbuf[INBUF_LEN];//串行口接收缓冲区

uchar in_task=0xff;//串行口输入任务的任务号


程序中用到的各个串行数据通信子函数如下。

1.缓冲区写子函数

缓冲区写子函数用于向8051单片机串口的SBUF或者发送缓冲区写入字符。在程序中,首先判断发送缓冲区满标志位Full,如果其未满则直接启动发送字符,否则向定义的发送缓冲区传送字符。缓冲区写子函数的示例代码如下。


void putbuf(char c)

{

if(!Full)//判断发送缓冲区满标志位

{

if(!Active&&!Stop)//缓冲区未满,发送器未处于活动状态

{

Active=1;

SBUF=c;//直接发送第一个字符到SBUF,并启动发送

}

else

{

outbuf[bufout_end++&(OUTBUF_LEN-1)]=c;//向定义的发送缓冲区传送字符

if(((bufout_end^bufout_start)&(OUTBUF_LEN-1))==0)

Full=1;//缓冲区满标志置位

}

}

}


2.字符发送子函数

字符发送子函数用于通过8051单片机的串行口发送字符。在程序中,使用while循环等待缓冲区满标志,然后调用缓冲区写子函数发送字符。该函数的返回值为发送的字符。字符发送子函数的示例代码如下。


char putchar(char c)

{

if(c==‘\n’)//扩展一行新字符

{

while(Full)//如果发送缓冲区满,则等待

{

out_task=os_running_task_id();//置位输出任务号

os_wait(K_SIG,0,0);//等待信号

out_task=0xff;//输出任务号清零

}

putbuf(0x0D);//在LF换行符之前发送CR回车符

}

while(Full)//如果发送缓冲区满,则等待

{

out_task=os_running_task_id();//置位输出任务号

os_wait(K_SIG,0,0);//等待信号

out_task=0xff;//输出任务号清零

}

putbuf(c);//发送字符

return c;//返回字符

}


3.输入缓冲区读子函数

输入缓冲区读子函数用于获取串行口输入缓冲区的值。在程序中,通过while语句判断发送缓冲区的起点bufin_start和终点bufin_end是否一致。如果一致,则表示输入缓冲区为空,等待;否则将通过return语句返回数值。输入缓冲区读子函数的示例代码如下。


char_getkey(void)

{

char crt;

while(bufin_end==bufin_start)//等待

{

in_task=os_running_task_id();//置位输入任务号

os_wait(K_SIG,0,0);//等待信号

in_task=0xff;//输入任务号清零

}

crt=inbuf[bufin_start++&(INBUF_LEN-1)];

return crt;//返回值

}


4.串行口初始化子函数

串行口初始化子函数用于初始化8051单片机的串行接口。在程序中,首先初始化串行口为方式1,即8位UART方式。接着,初始化定时器T1为自动重装方式,设置计数初值,并启动定时器T1,允许串行口中断。串行口初始化子函数的示例代码如下。


void init_serial(void)

{

SCON=0x50;//设置串行口:方式1,8位UART,允许接收

TMOD=0x20;//设置定时器T1,方式2,8位自动重装

PCON=0x80;//设置SMOD=1

TL1=0xF4;//波特率4800bit/s,初值

TH1=0xF4;

TR1=1;//启动定时器T1

ES=1;//允许串行口中断

TI=1;

}


5.串行接收/发送中断处理函数

串行接收/发送中断处理函数serial用于处理串行口的中断,从而实现数据发送和接收。该中断处理函数使用工作寄存器组2,程序中使用if语句来判断是接收中断还是发送中断。如果RI置1,则表示接收中断,程序中在switch语句进行字符处理;如果TI置1,则表示发送中断,首先清零中断请求标志TI,然后通过SBUF发送字符。串行接收/发送中断处理函数serial的示例代码如下。


void serial()interrupt 4 using 2//中断响应函数

{

unsigned char c;

bit start_trans=0;

if(RI)//RI置1,接收中断

{

c=SBUF;//读SBUF

RI=0;//中断请求标志RI清零

switch(c)//字符处理

{

case CTRL_S://如果是CTRL_S

Stop=1;//置位Stop,停止发送

break;

case CTRL_Q://如果是CTRL_Q

start_trans=Stop;//开始发送

Stop=0;

break;

default://对于其他字符,则读入输入缓冲区

if(bufin_start+INBUF_LEN!=bufin_end)

{

inbuf[bufin_end++&(INBUF_LEN-1)]=c;

}

if(in_task!=0xFF)//如果是任务等待

isr_send_signal(in_task);//发送信号

break;

}

}

if(TI||start_trans)//TI置位,发送中断

{

TI=0;//中断请求标志TI清零

if(bufout_start!=bufout_end)//如果输入缓冲区接收到字符

{

if(!Stop)

{//发送字符

SBUF=outbuf[bufout_start++&(OUTBUF_LEN-1)];

Full=0;//Full标志清零

if(out_task!=0xFF)//如果是任务等待

isr_send_signal(out_task);//发送信号

}

}

else Active=0;//全部发送完,Active清零

}

}


6.命令获取子函数

在GETCOMMAND.C程序文件中,包含了命令获取子函数,用于编辑从串口接收到的字符。在程序中,通过_getkey函数获取串行口输入的命令,然后对不同的命令进行不同的处理。在程序中规定的命令键及其代码示例如下。

❑CNTLQ,对应的字符代码为0x11。

❑CNTLS,对应的字符代码为0x13。

❑DEL,对应的字符代码为0x7F。

❑BACKSPACE,对应的字符代码为0x08。

❑CR,对应的字符代码为0x0D。

❑LF,对应的字符代码为0x0A。

命令获取子函数的示例代码如下。


01:#include<stdio.h>//头文件

02:

03:#define CNTLQ 0x11//定义控制字符

04:#define CNTLS 0x13

05:#define DEL 0x7F

06:#define BACKSPACE 0x08

07:#define CR 0x0D

08:#define LF 0x0A

09:

10:void getline(char idata*line,unsigned char n)//命令编辑函数

11:{

12:unsigned char cnt=0;

13:char c;

14:

15:do//循环读取输入的命令

16:{

17:if((c=_getkey())==CR)//读入字符

18:c=LF;

19:if(c==BACKSPACE||c==DEL)//处理BACKSPACE键

20:{

21:if(cnt!=0)

22:{

23:cnt—;//减1

24:line—;

25:putchar(0x08);//回显backspace字符

26:putchar(‘’);

27:putchar(0x08);

28:}

29:}

30:else if(c!=CNTLQ&&c!=CNTLS)//忽略CNTL_Q字符和NTL_S字符

31:{

32:putchar(*line=c);//回显字符并保存

33:line++;//加1

34:cnt++;

35:}

36:}while(cnt<n-1&&c!=LF);//检查

37:

38:*line=0;//字符串结尾标志

39:}


提示第3~8行宏定义程序中要用到的键,第15~36行循环判断用户输入的字符,直到达到输入的总数或按回车键时退出循环。第17~18行判断若输入的是CR,则将其替换为LF,第19~29行判断若输入的是退格键BACKSPACE或删除键DELETE,则删除缓冲区中的一个字符。第30~35行判断若输入的不是CNTLQ或CNTLS,则将输入字符的保存缓冲字符的指针位置,并调整缓冲区指针。第38行在缓冲区最后添加字符串结尾标志。

以上所有的多任务程序及用到的子函数构成了整个道路交通灯控制系统,可以在开发环境中进行程序的仿真和编译。如果编译通过,则可以将程序写入硬件中执行,便可以模拟道路交通灯运行。