欢迎光临,嵌入式开源博客

CH451驱动的数码管+按键+电子表+温度程序

//这是一个CH451芯片驱动的数码管+按键+ds1302电子表+ds18b20温度的c语言程序
//已经在51单片机开发板上测试通过,带有温度报警,有详细的注释.
//程序的完整版本下载地址:http://www.51hei.com/f/ch451.rar
//本程序版权所无您可以随意修改.
#include <reg51.h>
#include <math.h>
#include <intrins.h>
/*************************************************************
  _crol_ 字符循环左移 
  _cror_ 字符循环右移
  _irol_ 整数循环左移 
  _iror_ 整数循环右移 
  _lrol_ 长整数循环左移 
  _lror_ 长整数循环右移
  _nop_ 空操作8051 NOP 指令
*************************************************************/
#define uchar unsigned char
#define uint unsigned int
/******************************DS1302宏定义******************/
#define WRITE_SECOND       0x80 
#define WRITE_MINUTE       0x82 
#define WRITE_HOUR         0x84 
#define READ_SECOND        0x81 
#define READ_MINUTE        0x83 
#define READ_HOUR          0x85 
#define WRITE_PROTECT      0x8E 
/***********************CH451宏定义************************/
#define CH451_RESET     0x0201             //复位
#define CH451_LEFTMOV   0x0300             //设置移动方式-作移
#define CH451_LEFTCYC   0x0301             //设置移动方式-左循
#define CH451_RIGHTMOV  0x0302             //设置移动方式-右移
#define CH451_RIGHTCYC  0x0303             //设置移动方式-右循 
#define CH451_SYSOFF    0x0400             //关显示、键盘、看门狗
#define CH451_SYSON1    0x0401             //开显示
#define CH451_SYSON2    0x0403             //开显示、键盘
#define CH451_SYSON3    0x0407             //开显示、键盘、看门狗功能
#define CH451_DSP       0x0500             //设置默认显示方式
#define CH451_BCD       0x0580             //设置BCD译码方式
#define CH451_TWINKLE   0x0600             //设置闪烁控制——正常显示
#define CH451_DIG0      0x0800             //数码管位0显示
#define CH451_DIG1      0x0900             //数码管位1显示 
#define CH451_DIG2      0x0a00             //数码管位2显示
#define CH451_DIG3      0x0b00             //数码管位3显示
#define CH451_DIG4      0x0c00             //数码管位4显示
#define CH451_DIG5      0x0d00             //数码管位5显示 
#define CH451_DIG6      0x0e00             //数码管位6显示 
#define CH451_DIG7      0x0f00             //数码管位7显示
/*************************************************************/
sbit ACC_7=ACC^7;     //位寻址寄存器定义 
sbit SCLK=P2^1;                 //ds1302管脚定义            
sbit IO=P2^0;                                
sbit RST=P1^7;                    

sbit DQ=P1^6;   //ds18b20定义
sbit P26=P2^6; 

sbit ch451_dclk=P3^4;                    //串行数据时钟上升延激活
sbit ch451_din=P3^3;                     // 串行数据输出,接CH451的数据输人
sbit ch451_load=P2^3;                    //串行命令加载,上升延激活
sbit ch451_dout=P2^2;               //键值数据输入,接ch451数据输出  
      
int temp;
int tt;          //真实温度变量  
uchar  ch451_key=0;                     // 存放键盘中断中读取的键值
uint display[8]={0x0a00,0x0900,0x0b00,0x0800,
    0x0c00,0x0d00,0x0e00,0x0f00};
uchar BCD[10]={0xbe,0x24,0xea,0xe6,    //0,1,2,3,
               0x74,0xd6,0xde,0xa4,   //4,5,6,7,
               0xfe,0xf6     //8,9
              };    
/*************精确延时函数*****************/
void delay(uint i)  
{
while(–i);
}


/************************蜂鸣器程序********/
void speaker(void)
{
uchar i,j;
for(j=0;j<250;j++)
for(i=0;i<250;i++)
  {
P26=~P26;
for(i=0;i<250;i++);
}
   P26=1;      //防止结束时候是低电平
}
/**********************4*******************/
/*****************DS18B20******************/

void Init_Ds18b20(void)     //DS18B20初始化

{
DQ=0;                    //单片机拉低总线
delay(500);              //精确延时,维持至少480us
DQ=1;                    //释放总线,即拉高了总线
delay(200);            //此处延时有足够,确保能让DS18B20发出存在脉冲。
}

/*****************************************************************************************/
uchar Read_One_Byte()       //读取一个字节的数据
                            //读数据时,数据以字节的最低有效位先从总线移出
{
uchar i=0;
uchar dat=0;
for(i=8;i>0;i–)
{
   DQ=0;                  //将总线拉低,要在1us之后释放总线
                           //单片机要在此下降沿后的15us内读数据才会有效。
   _nop_();    
   _nop_();              //至少维持了1us,表示读时序开始
   dat>>=1;               //让从总线上读到的位数据,依次从高位移动到低位。
   DQ=1;                  //释放总线,此后DS18B20会控制总线,把数据传输到总线上
delay(1);                 //延时7us,此处参照推荐的读时序图,尽量把控制器采样时间放到读时序后的15us内的最后部分
   if(DQ)                   //控制器进行采样
   {
    dat|=0x80;            //若总线为1,即DQ为1,那就把dat的最高位置1;若为0,则不进行处理,保持为0
   }        
delay(60);      //此延时不能少,确保读时序的长度60us–很重要
}
return (dat);
}
/********************************************************/
void Write_One_Byte(uchar dat)
{
uchar i=0;
for(i=8;i>0;i–)
{
   DQ=0;                        //拉低总线
    _nop_();   
nop_();                   //至少维持了1us,表示写时序(包括写0时序或写1时序)开始
   DQ=dat&0x01;               //从字节的最低位开始传输
                              //指令dat的最低位赋予给总线,必须在拉低总线后的15us内,
                              //因为15us后DS18B20会对总线采样。

     delay(60);              //必须让写时序持续至少60us —-很重要
   DQ=1;                        //写完后,必须释放总线,
  /* _nop_(); */  
   dat>>=1;
                                 //因为15us后DS18B20会对总线采样。
delay(1);
  
}
}

/*************************************************************************/
uint Get_Tmp()                   //获取温度
{
float t;
uchar a=11,b=0;
Init_Ds18b20();                //初始化
Write_One_Byte(0xcc);          //忽略ROM指令
Write_One_Byte(0x44);          //温度转换指令
Init_Ds18b20();                 //初始化
Write_One_Byte(0xcc);          //忽略ROM指令
Write_One_Byte(0xbe);          //读读RAM的存储器指令
a=Read_One_Byte();           //读取到的第一个字节为温度LSB
b=Read_One_Byte();           //读取到的第二个字节为温度MSB
temp=b;                      //先把高八位有效数据赋于temp
temp<<=8;                    //把以上8位数据从temp低八位移到高八位
temp=temp|a;                 //两字节合成一个整型变量
t=temp*0.0625;              //得到真实十进制温度值
                                //因为DS18B20可以精确到0.0625度
                                //所以读回数据的最低位代表的是0.0625度
temp=t*10+0.5;                  //此处放大十倍
                                //这样做的目的将小数点后第一位也转换为可显示数字
                                //同时进行一个四舍五入操作。
return(temp);
}
/************************************************************************
      以上实现温度传感器的读取数据
*************************************************************************/
/******************************DS1302*************************************/



//地址、数据发送子程序 
void Write1302(uchar addr,uchar dat ) 

         uchar i,t; 
         RST=0;                                    //RST 引脚为低,数据传送终止 
         SCLK=0;                              //清零时钟总线 
         RST=1;                                //RST 引脚为高,逻辑控制有效
         //发送地址 
         for(i=8;i>0;i–) //循环8次移位 
         { 
                  SCLK=0; 
                  t=addr; 
                  IO=(bit)(t&0x01);                   //每次传输低字节
                  addr>>=1;                        //右移一位 
                  SCLK=1; 
         } 
    //发送数据 
         for(i=8;i>0;i–) 
         { 
                  SCLK=0; 
                  t=dat; 
                  IO=(bit)(t&0x01);
                  dat>>=1; 
                  SCLK=1; 
         } 
         RST=0; 


//数据读取子程序 
uchar Read1302(uchar addr ) 

         uchar i,t,dat1,dat2; 
         RST=0; 
         SCLK=0; 
         RST=1; 
         //发送地址 
         for(i=8;i>0;i–)                            //循环8次移位 
         { 
                  SCLK=0; 
                  t=addr; 
                 IO=(bit)(t&0x01);                 //每次传输低字节
                  addr>>=1;                           //右移一位
                  SCLK=1; 
         } 
         //读取数据 
         for(i=8;i>0;i–) 
         { 
                  ACC_7=IO; 
                  SCLK=1; 
                  ACC>>=1;           //右移   
                  SCLK=0; 
         } 
         RST=0; 
         dat1=ACC; 
         dat2=dat1/16;                                       //数据进制转换 
         dat1=dat1%16;                                     //十六进制转十进制
         dat1=dat1+dat2*10; 
         return (dat1); 


//初始化DS1302 
void Initial(void) 
{    

         Write1302 (WRITE_PROTECT,0x00);             //禁止写保护 
         Write1302 (WRITE_SECOND,0x00);              //秒位初始化,开始计时 
         Write1302 (WRITE_MINUTE,0x03);             //分钟初始化
         Write1302 (WRITE_HOUR,0x23);            // 小时初始化——24小时模式
     //Write1302 (WRITE_PROTECT,0x80);           // 允许写保护 
             // 调试证明必须禁止写保护
             //不然修改不了初始值

/*********************************CH451**********************/
//********************************************
//设置定时器中断__定时扫描键盘
void ch451_init()
{
  ch451_din=0;              //先低后高,选择4线输入
  ch451_din=1;
  TMOD=0x10;    //设置定时器T1工作在16位计时状态
  EA=1;        //开中断总开关
  ET1=1;                   //允许中断
  TR1=1;                   //开中断
  PT1=0;                   //设置低优先级
  TL1=1;     //装载计数初值
  TH1=0;     //此计数初值用来调试
}
//*****************************************************
//定义一无符号整型变量存储12字节的命令字。
void ch451_write(uint command)
{
uchar i;
  TR1=0;                       //禁止T1中断
  ch451_load=0;                                  //命令开始       
  for(i=0;i<12;i++){                             //送入12位数据,低位在前
    ch451_din=command&1; 
    ch451_dclk=0;
    command>>=1;
    ch451_dclk=1;                               //上升沿有效
  }
  ch451_load=1;                                 //上升沿加载数据
  TR1=1;          // 开启T1中断
}

//*************************************************
//*************************************************

//中断子程序  使用定时器中断T1,寄存器组1
void ch451_inter() interrupt 3 using 1
{      
  
  uchar i;           //定义循环变量
  
  uchar command,keycode;       //定义控制字寄存器,和中间变量定时器

  command=0x07;          //读取键值命令的高4位0111B
  ch451_load=0;          //命令开始
  for(i=0;i<4;i++){
    ch451_din=command&1;      //低位在前,高位在后
    ch451_dclk=0;
    command>>=1;         //右移一位
    ch451_dclk=1;         //产生时钟上升沿锁通知CH451输入位数据
}
  ch451_load=1;         //产生加载上升沿通知CH451处理命令数据
  keycode=0;          //清除keycode
  for(i=0;i<7;i++){
    keycode<<=1;         //数据作移一位,高位在前,低位在后
    keycode|=ch451_dout;       //从高到低读入451的数据
    ch451_dclk=0;         //产生时钟下升沿通知CH451输出下一位
    ch451_dclk=1;
}
  ch451_key=keycode;       //保存上次的键值

TL1=1;       //重新装载计数初值
TH1=0;       //此计数初值用来调试  
}

//***********************************************


void main()
{
int HIGH=270;      //定义温度上限变量
uchar A0,A1,A2,A3;    //记录温度各位数的变量
uchar b,c;      //定义存放DS1302中数据的变量        
P26=1;        //关闭蜂鸣器
Initial();     //初始化DS1302     
        //初始化以后此语句必须关掉 防止每次上电初始化DS1302 
        //关掉此语句编译时会为此产生一个警告
            /*改动*/
ch451_init();        // 设置定时器中断__定时扫描键盘
ch451_write(CH451_RESET);     //CH451复位
ch451_write(CH451_SYSON2);     //开显示、键盘
ch451_write(CH451_DSP);      //设置BCD不译码方式     由于板画错了  此设置很重要
ch451_write(CH451_TWINKLE);      //设置闪烁控制——正常显示
             

while(1)
{
tt=Get_Tmp();     //读出当前温度
A3=tt/1000;      //千位
A2=(tt%1000)/100;       //百位
A1=(tt%100)/10;       //十位——这么处理的目的是显示小数点,因为A1实际是温度的个位
A0=tt%10;         //个位
ch451_write(display[0]+BCD[A0]);    //显示温度小数位
ch451_write(display[1]+BCD[A1]+1);    //显示温度个位  此处加一为了加入小数点
ch451_write(display[2]+BCD[A2]);    //显示温度十位
ch451_write(display[3]+BCD[A3]);    //显示温度百位

if(Get_Tmp()>HIGH)         //如果温度大于上限度    
  {
delay(10);         //延时是为了防止脉冲不稳定干扰
if(Get_Tmp()>HIGH)          //可以消除蜂鸣器非正常响
            /*合理的改动*/
{
Write1302(0xc0,Read1302(READ_MINUTE)/10*16+Read1302(READ_MINUTE)%10);  //写入当前记录的数据所对应的时间——分   
Write1302(0xc2,Read1302(READ_HOUR)/10*16+Read1302(READ_HOUR)%10);  //写入当前记录的数据所对应的时间——小时
                  /**写入的应该是16进制而不是十进制**/
speaker();         //如果温度大于 HIGH度,蜂鸣器报警
  }
  }   
if(Get_Tmp()<0)         //如果温度小于0度
{
delay(10);         //延时是为了防止脉冲不稳定干扰
if(Get_Tmp()<0)      //可以消除温度非正常显示为负
            /*合理的改动*/
{
tt=abs(tt);
ch451_write(display[0]+BCD[A0]);    //显示温度小数位
ch451_write(display[1]+BCD[A1]+1);    //显示温度个位  此处加一为了加入小数点
ch451_write(display[2]+BCD[A2]);    //显示温度十位
ch451_write(display[3]+BCD[A2]);    //显示温度百位
ch451_write(display[4]+0x40);      //显示负号
}  
}         
//对各个按键进行定义
  switch(ch451_key)
  {
  case 0x40: {
              
     b=Read1302(0xc1);     //读出数据所对应的时间——分  
     c=Read1302(0xc3);     //读出数据所对应的时间——小时   */
     ch451_write(display[4]+BCD[b%10]);     //显示分钟个位
     ch451_write(display[5]+BCD[b/10]);    //显示分钟十位
     ch451_write(display[6]+BCD[c%10]);     //显示小时个位
     ch451_write(display[7]+BCD[c/10]);     //显示小时十位
    }     break;       //按下第1个键时调出DS1302中产生温度报警的时间
  case 0x43: { ch451_write(display[4]+0);     //关显示分钟个位
     ch451_write(display[5]+0);    //关显示分钟十位
     ch451_write(display[6]+0);     //关显示小时个位
     ch451_write(display[7]+0);     //关显示小时十位;
    }     break;          //按下第2个键关时间显示
  case 0x41: speaker(); break;       //按下第3个键时蜂鸣器响
  case 0x42: {
       A3=HIGH/1000;      //千位
     A2=(HIGH%1000)/100;       //百位
     A1=(HIGH%100)/10;       //十位——这么处理的目的是显示小数点,因为A1实际是温度的个位
     A0=HIGH%10;         //个位
     ch451_write(display[4]+BCD[A0]);    //显示温度小数位
     ch451_write(display[5]+BCD[A1]+1);    //显示温度个位  此处加一为了加入小数点
     ch451_write(display[6]+BCD[A2]);    //显示温度十位
     ch451_write(display[7]+BCD[A3]);    //显示温度十位    
    }       break;    //按下第4个键显示上限温度 
  case 0x48: {
     delay(65000);     //使按键增大温度上限值得过程变慢
       HIGH=HIGH+1;
       A3=HIGH/1000;      //千位
     A2=(HIGH%1000)/100;       //百位
     A1=(HIGH%100)/10;       //十位——这么处理的目的是显示小数点,因为A1实际是温度的个位
     A0=HIGH%10;         //个位
     ch451_write(display[4]+BCD[A0]);    //显示温度小数位
     ch451_write(display[5]+BCD[A1]+1);    //显示温度个位  此处加一为了加入小数点
     ch451_write(display[6]+BCD[A2]);    //显示温度十位
     ch451_write(display[7]+BCD[A3]);    //显示温度百位        
    }   ; break;      //按下第5个键增加温度上限值
  case 0x4b: {
     delay(65000);      //使按键减小温度上限值得过程变慢
       HIGH=HIGH-1;
       A3=HIGH/1000;      //千位
     A2=(HIGH%1000)/100;        //百位
     A1=(HIGH%100)/10;       //十位——这么处理的目的是显示小数点,因为A1实际是温度的个位
     A0=HIGH%10;          //个位
     ch451_write(display[4]+BCD[A0]);    //显示温度小数位
     ch451_write(display[5]+BCD[A1]+1);    //显示温度个位  此处加一为了加入小数点
     ch451_write(display[6]+BCD[A2]);    //显示温度十位
     ch451_write(display[7]+BCD[A3]);    //显示温度十位     
    
    }   ; break;      //按下第6个键减小温度上限值
  case 0x49: { 
      while(ch451_key!=0x43)
        {
     ch451_write(display[0]+BCD[Read1302(READ_SECOND)%10]);  //显示分钟的个位
     ch451_write(display[1]+BCD[Read1302(READ_SECOND)/10]);  //显示秒的十位
     ch451_write(display[2]+0x40);        //显示负号
     ch451_write(display[3]+BCD[Read1302(READ_MINUTE)%10]);  //显示分钟的个位
     ch451_write(display[4]+BCD[Read1302(READ_MINUTE)/10]);     //显示分钟的十位
     ch451_write(display[5]+0x40);           //显示负号  
     ch451_write(display[6]+BCD[Read1302(READ_HOUR)%10]);     //显示小时的个位
     ch451_write(display[7]+BCD[Read1302(READ_HOUR)/10]);     //显示分钟的十位 
     }       
    }   ; break;      //按下第7个键动态显示当前的时间——电子表
  case 0x4a:    ; break;      //按下第8个键无效
  }

/****************************************************************/
              
}
}

赞(0) 打赏
未经允许不得转载:Huangea Blog 嵌入式博客 » CH451驱动的数码管+按键+电子表+温度程序
分享到: 更多 (0)

评论 2

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
  1. #1

    看似很简单的一句话,或许都能给人以启发,支持一小下

    xuzhuan5年前 (2013-11-08)回复
  2. #2

    哈哈,网站做的不错,支持。。。。。

    幽默笑话4年前 (2014-11-26)回复

一个很有意思的网站

留言板友链

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏