//这是一个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个键无效 } /****************************************************************/ } }