使用STM32控制DS1302时钟模块实时获取时间
欢迎入群共同学习交流
时间记录:2024/4/30
一、知识点
(1)读写数据时序(伪SPI协议)
1.1 读写时序默认电平均为SCLK线低电平,CE线低电平
1.2 写数据,CE线拉高为高电平,开始传输数据,然后准备数据在SCLK线的上升沿DS1302读取数据,发送8位控制命令后,发送8位数据(低位先发)
1.3 读数据,CE线拉高为高电平,开始传输数据,然后在SCLK低电平时准备数据,上升沿发送数据,发送8位控制命令后,下一个下降沿开始读取数据(低位先发,低位先读)
(2)读写命令及数据格式
2.1 READ和WRITE的命令即为发送和读取数据前需要先发送的对应8位控制命令
2.2 秒数据的BIT7位CH决定时钟的开启/关闭
2.3 小时数据的BIT7位决定是12小时显示还是24小时显示
2.4 WP位决定开启写保护还是取消写保护,写入0取消写保护,写入1打开写保护
(3)数据转换
3.1 DS1302读取的数据为BCD编码格式,需要转为10进制,写入时10进制数据需要转为BCD格式
3.2 BCD->10进制,BCD/1610+BCD%16
3.3 10进制->BCD,10进制/1016+10进制%10
二、示例代码
(1)头文件
#ifndef __DS1302_H__
#define __DS1302_H__
#include "stm32f10x.h"
typedef struct __DS1302TIME
{
int year;
int mon;
int day;
int hour;
int min;
int sec;
int week;
}ds_time;
void vDS1302_Init(void);
void vDS1302_SetTime(ds_time *sys_time);
void vDS1302_GetTime(ds_time *sys_time);
#endif
(2)源文件
#include "ds1302.h"
/**
* 模块引脚宏定义
*/
#define DS1302_SCLK_PORT GPIOA
#define DS1302_SCLK_PIN GPIO_Pin_10
#define DS1302_DAT_PORT GPIOA
#define DS1302_DAT_PIN GPIO_Pin_11
#define DS1302_RST_PORT GPIOA
#define DS1302_RST_PIN GPIO_Pin_12
/**
* 获取设置时间命令宏定义
*/
#define READ_SEC 0x81
#define WRITE_SEC 0x80
#define READ_MIN 0x83
#define WRITE_MIN 0x82
#define READ_HOUR 0x85
#define WRITE_HOUR 0x84
#define READ_DAY 0x87
#define WRITE_DAY 0x86
#define READ_MON 0x89
#define WRITE_MON 0x88
#define READ_WEEK 0x8B
#define WRITE_WEEK 0x8A
#define READ_YEAR 0x8D
#define WRITE_YEAR 0x8C
#define ENABLE_WRITE 0x00
#define DISABLE_WRITE 0x80
/**
* 枚举定义
*/
enum __LINEMODE
{
INPUT_MODE = 0,
OUTPUT_MODE
};
/**
* 函数声明
*/
static void vDatLineMode(u8 mode);
static void vWriteRam(u8 addr, u8 data);
static u8 vReadRam(u8 addr);
/**
* 初始化函数定义
*/
void vDS1302_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Pin = DS1302_SCLK_PIN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(DS1302_SCLK_PORT, &GPIO_InitStruct);
GPIO_ResetBits(DS1302_SCLK_PORT, DS1302_SCLK_PIN);
GPIO_InitStruct.GPIO_Pin = DS1302_RST_PIN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(DS1302_RST_PORT, &GPIO_InitStruct);
GPIO_ResetBits(DS1302_RST_PORT, DS1302_RST_PIN);
}
/**
* 数据引脚切换输入输出模式
*/
static void vDatLineMode(u8 mode)
{
GPIO_InitTypeDef GPIO_InitStruct;
if (mode == INPUT_MODE)
{
GPIO_InitStruct.GPIO_Pin = DS1302_DAT_PIN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(DS1302_DAT_PORT, &GPIO_InitStruct);
}
else if (mode == OUTPUT_MODE)
{
GPIO_InitStruct.GPIO_Pin = DS1302_DAT_PIN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(DS1302_DAT_PORT, &GPIO_InitStruct);
}
}
/**
* 写入数据
*/
static void vWriteRam(u8 addr, u8 data)
{
vDatLineMode(OUTPUT_MODE);
GPIO_SetBits(DS1302_RST_PORT, DS1302_RST_PIN); // 开始传输数据
for (u8 i = 0; i < 8; i++) // 传输控制字节
{
GPIO_ResetBits(DS1302_SCLK_PORT, DS1302_SCLK_PIN); // 低电平准备数据
if (addr & 0x01) // 低位先发
GPIO_SetBits(DS1302_DAT_PORT, DS1302_DAT_PIN);
else
GPIO_ResetBits(DS1302_DAT_PORT, DS1302_DAT_PIN);
GPIO_SetBits(DS1302_SCLK_PORT, DS1302_SCLK_PIN); // 上升沿发送数据
addr >>= 1;
}
for (u8 i = 0; i < 8; i++) // 传输数据字节
{
GPIO_ResetBits(DS1302_SCLK_PORT, DS1302_SCLK_PIN);
if (data & 0x01)
GPIO_SetBits(DS1302_DAT_PORT, DS1302_DAT_PIN);
else
GPIO_ResetBits(DS1302_DAT_PORT, DS1302_DAT_PIN);
GPIO_SetBits(DS1302_SCLK_PORT, DS1302_SCLK_PIN);
data >>= 1;
}
// 传输完毕,恢复取消传输数据状态
GPIO_ResetBits(DS1302_RST_PORT, DS1302_RST_PIN);
GPIO_ResetBits(DS1302_SCLK_PORT, DS1302_SCLK_PIN);
}
/**
* 读取数据
*/
static u8 vReadRam(u8 addr)
{
vDatLineMode(OUTPUT_MODE);
GPIO_SetBits(DS1302_RST_PORT, DS1302_RST_PIN);
u8 retData = 0x00;
for (u8 i = 0; i < 8; i++) // 传输控制字节
{
GPIO_ResetBits(DS1302_SCLK_PORT, DS1302_SCLK_PIN); // 低电平准备数据
if (addr & 0x01) // 低位先发
GPIO_SetBits(DS1302_DAT_PORT, DS1302_DAT_PIN);
else
GPIO_ResetBits(DS1302_DAT_PORT, DS1302_DAT_PIN);
GPIO_SetBits(DS1302_SCLK_PORT, DS1302_SCLK_PIN);
addr >>= 1;
}
vDatLineMode(INPUT_MODE);
for (u8 i = 0; i < 8; i++)
{
retData >>= 1;
GPIO_ResetBits(DS1302_SCLK_PORT, DS1302_SCLK_PIN);
if (GPIO_ReadInputDataBit(DS1302_DAT_PORT, DS1302_DAT_PIN))
retData |= 0x80;
GPIO_SetBits(DS1302_SCLK_PORT, DS1302_SCLK_PIN);
}
GPIO_ResetBits(DS1302_RST_PORT, DS1302_RST_PIN);
GPIO_ResetBits(DS1302_SCLK_PORT, DS1302_SCLK_PIN);
return (retData & 0x7F); // 最高位是一些标志位可清零不获取
}
void vDS1302_SetTime(ds_time *sys_time)
{
vWriteRam(0x8E, ENABLE_WRITE); // 关闭写保护
sys_time->year = sys_time->year - 2000;
vWriteRam(WRITE_YEAR, ((sys_time->year / 10 * 16) + (sys_time->year % 10)));
vWriteRam(WRITE_MON, ((sys_time->mon / 10 * 16) + (sys_time->mon % 10)));
vWriteRam(WRITE_DAY, ((sys_time->day / 10 * 16) + (sys_time->day % 10)));
vWriteRam(WRITE_HOUR, ((sys_time->hour / 10 * 16) + (sys_time->hour % 10)));
vWriteRam(WRITE_MIN, ((sys_time->min / 10 * 16) + (sys_time->min % 10)));
vWriteRam(WRITE_SEC, ((sys_time->sec / 10 * 16) + (sys_time->sec % 10)));
vWriteRam(WRITE_WEEK, ((sys_time->week / 10 * 16) + (sys_time->week % 10)));
vWriteRam(0x8E, DISABLE_WRITE); // 开启写保护
}
void vDS1302_GetTime(ds_time *sys_time)
{
u8 tempDat = 0x00;
tempDat = vReadRam(READ_YEAR);
sys_time->year = (tempDat / 16 * 10) + (tempDat % 16) + 2000;
tempDat = vReadRam(READ_MON);
sys_time->mon = (tempDat / 16 * 10) + (tempDat % 16);
tempDat = vReadRam(READ_DAY);
sys_time->day = (tempDat / 16 * 10) + (tempDat % 16);
tempDat = vReadRam(READ_HOUR);
sys_time->hour = (tempDat / 16 * 10) + (tempDat % 16);
tempDat = vReadRam(READ_MIN);
sys_time->min = (tempDat / 16 * 10) + (tempDat % 16);
tempDat = vReadRam(READ_SEC);
sys_time->sec = (tempDat / 16 * 10) + (tempDat % 16);
tempDat = vReadRam(READ_WEEK);
sys_time->week = (tempDat / 16 * 10) + (tempDat % 16);
}
作者:KINO32