1 前言
DS1302 是美国DALLAS公司推出的一种高性能、低功耗、带RAM的实时时钟电路,它可以对年、月、日、周、时、分、秒进行计时,具有闰年补偿功能,工作电压为2.0V~5.5V。采用三线接口与CPU进行同步通信,并可采用突发方式一次传送多个字节的时钟信号或RAM数据。DS1302内部有一个31×8的用于临时性存放数据的RAM寄存器。DS1302是DS1202的升级产品,与DS1202兼容,但增加了主电源/后备电源双电源引脚,同时提供了对后备电源进行涓细电流充电的能力。
市场上常用的如下图所示
封装图如下所示
2 时钟芯片的写入与读取
如下图所示就是该时钟芯片的基本的工作模式
CE为使能端,IO为数据的发送和接受端,CLK为时序端,就是由这三个简单的引脚控制.
2.1设置GPIO
我们所操作的就是这三个端口,在stm32上面就是三个你所设置好的GPIO
如下就是设置GPIO的相关代码
/**
* @brief GPIO初始化
* @param
* @retval
*/
void DS1302_GPIO_Config()
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_DS1302_CMD(RCC_DS1302,ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = DS1302_SCLK;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIO_Port_SCLK,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = DS1302_CE;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIO_Port_CE,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = DS1302_IO;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIO_Port_IO,&GPIO_InitStructure);
GPIO_WriteBit(GPIO_Port_SCLK,DS1302_SCLK,Bit_RESET);
GPIO_WriteBit(GPIO_Port_CE,DS1302_CE,Bit_RESET);
GPIO_WriteBit(GPIO_Port_IO,DS1302_IO,Bit_RESET);
}
void DS1302_DATAOUT_Config()//配置双向I/O端口为输出态
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_DS1302_CMD(RCC_DS1302_IO, ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = DS1302_IO;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIO_Port_IO,&GPIO_InitStructure);
GPIO_WriteBit(GPIO_Port_IO,DS1302_IO,Bit_RESET);
}
void DS1302_DATAINPUT_Config()//配置双向I/O端口为输入态
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_DS1302_CMD(RCC_DS1302_IO, ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Pin = DS1302_IO;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIO_Port_IO,&GPIO_InitStructure);
}
这里的代码没有难度,因为需要输出和输入两种模式,所以需要IO端口有两种模式,这在后面的主机与DS1302的通信中将会运用.
那么,DS1302如何实现和主机的通信呢?
2.2通信的实现
如下就是通信的规则
这十分的简单,就是在拉高CE后读写.
CLK上升沿写入数据
CLK下降沿读取数据
读取数据和写入数据都是先要写入DS1302寄存器的地址,有了地址,才能写入和读取.
如下代码就是读取和写入的相关代码
void DS1302_WriteOnebyte(uint8_t data)
{
DS1302_DATAOUT_Config();
uint8_t count = 0;
SCLK_L;
for(count = 0;count < 8;count++)
{
SCLK_L;
if(data&0x01)
{
DATA_H;
}
else
{
DATA_L;
}
SCLK_H;
data >>= 1;
}
}
/**
* @brief 向DS1302指定寄存器发送数据
* @param addres-指向的寄存器地址,data-需要发送的数据
* @retval 无
*/
void DS1302_WriteByte(uint8_t address,uint8_t data)//向指定寄存器地址发送数据
{
u8 temp1=address;
u8 temp2=data;
CE_L;
SCLK_L;
Delay_us(1);
CE_H;
Delay_us(2);
DS1302_WriteOnebyte(temp1);
DS1302_WriteOnebyte(temp2);
CE_L;
SCLK_L;
Delay_us(2);
}
/**
* @brief 在DS1302读取数据
* @param 读取数据的寄存器的地址
* @retval 无
*/
uint8_t DS1302_ReadByte(uint8_t address)//从指定地址读取一字节数据
{
u8 temp3=address;
u8 count=0;
u8 return_data=0x00;
CE_L;SCLK_L;Delay_us(3);
CE_H;Delay_us(3);
DS1302_WriteOnebyte(temp3);
DS1302_DATAINPUT_Config();//配置I/O口为输入
Delay_us(2);
for(count=0;count<8;count++)
{
Delay_us(2);//使电平持续一段时间
return_data>>=1;
SCLK_H;Delay_us(4);//使高电平持续一段时间
SCLK_L;Delay_us(14);//延时14us后再去读取电压,更加准确
if(GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_10))
{
return_data=return_data|0x80;
}
}
Delay_us(2);
CE_L;DATA_L;
return return_data;
}
那么又有一个问题,写入的地址是什么呢,当然啊,生产厂家早就给我们设置好了固定的地址,用于存储各种时钟数据,只等待我们用户写入数据了.
如下图所示就是DS1302的地址
到此为止,其实对于DS1302的基本操作已经实现了,就是读取和写入.
3 基本应用
#include "Device/Include/stm32f10x.h" // Device header
#include "clock.h"
#include "OLED.h"
#include "DS1302.h"
#include "Delay.h"
unsigned char Week[24] = {' ',' ',' ','M','o','n','T','u','e','W','e','d','T','h','u','F','r','i','S','a','t','S','u','n'};
unsigned char TimE[7] = {0x50,0x59,0x23,0x29,0x11,0x23,0x05}; //秒、分、时、日、月、年、周几
unsigned char Buff[7] = {0}; //缓冲区
unsigned char Buff2[7] = {0};
unsigned char CommanD[7] = {0x80,0x82,0x84,0x86,0x88,0x8C,0x8A};
unsigned char Temp;
unsigned char Change = 0;
unsigned char Change2 = 0;
unsigned char i;
unsigned char j = 1;
unsigned char flag; //闪烁标志位
/**
* @brief 初始化时钟UI
* @param
* @retval
*/
void clock_Init()
{
OLED_ShowString(1,3,"clock by zp");
OLED_ShowString(2,1,"date:");
OLED_ShowString(2,10,"-");
OLED_ShowString(2,13,"-");
OLED_ShowString(3,1,"time:");
OLED_ShowString(3,8,":");
OLED_ShowString(3,11,":");
}
/**
* @brief 将数据写入单片机
* @param
* @retval
*/
void clock_in()
{
DS1302_WriteByte(0x8E,0x00); //打开写入权限
DS1302_WriteByte(CommanD[0],TimE[0]);
DS1302_WriteByte(CommanD[1],TimE[1]);
DS1302_WriteByte(CommanD[2],TimE[2]);
DS1302_WriteByte(CommanD[3],TimE[3]);
DS1302_WriteByte(CommanD[4],TimE[4]);
DS1302_WriteByte(CommanD[5],TimE[5]);
DS1302_WriteByte(CommanD[6],TimE[6]);
DS1302_WriteByte(0x8E,0x80); //关闭写入权限
}
/**
* @brief将数据从单片机读出来
* @param
* @retval
*/
void clock_out()
{
Temp = DS1302_ReadByte(CommanD[0]+1);
Buff[0] = Temp/16*10+Temp%16;
Temp = DS1302_ReadByte(CommanD[1]+1);
Buff[1] = Temp/16*10+Temp%16;
Temp = DS1302_ReadByte(CommanD[2]+1);
Buff[2] = Temp/16*10+Temp%16;
Temp = DS1302_ReadByte(CommanD[3]+1);
Buff[3] = Temp/16*10+Temp%16;
Temp = DS1302_ReadByte(CommanD[4]+1);
Buff[4] = Temp/16*10+Temp%16;
Temp = DS1302_ReadByte(CommanD[5]+1);
Buff[5] = Temp/16*10+Temp%16;
Buff[6] = DS1302_ReadByte(CommanD[6]+1);
}
/**
* @brief 将读取到的数据显示到屏幕上
* @param
* @retval
*/
void clock_Show()
{
OLED_ShowNum(2,6,20,2);
OLED_ShowNum(3,12,Buff[0],2);
OLED_ShowNum(3,9,Buff[1],2);
OLED_ShowNum(3,6,Buff[2],2);
OLED_ShowNum(2,14,Buff[3],2);
OLED_ShowNum(2,11,Buff[4],2);
OLED_ShowNum(2,8,Buff[5],2);
i = Buff[6]*3; //使用for循环乱套
OLED_ShowChar(3,14,Week[i]);
OLED_ShowChar(3,15,Week[i+1]);
OLED_ShowChar(3,16,Week[i+2]);
}
这段代码其实就是将DS1302的数据进行一些简单的处理,在显示器上显示相关信息.
其中关键的就是对几种简单数据的简单的处理
比如,我们存进去的秒是30秒
存进去的时候,按照规则,会以BCD码的形式存入,所以出来的时候还要进行BCD码转换的操作就是代码中的 clock_out() 函数
包括对于周的处理,使用的方法虽然简单,但是也是完全自己写的,没有丝毫借鉴.
最后关于此显示器的相关介绍代码,我将会在该实践项目之后的章节给出,我正在学习中.
4 下节预告
在下节,我会进一步完善我的时钟,将会加入修改,闹钟功能.敬请期待.