注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

东月之神

在单纯的观念里面,生命就容易变得比较深刻!

 
 
 

日志

 
 
关于我

别驻足,梦想要不停追逐,别认输,熬过黑暗才有日出,要记住,成功就在下一步,路很苦,汗水是最美的书!

网易考拉推荐

远程温度采集系统  

2011-01-29 13:47:38|  分类: 我的单片机 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

 

使用proteus作为开发工具,完成双机通讯的温度采集功能:

一、            使用LM20温度传感器、AD1674模数转换器、AT89C51单片机、4位拨码开关组成温度采集模块(从机),拨码开关用于设定模块的地址(00-15),从机在采集完温度信号(间隔5S)后通过串口发送到主机;

二、            使用AT89C51单片机、4位拨码开关、LED显示组成温度接收模块(主机),4位拨码开关用于设定通讯波特率(1200,2400,4800,9600)当主机波特率改变后,从机波特率也自动改变,上电运行显示波特率信息(停3S),之后显示接收地址与温度信息,当接收时间间隔超过6S未收到数据,系统报警;

三、            单片机一次只能发送一个字节的信息,使用多字节数据组成一帧数据,自定义一套通信协议来完成一、二的功能;

扩展要求:

       将双机通信改成多机通讯(一个接收器,多个发送器)

 

 

远程温度采集系统 - 东月之神 - 东月之神

 

(1)LM20AD1674转换为12位数据给从机

LM20温度传感器经过电压跟随器后输入到AD167410VIN口,AD1674组成双峰。STSCSA0CERC分别接到从机的P0-P4口实现AD1674的控制作用。一开始CE=1,CS=0,RC=0,A0=0启动12位温度转换,然后等待数据采集结束while(STS==1); 接着CE=1,CS=0,RC=1,12/8=1,A0=0允许高八位数据并行输出,最后CE=1,CS=0,RC=1,12/8=0,A0=1允许低四位数据并行输出。读出的12位数据存放在变量temp中。经过AD1674转换后输出的结果精确到(10.0/4095.0)。若LM20温度转换为电压值是1.13598v时,经过AD1674转换后输出的12位数为1.13598*4095/10=465转换为2进制为00011101000112位数据经过公式转换T = 1.8525-temp*10/4095.0*10000/11.79.然后把低位小数位给temp1temp就为整数部分,接着就是等待发送给主机。

/*读取AD1674转换结果*/

uint AD1674_Read(void)

{

    uint temp;

    uchar temp1,temp2;

    CS = 1;

    CE = 0; //初始化,关闭数据采集

    CS = 0;

    A0 = 0;

    RC = 0;

    CE = 1; //CE=1,CS1=0,RC=0,A0=0启动12位温度转换

    _nop_();

    while(STS==1);//等待数据采集结束

    CE = 0; //芯片使能关闭

    RC = 1;

    A0 = 0;

    CE = 1; //CE=1,CS1=0,RC=1,12/8=1,A0=0允许高八位数据并行输出

    _nop_();

    temp1 = P0;//读取转换结果的高八位

    CE = 0;//芯片使能关闭

    RC = 1;

    A0 = 1;

    CE = 1;//CE=1,CS1=0,RC=1,12/8=0,A0=1允许低四位数据并行输出

    _nop_();

    temp2 = P0;//读取转换结果的低四位

    temp = (temp1<<8)|temp2;//高位和低位合成实际温度,temp2P0口的高四位

    return (temp>>4);//返回转换结果,右移四位是因为temp2P0口的高四位

}

2)从机接收主机发送的波特率并且设置自己的波特率

  主机发送波特率给从机,从机查询法判断是否接收到主机发送的波特率,如果接收到主机发送的波特率则改变自己的波特率,跳出循环来实现发送温度地址的发送,否则一直循环判断是否接收到主机发送的波特率。

/*查询法接收波特率*/

while(1)

{

    if(RI == 1)

    {

       temp = SBUF;

       set_bote(temp);//设置波特率

       break;

    }

}

3)拨码开关实现地址的变换

  拨码开关接从机的P1口,然后改变拨码开关的值,P1口的数据变换,等待传送给主机。

4)定时器实现5秒间隔

  定时器0工作方式1,初值装(65536-50000)实现50ms的定时,num为定时次数,当num=100时,定时为5s的间隔。

5)发送温度和地址,及其通信协议

  由于温度经过转换后发送给主机要保留一位小数的话大于255(即8位),还有温度有正负要判断,所以要发送多次数据给主机,主机经过判断才能确认从机发送的温度是整数部分、小数部分还是地址。

通信协议如下:

因为采集到的温度有正负,所以定义j表示温度的正负,j=0表示正,j=1表示负。j = 0;  //温度正负标志位

temp = AD1674_Read();       //读取转换后的12位温度值  

temp = (int)((1.8528-temp*addo)*10000/11.79); //实现实际温度的转换其中add=10.0/4095

if(temp < 0) {temp = - temp;j = 0x20;} //温度若为负,则标志位00100000

temp2 = temp % 10;   //温度小数位存放在temp2

temp = temp / 10;

小数部分

temp2 = temp2+132;//温度小数位+132用于接收时的判断

温度正负标志和地址

temp = P1;

temp = temp & 0x0f;    //地址为P1口的低4

temp1 = temp1 << 4;

temp1 = temp1 | 0xc0; //地址高位置111000000用于接收判断

temp = temp | temp1;  

temp = temp | j;       //正负标志位存于temp

因为温度值是0-130之间的由于精度在1.41所以是0-132之间。所以温度值是小于132的。小数部分是0-9,所以小数部分+133133-142之间的。而地址低40-15,高位直接置为1100,即大于192。所以主机只要判断在0-132之间就为温度的整数部分,133-142之间的就为温度的小数部分。剩下的就是地址、温度正负标志位的组合了。

 

 

 

1、 温度接收模块

使用AT89C51单片机、4位拨码开关、LED显示组成温度接收模块(主机),4位拨码开关用于设定通讯波特率(1200240048009600)当主机波特率改变后,从机波特率也自动改变,上电运行显示波特率信息(停3S),之后显示接收地址与温度信息,当接收时间间隔超过6S未收到数据,系统报警;

1)波特率设定及发送

拨码开关接主机的P1口,用于设定主机的波特率。P1口的数据分别为1248时的波特率为1200240048009600。然后发送给从机波特率,接着自己的波特率也随之改变。

2)波特率显示并且延时3s

P1口可以得到不同的值,从而用软件设定波特率(即设定定时器1的初值),不同的值存放在bote[]数组中用以数码管的显示。定时器T0实现定时功能,初值装(65536-50000)实现50ms的定时,num为定时次数,当num60的时候定时到了3s,以后就不用显示波特率了,所以用m变量为1表示波特率显示结束,不再显示波特率。

3)温度、地址的接收和判断

温度地址接收是用串口中断实现的。RI1的时候表示一帧数据已经接收完了,所以就开始判断是温度的整数部分、小数部分、还是地址和正负标志位组成的数据。通信协议如上:

if(temp < 132)                          // 若所接收到的数小于132则是温度正数部分值

{    

   HEXTOBCD(temp, aa, cc);      //转换为温度值

}

else if(temp < 143)                   //若小于143,则为温度的小数部分值

{

       cc = temp - 132;     //小数部分还原

}

else

{

       bb = temp & 0x0f;        //温度为低4

       disnum[6] = bb / 10;   //存于数组中用于显示中

       disnum[7] = bb % 10;

       aa = temp & 0x20;       //温度正负标志位

       if(aa == 0) aa = 0;

       else aa = 1;

}

4)数码管显示模块

        数据存于disnum数组中,用8位数码管来显示温度、地址以及波特率。段选接主机的P0口,片选接主机的P2口。一开始显示波特率,就选中最后4位。

然后就是要显示温度和地址的信息了。温度为-55—130所以前面4位显示温度后2位显示地址即:XXX.XC-XX。由于数码管是动态扫描的,所以要一定速度才可以实现视觉残留。看上去好像是一直点亮一样的。

void display_led()

{

       uchar i;

       for(i = 0; i < 8; i++)

       {    

              P2 = disbit[i];    //使用查表法进行位选  

              if(i != 2)

                     P0 = table[disnum[i]];      

              else

                     P0 = table1[disnum[i]];

              delay(150);          //扫描间隔时间         太长会数码管会有闪烁感

       }

}

   5系统报警

定时器0实现定时功能,num1为报警的时间的计算,定时器是实现50ms的,所以要120才能实现6snum1 = 120,那么判断一下flag(是否接收到传送过来的数据)如果flag = 0;说明没有接收到采集的数据,接着就报警,还要把num1flag重新清0,实现下次的报警功能。  

{

       m = 0;            //波特率显示结束

       if(num1 == 120)      //若已经计时6s

       {

           if(flag == 0) //若没有接收到数据

           {

              uchar t = 8;

              while(t--) //报警

                  BEEP();

           }

           else

              flag = 0;  //否则接收到数据标志位清0

           num1 = 0;     //计数次数清0

       }

       display_led();       //显示温度和地址

}

 

 

附录:源程序代码及注释

从机代码:

#include<reg51.h>

#include<intrins.h>

#define uchar unsigned char

#define uint unsigned int

 

#define addo (10.0/4095.0)//转换进率,12位精度(2^12-1=4095),满量程为10V

uint num; //计数次数

 

/*管脚定义*/

sbit STS = P2^0;

sbit CS = P2^1;

sbit A0 = P2^2;

sbit CE = P2^3;

sbit RC = P2^4;

 

 

/*毫秒延时函数*/

void delay_ms(uint n)

{

    uint i;

    while(n--)

    {

        for(i=0;i<110;i++);

    }

}

 

/*读取AD1674转换结果*/

uint AD1674_Read(void)

{

    uint temp;

    uchar temp1,temp2;

 

    CS = 1;

    CE = 0; //初始化,关闭数据采集

 

    CS = 0;

    A0 = 0;

    RC = 0;

    CE = 1; //CE=1,CS1=0,RC=0,A0=0启动12位温度转换

    _nop_();

 

    while(STS==1);//等待数据采集结束

 

    CE = 0; //芯片使能关闭

    RC = 1;

    A0 = 0;

    CE = 1; //CE=1,CS1=0,RC=1,12/8=1,A0=0允许高八位数据并行输出

    _nop_();

    temp1 = P0;//读取转换结果的高八位

    CE = 0;//芯片使能关闭

    RC = 1;

    A0 = 1;

    CE = 1;//CE=1,CS1=0,RC=1,12/8=0,A0=1允许低四位数据并行输出

    _nop_();

    temp2 = P0;//读取转换结果的低四位

 

    temp = (temp1<<8)|temp2;//高位和低位合成实际温度,temp2P0口的高四位

 

    return (temp>>4);//返回转换结果,右移四位是因为temp2P0口的高四位

}

 

/*数据发送*/

void putbyte(uchar byte)// 利用硬件串口方式2 发送一个字节数据

{

   SBUF = byte;//将要发送的数据送到发送缓冲区域,系统自动发送

   while(!TI);//查询是否发送结束  TI=1  代表发送结束

   TI = 0;//清发送结束位方便下一次发送

}

 

void init()

{

    TMOD=0x21;//定时器为方式2

    TL1=0xFD;

    TH1=0xFD;//波特率为9.6K

    TH0 = (65536 - 50000) / 256;          //装初值实现50ms

    TL0 = (65526 - 50000) % 256;

    TR0 = 1;                              //启动定时器0

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

    ET0 = 1;                              //开定时器0

    SM0 = 0;                            //方式2

    SM1 = 1;

    RI = 0;

    TI = 0;

    REN = 1;

    EA = 1;               //开总中断

    ES = 1;               //开串口中断

}

 

//设置波特率

void set_bote(uchar temp)

{

    if(temp==1)

    {

        TL1=0xE8;

        TH1=0xE8;//波特率为1.2k

    }

    else if(temp==2)

    {

        TL1=0xF4;

        TH1=0xF4;//波特率为2.4K

    }

    else if(temp==4)

    {

        TL1=0xFA;

        TH1=0xFA;//波特率为4.8K

    }

    else if(temp==8)

    {

        TL1=0xFD;

        TH1=0xFD;//波特率为9.6K

    }

}

 

/*主函数*/

void main(void)

{  

    int temp,temp1;

    uchar a, temp2;

    uchar j;

    init();

    /*查询法接收波特率*/

    while(1)

    {

        if(RI == 1)

        {

            temp = SBUF;

            set_bote(temp);//设置波特率

            break;

        }

    }

    while(1)     

    {

        while(num==100);        //num计数100实现5秒的延时

        num = 0;

 

        j = 0;  //温度正负标志位

        temp = AD1674_Read();       //读取转换后的12位温度值  

        temp = (int)((1.8528-temp*addo)*10000/11.79); //实现实际温度的转换

        if(temp < 0) {temp = - temp;j = 0x20;} //温度若为负,则标志位00100000

        temp2 = temp % 10;  //温度小数位存放在temp2

        temp = temp / 10;

        a = (uchar)temp;

        putbyte(a); //发送温度

        delay_ms(10);//延时一段时间

 

        temp2 = temp2+132;//温度小数位+132用于接收时的判断

        putbyte(temp2);    

        delay_ms(10); //发送温度小数位

 

        temp = P1;

        temp = temp & 0x0f;   //地址为P1口的低4

        temp1 = temp1 << 4;

        temp1 = temp1 | 0xc0; //地址高位置111000000用于接收判断

        temp = temp | temp1;     

        temp = temp | j;          //正负标志位存于temp

        putbyte(temp); //发送地址  

    }

}

 

/*定时器0中断*/

void t0() interrupt 1

{

    TH0 = (65536 - 50000) / 256;//装初值50000实现50ms

    TL0 = (65526 - 50000) % 256;

    num++;                     //计数次数

}

 

 

 

主机代码:

#include <reg51.h>

#include <intrins.h>

#define uchar unsigned char

#define uint unsigned int

 

/*数码管显示’0-9‘,‘-’,’C‘无小数点*/

uchar code table[]={0xc0, 0xf9, 0xa4, 0xb0, 0x99,

                    0x92, 0x82, 0xf8, 0x80, 0x90,0xbf, 0xc6};

 

/*数码管显示’0-9‘有小数点*/

uchar code table1[]={0x40, 0x79, 0x24, 0x30, 0x19,

                    0x12, 0x02, 0x78, 0x00, 0x10,0x3f};

 

/*位选信号选中数码管*/

uchar disbit[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20,0x40,0x80};

uchar disnum[8],bote[4];//存放要显示的数据

uchar flag, num, num1, m, aa, cc;

 

sbit beep = P1^7;   //蜂鸣器报警

sbit led = P1^6;    //红灯亮报警

 

/*延时子程序*/

void delay(uint i)

{

    char j;

    for(i; i > 0; i--)

        for(j = 200; j > 0; j--);

}

 

/*向缓冲器中写入要显示的数据*/

void HEXTOBCD(uint temp, uchar a,uchar cc)

{

    temp = temp * 10 + cc;

    /*若为正数即0-130*/

    if(a == 0)

    {

        disnum[0] = temp/1000;     //百位

        disnum[1] = temp%1000/100; //十位

        disnum[2] = temp%100/10;   //各位

        disnum[3] = temp%10;       //小数位

        disnum[4] = 11;            //显示C

        disnum[5] = 10;            //显示-

    }

    /*若为负数即-55-0*/

    else if (a == 1)

    {

        disnum[0] = 10;            //显示-

        disnum[1] = temp/100;      //十位

        disnum[2] = temp%100/10;   //个位

        disnum[3] = temp%10;       //小数位

        disnum[4] = 11;            //显示C

        disnum[5] = 10;            //显示-

    }

 

}

 

void init()

{

    SCON = 0x50;

    TMOD=0x21;//定时器为方式2

    TL1=0xFD;

    TH1=0xFD;//波特率为9.6K

    TH0 = (65536 - 50000) / 256;   //装初值实现50ms

    TL0 = (65526 - 50000) % 256;

    TR0 = 1;              //开定时器0

    TR1 = 1;              //开定时器1

    ET0 = 1;              //开定时器0

    REN = 1;              //允许接收

    SM0 = 0;              //方式2

    SM1 = 1;

    EA = 1;               //开总中断

    ES = 1;               //开串口中断

}

 

/*数据发送*/

void putbyte(uchar byte)// 利用硬件串口方式2 发送一个字节数据

{

   SBUF = byte;//将要发送的数据送到发送缓冲区域,系统自动发送

   while(!TI);//查询是否发送结束  TI=1  代表发送结束

   TI = 0;//清发送结束位方便下一次发送

}

 

/*设置波特率 */

void set_bote()

{

    uint temp=P1;

    temp=temp&0x0f;

    putbyte(temp);

    if(temp==1)

    {

        TL1=0xE8;

        TH1=0xE8;//波特率为1.2K

    }

    else if(temp==2)

    {

        TL1=0xF4;

        TH1=0xF4;//波特率为2.4K

    }

    else if(temp==4)

    {

        TL1=0xFA;

        TH1=0xFA;//波特率为4.8K

    }

    else if(temp==8)

    {

        TL1=0xFD;

        TH1=0xFD;//波特率为9.6K

    }

}

 

 

void display_led()

{

    uchar i;

    for(i = 0; i < 8; i++)

    {  

        P2 = disbit[i];     //使用查表法进行位选   

        if(i != 2)

            P0 = table[disnum[i]]; 

        else

            P0 = table1[disnum[i]];

        delay(150);     //扫描间隔时间   太长会数码管会有闪烁感

    }

}

 

void BEEP()

{

    uchar i , j;

    led = 1;

    for (i = 0; i < 100; i++)

   { 

        beep = !beep;                 //beep取反

        for(j = 0; j < 250 ; j++)   //需要产生方波

        _nop_();

   }

   beep = 1;                      //关闭蜂鸣器

}

 

void main(void)

{

    uchar i;

    init();                     //初始化

    set_bote();                 //设置波特率并且发送给从机

    num = 0;                    //计数次数清0

    num1 = 0;                   //报警计数次数清0

    m = 1;                      //波特率显示结束标志

    flag = 1;                   //是否接收到从机发送的数据标志

    while(1)

    {

        led = 0;       

        if(num <= 60 && m == 1)

        {

            uchar temp = P1 & 0x0f;  

            if(temp==1) //显示波特率1200

            {

                bote[0] = 1;bote[1] = 2;bote[2] = 0;bote[3] = 0;

            }

            else if(temp==2) //显示波特率2400

            {

                bote[0] = 2;bote[1] = 4;bote[2] = 0;bote[3] = 0;

            }

            else if(temp==4)  //显示波特率4800

            {

                bote[0] = 4;bote[1] = 8;bote[2] = 0;bote[3] = 0;

            }                

            else if(temp==8)  //显示波特率9600

            {

                bote[0] = 9;bote[1] = 6;bote[2] = 0;bote[3] = 0;

            }

            for(i = 0; i < 4; i++)

            {  

                P2 = disbit[i+4];   //使用查表法进行位选   

                P0 = table[bote[i]];   

                delay(150);     //扫描间隔时间   太长会数码管会有闪烁感

            }

        }         

        else  

        {

            m = 0;              //波特率显示结束

            if(num1 == 120)     //若已经计时6s

            {

                if(flag == 0)   //若没有接收到数据

                {

                    uchar t = 8;

                    while(t--)  //报警

                        BEEP();

                }

                else

                    flag = 0;   //否则接收到数据标志位清0

                num1 = 0;       //计数次数清0

            }

            display_led();      //显示温度和地址

        }

    }      

}

 

void t0() interrupt 1

{

    TH0 = (65536 - 50000) / 256; //装初值实现50ms

    TL0 = (65526 - 50000) % 256;

    num++;

    num1++;

}

void recieve() interrupt 4

{

    uchar temp, bb;

    if(RI)   //RI=1,表示一桢数据接收完

    {

        flag = 1;                                 

        temp = SBUF;       

        if(temp < 132)              // 若所接收到的数小于132则是温度正数部分值

        {  

            HEXTOBCD(temp, aa, cc);   //转换为温度值

        }

        else if(temp < 143)         //若小于143,则为温度的小数部分值

        {

            cc = temp - 132;       //小数部分还原

        }

        else

        {

            bb = temp & 0x0f;      //温度为低4

            disnum[6] = bb / 10;   //存于显示中

            disnum[7] = bb % 10;

            aa = temp & 0x20;     //温度正负标志位

            if(aa == 0) aa = 0;

            else aa = 1;

        }

    }

    RI=0;  //RI0之后才能接收到下一帧数据。

} 

 

  评论这张
 
阅读(691)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017