DS1302教程与Proteus仿真(支持51和STM32
DS1302是一款时钟芯片,能精确对年月日时分秒进行计算,并且能自动校准闰年和每个月的不同天数,下面从51和stm32两款单片机介绍其用法
DS1302采用三线SPI通信 ,下图详细说明了各引脚的接线方式
DS1302使用的并不是标准的SPI通信,它的数据输入输出为同一根线。通信时序图如下图所示
空闲状态时SCK为低电平,时钟信号的第一个沿开始传输数据,低位在前高位在后,每次通信前都需要将CE引脚拉高。值得注意的是DS1302输出的数据为BCD码,比如串行通信输出的16进制数据0x21,按照正常其转换为十进制为33,但DS1302输出的0x21就代表十进制数21。其他没有什么需要特别注意的。
下面是代码和proteus仿真部分
先是51的,使用STC89C52RC单片机
DS1302驱动函数的头文件
#ifndef _DS1302_H_
#define _DS1302_H_
#include <reg52.h>
sbit CE=P1^0;
sbit SCK=P1^1;
sbit IO=P1^2;
void DS1302_Init();
void DS1302_Write_Reg(unsigned char Reg,unsigned char Dat);
unsigned char DS1302_Read_Reg(unsigned char Reg);
void SPI_Write(unsigned char Dat);
unsigned char SPI_Read();
#endif
DS1302的驱动函数
#include "ds1302.h"
void DS1302_Init()
{
CE=0;
SCK=0;
}
void DS1302_Write_Reg(unsigned char Reg,unsigned char Dat)
{
CE=1;
SPI_Write(Reg);
SPI_Write(Dat);
CE=0;
}
unsigned char DS1302_Read_Reg(unsigned char Reg)
{
unsigned char Dat;
CE=1;
SPI_Write(Reg);
Dat=SPI_Read();
CE=0;
return Dat;
}
void SPI_Write(unsigned char Dat)
{
unsigned char i=0;
for(i=0;i<8;i++)
{
if(Dat&0x01)
{
IO=1;
}
else
{
IO=0;
}
SCK=1;
SCK=0;
Dat>>=1;
}
IO=1;
}
unsigned char SPI_Read()
{
unsigned char i=0,Dat=0;
for(i=0;i<8;i++)
{
Dat>>=1;
SCK=1;
if(IO)
{
Dat|=0x80;
}
SCK=0;
}
return Dat;
}
主函数部分
首先进行DS1302的初始化,将时分秒部分清0,年月日暂且不管,然后进入主循环,不断的对时间进行读取,这里并没有执行BCD码的转换
#include "ds1302.h"
void main()
{
unsigned char h,m,s;
DS1302_Init();
DS1302_Write_Reg(0x8e,0x80);
DS1302_Write_Reg(0x80,0x00);
DS1302_Write_Reg(0x82,0x00);
DS1302_Write_Reg(0x84,0x00);
while(1)
{
h=DS1302_Read_Reg(0x85);
m=DS1302_Read_Reg(0x83);
s=DS1302_Read_Reg(0x81);
}
}
下面是proteus仿真电路图,DS1302的晶振不接也是可以正常运行的
接下来是stm32部分,使用stm32f103c8t6单片机
DS1302驱动函数的头文件
#ifndef _DS1302_H_
#define _DS1302_H_
void DS1302_Init(void);
void SPI_Write(unsigned char Dat);
unsigned char SPI_Read(void);
void DS1302_Write_Reg(unsigned char Reg,unsigned char Dat);
unsigned char DS1302_Read_Reg(unsigned char Reg);
#endif
DS1302驱动函数
#include "stm32f10x_rcc.h"
//CE PA0
//SCK PA1
//IO PA2
void DS1302_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef DS1302={0};
DS1302.GPIO_Mode=GPIO_Mode_Out_OD;
DS1302.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2;
DS1302.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&DS1302);
GPIO_ResetBits(GPIOA,GPIO_Pin_0);
GPIO_ResetBits(GPIOA,GPIO_Pin_1);
}
void SPI_Write(unsigned char Dat)
{
unsigned char i=0;
for(i=0;i<8;i++)
{
if(Dat&0x01)
{
GPIO_SetBits(GPIOA,GPIO_Pin_2);
}
else
{
GPIO_ResetBits(GPIOA,GPIO_Pin_2);
}
GPIO_SetBits(GPIOA,GPIO_Pin_1);
GPIO_ResetBits(GPIOA,GPIO_Pin_1);
Dat>>=1;
}
GPIO_SetBits(GPIOA,GPIO_Pin_2);
}
unsigned char SPI_Read(void)
{
unsigned char i=0,Dat=0;
for(i=0;i<8;i++)
{
Dat>>=1;
GPIO_SetBits(GPIOA,GPIO_Pin_1);
if(GPIOA->IDR&1<<2)
{
Dat|=0x80;
}
GPIO_ResetBits(GPIOA,GPIO_Pin_1);
}
return Dat;
}
void DS1302_Write_Reg(unsigned char Reg,unsigned char Dat)
{
GPIO_SetBits(GPIOA,GPIO_Pin_0);
SPI_Write(Reg);
SPI_Write(Dat);
GPIO_ResetBits(GPIOA,GPIO_Pin_0);
}
unsigned char DS1302_Read_Reg(unsigned char Reg)
{
unsigned char Dat=0;
GPIO_SetBits(GPIOA,GPIO_Pin_0);
SPI_Write(Reg);
Dat=SPI_Read();
GPIO_ResetBits(GPIOA,GPIO_Pin_0);
return Dat;
}
主函数
#include "ds1302.h"
#include "stm32f10x.h"
int main()
{
DS1302_Init();
DS1302_Write_Reg(0x8e,0x80);
DS1302_Write_Reg(0x80,0x00);
DS1302_Write_Reg(0x82,0x00);
DS1302_Write_Reg(0x84,0x00);
while(1)
{
DS1302_Read_Reg(0x85);
DS1302_Read_Reg(0x83);
DS1302_Read_Reg(0x81);
}
}
下面是proteus仿真,管于这个proteus仿真,我要说点什么
就是关于这个开漏输出的上拉电阻,之前用oled12864的时候,I2C通信我选用开漏输出,没有加上拉电阻,它可以正常显示,再加上stm32本身就比较强悍,使得我对它内部带有上拉电阻深信不疑。本想着花两三个小时在51上把这款芯片研究通,再把他映射到stm32上将会非常简单,万万没想到,对着程序研究了一下午,就在最后快要放弃的时候,我尝试性地在通讯线上加了三颗上拉电阻,没想到竟然成功了。。。。