使用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进制/10
16+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

物联沃分享整理
物联沃-IOTWORD物联网 » 使用STM32控制DS1302时钟模块实时获取时间

发表回复