【STM32】HAL库-RTC实时时钟

简介

实时时钟是一个独立的定时器

RTC模块和时钟配置系统(RCC_BDCR寄存器)处于后备区域,即在系统复位或从待机模式唤醒后,RTC的设置和时间维持不变

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

  • 设置寄存器RCC_APB1ENR的PWREN和BKPEN位,使能电源和后备接口时钟
  • 设置寄存器PWR_CR的DBP位,使能对后备寄存器和RTC的访问。

时钟源

通过设置备份域控制寄存器(RCC_BDCR)里的RTCSEL[1:0]位,RTCCLK时钟源可以由HSE/128、LSE或LSI时钟提供。
除非备份域复位,此选择不能被改变。

LSE由VDD或者VBAT供电。
LSI和HSE/128由VDD供电。
LSI可以在睡眠,停机和待机模式下保持运行

在这里插入图片描述

中断

  • 闹钟中断,用来产生一个软件可编程的闹钟中断。
  • 秒中断,用来产生一个可编程的周期性中断信号(最长可达1秒)。
  • 溢出中断,指示内部可编程计数器溢出并回转为0的状态。

在这里插入图片描述
在待机模式下了RTC_PRL、RTC_ALR、RTC_CNT和RTC_DIV寄存器维持供电,且仅能通过备份域复位信号复位。

读RTC寄存器

在这里插入图片描述

写RTC寄存器

在这里插入图片描述

RTC寄存器配置过程

一般是用LSE低速外部时钟作为RTC时钟源

  • 使能对后备寄存器和RTC的访问

    • 设置寄存器RCC_APB1ENR的PWREN和BKPEN位,使能电源和后备接口时钟
    • 设置寄存器PWR_CR的DBP位,使能对后备寄存器和RTC的访问。
  • 使能RTC的时钟,并选择RTC的时钟源
    寄存器RCC_BDCR的RTCEN位和RTCSEL[1:0]位(一旦RTC时钟源被选定,直到下次后备域被复位,它不能在被改变)

  • 检查RTC寄存器同步标志
    先将寄存器RTC_CRL的RSF写入0,再读取寄存器RTC_CRL的RSF是否被置位

  • 进入配置模式
    检查寄存器RTC_CRL的RTOFF是否被置位,被置位后将寄存器RTC_CRL的CNF位置位,RTC进入配置模式。

  • 配置RTC中断与输出
    根据需要配置RTC的输出(闹钟或者秒输出或者校准输出),寄存器BKP_RTCCR的 ASOS位,ASOE位,CCO位
    RTC中断配置 寄存器RTC_CRH的OWIE位 ALRIE位 SECIE位

  • 写入RTC预分配器与当前计数值
    预分配器寄存器RTC_PRLH/L,RTC计数器寄存器 (RTC_CNTH / RTC_CNTL)

  • 设置RTC闹钟
    RTC闹钟寄存器(RTC_ALRH/RTC_ALRL)

  • 退出配置模式
    将寄存器RTC_CRL的CNF位复位,RTC退出配置模式。

demo

RTC实数时钟-使用C库函数time.h-外部时钟LSE-待机模式

采用STM32F103C8T6单片机,KeilMDK5.32版本

使用LSE作为时钟源
采用time.h库函数,可通过串口助手上位机修改RTC当前计数值
串口与上位机进行通信,串口发送设置为DMA单次模式发送(仿printf)
串口接收设置为DMA循环串口空闲接收,接收到用户数据并修改RTC CNT寄存器后进入待机模式
通过PA0来唤醒单片机
PC13控制LED灯,LED亮灭指示程序正在运行

STM32CubeMX配置
未开启RTC全局中断
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

修改生成的rtc.c文件MX_RTC_Init()
通过一个宏来确认RTC是否已经初始化完毕(时钟源的选择,预分频器,闹钟等等)。

/* USER CODE BEGIN 0 */
#define RTC_INIT	1	/* 1 RTC已初始化	0 RTC未初始化 */
/* USER CODE END 0 */

RTC_HandleTypeDef hrtc;

/* RTC init function */
void MX_RTC_Init(void)
{

  /* USER CODE BEGIN RTC_Init 0 */
#if (RTC_INIT == 1)
	hrtc.Instance = RTC;
	hrtc.Init.AsynchPrediv = RTC_AUTO_1_SECOND;
	hrtc.Init.OutPut = RTC_OUTPUTSOURCE_NONE;
	/* 使能对后备寄存器和RTC的访问 */
	__HAL_RCC_BKP_CLK_ENABLE();
	__HAL_RCC_PWR_CLK_ENABLE();
	HAL_PWR_EnableBkUpAccess();
	/* 等待同步 */
	if (HAL_RTC_WaitForSynchro(&hrtc) != HAL_OK)
	{
		/* Set RTC state */
		hrtc.State = HAL_RTC_STATE_ERROR;
	}
	else
	{
		hrtc.State = HAL_RTC_STATE_READY;
	}
#else
  /* USER CODE END RTC_Init 0 */

  RTC_TimeTypeDef sTime = {0};
  RTC_DateTypeDef DateToUpdate = {0};

  /* USER CODE BEGIN RTC_Init 1 */

  /* 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();
  }

  /* 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 = 0x0;

  if (HAL_RTC_SetDate(&hrtc, &DateToUpdate, RTC_FORMAT_BCD) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN RTC_Init 2 */
#endif /* RTC_INIT */
  /* USER CODE END RTC_Init 2 */

实现time.h库中time()函数
通过读取RTC当前计数值寄存器获取系统秒数

/**
  * @brief  获取当前系统秒数
  * @param  _timer 指向存放当前系统秒数变量的地址
  * @retval 当前系统秒数
  */
time_t time (time_t *_timer)
{
	uint32_t timecounter = 0U;
	uint16_t high1 = 0U, high2 = 0U, low = 0U;
	
	/* read CNT register  */
	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);
	}

    if(_timer != NULL)
    {
		*_timer = timecounter;
    }
	
    return timecounter;
}

串口空闲接收回调函数
接收来自用户自定义的系统秒数,并在main修改RTC CNT寄存器
重置DMA剩余传输数据

extern uint8_t receiveBuff[15];
extern uint32_t timer;
extern uint8_t flag;
/**
  * @brief  Reception Event Callback (Rx event notification called after use of advanced reception service).
  * @param  huart UART handle
  * @param  Size  Number of data available in application reception buffer (indicates a position in
  *               reception buffer until which, data are available)
  * @retval None
  */
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
	if(huart == &huart1)
	{
		__HAL_UART_CLEAR_IDLEFLAG(huart);//清除串口空闲标志位
		__HAL_DMA_DISABLE(huart->hdmarx);//失能DMA
		WRITE_REG(huart->hdmarx->Instance->CNDTR, sizeof(receiveBuff));//修改DMA数据传输数量
		__HAL_DMA_ENABLE(huart->hdmarx);//使能DMA
		flag = 1;//置位标志位
		/* 获取用户设置的秒值 */
		timer = (uint32_t)receiveBuff[0] << 24 |
				(uint32_t)receiveBuff[1] << 16 |
				(uint32_t)receiveBuff[2] << 8  |
				(uint32_t)receiveBuff[3];
		
	}
}

在main()中修改系统秒数和串口打印当前时间

while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
	HAL_Delay(1000);
	/* 获取时间并输出时间 */
	time(&timeCount);//秒值
	localtime_r(&timeCount, &timeNow);//返回的是格林尼治时间  中国时间比其快8小时 
	USARTPrintf(&huart1, "%hu/%hhu/%hhu %hhu:%02hhu:%02hhu  %zu\r\n",
				timeNow.tm_year + 1900, timeNow.tm_mon + 1, timeNow.tm_mday,
				timeNow.tm_hour + 8, timeNow.tm_min, timeNow.tm_sec, timeCount);//输出时间
	/* 修改当前秒值 */
	if(flag == 1)
	{
		flag = 0;
		RTC_WriteTimeCounter(&hrtc, timer);
		SET_BIT(PWR->CR, PWR_CR_CWUF_Msk);//写1清除该位 唤醒位
		SET_BIT(PWR->CR, PWR_CR_CSBF_Msk);//写1清除该位 待机位
		HAL_PWR_EnterSTANDBYMode();//进入待机模式
	}
	PCout(13) = !PCin(13);//;LED亮灭翻转

使能WKUP引脚(在STM32CubeMX中配置无效)

HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN1);//强制使能WKUP(PA0)引脚

工程文件下载链接

RTC实数时钟-使用C库函数time.h-内部时钟LSI-待机模式

采用STM32F103C8T6单片机,KeilMDK5.32版本

使用LSI作为时钟源
采用time.h库函数,可通过串口助手上位机修改RTC当前计数值
串口与上位机进行通信,串口发送设置为DMA单次模式发送(仿printf)
串口接收设置为DMA循环串口空闲接收,接收到用户数据并修改RTC CNT寄存器后进入待机模式
通过PA0来唤醒单片机
PC13控制LED灯,LED亮灭指示程序正在运行

STM32CubeMX配置
在这里插入图片描述
其他配置与上面的工程一样

工程文件下载链接

调试发现 LSI 过快 LSE过慢。

低功耗模式下的自动唤醒(AWU)

在这里插入图片描述

RTC实数时钟-使用C库函数time.h-外部时钟LSE-周期性唤醒

采用STM32F103C8T6单片机,KeilMDK5.32版本

使用LSE作为时钟源
采用time.h库函数,可通过串口助手上位机修改RTC当前计数值
串口与上位机进行通信,串口发送设置为DMA单次模式发送(仿printf)
串口接收设置为DMA循环串口空闲接收,接收到用户数据并修改RTC CNT寄存器后进入待机模式(唤醒后等于复位,除了备份寄存器和RTC部分寄存器和电源控制/状态寄存器(PWR_CSR)没有被复位外,其他寄存器被复位。)
通过PA0来唤醒单片机或者RTC闹钟事件唤醒单片机
配置外部事件线17为上升沿触发
没有使用RTC的输出模式(秒/闹钟/校准输出到引脚PC13)故PC13还是可以正常使用
PC13控制LED灯,LED亮灭指示程序正在运行

为了简便起见,在每次串口接收到用户数据后,修改RTC的CNT寄存器后将RTC的闹钟寄存器的值加上10,也是就每次10秒后唤醒单片机。
本次实验在进入待机模式前设置闹钟每10秒唤醒一次

在这里插入图片描述
配置外部线事件17
在这里插入图片描述
在这里插入图片描述

STM32CubeMX配置
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
工程文件下载链接

  • 7
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值