目录
DS1302的性能指标
DS1302的寄存器及片内RAM
标准库实现
HAL库实现
源码链接
单片机型号:STM32F103C8T6
在日常生活中,很多情况下会需要使用时间,单片机中虽然也是有定时器但并不能较为准备的实现计时功能,而且定时计时纯属浪费资源,因此,为了解决此问题,就引入了时钟芯片,时钟芯片能够实现较为精确计时,可实现电子钟/闹钟等功能。
市面上时钟芯片种类很多,本文选用使用较为广泛的DS1302芯片。
DS1302是DALLAS公司推出的涓流充电时钟芯片,内含一个实时时钟/日历和31字节静态RAM,通过简单的串行接口与单片机进行通信。实时时钟/日历电路提供秒、分、时、日、周、月、年的信息,每月的天数和闰年的天数可自动调整。时钟操作可通过AM/PM指示决定采用24或12小时格式。DS1302与单片机之间能简单地采用同步串行的方式进行通信,仅需用到三个口线: (1) RST复位(2) I/O数据线(3)SCLK串行时钟。
时钟/RAM的读/写数据以一个字节或多达31个字节的字符组方式通信。DS1302工作时功耗很低保持数据和时钟信息时功率小于1mW。
下面是其电路图,通过自己设计也是可以实现的,电路图如下:
如果自己不想设计电路,也可以在网上购买时钟模块,其实就是按照上述电路图设计制成,效果图如下:
接下来简单介绍一下DS1302,网上关于其知识点很多,大家也可以自行学习:
1)可以计算2100年之前的秒、分、时、日、星期、月、年,并且可以调整闰年。
2)内部有31个字节静态RAM,供用户访问
3)串行数据传送方式(SPI3线接口)
4)工作电压:2.0~5.5v
5) 工作电流:2v时,小于300nA(功耗低)
6)时钟或RAM数据的读写,有两种传送方式:单字节传送、多字节传送
7)主电源和负电源双电源供电(备份电源可以用电池或大电容实现)
DS1302内部包括1个控制寄存器,12个寄存器(7个与日历、时钟相关,存放的数据是BCD码形式)和31个RAM
1)控制寄存器
用于存放ds1302的控制命令字,DS1302的复位引脚回到高电平后写入的第一个字就是控制命令,控制着ds1302的读写过程。
D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
1 | RAM/CK | A4 | A3 | A2 | A1 | A0 | RD/W |
寄存器0:最高位 CH 是一个时钟停止标志位。如果时钟电路有备用电源,上电后,我们要先检测一下这一位,如果这一位是0,那说明时钟芯片在系统掉电后,由于备用电源的供给,时钟是持续正常运行的;如果这一位是1,那么说明时钟芯片在系统掉电后,时钟部分不工作了。如果 Vcc1 悬空或者是电池没电了,当我们下次重新上电时,读取这一位,那这一位就是1,我们可以通过这一位判断时钟在单片机系统掉电后是否还正常运行。剩下的7位高3位是秒的十位,低4位是秒的个位,这里再提请注意一次,DS1302 内部是 BCD 码,而秒的十位最大是5,所以3个二进制位就够了。
寄存器1:最高位未使用,剩下的7位中高3位是分钟的十位,低4位是分钟的个位。
寄存器2:bit7 是1的话代表是12小时制,0代表是24小时制;bit6 固定是0,bit5 在12小时制下 0代表的是上午,1代表的是下午,在24小时制下和 bit4 一起代表了小时的十位,低4位代表的是小时的个位。
寄存器3:高2位固定是0,bit5 和 bit4 是日期的十位,低4位是日期的个位。
寄存器4:高3位固定是0,bit4 是月的十位,低4位是月的个位。
寄存器5:高5位固定是0,低3位代表了星期。
寄存器6:高4位代表了年的十位,低4位代表了年的个位。请特别注意,这里的00~99指的是2000年~2099年。
寄存器7:最高位一个写保护位,如果这一位是1,那么是禁止给任何其它寄存器或者那31个字节的 RAM 写数据的。因此在写数据之前,这一位必须先写成0。
接下来是代码实现:
接线表设计:
时钟模块 | STM32F103 |
VCC | 5V/3.3V |
GND | GND |
CLK | PA0 |
DATA | PA1 |
RST | PA2 |
- | PA9(串口1) |
- | PA10(串口1) |
DS1302.c
#include "ds1302.h"
#include "delay.h"
u8 read_time[7];
struct TIMEData TimeData;
char DS1302_data_1[10];
char DS1302_data_2[8];
/*
* SCLK 和 CE初始化
*/
void ds1302_gpio_init()
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure); //
GPIO_ResetBits(GPIOA,GPIO_Pin_2);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure); //
GPIO_ResetBits(GPIOA,GPIO_Pin_0);
}
/*
* 数据端口输出配置
*/
void ds1302_DATAOUT_init()//配置双向I/O端口为输出态
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; // DATA
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOA,&GPIO_InitStructure); // 初始化
GPIO_ResetBits(GPIOA,GPIO_Pin_1);
}
/*
* 数据端口输入配置
*/
void ds1302_DATAINPUT_init()//配置双向I/O端口为输入态
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; //PA.1 DATA
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.1
}
/*
* /向DS1302发送一字节数据
*/
void ds1302_write_onebyte(uint8_t data)//向DS1302发送一字节数据
{
uint8_t count=0;
ds1302_DATAOUT_init(); // I/O配置为输出
SCLK_L; // 拉低时钟
for(count=0;count<8;count++)
{
SCLK_L; // 拉低时钟
if(data&0x01)
{
DATA_H;
}
else
{
DATA_L;
}//先准备好数据再发送
SCLK_H; //拉高时钟线,发送数据
data>>=1;
}
}
/*
* 向DS1302发送指定数据
*/
void ds1302_wirte_rig(uint8_t address,uint8_t data)//向指定寄存器地址发送数据
{
uint8_t temp1=address;
uint8_t temp2=data;
CE_L; // 拉低CE
SCLK_L; // 拉低SCLK
delay_us(1);
CE_H; // 拉高CE
delay_us(2);
ds1302_write_onebyte(temp1); // 写命令
ds1302_write_onebyte(temp2); // 写数据
CE_L; // 拉低CE
SCLK_L; // 拉低时钟
delay_us(2);
}
/*
* 从DS1302读取数据
*/
uint8_t ds1302_read_rig(uint8_t address)//从指定地址读取一字节数据
{
uint8_t temp3=address;
uint8_t count=0;
uint8_t return_data=0x00;
CE_L; // 拉低CE
SCLK_L; // 拉低SCLK
delay_us(3);
CE_H; // 拉高CE
delay_us(3);
ds1302_write_onebyte(temp3); // 写地址
ds1302_DATAINPUT_init();//配置I/O口为输入
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(GPIOA,GPIO_Pin_1))
{return_data=return_data|0x80;}
}
delay_us(2);
CE_L; // 拉低CE
DATA_L; // 拉低SCLK
return return_data; // 返回数据
}
/*
* 初始化DS1302
*/
void ds1302_init()
{
ds1302_wirte_rig(0x8e,0x00);//关闭写保护
ds1302_wirte_rig(0x80,0x37);//seconds37秒
ds1302_wirte_rig(0x82,0x58);//minutes58分
ds1302_wirte_rig(0x84,0x23);//hours23时
ds1302_wirte_rig(0x86,0x30);//date30日
ds1302_wirte_rig(0x88,0x09);//months9月
ds1302_wirte_rig(0x8a,0x07);//days星期日
ds1302_wirte_rig(0x8c,0x20);//year2020年
//ds1302_wirte_rig(0x80,0x19);
ds1302_wirte_rig(0x8e,0x80);//关闭写保护
}
/*
* 读取DS1302数据
*/
void ds1302_read_time(void)
{
read_time[0]=ds1302_read_rig(0x81);//读秒
read_time[1]=ds1302_read_rig(0x83);//读分
read_time[2]=ds1302_read_rig(0x85);//读时
read_time[3]=ds1302_read_rig(0x87);//读日
read_time[4]=ds1302_read_rig(0x89);//读月
read_time[5]=ds1302_read_rig(0x8B);//读星期
read_time[6]=ds1302_read_rig(0x8D);//读年
}
void ds1302_read_realTime(void)
{
ds1302_read_time(); //BCD码转换为10进制
TimeData.second=(read_time[0]>>4)*10+(read_time[0]&0x0f);
TimeData.minute=((read_time[1]>>4)&(0x07))*10+(read_time[1]&0x0f);
TimeData.hour=(read_time[2]>>4)*10+(read_time[2]&0x0f);
TimeData.day=(read_time[3]>>4)*10+(read_time[3]&0x0f);
TimeData.month=(read_time[4]>>4)*10+(read_time[4]&0x0f);
TimeData.week=read_time[5];
TimeData.year=(read_time[6]>>4)*10+(read_time[6]&0x0f)+2000;
DS1302_data_1[0]='2';
DS1302_data_1[1]='0';
DS1302_data_1[2]='0'+(TimeData.year-2000)/10;
DS1302_data_1[3]='0'+(TimeData.year-2000)%10;
DS1302_data_1[4]='-';
DS1302_data_1[5]='0'+TimeData.month/10;
DS1302_data_1[6]='0'+TimeData.month%10;
DS1302_data_1[7]='-';
DS1302_data_1[8]='0'+TimeData.day/10;
DS1302_data_1[9]='0'+TimeData.day%10;
DS1302_data_2[0]='0'+TimeData.hour/10;
DS1302_data_2[1]='0'+TimeData.hour%10;
DS1302_data_2[2]=':';
DS1302_data_2[3]='0'+TimeData.minute/10;
DS1302_data_2[4]='0'+TimeData.minute%10;
DS1302_data_2[5]=':';
DS1302_data_2[6]='0'+TimeData.second/10;
DS1302_data_2[7]='0'+TimeData.second%10;
}
main.c
/**
******************************************************************************
* @file Project/STM32F10x_StdPeriph_Template/main.c
* @author MCD Application Team
* @version V3.5.0
* @date 08-April-2011
* @brief Main program body
******************************************************************************
* @attention
*
* THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
* WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
* TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY
* DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
* FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
* CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
*
* © COPYRIGHT 2011 STMicroelectronics
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "stm32f10x.h"
#include "delay.h"
#include "usart.h"
#include "stdio.h"
#include "string.h"
#include "ds1302.h"
/**
* @brief Main program.
* @param None
* @retval None
*/
int main(void)
{
int i = 0; // 循环变量
char str[10] =""; // 字符数组
char str2[100] =""; // 字符数组
char str3[10] =""; // 字符数组
char str4[100] =""; // 字符数组
delay_init(); // 延时函数初始化
uart_init(9600); // 串口1函数初始化
ds1302_gpio_init();//CE,SCLK端口初始化
ds1302_init();
/* Infinite loop */
while (1)
{
ds1302_read_realTime();
delay_ms(10);
for(i = 0;i < 10;i++)
{
sprintf(str3,"%c",DS1302_data_1[i]);
strcat(str4,str3);
}
strcat(str4,"\r\n");
for(i=0;i
实现效果:
ds1302.c
#include "main.h"
#include "ds1302.h"
uint8_t read_time[7];
struct TIMEData TimeData;
char DS1302_data_1[10];
char DS1302_data_2[8];
void ds1302_DATAOUT_init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOA_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOA,DATA_Pin, GPIO_PIN_RESET);
/*Configure GPIO pin : DATA_Pin */
GPIO_InitStruct.Pin = DATA_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(DATA_GPIO_Port, &GPIO_InitStruct);
}
void ds1302_DATAINPUT_init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOA_CLK_ENABLE();
/*Configure GPIO pin : PA2 */
GPIO_InitStruct.Pin = DATA_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(DATA_GPIO_Port, &GPIO_InitStruct);
}
/*
* /向DS1302发送一字节数据
*/
void ds1302_write_onebyte(uint8_t data)//向DS1302发送一字节数据
{
uint8_t count=0;
ds1302_DATAOUT_init(); // I/O配置为输出
HAL_GPIO_WritePin(GPIOA,CLK_Pin, GPIO_PIN_RESET);// 拉低时钟
for(count=0;count<8;count++)
{
HAL_GPIO_WritePin(GPIOA,CLK_Pin, GPIO_PIN_RESET);// 拉低时钟
if(data&0x01)
{
HAL_GPIO_WritePin(GPIOA,DATA_Pin, GPIO_PIN_SET);
}
else
{
HAL_GPIO_WritePin(GPIOA,DATA_Pin, GPIO_PIN_RESET);
}//先准备好数据再发送
HAL_GPIO_WritePin(GPIOA,CLK_Pin, GPIO_PIN_SET); //拉高时钟线,发送数据
data>>=1;
}
}
/*
* 向DS1302发送指定数据
*/
void ds1302_wirte_rig(uint8_t address,uint8_t data)//向指定寄存器地址发送数据
{
uint8_t temp1=address;
uint8_t temp2=data;
HAL_GPIO_WritePin(GPIOA,RST_Pin, GPIO_PIN_RESET); // 拉低RST
HAL_GPIO_WritePin(GPIOA,CLK_Pin, GPIO_PIN_RESET);// 拉低时钟
delay_us(1);
HAL_GPIO_WritePin(GPIOA,RST_Pin, GPIO_PIN_SET); // 拉高RST
delay_us(2);
ds1302_write_onebyte(temp1); // 写命令
ds1302_write_onebyte(temp2); // 写数据
HAL_GPIO_WritePin(GPIOA,RST_Pin, GPIO_PIN_RESET); // 拉低RST
HAL_GPIO_WritePin(GPIOA,CLK_Pin, GPIO_PIN_RESET);// 拉低时钟
delay_us(2);
}
/*
* 从DS1302读取数据
*/
uint8_t ds1302_read_rig(uint8_t address)//从指定地址读取一字节数据
{
uint8_t temp3=address;
uint8_t count=0;
uint8_t return_data=0x00;
HAL_GPIO_WritePin(GPIOA,RST_Pin, GPIO_PIN_RESET); // 拉低RST
HAL_GPIO_WritePin(GPIOA,CLK_Pin, GPIO_PIN_RESET);// 拉低时钟
delay_us(3);
HAL_GPIO_WritePin(GPIOA,RST_Pin, GPIO_PIN_SET); // 拉高RST
delay_us(3);
ds1302_write_onebyte(temp3); // 写地址
ds1302_DATAINPUT_init();//配置I/O口为输入
for(count=0;count<8;count++)
{
delay_us(2);//使电平持续一段时间
return_data>>=1;
HAL_GPIO_WritePin(GPIOA,CLK_Pin, GPIO_PIN_SET);// 拉高时钟
delay_us(4);//使高电平持续一段时间
HAL_GPIO_WritePin(GPIOA,CLK_Pin, GPIO_PIN_RESET);// 拉低时钟
delay_us(14);//延时14us后再去读取电压,更加准确
if(HAL_GPIO_ReadPin(GPIOA,DATA_Pin))
{return_data=return_data|0x80;}
}
delay_us(2);
HAL_GPIO_WritePin(GPIOA,RST_Pin, GPIO_PIN_RESET); // 拉低RST
HAL_GPIO_WritePin(GPIOA,DATA_Pin, GPIO_PIN_RESET); // DATA拉低
return return_data; // 返回数据
}
/*
* 初始化DS1302
注意,写的时候D0配置为0
写入时
秒:1000 0000即0x80
分:1000 0010即0x82
时:1000 0100即0x84
日:1000 0110即0x86
月:1000 1000即0x88
周:1000 1010即0x8a
年:1000 1100即0x8c
*/
void ds1302_init()
{
ds1302_wirte_rig(0x8e,0x00);//关闭写保护
ds1302_wirte_rig(0x80,0x37);//seconds37秒
ds1302_wirte_rig(0x82,0x58);//minutes58分
ds1302_wirte_rig(0x84,0x23);//hours23时
ds1302_wirte_rig(0x86,0x30);//date30日
ds1302_wirte_rig(0x88,0x09);//months9月
ds1302_wirte_rig(0x8a,0x07);//days星期日
ds1302_wirte_rig(0x8c,0x20);//year2020年
//ds1302_wirte_rig(0x80,0x19);
ds1302_wirte_rig(0x8e,0x80);//关闭写保护
}
/*
* 读取DS1302数据
注意,读的时候D0配置为1
读取时
秒:1000 0001即0x81
分:1000 0011即0x83
时:1000 0101即0x85
日:1000 0111即0x87
月:1000 1001即0x89
周:1000 1011即0x8b
年:1000 1101即0x8d
*/
void ds1302_read_time(void)
{
read_time[0]=ds1302_read_rig(0x81);//读秒
read_time[1]=ds1302_read_rig(0x83);//读分
read_time[2]=ds1302_read_rig(0x85);//读时
read_time[3]=ds1302_read_rig(0x87);//读日
read_time[4]=ds1302_read_rig(0x89);//读月
read_time[5]=ds1302_read_rig(0x8B);//读星期
read_time[6]=ds1302_read_rig(0x8D);//读年
}
void ds1302_read_realTime(void)
{
ds1302_read_time(); //BCD码转换为10进制
TimeData.second=(read_time[0]>>4)*10+(read_time[0]&0x0f);
TimeData.minute=((read_time[1]>>4)&(0x07))*10+(read_time[1]&0x0f);
TimeData.hour=(read_time[2]>>4)*10+(read_time[2]&0x0f);
TimeData.day=(read_time[3]>>4)*10+(read_time[3]&0x0f);
TimeData.month=(read_time[4]>>4)*10+(read_time[4]&0x0f);
TimeData.week=read_time[5];
TimeData.year=(read_time[6]>>4)*10+(read_time[6]&0x0f)+2000;
DS1302_data_1[0]='2';
DS1302_data_1[1]='0';
DS1302_data_1[2]='0'+(TimeData.year-2000)/10;
DS1302_data_1[3]='0'+(TimeData.year-2000)%10;
DS1302_data_1[4]='-';
DS1302_data_1[5]='0'+TimeData.month/10;
DS1302_data_1[6]='0'+TimeData.month%10;
DS1302_data_1[7]='-';
DS1302_data_1[8]='0'+TimeData.day/10;
DS1302_data_1[9]='0'+TimeData.day%10;
DS1302_data_2[0]='0'+TimeData.hour/10;
DS1302_data_2[1]='0'+TimeData.hour%10;
DS1302_data_2[2]=':';
DS1302_data_2[3]='0'+TimeData.minute/10;
DS1302_data_2[4]='0'+TimeData.minute%10;
DS1302_data_2[5]=':';
DS1302_data_2[6]='0'+TimeData.second/10;
DS1302_data_2[7]='0'+TimeData.second%10;
}
main.c中mian函数
int main(void)
{
/* USER CODE BEGIN 1 */
char str[100] =""; // 字符数组
char str2[100] =""; // 字符数组
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
ds1302_init(); //
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
ds1302_read_realTime();
HAL_Delay(10);
strcat(str,DS1302_data_2);
strcat(str,"\r\n");
strcat(str2,DS1302_data_1);
strcat(str2,"\r\n");
while(HAL_OK != HAL_UART_Transmit(&huart1,(unsigned char *)str2,strlen(str2), 500));
while(HAL_OK != HAL_UART_Transmit(&huart1,(unsigned char *)str,strlen(str), 500));
HAL_Delay(500);
memset(str,0,sizeof(str));
memset(str2,0,sizeof(str2));
}
/* USER CODE END 3 */
}
效果如下:
以上是将数据读取出来,然后通过串口发送出来,这样便于大家移植,祝大家学习愉快,还要沟通交流!!!
STM32F103操作DS1302时钟芯片串口显示(标准库和HAL库)-C文档类资源-CSDN文库