目录
一、DS1302时钟模块介绍
二、DS1302时钟模块接线说明
三、测试说明、DS1302时钟模块驱动代码
四、串口调试助手软件下载
五、源代码
六、DS1302时钟模块原理图资料
一、 DS1302时钟芯片介绍
DS1302是DALLAS公司推出的涓流充电时钟芯片,内部含有一个实时时钟/日历和31字节静态RAM,通过简单的串行接口与单片机进行通信。该芯片是3线SPI接口。实时时钟/日历电路提供秒、分、时、日、周、月、年的信息,每月的天数和闰年的天数可以自动调整。时钟操作通过AM/PM只是决定采用24或12小时格式。DS1302与单片机之间能简单地采用同步串行的方式进行通信,仅需要用到三根通信线:
① RES复位;② I/O数据线;③ SCLK串行时钟。
时钟/RAM的读/写数据以一个字节或多达31个字节的字符组方式通信。DS1302工作时功耗很低,保持数据个时钟信息时功率小于1mW。DS1302的双电源管脚用于主电源和备份电源供应,Vcc1为可编程涓流充电电源,附加七个字节存储器。它广泛应用与电话、传真、便携式仪器以及电池供电的仪器仪表等产品领域下面。
DS1302主要的性能指标:实时时钟具有能计算2100年之前的秒、分、时、日、周、月、年的能力,还有闰年调整能力。31个8位暂存数据存储RAM。串行I/O口方式使得管脚数量最少。宽范围工作电压3.0~5.5V。工作在2.0V时,电流小于300nA。读/写时钟或RAM数据时有两种传送方式:单字节传送和多字节传送方式。5脚DIP封装或可选的8脚SOIC封装根据表面装配。
简单3线接口。与TTL兼容Vcc=5V。可选工业级温度范围-40~+85。具有涓流充电能力。
采用主电源和备份电源双电源供应,备份电源可用电池或大容量电容实现。
VCC(+):电源正极接口,外接电源3 ~ 5V电源正极,接单片机的5v或者3v引脚
GND(-):电源负极接口,外接电源负极或地线GND,接单片机的GND
CLK :时钟线,接单片机的PB10引脚
DAT :数据线,接单片机的PB1引脚
RST :接单片机的PB11引脚
编译环境:keil5
测试单片机: STM32F103C8T6
功能:功能1:按键1设置时间、按键2选择功能、按键3加功能、按键4减功能、按键5取消功能
DS1302.c文件编写雨滴传感器驱动代码如下:
#include "DS1302.h"
#include "delay.h"
#include "OLED.h"
#include "Key.h"
extern uint8_t KeyNum,MODE,TimeSetSelect,TimeSetFlashFlag; //按键情况、模式选择、时间修改位、修改位闪烁标志
extern uint8_t AlarmSetSelect,AlarmSetFlashFlag,AlarmNum;
extern uint16_t Tempo;
uint16_t datetime[7] = {24, 6, 1, 12, 0, 0, 1};//默认时间
void ds1302_gpio_init()//使用前用这个函数初始化
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11; //RST
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_ResetBits(GPIOB,GPIO_Pin_11);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//CLK
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_ResetBits(GPIOB,GPIO_Pin_10);
}
void ds1032_DATAOUT_init()
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;//DAT
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_ResetBits(GPIOB,GPIO_Pin_1);
}
void ds1032_DATAINPUT_init()
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
void ds1302_write_onebyte(u8 data)
{
ds1032_DATAOUT_init();
u8 count=0;
SCLK_L;
for(count=0;count<8;count++)
{ SCLK_L;
if(data&0x01)
{DATA_H;}
else{DATA_L;}
SCLK_H;
data>>=1;
}
}
void ds1302_wirte_rig(u8 address,u8 data)
{
u8 temp1=address;
u8 temp2=data;
CE_L;SCLK_L;Delay_us(1);
CE_H;Delay_us(2);
ds1302_write_onebyte(temp1);
ds1302_write_onebyte(temp2);
CE_L;SCLK_L;Delay_us(2);
}
u8 ds1302_read_rig(u8 address)
{
u8 temp3=address;
u8 count=0;
u8 return_data=0x00;
CE_L;SCLK_L;Delay_us(3);
CE_H;Delay_us(3);
ds1302_write_onebyte(temp3);
ds1032_DATAINPUT_init();
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);
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1))
{return_data=return_data|0x80;}
}
Delay_us(2);
CE_L;DATA_L;
return return_data;
}
void ds1032_set_time()//将datetime数据写入DS1302(在初始化后应该用该函数写入一次时间数据)
{
ds1302_wirte_rig(0x8e,0x00);//关闭写保护
ds1302_wirte_rig(0x80,datetime[5]/10*16+datetime[5]%10);//seconds
ds1302_wirte_rig(0x82,datetime[4]/10*16+datetime[4]%10);//minutes
ds1302_wirte_rig(0x84,datetime[3]/10*16+datetime[3]%10);//hours
ds1302_wirte_rig(0x86,datetime[2]/10*16+datetime[2]%10);//date
ds1302_wirte_rig(0x88,datetime[1]/10*16+datetime[1]%10);//months
ds1302_wirte_rig(0x8a,datetime[6]/10*16+datetime[6]%10);//days
ds1302_wirte_rig(0x8c,datetime[0]/10*16+datetime[0]%10);//year
ds1302_wirte_rig(0x8e,0x80);//开启写保护
}
void ds1032_read_time()//从DS1302读出时间数据并写入datetime,可以在主函数对datetime进行读取和修改操作
{
uint16_t Temp;
Temp=ds1302_read_rig(0x81);
datetime[5]=Temp/16*10+Temp%16;
Temp=ds1302_read_rig(0x83);
datetime[4]=Temp/16*10+Temp%16;
Temp=ds1302_read_rig(0x85);
datetime[3]=Temp/16*10+Temp%16;
Temp=ds1302_read_rig(0x87);
datetime[2]=Temp/16*10+Temp%16;
Temp=ds1302_read_rig(0x89);
datetime[1]=Temp/16*10+Temp%16;
Temp=ds1302_read_rig(0x8B);
datetime[6]=Temp/16*10+Temp%16;
Temp=ds1302_read_rig(0x8D);
datetime[0]=Temp/16*10+Temp%16;
}
//struct Alarm_Set{
//uint8_t num;
//uint8_t enable;
//uint8_t mode;
//uint8_t flag;
//uint8_t hour;
//uint8_t minute;
//};
//struct Alarm_Set Alarm[5]={
//{0,1,0,0,0,0},
//{1,1,1,0,0,1},
//{2,1,2,0,0,2},
//{3,1,3,0,0,3},
//{4,0,0,0,0,4}
//};
uint8_t GetDay(void)
{
uint8_t week;
uint16_t temp[3];
temp[0]=datetime[0];
temp[1]=datetime[1];
temp[2]=datetime[2];
if (temp[1] == 1 || temp[1] == 2)
{
temp[1] += 12;
temp[0]--;
}
week = (temp[0] + temp[0] / 4 + (13*(temp[1]+1)/5) + temp[2] - 36) % 7;
return week;
}
void ShowDay(void)
{
switch(GetDay())
{
case 0:
OLED_ShowString(4,8,"7");
break;
case 1:
OLED_ShowString(4,8,"1");
break;
case 2:
OLED_ShowString(4,8,"2");
break;
case 3:
OLED_ShowString(4,8,"3");
break;
case 4:
OLED_ShowString(4,8,"4");
break;
case 5:
OLED_ShowString(4,8,"5");
break;
case 6:
OLED_ShowString(4,8,"6");
break;
}
}
void ShowTime(void)
{
ds1032_read_time();
ShowDay();
OLED_ShowNum(2,4,2000+datetime[0],4);
OLED_ShowNum(2,9,datetime[1],2);
OLED_ShowNum(2,12,datetime[2],2);
OLED_ShowNum(3,5,datetime[3],2);
OLED_ShowNum(3,8,datetime[4],2);
OLED_ShowNum(3,11,datetime[5],2);
}
void SetTime(void)
{
if(KeyNum==4)//取消修改
{
MODE=0;
}
if(KeyNum==2)//保存修改
{
MODE=0;
ds1032_set_time();
}
if(KeyNum==3)//移位
{
TimeSetSelect++;
if(TimeSetSelect>5)TimeSetSelect=0;
// TimeSetSelect%=6;//TimeSetSelect = TimeSetSelect % 6;
}
if(KeyNum==5)//数值+1
{
datetime[TimeSetSelect]++;//时间设置位数值加1
if(datetime[0]>99){datetime[0]=0;}//年越界判断
if(datetime[1]>12){datetime[1]=1;}//月越界判断
if( datetime[1]==1 || datetime[1]==3 || datetime[1]==5 || datetime[1]==7 ||
datetime[1]==8 || datetime[1]==10 || datetime[1]==12)//日越界判断
{
if(datetime[2]>31){datetime[2]=1;}//大月
}
else if(datetime[1]==4 || datetime[1]==6 || datetime[1]==9 || datetime[1]==11)
{
if(datetime[2]>30){datetime[2]=1;}//小月
}
else if(datetime[1]==2)
{
if(datetime[0]%4==0)
{
if(datetime[2]>29){datetime[2]=1;}//闰年2月
}
else
{
if(datetime[2]>28){datetime[2]=1;}//平年2月
}
}
if(datetime[3]>23){datetime[3]=0;}//时越界判断
if(datetime[4]>59){datetime[4]=0;}//分越界判断
if(datetime[5]>59){datetime[5]=0;}//秒越界判断
}
if(KeyNum==1)//数值-1
{
datetime[TimeSetSelect]--;//时间设置位数值减1
if(datetime[0]>100){datetime[0]=99;}//年越界判断
if(datetime[1]<1){datetime[1]=12;}//月越界判断
if( datetime[1]==1 || datetime[1]==3 || datetime[1]==5 || datetime[1]==7 ||
datetime[1]==8 || datetime[1]==10 || datetime[1]==12)//日越界判断
{
if(datetime[2]<1){datetime[2]=31;}//大月
if(datetime[2]>31){datetime[2]=1;}
}
else if(datetime[1]==4 || datetime[1]==6 || datetime[1]==9 || datetime[1]==11)
{
if(datetime[2]<1){datetime[2]=30;}//小月
if(datetime[2]>30){datetime[2]=1;}
}
else if(datetime[1]==2)
{
if(datetime[0]%4==0)
{
if(datetime[2]<1){datetime[2]=29;}//闰年2月
if(datetime[2]>29){datetime[2]=1;}
}
else
{
if(datetime[2]<1){datetime[2]=28;}//平年2月
if(datetime[2]>28){datetime[2]=1;}
}
}
if(datetime[3]>100){datetime[3]=23;}//时越界判断
if(datetime[4]>100){datetime[4]=59;}//分越界判断
if(datetime[5]>100){datetime[5]=59;}//秒越界判断
}
//以下是实现被选中位闪烁效果
if(TimeSetSelect==0 && TimeSetFlashFlag==1){OLED_ShowString(2,4," ");}
else {OLED_ShowNum(2,4,2000+datetime[0],4);}
if(TimeSetSelect==1 && TimeSetFlashFlag==1){OLED_ShowString(2,9," ");}
else {OLED_ShowNum(2,9,datetime[1],2);}
if(TimeSetSelect==2 && TimeSetFlashFlag==1){OLED_ShowString(2,12," ");}
else {OLED_ShowNum(2,12,datetime[2],2);}
if(TimeSetSelect==3 && TimeSetFlashFlag==1){OLED_ShowString(3,5," ");}
else {OLED_ShowNum(3,5,datetime[3],2);}
if(TimeSetSelect==4 && TimeSetFlashFlag==1){OLED_ShowString(3,8," ");}
else {OLED_ShowNum(3,8,datetime[4],2);}
if(TimeSetSelect==5 && TimeSetFlashFlag==1){OLED_ShowString(3,11," ");}
else {OLED_ShowNum(3,11,datetime[5],2);}
ShowDay();
}
void Scan_Key()
{
KeyNum = Key_GetNum();
switch(KeyNum)
{
case 2: //按2进入时间设置
MODE=1;
TimeSetSelect=0;
OLED_Clear();
OLED_ShowString(1,1,"SetTime");
OLED_ShowString(2,8,"- -");
OLED_ShowString(3,7,": :");
OLED_ShowString(4,1,"week:");
while(MODE==1)
{
KeyNum = Key_GetNum();
SetTime();
}
OLED_Clear();
OLED_ShowString(2,8,"- -");
OLED_ShowString(3,7,": :");
OLED_ShowString(4,1,"week:");
break;
case 3: //按3进入闹钟设置
// MODE=2;
// AlarmSetSelect=0;
// AlarmNum=0;
// OLED_Clear();
// OLED_ShowString(4,13,":");
// while(MODE==2)
// {
// KeyNum = Key_GetNum();
// //SetAlarm();
// }
// OLED_Clear();
// OLED_ShowString(2,8,"- -");
// OLED_ShowString(3,7,": :");
break;
default:break;
}
ShowTime();
//CheckAlarm(); //检查闹钟
}
DS1302.h文件,对驱动函数经行了声明:
#ifndef __DS1302_H_
#define __DS1302_H_
//#include "sys.h"
#include "stm32f10x.h"
#define CE_L GPIO_ResetBits(GPIOB,GPIO_Pin_11) // CE口接在PB11,若更改,.c文件中也要更改,下同
#define CE_H GPIO_SetBits(GPIOB,GPIO_Pin_11)
#define SCLK_L GPIO_ResetBits(GPIOB,GPIO_Pin_10)// SCLK口接在PB10
#define SCLK_H GPIO_SetBits(GPIOB,GPIO_Pin_10)
#define DATA_L GPIO_ResetBits(GPIOB,GPIO_Pin_1)// IO/data数据口接在PB1
#define DATA_H GPIO_SetBits(GPIOB,GPIO_Pin_1)
extern uint16_t datetime[7];
void ds1302_gpio_init(void);
void ds1302_write_onebyte(u8 data);
void ds1302_wirte_rig(u8 address,u8 data);
u8 ds1302_read_rig(u8 address);
void ds1032_set_time(void);
void ds1032_DATAOUT_init(void);
void ds1032_DATAINPUT_init(void);
void ds1032_read_time(void);
uint8_t GetDay(void);
void ShowDay(void);
void ShowTime(void);
void SetTime(void);
void Scan_Key(void);
#endif
串口调试助手-keil5调试工具资源-CSDN文库
《STM32单片机+DS1302时钟模块+OLED屏幕》源代码资源-CSDN文库
DS1302时钟模块原理图、模块资料资源-CSDN文库