学习与思考之三,一个串口通讯程序
我曾做个一简单的软件锁,采用查询的办法与上位机通讯。上位机总控下位机的状态,每次通讯的字节数也是固定的,比较简单。
学习中碰到了一个用中断方式写的串口通讯程序,研讨时深觉其立意深远,构思巧妙,千丝百茧,奇正相间,无穷如天地,不绝如江河,繁复精巧,变化多方,一击两鸣,百呼千应,却又有迹可寻,有形可求,三复其言,回味无穷。
现将推求过程写在下面,荒谬之处敬请高手指正。
#include <AT89X52.H>
#include <stdio.h> //嵌套包含是必要的,本文件也是一个头文件
#define XTAL 11059200
#define baudrate 9600
#define OLEN 8
unsigned char ostart;
unsigned char oend;
char idata outbuf[OLEN]; //发送数据时用的数据缓冲区
#define ILEN 8
unsigned char istart;
unsigned char iend;
char idata inbuf[ILEN]; //接收数据时用的数据缓冲区
bit sendfull;
bit sendactive;
void com_isr(void) interrupt 4 using 1 //中断方法进行串口收发
{
char c;
if(RI)
{
c=SBUF;
RI=0;
if(istart+ILEN!=iend)
inbuf[iend++&(ILEN-1)]=c;
// 后面有一个_getkey()函数,你调用一次,则istart++。当上面的if成立时,则iend也++,很显然,这是此呼而彼的
//技法,递推而进
// _getkey()函数的功能是从inbuf[]中获得一个数,这是一个后台函数
// &(ILEN-1)非常重要,使数组的下标始终在0~7之间,确保这个赋值语句不破坏其它RAM,即保证数据指针终
//不越界,精巧!
// istart+ILEN与iend的关系很重要,二者相等时,接收的数据会被丢掉。相等的原因在于,在中断里收进来了八个
//数,但未及时 调用_getkey()函数,将其从数组中取走。
// _getkey()函数主要供scanf()函数驱策,不明白scanf()函数是如何运作的,但根据这个接收数据时的前后台
//程序机制,可以编写 如下字符串接收函数
// void gets(void){
// istart=0;
// iend=0;
// while(istart<=1) //假如接收两个字符
// _getkey();
// }
}
if(TI)
{
TI=0;
if(ostart!=oend)
// 两数不相等,表明写入发送数据缓冲区的字节数与通过串口发送出去的字节数不相等(不平衡),这样就需写
//一次串口缓冲区, 且将ostart+1,后台有一个putbuf()函数,在这个函数里,有oend++的机制,用以ostart
//平衡。
// 在接收时,中断函数是主动函数,后台函数是从动函数;发送时相反,后台函数是主动函数,中断函数从动函
//数,即由后台函数 触发中断函数。
// 有些书中将sendfull理解为用以标识为发送缓冲区满,窃以为非是,sendfull=1实质上表明串行发送瘫痪另,
//只要串口发送了 一个数据,则清零之,表明串口已经通畅。
{
SBUF=outbuf[ostart++&(OLEN-1)];
sendfull=0;
}
else
// 当两个指针(非真正的指针,比附也)相等时,清零sendactive,表明写入发送数据缓冲区的数据,全部由口
//发送出去了。
sendactive=0;
}
}
void putbuf(char c)
{
if(!sendfull)
// 如果串行发送瘫痪,则本函数不能有效执行
{
if(!sendactive)
// 在初始化或一个阶段的发送结束后,sendactive会被清零,这时再调用本函数,则将实参写入串口缓冲区,将
//引起连锁中断。
// 理想情况是随写随发;正常情况是虽不能来之则发,但可以写入缓冲区待发;糟糕的情况是,只进不出,形堵
//塞。作者思虑 周详,处置巧妙,任凭风浪起,自有安澜术。何等心机,何等肺肠,何等境界,何等手眼,何笔
//墨,尽造化之工,极神鬼之巧, 佩服!佩服!
// 由于测试其为零,则立即置位,所以接下来调用本函数,将执行另外的分支。
{
sendactive=1;
SBUF=c;
}
else
// 根据前面的分析,这个分支系由于执行了前一个分支后,置位sendactive,再次调用本函数时,则将流程引导此。
// 这个分支将实参写入发送数据缓冲区,且将指针加1
{
ES=0;
outbuf[oend++&(OLEN-1)]=c;
if(((oend^ostart)&(OLEN-1))==0 )
sendfull=1;
// 两个指针相同,条件成立,置位sendfull,表明串口已经瘫痪(拥堵)。指针相同的可能原因显然是,后台(主
//动)函数不停地 写数据到数据缓冲区,而串口却来不及发送,当oend计满(256)时继续计数,则二者有可能
//相等。
ES=1;
}
}
}
char putchar(char c) //本函数似乎是供printf()函数驱策用的
{
if(c=='\n')
{
while(sendfull);
putbuf(0x0d);
}
while(sendfull);
putbuf(c);
return (c);
}
char _getkey(void)
{
char c;
while(iend==istart);
// 相等表明串口缓冲区中没有接收到数据或接收到的数据已被完全取走,在此情况下调用本函数,则堵塞于此等
//待一个数据送入串口, 然后接收之。以下等桧无讥也。
ES=0;
c=inbuf[istart++&(ILEN-1)];
ES=1;
return (c);
}
void com_initialize(void)
{
TMOD|=0x20;
SCON=0x50;
TH1=0xfd;
TL1=0xfd;
TR1=1;
ES=1;
}
void uart_Init()
{
com_initialize();
EA=1;
} |