C51单片机串口通信之上位机交互

概述:上位机(PC机)向MCU发送指令,MCU串口接收指令并根据列表发送响应。

 

全局变量声明

#include

BYTE ComBuf[18];//串口通讯数据缓存,发送和接收都使用
UINT nAddress;//ROM中地址计数
UINT nTimeOut;//超时计数
ProWork pw;//编程器一般操作

 

1 定时器延时

1.1 微秒级

void Delay_us(BYTE nUs)//微秒级延时<255us
{
     TH0=0;
     TL0=0;
     TR0=1;
     while(TL0      TR0=0;
}

分析:

定时器初始值归零,自增1次约1us。

打开定时器中断,BYTE有效值为0~255,因此TL

最后关闭定时器中断。

1.2 毫秒级

void Delay_ms(UINT nMs)   //毫秒级的延时<65535ms
{
     UINT n=0;
     TR0=1;
     while(n      {
          TH0=0;
          TL0=20;
          while(TH0<4);

           n++;
      }

      TR0=0;

}

TH自增1,即TL自增255,故TH自增4次约1ms,其他同微秒级。

 

2 等待上位机命令

BOOL WaitComm() //等待上位机的命令,18字节
{
 BYTE n=0;
 RI=0;

//*****1******
 while(!RI){}   //等待第一个字节
 ComBuf[n]=SBUF;
 RI=0;
 n++;

 //*****2******
 for(n;n<=17;n++) 

{
  nTimeOut=0;
  while(!RI)
  {
     nTimeOut++;
     if(nTimeOut>10000)       return 0;     //后17个字节都有超时限制 
  }
  ComBuf[n]=SBUF;
  RI=0;
 }
 return 1;
}

**1** 上位机发送第一个字节,接收中断,接收字符存入ComBuf数组;软件置零,继续接收下一字节。

**2** 下一字节要若在超时计数nTimeOut自增超过1W,则返回0,不再接收;未超时,则把剩下17字节依次存入ComBuf数组。

 

3 等待上位机回应

BOOL WaitResp()//等待上位机回应,1字节,有超时限制
{
       nTimeOut=0;
       RI=0;
       while(!RI)
       {
            nTimeOut++;
            if(nTimeOut>50000)
            {
                  return 0;
            }
        }
       RI=0;
       ComBuf[0]=SBUF;
       return 1;
}

上位机在5Wus内回应,存入ComBuf。

 

4 写器件等待上位机,与等待上位机命令有区别

BOOL WaitData()    //写器件时等待上位机数据,18字节,有超时限制
{
        BYTE n;
        RI=0;
        for(n=0;n<=17;n++)
        {
             nTimeOut=0;
             while(!RI)
             {
                 nTimeOut++;
                 if(nTimeOut>10000)
                 {
                     return 0;
                 }
             }
             RI=0;
             ComBuf[n]=SBUF;
        }
        return 1;
}

所有18字节均有超时限制

 

6 把Com数组内容发送到上位机,回应操作完成

void SendData()   //发送数据或回应操作完成,18字节
{
         BYTE n=0;
         for(n;n<=17;n++)
         {
               TI=0;
               SBUF=ComBuf[n];
               while(!TI){}
               TI=0;
         }
}

 

7  写器件对上位机回应

void SendResp()//回应上位机1个字节,在写器件函数中使用
{
         TI=0;
         SBUF=ComBuf[0];
         while(!TI){}
         TI=0;
}

 

8 读特征字

void ReadSign()  //读特征字
{
      pw.fpReadSign();
      SendData();   //通知上位机,送出读出器件特征字
}

读取PW特殊寄存器

 

 

 

 

附一:源码

//Easy 51Pro编程器主程序,负责通讯,管理编程操作
///

#include

BYTE ComBuf[18];//串口通讯数据缓存,发送和接收都使用
UINT nAddress;//ROM中地址计数
UINT nTimeOut;//超时计数
ProWork pw;//编程器一般操作

void Delay_us(BYTE nUs)//微秒级延时<255us
{
TH0=0;
TL0=0;
TR0=1;
while(TL0 {
}
TR0=0;
}

void Delay_ms(UINT nMs)//豪秒级的延时<65535ms
{
UINT n=0;
TR0=1;
while(n {
TH0=0;
TL0=20;
while(TH0<4)
{
}
n++;
}
TR0=0;
}

BOOL WaitComm()//等待上位机的命令,18字节
{
BYTE n=0;
RI=0;
while(!RI){}//等待第一个字节
ComBuf[n]=SBUF;
RI=0;
n++;
for(n;n<=17;n++)
{
nTimeOut=0;
while(!RI)
{
   nTimeOut++;
   if(nTimeOut>10000)//后17个字节都有超时限制
    return 0;
}
ComBuf[n]=SBUF;
RI=0;
}
return 1;
}

BOOL WaitResp()//等待上位机回应,1字节,有超时限制
{
nTimeOut=0;
RI=0;
while(!RI)
{
nTimeOut++;
if(nTimeOut>50000)
{
   return 0;
}
}
RI=0;
ComBuf[0]=SBUF;
return 1;
}

BOOL WaitData()//写器件时等待上位机数据,18字节,有超时限制
{
BYTE n;
RI=0;
for(n=0;n<=17;n++)
{
nTimeOut=0;
while(!RI)
{
   nTimeOut++;
   if(nTimeOut>10000)
   {
    return 0;
   }
}
RI=0;
ComBuf[n]=SBUF;
}
return 1;
}

void SendData()//发送数据或回应操作完成,18字节
{
BYTE n=0;
for(n;n<=17;n++)
{
TI=0;
SBUF=ComBuf[n];
while(!TI){}
TI=0;
}
}

void SendResp()//回应上位机1个字节,在写器件函数中使用
{
TI=0;
SBUF=ComBuf[0];
while(!TI){}
TI=0;
}

void SetVpp5V()//设置Vpp为5v
{
P3_4=0;
P3_3=0;
}

void SetVpp0V()//设置Vpp为0v
{
P3_3=0;
P3_4=1;
}

void SetVpp12V()//设置Vpp为12v
{
P3_4=0;
P3_3=1;
}

void RstPro()//编程器复位
{
pw.fpProOver();//直接编程结束
SendData();//通知上位机,表示编程器就绪,可以直接用此函数因为协议号(ComBuf[0])还没被修改,下同
}

void ReadSign()//读特征字
{
pw.fpReadSign();
SendData();//通知上位机,送出读出器件特征字
}

void Erase()//擦除器件
{
pw.fpErase();
SendData();//通知上位机,擦除了器件
}

void Write()//写器件
{
BYTE n;
pw.fpInitPro();//编程前的准备工作
SendData();//回应上位机表示进入写器件状态,可以发来数据
while(1)
{
if(WaitData())//如果等待数据成功
{
   if(ComBuf[0]==0x07)//判断是否继续写
   {
    for(n=2;n<=17;n++)//ComBuf[2~17]为待写入数据块
    {
     if(!pw.fpWrite(ComBuf[n]))//<<<<<<<<<<<<<<<<<<<调用写该器件一个单元的函数
     {
      pw.fpProOver();//出错了就结束编程
      ComBuf[0]=0xff;
      SendResp();//回应上位机一个字节,表示写数据出错了
      WaitData();//等待上位机的回应后就结束
      return;
     }
     nAddress++;//下一个单元
    }
    ComBuf[0]=1;//回应上位机一个字节,表示数据块顺利完成,请求继续
    SendResp();
   }
   else if(ComBuf[0]==0x00)//写器件结束
    break;
   else//可能是通讯出错了
   {
    pw.fpProOver();
    return;
   }
}
else//等待数据失败
{
   pw.fpProOver();
   return;
}
}
pw.fpProOver();//编程结束后的工作
Delay_ms(50);//延时等待上位机写线程结束
ComBuf[0]=0;//通知上位机编程器进入就绪状态
SendData();
}

void Read()//读器件
{
BYTE n;
pw.fpInitPro();//先设置成编程状态
SendData();//回应上位机表示进入读状态
while(1)
{
if(WaitResp())//等待上位机回应1个字节
{
   if(ComBuf[0]==0)//ComBuf[0]==0表示读结束
   {
    break;
   }
   else if(ComBuf[0]==0xff)//0xff表示重发
   {
    nAddress=nAddress-0x0010;
   }
   for(n=2;n<=17;n++)//ComBuf[2~17]保存读出的数据块
   {
    ComBuf[n]=pw.fpRead();//<<<<<<<<<<<<<<<<<<<调用写该器件一个单元的函数
    nAddress++;//下一个单元
   }
   ComBuf[0]=6;//向上位机发送读出的数据块
   SendData();
}
else
   break;//等待回应失败

}
pw.fpProOver();//操作结束设置为运行状态
ComBuf[0]=0;//通知上位机编程器进入就绪状态
SendData();
}

void Lock()//写锁定位
{
pw.fpLock();
SendData();
}

///
//所支持的FID,请在这里继续添加
///
extern void PreparePro00();//FID=00:AT89C51编程器
extern void PreparePro01();//FID=01:AT89C2051编程器
extern void PreparePro02();//FID=02:AT89S51编程器

void main()
{
SP=0x60;
SetVpp5V();//先初始化Vpp为5v
SCON=0x00;
TCON=0x00;
//PCON=0x00;//波特率*2
IE=0x00;
//TMOD: GATE|C/!T|M1|M0|GATE|C/!T|M1|M0
//        0    0   1 0   0    0   0 1
TMOD=0x21;//T0用于延时程序
TH1=0xff;
TL1=0xff;//波特率28800*2,注意PCON
//SCON: SM0|SM1|SM2|REN|TB8|RB8|TI|RI
    //        0   1   0   1   0   0 0 0
SCON=0x50;
TR1=1;
Delay_ms(1000);//延时1秒后编程器自举
ComBuf[0]=0;
SendData();
while(1)//串口通讯采用查询方式
{
if(!WaitComm())//如果超时,通讯出错
{
   Delay_ms(500);
   ComBuf[0]=0;//让编程器复位,使编程器就绪
}
switch(ComBuf[1])//根据FID设置(ProWork)pw中的函数指针
{
   case 0://at89c51编程器
    PreparePro00();
    break;
   case 1://at89c2051编程器
    PreparePro01();
    break;
   case 2://at89s51编程器
    PreparePro02();
    break;
   //case 3:支持新器件时,请继续向下添加
   // break;
   //case 4:
   // break;
   default:
    ComBuf[0]=0xff;
    ComBuf[1]=0xff;//表示无效的操作
    break;
}
switch(ComBuf[0])//根据操作ID跳到不同的操作函数
{
   case 0x00:
    RstPro();//编程器复位
    break;
   case 0x01:
    ReadSign();//读特征字
    break;
   case 0x02:
    Erase();//擦除器件
    break;
   case 0x03:
    Write();//写器件
    break;
   case 0x04:
    Read();//读器件
    break;
   case 0x05:
    Lock();//写锁定位
    break;
   default:
    SendData();
    break;
}
}
}

 

 

 

你可能感兴趣的:(C51单片机串口通信之上位机交互)