STM32-RTC实时时钟

这篇具有很好参考价值的文章主要介绍了STM32-RTC实时时钟。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

RTC实时时钟

功能框图

UNIX时间戳

初始化结构体

RTC时间结构体

RTC日期结构体

RTC闹钟结构体

进入和退出配置函数

实验环节1:显示日历

常规配置

RTC配置

测试环节

实验现象

实验环节2:闹钟

常规配置

RTC配置

测试环节

实验现象


RTC实时时钟

STM32的RTC外设,实质上是一个掉电后还继续运行的定时器。类似于通用定时器TIM外设,可以计时和触发中断。

掉电指的是电源VDD断开时为了RTC外设掉电继续运行,必须接上锂电池给STM32的RTC、备份发卡通过Vbat引脚供电。当主电源VDD有效时由VDD给RTC外设供电,当CDD掉电后,由Vbat给RTC外设供电。但无论由什么电源供电,RTC的数据都保存在属于RTC的备份域中,若主电源VDD和Vbat都掉电,则备份域保存的所有数据将丢失。备份域除了RTC模块的寄存器,还有42个16位的寄存器可以在VDD掉电时保存用户程序的数据,系统复位或电源复位时,数据也不会清空。

从RTC的定时器特性来说,是一个32位的递增计数器。时钟源有三种:HSE/128、LSI和LSE。

使用LSI或HSE/8时钟源,在主电源VDD掉电时,这两个时钟来源都会受到影响,因此无法保证RTC正常工作。因此RTC一般使用LSE。在设计中,频率通常为32.768kHz

在主电源VDD有效并处于待机模式时,RTC还可以配置闹钟事件时STM32退出待机模式

功能框图

STM32-RTC实时时钟,# 野火指南者STM32F103,stm32

浅灰色部分属于备份域,VDD掉电时可在Vbat的驱动下继续运行。包括了RTC分频器、计数器、闹钟控制器。

若VDD电源有效,RTC可以触发RTC_Second(秒中断)、RTC_Overflow(溢出事件)和RTC_Alarm(闹钟中断)。从结构图可以分析出,定时器溢出中断无法被配置为中断。

若STM32处于待机模式,可由闹钟事件或WKUP事件(外部唤醒事件,属于EXTI模块,不属于RTC)退出待机模式。

闹钟事件在计数器RTC_CNT的值等于闹钟寄存器RTC_ALR的值时触发。

在备份域中所有寄存器都是16位的,RTC控制相关的寄存器也不例外。它的计数器RTC_CNT的32位由RTC_CNTL和RTC_CNTH这两个寄存器组成。

在配置RTC模块的时钟时,通常把输入的32768Hz的RTCCLK进行32768分频得到实际驱动计数器的时钟TR_CLK = RTCCLK / 32768 =1Hz,计数周期为1s,计数器在TR_CLK的驱动下计数,即每秒计数器RTC_CNT的值+1。

由于备份域的存在,使得RTC核具有了完全独立于APB1接口的特性,也因此对RTC寄存器的访问要遵守一定的规则。

系统复位后,默认禁止访问后备寄存器和RTC,防止对后备区域(BKP)的意外写操作。执行以下操作使能对后备寄存器和RTC的访问:

        设置RCC_APB1ENR:PWREN、BKPEN位来使能电源和后备接口时钟。

        设置PWR_CR:DBP位使能对后备寄存器和RTC的访问。

设置后备寄存器为可写访问后,在第一次通过APB1接口访问RTC时,因为时钟频率的差异,所以必须等待APB1和RTC外设同步,确保被读取出来的RTC寄存器值是正确的。若在同步后,一直没有关闭APB1的RTC外设接口,就不需要再次同步了。

如果内核要对RTC寄存器进行任何写操作,在内核写出写指令后,RTC模块在3个RTCCLK时钟后才开始正式的写RTC寄存器操作。由于RTCCLK的频率比内核主频低得多,所以每次操作后都必须检查RTC关闭操作标志位RTOFF,当这个标志位被置1,写操作才正式完成。

当然,以上操作都具有对应的库函数,不需要具体的查阅寄存器。 

UNIX时间戳

RTC_CNT是32位寄存器,可存储的最大值为2^32-1,即约等于136年。

如某个时刻读取计数器的值为2天的秒数,以2011.1.1 0:0:0时间置0计数器的,则可以算出是2011.1.3 0:0:0时间,计数器会在2011+136年左右溢出。定时器被置0的时间为计数元年,相对计时元年的秒数为时间戳(计数器的值)。

大多数操作系统都是利用时间戳和计时元年来计算当前时间的,有个标准:UNIX时间戳和UNIX计时元年。

UNIX计时元年被设置为格林威治时间1970.1.1 0:0:0时间。

在这个计时系统上,使用的是有符号的32位整形变量来保存UNIX时间戳,因此最高位表示符号,时间戳能显示的范围更小了,会在2038.1.19 3:14:07时间溢出。

网页上可搜:UNIX时间戳。可实时查看。

初始化结构体

STM32 HAL库对RTC控制提供了完善的函数。

typedef struct {
     uint32_t AsynchPrediv;    /* 配置RTC_CLK的异步分频因子(0x00~0x7F ) ,具体由RTC_PRER:PREDIV_A[6:0]配置 */
     uint32_t OutPut;          /* RTCEx输出通道设置,指定哪一路信号作为RTC的输出,禁止输出/闹钟A输出/闹钟B输出/唤醒输出 */
 } RTC_InitTypeDef;

RTC时间结构体

用来设置初始时间,配置的是RTC时间寄存器RTC_TR。

 typedef struct {
     uint8_t Hours;    /* 小时设置。12小时制式时,0~11;24小时制式时,0~23 */
     uint8_t Minutes;  /* 分钟设置,0~59 */
     uint8_t Seconds;  /* 秒设置,0~59 */
 } RTC_TimeTypeDef;

RTC日期结构体

用来设置初始日期,配置的是RTC日期寄存器RTC_DR。

typedef struct {
     uint8_t WeekDay; /* 星期几设置,1~7 */
     uint8_t Month;   /* 月份设置,1~12 */
     uint8_t Date;    /* 日期设置,1~31 */
     uint8_t Year;    /* 年份设置,0~99 */
 } RTC_DateTypeDef;

RTC闹钟结构体

用来设置闹钟时间,设置的格式为[星期/日期]-[时]-[分]-[秒],4个字段,每个字段可以设置为有效或无效(MASK)。如果MASK掉[星期/日期]字段,则每天闹钟都会响。

typedef struct {
     RTC_TimeTypeDef AlarmTime;     /* 设定RTC时间寄存器的值:时/分/秒 */
     uint32_t Alarm;                /* RTC 闹钟选择:闹钟A、闹钟B */
 } RTC_AlarmTypeDef;

进入和退出配置函数

/**
 * @brief  进入 RTC 配置模式 .
 * @param  None
 * @retval None
 */
 void RTC_EnterConfigMode(void)
 {
     /* 设置 CNF 位进入配置模式 */
     RTC->CRL |= RTC_CRL_CNF;
 }

/*
 * @brief  退出 RTC 配置模式 .
 * @param  None
 * @retval None
 */
 void RTC_ExitConfigMode(void)
 {
     /* 清空  CNF 位退出配置模式 */
     RTC->CRL &= (uint16_t)~((uint16_t)RTC_CRL_CNF);
 }

实验环节1:显示日历

常规配置

USART1:带中断,支持printf输出。

RTC配置

STM32-RTC实时时钟,# 野火指南者STM32F103,stm32

RTC_HandleTypeDef hrtc;

/* RTC init function */
void MX_RTC_Init(void)
{
    RTC_TimeTypeDef sTime = {0};
    RTC_DateTypeDef DateToUpdate = {0};

    /* USER CODE BEGIN RTC_Init 1 */
    /* 判断是否首次上电 */
    if (HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR1) != 0x5050)
    {
        HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR1, 0x5050);	// 插入BKP数值判断
        /* USER CODE END RTC_Init 1 */

        /** Initialize RTC Only
        */
        hrtc.Instance = RTC;
        hrtc.Init.AsynchPrediv = RTC_AUTO_1_SECOND;
        hrtc.Init.OutPut = RTC_OUTPUTSOURCE_NONE;
        if (HAL_RTC_Init(&hrtc) != HAL_OK)
        {
            Error_Handler();
        }

        /** Initialize RTC and set the Time and Date
        */
        sTime.Hours = 0x0;
        sTime.Minutes = 0x0;
        sTime.Seconds = 0x0;
        if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BCD) != HAL_OK)
        {
            Error_Handler();
        }

        DateToUpdate.WeekDay = RTC_WEEKDAY_MONDAY;
        DateToUpdate.Month = RTC_MONTH_JANUARY;
        DateToUpdate.Date = 0x1;
        DateToUpdate.Year = 0x0;
        if (HAL_RTC_SetDate(&hrtc, &DateToUpdate, RTC_FORMAT_BCD) != HAL_OK)
        {
            Error_Handler();
        }

    /* USER CODE BEGIN RTC_Init 2 */
    }
    /* USER CODE END RTC_Init 2 */
}

void HAL_RTC_MspInit(RTC_HandleTypeDef *rtcHandle)
{
    if (rtcHandle->Instance == RTC)
    {
        HAL_PWR_EnableBkUpAccess();	// 取消BKP区域写保护,才能进行时间保存和计时
        /* Enable BKP CLK enable for backup registers */
        __HAL_RCC_BKP_CLK_ENABLE();	// 开启BKP时钟
        /* RTC clock enable */
        __HAL_RCC_RTC_ENABLE();
    }
}

void HAL_RTC_MspDeInit(RTC_HandleTypeDef *rtcHandle)
{
    if (rtcHandle->Instance == RTC)
    {
        __HAL_RCC_RTC_DISABLE();
    }
}

测试环节

#include "string.h"

uint8_t RxBuffer[20];

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
	if(huart == &huart1)
    {
    }
}

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	if(huart == &huart1)
    {
		// A5 5A 00 01 01 00 00 00
		if(RxBuffer[0]==0xA5 && RxBuffer[1]==0x5A)
		{
			RTC_DateTypeDef RtcDate;
			RTC_TimeTypeDef RtcTime;
		
			RtcTime.Hours = RxBuffer[5];
			RtcTime.Minutes = RxBuffer[6];
			RtcTime.Seconds = RxBuffer[7];
			
			if (HAL_RTC_SetTime(&hrtc, &RtcTime, RTC_FORMAT_BCD) != HAL_OK)
			{
				Error_Handler();
			}
			
			// 星期内部自动校正
			RtcDate.WeekDay = RTC_WEEKDAY_MONDAY;
			RtcDate.Month = RxBuffer[3];
			RtcDate.Date = RxBuffer[4];
			RtcDate.Year = RxBuffer[2];
			
			if (HAL_RTC_SetDate(&hrtc, &RtcDate, RTC_FORMAT_BCD) != HAL_OK)
			{
				Error_Handler();
			}

			memset(RxBuffer, 0, sizeof(RxBuffer));
			HAL_UART_Receive_IT(&huart1, (uint8_t *)&RxBuffer, 8);
		}
    }	
}

void test(void)
{
	RTC_DateTypeDef RtcDate;
	RTC_TimeTypeDef RtcTime;
	
	HAL_UART_Receive_IT(&huart1, (uint8_t *)&RxBuffer, 8);
	while(1)
	{
		HAL_RTC_GetTime(&hrtc, &RtcTime,  RTC_FORMAT_BIN);	// 读出时间值
		HAL_RTC_GetDate(&hrtc, &RtcDate,  RTC_FORMAT_BIN);	// 一定要先读时间后读日期,这样才能校正星期参数
		printf("实时时间:%04d-%02d-%02d  %02d:%02d:%02d 星期:%2d\r\n", 2000+RtcDate.Year, RtcDate.Month, 
			RtcDate.Date, RtcTime.Hours, RtcTime.Minutes, RtcTime.Seconds, RtcDate.WeekDay);//显示日期时间
		HAL_Delay(1000);
	}
}

实验现象

STM32-RTC实时时钟,# 野火指南者STM32F103,stm32

实验环节2:闹钟

常规配置

USART1:带中断,支持printf输出。

蜂鸣器配置。

RTC配置

STM32-RTC实时时钟,# 野火指南者STM32F103,stm32

RTC_HandleTypeDef hrtc;

void MX_RTC_Init(void)
{
    RTC_TimeTypeDef sTime = {0};
    RTC_DateTypeDef DateToUpdate = {0};
    RTC_AlarmTypeDef sAlarm = {0};

    /* USER CODE BEGIN RTC_Init 1 */
    /* 判断是否首次上电 */
    if (HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR1) != 0x5050)
    {
        HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR1, 0x5050);	// 插入BKP数值判断
    /* USER CODE END RTC_Init 1 */

        /** Initialize RTC Only
        */
        hrtc.Instance = RTC;
        hrtc.Init.AsynchPrediv = RTC_AUTO_1_SECOND;
        hrtc.Init.OutPut = RTC_OUTPUTSOURCE_ALARM;
        if (HAL_RTC_Init(&hrtc) != HAL_OK)
        {
            Error_Handler();
        }

        /** Initialize RTC and set the Time and Date
        */
        sTime.Hours = 0x0;
        sTime.Minutes = 0x0;
        sTime.Seconds = 0x0;
        if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BCD) != HAL_OK)
        {
            Error_Handler();
        }

        DateToUpdate.WeekDay = RTC_WEEKDAY_MONDAY;
        DateToUpdate.Month = RTC_MONTH_JANUARY;
        DateToUpdate.Date = 0x1;
        DateToUpdate.Year = 0x0;
        if (HAL_RTC_SetDate(&hrtc, &DateToUpdate, RTC_FORMAT_BCD) != HAL_OK)
        {
            Error_Handler();
        }

        /** Enable the Alarm A
        */
        sAlarm.AlarmTime.Hours = 0x0;
        sAlarm.AlarmTime.Minutes = 0x1;
        sAlarm.AlarmTime.Seconds = 0x0;
        sAlarm.Alarm = RTC_ALARM_A;
        if (HAL_RTC_SetAlarm_IT(&hrtc, &sAlarm, RTC_FORMAT_BCD) != HAL_OK)
        {
            Error_Handler();
        }

    /* USER CODE BEGIN RTC_Init 2 */
    }

    /* USER CODE END RTC_Init 2 */
}

void HAL_RTC_MspInit(RTC_HandleTypeDef *rtcHandle)
{
    if (rtcHandle->Instance == RTC)
    {
        HAL_PWR_EnableBkUpAccess();
        /* Enable BKP CLK enable for backup registers */
        __HAL_RCC_BKP_CLK_ENABLE();
        /* RTC clock enable */
        __HAL_RCC_RTC_ENABLE();

        /* RTC interrupt Init */
        HAL_NVIC_SetPriority(RTC_Alarm_IRQn, 0, 0);
        HAL_NVIC_EnableIRQ(RTC_Alarm_IRQn);
    }
}

void HAL_RTC_MspDeInit(RTC_HandleTypeDef *rtcHandle)
{
    if (rtcHandle->Instance == RTC)
    {
        __HAL_RCC_RTC_DISABLE();

        /* RTC interrupt Deinit */
        HAL_NVIC_DisableIRQ(RTC_Alarm_IRQn);
    }
}

测试环节

#include "string.h"

uint8_t RxBuffer[20];

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
	if(huart == &huart1)
    {
    }
}

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	if(huart == &huart1)
    {
		// A5 5A 00 01 01 00 00 00
		if(RxBuffer[0]==0xA5 && RxBuffer[1]==0x5A)
		{
			RTC_DateTypeDef RtcDate;
			RTC_TimeTypeDef RtcTime;
			RTC_AlarmTypeDef RtcAlarm;
			
			RtcTime.Hours = RxBuffer[5];
			RtcTime.Minutes = RxBuffer[6];
			RtcTime.Seconds = RxBuffer[7];
			
			if (HAL_RTC_SetTime(&hrtc, &RtcTime, RTC_FORMAT_BCD) != HAL_OK)
			{
				Error_Handler();
			}
			
			// 星期内部自动校正
			RtcDate.WeekDay = RTC_WEEKDAY_MONDAY;
			RtcDate.Month = RxBuffer[3];
			RtcDate.Date = RxBuffer[4];
			RtcDate.Year = RxBuffer[2];
			
			if (HAL_RTC_SetDate(&hrtc, &RtcDate, RTC_FORMAT_BCD) != HAL_OK)
			{
				Error_Handler();
			}

			
			RtcAlarm.AlarmTime.Hours = RxBuffer[5];
			RtcAlarm.AlarmTime.Minutes = RxBuffer[6];
			RtcAlarm.AlarmTime.Seconds = RxBuffer[7] + 0x10;
			RtcAlarm.Alarm = RTC_ALARM_A;
			if (HAL_RTC_SetAlarm_IT(&hrtc, &RtcAlarm, RTC_FORMAT_BCD) != HAL_OK)
			{
				Error_Handler();
			}
  
			memset(RxBuffer, 0, sizeof(RxBuffer));
			HAL_UART_Receive_IT(&huart1, (uint8_t *)&RxBuffer, 8);
		}
    }	
}

void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
{
	HAL_GPIO_TogglePin(LED_G_GPIO_Port, LED_G_Pin);
}

void test(void)
{
	RTC_DateTypeDef RtcDate;
	RTC_TimeTypeDef RtcTime;
	
	初始化
	
	HAL_UART_Receive_IT(&huart1, (uint8_t *)&RxBuffer, 8);
	while(1)
	{
		HAL_RTC_GetTime(&hrtc, &RtcTime,  RTC_FORMAT_BIN);	// 读出时间值
		HAL_RTC_GetDate(&hrtc, &RtcDate,  RTC_FORMAT_BIN);	// 一定要先读时间后读日期,这样才能校正星期参数
		printf("实时时间:%04d-%02d-%02d  %02d:%02d:%02d 星期:%2d\r\n", 2000+RtcDate.Year, RtcDate.Month, 
			RtcDate.Date, RtcTime.Hours, RtcTime.Minutes, RtcTime.Seconds, RtcDate.WeekDay);//显示日期时间
		HAL_Delay(1000);
	}
}

实验现象

上电运行,LED默认灭。一分钟后LED亮绿灯。

通过串口调试助手发送A55A231101000000,10秒后LED灭。文章来源地址https://www.toymoban.com/news/detail-740231.html

到了这里,关于STM32-RTC实时时钟的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • STM32-RTC实时时钟详解

    RTC的本质很简单,就是一个时钟经过精确分频最后得到的一个1Hz的时钟,也可以说是计数器,其他大部分功能都是基于这个计数器设计的数字逻辑。 本文讲的RTC是基于STM32F030来讲的,相比与F1系列的RTC来说,M0的将很多原本需要软件实现的功能硬件化了,使用起来更加便利。

    2024年02月04日
    浏览(39)
  • 【STM32学习】实时时钟 —— RTC

    STM32RTC实时时钟实验讲解,从入门到放弃 【STM32】RTC休眠唤醒(停机模式)、独立看门狗开启状态下 关于STM32使用RTC唤醒停止模式的设置 RTC(Real Time Clock):实时时钟,是指可以像时钟一样输出实际时间的电子设备,一般会是集成电路,因此也称为时钟芯片。总之,RTC只是个能靠电

    2024年02月01日
    浏览(40)
  • STM32-实时时钟RTC-2

                                     

    2024年01月20日
    浏览(38)
  • STM32基础10--实时时钟(RTC)

     目录 前言 RTC框图 STM32实时时钟电路 功能需要 STM32CubeMx配置RTC 配置RCC 配置RTC 配置时间,闹钟,唤醒 开启中断 设置中断优先级 功能代码实现 STM32Cude生成RTC初始化 自定义触发闹钟次数变量  重写周期唤醒回调函数 重写闹钟中断函数         在做51单片机项目时,如果需

    2023年04月11日
    浏览(29)
  • STM32学习笔记(十二)丨RTC实时时钟

    ​  本次课程采用单片机型号为STM32F103C8T6。 ​  课程链接:江协科技 STM32入门教程   往期笔记链接:   STM32学习笔记(一)丨建立工程丨GPIO 通用输入输出   STM32学习笔记(二)丨STM32程序调试丨OLED的使用   STM32学习笔记(三)丨中断系统丨EXTI外部中断   

    2024年02月16日
    浏览(41)
  • STM32中的RTC实时时钟和配套闹钟设置

    主要初始化函数,以及设置闹钟函数,闹钟中断函数 RTC.c RTC.h main.c

    2024年04月23日
    浏览(36)
  • stm32第一节:认识寄存器(野火指南者——STM32F103VET6)

            STM32芯片架构                 Cortex-M3内核(arm)——(I,S,D)——总线矩阵——外设, Flash,SRAM                 外设—— GPIO,USART,12C,SPI ……         Flash及SRAM储存                 Flash——常量                 SRAM——变量         DMA作用   

    2024年02月21日
    浏览(44)
  • stm32-OLED屏+RTC实现简易实时时钟(上篇)

    oled屏选择ssd1306,使用RTC实现简易实时时钟 1、MCU接口选择 SSD1306单片机接口由8个数据引脚和5个控制引脚组成。通过BS[2:0]引脚上的硬件选择可以设置不同的MCU模式    通过控制BS[2:0]引脚可以设置MCU与OLED屏的通信方式。因为我使用的是正点原子的开发板,所以我用了适配的接

    2024年01月17日
    浏览(39)
  • stm32-OLED屏+RTC实现简易实时时钟(下篇)

    一、RTC简介 实时时钟是一个独立的定时器。RTC模块拥有一组连续计数的计数器,在相应软件配置下,可 提供时钟日历的功能。修改计数器的值可以重新设置系统当前的时间和日期。 RTC模块和时钟配置系统(RCC_BDCR寄存器)处于后备区域,即在系统复位或从待机模式唤醒 后,R

    2024年01月18日
    浏览(39)
  • 【STM32】读写BKP备份寄存器&RTC实时时钟

    目录 BKP BKP简介 BKP基本结构 BKP测试代码 RTC RTC简介 RTC框图 RTC基本结构 硬件电路 RTC操作注意事项 接线图 初始化 使用BKP解决只初始化一次时间  初始化参考代码 RTC设置时间 RTC读取时间 完整代码 MyRTC.c MyRTC.h main.c BKP(Backup Registers)备份寄存器 BKP可用于存储用户应用程序数据

    2024年04月22日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包