STM32F103单片机内部RTC实时时钟驱动程序

STM32F103学习笔记(十二)——RTC实时时钟 实验十二 RTC实时时钟 这一章我们将介绍 STM32F1 的内部实时时钟(RTC)。在本章中,我们将利用 ALIENTEK 2.8 寸 TFTLCD 模块来显示日期和时间,实现一个简单的时钟。 改实验所需代码较多,这里仅记录初始化和中断服务函数代码,注意需要需要使用闹钟功能需要开启闹钟中断 初始化参考代码 //实时时钟配置 //初始化RTC时钟,同时检测时钟是否工作正常 //BKP->DR1用于保存是否第一次配置的设置 //返回0:正常 //其他:错误代码 u8 RTC_Init(void 阅读详情
AI摘要

本文围绕STM32F103系列RTC功能展开,该系列RTC只有一个每秒加1的计数器,需软件计算时间。介绍了STM32Cube MX配置,给出判断闰年、平年,Unix时间戳与UTC时间相互转换,时间计数器读写等主要代码,最后试验测试效果良好,总结了其特点与注意事项。

一、STM32f103系列RTC功能

RTC实时时钟功能是嵌入式软件开发中比较常用的功能,一般MCU的RTC功能都带有年月日时间寄存器,比如STM32F4xx系列,RTC描述如下:
在这里插入图片描述可见F4系列的RTC功能比较强大,设置好初始时间后,读取各个寄存器就可以获取日期及时间。
但有一些芯片的RTC功能比较简单,比如在STM32F103系列的手册中,是这样描述的:
在这里插入图片描述在这里插入图片描述由上可知,STM32F103系列的RTC功能只有一个计数器,每1秒加1,没有年月日及时间寄存器,读取计数器的值后,需要使用软件计算出时间,如果32位的寄存器存储无符号整型数,则2^32 -1秒≈136.19年,最长可计时100多年,对大部分场景来说足够用了。当然,如果再加上后备寄存器配合使用,可计时更长的时间。
计数器每秒加1,这样产生的时间是从开始时刻以来经过的秒数,不得不让我们想到另一个知识——Unix时间戳。

Unix时间戳(Unix timestamp),或称Unix时间(Unix time)、POSIX时间(POSIX time),是一种时间表示方式,定义为从格林威治时间1970年01月01日00时00分00秒起至现在的总秒数,不考虑闰秒。Unix时间戳不仅被使用在Unix系统、类Unix系统中,也在许多其他操作系统中被广泛采用。
所以,我们可以在读出32位寄存器中的计数值时,当做Unix时间戳,然后通过软件算法,转换为标准时间,这样程序的通用性就较强了。
下面使用STM32F103ZET6(正点原子战舰开发板),STM32Cube MX配置,IAR开发环境,进行程序编写、试验测试。

二、STM32Cube MX配置

使用的开发板上,有外接备用电池以及32.768kHz的晶振,断电后时钟可以继续计时,因此RTC的时钟源要现在32.768kHz的,其他时钟根据需要自行配置,如下图:
在这里插入图片描述接下来使能RTC功能,如下图:
在这里插入图片描述可根据需要,使能或禁用RTC中断以及闹钟中断功能,这里我们不使能:
在这里插入图片描述

三、 主要代码

首先,需要思考整个程序的流程,因为MCU有后备寄存器,其中的值会在电池供电的时候一直保持,我们可以在寄存器里写入一些标志,来在程序运行时判断RTC是首次运行,还是一直有电池供电运行,时间在继续计时。如果后备寄存器里没有我们想要的标志,说明RTC是首次运行,这个时候我们应该对时间计数器重新初始化,将日期和时间转为时间戳,写入计数器;如果后备寄存器里的值正是我们之前写入过的标志,则说明RTC一直在供电持续运行,此时读出计数器的值,转化成日期及时间。
而把Unix时间戳转换为时间,则不得不需要知道一些历法的知识,因为Unix时间不考虑闰秒,则只需要考虑闰年。把读出的秒数,按照每年(考虑闰年、平年)有多少秒,每月有多少秒,每天有多少秒,每小时有多少秒,每分钟多少秒,一一减掉,则可得出日期和时间。

1.判断闰年、平年

1582年以来公历的置闰规则:
普通闰年:公历年份是4的倍数,且不是100的倍数的,为闰年(如2004年、2020年等就是闰年)。
世纪闰年:公历年份是整百数的,必须是400的倍数才是闰年(如1900年不是闰年,2000年是闰年)。
1582年以前的惯例:四年一闰;如果公元A年的A(正数)能被4整除,那么它就是闰年;如果公元前B年的B(正数)除以4余1,那么它也是闰年。(1582年是神奇的一年,有兴趣的可以搜索一下1582年10月,看看发生了什么事。)
根据上述规则可以写一个判断是否是闰年的函数。实际上,STM32Cube MX自动生成的HAL库文件,在stm32f1xx_hal_rtc.c中有一个函数:

/**
  * @brief  Check whether the passed year is Leap or not.
  * @param  nYear  year to check
  * @retval 1: leap year
  *         0: not leap year
  */
static uint8_t RTC_IsLeapYear(uint16_t nYear)
{
  if ((nYear % 4U) != 0U)
  {
    return 0U;
  }

  if ((nYear % 100U) != 0U)
  {
    return 1U;
  }

  if ((nYear % 400U) == 0U)
  {
    return 1U;
  }
  else
  {
    return 0U;
  }
}

但这个函数是静态函数,头文件里没有,为了不对自动生成的文件做修改,我们可以自己写一个相同功能的函数(实际上可以完全复制自动生成的函数static uint8_t RTC_IsLeapYear(uint16_t nYear)放到另一个文件里,去掉static即可):

/*******************************************************************************
  * 函数名:IsLeapYear
  * 功  能:判断是否是闰年
  * 参  数:无
  * 返回值:1闰年,0平年
  * 说  明:整百年被400整除是闰年,其他被4整除是闰年
*******************************************************************************/
bool IsLeapYear(uint16_t Year)
{
	bool temp = 0;
	if ((Year % 4) == 0)//被4整除
	{
		if ((Year % 100) == 0)//整百年
		{
			if ((Year % 400) == 0)
			{
				temp = 1;
			}
		}else//非整百年,能被4整除是闰年
		{
			temp = 1;
		}
	}
	return temp;
}

2.Unix时间戳转为UTC时间

该过程,以1970年1月1日00:00:00为开始时间,把32位寄存器的值,转化成日历时间,笔者根据程序总结的流程如下:
① 先计算较简单的weekday,起始时间是周四;因为不管平年还是闰年,不管哪个月份,每一周都是7天,算出寄存器值总共有多少天,天数模7的余数,经过简单计算,就是当前的weekday;
② 计算年份;如果天数大于1年,则根据该年平年或闰年,减去相应天数,年数递增,继续判断剩余天数包含的年数,直至剩余天数不足一年,退出循环;
③ 计算月份和日期;步骤②最后剩余的天数,已不满1年,逐月减去相应的天数,月份递增,注意如果遇到闰年二月,要减去29天,直至剩余天数不足一个月,退出循环;剩余的天数+1为日期;
④ 计算时、分、秒;时间戳模86400的余数,为不足整天的秒数s,s/3600,为小时,(s%3600)/60为分钟,s%60为秒,这部分的计算比较好理解;
该部分代码如下(RTC_MONTH_XX以及RTC_WEEKDAY_XX的宏定义在stm32f1xx_hal_rtc.h中):

typedef struct
{
	uint8_t Hours;
    uint8_t Minutes;
    uint8_t Seconds;
    uint8_t WeekDay;
    uint8_t Month;
    uint8_t Date;
    uint16_t Year;
}RTC_ts;

//平年每月的天数
const uint8_t u8DayNumTab[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
/*******************************************************************************
  * 函数名:UnixToUTC
  * 功  能:Unix时间戳转为UTC时间
  * 参  数:u32TimeStamp:Unix时间戳
  * 返回值:UTC时间,RTC_ts结构体格式
  * 说  明:无
*******************************************************************************/
RTC_ts UnixToUTC(uint32_t u32TimeStamp)
{
	RTC_ts sTemp;
	sTemp.Year = 1970;
	sTemp.Month = RTC_MONTH_JANUARY;
	sTemp.Date = 1;
	sTemp.Hours = 0;
	sTemp.Minutes = 0;
	sTemp.Seconds = 0;
	sTemp.WeekDay = RTC_WEEKDAY_THURSDAY;
	
	uint32_t temp1 = 0;//天数
	uint32_t temp2 = 1970;//年份
	temp1 = u32TimeStamp / 86400;//计算出天数
	
	sTemp.WeekDay = (uint8_t)((temp1 % 7) + RTC_WEEKDAY_THURSDAY);//计算weekday,1970年1月1日为周四
	if (sTemp.WeekDay > RTC_WEEKDAY_SATURDAY)
	{
		sTemp.WeekDay -= 7;
	}
	
	if (temp1 > 0)
	{ 
		while (temp1 >= 365)//天数还大于1年,减去一年的天数,剩余的天数继续判断
		{
			if (IsLeapYear(temp2) == 1)//闰年
			{
				if (temp1 >= 366)
				{
					temp1 -= 366;
					temp2++;
				}else
				{
					break;//不足一年,跳出循环
				}		
			}else//平年
			{
				temp1 -= 365;
				temp2++;
			}
		}
		sTemp.Year = (uint16_t)temp2;//年份
		
		temp2 = RTC_MONTH_JANUARY;//月份,从1月份开始
		while (temp1 >= 28)//去掉整年/整月,剩余的天数
		{
			if ((IsLeapYear(sTemp.Year) == 1) && (temp2 == RTC_MONTH_FEBRUARY))//当年是闰年且是二月
			{
				if (temp1 >= 29)
				{
					temp1 -= 29;
				}else
				{
					break;
				}
			}else//平年
			{
				if (temp1 >= u8DayNumTab[temp2 - 1])//剩余天数比1个月大
				{
					temp1 -= u8DayNumTab[temp2 - 1];
				}else
				{
					break;
				}
			}
			temp2++;
		}
		sTemp.Month = (uint8_t)temp2;//月份
		sTemp.Date = (uint8_t)(temp1 + 1);//日期		
	}
	temp1 = (u32TimeStamp % 86400);//去掉整天,剩余的秒数
	sTemp.Hours = (uint8_t)(temp1 / 3600);//计算出小时数
	sTemp.Minutes = (uint8_t)((temp1 % 3600) / 60);//计算出分钟数
	sTemp.Seconds = (uint8_t)(temp1 % 60);//计算出秒数
	return sTemp;
}

3.UTC时间转为Unix时间戳

这一部分,与“2.Unix时间戳转为UTC时间”是相反的过程,将日期和时间,转换为1970-1-1 00:00:00以来的秒数,但这个过程要简单一些,流程如下:
① 计算整年的秒数;以1970年为起始年,逐年增加一年的秒数,直至UTC时间年的前一年,需要注意是平年还是闰年;
② 计算整月的秒数;逐月增加一个月的秒数,直至UTC时间月的前一个月,如果有闰月,需要多加1天;
③ 计算整天、整小时、整分钟的秒数,加上剩余的UTC时间秒的秒数,以上所有数值的和,即为Unix时间戳;

/*******************************************************************************
  * 函数名:UTCToUnix
  * 功  能:UTC时间转为Unix时间戳
  * 参  数:UTC时间,RTC_ts结构体格式
  * 返回值:Unix时间戳
  * 说  明:标准UTC时间转为Unix时间戳,时间范围1970~2100年
*******************************************************************************/
uint32_t UTCToUnix(RTC_ts sTime)
{
	uint32_t u32TimeStamp = 0;
	uint16_t i;
	if ((sTime.Year < 1970) || (sTime.Year > 2100))
	{
		return 0;
	}
	for (i = 1970; i < sTime.Year; i++)//计算整年的秒数
	{
		if (IsLeapYear(i) == 1)
		{
			u32TimeStamp += (86400 * 366);
		}else
		{
			u32TimeStamp += (86400 * 365);
		}
	}
	for (i = 0; i < (sTime.Month - 1); i++)//增加整月的秒数
	{
		u32TimeStamp += ((uint32_t)u8DayNumTab[i] * 86400);
	}
	if ((IsLeapYear(sTime.Year) == 1) && ((sTime.Month  - 1) >= RTC_MONTH_FEBRUARY))//当年是闰年且月份超过2月
	{
		u32TimeStamp += 86400;//多加1天
	}
	u32TimeStamp += (((uint32_t)sTime.Date - 1) * 86400);//增加整天的秒数	
	u32TimeStamp += ((uint32_t)sTime.Hours * 3600);//增加小时的秒数
	u32TimeStamp += ((uint32_t)sTime.Minutes * 60);//增加分钟的秒数
	u32TimeStamp += (uint32_t)sTime.Seconds;//增加剩余秒数
	return u32TimeStamp;
}

4.时间计数器读写函数

有了以上的函数后,还需要对计数器进行读、写操作,才能计算出时间,或者将时间更新。在stm32f1xx_hal_rtc.c中,也已经有该功能的函数,但不幸的是,这些函数仍然是静态函数,无法调用,只能自己在别处再写一个。不明白这么重要的函数,为什么不放到头文件里。我们把需要的函数复制过来,改个名字:

/*******************************************************************************
  * 函数名:Drv_RTC_EnterInitMode
  * 功  能:进入初始化模式
  * 参  数:RTC_HandleTypeDef结构体指针
  * 返回值:HAL Status
  * 说  明:stm32f1xx_hal_rtc.c中有相似static函数
*******************************************************************************/
static HAL_StatusTypeDef Drv_RTC_EnterInitMode(RTC_HandleTypeDef *hrtc)
{
  uint32_t tickstart = 0U;

  tickstart = HAL_GetTick();
  /* Wait till RTC is in INIT state and if Time out is reached exit */
  while ((hrtc->Instance->CRL & RTC_CRL_RTOFF) == (uint32_t)RESET)
  {
    if ((HAL_GetTick() - tickstart) >  RTC_TIMEOUT_VALUE)
    {
      return HAL_TIMEOUT;
    }
  }

  /* Disable the write protection for RTC registers */
  __HAL_RTC_WRITEPROTECTION_DISABLE(hrtc);


  return HAL_OK;
}
/*******************************************************************************
  * 函数名:Drv_RTC_ExitInitMode
  * 功  能:退出初始化模式
  * 参  数:RTC_HandleTypeDef结构体指针
  * 返回值:HAL Status
  * 说  明:stm32f1xx_hal_rtc.c中有相似static函数
*******************************************************************************/
static HAL_StatusTypeDef Drv_RTC_ExitInitMode(RTC_HandleTypeDef *hrtc)
{
  uint32_t tickstart = 0U;

  /* Disable the write protection for RTC registers */
  __HAL_RTC_WRITEPROTECTION_ENABLE(hrtc);

  tickstart = HAL_GetTick();
  /* Wait till RTC is in INIT state and if Time out is reached exit */
  while ((hrtc->Instance->CRL & RTC_CRL_RTOFF) == (uint32_t)RESET)
  {
    if ((HAL_GetTick() - tickstart) >  RTC_TIMEOUT_VALUE)
    {
      return HAL_TIMEOUT;
    }
  }

  return HAL_OK;
}
/*******************************************************************************
  * 函数名:Drv_RTC_ReadTimeCounter
  * 功  能:读取RTC计数器的值
  * 参  数:RTC_HandleTypeDef结构体指针
  * 返回值:计数器的值
  * 说  明:stm32f1xx_hal_rtc.c中有相似static函数
*******************************************************************************/
uint32_t Drv_RTC_ReadTimeCounter(RTC_HandleTypeDef *hrtc)
{
	uint16_t high1 = 0U, high2 = 0U, low = 0U;
	uint32_t timecounter = 0U;

	high1 = READ_REG(hrtc->Instance->CNTH & RTC_CNTH_RTC_CNT);
	low   = READ_REG(hrtc->Instance->CNTL & RTC_CNTL_RTC_CNT);
	high2 = READ_REG(hrtc->Instance->CNTH & RTC_CNTH_RTC_CNT);

	if (high1 != high2)
	{
		/* In this case the counter roll over during reading of CNTL and CNTH registers,
		   read again CNTL register then return the counter value */
		timecounter = (((uint32_t) high2 << 16U) | READ_REG(hrtc->Instance->CNTL & RTC_CNTL_RTC_CNT));
	}
	else
	{
		/* No counter roll over during reading of CNTL and CNTH registers, counter
		   value is equal to first value of CNTL and CNTH */
		timecounter = (((uint32_t) high1 << 16U) | low);
	}

	return timecounter;
}
/*******************************************************************************
  * 函数名:Drv_RTC_WriteTimeCounter
  * 功  能:写入RTC计数器的值
  * 参  数:hrtc:RTC_HandleTypeDef结构体指针
			TimeCounter:要写入的值
  * 返回值:HAL Status
  * 说  明:stm32f1xx_hal_rtc.c中有相似static函数
*******************************************************************************/
HAL_StatusTypeDef Drv_RTC_WriteTimeCounter(RTC_HandleTypeDef *hrtc, uint32_t TimeCounter)
{
	HAL_StatusTypeDef status = HAL_OK;

	/* Set Initialization mode */
	if (Drv_RTC_EnterInitMode(hrtc) != HAL_OK)
	{
		status = HAL_ERROR;
	}
	else
	{
		/* Set RTC COUNTER MSB word */
		WRITE_REG(hrtc->Instance->CNTH, (TimeCounter >> 16U));
		/* Set RTC COUNTER LSB word */
		WRITE_REG(hrtc->Instance->CNTL, (TimeCounter & RTC_CNTL_RTC_CNT));

		/* Wait for synchro */
		if (Drv_RTC_ExitInitMode(hrtc) != HAL_OK)
		{
		  status = HAL_ERROR;
		}
	}

	return status;
}
/*******************************************************************************
  * 函数名:Drv_RTC_SetTime
  * 功  能:设置时间
  * 参  数:RTC_ts时间结构体
  * 返回值:无
  * 说  明:将时间转为时间戳,写入寄存器
*******************************************************************************/
void Drv_RTC_SetTime(RTC_ts sTime)
{
	//Drv_RTC_WriteTimeCounter(&hrtc, UTC8ToUnix(sTime));
	Drv_RTC_WriteTimeCounter(&hrtc, UTCToUnix(sTime));
}
/*******************************************************************************
  * 函数名:Drv_RTC_GetTime
  * 功  能:获取时间
  * 参  数:无
  * 返回值:无
  * 说  明:每隔较短查询RTC模块获取一次
*******************************************************************************/
void Drv_RTC_GetTime(void)
{
	uint32_t u32Cnt = 0;
	u32Cnt = Drv_RTC_ReadTimeCounter(&hrtc);
	sTime = UnixToUTC(u32Cnt);
}

5.

首次上电时,初始化RTC模块(STM32CubeMx已自动生成该部分代码),读取后备寄存器的值,如果值不正确,则是首次配置,则初始化时间;如果值正确,则直接读取寄存器的值,将值转换为时间:

static RTC_ts sTime;//时间结构体变量
/*******************************************************************************
  * 函数名:Drv_RTC_Init
  * 功  能:初始化
  * 参  数:无
  * 返回值:无
  * 说  明:无
*******************************************************************************/
void Drv_RTC_Init(void)
{
	uint16_t u16Backup = 0;
	RTC_ts sDefaultTime;//初始时间,可根据需要设定
	memset(&sTime, 0 , sizeof(sTime));
	u16Backup = HAL_RTCEx_BKUPRead(&hrtc, RTC_BACKUP_REG);//读后备寄存器
	if (u16Backup != RTC_BACKUP_DATA)//检查备份寄存器,不对则是首次配置,初始化时间
	{
		sDefaultTime.Hours = 12;//初始时间,可根据需要设定
		sDefaultTime.Minutes = 0;//初始时间,可根据需要设定
		sDefaultTime.Seconds = 0;//初始时间,可根据需要设定
		sDefaultTime.WeekDay = RTC_WEEKDAY_SATURDAY;//初始时间,可根据需要设定
		sDefaultTime.Month = RTC_MONTH_MAY;//初始时间,可根据需要设定
		sDefaultTime.Date = 20;//初始时间,可根据需要设定
		sDefaultTime.Year = 2023;//初始时间,可根据需要设定
		Drv_RTC_SetTime(sDefaultTime);//设置为默认起始时间
		HAL_PWR_EnableBkUpAccess();//启用对备份寄存器的写访问
		HAL_RTCEx_BKUPWrite(&hrtc, RTC_BACKUP_REG, RTC_BACKUP_DATA);
		HAL_PWR_DisableBkUpAccess();//关闭对备份寄存器的写访问
	}else//寄存器数值正确,读取寄存器,转换为时间
	{
		Drv_RTC_GetTime();
	}
} 

在需要的时候,调用Drv_RTC_GetTime()函数,即可获得时间sTime。

四、试验测试效果

以1970年1月1日的某个时刻为例,如下图,算法输出了日期、时间、周几,最后一个数字为TimeCounter中的值,
在这里插入图片描述
找一个Uinx时间戳在线转换的网站,验证一下红框中的时间和TimeCounter中的值,完全正确:
在这里插入图片描述
有兴趣的可以继续验证平年、闰年,以及年月日切换是否正确。

五、总结

  1. STM32F103系列的RTC模块,没有年月日及时间寄存器,只有一个32位计数器,每1秒加1,没有年月日及时间寄存器,需要软件配合,才能实现日历、时钟功能;
  2. 由于只有计数功能,可以任意时间作为基准时间(即开始时间),用UTC时间戳(以1970年1月1日00:00:00为开始时间),可以使程序通用性更强;
  3. 以上UTCToUnix和UnixToUTC两个函数是以标准UTC时间为基础的,实际北京时间为UTC+8,相差8个小时,如果需要计算北京时间,需要加8个小时;

水平有限,以上如有疏漏之处,欢迎指正。

STM32F103 单片机RTC芯片DS3231驱动程序 define DS3231_CONTROL 0x0e //控制寄存器。#define DS3231_STATUS 0x0f //状态寄存器。#define DS3231_ALARM1WEEK 0x0A //星期/日。#define DS3231_ALARM2WEEK 0x0d //星期/日。#define DS3231_WEEK 0x03 //星期。#define DS3231_MINUTE 0x01 //分。 阅读详情

相关推荐

STM32最小系统概述

概述 STM32最小系统是由电源 复位 时钟 调试接口 启动的搭建组合。下面我主要以STM32F103ZET6这款MCU作为介绍给大家认识。 1.主芯片部分 主芯片部分采用的是ST公司系列下的STM32F103ZET6,是一种嵌入式-微控制器的集成电路,是由ST公司开发的STM32F1系列的其中一种,芯体尺寸是32位,速度是72MHz,程序存储器容量是256KB,程序存储器类型是F...

qeekje的博客 6537

STM32F103学习笔记(六) RTC实时时钟(原理篇)

实时时钟(RTC)是嵌入式系统中的一种重要组件,在嵌入式系统中,RTC的应用非常广泛。它被用于记录事件的时间戳、执行定时任务、调度系统操作等等。比如,您可能在计时器中使用RTC来定时执行某个任务,或者在数据记录应用中使用它来标记数据的时间戳。

全社区最菜的博客 6106

STM32F103RCT6嵌入式开发实战:从芯片架构到项目应用

在嵌入式系统开发领域,微控制器(MCU)作为核心处理单元,其架构设计与外设资源直接决定了项目的功能边界与性能上限。ARM Cortex-M系列内核凭借其高效能、低功耗的特性,已成为工业控制、物联网设备的主流选择。其中,Cortex-M3内核通过三级流水线与硬件除法器等设计,在实时控制与信号处理场景中展现出优异的运算效率。基于此内核的STM32F103系列MCU,以丰富的外设接口和成熟的生态工具链,为开发者提供了从基础IO控制到复杂网络通信的全套解决方案。在实际工程中,开发者常需面对外设驱动稳定性、实时操作系

weixin_30784501的博客 411

STM32F103学习笔记(六) RTC实时时钟(应用篇)

本节我们将利用RTC实现一个时钟显示的功能,通过读取时间戳来获取当前时间(主供电断开依然可以由备用电池供电,故主供电断开依然可以记时),本程序可以通过动态传参的方法来配置当前的初始时间。

全社区最菜的博客 6887

STM32F103开发板入门:从硬件解析到LED工程实战

嵌入式系统开发的核心在于理解微控制器与外设的协同工作原理。以广泛应用的ARM Cortex-M3内核为例,其通过丰富的外设接口(如GPIO、USART、I2C、SPI)实现对现实世界的感知与控制。掌握这些基础外设的驱动方法,对于构建智能硬件、物联网终端等应用具有重要技术价值。本文以普中PZ6806L开发板(主控STM32F103ZET6)为实践平台,深入解析其电源、时钟、调试接口等硬件设计,并手把手指导从零搭建Keil开发环境。通过从寄存器层面操作GPIO点亮LED的实战,帮助读者理解**时钟树配置**与*

weixin_29163797的博客 252

实现STM32的RTC驱动

RTC(Real-Time Clock)是一种精确的、可编程的时钟,通常用于应用中需要精确时间戳的场景。在STM32单片机中,RTC模块是一个独立的硬件模块,提供了秒级别的精度,而且在系统关机后也能继续运行,保证了时间的连续性。本文将介绍如何在STM32上实现RTC的驱动。在RTC_Init函数中,按照上文中的配置代码进行初始化RTC模块的操作。然后将RTC时钟源配置为LSE,使能RTC时钟,并初始化RTC模块。需要注意的是,本示例使用了printf函数,在实际应用中需要根据自己的需求选择合适的输出方式。

qq_37934722的博客 632

瑞萨RL78/G13单片机R5F100FE内部RTC驱动程序

使用单片机为R5F100FE,IDE为CS+ for CA,CX V4.00.00,编译工具为CA78K0R。瑞萨RL78/G13系列芯片没有外接电池的引脚,没有后备寄存器,因此在系统断电后无法保存时间;如果要实现断电后RTC继续计时,必须通过其他方式给系统切换供电,否则上电会将RTC时间也初始化;因此,该系列芯片的RTC功能用来做实时时钟的话,功能不全,但用来做中断,唤醒STOP模式,会比较好用。

wanglong3713的博客。 1650

STM32开发(十六)STM32F103 片内资源 —— 实时时钟RTC 详解

因此代码里的需要在串口中显示的中文,会以UTF-8的形式编码。(3)2个分离的时钟:用于APB1接口的PCLK1和RTC时钟(RTC时钟的频率必须小于PCLK1时钟频率的四分之一以上)。后备区域包括(RTC时钟预分配器、计数器),这里注意的是闹钟,RTC溢出,秒中断在断电之后不起作用。vscode 编写代码默认是以UTF-8格式编写的,但是SSCOM串口工具是以GBK格式显示的。(2)32位的可编程计数器,可用于较长时间段的测量。(1)可编程的预分频系数:分频系数最高为2^20。

weixin_43564241的博客 2366

实时时钟芯片DS1307单片机C语言驱动程序

DS1307是一款非易失性实时时钟(RTC)芯片,采用IIC总线接口进行通信,可以提供秒、分、时、日、月、年等时间和日期信息。DS1307还具有控制和配置寄存器,可以通过读写这些寄存器来设置和调整时间、日期和其他功能。主要特点如下:1.提供秒、分钟、小时、日、月、星期及年计时,带闰年补偿,有效期至2100年;2.56字节通用RAM,写次数不受限制;3. IIC串口通信;4. 可编程方波输出信号;5. 自动电源失效检测和切换电路;6. 电池备份模式下,振荡器运行功耗低于500nA;

wanglong3713的博客。 4485

ECC密码的加密和解密(附带代码实现)

ECC,全称Elliptic Curve Cryptography,是椭圆曲线密码学。这是一种公钥加密技术,其安全性基于椭圆曲线离散对数问题,被广泛应用于信息安全领域。ECC的主要优势在于,相较于RSA和DSA等其他传统的公钥密码体系,它能够在提供相同安全级别的情况下,使用更短的密钥,从而减少计算量和存储空间的需求。例如,一个256位的ECC密钥提供的安全性可以与3072位的RSA密钥或DSA密钥相媲美。ECC的主要应用领域包括SSL安全证书、安全电子邮件、移动设备安全、物联网设备安全等。

xixixi7777的博客 8271

从零到一:基于JasperGold的FPV实战入门与避坑指南

本文详细介绍了基于JasperGold的形式化验证(FPV)实战入门指南,涵盖环境搭建、SVA断言编写、TCL脚本调试等关键步骤。通过实际案例解析,帮助IC设计工程师掌握FPV技术,有效验证RTL设计中的复杂逻辑和corner case,提升验证效率与质量。

weixin_30899023的博客 364

单片机RTC及时钟芯片的时间到底从哪一年起始?

1.文中用到的设备,初始年份大多为00年,即四位年份yyyy格式的后两位,且当作闰年处理,如果是指1970年的话,1970年不是闰年;无论是单片机还是RTC专用芯片,没有一个手册中提到1970年1月1日这个时间,因此,说RTC功能的起始时间是1970-01-01 00:00:00的,不准确,也没有依据;2.对于类似STM32F103C8T6这样,RTC功能只有一个计数器,没有年月日时分秒这种时间寄存器的,需要由软件算法配合来计时,起始时间可以任意;3。

wanglong3713的博客。 3980

告别繁琐同步:Zotero与Obsidian双向联动新方案(基于Better BibTeX与MarkDBConnect)

本文详细介绍了一套基于Better BibTeX、Bibnotes Formatter和MarkDBConnect插件的Zotero与Obsidian双向联动方案。该方案通过建立智能数据通道,实现文献信息的自动同步与笔记的增量更新,解决了手动同步的繁琐问题,并支持在两大工具间无缝跳转,极大提升了科研和知识管理的工作效率。

weixin_29213827的博客 643

基于STM32F103的实时时钟程序:高效稳定的时钟解决方案

基于STM32F103的实时时钟程序:高效稳定的时钟解决方案 去发现同类优质开源项目:https://gitcode.com/ 项目介绍 在现代电子设备的开发中,实时时钟(RTC)功能是不可或缺的一部分。今天,我们将为您推荐一个基于STM32F103微控制器的实时时钟程序,该程序利用DS1302实时时钟模块以及TM1637四位数码管,为您提供直观、精确的时钟显示和调整功能。 项目技术分析 核心技术...

gitblog_06738的博客 951

Labview程序框架介绍

当掌握LabVIEW基础知识并达到,理解单个小型模块程序会相对轻松。而若想进一步探索大型程序,就必然需要了解程序框架相关内容。LabVIEW常用的程序架构包括等。在多年工作中,我接触过的众多高级工程师所开发的大型程序,基本都基于这些架构搭建。接下来,将为大家详细介绍这些框架。

Misterlhw的博客 4620

MacOS新手必看:HomeBrew安装避坑指南(含M1芯片适配方案)

本文为MacOS新手提供全面的HomeBrew安装指南,特别包含M1/M2芯片适配方案。从系统准备到核心安装流程,再到日常维护和高级配置,详细解析每个步骤的潜在问题及解决方案,帮助用户高效管理Mac软件。

weixin_28831399的博客 471

08_STM32Cubeide开发_RTC实时时钟配置

RTC实时时钟

DDGGG1的博客 2655

gem5 prefetcher

最近在gem5上做预取实验,添加自己的预取算法,这里采用hardware stream prefetcher , 修改了几个bug才给实验调试通过,发文记录下实验过程。 gem5上添加自己的预取算法步骤: (1)路径gem5-master/configs/common/Caches.py下,开启预取:class L1Cache(Cache): assoc = 2 tag_late

编程邦 3845

实时时钟芯片DS1302单片机C语言驱动程序

DS1302 是 DALLAS(达拉斯)公司推出的一款涓流充电时钟芯片。主要特点:1.可计算秒、分、时、日、日期、星期、月、年的能力,并有闰年补偿功能;2.内部含有31个字节静态RAM;3.采用串行数据传送方式,3线接口;4.与TTL兼容,Vcc=5V;5.时钟或RAM数据的读/写有两种传送方式:单字节传送和多字节传送方式;6.工作电压范围宽:2.0~5.5V;7.功耗低,2.0V时工作电流小于300nA。8.采用8脚DIP封装或SOIC封装。

wanglong3713的博客。 3281

YOLO26涨点改进 | 全网独家创新,注意力改进篇 | TGRS 2025 | 引入APCM自适应像素级协同机制,动态增强目标区域、抑制背景噪声,助力YOLO26做红外小目标检测有效涨点

本文提出一种基于APCM模块改进的YOLO26网络模型,通过像素级局部-全局协同建模动态增强目标特征并抑制背景噪声。APCM模块采用轻量化设计,无需复杂参数,在特征提取阶段有效提升目标区域特征表达质量,尤其适用于低对比度和复杂场景下的目标检测。实验表明,该方法能显著提高小目标检测精度和模型鲁棒性,同时保持较低计算开销。模块通过局部特征分支和全局特征分支的协同融合,实现了对红外小目标的有效增强,在NUAA-SIRST和IRSTD-1K数据集上性能优于现有方法。文章还提供了完整的代码实现和配置指南。

Ai缝合怪、助力小伙伴高效发论文 190

【OpenClaw从入门到精通】第11篇:OpenClaw 2026多模态实战全攻略:OCR/语音/图像自动化(附完整代码+双部署方案)

本文聚焦OpenClaw 2026版核心升级的多模态能力,从底层架构原理到实操落地全流程拆解。先解析“统一指令解析+多工具适配器”的核心设计逻辑,再提供阿里云规模化部署(适合批量任务)与本地汉化版部署(适合隐私数据调试)的详细步骤,含依赖安装、配置验证完整操作;随后落地三大高频实战场景——OCR合同信息结构化提取、会议录音自动转写纪要、电商商品图片智能打标,每个场景均提供可直接运行的完整代码、执行结果演示及流程说明;最后补充多模态任务避坑指南、性能优化技巧与成本控制方案。

专注于人工智能、软件开发、工控自动化、工厂数字化及智能化等领域,希望和大家共同进步! 2611

stm32f103

标红色的电源相关的引脚标蓝色的是最小系统相关的引脚绿色的IO口,功能口类型s电源 ,I 输入,O输出,IO输入输出IO口电平代表IO口所能容忍的电压FT,容忍5V电压,没有FT容忍3.3V电压没有FT需要接5V的电平,需要加装电平转换电路。

2301_78233804的博客 300

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

【单片机毕业设计】【mcuclub-dz-700】基于STM32智能水族箱的设计

本文介绍了一款基于STM32F103C9T6单片机的智能水族箱系统。该系统通过多种传感器实时监测水位、水温、PH值、浑浊度和光照强度,并自动控制加水、加热、换水、补光等功能。系统还具备人体感应自动补光、定时投喂和充氧功能,可通过按键设置参数,OLED显示屏显示数据,并通过WiFi将数据上传至手机端实现远程监控和控制。硬件采用双层PCB板设计,软件基于Keil5开发,实现了水族环境的智能化管理。

qq_40472627的博客 615

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

单片机知识大合集

STM32F10x_StdPeriph_Lib_V3.6.0\Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x\startup\arm 中的所有文件复制到CMSIS文件中。STM32F10x_StdPeriph_Lib_V3.6.0\Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x中的以下三个文件复制到 User 文件中。新建CMSIS,FWLIB,User 三个文件,然后把刚刚复制在对应文件夹下的文件添加。

weixin_55418511的博客 1020

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

STM32F1 HAL库实战开发——【实验1 点亮LED实验】

基于ARM内核的32位MCU系列,标准的ARM架构。内核为ARM公司为要求高性能、低成本、低功耗的嵌入式应用。【学习笔记】

apple_55903083的博客 2571
上一篇: 51单片机STC8G使用printf函数的方法
下一篇: 用逻辑分析仪观察串口Uart数据波形
wanglong3713
博客等级 码龄16年 1万+粉丝 42原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值