打印

51串口非堵塞收发程序

[复制链接]
2849|11
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
icecut|  楼主 | 2007-6-29 14:09 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
*------------------------------------------------
本程序适用于学习交流,对读入一个字节(不带回车)读入一个字符串带回车,发送字符串函数测试成功
由于不清楚keil自带的STDIO.H函数是否是堵塞发送接受,所以本程序使用非堵塞方式发送接受,缺点是占
用一些内存,缓冲区大小可自由配置。
如果发现BUG请发送邮件到:
我的邮箱:jiangyichun1@sina.com
    jiangyichun1@gmail.com
  我的名字+数字1,请复制,防止抄错。
我的BLOG:http://lovelytime.21ic.org

testcom.c file

介绍:由于本人08年毕业(北京——机械工业学院(bim)单外生源),有必要在最后一年整理一些东西
.串口收发例程在网上有但不多,堵塞式收发,占用CPU时间太多,本程序用中断收发,需要时读缓冲区获得数据,
本程序免费使用自由修改!如有BUG请到我BLOG上注明,谢谢!
-------------------------------------------------*/
#i nclude <reg52.h>

#define max 20//定义缓冲区大小
struct comset{
char tcombuffer[max];//发送缓冲区:环状
char rcombuffer[max];//接收缓冲区
char rcount,tcount;//收发的有效数据数
char rstart,rend,tstart,tend;//当前读写起始与结束位置
char issent;//正在发送过程中1:发送,0未发送
};

struct comset a;


void inibuffer(void)//初始化缓冲区,所有数据置0或初值
{
char i;
for(i=0;i<max;i++)
  {a.rcombuffer=0;a.tcombuffer=0;}
a.tcount=0;a.rcount=0;a.rstart=0;a.rend=0;a.tstart=0;a.tend=0;a.issent=0;
}

void comini(void)
{

SCON  = 0x50;       //SCON: serail mode 1, 8-bit UART, enable ucvr 
    TMOD |= 0x20;       //TMOD: timer 1, mode 2, 8-bit reload 
       //SMOD=0; 
    TH1   = 0xFd;       //Baud:9600  fosc=11.0592MHz 
TL1   = 0xF4;
    IE   |= 0x90;       //Enable Serial Interrupt 
    TR1   = 1;          // timer 1 run 

}

char sent(char *b,char n)//发送的字节数=发送(字符串指针,字符个数)
{
char i;
if(n>max-a.tcount)//缓冲区字节不够拒绝发送返回0
{return 0;}
for(i=0;i<n;i++)
{
  a.tcombuffer[a.tend]=b;//向缓冲区尾部拷贝字符
  a.tend++;//尾部指针++
  a.tcount++;//缓冲区总字节数++
  if(a.tend>max)//循环利用缓冲区
   a.tend=0;
  if(a.tcount>=max)//达到最大值就退出(单线程程序不会达到最大值)
   break;

}
if(a.issent==0)//如果不在发送状态就开始发送
{
SBUF=a.tcombuffer[a.tstart];//发送第一个字节
a.tstart++;//其实字节后移
a.issent=1;//发送标志置一
a.tcount--;//总字节--
}
return i;//返回复制到缓冲区的字节数
}

void sentoff(void)//等待发送完成,也可以发送开始就执行代码以提高效率,有些交互式需要等待发送完成才能读取输入。
{
while(a.tcount!=0);//等待发送总数为0


char getchar(void)//从缓冲区读取一个字节
{
char e;
if(a.rcount>0)
{
e=a.rcombuffer[a.rstart];//从开头读,要求所有输入字节都有对应的函数处理
a.rstart++;
a.rcount--;
return e; 
}
return 0;
}

char getstring(char *c,char oeb)//返回值是读的字节数,oeb:允许输出最大字节数,c是读取字节存放的位置
{
char k,l=0;
sentoff();//等待发送完毕,这样才有有效输入
while(a.rstart!=(a.rend+1)%(max+1))//缓冲区不满
{
for(k=a.rstart;k!=a.rend;k++,k=k%(max+1))//判断输入完的回车是否读入
{
  
  if(a.rcombuffer[k]=='\r')
  {
   while(a.rcombuffer[a.rstart]!='\r')//有则拷贝
   {
    c[l]=a.rcombuffer[a.rstart];
    a.rstart++;
    a.rcount--;
    l++;
    if(l==oeb)//超过允许最大值就返回
    return l;
   }
   //a[l]=a.rcombuffer[a.rstart];//不复制回车符
   a.rstart++;//删除回车
   a.rcount--;
   return l;
  }
}
}
//缓冲区满
while(sent("\nerror:full\n",12)!=12);//会留在缓冲区未读字节,所以输出错误提示
for(k=a.rstart;k<oeb;k++,k=(k)%(max+1))//返回最大有效的 字符数注意:OEB《max
{
  c[l++]=a.rcombuffer[k];
}
sentoff();//等到提示发送完毕
inibuffer();//初始化缓冲区,删除垃圾,由于发送较慢,有可能还有垃圾存在
return l;



}

void serial () interrupt 4 using 3 
{
    if(RI)
    {
  RI=0;
     if(a.rcount<max)//缓冲区满后字节被丢弃
  {
   a.rcount++;
   
   a.rcombuffer[a.rend]=SBUF;//不满则读入
   a.rend++;
   if(a.rend>max)//循环利用缓冲区
    a.rend=0;
  }
    }
if(TI)
{
  TI=0;
  if(a.tcount>0)//如有数据则发送,并设置相关标志字
  {
   SBUF=a.tcombuffer[a.tstart];
   a.tstart++;
   if( a.tstart>max)
    a.tstart=0;
   a.tcount--;
  }
  else
   a.issent=0;//否则设置停止发送字
}
}

void main()//测试函数
{
char f,n[20],k=0;

comini();
inibuffer();

while(sent("my test\r\n",9)!=9);
sentoff();
while(sent("1.inset your name.\r\n",20)!=20);
while(sent("2.inset your age.\r\n",19)!=19);

while((f=getchar())==0);//输入字符不回显,不要输入回车
switch(f)
{ case '1': while(sent("inset your name:",16)!=16);

    k=getstring(n,20);//输入字符串不回显,需要回车结束
    while(sent(n,k)!=k);//回显
    break;
  case '2':
    while(sent("age:",4)!=4);
    k=getstring(n,2);
    while(sent(n,k)!=k);
    break;

while(1);//停机


}




相关链接:http://blog.21ic.com/uploadfile-/2007-6/629788290.rar

相关帖子

沙发
HotPower| | 2007-6-30 04:31 | 只看该作者

只要遇到while()的字样估计效率不会太高~~~

使用特权

评论回复
板凳
xwj| | 2007-6-30 08:44 | 只看该作者

一大堆while,这还叫“非堵塞”啊???

使用特权

评论回复
地板
isbit| | 2007-6-30 16:14 | 只看该作者

楼主,我用16M晶振怎么跑不了?

 TH1   = 0xFd;       //Baud:9600  fosc=11.0592MHz
TL1   = 0xF4;

 TIMER1 MODM2 TL1数值从TH1中自动取用,
我的16M晶体,我计算为th1=0xf7
怎么没有数据?

使用特权

评论回复
5
古道热肠| | 2007-6-30 16:26 | 只看该作者

思路可取,风格可学

   但细看代码,效率有待提高,51单片机少用或不用无符号数,应使用unsigned char 来代替char ,楼主务必注意这一点。
   串口收发操作在中断中完成,且采用结构体来规划变量,这一点是值得学习的,好好的规范一下,通用性有待提高。

使用特权

评论回复
6
icecut|  楼主 | 2007-6-30 17:56 | 只看该作者

关于while()的解释

关于while()大家想一下,我这里提供的是函数,为什么用while()
是因为如果缓冲区不够需要等待缓冲区够了,解决办法是把缓冲区放大一点或者发送速度快一些,都能解决一次成功问题。由于这里是于串口助手进行人机交互,当然,这里还不需要特别重要的考虑时间问题,这里我提出过是非堵塞式,就是while的返回问题!!特别注意,while在所有字节都放进缓冲区并且发送第一个字节时返回,并不是所有都发送完毕后返回,这种方法我没有见过,所以才这么写,还有如果KEILc的printf是利用堆栈当缓冲区的话,他的速度和我的一样快,我没有测试,所以还要等待高手下结论。其实测试方法也很简单,就是在printf后直接加一初始化串口函数,禁用串口,非堵塞式发送就会立即失败,而堵塞式程序上会在发送完毕才执行初始化所以能发送成功。
while(sent("my test\r\n",9)!=9);
sentoff();
这两句就等于堵塞式发送因为它等待发送完成后再继续执行
while(sent("1.inset your name.\r\n",20)!=20);
由于缓冲区是20字节,所以这条语句带不带WHILE都一样
while(sent("2.inset your age.\r\n",19)!=19);
由于上句发20字节,缓冲区满,所以如此量大的数据无论如何变成都是要等待的,或者初始化缓冲区40字节即可解决等待问题,但是单片机RAM宝贵。所以这句话重新让上一句的效率等同于堵塞式发送。只是提前一个字节的发送时间结束。如果这两句话之间有很多语句,执行时间超过了发送整个缓冲区用的时间,他的优势就能体现出来。

使用特权

评论回复
7
icecut|  楼主 | 2007-6-30 18:07 | 只看该作者

怎么没有数据?

关于这个你用调试助手的16禁制显示,你发送的字节数如果和接收的字节数不同,可能是波特率定时有问题。如果根本收不到,估计是其他问题。还有,我只在仿真和STC89C52下测试,所以请大家先仿真,ok再烧写

使用特权

评论回复
8
icecut|  楼主 | 2007-6-30 18:09 | 只看该作者

unsigned char

谢谢指导,我也是第一次认真写程序
学习中……

使用特权

评论回复
9
农民讲习所| | 2007-6-30 21:09 | 只看该作者

转变思路,用状态转移方式处理发送的字符串

这样可以把时间出让给其它程序。

使用特权

评论回复
10
高建明| | 2007-7-1 10:19 | 只看该作者

严重同意老农意见

使用特权

评论回复
11
conwh| | 2007-7-1 13:39 | 只看该作者

有小虫

使用特权

评论回复
12
icecut|  楼主 | 2007-7-1 16:29 | 只看该作者

虫子在哪?还是你指出来吧

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

1120

主题

15358

帖子

585

粉丝