一、前期准备
单片机:STM32F103ZET6
开发环境:MDK5.14
库函数:标准库V3.5
DS1302模块:淘宝有售
二、实验效果
三、驱动原理
根据数据手册看时序,注意寄存器地址,读0x80、写0x81,支持时间修改,重新上电,时间数据一直走。
需要完整工程或者有问题的请加QQ:1002521871,验证:呵呵。
四、驱动代码
ds1302.h
#ifndef __DS_1302_H__
#define __DS_1302_H__
#include "stm32f10x.h"
#include "delay.h"
#include "gpio.h"
/* IO Definitions */
#define SCLK PAout(0) //时钟
#define SDA PAout(1) //数据
#define RST PAout(2) //DS1302复位(片选)
#define SCLK_Pin GPIO_Pin_0
#define SDA_Pin GPIO_Pin_1
#define RST_Pin GPIO_Pin_2
#define DS1302PORT GPIOA
#define DS1302CLKLINE RCC_APB2Periph_GPIOA
#define DS1302_W_ADDR 0x80
#define DS1302_R_ADDR 0x81
typedef struct
{
uint8_t second;
uint8_t minute;
uint8_t hour;
uint8_t day;
uint8_t month;
uint8_t week;
uint8_t year;
} __Date;
extern __Date ModifyDate;
extern void DS1302Configuration(void);
extern void read_time(uint8_t *timedata);
extern void ModifyCurrntTime(void);
extern uint8_t time[7];
#endif
ds1302.c
#include "ds1302.h"
uint8_t time[7] = {0x50, 0x30, 0x15, 0x05, 0x06, 0x02, 0x18};
// second,minute,hour,date,month,week,year
__Date ModifyDate;
GPIO_InitTypeDef GPIO;
//Enable APB2 Bus
void DS1302_RST(void)//令SCK=0;RST=1
{
RST = 0;//停止数据传送
SCLK = 0;//时钟总线清零
RST = 1;//数据操作
}
//写一个字节
void DS1302_Write_Byte(uint8_t dat)
{
uint8_t i;
RCC_APB2PeriphClockCmd(DS1302CLKLINE, ENABLE);
GPIO.GPIO_Pin = SDA_Pin;
GPIO.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(DS1302PORT, &GPIO);
for(i = 0; i < 8; i ++)
{
SDA = dat & 0x01;
SCLK = 1;
SCLK = 0;
dat >>= 1;
}
}
//读一个字节
//对于数据输出:开始的8 个SCLK 周期,输入一个读命令字节,数据字节在后8 个SCLK周期的下降沿输出
//注意,第一个数据字节的第一个下降沿发生后,命令字的最后一位被写入
//(Note that the first data bit to be transmitted occurs on the first falling edge after the last bit of the command byte is written. ),
//也就是命令字节最后一个字节写入后产生的下降沿使得第一个数据位被读取
uint8_t DS1302_Read_Byte(void)
{
uint8_t i, dat = 0;//用dat存储读到的数据
RCC_APB2PeriphClockCmd(DS1302CLKLINE, ENABLE);
GPIO.GPIO_Pin = SDA_Pin;
GPIO.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(DS1302PORT, &GPIO);
for(i = 0; i < 8; i ++)
{
dat >>= 1;
if(BIT_ADDR(GPIOA_IDR_Addr,1) == 1)
dat |= 0x80;//因为从低位开始读
SCLK = 1;
SCLK = 0; //由上面解释可以知道:我们首先应该判断 if(SDA==1),然后进行SCK=1,SCK=0
}
return dat;
}
//清除写保护
void DS1302_CLR_WP(void)
{
DS1302_RST();
RST = 1;
DS1302_Write_Byte(0x8E);//写入地址
DS1302_Write_Byte(0x00);//写入数据
//写完后
SDA = 0;
RST = 0;
}
//设置写保护
void DS1302_SET_WP(void)
{
DS1302_RST();
RST = 1;
DS1302_Write_Byte(0x8E);//写入地址
DS1302_Write_Byte(0x80);//写入数据
//写完后
SDA = 0;
RST = 0;
}
//写入ds1302
void DS1302_Write(uint8_t addr, uint8_t dat)
{
DS1302_RST();
RST = 1;
DS1302_Write_Byte(addr);//写入地址
DS1302_Write_Byte(dat);//写入数据
//写完后
RST = 0;
SDA = 0;
}
//从ds1302读数据
uint8_t DS1302_Read(uint8_t addr)
{
uint8_t temp = 0;//存储读出来的数据
DS1302_RST();
RST = 1;
DS1302_Write_Byte(addr);//写入地址
temp = DS1302_Read_Byte();//写入数据
//写完后
RST = 0;
SDA = 0;
return temp;
}
//void delay(uint16_t n)
//{
// while(n--);
//}
/**
* 设定时钟数据
其中涉及二进制和BCD转换 因为ds1302存BCD ,但是机器只认识二进制
例如:存入ds1302为50 0101 0000 那么我们写入的是ds1302_write(80)
*/
//void set_time(uint8_t *timedata)
void DS1302Configuration(void)
{
uint8_t i;
uint8_t temp = 0;
RCC_APB2PeriphClockCmd(DS1302CLKLINE, ENABLE);
//Register IO
GPIO.GPIO_Pin = SCLK_Pin | RST_Pin | SDA_Pin;
GPIO.GPIO_Speed = GPIO_Speed_50MHz;
GPIO.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(DS1302PORT, &GPIO);
if (DS1302_Read(0xcf) != 0xaa)
{
DS1302_CLR_WP();
temp = DS1302_W_ADDR;//写入起始位置
for(i = 0; i < 7; i ++)
{
DS1302_Write(temp, time[i]);
temp += 2;
}
// DS1302_Write(0x90, 0xa5);
DS1302_Write(0xce, 0xaa);
DS1302_SET_WP();
}
}
/**
* 读时钟数据(BCD格式)
*/
void read_time(uint8_t *timedata)
{
uint8_t i, temp;
temp = DS1302_R_ADDR;//读起始位置
for(i = 0; i < 7; i ++)
{
timedata[i] = DS1302_Read(temp);
temp += 2;
}
}
void ModifyCurrntTime(void)
{
DS1302_CLR_WP();
DS1302_Write(0x80, ModifyDate.second);
DS1302_Write(0x82, ModifyDate.minute);
DS1302_Write(0x84, ModifyDate.hour);
DS1302_Write(0x86, ModifyDate.day);
DS1302_Write(0x88, ModifyDate.month);
DS1302_Write(0x8A, ModifyDate.week);
DS1302_Write(0x8C, ModifyDate.year);
DS1302_Write(0xce, 0xaa);
DS1302_SET_WP();
}
由于作者能力有限,有不妥之处欢迎指正,邮箱alex_hua@foxmail.com