景安 怎么把网站做别名,无锡富通电力建设有限公司网站,怎么架设网站,邢台制作目录
1#xff0c;DS1302 时钟芯片介绍
2#xff0c;BCD码介绍
3#xff0c;涉及到的寄存器
3.1#xff0c;控制寄存器
3.2#xff0c;日历/时钟寄存器
3.3#xff0c;DS1302 的读写时序
4#xff0c;相关代码 这一章我们来学习DS1302 时钟芯片#xff0c…目录
1DS1302 时钟芯片介绍
2BCD码介绍
3涉及到的寄存器
3.1控制寄存器
3.2日历/时钟寄存器
3.3DS1302 的读写时序
4相关代码 这一章我们来学习DS1302 时钟芯片该芯片是3 线SPI 接口所以需要使用51单片机的3 个IO 口模拟SPI 时序与DS1302 时钟芯片通信将时钟日历数据读取出来。开发板上集成了1 个DS1302 时钟模块可使用它设计一个多功能电子时钟。本章要实现的功能是系统运行时数码管上显示电子时钟时分秒格式为“XX-XX-XX”。
1DS1302 时钟芯片介绍
DS1302 是DALLAS 公司推出的涓流充电时钟芯片内含有一个实时时钟/日历和31 字节静态RAM通过简单的串行接口与单片机进行通信。实时时钟/日历电路提供秒、分、时、日、周、月、年的信息每月的天数和闰年的天数可自动调整。时钟操作可通过AM/PM 指示决定采用24 或12 小时格式。DS1302 与单片机之间能简单地采用同步串行的方式进行通信仅需用到三根通信线①RES复位②I/O 数据线③SCLK 串行时钟。时钟/RAM 的读/写数据以一个字节或多达31 个字节的字符组方式通信。DS1302 工作时功耗很低保持数据和时钟信息时功率小于1mW。
DS1302 由DS1202 改进而来增加了以下的特性双电源管脚用于主电源和备份电源供应Vcc1 为可编程涓流充电电源附加七个字节存储器。它广泛应用于电话、传真、便携式仪器以及电池供电的仪器仪表等产品领域下面。
主要的性能指标 ★ 实时时钟具有能计算2100 年之前的秒、分、时、日、星期、月、年的能力还有闰年调整的能力 ★ 31 个8 位暂存数据存储RAM ★ 串行I/O 口方式使得管脚数量最少 ★ 宽范围工作电压2.0~5.5V ★ 工作在2.0V 时电流小于300nA ★ 读/写时钟或RAM 数据时有两种传送方式单字节传送和多字节传送字符组方式 ★ 8 脚DIP 封装或可选的8 脚SOIC 封装根据表面装配 ★ 简单3 线接口 ★ 与TTL 兼容Vcc5V ★ 可选工业级温度范围-40~85 1VCC2主电源引脚 2X1、X2DS1302 外部晶振引脚通常需外接32.768K 晶振 3GND电源地 4CE使能引脚也是复位引脚新版本功能变 5I/O串行数据引脚数据输出或者输入都从这个引脚 6SCLK串行时钟引脚 7VCC1备用电源
51单片机和DS1302芯片之间的通讯是SPISPI的主要特点3线or4线、同步、主从、串行
3线or4线 CE、I/O、SCLK3线I/O是一根线4线I/O是两根线
同步与UART的异步通讯相比需要SCLK同步信息的发送和接收
主从谁来发出SCLK同步信号
串行在数据线上按位传输数据
DS1302是变种SPI通过SCLK和I/O相互配合发送和接收信息。SCLK在上升沿前主机通过I/O口发送数据DS1302在上升沿是接收SCLK在下降沿后DS1302通过I/O口发送数据主机接收。 SPISPI Serial Peripheral Interface是串行外围设备接口是一种高速全双工同步的通信总线。
DS1302参考
DS1302介绍
2BCD码介绍
为什么要介绍BCD码因为从DS1302芯片读出来的数据是BCD 码形式当使用串口助手查看数据时看到的是十进制形式但其本质上是十六进制数据。
BCD码是一种数字编码这种计数编码有个特点很像10进制和16进制的结合。看起来很像10进制29下来是30而不是2ABCD码实际是用十六进制来表示的BCD码的21其实在计算机中就是0x21。BCD码中只有0-9而没有ABCDEF等字目。
综合来讲BCD码其实就是看起来很像十进制数的十六进制数。意思就是BCD码本质上是十六进制数但是因为他没有ABCDEF所以看起来很像十进制数。
DS1302使用BCD码来处理时间信息对单片机来说是16进制方便单片机处理对人来说可以认为是10进制同时方便单片机和人来识别信息。
例如
如果把一个数21分成两个数对于十进制的21需要通过21/10得到2通过21%10得到1
对于十六进制的21分成两个数通过0x214的得到2 通过0x210x0F得到1
对于计算机来说移位和与操作的复杂度优于除和取余。
3涉及到的寄存器
操作DS1302 的大致过程就是将各种数据写入DS1302 的寄存器以设置它当前的时间的格式。然后使DS1302 开始运作DS1302 时钟会按照设置情况运转再用单片机将其寄存器内的数据读出。
3.1控制寄存器
控制寄存器用于存放DS1302 的控制命令字DS1302 的RST 引脚回到高电平后写入的第一个字节就为控制命令。它用于对DS1302 读写过程进行控制格式如下 上图是DS1302 的寄存器样式我们看到 1、第7 位永远都是1 2、第6 位1 表示RAM寻址内部存储器地址0 表示CK寻址内部寄存器
3、第5 到第1 位为RAM 或者寄存器的地址 4、最低位高电平表示RD即下一步操作将要“读”低电平表示W即下一步操作将要“写”。 比如要读秒寄存器则命令为1000 0001反之写为1000 0000要注意其含义。
3.2日历/时钟寄存器
DS1302 共有12 个寄存器其中有7 个与日历、时钟相关存放的数据为BCD码形式 。 秒寄存器低四位为秒的个位高的次三位为秒的十位。最高位CH 为DS1302 的运行标志当CH0 时DS1302 内部时钟运行反之CH1 时停止
小时寄存器时寄存器。最高位为12/24 小时的格式选择位该位为1 时表示12 小时格式。当设置为12 小时显示格式时第5 位的高电平表示下午PM而当设置为24 小时格式时第5 位位具体的时间数据。
写保护寄存器当该寄存器最高位WP 为1 时DS1302 只读不写所以要在往DS1302 写数据之前确保WP 为0。
慢充电寄存器涓细电流充电寄存器我们知道当DS1302 掉电时可以马上调用外部电源保护时间数据。该寄存器就是配置备用电源的充电选项的。其中高四位4 个TCS只有在1010 的情况下才能使用充电选项低四位的情况与DS1302 内部电路有关。
思考日历/时钟寄存器中还存储了除了时间数据之外的其它数据这些数据在读写时是如何操作的
3.3DS1302 的读写时序
在控制指令字输入后的下一个SCLK 时钟的上升沿时数据被写入DS1302数据输入从低位位0开始。同样在紧跟8 位的控制指令字后的下一个SCLK脉冲的下降沿读出DS1302 的数据读出数据时从低位0 位到高位7。其时序图如下所示 上图就是DS1302 的三个时序复位时序单字节写时序单字节读时序CERST复位时序即在RST 引脚产生一个正脉冲在整个读写器件RST 要保持高电平一次字节读写完毕之后要注意把RST 返回低电平准备下次读写周期
单字节读时序注意读之前还是要先对寄存器写命令从最低位开始写可以看到写数据是在SCLK 的上升沿实现而读数据在SCLK 的下降沿实现。所以在单字节读时序中写命令的第八个上升沿结束后紧接着的第八个下降沿就将要读寄存器的第一位数据读到数据线上了这个就是DS1302 操作中最特别的地方。当然读出来的数据也是最低位开始。
单字节写时序两个字节的数据配合16 个上升沿将数据写入即可。
程序注意事项 ★要记得在操作DS1302 之前关闭写保护 ★注意用延时来降低单片机的速度以配合器件时序
★DS1302 读出来的数据是BCD 码形式要转换成我们习惯的10 进制转换 方法在源程序里 ★读取字节之前将IO 设置为输入口读取完之后要将其改回输出口 ★在写程序的时候建议实现开辟数组内存空间来集中放置DS1302 的 一系列数据方便以后扩展键盘输入。
4相关代码
ds1302底层时序代码分为ds1320.c和ds1302.h两个文件 ds1302.h
#ifndef __DS1302_H__
#define __DS1302_H__//从ds1302的内部寄存器addr写入一个值value
void ds1302_write_reg(unsigned char addr, unsigned char value);
//从ds1302的内部寄存器addr读出一个值作为返回值
unsigned char ds1302_read_reg(unsigned char addr);#endif
ds1320.c
#include ds1302.h#include reg51.hsbit DSIO P3^4;
sbit RST P3^5;
sbit SCLK P3^6;void delay(void)
{unsigned char i;for (i0; i3; i);
}//从ds1302的内部寄存器addr写入一个值value
void ds1302_write_reg(unsigned char addr, unsigned char value)
{unsigned char i,dat;//第一部分时序初始化RST 0;delay();SCLK 0;delay();RST 1;delay();//第二部分:写入地址for(i0; i8; i){dat addr 0x01; //取地址的低位DSIO dat;addr 1;SCLK 1; //创造一个上升沿让ds1302将信号线上的值读走delay();SCLK 0;}//第三步写入数据for(i0; i8;i){dat value 0x01; //取数据的低位DSIO dat;value 1;SCLK 1; //创造一个上升沿让ds1302将信号线上的值读走delay();SCLK 0; }//第四部分时序结束RST 0;delay();SCLK 0;delay();}// 从ds1302的内部寄存器addr读出一个值作为返回值
unsigned char ds1302_read_reg(unsigned char addr)
{unsigned char i 0;unsigned char dat 0; // 用来存储读取到的一字节数据的unsigned char tmp 0;// 第1部分: 时序起始SCLK 0;delay();RST 0;delay();RST 1; // SCLK为低时RST由低变高意味着一个大的周期的开始delay();// 第2部分: 写入要读取的寄存器地址addrfor (i0; i8; i){dat addr 0x01; // SPI是从低位开始传输的DSIO dat; // 把要发送的bit数据丢到IO引脚上去准备好SCLK 1; // 制造上升沿让DS1302把IO上的值读走delay(); // 读走之后一个小周期就完了SCLK 0; // 把SCLK拉低是为了给下一个小周期做准备delay();addr 1; // 把addr右移一位}// 第3部分: 读出一字节DS1302返回给我们的值dat 0;for (i0; i8; i){// 在前面向ds1302写入addr的最后一个bit后ds1302就会将读取到的寄存器值// 的第一个bit放入到IO引脚上所以我们应该先读取IO再制造下降沿然后继续// 读取下一个bittmp DSIO;dat | (tmp i); // 读出来的数值是低位在前的SCLK 1; // 由于上面SCLK是低所以要先拉到高delay();SCLK 0; // 拉低SCLK制造一个下降沿delay();}// 第4部分: 时序结束SCLK 0; // SCLK拉低为了后面的周期时初始状态是对的delay();RST 0; // RST拉低意味着一个大周期的结束delay();// 第5部分解决读取时间是ff的问题DSIO 0;return dat;
}
通过串口接收时钟数据分为uart.c和uart.h两个文件 uart.h
#ifndef __UART_H__
#define __UART_H__//串口初始化
void uart_init();
//串口发送数据
void uart_send_byte(unsigned char c);#endif
uart.c
#include uart.h
#include reg51.hvoid uart_init()
{TMOD|0X20; //设置计数器工作方式2SCON0X50; //设置为工作方式1PCON0X80; //波特率加倍TH10xFA; //计数器初始值设置TL10xFA;ES1; //打开接收中断EA1; //打开总中断TR11; //打开计数器
}void uart_send_byte(unsigned char c)
{// 第1步发送一个字节SBUF c;// 第2步先确认串口发送部分没有在忙while (!TI);// 第3步软件复位TI标志位TI 0;
}
main函数
包括ds1302的上层时序读出时钟数据和写入时钟数据以及调用uart函数。
#include reg51.h
#include intrins.h#include ds1302.h
#include uart.hunsigned char code READ_RTC_ADDR[7] {0x81, 0x83, 0x85, 0x87, 0x89, 0x8b, 0x8d};
unsigned char code WRITE_RTC_ADDR[7] {0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c};
unsigned char time[7]{0};void delay1s(void) //误差 0us
{unsigned char a,b,c;for(c167;c0;c--)for(b171;b0;b--)for(a16;a0;a--);_nop_(); //if Keil,require use intrins.h
}void ds1302_read_time(void)
{unsigned char i 0;for (i0; i7; i){time[i] ds1302_read_reg(READ_RTC_ADDR[i]);}
}void ds1302_write_time(void)
{unsigned char i 0;// 准备好要写入的时间time[0] 0x24; // 对应 24stime[1] 0x39; // 对应 39mtime[2] 0x11; // 对应 11htime[3] 0x06; // 对应 6日time[4] 0x12; // 对应 12月time[5] 0x02; // 对应 星期2time[6] 0x16; // 对应 2016年ds1302_write_reg(0x8E, 0x00); // 去掉写保护for (i0; i7; i){ds1302_write_reg(WRITE_RTC_ADDR[i], time[i]);}ds1302_write_reg(0x8E, 0x80); // 打开写保护
}void main()
{uart_init();ds1302_write_time();while(1){int i 0;ds1302_read_time();for(i0;i7;i) {uart_send_byte(time[i]);}delay1s();}}