标题:
单片机数码管动态扫描程序DS12C887红外遥控ds18b20
[打印本页]
作者:
daniu
时间:
2014-12-26 21:21
标题:
单片机数码管动态扫描程序DS12C887红外遥控ds18b20
///////////////////////////////////////////////////////////////////////////
/////////DS12C887+8位数码管定时器中断动态扫描遥控时钟单片机程序模块////////
///////////////////////////////////////////////////////////////////////////
[功能] 0: 8位数码管定时器中断动态扫描显示
1:通过单片机解码红外遥控编码并按上图方式显示出来
2:可通过遥控器调整时间,日期以及闹钟
3:时钟芯片采用DS12C887+,内部集成电池与晶体,不怕掉电丢时间信息
4:在调整时间过程中会有动态效果(正在调试中!!!以经搞好了)
5:在下一版本中会增计算器功能,也是用遥控器操作 值得大家期待!
6: ds18b20温度显示(正在弄这个ds18b20)
7:数码管扫描数据在P0口通过锁存器(74HC573)输出,ds12c887的双向数据则直接接P0口
//以下是C源文件==============================================================================
#include <reg51.h>//80C51头文件
#define uint unsigned int//16bit无符整型0~465536
#define ulint unsigned long int//32bit无符长整0~4294967295
#define uchar unsigned char//8bit宏定义无符字符型0-256
#define c(x) (x*120000/120000)// (x*216000/120000) //定义时钟频率便于红外遥控解码
//单片机硬件设置
sbit duan = P2^0;//数码管段选定义
sbit wei = P2^1;//数码管位选定义
sbit Ir_Pin=P3^3;//红外接收端口
sbit beep = P2^7;//蜂鸣器
//时钟芯片ds12c887控制引脚与单片机连接
sbit dscs = P2^6;//时钟片选
sbit dsas = P2^5;//地址选取通
sbit dsrw = P2^4;//读写输入
sbit dsds = P2^3;//数据选通或输入
sbit dsirq= P2^2;//中断请求输出
sbit DQ =P2^2;//定义温度传感器(18b20)通信端口
//字符码表
uchar code Led_Tab[]={//共阳数码管字符码
0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,//0---f,ff全灭,
0x88,0x83,0xc6,0xa1,0x86,0x8e,0xff,0xfe,0xbf,0xf7,0xc6,0x9c};//上,中,下3短横线
uchar code Led_Sel[]={//位码
0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
//定义各个变量
uchar shan_xia=0,zuo_you=8,Ir_Buf[4],ds_temp=0,i;//上下变量,左右变量,遥控数字,红外结果
bit ok=0,power=0,mute,ds=0,i_k=0;//ds为0时才准读时钟芯片,减少频繁读时钟, i_k为5秒延时开关
uint ms;//屏闪时基变量,温度传感变量
//以下是动态扫描变量
uchar Led_Buf[8],Led_Buf_temp[8],Led_Index;//显示缓冲区,调时闪动缓冲区,位码索引
//声明子函数
void init();//初始化
void write_ds(uchar add,uchar date);//时钟芯片写函数
uchar read_ds(uchar add);//时钟芯片读函数
void delay(uint z);//延时
void hyyk();//红外遥控
uint Ir_Get_Low();//低电平时间采集
uint Ir_Get_High();//高电平时间采集
//遥控调时,数值放入显示缓冲区同步显示,预写时钟
//经数据采集函数(time_set())根据各项格式显示特征识别后
//按mute键写时钟芯片相应地址达到调整时钟信息的目的
void ykm_set();
void time_set();//时间调整数据采集分析写调时缓冲区函数
void ykm_out();//遥控码显示
void led_out(uchar add_1,uchar add_2,uchar add_3,uchar add_4);//送出地址,读出信息,更新显示缓冲区
void led_flash(uchar k,uchar z);//调时闪烁 K为闪动开关 Z为闪动内容
//温度显示子函数声明
void delay_18b20(uint i);//延时
Init_DS18B20(void);//初始化函数
WriteOneChar(uchar dat);//写一个字节
ReadOneChar(void);//读一个字节
ReadTemperature(void);//读取温度
void out_18b20();//显示
//主程序==================================================================================
void main()//程序入口
{
init();//初始化
while(1)//循环等待中断
{
hyyk();//遥控
if ((Ir_Buf[0]==0x02)&&(Ir_Buf[2]==0x14))//POWER键值翻转并响铃
{
ok=0;zuo_you=8;shan_xia=0;//有遥控power时复位ok与上下,左右值,取消闪屏显示时间
power=(!power);
if(power==0)
{
delay(10);
beep=0;
delay(20);
beep=1;
}
Ir_Buf[2]=0xff;
}
if (power==1){uchar i;mute=1; for(i=0;i<8;i++) Led_Buf_temp[i]=16;}//在power标志为1时关屏关铃
if(power==0)//power标志为0时根据shan_xia键值显示内容
{
ykm_set();//遥控调时,调整好后按mute键写时钟
led_flash(ok,shan_xia);
if(ok==0){ds=0;i_k=0;ds_temp=0;}
if(ds==0)
{
if(shan_xia==0)led_out(4,2,0,19);//时间
else if(shan_xia==1)led_out(9,8,7,17);//日期
else if(shan_xia==2)led_out(5,3,1,18);//闹钟
else if(shan_xia==3)led_out(6,6,6,18);//星期
else if(shan_xia==4)out_18b20();//温度
else ykm_out();//遥控码
}
ds=1;//关时钟芯片读与显存刷新,在中断扫描时会打开
//只有在需要显示具体内容时才会访问时钟芯片对应的具体地址并更新显示缓冲区
//ok键值翻转
if ((Ir_Buf[0]==0x02)&&(Ir_Buf[2]==0x15))
{
if((ok==1)&&(zuo_you<8)){zuo_you=8;i_k=1;i=1;}//如果是位闪状态按ok键就进入全闪状态
else {ok=(!ok);zuo_you=8;}//否则闪<==>不闪 状态翻转一次
//if(ok==0){i_k=0;ds_temp=0;}//不闪时可以刷新数据
Ir_Buf[2]=0xff;
}
//MUTE键值翻转并响铃
if ((Ir_Buf[0]==0x02)&&(Ir_Buf[2]==0x0c))
{
if(((ok==1)&&(zuo_you>7))&&(ds_temp>0))time_set();//如果从位闪变为全闪并且8秒写时钟窗口打开则写时钟
else mute=(!mute);//否则此键为静音转换功能
if(mute==0)
{
delay(10);
beep=0;
delay(20);
beep=1;
}
Ir_Buf[2]=0xff;
}
//按遥控器左键加一
if ((Ir_Buf[0]==0x02)&&(Ir_Buf[2]==0x11))
{
if(ok==0)mute=1;//如果没有闪屏按此键关闭声音
if(ok==1)
{
if(++zuo_you>7) zuo_you=0;//左移,超出苑围转到最右边
while(Led_Buf[zuo_you]>9){if((++zuo_you)>7) zuo_you=0;}//正在如果要闪动的位不是十则一直数还
Ir_Buf[2]=0xff;
}
}
//按遥控器右键减一
if ((Ir_Buf[0]==0x02)&&(Ir_Buf[2]==0x10))
{
if(ok==0)mute=0;//如果没有闪屏按此键打开声音
if(ok==1)
{
if((--zuo_you)==255) zuo_you=7;//右移,超出苑围转到最左边
while(Led_Buf[zuo_you]>9){if((--zuo_you)==255) zuo_you=7;}//正在闪动的位不是十进制数则一直移动开遥控
Ir_Buf[2]=0xff;
}
}
//按遥控器上键加一
if ((Ir_Buf[0]==0x02)&&(Ir_Buf[2]==0x12))
{
if((ok==1)&&(zuo_you<8))
{
Led_Buf[zuo_you]++;//位闪状态有上键按下对应位加一
if(Led_Buf[zuo_you]>9)Led_Buf[zuo_you]=0;//超出为0
}
else
{
zuo_you=8;//改变显示内容时位闪变全屏闪
if(++shan_xia>5) shan_xia=0;//超出为0
}
Ir_Buf[2]=0xff;
}
//按遥控器下键减一
if ((Ir_Buf[0]==0x02)&&(Ir_Buf[2]==0x13))
{
if((ok==1)&&(zuo_you<8))
{
Led_Buf[zuo_you]--;//位闪状态有上键按下对应位减一
if(Led_Buf[zuo_you]==255)Led_Buf[zuo_you]=9;//超出则为9
}
else
{
zuo_you=8;//改变显示内容时位闪变全屏闪
if((--shan_xia)==255) shan_xia=5;//自减,超出则为4
}
Ir_Buf[2]=0xff;
}
}
}
}
//数码管扫描===============================================================
timer0() interrupt 1 using 1//定时中断发生,运行中断函数
{
ms++;//屏闪时基
if(ms>=500) ms=0;
if(i_k==1) //不闪屏时允许刷新读写信息
{
if(ms==0){if((++i)>8)i=0;}//延时8秒给mute键的写时钟芯片时间窗口,
ds_temp=i;
}
if(ds_temp==0)i_k=0;
if(((ok==1)&&(zuo_you<8))||(ds_temp>0))ds=1;//在全屏闪5秒内和位闪调时时不允许刷新显存
else ds=0;//ds为何0时才允许刷新显存
TL0=0x25;//12M 2ms(测试这个晶振误差后的初值)
TH0=0xf8;
//以下9行消隐
dscs=1;//关掉时钟片选让数据接收口(74hc573数码管的位码和段码锁存器)得到纯净的数据信号
duan=0; // 关闭段选
wei=0; // 关闭位选
P0=0x00; // 消陷码
wei=1; // 打开位选
wei=0; // 关闭位选,锁存消隐码
P0=0xff; // 消隐码
duan=1; // 打开段选
duan=0; // 关闭段选,锁存消隐码
//数字显示 在锁存器的锁存端出现下降沿时锁存数据
P0=Led_Tab[Led_Buf_temp[Led_Index]];// 数模段码送P0口
duan=1;// 打开段选
duan=0;// 关闭段选,锁存段码
P0=Led_Sel[Led_Index];// 位码数据送P0口
wei=1;// 打开位选
wei=0;// 关闭位选,锁存位码
if(++Led_Index>7) Led_Index=0;
}
//初始化=============================================================================
void init()
{
//以下9行消隐 在开机瞬间消除屏闪
dscs=1;//关掉时钟片选让数据接收口(74hc573为数码管的位码和段码锁存器)得到纯静净的数据信号
duan=0;// 关闭段选
wei=0;// 关闭位选
P0=0xff;// 消隐码
duan=1;// 打开段选
duan=0;// 关闭段选,锁存消隐码
P0=0x00;// 消陷码
wei=1;// 打开位选
wei=0;// 关闭位选,锁存消隐码
//定时器设置
TMOD=0x11;
TL0=0xc5;//重装初值
TH0=0xf1;//数码管的刷新频率2ms
EA=1;//开定时总中断
ET0=1;//开定时器0中断 用于遥控高低电平时间采集
ET1=1;//开定时器1中断
TR0=1;//启动定时器1 用于数码管定时中断扫描
//以下是初始化时钟芯片的控制寄存器A和B
write_ds(0x0A,47); //写时钟芯片DS12C887+A寄存器,打开振荡器,启动记时 输出2HZ方波
write_ds(0x0B,126); //写时钟芯片B
}
//写时钟芯片DS12C887+时序========(请参考DS12C887芯片手册)
void write_ds(uchar add,uchar date)
{dscs=0;dsas=0;dsas=1;dsrw=1;dsds=1;P0=add;dsas=0;dsrw=0;P0=date;dsrw=1;dsas=1;dscs=1;}
//读时钟芯片DS12C887+时序========(请参考DS12C887芯片手册)
uchar read_ds(uchar add)
{uchar ds_date;dscs=0;dsas=0;dsas=1;dsrw=1;dsds=1;P0=add;dsas=0;
dsds=0;P0=0xff;ds_date=P0;dsds=1;dsas=1;dscs=1;return ds_date;}
//根据具体内容格式化输出函数
void led_out(uchar add_1,uchar add_2,uchar add_3,uchar add_4)
{
uchar a,b,c,d=add_4,i;
for(i=0;i<4;i++)
{
if(i==0) a=read_ds(add_1);
if(i==1) b=read_ds(add_2);
if(i==2) c=read_ds(add_3);
if(i==3) d=read_ds(add_4);
}
if(add_1==4)d=18;//时钟间隔位
if(add_1==5)d=16;//闹钟间隔位
if(add_1==4||add_1==5)//按不同的内容用不同的格式输出(参数特征决定输出内容)
{
Led_Buf[7]=a/10;//分离小时位数据
Led_Buf[6]=a%10;
Led_Buf[4]=b/10;//分离分钟位数据
Led_Buf[3]=b%10;
Led_Buf[1]=c/10;//分离秒钟位数据
Led_Buf[0]=c%10;
Led_Buf[5]=d; //间隔位
Led_Buf[2]=d;
}
else if(add_1==9)
{
Led_Buf[7]=2; //分离小时位数据
Led_Buf[6]=0;
Led_Buf[5]=a/10;
Led_Buf[4]=a%10;//分离分钟位数据
Led_Buf[3]=b/10;
Led_Buf[2]=b%10;
Led_Buf[1]=c/10;//分离秒钟位数据
Led_Buf[0]=c%10;
}
else for(i=0;i<8;i++) Led_Buf[i]=a;
}
//遥控码显示========================================================================
void ykm_out()
{
uchar y;//由于使用过的键会对用户码全置1操作,所以在此根据用户反码取反操作还原用户码
if(Ir_Buf[0]==0x02)y=(~Ir_Buf[3]);
else y=Ir_Buf[2];
Led_Buf[0]=Ir_Buf[3]&0xf;//结果装入显示缓冲区
Led_Buf[1]=(Ir_Buf[3]/16)&0xf;//数据反码
Led_Buf[2]=y&0xf;
Led_Buf[3]=(y/16)&0xf;//数据码
Led_Buf[4]=Ir_Buf[1]&0xf;
Led_Buf[5]=(Ir_Buf[1]/16)&0xf;//用户反码
Led_Buf[6]=Ir_Buf[0]&0xf;
Led_Buf[7]=(Ir_Buf[0]/16)&0xf;//用户码
}
//遥控数字键直接预调时==================================================================================
void ykm_set()//从最左第一位开始直接输入一个0--9的数时显示出来后自动右移一位,当到最右边时转到最左边
{
if((ok==1)&&((Ir_Buf[0]==0x02)&&(Ir_Buf[2]<10)))
{
if(zuo_you>7){zuo_you=7;ds=1;}//ds=1;保证此函数结束后不从写 Led_Buf[7]
while(Led_Buf[zuo_you]>9){if((--zuo_you)==255) zuo_you=7;}//闪动的第7位不是十进制数则一直移动
if(Ir_Buf[2]<9)Led_Buf[zuo_you]=Ir_Buf[2]+1;//因为我的遥控器按1时数据码是0x00,按2是0x01...,按0是0x09
else Led_Buf[zuo_you]=0;
if((--zuo_you)==255) {zuo_you=8;i_k=1;i=1;}//先右移,如果到最右边一位则转到最左边一位
while(Led_Buf[zuo_you]>9){if((--zuo_you)==255)zuo_you=8;}//正在闪动的位不是十进制数则一直移动
Ir_Buf[2]=0xff;
}
if((ok==0)&&((Ir_Buf[0]==0x02)&&(Ir_Buf[2]<10)))
{
if(Ir_Buf[2]<6)shan_xia=Ir_Buf[2];
}
}
//调时闪动函数========================================================================
void led_flash(uchar k,uchar z)//K为闪动开关,Z为闪动内容
{
uchar x,y,a=k;
if(z==1)y=17;
else if(z==2)y=18;
else y=19;
if(a==0)for(x=0;x<8;x++)Led_Buf_temp[x]=Led_Buf[x];//正常状态不闪动
else//当K值不为0里闪动
{
if(ms>220)for(x=0;x<8;x++){Led_Buf_temp[x]=Led_Buf[x];}//MS为闪动时基
else
{
if(zuo_you<=7)Led_Buf_temp[zuo_you]=y;
else for(x=0;x<8;x++)Led_Buf_temp[x]=y;
}
}
}
//延时void delay(uint z)
{ uint x,y;for(x=z;x>0;x--)for(y=500;y>0;y--);}
//红外遥控=========================================================================
void hyyk()
{
uint temp;
uchar i,j;
temp=Ir_Get_Low();//低电平时间采集
if(temp<c(8500) || temp>c(9500)) goto restart;//引导脉冲低电平在9ms 附近
temp=Ir_Get_High();//高电平时间采集
if(temp<c(4000) || temp>c(5000)) goto restart;//引导脉冲高电平在4。5ms 附近
for(i=0;i<4;i++)//4个字节
for(j=0;j<8;j++)//每个字节8位 共32bit装入Ir_Buf[4]
{
temp=Ir_Get_Low();
if(temp<c(200) || temp>c(800)) goto restart;//低电平时间符合
temp=Ir_Get_High();
if(temp<c(200) || temp>c(2000)) goto restart;//高电平时间符合
Ir_Buf[i]>>=1;//数据右移一位
if(temp>c(1120)) Ir_Buf[i]|=0x80;//高电平时间分析,大于1120us为1否则为0
}
if(mute==0)//静音功能,默认打开
{
beep=0;
delay(15);
beep=1;
}
restart://无效信号匀跳转至此 加速循环提高程式运行效率
TR1=0;//关定时器1中断
}
//红外低电平时间采集=======================================================================
uint Ir_Get_Low()
{ //定时器清0
TL1=0;
TH1=0;//开始记数
TR1=1;//完成一次记数
while(!Ir_Pin && (TH1&0x80)==0);
TR1=0;//关定时器
return TH1*256+TL1;//反回记数值
}
//红外高电平时间采集uint Ir_Get_High()
{
TL1=0;
TH1=0;
TR1=1;
while(Ir_Pin && (TH1&0x80)==0);
TR1=0;
return TH1*256+TL1;
}
//时间调整数据采集分析写调时缓冲区函数
void time_set()
{
uchar write[3]//write[3]为预写缓冲区
ok=0;//当写时钟芯片后不闪,并可以马上刷新屏幕
if((Led_Buf[2]==16)||(Led_Buf[2]==18))//此时显示的是闹钟或时间,那么预写缓冲区的数到显示缓冲区相应的数
{
write[0]=Led_Buf[0]+Led_Buf[1]*10; //预写秒位
write[1]=Led_Buf[3]+Led_Buf[4]*10; //预写分位
write[2]=Led_Buf[6]+Led_Buf[7]*10; //预写时位
if(Led_Buf[2]==18) //是时间格式特征
{
write_ds(0x04,write[2]); //写时 向时钟芯片ds12c887+写信息
write_ds(0x02,write[1]); //写分
write_ds(0x00,write[0]); //写秒
}
if(Led_Buf[2]==16) //是闹钟格式特征
{
write_ds(0x05,write[2]); //写时(闹钟) 向时钟芯片ds12c887+写信息
write_ds(0x03,write[1]); //写分(闹钟)
write_ds(0x01,write[0]); //写秒(闹钟)
}
}
if((Led_Buf[7]==2)&&(Led_Buf[6]==0)&&(Led_Buf[3]<2)&&(Led_Buf[1]<4))//是日期的格式特征
{
write[0]=Led_Buf[0]+Led_Buf[1]*10;//预写日
write[1]=Led_Buf[2]+Led_Buf[3]*10;//预写月
write[2]=Led_Buf[4]+Led_Buf[5]*10;//预写年
write_ds(0x09,write[2]); //写年 向时钟芯片ds12c887+写信息
write_ds(0x08,write[1]); //写月
write_ds(0x07,write[0]); //写日
}
if((Led_Buf[1]==Led_Buf[2])&&(Led_Buf[6]==Led_Buf[7]))//是星期格式特征(先排除时间与闹钟格式再排除日期格式)
{
write[0]=Led_Buf[7];
// if(write[0]>7)write[0]=0;
write_ds(0x06,write[0]); //写星期 向时钟芯片ds12c887+写信息
}
}
//以下全为温度传感函数
//延时
void delay_18b20(uint i)
{while(i--);}
//初始化函数
Init_DS18B20(void)
{
uchar x=0;
DQ = 1; //DQ复位
delay_18b20(8); //稍做延时
DQ = 0; //单片机将DQ拉低
delay_18b20(80); //精确延时 大于 480us
DQ = 1; //拉高总线
delay_18b20(14);
x=DQ; //稍做延时后 如果x=0则初始化成功 x=1则初始化失败
delay_18b20(20);
}
//读一个字节
ReadOneChar(void)
{
uchar i=0;
uchar dat = 0;
for (i=8;i>0;i--)
{
DQ = 0; //给脉冲信号
dat>>=1;
DQ = 1; //给脉冲信号
if(DQ)
dat|=0x80;
delay_18b20(4);
}
return(dat);
}
//写一个字节
WriteOneChar(unsigned char dat)
{
unsigned char i=0;
for (i=8; i>0; i--)
{
DQ = 0;
DQ = dat&0x01;
delay_18b20(5);
DQ = 1;
dat>>=1;
}
delay_18b20(4);
}
//读取温度
ReadTemperature(void)
{
unsigned char a=0;
unsigned char b=0;
unsigned char t=0;
Init_DS18B20();
WriteOneChar(0xCC);// 跳过读序号列号的操作
WriteOneChar(0x44);// 启动温度转换
Init_DS18B20();
WriteOneChar(0xCC);//跳过读序号列号的操作
WriteOneChar(0xBE);//读取温度寄存器等(共可读9个寄存器)前两个就是温度
a=ReadOneChar();//读取温度值低位
b=ReadOneChar();//读取温度值高位
a=a>>4;//低位右移4位,舍弃小数部分
t=b<<4;//高位左移4位,舍弃符号位
t=t|a;
return(t);
}
void out_18b20()//温度显示函数
{
int temp_18b20;
temp_18b20=ReadTemperature();
Led_Buf[7]=16;
Led_Buf[6]=16;
Led_Buf[5]=16;
Led_Buf[4]=16;
Led_Buf[3]=temp_18b20/10;
Led_Buf[2]=temp_18b20%10;
Led_Buf[1]=20;
Led_Buf[0]=21;
}
复制代码
欢迎光临 (http://www.51hei.com/bbs/)
Powered by Discuz! X3.1