![]() 用于指示星期和冒号的氖灯灯管做好了,这下可以跟辉光管完美搭配了,因为有了底座,所以很容易固定在电路板上,所以马上把它们各就各位,效果出乎我的想想,实在是太般配了。 看一下整体效果吧,高度几乎一致,而且氖灯位于顶端,发光的位置也刚好,并且粗细作为小点来说刚刚合适,不粗也不细。 SHOW一下: ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() 六、编写译码电路驱动程序 74HC595是很常用的器件,3线串口通讯,每个驱动芯片提供8个译码输出,以前常用于LED数码管的驱动,加上小数点正好8段。但这次使用的是辉光管,每显示一个数字就需要有一个驱动,所以为了6个辉光管每个数字都能亮,一共需要60个译码输出,这就要用到8片74HC595芯片。 这颗芯片很常用,看了一下DATASHEET,很快就做好了通讯程序,下面我把74HC595的通讯代码及控制各辉光管显示数字的代码部分贴出来: /******************************************************************************************************** ** 功能描述: 向74HC595发送数据 ** 入口参数: count: 发送数据的位数 ** dat: 发送的数据(MAX 16Bit) ** 创 建 者: 严泽远 ** 创建时间: 2010-11-21 13:09 ** 版 本: v1.0.0 ********************************************************************************************************/ void send_data(uchar count,uint dat) { for(;count>0;count--) { DATA = dat&0x0001;_nop_(); SHCP = 1;_nop_();SHCP = 0;_nop_(); dat>>=1; } } /******************************************************************************************************** ** 功能描述: 刷新QS30-1辉光管的显示数字 ** 入口参数: N1: 第一位显示的数 ** N2: 第一位显示的数 ** N3: 第一位显示的数 ** N4: 第一位显示的数 ** N5: 第一位显示的数 ** N6: 第一位显示的数 ** 创 建 者: 严泽远 ** 创建时间: 2010-11-21 14:23 ** 版 本: v1.0.0 ********************************************************************************************************/ void DispNum(uchar N1,uchar N2,uchar N3,uchar N4,uchar N5,uchar N6) { uchar buf,buf2; if(N1>9) buf = 0x00; else buf = 0x08>>N1; send_data(8,buf); //U1 if(N1>9) buf = 0x00; else buf = 0x80>>(N1-4); if(N2>9) buf2= 0x00; else buf2= 0x02>>N2; send_data(8,buf|buf2); //U2 if(N2>9) buf = 0x00; else buf = 0x80>>(N2-2); send_data(8,buf); //U3 if(N3>9) buf = 0x00; else buf = 0x80>>N3; send_data(8,buf); //U4 if(N3>9) buf = 0x00; else buf = 0x80>>(N3-8); if(N4>9) buf2= 0x00; else buf2= 0x20>>N4; send_data(8,buf|buf2); //U5 if(N4>9) buf = 0x00; else buf = 0x80>>(N4-6); if(N5>9) buf2= 0x00; else buf2= 0x08>>N5; send_data(8,buf|buf2); //U6 if(N5>9) buf = 0x00; else buf = 0x80>>(N5-4); if(N6>9) buf2= 0x00; else buf2= 0x02>>N6; send_data(8,buf|buf2); //U7 if(N6>9) buf = 0x00; else buf = 0x80>>(N6-2); send_data(8,buf); //U8 STCP = 1;_nop_();STCP = 0;_nop_(); //数据锁存} 这段程序调试成功了以后就能够在每个辉光管上显示任意字符了,有模有样的效果出来啦,SHOW一下: ![]() 七、编写DS1302时钟芯片读写程序 DS1302也是很常用的一款时钟芯片,外围元器件非常少,仅一个电池,一颗晶体,一颗电容即可,通过3线与MCU通讯, 具体介绍网上一搜一大把,时序也很简单,以前在其他作品上用过这个芯片,程序也很简单,调试几分钟就OK,下面我也把代码贴出来,可以给朋友们做个参考: /****************************************Copyright (c)************************************************** ** ** 项目名称: QS30-1辉光钟 目标板试验程序 ** 文件名称: DS1302.c ** 模块功能: 实现DS1302时钟芯片的完全控制 ** **------------------------------------------------------------------------------------------------------ ** 创 建 者: 严泽远 ** E-mail : yanzeyuan@163.com ** QQ : 6626209 ** Mobile : 18602007878 ** 创建时间: 2010-11-19 ** 版 本: v1.0.0 ** 描 述: 基础程序 ** ********************************************************************************************************/ #include <C8051F310.h> //加载C8051F310.h头文件 #include <Define.h> //加载Define.h头文件 #include <DS1302Function.h> //加载DS1302Function.h头文件 #include <DS1302Extern.h> //加载DS1302Extern.h头文件 /******************************************************************************************************** ** 功能描述: 往DS1302写入1Byte数据 ** 入口参数: ucDa 写入的数据 ** 创 建 者: 严泽远 ** 创建时间: 2010-02-05 23:52 ** 版 本: v2.1.3 ********************************************************************************************************/ void v_RTInputByte(uchar ucDa) { xdata uchar i; xdata uchar j; j = ucDa; for(i=8; i>0; i--) { T_IO = (j&0x01); /*相当于汇编中的 RRC */ T_CLK = 1; _nop_();_nop_(); T_CLK = 0; j=j>>1; } } /******************************************************************************************************** ** 功能描述: 从DS1302读取1Byte数据 ** 出口参数: ACC ** 创 建 者: 严泽远 ** 创建时间: 2010-02-06 00:11 ** 版 本: v2.1.3 ********************************************************************************************************/ uchar uc_RTOutputByte(void) { uchar i; uchar j; for(i=8; i>0; i--) { j=j>>1; if(T_IO){j|=0x80;}else{j&=0x7f;} T_CLK = 1; _nop_();_nop_(); T_CLK = 0; } return(j); } /******************************************************************************************************** ** 功能描述: 往DS1302写入数据 ** 入口参数: ucAddr: DS1302地址 ** ucDa: 要写的数据 ** 创 建 者: 严泽远 ** 创建时间: 2010-02-06 00:11 ** 版 本: v2.1.3 ********************************************************************************************************/ void v_W1302(uchar ucAddr, uchar ucDa) { T_RST = 0; T_CLK = 0; T_RST = 1; _nop_();_nop_(); v_RTInputByte(ucAddr); /* 地址,命令 */ v_RTInputByte(ucDa); /* 写1Byte数据*/ T_CLK = 1; T_RST =0; } /******************************************************************************************************** ** 功能描述: 读取DS1302某地址的数据 ** 入口参数: ucAddr: DS1302地址 ** 创 建 者: 严泽远 ** 创建时间: 2010-02-06 00:17 ** 版 本: v2.1.3 ********************************************************************************************************/ uchar uc_R1302(uchar ucAddr) { uchar ucDa; T_RST = 0; T_CLK = 0; T_RST = 1; _nop_();_nop_(); v_RTInputByte(ucAddr); /* 地址,命令 */ ucDa = uc_RTOutputByte(); /* 读1Byte数据 */ T_CLK = 1; T_RST =0; return(ucDa); } /******************************************************************************************************** ** 功能描述: 设置初始时间 ** 入口参数: pSecDa: 初始时间地址。初始时间格式为: 秒 分 时 日 月 星期 年 ** 7Byte (BCD码) 1B 1B 1B 1B 1B 1B 1B ** 创 建 者: 严泽远 ** 创建时间: 2010-02-06 01:02 ** 版 本: v2.1.3 ********************************************************************************************************/ void v_Set1302(uchar *pSecDa) { uchar i; uchar ucAddr = 0x80; v_W1302(0x8e,0x00); /* 控制命令,WP=0,写操作?*/ for(i =7;i>0;i--) { v_W1302(ucAddr,*pSecDa); /* 秒 分 时 日 月 星期 年 */ pSecDa++; ucAddr +=2; } v_W1302(0x8e,0x80); /* 控制命令,WP=1,写保护?*/ } /******************************************************************************************************** ** 功能描述: 读取DS1302当前时间 ** 入口参数: ucCurtime: 保存当前时间地址。当前时间格式为: 秒 分 时 日 月 星期 年 ** 7Byte (BCD码) 1B 1B 1B 1B 1B 1B 1B ** 创 建 者: 严泽远 ** 创建时间: 2010-02-06 01:02 ** 版 本: v2.1.3 ********************************************************************************************************/ void v_Get1302(uchar ucCurtime[]) { uchar i; uchar ucAddr = 0x81; for (i=0;i<7;i++) { ucCurtime = uc_R1302(ucAddr);/*格式为: 秒 分 时 日 月 星期 年 */ ucAddr += 2; } } 七、编写18B20温度传感器读写程序 18B20我就更不用多介绍了,常用的不得了,他的封装就像一个直插的三极管一样,只有三个管脚,电源两个脚,通讯只需要一个管脚。这也是它的特点,单线接口。而且不需要任何外围元器件,温度测量范围是-55℃至125℃,所以我说用在测量室温里面的确有点大材小用,呵呵。 虽然读写都用一个口线,但是通讯时序并不复杂,也很简单,很容易调试出来,当然在这里我只用了读取温度的功能,其他还有很多功能比如温度告警设置,唯一序列号读取等等都没有用,程序还是很简单的,我也贴出来,有用的朋友们可以借鉴一下。 /****************************************Copyright (c)************************************************** ** ** 项目名称: QS30-1辉光钟 目标板试验程序 ** 文件名称: 18B20.c ** 模块功能: 实现18B20温度传感器的温度读取 ** **------------------------------------------------------------------------------------------------------ ** 创 建 者: 严泽远 ** E-mail : [email=yanzeyuan@163.com]yanzeyuan@163.com[/email] ** QQ : 6626209 ** Mobile : 18602007878 ** 创建时间: 2010-11-19 ** 版 本: v1.0.0 ** 描 述: 基础程序 ** ********************************************************************************************************/ #include <C8051F310.h> //加载C8051F310.h头文件 #include <Define.h> //加载Define.h头文件 uint temperature; //温度全局变量 /******************************************************************************************************** ** 功能描述: 简单的延时程序 ** 创 建 者: 严泽远 ** 创建时间: 2010-11-17 20:52 ** 版 本: v1.0.0 ********************************************************************************************************/ void delay_18b20(uchar N) { uchar i,j; for(j=0;j<40;j++) for(i=0;i<N;i++) {;} } /******************************************************************************************************** ** 功能描述: DS18B20初始化子程序 ** 创 建 者: 严泽远 ** 创建时间: 2010-11-17 21:03 ** 版 本: v1.0.0 ********************************************************************************************************/ void Init_DS18B20(void) { unsigned char result_18b20; //用来存储DQ的脉冲 do{ DQ=0; //拉低总线,发出复位脉冲 delay_18b20(25); //延时530us DQ=1; //释放总线,等待ds18b20的应答脉冲 delay_18b20(5); //等待110us result_18b20=DQ; //读取ds18b20的应答脉冲 delay_18b20(15); //等待周期结束 }while(result_18b20==1); //若ds18b20无应答,则继续发复位脉冲 } /******************************************************************************************************** ** 功能描述: 向DS18B20读一字节数据 ** 出口参数: 读取的一个字节 ** 创 建 者: 严泽远 ** 创建时间: 2010-11-17 20:55 ** 版 本: v1.0.0 ********************************************************************************************************/ unsigned char ReadOneChar(void) { unsigned char i,j; unsigned char dat=0; for(i=8;i>0;i--) { dat>>=1; //右移一位 DQ=0; //拉低总线 for(j=2;j>0;j--); //延时4us DQ=1; //释放总线,以让DS18B20把数据传输到单总线上 for(j=4;j>0;j--); //延时8us if(DQ==1) //读取ds18b20发来的数据,从低位读起 dat|=0x80; //将读到的数据保存在d中 delay_18b20(2); //延时等待读周期结束 DQ=1; } return(dat); //返回读到的数据 } /******************************************************************************************************** ** 功能描述: 向DS18B20写一字节数据 ** 如口参数: dat: 要写入的一个字节 ** 创 建 者: 严泽远 ** 创建时间: 2010-11-17 21:09 ** 版 本: v1.0.0 ********************************************************************************************************/ void WriteOneChar(unsigned char dat) { unsigned char i,j; for(i=8;i>0;i--) { DQ=0; //拉低总线 for(j=2;j>0;j--); //延时4us DQ=dat&0x01; //主机在总线上写数据传送到ds18b20 delay_18b20(3); //延时等待写周期结束 DQ=1; //重新将总线拉高 dat>>=1; //右移一位 } } /******************************************************************************************************** ** 功能描述: 向DS18B20读温度值并转换成实际温度 ** 创 建 者: 严泽远 ** 创建时间: 2010-11-17 21:49 ** 版 本: v1.0.0 ********************************************************************************************************/ void ReadTemperature() { uchar tempL,tempH; Init_DS18B20(); //初始化ds18b20 WriteOneChar(0xcc); //发送读指令跳过rom WriteOneChar(0x44); //发送指令启动温度转换 Init_DS18B20(); //初始化ds18b20 WriteOneChar(0xcc); //发送读指令匹配rom WriteOneChar(0xbe); //发送指令开始读取温度 tempL=ReadOneChar(); //读取温度值低位 tempH=ReadOneChar(); //读取温度值高位 Init_DS18B20(); //再发送一次复位脉冲,终止温度的读取 temperature=((tempH*256)+tempL);//*0.625; //转换成实际温度 temperature/=16; } 八、测试红外线遥控器发射和接受,编写红外遥控器解码程序 为了让复古的辉光管与现代科技完美结合,我花了几块钱淘了一个很可爱的红外线遥控器,打算用它来实现所有的控制功能。包括开关机、调整时间等等。红外遥控在十年前要搞解码会很麻烦,要用红外线接收管,然后要用一大堆电路去解调红外线编码。现在方便了,直接用38B的红外线接收头,三个脚,两个接电源,另外一个在接收到红外线遥控编码的时候就直接将编码输出出来,可以直接接到MCU的输入口上进行软件处理。 我在电路板上设计了一个位置用于安装红外线接收头,看一下,就在冒号的底下: ![]() ![]() 红外线解码程序最好是用MCU的外部中断口去连接红外接收头的数据脚,这样软件会好做一些,可惜我用的MCU不带外部中断输入,所以我在MCU上随便找了一个IO,打算用定时器加软件查询的办法去对红外线数据进行解码。 首先先认识一下红外线编码格式,我们所使用的遥控器为NEC格式的码(最常用的编码格式),通过红外接收头出来的电平是这样的,我用示波器给掐出来分析一下: ![]() ![]() 由上面可以看出来,这是一个标准的完整红外线遥控数据,其中由引导码+16位系统码+8位按键码+8位按键反码组成。 其中引导码由9ms的低电平和4.5ms的高电平组成。 数据码为一段560us低电平,后引一段高电平,高电平长度为1680us时表示该BIT为1,高电平长度为560us时表示该BIT为0。 如果我们按着遥控器的按键不放,那么遥控器会送出一段重复码,表示该按键没有释放, 重复码由9ms的低电平和2.25ms的高电平作开头,然后跟着一个脉冲。 要精确的解码,最重要的是要去精确测量这些电平的时间长短,所以要产生一个用于测量的标准时间出来,所以我用了MCU的一个定时器,产生一个100us的标准时间,看一下测量的结果,好准啊,呵呵: ![]() 接下来就是把所有遥控器的按键对应的编码解出来,然后根据按下的键是哪个,执行相应的功能就好了。 在这里我把用定时器2中断去查询解码的一段程序贴出来,有需要的朋友可以借鉴一下。 /******************************************************************************************************** ** 功能描述: T2定时器计时完毕后执行的红外线数据解码函数 ** 创 建 者: 严泽远 ** 创建时间: 2010-11-19 01:45 ** 版 本: v1.0.0 ********************************************************************************************************/ void Timer2Interrupt() interrupt 5 { bit F_Pulse = 0; //是否收到一个数据 TF2H=0; //清零中断标志位 if(IR_LastLH==1 && IR==0) //一个脉冲完毕 { switch(IR_State) { case WAIT: //等待状态 if((IR_LC>=85 && IR_LC<=95) && (IR_HC>=40 && IR_HC<=50)) //新引导码由9ms的低电平和4.5ms的高电平组成 { IR_State = RECIVE; //当前为开始接收数据状态 IR_BufByte = 0; //从第一个数据开始接收 IR_BufBit = 0; //从第零位数据开始接收 IR_Rep = 0; //重复码数量减少 } if((IR_LC>=85 && IR_LC<=95) && (IR_HC>=17 && IR_HC<=27)) //重复码由9ms的低电平,2.25ms的高电平,跟着是一个短脉冲 { F_NewIRdata = 1; //有了新红外数据 if(IR_Rep<255) IR_Rep++; //重复码数量增加 } break; case RECIVE: //开始接受数据状态 if((IR_LC>=3 && IR_LC<=7) && (IR_HC>=14 && IR_HC<=18)) //数据码为一段560us低电平,后引一段高电平,高电平长度为1680us时为1 { IR_Buf[IR_BufByte] <<= 1; //左移一位 IR_Buf[IR_BufByte] |= 0x01; //或上数据 } if((IR_LC>=3 && IR_LC<=7) && (IR_HC>=3 && IR_HC<=7)) //数据码为一段560us低电平,后引一段高电平,高电平长度为560us时为0 { IR_Buf[IR_BufByte] <<= 1; //左移一位 } IR_BufBit++; //数据位数增加 if(IR_BufBit>=8) //如果记录了8位数据 { IR_BufBit = 0; //开始计算另一字节的第零位数据 IR_BufByte++; //字节数增加 } if(IR_BufByte>3) //如果记录满了4个字节则开始退出分析按键码 { IR_State = WAIT; //恢复到等待接受红外信号状态 F_NewIRdata = 1; //有了新红外数据 } } IR_LC=0; IR_HC=0; //复位电平计数值 } if(IR==0) {IR_LC++;IR_LastLH=0;IR_HC=0;} //低电平时长计数 if(IR_LC>100) {IR_LC=100; IR_State=WAIT;} //如果低电平连续超过10ms即为非正常数据 if(IR==1) {IR_HC++;IR_LastLH=1;} //高电平时长计数 if(IR_HC>100) {IR_HC=100; IR_State=WAIT;} //如果高电平连续超过10ms即为非正常数据 (责任编辑:admin) |