当前位置:首页 > 单片机 > 单片机
[导读]DS1302 我们前边也有提起过,是三根线,分别是 CE、I/O 和 SCLK,其中 CE 是使能线,SCLK 是时钟线,I/O 是数据线。前边我们介绍过了 SPI 通信,同学们发现没发现,这个 DS1302 的通信线定义和 SPI 怎么这么像呢?事

DS1302 我们前边也有提起过,是三根线,分别是 CE、I/O 和 SCLK,其中 CE 是使能线,SCLK 是时钟线,I/O 是数据线。前边我们介绍过了 SPI 通信,同学们发现没发现,这个 DS1302 的通信线定义和 SPI 怎么这么像呢?

事实上,DS1302 的通信是 SPI 的变异种类,它用了 SPI 的通信时序,但是通信的时候没有完全按照 SPI 的规则来,下面我们一点点解剖 DS1302 的变异 SPI 通信方式。先看一下单字节写入操作,如图15-11所示。

图15-11 DS1302 单字节写操作

然后我们再对比一下 CPOL=0/CPHA=0 情况下的 SPI 的操作时序,如图15-12所示。

图15-12 CPOL=0/CPHA=0 通信时序

图15-11和图15-12的通信时序,其中 CE 和 SSEL 的使能控制是反的,对于通信写数据,都是在 SCK 的上升沿,从机进行采样,下降沿的时候,主机发送数据。DS1302 的时序里,单片机要预先写一个字节指令,指明要写入的寄存器的地址以及后续的操作是写操作,然后再写入一个字节的数据。

对于单字节读操作,我就不做对比了,把 DS1302 的时序图贴出来,大家自己看一下即可,如图15-13所示。

图15-13 DS1302 单字节读操作

读操作有两处需要特别注意的地方。第一,DS1302 的时序图上的箭头都是针对 DS1302 来说的,因此读操作的时候,先写第一个字节指令,上升沿的时候 DS1302 来锁存数据,下降沿我们用单片机发送数据。到了第二个字数据,由于我们这个时序过程相当于 CPOL=0/CPHA=0,前沿发送数据,后沿读取数据,第二个字节是 DS1302 下降沿输出数据,我们的单片机上升沿来读取,因此箭头从 DS1302 角度来说,出现在了下降沿。

第二个需要注意的地方就是,我们的单片机没有标准的 SPI 接口,和 I2C 一样需要用 IO 口来模拟通信过程。在读 DS1302 的时候,理论上 SPI 是上升沿读取,但是程序是用 IO 口模拟的,所以数据的读取和时钟沿的变化不可能同时了,必然就有一个先后顺序。通过实验发现,如果先读取 IO 线上的数据,再拉高 SCLK 产生上升沿,那么读到的数据一定是正确的,而颠倒顺序后数据就有可能出错。这个问题产生的原因还是在于 DS1302 的通信协议与标准 SPI 协议存在的差异造成的,如果是标准 SPI 的数据线,数据会一直保持到下一个周期的下降沿才会变化,所以读取数据和上升沿的先后顺序就无所谓了;但 DS1302 的 IO 线会在时钟上升沿后被 DS1302 释放,也就是撤销强推挽输出变为弱下拉状态,而此时在51单片机引脚内部上拉的作用下,IO 线上的实际电平会慢慢上升,从而导致在上升沿产生后再读取 IO 数据的话就可能会出错。因此这里的程序我们按照先读取 IO 数据,再拉高 SCLK 产生上升沿的顺序。

下面我们就写一个程序,先将2013年10月8号星期二12点30分00秒这个时间写到 DS1302 内部,让 DS1302 正常运行,然后再不停的读取 DS1302 的当前时间,并显示在我们的液晶屏上。 /*Lcd1602.c 文件程序源代码***/ (此处省略,可参考之前章节的代码)

/*****************************main.c文件程序源代码******************************/#includesbitDS1302_CE=P1^7;sbitDS1302_CK=P3^5;sbitDS1302_IO=P3^4;bitflag200ms=0;//200ms定时标志unsignedcharT0RH=0;//T0重载值的高字节unsignedcharT0RL=0;//T0重载值的低字节voidConfigTimer0(unsignedintms);voidInitDS1302();unsignedcharDS1302SingleRead(unsignedcharreg);externvoidInitLcd1602();externvoidLcdShowStr(unsignedcharx,unsignedchary,unsignedchar*str);voidmain(){unsignedchari;unsignedcharpsec=0xAA;//秒备份,初值AA确保首次读取时间后会刷新显示unsignedchartime[8];//当前时间数组unsignedcharstr[12];//字符串转换缓冲区EA=1;//开总中断ConfigTimer0(1);//T0定时1msInitDS1302();//初始化实时时钟InitLcd1602();//初始化液晶while(1){if(flag200ms){//每200ms读取一次时间flag200ms=0;for(i=0;i<7;i++){//读取DS1302当前时间time[i]=DS1302SingleRead(i);}if(psec!=time[0]){//检测到时间有变化时刷新显示str[0]='2';//添加年份的高2位:20str[1]='0';str[2]=(time[6]>>4)+'0';//“年”高位数字转换为ASCII码str[3]=(time[6]&0x0F)+'0';//“年”低位数字转换为ASCII码str[4]='-';//添加日期分隔符str[5]=(time[4]>>4)+'0';//“月”str[6]=(time[4]&0x0F)+'0';str[7]='-';str[8]=(time[3]>>4)+'0';//“日”str[9]=(time[3]&0x0F)+'0';str[10]='';LcdShowStr(0,0,str);//显示到液晶的第一行str[0]=(time[5]&0x0F)+'0';//“星期”str[1]='';LcdShowStr(11,0,"week");LcdShowStr(15,0,str);//显示到液晶的第一行str[0]=(time[2]>>4)+'0';//“时”str[1]=(time[2]&0x0F)+'0';str[2]=':';//添加时间分隔符str[3]=(time[1]>>4)+'0';//“分”str[4]=(time[1]&0x0F)+'0';str[5]=':';str[6]=(time[0]>>4)+'0';//“秒”str[7]=(time[0]&0x0F)+'0';str[8]='';LcdShowStr(4,1,str);//显示到液晶的第二行psec=time[0];//用当前值更新上次秒数}}}}/*发送一个字节到DS1302通信总线上*/voidDS1302ByteWrite(unsignedchardat){unsignedcharmask;for(mask=0x01;mask!=0;mask<<=1){//低位在前,逐位移出if((mask&dat)!=0){//首先输出该位数据DS1302_IO=1;}else{DS1302_IO=0;}DS1302_CK=1;//然后拉高时钟DS1302_CK=0;//再拉低时钟,完成一个位的操作}DS1302_IO=1;//最后确保释放IO引脚}/*由DS1302通信总线上读取一个字节*/unsignedcharDS1302ByteRead(){unsignedcharmask;unsignedchardat=0;for(mask=0x01;mask!=0;mask<<=1){//低位在前,逐位读取if(DS1302_IO!=0){//首先读取此时的IO引脚,并设置dat中的对应位dat"=mask;}DS1302_CK=1;//然后拉高时钟DS1302_CK=0;//再拉低时钟,完成一个位的操作}returndat;//最后返回读到的字节数据}/*用单次写操作向某一寄存器写入一个字节,reg-寄存器地址,dat-待写入字节*/voidDS1302SingleWrite(unsignedcharreg,unsignedchardat){DS1302_CE=1;//使能片选信号DS1302ByteWrite((reg<<1)|0x80);//发送写寄存器指令DS1302ByteWrite(dat);//写入字节数据DS1302_CE=0;//除能片选信号}/*用单次读操作从某一寄存器读取一个字节,reg-寄存器地址,返回值-读到的字节*/unsignedcharDS1302SingleRead(unsignedcharreg){unsignedchardat;DS1302_CE=1;//使能片选信号DS1302ByteWrite((reg<<1)|0x81);//发送读寄存器指令dat=DS1302ByteRead()//读取字节数据DS1302_CE=0;//除能片选信号returndat;}/*DS1302初始化,如发生掉电则重新设置初始时间*/voidInitDS1302(){unsignedchari;unsignedcharcodeInitTime[]={//2013年10月8日星期二12:30:000x00,0x30,0x12,0x08,0x10,0x02,0x13};DS1302_CE=0;//初始化DS1302通信引脚DS1302_CK=0;i=DS1302SingleRead(0);//读取秒寄存器if((i&0x80)!=0){//由秒寄存器最高位CH的值判断DS1302是否已停止DS1302SingleWrite(7,0x00);//撤销写保护以允许写入数据for(i=0;i<7;i++){//设置DS1302为默认的初始时间DS1302SingleWrite(i,InitTime[i]);}}}/*配置并启动T0,ms-T0定时时间*/voidConfigTimer0(unsignedintms){unsignedlongtmp;//临时变量tmp=11059200/12;//定时器计数频率tmp=(tmp*ms)/1000;//计算所需的计数值tmp=65536-tmp;//计算定时器重载值tmp=tmp+12;//补偿中断响应延时造成的误差T0RH=(unsignedchar)(tmp>>8);//定时器重载值拆分为高低字节T0RL=(unsignedchar)tmp;TMOD&=0xF0;//清零T0的控制位TMOD|=0x01;//配置T0为模式1TH0=T0RH;//加载T0重载值TL0=T0RL;ET0=1;//使能T0中断TR0=1;//启动T0}/*T0中断服务函数,执行200ms定时*/voidInterruptTimer0()interrupt1{staticunsignedchartmr200ms=0;TH0=T0RH;//重新加载重载值TL0=T0RL;tmr200ms++;if(tmr200ms>=200){//定时200mstmr200ms=0;flag200ms=1;}}

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: 驱动电源

在工业自动化蓬勃发展的当下,工业电机作为核心动力设备,其驱动电源的性能直接关系到整个系统的稳定性和可靠性。其中,反电动势抑制与过流保护是驱动电源设计中至关重要的两个环节,集成化方案的设计成为提升电机驱动性能的关键。

关键字: 工业电机 驱动电源

LED 驱动电源作为 LED 照明系统的 “心脏”,其稳定性直接决定了整个照明设备的使用寿命。然而,在实际应用中,LED 驱动电源易损坏的问题却十分常见,不仅增加了维护成本,还影响了用户体验。要解决这一问题,需从设计、生...

关键字: 驱动电源 照明系统 散热

根据LED驱动电源的公式,电感内电流波动大小和电感值成反比,输出纹波和输出电容值成反比。所以加大电感值和输出电容值可以减小纹波。

关键字: LED 设计 驱动电源

电动汽车(EV)作为新能源汽车的重要代表,正逐渐成为全球汽车产业的重要发展方向。电动汽车的核心技术之一是电机驱动控制系统,而绝缘栅双极型晶体管(IGBT)作为电机驱动系统中的关键元件,其性能直接影响到电动汽车的动力性能和...

关键字: 电动汽车 新能源 驱动电源

在现代城市建设中,街道及停车场照明作为基础设施的重要组成部分,其质量和效率直接关系到城市的公共安全、居民生活质量和能源利用效率。随着科技的进步,高亮度白光发光二极管(LED)因其独特的优势逐渐取代传统光源,成为大功率区域...

关键字: 发光二极管 驱动电源 LED

LED通用照明设计工程师会遇到许多挑战,如功率密度、功率因数校正(PFC)、空间受限和可靠性等。

关键字: LED 驱动电源 功率因数校正

在LED照明技术日益普及的今天,LED驱动电源的电磁干扰(EMI)问题成为了一个不可忽视的挑战。电磁干扰不仅会影响LED灯具的正常工作,还可能对周围电子设备造成不利影响,甚至引发系统故障。因此,采取有效的硬件措施来解决L...

关键字: LED照明技术 电磁干扰 驱动电源

开关电源具有效率高的特性,而且开关电源的变压器体积比串联稳压型电源的要小得多,电源电路比较整洁,整机重量也有所下降,所以,现在的LED驱动电源

关键字: LED 驱动电源 开关电源

LED驱动电源是把电源供应转换为特定的电压电流以驱动LED发光的电压转换器,通常情况下:LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: LED 隧道灯 驱动电源
关闭