#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)) // 微秒延时 /* 函数作用:为 DS1302 时序提供简短的微秒级软件延时。 */ static void delay_us(uint32_t us) { volatile uint32_t i; while (us--) { for (i = 0; i < 12; i++); // 72MHz下约1us } } /** * @brief 配置IO口为输出 */ /* 函数作用:把 DS1302 数据引脚配置为推挽输出模式。 */ 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口为输入 */ /* 函数作用:把 DS1302 数据引脚配置为浮空输入模式。 */ 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初始化 */ /* 函数作用:初始化 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 要发送的数据 */ /* 函数作用:按 DS1302 时序向芯片写入一个字节。 */ 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 数据 */ /* 函数作用:向 DS1302 指定寄存器写入一个字节数据。 */ 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 读取到的数据 */ /* 函数作用:从 DS1302 指定寄存器读取一个字节数据。 */ 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 时间结构体指针 */ /* 函数作用:把时间结构体转换为 BCD 后写入 DS1302。 */ 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 时间结构体指针 */ /* 函数作用:读取 DS1302 时间寄存器并转换为十进制时间结构体。 */ 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; }