其实在stm32的系统里面她已经给我们提供了准确的时钟,但是项目的需求是在多种情况下还要进行计时,所以使用一个外设时钟模块来进行计时,ds1302自己带了一个纽扣电池所以掉电以后不会将时间置零,先来代码:(hal库)
#include "DS1302.h"
// Регистры DS1302
#define DS1302_SEC 0x80
#define DS1302_MIN 0x82
#define DS1302_HOUR 0x84
#define DS1302_DATE 0x86
#define DS1302_MONTH 0x88
#define DS1302_DAY 0x8A
#define DS1302_YEAR 0x8C
#define DS1302_CONTROL 0x8E
#define DS1302_CHARGER 0x90
#define DS1302_CLKBURST 0xBE
#define DS1302_RAMBURST 0xFE
#define RAMSIZE 0x31 // Размер RAM в байтах
#define DS1302_RAMSTART 0xC0 // Первый адрес RAM
#define HEX2BCD(v) ((v) % 10 + (v) / 10 * 16)
#define BCD2HEX(v) ((v) % 16 + (v) / 16 * 10)
// SDA Write(output) Mode
static void writeSDA(void) {
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.Pin = DS1302_SDA;
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(DS1302_GPIO, &GPIO_InitStructure);
}
// SDA Read(input) Mode
static void readSDA(void) {
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.Pin = DS1302_SDA;
GPIO_InitStructure.Mode = GPIO_MODE_INPUT;
GPIO_InitStructure.Pull = GPIO_PULLDOWN;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(DS1302_GPIO, &GPIO_InitStructure);
}
/* Отправка адреса или команды */
static void DS1302_SendCmd(uint8_t cmd) {
uint8_t i;
for (i = 0; i < 8; i ++)
{
// DS1302_SDA = (bit)(addr & 1);
HAL_GPIO_WritePin(DS1302_GPIO, DS1302_SDA, (cmd & 1) ? GPIO_PIN_SET : GPIO_PIN_RESET);
// DS1302_SCK = 1;
HAL_GPIO_WritePin(DS1302_GPIO, DS1302_SCLK, GPIO_PIN_SET);
delayUS_DWT(1);
// DS1302_SCK = 0;
HAL_GPIO_WritePin(DS1302_GPIO, DS1302_SCLK, GPIO_PIN_RESET);
delayUS_DWT(1);
cmd >>= 1;
}
}
static void DS1302_WriteByte(uint8_t addr, uint8_t d)
{
uint8_t i;
// DS1302_RST = 1;
HAL_GPIO_WritePin(DS1302_GPIO, DS1302_RST, GPIO_PIN_SET);
//addr = addr & 0xFE;
DS1302_SendCmd(addr);
for (i = 0; i < 8; i ++)
{
// DS1302_SDA = (bit)(d & 1);
HAL_GPIO_WritePin(DS1302_GPIO, DS1302_SDA, (d & 1) ? GPIO_PIN_SET : GPIO_PIN_RESET);
// DS1302_SCK = 1;
HAL_GPIO_WritePin(DS1302_GPIO, DS1302_SCLK, GPIO_PIN_SET);
delayUS_DWT(1);
// DS1302_SCK = 0;
HAL_GPIO_WritePin(DS1302_GPIO, DS1302_SCLK, GPIO_PIN_RESET);
delayUS_DWT(1);
d >>= 1;
}
// DS1302_RST = 0;
HAL_GPIO_WritePin(DS1302_GPIO, DS1302_RST, GPIO_PIN_RESET);
// DS1302_SDA = 0;
HAL_GPIO_WritePin(DS1302_GPIO, DS1302_SDA, GPIO_PIN_RESET);
}
/* Sends 'cmd' command and writes in burst mode 'len' bytes from 'temp' */
static void DS1302_WriteBurst(uint8_t cmd, uint8_t len, uint8_t * temp)
{
uint8_t i, j;
DS1302_WriteByte(DS1302_CONTROL, 0x00);
// DS1302_RST = 1;
HAL_GPIO_WritePin(DS1302_GPIO, DS1302_RST, GPIO_PIN_SET);
DS1302_SendCmd(cmd); // Sends burst write command
for(j = 0; j < len; j++) {
for (i = 0; i < 8; i ++)
{
// DS1302_SDA = (bit)(d & 1);
HAL_GPIO_WritePin(DS1302_GPIO, DS1302_SDA, (temp[j] & 1) ? GPIO_PIN_SET : GPIO_PIN_RESET);
// DS1302_SCK = 1;
HAL_GPIO_WritePin(DS1302_GPIO, DS1302_SCLK, GPIO_PIN_SET);
delayUS_DWT(1);
// DS1302_SCK = 0;
HAL_GPIO_WritePin(DS1302_GPIO, DS1302_SCLK, GPIO_PIN_RESET);
delayUS_DWT(1);
temp[j] >>= 1;
}
}
// DS1302_RST = 0;
HAL_GPIO_WritePin(DS1302_GPIO, DS1302_RST, GPIO_PIN_RESET);
// DS1302_SDA = 0;
HAL_GPIO_WritePin(DS1302_GPIO, DS1302_SDA, GPIO_PIN_RESET);
DS1302_WriteByte(DS1302_CONTROL, 0x80);
}
/* Reads a byte from addr */
static uint8_t DS1302_ReadByte(uint8_t addr)
{
uint8_t i;
uint8_t temp = 0;
// DS1302_RST = 1;
HAL_GPIO_WritePin(DS1302_GPIO, DS1302_RST, GPIO_PIN_SET);
addr = addr | 0x01; // Generate Read Address
DS1302_SendCmd(addr); // Sends address
readSDA();
for (i = 0; i < 8; i ++)
{
temp >>= 1;
// if(DS1302_SDA)
if(HAL_GPIO_ReadPin(DS1302_GPIO, DS1302_SDA))
temp |= 0x80;
// DS1302_SCK = 1;
HAL_GPIO_WritePin(DS1302_GPIO, DS1302_SCLK, GPIO_PIN_SET);
delayUS_DWT(1);
// DS1302_SCK = 0;
HAL_GPIO_WritePin(DS1302_GPIO, DS1302_SCLK, GPIO_PIN_RESET);
delayUS_DWT(1);
}
writeSDA();
// DS1302_RST = 0;
HAL_GPIO_WritePin(DS1302_GPIO, DS1302_RST, GPIO_PIN_RESET);
// DS1302_SDA = 0;
HAL_GPIO_WritePin(DS1302_GPIO, DS1302_SDA, GPIO_PIN_RESET);
return temp;
}
/* Sends 'cmd' command and reads in burst mode 'len' bytes into 'temp' */
static void DS1302_ReadBurst(uint8_t cmd, uint8_t len, uint8_t * temp)
{
uint8_t i, j;
// DS1302_RST = 1;
HAL_GPIO_WritePin(DS1302_GPIO, DS1302_RST, GPIO_PIN_SET);
cmd = cmd | 0x01; // Generate read command
DS1302_SendCmd(cmd); // Sends burst read command
readSDA();
for (j = 0; j < len; j ++) {
temp[j] = 0;
for (i = 0; i < 8; i ++)
{
temp[j] >>= 1;
// if(DS1302_SDA)
if(HAL_GPIO_ReadPin(DS1302_GPIO, DS1302_SDA))
temp[j] |= 0x80;
// DS1302_SCK = 1;
HAL_GPIO_WritePin(DS1302_GPIO, DS1302_SCLK, GPIO_PIN_SET);
delayUS_DWT(1);
// DS1302_SCK = 0;
HAL_GPIO_WritePin(DS1302_GPIO, DS1302_SCLK, GPIO_PIN_RESET);
delayUS_DWT(1);
}
}
writeSDA();
// DS1302_RST = 0;
HAL_GPIO_WritePin(DS1302_GPIO, DS1302_RST, GPIO_PIN_RESET);
HAL_GPIO_WritePin(DS1302_GPIO, DS1302_SDA, GPIO_PIN_RESET);
}
/* Writes time byte by byte from 'buf' */
void DS1302_WriteTime(uint8_t *buf)
{
DS1302_WriteByte(DS1302_CONTROL, 0x00); // 解除保护
delayUS_DWT(1);
DS1302_WriteByte(DS1302_SEC, 0x80);
DS1302_WriteByte(DS1302_YEAR, HEX2BCD(buf[1]));
DS1302_WriteByte(DS1302_MONTH, HEX2BCD(buf[2]));
DS1302_WriteByte(DS1302_DATE, HEX2BCD(buf[3]));
DS1302_WriteByte(DS1302_HOUR, HEX2BCD(buf[4]));
DS1302_WriteByte(DS1302_MIN, HEX2BCD(buf[5]));
DS1302_WriteByte(DS1302_SEC, HEX2BCD(buf[6]));
DS1302_WriteByte(DS1302_DAY, HEX2BCD(buf[7]));
DS1302_WriteByte(DS1302_CONTROL, 0x80); // 写保护
delayUS_DWT(1);
}
//添加 掉电计时
void DS1302_init_time(uint8_t *buf)
{
// DS1302_RST = 0;
HAL_GPIO_WritePin(DS1302_GPIO, DS1302_RST, GPIO_PIN_RESET);
// DS1302_SCK = 0;
HAL_GPIO_WritePin(DS1302_GPIO, DS1302_SCLK, GPIO_PIN_RESET);
DS1302_WriteByte(DS1302_CONTROL, 0x00);//允许写入数据
if(DS1302_ReadByte(0x81)&0x80)
DS1302_WriteTime(buf);
}
/* Reads time byte by byte to 'buf' */
void DS1302_ReadTime(DS1302_Time_t* time)
{
uint8_t tmp;
tmp = DS1302_ReadByte(DS1302_YEAR);
time->year= BCD2HEX(tmp);
tmp = DS1302_ReadByte(DS1302_MONTH);
time->month = BCD2HEX(tmp);
tmp = DS1302_ReadByte(DS1302_DATE);
time->date = BCD2HEX(tmp);
tmp = DS1302_ReadByte(DS1302_HOUR);
time->hour = BCD2HEX(tmp);
tmp = DS1302_ReadByte(DS1302_MIN);
time->minute = BCD2HEX(tmp);
tmp = DS1302_ReadByte((DS1302_SEC)) & 0x7F;
time->second = BCD2HEX(tmp);
tmp = DS1302_ReadByte(DS1302_DAY);
time->week = BCD2HEX(tmp);
}
/* DS1302初始化 */
void DS1302_Init(void)
{
DWT_Delay_Init(); //初始化计时器以进行毫秒级定时。
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.Pin = DS1302_SCLK | DS1302_SDA | DS1302_RST;
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(DS1302_GPIO, &GPIO_InitStructure);
DS1302_WriteByte(DS1302_CHARGER, 0x00);
// DS1302_RST = 0;
HAL_GPIO_WritePin(DS1302_GPIO, DS1302_RST, GPIO_PIN_RESET);
// DS1302_SCK = 0;
HAL_GPIO_WritePin(DS1302_GPIO, DS1302_SCLK, GPIO_PIN_RESET);
delayUS_DWT(10); // 不小于10微秒的延时。.
DS1302_ClockStart();
}
/* Writes 'val' to ram address 'addr' */
/* Ram addresses range from 0 to 30 */
void DS1302_WriteRam(uint8_t addr, uint8_t val) {
DS1302_WriteByte(DS1302_CONTROL, 0x00); // 禁用写入保护
delayUS_DWT(1);
if (addr >= RAMSIZE) {
return;
}
DS1302_WriteByte(DS1302_RAMSTART + (2 * addr), val);
DS1302_WriteByte(DS1302_CONTROL, 0x80); // 启用写入保护。
delayUS_DWT(1);
}
/* Reads ram address 'addr' */
uint8_t DS1302_ReadRam(uint8_t addr) {
if (addr >= RAMSIZE) {
return 0;
}
return DS1302_ReadByte(DS1302_RAMSTART + (2 * addr));
}
/* Clears the entire ram writing 0 */
void DS1302_ClearRam(void) {
uint8_t i;
for(i=0; i< RAMSIZE; i++){
DS1302_WriteRam(i, 0x00);
}
}
/* Reads time in burst mode, includes control byte */
void DS1302_ReadTimeBurst(uint8_t * buf) {
uint8_t temp[8] = {0, 0, 0, 0, 0, 0, 0, 0};
DS1302_ReadBurst(DS1302_CLKBURST, 8, temp);
buf[1] = BCD2HEX(temp[6]); // Year
buf[2] = BCD2HEX(temp[4]); // Month
buf[3] = BCD2HEX(temp[3]); // Date
buf[4] = BCD2HEX(temp[2]); // Hour
buf[5] = BCD2HEX(temp[1]); // Min
buf[6] = BCD2HEX(temp[0]); // Sec
buf[7] = BCD2HEX(temp[5]); // Day
buf[0] = temp[7]; // Control
}
/* Writes time in burst mode, includes control byte */
void DS1302_WriteTimeBurst(uint8_t * buf) {
uint8_t temp[8];
temp[0]=HEX2BCD(buf[6]); // Sec
temp[1]=HEX2BCD(buf[5]); // Min
temp[2]=HEX2BCD(buf[4]); // Hour
temp[3]=HEX2BCD(buf[3]); // Date
temp[4]=HEX2BCD(buf[2]); // Month
temp[5]=HEX2BCD(buf[7]); // Day
temp[6]=HEX2BCD(buf[1]); // Year
temp[7]=buf[0]; // Control
DS1302_WriteBurst(DS1302_CLKBURST, 8, temp);
}
/* Reads ram in burst mode 'len' bytes into 'buf' */
void DS1302_ReadRamBurst(uint8_t len, uint8_t * buf) {
uint8_t i;
if(len <= 0) {
return;
}
if (len > RAMSIZE) {
len = RAMSIZE;
}
for(i = 0; i < len; i++) {
buf[i] = 0;
}
DS1302_ReadBurst(DS1302_RAMBURST, len, buf);
}
/* Writes ram in burst mode 'len' bytes from 'buf' */
void DS1302_WriteRamBurst(uint8_t len, uint8_t * buf) {
if(len <= 0) {
return;
}
if (len > RAMSIZE) {
len = RAMSIZE;
}
DS1302_WriteBurst(DS1302_RAMBURST, len, buf);
}
//启动时钟。
//DS1302 最初处于 Halt 模式(已停止,省电模式)。
//为了开始计时,需要执行此函数一次。
void DS1302_ClockStart(void)
{
uint8_t buf = 0x00;
DS1302_WriteByte(DS1302_CONTROL, 0x00); // 禁用写入保护。
delayUS_DWT(1);
buf = DS1302_ReadByte(DS1302_SEC) & 0x7F; // 写入 8 位中的零的同时,保留当前秒数的值
DS1302_WriteByte(DS1302_SEC, buf);
DS1302_WriteByte(DS1302_CONTROL, 0x80); // 启用写入保护。
delayUS_DWT(1);
}
//停止时钟。
//为了进入 Halt 模式(省电模式),此功能可能并不实用
void DS1302_ClockStop(void)
{
uint8_t buf = 0x00;
DS1302_WriteByte(DS1302_CONTROL, 0x00); // 禁用写入保护
delayUS_DWT(1);
buf = DS1302_ReadByte(DS1302_SEC) | 0x80; // 写入 8 位中的1的同时,保留当前秒数的值
DS1302_WriteByte(DS1302_SEC, buf);
DS1302_WriteByte(DS1302_CONTROL, 0x80); // 启用写入保护
delayUS_DWT(1);
}
//重置时钟
//将0写入所有时钟寄存器(从0x80到0x8C)并将 DS1302 传输到 Halt 模式(省电模式)。
//要启动时钟,请使用 DS1302_ClockStart() 函数。
void DS1302_ClockClear(void)
{
DS1302_WriteByte(DS1302_CONTROL, 0x00); // 禁用写入保护
delayUS_DWT(1);
DS1302_WriteByte(DS1302_SEC, 0x80); //重置秒并进入 Halt 模式。
DS1302_WriteByte(DS1302_MIN, 0x00);
DS1302_WriteByte(DS1302_HOUR, 0x00);
DS1302_WriteByte(DS1302_DATE, 0x00);
DS1302_WriteByte(DS1302_MONTH, 0x00);
DS1302_WriteByte(DS1302_DAY, 0x00);
DS1302_WriteByte(DS1302_YEAR, 0x00);
DS1302_WriteByte(DS1302_CONTROL, 0x80); // 启用写入保护。
delayUS_DWT(1);
}
其实各个外设的驱动在博客里面有很多,但是难的在于看懂这个驱动和将驱动代码微调后匹配到自己的项目里面,上面的代码就是ds1302的驱动,还有.h文件:
#ifndef _DS1302_H
#define _DS1302_H
#include "stm32f1xx_hal.h"
#include "dwt_delay.h"
//----------------------------------------------------------------------------------
//将下面列出的端口和引脚根据自己的需求进行重新分配
//----------------------------------------------------------------------------------
#define DS1302_CLK_Pin GPIO_PIN_0
#define DS1302_IO_Pin GPIO_PIN_1
#define DS1302_RST_Pin GPIO_PIN_10
#define DS1302_GPIO GPIOB
#define DS1302_SCLK DS1302_CLK_Pin
#define DS1302_SDA DS1302_IO_Pin
#define DS1302_RST DS1302_RST_Pin
//存放时间
typedef struct _time
{
uint8_t flag[3];
uint8_t second;
uint8_t minute;
uint8_t hour;
uint8_t date;
uint8_t month;
uint8_t week;
uint8_t year;
} DS1302_Time_t;
/* 初始化 */
/* 配置 STM32 端口,启动微秒定时器,并将时钟从省电模式唤醒 */
void DS1302_Init(void);
//掉电
void DS1302_init_time(uint8_t *buf);
/* Reads time byte by byte to 'buf' */
void DS1302_ReadTime(DS1302_Time_t* time);
/* Writes time byte by byte from 'buf' */
void DS1302_WriteTime(uint8_t *buf);
/* Writes 'val' to ram address 'addr' */
/* Ram addresses range from 0 to 30 */
void DS1302_WriteRam(uint8_t addr, uint8_t val);
/* Чтение из RAM по адресу 'addr' */
uint8_t DS1302_ReadRam(uint8_t addr);
/* Очистка RAM памяти */
void DS1302_ClearRam(void);
/* Reads time in burst mode, includes control byte */
void DS1302_ReadTimeBurst(uint8_t * temp);
/* Writes time in burst mode, includes control byte */
void DS1302_WriteTimeBurst(uint8_t * buf);
/* Reads ram in burst mode 'len' bytes into 'buf' */
void DS1302_ReadRamBurst(uint8_t len, uint8_t * buf);
/* Writes ram in burst mode 'len' bytes from 'buf' */
void DS1302_WriteRamBurst(uint8_t len, uint8_t * buf);
//Запуск часов.
void DS1302_ClockStart(void);
//Остановка часов.
void DS1302_ClockStop(void);
//Сброс часов
void DS1302_ClockClear(void);
#endif //_DS1302_H
这个代码也是我在网上找的,但具体的作者找不到了(若有冒犯之处,请及时联系我),.h文件里面的注释有一些乱码,但是不影响我们的正常使用,下面是具体的使用,在具体的c文件里面:
OLED_Init();
DS1302_Init();
DS1302_Time_t time = {0};
uint8_t Buf[8]={1,24,5,17,17,31,50,5};
DWT_Delay_Init();
DS1302_Init();
DS1302_init_time(Buf);
//DS1302_WriteTime(Buf);
uint32_t TimerUART = HAL_GetTick();
其实具体到使用就很简单的几行代码就欧克了,来看上面的一小段代码,
OLED_Init:oled初始化
DS1302_Time_t time = {0};
uint8_t Buf[8]={1,24,5,17,17,31,50,5};存放时间信息的数组,前面的1有意义,后面存放的是24年5月17日17点31min50s,星期五,
DWT_Delay_Init();
DS1302_Init();
DS1302_init_time(Buf);这个函数里面有掉电计时,可以看上面的驱动,本来驱动里面是没有的,后来自己添加的
//DS1302_WriteTime(Buf);这个计时驱动的,没有掉电计时功能。
uint32_t TimerUART = HAL_GetTick();
然后就没有什么了,然后就可以获取到ds1302的时间信息,在用我前面写过的oled结合来就可以显示时间信息以及星期信息了,但是布局需要自己写,Ow2。。。。。