专注电子技术学习与研究
当前位置:单片机教程网 >> MCU设计实例 >> 浏览文章

温度控制led灯灰度变化c51

作者:佚名   来源:本站原创   点击数:  更新时间:2011年02月17日   【字体:

18b20时序要求比较严格。
所遇问题:测出温度值不准确,不停的从255于实际温度值跳变。
解决方法:在读取温度值时,关掉中断。不过灯有一些闪。
该程序所测温度的精度不能达到0.5,需要稍微改动。
不知道1602稳定了没,明天再试一次。
反应好像还不够灵敏。

#include "reg52.h"   
#include<intrins.h> 
#define uchar unsigned char 
//温度传感器与单片机接口为 P0.1 
sbit DQ=P1^5; 
// 
sbit E=P0^7; 
sbit RW=P0^6; 
sbit RS=P0^5; 
sbit LED=P3^5; 
//sbit lcd_flag=P2^7;             //定义是lcd否忙标志 
sbit P1_7 = P3 ^ 7;                              // 控制LED11               
sbit P1_6 = P3 ^ 6;                              // 控制LED10   
sbit P1_2 = P3 ^ 4;   
// 
uchar timer=0x00; //字节变量timer用于计时,每计数器记满100次,就采样一次温度 
uchar chars[]={"GYRGB.Liuzhou^_^"};      // 液晶显示第一行 
uchar chars2[]={"Welcome!"};            //液晶显示第二行         
             
uchar  scale_0;                         //灯光灰度 
/////////////////////////////// 
//各个功能子函数 
/////////////////////////////// 
void delay(void);       //18b20温度转换后所需的延时 
void delay1(void);      //动态数码管显示所需的较短的延时 
unsigned char Read18B20(void);  //读18b20子函数   
void Write18B20(uchar ch);       //写18b20子函数 
void Delay15(uchar n);     //读写所需的最基本单位时间的延时 
void Init18B20(void);     //初始化18b20函数 

///////////////////////////// 
//新增的液晶显示程序 
///////////////////////////// 
void WriteLCDcom(uchar ch); 
void WriteLCDdat(uchar ch); 
void main(void) 
{ 
uchar i,tl,th;     //变量i用于循环计数,tl和th用于获得二进制温度值 
uchar temp;      //变量temp用于存放有效的温度值 
uchar bith,bitt,biti,bitz;  //将要显示在数码管上的百位、十位、个位和十分位的码值 
unsigned  int   n; 

RCAP2H =0xFE;                                  // 赋T2的预置值,溢出1次是1/2000秒钟 
    RCAP2L =0x0C; 
    ET2=1;                                         // 打开定时器2中断 
EA=1; //总中断允许 
    TR2=1;                                      // 启动定时器2 

WriteLCDcom(0x01);        //清屏 
WriteLCDcom(0x38);       
WriteLCDcom(0x0c); 
WriteLCDcom(0x06);   
WriteLCDcom(0x80); 
for(i=0;i<16;i++) 
  WriteLCDdat(chars); 
WriteLCDcom(0xc0); 
for(i=0;i<8;i++) 
  WriteLCDdat(chars2); 
WriteLCDcom(0xce); 
WriteLCDdat(0xdf); 
WriteLCDdat(0x43); 

//////////////////////////////// 
//12M晶振,16位计数器 
//溢出时间 65536us约等于65.5ms 
//溢出100次采样温度一次,采样周期6.5秒 
//////////////////////////////// 
while(1) 
{ 

   LED=1; 
   Init18B20(); //复位18b20,每次复位18b20都是默认的12位转换精度 
   Write18B20(0xcc);//向18b20写入跳过激光rom操作 
   _nop_();   //稍等片刻 
   Write18B20(0x44);//命令18b20开始温度的测量以及模数转换 
   
   delay();   //18b20转换时间较长应该等待稍长时间 
   
   Init18B20();  //每一次对18b20的读写都要先复位 
   TR2=0;           //18b20对时序要求比较严格,因此在读取温度时要关中断,否则测的温度值会出错 
   Write18B20(0xcc);//照例跳过ROM的操作 
   _nop_();   //等 
   Write18B20(0xbe); //读18b20的温度数据,可以连续读5个字节 
   _nop_();    //等 
   tl=Read18B20();   //读第一个字节,里面是12位有效数字的低八位 
   th=Read18B20();   //读第二个字节,是扩展的符号位和有效值的高四位 
   TR2=1; 
   Init18B20(); 
   temp=(th<<4)+(tl>>4); 
//实际上,temp=(th<<4)+(tl>>4)这个式子得到的是只包含了数整值的温度值 
//th向左移4位,抛弃了扩展的符号位;tl向右移4位,抛弃了4位小数位 
   bith=temp/100;  //得到百位数字 
   bitt=(temp%100)/10; //得到十位数字 
   biti=temp%10;  //得到个位数字,由于要显示小数点,所以要减去一个0x80 
   bitz=0;    //只是作为好看的位数,实际我们在计算temp的时候已经将小数位舍去了 
   
/*****************************************/ 
// 液晶的显示 
/*****************************************/ 
     WriteLCDcom(0xc8); 
     WriteLCDdat(bith+0x30); 
     WriteLCDdat(bitt+0x30); 
     WriteLCDdat(biti+0x30); 
     WriteLCDdat(0x2e); 
     WriteLCDdat(bitz+0x30); 
     LED=1; 

     
     for(n=0;n<50000;n++);                    // 每过一会儿就自动加一个档次的亮度 
     if(temp==26)  scale_0=0; 
       else if(temp==27)  scale_0=1; 
      else if(temp==28)  scale_0=2; 
       else if(temp==29)  scale_0=3; 
     else if(temp==30)  scale_0=4; 
      else if(temp==31)  scale_0=5; 
       else if(temp==32)  scale_0=6; 
        else if(temp==33)  scale_0=7; 
      else if(temp==34)  scale_0=8; 
       else   scale_0=9; 
         
               
} 
} 

  /******************************************************************************************** 
* 函数名称:Timer2_Server() 
* 功    能:定时器2溢出中断服务程序。1/2000 秒中断1次。 
* 入口参数:无   
* 出口参数:无   
*********************************************************************************************/ 
void Timer2_Server(void) interrupt 5   
{ 
  static uchar tt;                               // tt用来保存当前时间在1秒中的比例位置 
   
  TF2=0; 
   
  tt++; 
  if(tt==10)                                     // 每1/200秒整开始输出低电平 
   { 
     tt=0; 
  if(scale_0!=0)                              // 加入该句的目的是避免灭灯时发生闪烁 
   { 
     P1_7=0; 
     P1_6=0; 
     P1_2=0; 
   } 
   } 
  if(scale_0==tt)                                // 按照当前占空比切换输出高电平 
   { 
     P1_7=1; 
     P1_6=1; 
     P1_2=1; 
   } 
} 


void delay(void)  //长延时,18b20在执行温度转换操作的时候需要耗费较长时间 
{      //在这段时间里18b20需要测温,做模数转换,并将转换的二进制数值存储到自带的临时寄存区里去 
  uchar i,j; 
for(i=0;i<200;i++)   //具体需要多长时间芯片手册上有介绍,这个要继续深入了解 /*************************??******/ 
  for(j=0;j<100;j++) 
  ; 
} 

unsigned char Read18B20(void)  //最基本的读18b20的函数,并向主函数返回读到的那个字节 
{ 
unsigned char ch; //相当于串行缓存器 
    unsigned char q ; //循环计数器 
    for(q=0;q<8;q++)   //循环8次,读出串行的8位数据,先读到的是数据的最低位 
        //因此要从ch的最高位存起,然后依次将ch向右移,就像火车进站那样 
    {       //    7 6 5 4 3 2 1 0   (ch) 
        // MSB-6-5-4-3-2-1-LSB  ---->      (数据) 
  ch=ch>>1;   //先移位再赋值,出现赋值8次但是只移位7次的效果 
      DQ=0;    //单线总线的要求,要读器件,就要产生一个上升沿,然后释放总线,现在要回到低电平 
      _nop_();   // 稍微停顿,让器件探测到电平已经变低了 

      DQ=1;    //拉高总线产生上升沿,同时,在某种意义上,对单片机的端口写1,也就是让端口处于待读的状态,一举两得 
      _nop_();_nop_(); //4个空操作函数,等待,给18b20响充分的时间响应,具体需要多久的时间要看器件手册 
  _nop_();_nop_();  // 读响应是多久,需要继续深入了解        /********************??*****/     
       //而且这个-nop-()函数到底会延时多长时间,要深入了解 /****************??***/ 
      if(DQ==1)      //开始读端口,如果为1,则将ch最高位写为1 
        { ch=ch|0x80;}  //ch|0x80就是 XXXX XXXX | 1000 0000 每一位相或的结果就是得到 1XXX XXXX,火车开始进站了 
     else      //如果读到的数是0,那么就把ch的最高位置为0 
  { ch=ch&0x7f;}  // ch & 0x7f 就是 XXXX XXXX & 0111 1111 ,结果自然就是 0XXX XXXX,数字最低位就进站好了 
      Delay15(3);    //延时少少,延时多长要继续深入了解    /****************??*****/ 
    DQ=1;     //读完之后再次置端口为1,好为下一次读做准备,其实很关键的一步 
     }   
  return (ch);  //将读到的数据返回给主函数,这就我们想要的结果了 
} 
void Write18B20(uchar ch)  //写18b20的函数 
{   
uchar i;     //一个循环计数变量 
for(i=0;i<8;i++)   //循环8次,每次一位二进制 
{ 
  DQ=0;   //从读和写函数的比较可以得知,产生读/写的条件都是要先产生低电平,只是低电平的维持时间长短不一 
    Delay15(1);  //写操作需要的低电平持续时间比读操作要长的多 
    DQ=ch &0x01; //向总线上写ch的最低位,跟读的操作类似,只是这时“车站”是18b20,而“列车”是ch 
    Delay15(3);  //           7 6 5 4 3 2 1 0  (18b20) 
      // MSB-6-5-4-3-2-1-LSB  ---->      (ch) 
      // ch & 0x01 就是 xxxx xxxx & 0000 0001,结果是0000 000x 
      //为什么可以用一个位变量  DQ= 0000 000x,这个问题需要继续深入了解 /*************??****/ 
    DQ=1;   //写完一位后,将总线抬高,为下一次拉低做准备 
    ch=ch >>1;  //将ch第二低位推到最低位,等待发射出去 
    _nop_();  //等待一段时间 
} 
} 
void Init18B20(void) //初始化18b20 
{ 
DQ=0;    //初始化操作同样是由低电平开始,但是这个初始化低电平要持续得最久 
    Delay15(30);//至少延时480us,到底是多少,要找到-nop-()函数源码,反汇编之后才知道/*******************??****/ 
    DQ=1;    //抬高总线,一举两得,可以准备接受18b20的存在低电平 
    Delay15(10);//至少延时100us 
} 
void Delay15(uchar n)  //貌似满精确的延时程序 
{         //具体要在 intrins.h头文件中找到它,那么首先就要找到intrins.h /****************??*****/ 
  do 
{ 
  _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); 
        _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); 
        _nop_(); _nop_(); _nop_(); 
   
    n--; 
   
    }while(n); 
} 
/* 
bit LCDbusy() 
{ 
  bit flag; 
  RS=0;         //寄存器为LOW 
  RW=1;         //是否读写为high 
  E=1;          //使能端为high 
  if(lcd_flag==1) flag=1; 
  E=0; 
  return flag; 
} 
*/ 
void WriteLCDcom(uchar ch) 
{ 
   // while(LCDbusy()); 
RS=0; 
RW=0; 
E=0; 
P2=ch; 
delay(); 
E=1; 
E=0; 
} 
void WriteLCDdat(uchar ch) 
{ 
   // while(LCDbusy()); 
RS=1; 
RW=0; 
E=0; 
P2=ch; 
delay(); 
E=1; 
E=0; 
} 

不明白为什么加了检测1602是否忙的程序进去,为什么P2口就没有数据了呢?液晶就不能显示了呢?  
关闭窗口

相关文章