247 lines
6.2 KiB
C
247 lines
6.2 KiB
C
#include "DS1302.h"
|
|
#include "Delay.h"
|
|
|
|
// 根据连接图.md: DS1302引脚配置
|
|
// CLK: PA4, DAT: PA5, RST: PA6
|
|
|
|
#define DS1302_SCLK_PIN GPIO_Pin_4 // CLK
|
|
#define DS1302_IO_PIN GPIO_Pin_5 // DAT (双向)
|
|
#define DS1302_CE_PIN GPIO_Pin_6 // RST/CE
|
|
#define DS1302_GPIO GPIOA
|
|
|
|
// GPIO控制宏
|
|
#define SCLK_H GPIO_SetBits(DS1302_GPIO, DS1302_SCLK_PIN)
|
|
#define SCLK_L GPIO_ResetBits(DS1302_GPIO, DS1302_SCLK_PIN)
|
|
#define DATA_H GPIO_SetBits(DS1302_GPIO, DS1302_IO_PIN)
|
|
#define DATA_L GPIO_ResetBits(DS1302_GPIO, DS1302_IO_PIN)
|
|
#define CE_H GPIO_SetBits(DS1302_GPIO, DS1302_CE_PIN)
|
|
#define CE_L GPIO_ResetBits(DS1302_GPIO, DS1302_CE_PIN)
|
|
#define DATA_READ GPIO_ReadInputDataBit(DS1302_GPIO, DS1302_IO_PIN)
|
|
|
|
// DS1302寄存器地址(写地址)
|
|
#define DS1302_SECOND 0x80
|
|
#define DS1302_MINUTE 0x82
|
|
#define DS1302_HOUR 0x84
|
|
#define DS1302_DATE 0x86
|
|
#define DS1302_MONTH 0x88
|
|
#define DS1302_DAY 0x8A
|
|
#define DS1302_YEAR 0x8C
|
|
#define DS1302_WP 0x8E
|
|
|
|
// BCD码转换
|
|
#define BCD_TO_DEC(bcd) (((bcd) >> 4) * 10 + ((bcd) & 0x0F))
|
|
#define DEC_TO_BCD(dec) ((((dec) / 10) << 4) | ((dec) % 10))
|
|
|
|
// 微秒延时
|
|
static void delay_us(uint32_t us)
|
|
{
|
|
volatile uint32_t i;
|
|
while (us--) {
|
|
for (i = 0; i < 12; i++); // 72MHz下约1us
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief 配置IO口为输出
|
|
*/
|
|
static void DS1302_DATAOUT_init(void)
|
|
{
|
|
GPIO_InitTypeDef GPIO_InitStructure;
|
|
GPIO_InitStructure.GPIO_Pin = DS1302_IO_PIN;
|
|
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
|
|
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
|
|
GPIO_Init(DS1302_GPIO, &GPIO_InitStructure);
|
|
}
|
|
|
|
/**
|
|
* @brief 配置IO口为输入
|
|
*/
|
|
static void DS1302_DATAINPUT_init(void)
|
|
{
|
|
GPIO_InitTypeDef GPIO_InitStructure;
|
|
GPIO_InitStructure.GPIO_Pin = DS1302_IO_PIN;
|
|
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
|
|
GPIO_Init(DS1302_GPIO, &GPIO_InitStructure);
|
|
}
|
|
|
|
/**
|
|
* @brief DS1302初始化
|
|
*/
|
|
void DS1302_Init(void)
|
|
{
|
|
GPIO_InitTypeDef GPIO_InitStructure;
|
|
|
|
// 使能GPIOA时钟
|
|
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
|
|
|
|
// 配置SCLK和CE为推挽输出
|
|
GPIO_InitStructure.GPIO_Pin = DS1302_SCLK_PIN | DS1302_CE_PIN;
|
|
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
|
|
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
|
|
GPIO_Init(DS1302_GPIO, &GPIO_InitStructure);
|
|
|
|
// 配置IO为输出(初始状态)
|
|
DS1302_DATAOUT_init();
|
|
|
|
// 初始化引脚状态
|
|
SCLK_L;
|
|
CE_L;
|
|
}
|
|
|
|
/**
|
|
* @brief 向DS1302发送一字节数据(根据demo代码)
|
|
* @param data 要发送的数据
|
|
*/
|
|
static void DS1302_write_onebyte(uint8_t data)
|
|
{
|
|
uint8_t count = 0;
|
|
|
|
DS1302_DATAOUT_init(); // 配置为输出
|
|
SCLK_L;
|
|
|
|
for (count = 0; count < 8; count++)
|
|
{
|
|
SCLK_L;
|
|
|
|
// 先准备好数据再发送
|
|
if (data & 0x01)
|
|
DATA_H;
|
|
else
|
|
DATA_L;
|
|
|
|
SCLK_H; // 拉高时钟线,发送数据
|
|
data >>= 1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief 向指定寄存器地址发送数据(根据demo代码)
|
|
* @param address 寄存器地址
|
|
* @param data 数据
|
|
*/
|
|
void DS1302_WriteByte(uint8_t address, uint8_t data)
|
|
{
|
|
CE_L;
|
|
SCLK_L;
|
|
delay_us(1);
|
|
|
|
CE_H;
|
|
delay_us(2);
|
|
|
|
DS1302_write_onebyte(address);
|
|
DS1302_write_onebyte(data);
|
|
|
|
CE_L;
|
|
SCLK_L;
|
|
delay_us(2);
|
|
}
|
|
|
|
/**
|
|
* @brief 从指定地址读取一字节数据(根据demo代码)
|
|
* @param address 寄存器地址
|
|
* @retval 读取到的数据
|
|
*/
|
|
uint8_t DS1302_ReadByte(uint8_t address)
|
|
{
|
|
uint8_t count = 0;
|
|
uint8_t return_data = 0x00;
|
|
|
|
CE_L;
|
|
SCLK_L;
|
|
delay_us(3);
|
|
|
|
CE_H;
|
|
delay_us(3);
|
|
|
|
DS1302_write_onebyte(address);
|
|
|
|
DS1302_DATAINPUT_init(); // 配置I/O口为输入
|
|
delay_us(2);
|
|
|
|
for (count = 0; count < 8; count++)
|
|
{
|
|
delay_us(2); // 使电平持续一段时间
|
|
return_data >>= 1;
|
|
|
|
SCLK_H;
|
|
delay_us(4); // 使高电平持续一段时间
|
|
|
|
SCLK_L;
|
|
delay_us(14); // ⭐ 关键延时:延时14us后再去读取电压,更加准确
|
|
|
|
if (DATA_READ)
|
|
return_data = return_data | 0x80;
|
|
}
|
|
|
|
delay_us(2);
|
|
CE_L;
|
|
DATA_L;
|
|
|
|
return return_data;
|
|
}
|
|
|
|
/**
|
|
* @brief 设置DS1302时间
|
|
* @param time 时间结构体指针
|
|
*/
|
|
void DS1302_SetTime(DS1302_Time* time)
|
|
{
|
|
// 关闭写保护
|
|
DS1302_WriteByte(DS1302_WP, 0x00);
|
|
|
|
// 写入时间数据(BCD码格式)
|
|
DS1302_WriteByte(DS1302_SECOND, DEC_TO_BCD(time->second));
|
|
DS1302_WriteByte(DS1302_MINUTE, DEC_TO_BCD(time->minute));
|
|
DS1302_WriteByte(DS1302_HOUR, DEC_TO_BCD(time->hour));
|
|
DS1302_WriteByte(DS1302_DATE, DEC_TO_BCD(time->day));
|
|
DS1302_WriteByte(DS1302_MONTH, DEC_TO_BCD(time->month));
|
|
DS1302_WriteByte(DS1302_DAY, DEC_TO_BCD(time->week));
|
|
DS1302_WriteByte(DS1302_YEAR, DEC_TO_BCD(time->year % 100));
|
|
|
|
// 开启写保护
|
|
DS1302_WriteByte(DS1302_WP, 0x80);
|
|
}
|
|
|
|
/**
|
|
* @brief 读取DS1302时间
|
|
* @param time 时间结构体指针
|
|
*/
|
|
void DS1302_GetTime(DS1302_Time* time)
|
|
{
|
|
uint8_t read_time[7];
|
|
uint8_t hour_reg;
|
|
|
|
// 读取时间数据(BCD码)
|
|
read_time[0] = DS1302_ReadByte(0x81); // 读秒
|
|
read_time[1] = DS1302_ReadByte(0x83); // 读分
|
|
read_time[2] = DS1302_ReadByte(0x85); // 读时
|
|
read_time[3] = DS1302_ReadByte(0x87); // 读日
|
|
read_time[4] = DS1302_ReadByte(0x89); // 读月
|
|
read_time[5] = DS1302_ReadByte(0x8B); // 读星期
|
|
read_time[6] = DS1302_ReadByte(0x8D); // 读年
|
|
|
|
// BCD码转换为10进制(先屏蔽控制位,再转换)
|
|
time->second = BCD_TO_DEC(read_time[0] & 0x7F); // bit7=CH
|
|
time->minute = BCD_TO_DEC(read_time[1] & 0x7F);
|
|
|
|
hour_reg = read_time[2];
|
|
if (hour_reg & 0x80) {
|
|
// 12小时制:bit5=PM, bit4~0=小时(1~12)
|
|
uint8_t hour12 = BCD_TO_DEC(hour_reg & 0x1F);
|
|
uint8_t is_pm = (hour_reg & 0x20) ? 1 : 0;
|
|
if (hour12 == 12) {
|
|
time->hour = is_pm ? 12 : 0;
|
|
} else {
|
|
time->hour = is_pm ? (hour12 + 12) : hour12;
|
|
}
|
|
} else {
|
|
// 24小时制:bit5~0=小时(0~23)
|
|
time->hour = BCD_TO_DEC(hour_reg & 0x3F);
|
|
}
|
|
|
|
time->day = BCD_TO_DEC(read_time[3] & 0x3F);
|
|
time->month = BCD_TO_DEC(read_time[4] & 0x1F);
|
|
time->week = read_time[5] & 0x07;
|
|
time->year = BCD_TO_DEC(read_time[6]) + 2000;
|
|
}
|