
一.概要RTCReal-Time Clock是一种用于追踪和记录实际时间的时钟系统。在STM32中RTC通常用于提供实时时钟和日期信息例如年、月、日、时、分、秒等。RTC在许多应用中都是重要的尤其是需要记录事件发生时间的系统如数据日志、定时器 等。RTC位于系统的后备区域这意味着在系统复位时其数据不会清零。此外即使在电源VDD2.0-3.6V断开的情况下RTC仍可以通过备份电源VBAT1.8~3.6V供电继续运行。本文介绍了STM32 RTC实时时钟的基本概念内部结构以及用内部RTC进行时间计时的例程。二.RTC基本介绍实时时钟Real Time Clock是一个独立的定时器。RTC模块拥有一组连续计数的计数器在相应软件配置下可提供时钟日历的功能。修改计数器的值可以重新设置系统当前的时间和日期。这款MCU的RTC没有硬件万年历功能是通过RTC库的HAL_RTC_GetTime()函数将秒数转换成日期数据的。RTC模块和时钟配置系统(RCC_BDCR寄存器)处于后备区域即在系统复位或从待机模式唤醒后RTC的设置和时间维持不变。RTC主要特性可编程的预分频系数分频系数最高为220。● 32位的可编程计数器可用于较长时间段的测量。● 2个分离的时钟用于APB1接口的PCLK1和RTC时钟(RTC时钟的频率必须小于PCLK1时钟频率的四分之一以上)。● 可以选择以下三种RTC的时钟源─ HSE时钟除以128。─ LSE振荡器时钟这个是最常用的时钟源。─ LSI振荡器时钟。● 2个独立的复位类型─ APB1接口由系统复位─ RTC核心(预分频器、闹钟、计数器和分频器)只能由后备域复位。● 3个专门的可屏蔽中断─ 闹钟中断用来产生一个软件可编程的闹钟中断。─ 秒中断用来产生一个可编程的周期性中断信号(最长可达1秒)。─ 溢出中断指示内部可编程计数器溢出并回转为0的状态。三.STM32单片机RTC内部结构图RTC内部结构图如下灰色区域是后备区域这些电路在主电源VDD掉电后可以使用备用电池VBAT维持工作所以这里是RTC的主要部分。RTCCLK进来需要首先经过RTC预分频器进行分频得到时钟信号提供给32位寄存器RTC_CNT是计时最核心的部分。RTC_CNT寄存器用来存放计数器的计数值对RTC_CNT进行读操作时直接返回计数器内的计数值(系统时间)。RTC_CNT一个 32 位的时钟计数器按秒钟计算可以记录 4294967296 秒约合 136 年左右STM32F103C8T6 通过RTC库的HAL_RTC_GetTime()(小时、分钟、秒)和HAL_RTC_GetDate()(年、月、日和星期)把RTC_CNT转换成日历值。简化结构图四硬件接线图VBAT引脚接3.3V备用电源。RTC程序不使用按键RTC的外部低速晶振板子自带就有所以RTC晶振部分接线不用管。rtc外设引脚就是TAMPER-RTC引脚。五.CubeMX配置一个RTC走秒例程打开STM32CubeMX软件,新建工程Part Number处输入STM32F103C8再双击就创建新的工程配置下载口引脚配置外部晶振引脚,配置外部低频晶振引脚系统时钟源配置RTC时钟源配置配置RTC使能配置系统主频配置RTC时钟为外部32.768K配置工程文件名保存路径KEIL5工程输出方式生成工程用Keil5打开工程相关代码stm32f1xx_hal_rtc.h/** defgroup RTC_Automatic_Prediv_1_Second Automatic calculation of prediv for 1sec timebase */ #define RTC_AUTO_1_SECOND 0xFFFFFFFFU /** defgroup RTC_Input_parameter_format_definitions Input Parameter Format */ #define RTC_FORMAT_BIN 0x000000000U //十进制格式 #define RTC_FORMAT_BCD 0x000000001U //十六进制格式 /** defgroup RTC_output_source_to_output_on_the_Tamper_pin Output source to output on the Tamper pin 对 STM32 来说很多外设功能都复用在某些 GPIO 上。 比如USART TX/RX 复用在某些引脚 I2C/SPI 复用在某些引脚 RTC/TAMPER 也复用在某些引脚 */ #define RTC_OUTPUTSOURCE_NONE 0x00000000U /*! 不输出 */ #define RTC_OUTPUTSOURCE_CALIBCLOCK BKP_RTCCR_CCO /*! 输出校准时钟 */ #define RTC_OUTPUTSOURCE_ALARM BKP_RTCCR_ASOE /*! 输出报警信号 */ #define RTC_OUTPUTSOURCE_SECOND (BKP_RTCCR_ASOS | BKP_RTCCR_ASOE) /*! 输出秒脉冲 */rtc.c#include rtc.h RTC_HandleTypeDef hrtc; /* RTC init function */ void MX_RTC_Init(void) { RTC_TimeTypeDef sTime {0}; RTC_DateTypeDef DateToUpdate {0}; /** Initialize RTC Only */ hrtc.Instance RTC; hrtc.Init.AsynchPrediv RTC_AUTO_1_SECOND; //让 RTC 自动按 1 秒为时间基准 hrtc.Init.OutPut RTC_OUTPUTSOURCE_ALARM; // RTC 的报警信号路由到 RTC/TAMPER 复用输出脚上 if (HAL_RTC_Init(hrtc) ! HAL_OK) { Error_Handler(); } /* USER CODE BEGIN Check_RTC_BKUP */ /* USER CODE END Check_RTC_BKUP */ /** 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 0x21; if (HAL_RTC_SetDate(hrtc, DateToUpdate, RTC_FORMAT_BCD) ! HAL_OK) { Error_Handler(); } } void HAL_RTC_MspInit(RTC_HandleTypeDef* rtcHandle) { if(rtcHandle-InstanceRTC) { /* USER CODE BEGIN RTC_MspInit 0 */ /* USER CODE END RTC_MspInit 0 */ //允许访问备份寄存器和 RTC HAL_PWR_EnableBkUpAccess(); /* 打开 BKP 时钟 */ __HAL_RCC_BKP_CLK_ENABLE(); /* 打开 RTC 时钟 */ __HAL_RCC_RTC_ENABLE(); /* USER CODE BEGIN RTC_MspInit 1 */ /* USER CODE END RTC_MspInit 1 */ } } void HAL_RTC_MspDeInit(RTC_HandleTypeDef* rtcHandle) { if(rtcHandle-InstanceRTC) { /* USER CODE BEGIN RTC_MspDeInit 0 */ /* USER CODE END RTC_MspDeInit 0 */ /* Peripheral clock disable */ __HAL_RCC_RTC_DISABLE(); /* USER CODE BEGIN RTC_MspDeInit 1 */ /* USER CODE END RTC_MspDeInit 1 */ } }stm32f1xx_hal_rtc.c这是rtc.c里面调用的hal库接口官方提供的底层驱动函数这里面才是真正配置数据手册中的各个外设寄存器。/** * brief Initializes the RTC peripheral * param hrtc pointer to a RTC_HandleTypeDef structure that contains * the configuration information for RTC. * retval HAL status */ HAL_StatusTypeDef HAL_RTC_Init(RTC_HandleTypeDef *hrtc) { uint32_t prescaler 0U; /* Check input parameters */ if (hrtc NULL) { return HAL_ERROR; } /* Check the parameters */ assert_param(IS_RTC_ALL_INSTANCE(hrtc-Instance)); assert_param(IS_RTC_CALIB_OUTPUT(hrtc-Init.OutPut)); assert_param(IS_RTC_ASYNCH_PREDIV(hrtc-Init.AsynchPrediv)); #if (USE_HAL_RTC_REGISTER_CALLBACKS 1) if (hrtc-State HAL_RTC_STATE_RESET) { /* Allocate lock resource and initialize it */ hrtc-Lock HAL_UNLOCKED; hrtc-AlarmAEventCallback HAL_RTC_AlarmAEventCallback; /* Legacy weak AlarmAEventCallback */ hrtc-Tamper1EventCallback HAL_RTCEx_Tamper1EventCallback; /* Legacy weak Tamper1EventCallback */ if (hrtc-MspInitCallback NULL) { hrtc-MspInitCallback HAL_RTC_MspInit; } /* Init the low level hardware */ hrtc-MspInitCallback(hrtc); if (hrtc-MspDeInitCallback NULL) { hrtc-MspDeInitCallback HAL_RTC_MspDeInit; } } #else if (hrtc-State HAL_RTC_STATE_RESET) { /* Allocate lock resource and initialize it */ hrtc-Lock HAL_UNLOCKED; /* Initialize RTC MSP */ HAL_RTC_MspInit(hrtc); } #endif /* (USE_HAL_RTC_REGISTER_CALLBACKS) */ /* Set RTC state */ hrtc-State HAL_RTC_STATE_BUSY; /* Waiting for synchro */ if (HAL_RTC_WaitForSynchro(hrtc) ! HAL_OK) { /* Set RTC state */ hrtc-State HAL_RTC_STATE_ERROR; return HAL_ERROR; } /* Set Initialization mode */ if (RTC_EnterInitMode(hrtc) ! HAL_OK) { /* Set RTC state */ hrtc-State HAL_RTC_STATE_ERROR; return HAL_ERROR; } else { /* Clear Flags Bits */ CLEAR_BIT(hrtc-Instance-CRL, (RTC_FLAG_OW | RTC_FLAG_ALRAF | RTC_FLAG_SEC)); if (hrtc-Init.OutPut ! RTC_OUTPUTSOURCE_NONE) { /* Disable the selected Tamper pin */ CLEAR_BIT(BKP-CR, BKP_CR_TPE); } /* Set the signal which will be routed to RTC Tamper pin*/ MODIFY_REG(BKP-RTCCR, (BKP_RTCCR_CCO | BKP_RTCCR_ASOE | BKP_RTCCR_ASOS), hrtc-Init.OutPut); if (hrtc-Init.AsynchPrediv ! RTC_AUTO_1_SECOND) { /* RTC Prescaler provided directly by end-user*/ prescaler hrtc-Init.AsynchPrediv; } else { /* RTC Prescaler will be automatically calculated to get 1 second timebase */ /* Get the RTCCLK frequency */ prescaler HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_RTC); /* Check that RTC clock is enabled*/ if (prescaler 0U) { /* Should not happen. Frequency is not available*/ hrtc-State HAL_RTC_STATE_ERROR; return HAL_ERROR; } else { /* RTC period RTCCLK/(RTC_PR 1) */ prescaler prescaler - 1U; } } /* Configure the RTC_PRLH / RTC_PRLL */ MODIFY_REG(hrtc-Instance-PRLH, RTC_PRLH_PRL, (prescaler 16U)); MODIFY_REG(hrtc-Instance-PRLL, RTC_PRLL_PRL, (prescaler RTC_PRLL_PRL)); /* Wait for synchro */ if (RTC_ExitInitMode(hrtc) ! HAL_OK) { hrtc-State HAL_RTC_STATE_ERROR; return HAL_ERROR; } /* Initialize date to 1st of January 2000 */ hrtc-DateToUpdate.Year 0x00U; hrtc-DateToUpdate.Month RTC_MONTH_JANUARY; hrtc-DateToUpdate.Date 0x01U; /* Set RTC state */ hrtc-State HAL_RTC_STATE_READY; return HAL_OK; } }main.cuint8_t aShowTime[16] hh:ms:ss;//定义时分秒格式 uint8_t aShowDate[16] dd-mm-yyyy;//定义年月日格式 //获取日期时间 static void RTC_CalendarShow(uint8_t *showtime, uint8_t *showdate) { RTC_DateTypeDef sdatestructureget; RTC_TimeTypeDef stimestructureget; /* Get the RTC current Time */ HAL_RTC_GetTime(hrtc, stimestructureget, RTC_FORMAT_BIN); //以十进制格式获取时间 /* Get the RTC current Date */ HAL_RTC_GetDate(hrtc, sdatestructureget, RTC_FORMAT_BIN); //以十进制格式获取日期 // /* Display time Format : hh:mm:ss */ sprintf((char *)showtime, %2d:%2d:%2d, stimestructureget.Hours, stimestructureget.Minutes, stimestructureget.Seconds); // /* Display date Format : mm-dd-yy */ sprintf((char *)showdate, %2d-%2d-%2d, sdatestructureget.Month, sdatestructureget.Date, 2000 sdatestructureget.Year); } /* USER CODE END 0 */ /** * brief The application entry point. * retval int */ int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_RTC_Init(); //RTC的初始化 /* USER CODE BEGIN 2 */ /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ RTC_CalendarShow(aShowTime, aShowDate);//读取时间日期保存到数组 } /* USER CODE END 3 */ }实验效果查看aShowTime数组能看到时间会一秒一秒走六.小结RTC 可以为嵌入式系统提供精确的时间基准用于各种定时任务和事件。RTC 可以用于智能家居设备中的时间控制如智能门锁、智能照明等。原文链接零基础STM32单片机编程入门(二十三) 内部RTC实时时钟详解及实战含源码_单片机rtc-CSDN博客