前言
在本篇文章中,我们将介绍如何在STM32F103C8T6上集成DS1302实时时钟芯片。我们将讨论DS1302的硬件连接和软件驱动的实现,以及如何获取并显示当前的时间和日期信息。通过学习本篇文章,读者将掌握如何在STM32F103C8T6嵌入式系统中使用DS1302实时时钟芯片,为自己的嵌入式应用添加时间和日期功能。
一、DS1302是什么?
DS1302是一款实时时钟芯片,可用于各种电子设备中的时间和日期记录。它具有低功耗和高精度等特点,并提供了内部RAM和定时器功能。DS1302通过SPI(串行外设接口)或I2C(双线串行总线)与主控制器相连,可以通过主控制器进行配置和读取时间数据。这款芯片通常用于电子钟表、计时器、温度计等设备中。
引脚连接表
二、使用步骤
1.DS1302.c
代码如下(示例):
#include "ds1302.h"
#include "OLED.h"
struct TIMEData TimeData;
uint8_t read_time[7];
void ds1302_gpio_init(void) // CE,SCLK端口初始化
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(CE_GPIO_CLK, ENABLE);
GPIO_InitStructure.GPIO_Pin = CE_GPIO_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽输出
GPIO_Init(CE_GPIO_PORT, &GPIO_InitStructure); // 初始化GPIOB.12
GPIO_ResetBits(CE_GPIO_PORT, CE_GPIO_PIN);
GPIO_InitStructure.GPIO_Pin = SCLK_GPIO_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽输出
GPIO_Init(SCLK_GPIO_PORT, &GPIO_InitStructure); // 初始化GPIOC.12
GPIO_ResetBits(SCLK_GPIO_PORT, SCLK_GPIO_PIN);
}
void ds1302_DATAOUT_init(void) // 配置双向I/O端口为输出态
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(DATA_GPIO_CLK, ENABLE);
GPIO_InitStructure.GPIO_Pin = DATA_GPIO_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(DATA_GPIO_PORT, &GPIO_InitStructure); // 初始化GPIOB.14
GPIO_ResetBits(DATA_GPIO_PORT, DATA_GPIO_PIN);
}
void ds1302_DATAINPUT_init() // 配置双向I/O端口为输入态
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(DATA_GPIO_CLK, ENABLE);
GPIO_InitStructure.GPIO_Pin = DATA_GPIO_PIN; // PB.14 DATA
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(DATA_GPIO_PORT, &GPIO_InitStructure); // 初始化GPIOB.14
}
void ds1302_write_onebyte(u8 data) // 向DS1302发送一字节数据
{
ds1302_DATAOUT_init();
u8 count = 0;
SCLK_L;
for (count = 0; count < 8; count++)
{
SCLK_L;
if (data & 0x01)
{
DATA_H;
}
else
{
DATA_L;
} // 先准备好数据再发送
SCLK_H; // 拉高时钟线,发送数据
data >>= 1;
}
}
void ds1302_wirte_rig(u8 address, u8 data) // 向指定寄存器地址发送数据
{
u8 temp1 = address;
u8 temp2 = data;
CE_L;
SCLK_L;
Delay_us(1);
CE_H;
Delay_us(2);
ds1302_write_onebyte(temp1);
ds1302_write_onebyte(temp2);
CE_L;
SCLK_L;
Delay_us(2);
}
void ds1302_wirte(u8 address) // 向指定寄存器地址发送数据
{
u8 temp1 = address;
CE_L;
SCLK_L;
Delay_us(1);
CE_H;
Delay_us(2);
ds1302_write_onebyte(temp1);
CE_L;
SCLK_L;
Delay_us(2);
}
u8 ds1302_read_rig(u8 address) // 从指定地址读取一字节数据
{
u8 temp3 = address;
u8 count = 0;
u8 return_data = 0x00;
CE_L;
SCLK_L;
Delay_us(3);
CE_H;
Delay_us(3);
ds1302_write_onebyte(temp3);
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 (GPIO_ReadInputDataBit(DATA_GPIO_PORT, DATA_GPIO_PIN))
{
return_data = return_data | 0x80;
}
}
Delay_us(2);
CE_L;
DATA_L;
return return_data;
}
void ds1302_init(void)
{
ds1302_wirte_rig(0x00, 0x8e); // 关闭写保护
ds1302_wirte_rig(0x80, 0x00); // seconds00秒
ds1302_wirte_rig(0x82, 0x31); // minutes31分
ds1302_wirte_rig(0x84, 0x09); // hours9时
ds1302_wirte_rig(0x86, 0x18); // date18日
ds1302_wirte_rig(0x88, 0x11); // months11月
ds1302_wirte_rig(0x8a, 0x06); // days星期6
ds1302_wirte_rig(0x8c, 0x23); // year2023年
ds1302_wirte_rig(0x00, 0x80); // 开启写保护
}
void time_set(uint8_t* time)
{
ds1302_wirte_rig(0x00, 0x8e); // 关闭写保护
ds1302_wirte_rig(0x80, time[5]); // seconds00秒
ds1302_wirte_rig(0x82, time[4]); // minutes31分
ds1302_wirte_rig(0x84, time[3]); // hours9时
ds1302_wirte_rig(0x86, time[2]); // date18日
ds1302_wirte_rig(0x88, time[1]); // months11月
ds1302_wirte_rig(0x8c, time[0]); // year2023年
ds1302_wirte_rig(0x00, 0x80); // 关闭写保护
}
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;
}
2.DS1302.h
代码如下(示例):
#ifndef __DS1302_H
#define __DS1302_H
#include "stm32f10x.h" // 包含STM32F10x系列微控制器的设备头文件
#include "Delay.h" // 包含延时函数库
// 定义SCLK(串行时钟)引脚相关的宏
#define SCLK_GPIO_PORT GPIOB // SCLK引脚所在的GPIO端口
#define SCLK_GPIO_PIN GPIO_Pin_12 // SCLK引脚编号
#define SCLK_GPIO_CLK RCC_APB2Periph_GPIOB // SCLK引脚所在端口的时钟
// 定义DATA(数据)引脚相关的宏
#define DATA_GPIO_PORT GPIOB // DATA引脚所在的GPIO端口
#define DATA_GPIO_PIN GPIO_Pin_13 // DATA引脚编号
#define DATA_GPIO_CLK RCC_APB2Periph_GPIOB // DATA引脚所在端口的时钟
// 定义CE(芯片使能)引脚相关的宏
#define CE_GPIO_PORT GPIOB // CE引脚所在的GPIO端口
#define CE_GPIO_PIN GPIO_Pin_14 // CE引脚编号
#define CE_GPIO_CLK RCC_APB2Periph_GPIOB // CE引脚所在端口的时钟
// 定义操作CE, SCLK, 和 DATA引脚高低电平的宏
#define CE_L GPIO_ResetBits(CE_GPIO_PORT, CE_GPIO_PIN) // 拉低CE引脚
#define CE_H GPIO_SetBits(CE_GPIO_PORT, CE_GPIO_PIN) // 拉高CE引脚
#define SCLK_L GPIO_ResetBits(SCLK_GPIO_PORT, SCLK_GPIO_PIN) // 拉低SCLK引脚
#define SCLK_H GPIO_SetBits(SCLK_GPIO_PORT, SCLK_GPIO_PIN) // 拉高SCLK引脚
#define DATA_L GPIO_ResetBits(DATA_GPIO_PORT, DATA_GPIO_PIN) // 拉低DATA引脚
#define DATA_H GPIO_SetBits(DATA_GPIO_PORT, DATA_GPIO_PIN) // 拉高DATA引脚
// 创建一个用于存储时间日期数据的结构体
struct TIMEData {
uint16_t year; // 年份
uint8_t month; // 月份
uint8_t day; // 日期
uint8_t hour; // 小时
uint8_t minute; // 分钟
uint8_t second; // 秒
uint8_t week; // 星期
};
extern struct TIMEData TimeData; // 声明一个全局变量TimeData,用于存储当前的时间和日期信息
// 函数声明,以下函数用于初始化和操作DS1302
void ds1302_gpio_init(void); // 初始化DS1302的GPIO引脚
void ds1302_write_onebyte(u8 data); // 向DS1302发送一字节的数据
void ds1302_wirte_rig(u8 address, u8 data); // 向DS1302的指定寄存器写入一字节的数据
u8 ds1302_read_rig(u8 address); // 从DS1302的指定寄存器读取一字节的数据
void ds1302_init(void); // DS1302的初始化函数
void ds1302_DATAOUT_init(void); // 配置DATA引脚为输出模式
void ds1302_DATAINPUT_init(void); // 配置DATA引脚为输入模式
void ds1302_read_time(void); // 从DS1302读取实时时间(BCD码)
void ds1302_read_realTime(void); // 将从DS1302读取的BCD码转换为十进制数据
void time_set(uint8_t* time); // 设置DS1302的时间
#endif // __DS1302_H
3.main.c
代码如下(示例):
#include "stm32f10x.h" //STM32头文件
#include "DS1302.h" //时钟的头文件
#include "oled.h" //OLED屏头文件
uint8_t Time[6] = {0x23, 0x05, 0x10, 0x12, 0x00, 0x00}; // 年, 月, 日, 时, 分, 秒
int main(void)
{
ds1302_gpio_init(); //时钟模块初始化
OLED_Init(); //OLED屏初始化
OLED_Clear(); //连接完成后清屏
// time_set(Time); //时间设定注释是为了锁定时间
while(1)
{
OLED_Clear(); //连接完成后清屏
OLED_ShowNum(23, 16, TimeData.year, 4, OLED_8X16); // 年份
OLED_ShowChar(55, 16, '-', OLED_8X16); // 分隔符
OLED_ShowNum(63, 16, TimeData.month, 2, OLED_8X16); // 月份
OLED_ShowChar(79, 16, '-', OLED_8X16); // 分隔符
OLED_ShowNum(87, 16, TimeData.day, 2, OLED_8X16); // 日期
OLED_ShowNum(31, 32, TimeData.hour, 2, OLED_8X16); // 小时
OLED_ShowChar(47, 32, ':', OLED_8X16); // 冒号
OLED_ShowNum(55, 32, TimeData.minute, 2, OLED_8X16); // 分钟
OLED_ShowChar(71, 32, ':', OLED_8X16); // 冒号
OLED_ShowNum(79, 32, TimeData.second, 2, OLED_8X16); // 秒
OLED_Update(); //数据更新
}
}
总结
STM32F103C8T6是一款常用的嵌入式微控制器,而DS1302是一款实时时钟集成电路。在将DS1302与STM32F103C8T6集成在一起时,可以通过以下步骤进行操作:
1.连接电路:将DS1302的引脚与STM32F103C8T6的引脚连接起来。常见的连接方式包括连接DS1302的CLK引脚到STM32F103C8T6的串行时钟引脚(SCK),将DS1302的RST引脚连接到STM32F103C8T6的复位引脚(RST),将DS1302的DAT引脚连接到STM32F103C8T6的串行数据引脚(SDA)。
2.编写代码:利用STM32F103C8T6的开发环境,编写代码实现与DS1302的通信。可以使用SPI或软件模拟SPI进行通信。首先,需要初始化SPI或模拟SPI,然后通过SPI或模拟SPI发送指令和数据给DS1302。指令包括读写时钟数据、读写控制寄存器等。可以通过查询DS1302的数据手册获取相应的指令。
3.读取时间:通过发送读取时钟数据的指令,可以从DS1302读取当前的时间。时间数据可以以二进制或BCD格式返回。可以根据需求进行相应的转换和处理,例如将BCD格式转换为十进制格式。
4.设置时间:通过发送写入时钟数据的指令,可以向DS1302设置新的时间。可以将新的时间数据以二进制或BCD格式发送给DS1302。
5.其他功能:DS1302还提供其他一些功能,例如定时器和RAM。通过相应的指令可以设置和读取这些功能。
总结来说,将DS1302与STM32F103C8T6集成在一起可以实现实时时钟的功能。通过合适的连接电路和编写代码,可以读取和设置DS1302的时间数据,以及使用其他附加功能。
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/m0_63037616/article/details/144810625
|
|