目录
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,网上关于其知识点很多,大家也可以自行学习:
-
DS1302的性能指标
1)可以计算2100年之前的秒、分、时、日、星期、月、年,并且可以调整闰年。
2)内部有31个字节静态RAM,供用户访问
3)串行数据传送方式(SPI3线接口)
4)工作电压:2.0~5.5v
5) 工作电流:2v时,小于300nA(功耗低)
6)时钟或RAM数据的读写,有两种传送方式:单字节传送、多字节传送
7)主电源和负电源双电源供电(备份电源可以用电池或大电容实现)
-
DS1302的寄存器及片内RAM
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 |
- 其中:
- D7:固定为1
- D6:RAM/CK位,=1片内RAM,=0日历、时钟寄存器选择位。
- D5-D1:地址位,用于选择进行读写的日历、时钟寄存器或片内RAM。对日历、时钟寄存器或片内RAM的选择。
- D0:读写选择,=0写,=1读
寄存器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; // DATAGPIO_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 DATAGPIO_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; // 拉低CESCLK_L; // 拉低SCLKdelay_us(1);CE_H; // 拉高CEdelay_us(2);ds1302_write_onebyte(temp1); // 写命令ds1302_write_onebyte(temp2); // 写数据CE_L; // 拉低CESCLK_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; // 拉低CESCLK_L; // 拉低SCLKdelay_us(3);CE_H; // 拉高CEdelay_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; // 拉低CEDATA_L; // 拉低SCLKreturn 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.** <h2><center>© COPYRIGHT 2011 STMicroelectronics</center></h2>*******************************************************************************/ /* 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<strlen(str4);i++){USART_SendData(USART1,str4[i]);delay_ms(20);}ds1302_read_realTime();delay_ms(10);for(i = 0;i < 8;i++){sprintf(str,"%c",DS1302_data_2[i]); strcat(str2,str); }strcat(str2,"\r\n"); for(i=0;i<strlen(str2);i++){USART_SendData(USART1,str2[i]);delay_ms(20);}delay_ms(200); memset(str2,0,sizeof(str2)); memset(str4,0,sizeof(str4)); }
}/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/
实现效果:
HAL库实现:
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); // 拉低RSTHAL_GPIO_WritePin(GPIOA,CLK_Pin, GPIO_PIN_RESET);// 拉低时钟delay_us(1);HAL_GPIO_WritePin(GPIOA,RST_Pin, GPIO_PIN_SET); // 拉高RSTdelay_us(2);ds1302_write_onebyte(temp1); // 写命令ds1302_write_onebyte(temp2); // 写数据HAL_GPIO_WritePin(GPIOA,RST_Pin, GPIO_PIN_RESET); // 拉低RSTHAL_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); // 拉低RSTHAL_GPIO_WritePin(GPIOA,CLK_Pin, GPIO_PIN_RESET);// 拉低时钟delay_us(3);HAL_GPIO_WritePin(GPIOA,RST_Pin, GPIO_PIN_SET); // 拉高RSTdelay_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); // 拉低RSTHAL_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文库