单片机 > 单片机程序设计 > 详情

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

发布时间:2020-06-08 发布时间:
|

概述:上位机(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;

}

}

}



『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
PIC单片机基础知识之二