基于stc15f204s的水晶倒计时牌
第一次在这发帖,如有不周请指正!
以前在网上发现了一个神操作,就是把搭棚电路封装在滴胶里,美观耐用且防止氧化。与其说是电路制作,我觉得是艺术品。寒假正好没事干,就搞了一个倒计时牌,把之前学的51单片机知识用一用,顺便督促一下自己珍惜时间。
刚开始是想用 STC15F104E(因为买多了)【注意,这个单片机内部定时器是T0和T1,跟现在的新产品不一样】 ,做了几个功能发现到处出bug,而且下载器里面没有这个型号,数据手册里面也只不过提了一两次。后来一查发现这个型号早已经停产了,而且它的定时器和外部中断由BUG(怒)。于是换了个stc15w204s。工欲善其事必先利其器,计划在面包板上先做程序,调试好确定管脚分配后再搭棚。面包板接触不好(通病),所以做了个单片机转接座:
程序不是很复杂,有的地方有点啰嗦(大神勿喷),这里简单解释一下,整体思路是单片机驱动hc595,595驱动三位数码管的段,单片机用9012扩流驱动数码管的位,int0借1838b红外接收,再加上ds1302时钟芯片和ds18b20温度芯片,还剩一个管脚接了rgb自闪灯(本来可以接红外发射实现数据双向传输的,不过我懒得写程序和功能了)平时单片机就是while(1)等待,刷新、ir接收什么的全靠两个定时器T0 T1和外部中断0触发。红外接收程序是我自己想的,非常繁琐(本可以简单一些,不过我这么写可以最大程度上减少误触发)。
单片机代码如下:- #include<STC15.h>
- #include<stdio.h>
- #include<INTRINS.H>
- sfr WDT_COUNTER = 0xc1;//【下载的时候请勾选‘上电复位由硬件启动看门狗’】
- //sbit CLR_WDT = WDT_COUNTER^5; //软件置1,防止误杀
- /*
- STC15w204s单片机,。数码管共阳,0亮1灭。
- 使用两片74hc595扩充管脚数目,连接ds1302时钟芯片和1838B红外接收头。
- **********IR接收程序(NEC协议)************
- 基于状态机,拒绝软件delay函数,只使用一个定
- 时器和一个中断(必须支持上升/下降沿中断),
- 有利于系统资源的节约。程序错误无累计,利于
- 长期稳定运行。使用定时器作为时间基准,如需
- 要移植程序,只需要按照源程序时间要求重新生
- 成定时器初始化函数。其中所有关键组件,均已
- 在其后注明【这是计划的一部分】。
- *******************************2020年2月20日
- BUG记录:
- int0输入信息量过大会造成程序跑飞
- */
- typedef unsigned char u8;//【这是计划的一部分】
- typedef unsigned int u16;
- //595引脚连接(待定)Q0对应低位,Q7对应高位
- sbit ser=P5^5; //也叫SI
- sbit srclk=P1^5; //刷新输出,上升沿有效
- sbit rclk=P1^4; //内部移位,上升沿有效
- //1838B
- sbit ir=P3^2;//【这是计划的一部分】
- //18B20
- sbit DSPORT=P5^4;
- //DS1302
- sbit RST=P1^3; //控制器复位 高电平时才能工作,其实与复位关系不大
- sbit DSIO=P1^2; //数据
- sbit SCLK=P1^1; //时
- //led
- sbit led=P1^0;
- //有关于红外接收的状态标志位,定时器用t0,中断用int0。【这是计划的一部分】
- bit iract=0; //红外活动
- u8 irstate=0; //红外接受状态0-6{未激活;报头低;报头升;报头高;报头降;高电平;低电平}
- bit started=0; //已收到报头,请忽视报尾
- bit t0reset=0; //定时器0溢出就回到等待状态
- bit int0reset=0; //int0触发就回到等待状态
- bit int0negfall=0; //忽视int0的下降沿(只允许上升沿中断)
- u8 bitamount=0; //收到的总位数(0-32)
- bit irbit=0; //当前ir位数
- u8 ir1=0x00,ir2=0xff,ir3=0x00,ir4=0xff;//ir接受结果
- bit irreceived=0;//如有待处理的红外信息,此位置1
- u8 irresult=0;//irdecode函数解码结果
- //system
- u16 data count=1000;
- u8 data stat=1;//0熄屏;1正常带温度;2正常无温度;3设置界面(3set,4h十位,5h个位,6d百位,7d十位,8d个位)
- u8 data days=123; //倒计时应该显示的天数,同步存放于0000h
- u8 data wk=0x00;//上次查询到的星期,将与此次查询到的比较同步存放于0001h
- u8 data keynum=0;//按键输入的数字
- //sbit led=P1^2;
- //显示相关变量
- u8 code duan[]={0x03,0x9f,0x25,0x0d,0x99,0x49,0x41,0x1f,0x01,0x09,0x11,0xc1,0x63,0x85,0x61,0x71,//0-15
- 0x02,0x9e,0x24,0x0c,0x98,0x48,0x40,0x1e,0x00,0x08,0x10,0xc0,0x62,0x84,0x60,0x70,//16-31
- 0xff,0xef,0x91};//数码管显示,0-9,A,b,C,d,E,F,不亮,(5)(14),_,H,(13).0-34
- u8 data num2,num1,num0;
- u8 data w=2;//数码管的位号
- sbit wei0=P3^3;
- sbit wei1=P3^7;
- sbit wei2=P3^6;
- //18b20
- int temp;//温度*100
- //1302
- //---DS1302时钟初始化2016年5月7日星期六12点00分00秒。---//
- //---存储顺序是秒,分,时, 日, 月, 星期, 周年,存储格式是用BCD码---//
- //u8 TIME[7] = {0, 0, 0x12, 0x07, 0x05, 0x06, 0x16};
- u8 TIME[7]= {0x00, 0x55, 0x23, 0x15, 0x10, 0x02, 0x19}; //星期是time[5],小时是time【2】
- u8 timeset[7]={0x00, 0x02, 0x13, 0x07, 0x12, 0x06, 0x19};
- //---DS1302写入和读取时分秒的地址命令---//
- //---秒分时日月周年 最低位读写位;-------//
- u8 code READ_RTC_ADDR[7] = {0x81, 0x83, 0x85, 0x87, 0x89, 0x8b, 0x8d};
- u8 code WRITE_RTC_ADDR[7] = {0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c}; //两者的区别只在于末位(发送的时候作为最先发送的)用于决定该命令是读还是写
- //基本函数
- void hc595write();
- void intinit();
- void Timer1Init();
- //红外相关函数
- void irinit();
- void irdecode();
- //温度相关函数
- void Ds18b20Init();
- void Ds18b20WriteByte(unsigned char dat);
- unsigned char Ds18b20ReadByte();
- int Ds18b20ReadTemp();
- //eeprom相关函数
- void iaperase(unsigned char addrh,unsigned char addrl);
- void iapwrite(unsigned char addrh,unsigned char addrl,unsigned char dat);
- unsigned char iapread(unsigned char addrh,unsigned char addrl);
- void iaprst();
- //ds1302相关函数
- void Ds1302Write(u8 addr, u8 dat);
- u8 Ds1302Read(u8 addr);
- void Ds1302set();
- void Ds1302ReadTime();
- void intinit()
- {
- EA=1;
- ET0=1;
- //ET1=1;
- IE2=0x04; //T2使能,T2的优先级固定为低。IE2不可位寻址,ET2在右数第三位。
- EX0=1; //P3.2中断使能
- IT0=1; //P3.2仅接受下降沿中断【这是计划的一部分】
- PX0=1; //P3.2中断优先级高,又因为自然优先级的缘故,高于T0。
- PT0=1; //T0优先级高
- //PT1=0; //T1优先级低
- }
- void Timer2Init(void) //5毫秒@11.0592MHz
- {
- AUXR &= 0xFB; //定时器时钟12T模式
- T2L = 0x00; //设置定时初值
- T2H = 0xEE; //设置定时初值
- AUXR |= 0x10; //定时器2开始计时
- }
- void irdecode()
- {
- switch(ir3)
- {
- case 0x16:
- irresult=0;
- break;
- case 0x0c:
- irresult=1;
- break;
- case 0x18:
- irresult=2;
- break;
- case 0x5e:
- irresult=3;
- break;
- case 0x08:
- irresult=4;
- break;
- case 0x1c:
- irresult=5;
- break;
- case 0x5a:
- irresult=6;
- break;
- case 0x42:
- irresult=7;
- break;
- case 0x52:
- irresult=8;
- break;
- case 0x4a:
- irresult=9;
- break;
- case 0x45:
- irresult=10;//电源键
- break;
- case 0x47:
- irresult=11;//菜单键
- break;
- case 0x15:
- irresult=12;//播放键
- break;
-
- }
- }
- void irinit() //红外接受状态0的初始化函数【这是计划的一部分】,状态标志初始化,定时器初始化,int0初始化,数据接收配置初始化
- {
- iract=0;
- irstate=0;//红外接受状态0-6{未激活;报头低;报头升;报头高;报头降;高电平;低电平}
- started=0;
- t0reset=0;
- int0reset=0;
- int0negfall=0;
- EX0=1; //P3.2中断使能
- IT0=1; //P3.2仅接受下降沿中断【这是计划的一部分】
- PX0=1;
-
- AUXR &= 0x7F; //定时器时钟12T模式
- TMOD &= 0xF0; //设置定时器模式(先清零)
- TMOD |= 0x01; //设置定时器模式(再写入)统一规定:T0,16位不自动重装载,12T。
- TF0 = 0; //清除TF0标志
- TR0 = 0; //定时器0不计时
-
- EX0=1; //P3.2中断使能
- IT0=1; //P3.2仅接受下降沿中断
- PX0=1; //P3.2中断优先级高,又因为自然优先级的缘故,高于T0。
-
- bitamount=0;
-
- }
- void hc595write(void) //给595输入数据c是数码管,最后送进去的那一位出现在q0
- {
- u8 t=0,a=0,shu=0x00;//t是循环变量,a是待发送的值,先发送级联的那片,显示8段,再发送直连的
- wei2=1;
- wei1=1;
- wei0=1;
- switch(w)
- {
- case 0:
- shu=duan[num0];
- break;
- case 1:
- shu=duan[num1];
- break;
- case 2:
- shu=duan[num2];
- break;
- }
- for(t=0;t<=7;t++)//数字区,8位
- {
- if((shu&0x01)==0)
- ser=0;
- else
- ser=1;
- rclk=1;
- rclk=0;
- shu=shu>>1;
- }
- if(w>=2)//可以控制扫描顺序
- w=0;
- else
- w++;
- srclk=1;
- srclk=0;
- switch(w)
- {
- case 0:
- wei0=0;
- break;
- case 1:
- wei1=0;
- break;
- case 2:
- wei2=0;
- break;
- }
- }
- void main() // 我是main函数
- {
- int a=3;
- led=1;
- srclk=0;
- rclk=0;
- ser=0;
- wk=iapread(0x00,0x01);
- days=iapread(0x00,0x00);
- intinit(); //【这是计划的一部分】
- Timer2Init();
- WDT_COUNTER=0x37;//【这不是计划的一部分】
- irinit();
- num2=24;
- num1=24;
- num0=24;//试机
- while(1)
- {
- //跑飞的时候while(1)里面的内容仍然正常执行
- }
- }
- void timer2() interrupt 12//正常显示的扫描刷新步骤
- {
- if(irreceived==1)//有按键刚刚按下且未处理
- {
- irdecode();
- switch(irresult)
- {
- case 10://电源键
- {
- if(stat==0)
- {
- stat=1;
- num2=days/100;
- num1=(days%100)/10;
- num0=days%10;
- }
- else
- {
- stat=0;
- num2=32;
- num1=32;
- num0=32;
- hc595write();
- }
- break;
- }
- case 11://菜单键
- {
- if(stat<=2)
- {
- stat=3;//确认进入设置模式
- num2=34;//H
- num1=33;//_
- num0=34;//H
- }
- else
- {
- stat=1;
- }
- break;
- }
- case 12://播放键
- {
- if(stat==1)
- stat=2;
- if(stat==2)
- stat=1;
- break;
- }
- case 0:
- led=~led;
- case 1:
- case 2:
- case 3:
- case 4:
- case 5:
- case 6:
- case 7:
- case 8:
- case 9:
- keynum=irresult;//先记录下键值
- switch (stat)
- {
- case 3:
- timeset[2]=keynum*16;
- num1=keynum;
- num0=33;//_
- stat=4;
- break;
- case 4:
- timeset[2]=timeset[2]+keynum;
- num2=33;//_
- num1=13;//d
- num0=13;//d
- stat=5;
- break;
- case 5:
- days=keynum*100;
- num2=keynum;
- num1=33;//_
- num0=13;//d
- stat=6;
- break;
- case 6:
- days=days+keynum*10;
- num1=keynum;
- num0=33;//_
- stat=7;
- break;
- case 7:
- days=days+keynum;
- Ds1302set();//向ds1302写入小时
- iaperase(0x00,0x00);
- iapwrite(0x00,0x00,days);
- Ds1302ReadTime();
- wk=TIME[5];
- iapwrite(0x00,0x01,wk);//向eeprom写入正确的天数和星期
- num2=days/100;
- num1=(days%100)/10;
- num0=days%10;//更新显示内容
- stat=1;
- break;
- }
- break;
- }
- irreceived=0;
- }
- if(count>0)
- {
- count--;
- if(count==1000)
- WDT_COUNTER=WDT_COUNTER|0x10; //WDT喂狗【这是计划的一部分】
- }
- else
- {
- count=2000;//count溢出之时,应向时钟芯片询问日期,1000->5s
- WDT_COUNTER=WDT_COUNTER|0x10; //WDT喂狗【这是计划的一部分】
- Ds1302ReadTime();
- if(wk!=TIME[5])//星期数发生变化
- {
- wk=TIME[5];//更新星期
- if(days>0)
- days--;//更新天数
- else
- stat=0;
- iaperase(0x00,0x00);
- iapwrite(0x00,0x00,days);
- iapwrite(0x00,0x01,wk);//向eeprom写入正确的天数和星期
- }
- }
- if((stat==1)&&(count==1500))//
- {
- temp=Ds18b20ReadTemp();
- num2=temp % 10000 / 1000;
- num1=(temp % 1000 / 100)+16;
- num0=temp % 100 / 10;
- }
- else if((stat==1|stat==2)&&count==1000)
- {
- num2=days/100;
- num1=(days%100)/10;
- num0=days%10;
- //num2=33;
- //num1=TIME[2]/16;
- //num0=TIME[2]%16;
- }
-
- if(stat==0)
- {
-
- }
- else
- {
- hc595write();
- }
- }
- void timer0() interrupt 1
- {
- if(t0reset==0)//定时器0溢出就回到等待状态标志位
- {
- if(irstate==1)
- {
- irstate=2; //红外接受状态0-6{未激活;报头低;报头升;报头高;报头降;高电平;低电平}
- t0reset=1; //定时器0溢出就回到等待状态
- int0reset=0; //int0触发就回到等待状态
- int0negfall=1; //忽视int0的下降沿(只允许上升沿中断)
- IT0=0; //P3.2=1仅接收下降沿中断,=0上下皆可
-
- AUXR &= 0x7F; //定时器时钟12T模式,2ms
- TMOD &= 0xF0; //设置定时器模式
- TMOD |= 0x01; //设置定时器模式
- TL0 = 0xCD; //设置定时初值
- TH0 = 0xF8; //设置定时初值
- TF0 = 0; //清除TF0标志
- TR0 = 1; //定时器0开始计时
- }
- else if(irstate==3)
- {
- irstate=4; //红外接受状态0-6{未激活;报头低;报头升;报头高;报头降;高电平;低电平}
- t0reset=1; //定时器0溢出就回到等待状态
- int0reset=0; //int0触发就回到等待状态
- int0negfall=1; //忽视int0的下降沿(只允许上升沿中断)
- IT0=0; //=1仅接收下降沿中断,=0上下皆可
-
- AUXR &= 0x7F; //定时器时钟12T模式 2.5ms
- TMOD &= 0xF0; //设置定时器模式
- TMOD |= 0x01; //设置定时器模式
- TL0 = 0x00; //设置定时初值
- TH0 = 0xF7; //设置定时初值
- TF0 = 0; //清除TF0标志
- TR0 = 1; //定时器0开始计时
- }
- else if(irstate==5)//重头戏:判断逻辑值【6-long】
- {
- irstate=6; //红外接受状态0-6{未激活;报头低;报头升;报头高;报头降;
- irbit=1;
- //out=0;
-
- t0reset=0; //定时器0溢出就回到等待状态
- int0reset=0; //int0触发就回到等待状态
- int0negfall=1; //忽视int0的下降沿(只允许上升沿中断)
- IT0=0; //=1仅接收下降沿中断,=0上下皆可
-
- AUXR &= 0x7F; //定时器时钟12T模式 1200us
- TMOD &= 0xF0; //设置定时器模式
- TMOD |= 0x01; //设置定时器模式
- TL0 = 0xAE; //设置定时初值
- TH0 = 0xFB; //设置定时初值
- TF0 = 0; //清除TF0标志
- TR0 = 1; //定时器0开始计时
-
- if(bitamount<=7) //【禁忌二重奏之一】
- {
- ir1=ir1>>1;
- ir1=ir1+(irbit*0x80);
- }
- else if(bitamount<=15)
- {
- ir2=ir2>>1;
- ir2=ir2+(irbit*0x80);
-
- }
- else if(bitamount<=23)
- {
- ir3=ir3>>1;
- ir3=ir3+(irbit*0x80);
- }
- else
- {
- ir4=ir4>>1;
- ir4=ir4+(irbit*0x80);
- }
-
- bitamount++;
-
- if(bitamount>=32)
- {
- irreceived=1;
- irinit();
-
- }
- }
- }
- else
- {
-
- irinit();
- }
- }
- void int0() interrupt 0
- {
- _nop_();
- _nop_();
- _nop_();
- if((ir==1)|(int0negfall==0))//如果“忽视下降沿”标志位为1时(&)为下降沿(输入端=0),就不能执行下面的程序。
- {
- if(int0reset==1)//如果“触发中断即复位”标志位为1,就立刻复位。
- {
-
- irinit();
-
- }
- else//好戏开始
- {
- if(irstate==0)//报头低开始 0未激活;1报头低;2报头升;3报头高;4报头降;5高电平;6低电平
- {
- iract=1; //红外活动
- irstate=1; //红外接受状态0-6{未激活;报头低;报头升;报头高;报头降;高电平;低电平}
- t0reset=0; //定时器0溢出就回到等待状态
- int0reset=1; //int0触发就回到等待状态
- int0negfall=0; //忽视int0的下降沿(只允许上升沿中断)
- IT0=0; //=1仅接收下降沿中断,=0上下皆可
-
- AUXR &= 0x7F; //8毫秒@11.0592MHz 定时器时钟12T模式
- TMOD &= 0xF0; //设置定时器模式
- TMOD |= 0x01; //设置定时器模式
- TL0 = 0x33; //设置定时初值
- TH0 = 0xE3; //设置定时初值
- TF0 = 0; //清除TF0标志
- TR0 = 1; //定时器0开始计时
-
- }
- else if(irstate==2)//报头高开始
- {
- irstate=3; //红外接受状态0-7{未激活;报头低;报头升;报头高;报头降;高电平1;高电平2;低电平}
- t0reset=0; //定时器0溢出就回到等待状态
- int0reset=1; //int0触发就回到等待状态
- int0negfall=0; //忽视int0的下降沿(只允许上升沿中断),下降沿此时不会触发reset
- IT0=0; //=1仅接收下降沿中断,=0上下皆可
-
- AUXR &= 0x7F; //定时器时钟12T模式 3.5ms
- TMOD &= 0xF0; //设置定时器模式
- TMOD |= 0x01; //设置定时器模式
- TL0 = 0x66; //设置定时初值
- TH0 = 0xF3; //设置定时初值
- TF0 = 0; //清除TF0标志
- TR0 = 1; //定时器0开始计时
-
- }
- else if(irstate==4)//高电平开始【5-first】
- {
- irstate=5; //红外接受状态0-6{未激活;报头低;报头升;报头高;报头降;高电平;低电平}
- t0reset=0; //定时器0溢出就回到等待状态
- int0reset=0; //int0触发就回到等待状态
- int0negfall=0; //忽视int0的下降沿(只允许上升沿中断)
- IT0=1; //P3.2=1仅接收下降沿中断,=0上下皆可
-
- AUXR &= 0x7F; //定时器时钟12T模式 840us
- TMOD &= 0xF0; //设置定时器模式
- TMOD |= 0x01; //设置定时器模式
- TL0 = 0xFA; //设置定时初值
- TH0 = 0xFC; //设置定时初值
- TF0 = 0; //清除TF0标志
- TR0 = 1; //定时器0开始计时
-
- //out=1;
-
- if(started==0)//收到的是报头
- {
- started=1;
- }
- else//收到的是报尾
- {
- iract=0;
- started=0;
- irinit();
- }
- }
- else if(irstate==5)//低电平开始【6-short】
- {
- irstate=6; //红外接受状态0-6{未激活;报头低;报头升;报头高;报头降;高电平;低电平}
- irbit=0;
- //out=0;
-
- t0reset=1; //定时器0溢出就回到等待状态
- int0reset=0; //int0触发就回到等待状态
- int0negfall=1; //忽视int0的下降沿(只允许上升沿中断)
- IT0=0; //P3.2=1仅接收下降沿中断,=0上下皆可
-
- AUXR &= 0x7F; //定时器时钟12T模式 840us
- TMOD &= 0xF0; //设置定时器模式
- TMOD |= 0x01; //设置定时器模式
- TL0 = 0xFA; //设置定时初值
- TH0 = 0xFC; //设置定时初值
- TF0 = 0; //清除TF0标志
- TR0 = 1; //定时器0开始计时
-
- if(bitamount<=7) //【禁忌二重奏之二】
- {
- ir1=ir1>>1;
- ir1=ir1+(irbit*0x80);
- }
- else if(bitamount<=15)
- {
- ir2=ir2>>1;
- ir2=ir2+(irbit*0x80);
-
- }
- else if(bitamount<=23)
- {
- ir3=ir3>>1;
- ir3=ir3+(irbit*0x80);
- }
- else
- {
- ir4=ir4>>1;
- ir4=ir4+(irbit*0x80);
- }
-
- bitamount++;
-
- if(bitamount>=32)
- {
- irreceived=1;
- irinit();
-
- }
- }
- else if(irstate==6)//收到上升沿信号,由低电平转为高电平状态【5-repeat】
- {
- irstate=5; //红外接受状态0-6{未激活;报头低;报头升;报头高;报头降;高电平;低电平}
- t0reset=0; //定时器0溢出就回到等待状态
- int0reset=0; //int0触发就回到等待状态
- int0negfall=0; //忽视int0的下降沿(只允许上升沿中断)
- IT0=1; //P3.2=1仅接收下降沿中断,=0上下皆可
-
- AUXR &= 0x7F; //定时器时钟12T模式 840us
- TMOD &= 0xF0; //设置定时器模式
- TMOD |= 0x01; //设置定时器模式
- TL0 = 0xFA; //设置定时初值
- TH0 = 0xFC; //设置定时初值
- TF0 = 0; //清除TF0标志
- TR0 = 1; //定时器0开始计时
-
- //out=1;
-
- }
-
- }
- }
-
- }
- /*
- 日志:在完成了数码管显示和定时
- 器中断的基础上,新增了状态机查
- 询红外信号输入的功能,在15w204
- s上能稳定运行。2020-2-21
- */
- void Ds18b20Init()
- {
- unsigned char i,j;
- DSPORT=1; //将总线拉高4us
- _nop_();
- _nop_();
- i = 8;
- while (--i);
- DSPORT=0; //然后拉低总线480us
- i = 6;
- j = 38;
- do
- {
- while (--j);
- } while (--i);
- DSPORT=1; //然后拉高总线,如果DS18B20做出反应会将在15us~60us后总线拉低
- i = 6;
- j = 38;
- do
- {
- while (--j);
- } while (--i);
- }
- void Ds18b20WriteByte(unsigned char dat)
- {
- unsigned char i,j,e;
- for(e=8;e>0;e--)
- {
- DSPORT=0; //每写入一位数据之前先把总线拉低1us
- _nop_();
- _nop_();
- _nop_();
- DSPORT=dat&0x01; //然后写入一个数据,从最低位开始
- i = 1;//延时68us,持续时间最少60us
- j = 184;
- do
- {
- while (--j);
- } while (--i);
- DSPORT=1; //然后释放总线,至少1us给总线恢复时间才能接着写入第二个数值
- dat>>=1;
- }
- }
- unsigned char Ds18b20ReadByte()
- {
- unsigned char byte,i,j;
- for(j=8;j>0;j--)
- {
- DSPORT=0;//先将总线拉低1us
- _nop_();
- _nop_();
- _nop_();
- byte>>=1;
- DSPORT=1;//然后释放总线
- _nop_();//延时15us等待数据稳定[]
- i = 39;
- while (--i);
- if(DSPORT)
- byte|=0x80;
- i = 122;//delay45us
- while (--i);
- }
- return byte;
- }
- int Ds18b20ReadTemp()
- {
- int temp=0;
- int tp;
- u8 i,j;
- unsigned char tmh,tml;
- Ds18b20Init();
- Ds18b20WriteByte(0xcc);
- Ds18b20WriteByte(0x44);
- _nop_();//delay125us
- _nop_();
- i = 2;
- j = 84;
- do
- {
- while (--j);
- } while (--i);
- Ds18b20Init();
- Ds18b20WriteByte(0xcc);
- Ds18b20WriteByte(0xbe);
- tml=Ds18b20ReadByte();
- tmh=Ds18b20ReadByte();
-
- temp=tmh;
- temp<<=8;
- temp|=tml;
- if((temp&0xf800)==0xf800) //*0.0625是把16进制转化位10进制所需,原始信息里的16代表1摄氏度。
- {
- temp=temp-1;//如果温度是零下,此处未加入负温度标志位,所以显示的还是正的
- temp=~temp;
- tp=temp;
- temp=tp*0.0625*100+0.5;
- }
- else
- {
- tp=temp;//如果温度是零上
- temp=tp*0.0625*100+0.5;
- }
- return temp;
- }
- /*
- 日志 移植了某大佬的ds18b20程序,
- 并使之能在stc-y5内核单片机上运
- 行。正常情况下输出的16bit信息可
- 以表示12位精度的温度,但是stc单
- 片机不支持浮点数运算,所以temp
- 是乘了100的。所有延时函数(微秒
- 级)均已内置,无需调用,也不费
- 什么时间。 2020-2-24
- */
- //eeprom操作--IAP法
- void iaprst() //复位
- {
- IAP_CONTR=0;
- IAP_CMD=0;
- IAP_TRIG=0;
- IAP_ADDRH=0x80;
- }
- unsigned char iapread(unsigned char addrh,unsigned char addrl)
- {
- unsigned char dat;
- IAP_CONTR=0x83; //适用于12mhz以下的频率
- IAP_CMD=1;
- IAP_ADDRL=addrl;
- IAP_ADDRH=addrh;
- IAP_TRIG=0x5a;
- IAP_TRIG=0xa5;
- _nop_();
- _nop_();
- dat=IAP_DATA;
- iaprst();
- return dat;
- }
- void iapwrite(unsigned char addrh,unsigned char addrl,unsigned char dat)
- {
- IAP_CONTR=0x83;//适用于12mhz以下的频率
- IAP_CMD=2;
- IAP_ADDRL=addrl;
- IAP_ADDRH=addrh;
- IAP_DATA=dat;
- IAP_TRIG=0x5a;
- IAP_TRIG=0xa5;
- _nop_();
- _nop_();
- iaprst();
- }
- void iaperase(unsigned char addrh,unsigned char addrl)
- {
- IAP_CONTR=0x83;//适用于12mhz以下的频率
- IAP_CMD=3;
- IAP_ADDRL=addrl;
- IAP_ADDRH=addrh;
- IAP_TRIG=0x5a;
- IAP_TRIG=0xa5;
- _nop_();
- _nop_();
- iaprst();
- }
- /*
- 日志 增加了关于eeprom的操作的函数
- ,现学现卖,一共就三个函数(iapinit
- 不需要管),204s就只有两个扇区,
- 0000-01ff,0200-03ff,每个地址号
- 都能存储一个char,一共是1024个。
- 使用方法:(先读出原有数据,)再
- 擦除扇区(擦除地址是扇区的首地址
- ),然后写入新值,(最后读出来)。
- 打算用0000存储倒计时还剩多少天,
- 0001存储当前的日期。程序上电先把
- 日期和倒数读到ram里,每隔几秒询
- 问1302,若有不同,则ram日期更新
- ,倒数减一(检测大于0),重写
- eeprom。 2020-2-25
- */
- /*******************************************************************************
- * 函 数 名 : Ds1302Write
- * 函数功能 : 向DS1302命令(地址+数据)
- * 输 入 : addr,dat
- * 输 出 : 无
- *******************************************************************************/
- void Ds1302Write(u8 addr, u8 dat)
- {
- u8 n;
- RST = 0;
- _nop_();
- SCLK = 0;//先将SCLK置低电平。
- _nop_();
- RST = 1; //然后将RST(CE)置高电平。
- _nop_();
- for (n=0; n<8; n++)//开始传送八位地址命令
- {
- DSIO = addr & 0x01;//数据从低位开始传送
- addr >>= 1;
- SCLK = 1;//数据在上升沿时,DS1302读取数据
- _nop_(); //时钟高电平时数据需要保持不动
- SCLK = 0;
- _nop_();
- }
- for (n=0; n<8; n++)//写入8位数据
- {
- DSIO = dat & 0x01;
- dat >>= 1;
- SCLK = 1;//数据在上升沿时,DS1302读取数据
- _nop_();
- SCLK = 0;
- _nop_();
- }
-
- RST = 0;//传送数据结束
- _nop_();
- }
- /*******************************************************************************
- * 函 数 名 : Ds1302Read
- * 函数功能 : 读取一个地址的数据
- * 输 入 : addr
- * 输 出 : dat
- *******************************************************************************/
- u8 Ds1302Read(u8 addr)
- {
- u8 n,dat,dat1;
- RST = 0;
- _nop_();
- SCLK = 0;//先将SCLK置低电平。
- _nop_();
- RST = 1;//然后将RST(CE)置高电平。
- _nop_();
- for(n=0; n<8; n++)//开始传送八位地址命令
- {
- DSIO = addr & 0x01;//数据从低位开始传送
- addr >>= 1;
- SCLK = 1;//数据在上升沿时,DS1302读取数据
- _nop_();
- SCLK = 0;//DS1302下降沿时,放置数据
- _nop_();
- }
- _nop_();
- for(n=0; n<8; n++)//读取8位数据
- {
- dat1 = DSIO;//从最低位开始接收
- dat = (dat>>1) | (dat1<<7);
- SCLK = 1;
- _nop_();
- SCLK = 0;//DS1302下降沿时,放置数据
- _nop_();
- }
- RST = 0;
- _nop_(); //以下为DS1302复位的稳定时间,必须的。
- SCLK = 1;
- _nop_();
- DSIO = 0;
- _nop_();
- DSIO = 1;
- _nop_();
- return dat;
- }
- /*******************************************************************************
- * 函 数 名 : Ds1302Init
- * 函数功能 : 初始化DS1302.
- * 输 入 : 无
- * 输 出 : 无
- *******************************************************************************/
- void Ds1302set()
- {
- u8 n;
- Ds1302Write(0x8E,0X00); //关闭写保护功能
- for (n=0; n<7; n++)//写入7个字节的时钟信号:分秒时日月周年 !!!此操作会破坏原有的时间数据!!!
- {
- Ds1302Write(WRITE_RTC_ADDR[n],timeset[n]);
- }
- Ds1302Write(0x8E,0x80); //打开写保护功能
- }
- /*******************************************************************************
- * 函 数 名 : Ds1302ReadTime
- * 函数功能 : 读取时钟信息
- * 输 入 : 无
- * 输 出 : 无
- *******************************************************************************/
- void Ds1302ReadTime()
- {
- u8 n;
- for (n=0; n<7; n++)//读取7个字节的时钟信号:分秒时日月周年
- {
- TIME[n] = Ds1302Read(READ_RTC_ADDR[n]);
- }
-
- }
- /*
- 添加了ds1302函数,通过全局变量传递
- 数据。通过每5秒钟检测一下星期并与
- ram里面的值比较,若有变化就日期减
- 一,并更新eeprom里面的值。注意ds1302
- 的数据格式,比如’13点’不是0x0c,
- 而是0x13,此之谓8421bcd码.
- */
复制代码 stc15.h可以用stc下载工具添加了mcu型号到keil中后再keil的文件夹里面找到,所有的函数都在里面了,(帮助没黑币的小白真正实现白嫖),标有这是计划的一部分的是与ir接收相关的函数(包括看门狗设置)。说实在的,这是我首次给51单片机写这么长的代码。里面有的函数是我之前学的时候借鉴坛里大神的,在此表示感谢。

硬件制作
搭棚过程是先从数码管做起的(隔壁五家的垃圾数码管虽然便宜,可挑了半天才找到个好的)
搭棚的过程没怎么拍全,因为当时自己也没有做下去的信心
|