蓝桥杯国赛——第三站RTC秒中断

端午假期,周六上了一天实验课,晚上用来打游戏了。今天,周日,再继续搞,。

RTC中断相关:

1.目前看来,断电运行是不可能的,因为板子不带电源。不过说起来,想要实现断电运行,关键就是RTC备份域【BKP】能够在断电时保持数据,所以只要使能寄存器,正确读写就没问题。

从RTC 的定时器特性来说,它是一个32 位的计数器,只能向上计数。它使用的时钟源有三种,分别为高速外部时钟的128 分频(HSE/128)、低速内部时钟LSI 以及低速外部时钟LSE;使HSE分频时钟或LSI 的话,在主电源VDD 掉电的情况下,这两个时钟来源都会受到影响,因此没法保证RTC 正常工作。因此RTC 一般使用低速外部时钟LSE,在设计中,频率通常为实时时钟模块中常用的32.768KHz,这是因为32768 = 215,分频容易实现,所以它被广泛应用到RTC 模块。在主电源VDD 有效的情况下(待机),RTC 还可以配置闹钟事件使STM32 退出待机模式。

实际上,在阅读手册的时候,可以发现st对RTC读写时间和访问备份域设置了很多reg级别的约束条件,但是在直接使用Hal库编程时,很多东西被库函数屏蔽了(搞得我抓不清学习的重点),所以,库这个东西,有利有弊吧。

2.RTC秒中断和定时闹钟

hal库的delay函数会使得程序重心都放到delay上面,其他动作不会执行,而使用uwTick来比较的方式,就不会这样。但是如果在中断中sys定时时间到,理论上会先执行中断,之后再重新运行。那么这种时间上的冲突应该怎么避免呢?也就是,单片机执行多个程序,如果没有理解错的话,它作为单核单线程,应该不能同时调用cpu资源运行,所以它采取的应该是分时复用,或者索性直接在while循环里面顺序执行,用高速的80MHz乃至更高的频率来实现基本同时运行。故而应该不存在能够让冲突的程序严格按照时序运行的解决方案。

现在疑问的就是有没有相关文章介绍过这些?

谢邀,一般单片机一般只有一个核心,做多线程实际上是分时复用。

谢邀,一般单片机一般只有一个核心,做多线程实际上是分时复用。自己做的话可以写个循环,循环里面多个进程轮流执行。
更方便一点是用嵌入式操作系统,如ucos freertos vxworks 等操作系统做,里面自带任务调度及任务间通信等功能。
里面的任务调度可以按照时间片轮训,优先级抢占等方式,比你自己搞省事多了。 觉得不错请按赞XD! 作者:东东bh 链接:https://www.zhihu.com/question/323241954/answer/674988619 来源:知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

仿佛从中看到了操作系统以及嵌入式更有趣的前沿,可能单片机还是太入门了吧。haha

那么,我目前就先采用这种解决方案吧,尽可能使得分时复用之间的时间冲突变小。如果你在疑问我为什么在RTC这里思考这些,往下面看吧//。

实现RTC的秒中断【分钟中断等同理】

有关秒中断的原理,简单概述,大家感兴趣可以康康其他文章。有相关基础的想迫切知道其中有没有什么坑的可以继续看下去。

复制代码
42.4.1 等待时钟同步和操作完成
RTC 区域的时钟比APB 时钟慢,访问前需要进行时钟同步,只要调用库函数RTC_WaitForSynchro即可,而如果修改了RTC 的寄存器,又需要调用RTC_WaitForLastTask 函数确保数据已写入。
42.4.2 使能备份域说及RTC 配置
默认情况下,RTC 所属的备份域禁止访问,可使用库函数PWR_BackupAccessCmd 使能访问,该函数通过PWR_CR 寄存器的DBP 位使能访问,使能后才可以访问RTC 相关的寄存器,然而若
希望修改RTC 的寄存器,还需要进一步使能RTC 控制寄存器的CNF 位使能寄存器配置。
42.4.3 设置RTC 时钟分频
42.4.4 设置、获取RTC 计数器及闹钟
tip:配置标志位用于上电继续运行
检测备份域寄存器RTC_BKP_DRX 内的值是否等于RTC_BKP_DATA 而分成两个分支。

//*摘自火哥的《零死角玩转stm32》
在我刚接触单片机时,火哥的固件库的参考书目有莫大作用。它们编写的真的不错。
复制代码

【目的:实现秒中断,每次中断通过串口发送当前时间】

CubeMX配置

 说明:

cubemx基本把调用RTC初始化和闹钟(中断)的配置都完成了。

tip:wakeup(唤醒)中断暂时不想探究

等待时钟同步不需要额外配置,库函数基本能自己完成【推测,我没有详细读相关的代码哈】

其中active Clock Source 和 Active Calendar基本对应于使能备份域;

Data Format建议就采用binary,虽然bcd也能用,但是似乎bcd编码格式在之后获取时间然后再人为递增时候有点麻烦。下面贴一个bcd格式在库里面的写法【库还提供了一个函数转换rtc的binary和bcd】

 

 下面,想要实现秒中断就要mask一些不关注的东西:

 

 

 屏蔽day、hour、minute,这样就只关注second,在second达到条件就中断,实现不管是什么分钟、小时,都能有秒中断。

finished。

代码

复制代码
/**************Cube自动生成的RTC代码*****************/
/* Includes ------------------------------------------------------------------*/
#include "rtc.h"

/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

RTC_HandleTypeDef hrtc;

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

  /* USER CODE BEGIN RTC_Init 0 */

  /* USER CODE END RTC_Init 0 */

  RTC_TimeTypeDef sTime = {0};
  RTC_DateTypeDef sDate = {0};
  RTC_AlarmTypeDef sAlarm = {0};

  /* USER CODE BEGIN RTC_Init 1 */

  /* USER CODE END RTC_Init 1 */
  /** Initialize RTC Only
  */
  hrtc.Instance = RTC;
  hrtc.Init.HourFormat = RTC_HOURFORMAT_24;
  hrtc.Init.AsynchPrediv = 124;
  hrtc.Init.SynchPrediv = 5999;
  hrtc.Init.OutPut = RTC_OUTPUT_DISABLE;
  hrtc.Init.OutPutRemap = RTC_OUTPUT_REMAP_NONE;
  hrtc.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
  hrtc.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
  hrtc.Init.OutPutPullUp = RTC_OUTPUT_PULLUP_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 = 1;
  sTime.Minutes = 1;
  sTime.Seconds = 50;
  sTime.SubSeconds = 0;
  sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
  sTime.StoreOperation = RTC_STOREOPERATION_RESET;
  if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN) != HAL_OK)
  {
    Error_Handler();
  }
  sDate.WeekDay = RTC_WEEKDAY_SUNDAY;
  sDate.Month = RTC_MONTH_JUNE;
  sDate.Date = 5;
  sDate.Year = 22;
  if (HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BIN) != HAL_OK)
  {
    Error_Handler();
  }
  /** Enable the Alarm A
  */
  sAlarm.AlarmTime.Hours = 1;
  sAlarm.AlarmTime.Minutes = 1;
  sAlarm.AlarmTime.Seconds = 55;
  sAlarm.AlarmTime.SubSeconds = 0;
  sAlarm.AlarmMask = RTC_ALARMMASK_DATEWEEKDAY|RTC_ALARMMASK_HOURS
                              |RTC_ALARMMASK_MINUTES;
  sAlarm.AlarmSubSecondMask = RTC_ALARMSUBSECONDMASK_ALL;
  sAlarm.AlarmDateWeekDaySel = RTC_ALARMDATEWEEKDAYSEL_DATE;
  sAlarm.AlarmDateWeekDay = 1;
  sAlarm.Alarm = RTC_ALARM_A;
  if (HAL_RTC_SetAlarm_IT(&hrtc, &sAlarm, RTC_FORMAT_BIN) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN RTC_Init 2 */

  /* USER CODE END RTC_Init 2 */

}

void HAL_RTC_MspInit(RTC_HandleTypeDef* rtcHandle)
{

  RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
  if(rtcHandle->Instance==RTC)
  {
  /* USER CODE BEGIN RTC_MspInit 0 */

  /* USER CODE END RTC_MspInit 0 */
  /** Initializes the peripherals clocks
  */
    PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_RTC;
    PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_HSE_DIV32;

    if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
    {
      Error_Handler();
    }

    /* RTC clock enable */
    __HAL_RCC_RTC_ENABLE();
    __HAL_RCC_RTCAPB_CLK_ENABLE();

    /* RTC interrupt Init */
    HAL_NVIC_SetPriority(RTC_Alarm_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(RTC_Alarm_IRQn);
  /* USER CODE BEGIN RTC_MspInit 1 */

  /* USER CODE END RTC_MspInit 1 */
  }
}

void HAL_RTC_MspDeInit(RTC_HandleTypeDef* rtcHandle)
{

  if(rtcHandle->Instance==RTC)
  {
  /* USER CODE BEGIN RTC_MspDeInit 0 */

  /* USER CODE END RTC_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_RTC_DISABLE();
    __HAL_RCC_RTCAPB_CLK_DISABLE();

    /* RTC interrupt Deinit */
    HAL_NVIC_DisableIRQ(RTC_Alarm_IRQn);
  /* USER CODE BEGIN RTC_MspDeInit 1 */

  /* USER CODE END RTC_MspDeInit 1 */
  }
}

/* USER CODE BEGIN 1 */

/* USER CODE END 1 */
复制代码

手敲大部分的main代码:

复制代码
/************* 手敲大部分的main代码:***********/
/***************不重要的就删了***************/

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "rtc.h"
#include "usart.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
RTC_TimeTypeDef Time_RTC;
RTC_DateTypeDef Date_RTC;
RTC_AlarmTypeDef Alarm_RTC;
void Uart_Proc(void);
void SystemClock_Config(void);
/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  HAL_Init();
  SystemClock_Config();

  MX_GPIO_Init();
  MX_RTC_Init();
    
  MX_USART1_UART_Init();
  HAL_UART_Receive_IT(&huart1,&rx_data,1);
  while (1)
  {
        Uart_Proc();
  }
}



void Uart_Proc(void)
{
    sprintf((char *)tx_buff,"Hello\n");//*似乎是这个sprintf特别占用系统时间,使得秒中断没有立刻进入
    //*不对,sprintf屏蔽之后还是两秒进入一次中断
    //*因此,我担心的是程序执行其他时候,会不会也打断中断,无法实现秒中断。因此有必要在完整的模板程序中试验
    //*又或者,只是因为一个时刻占用了两词transmit导致的?
    //*又或者,是sys中断优先级高于秒中断优先级导致的?但是如果秒中断优先级高于sys,那么系统其他的程序赖以依靠的sys时钟就不准确了
    
    //改变rtc优先级更高的时候,能够做到即时走进秒中断,但是明显对uart两秒钟传输一次的精度有影响;
    //我能想到最优的方案就是中断服务函数中尽量少的程序,然后把执行程序提出来让他在sys的协调下运行
    HAL_UART_Transmit(&huart1,tx_buff,strlen(tx_buff),50);
}
void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
{
   //*获取当前的闹钟时间设置情况 HAL_RTC_GetAlarm(hrtc,
&Alarm_RTC, RTC_ALARM_A, RTC_FORMAT_BIN); //*设置下一次闹钟的时间 if(Alarm_RTC.AlarmTime.Seconds != 59) Alarm_RTC.AlarmTime.Seconds= Alarm_RTC.AlarmTime.Seconds+1; else Alarm_RTC.AlarmTime.Seconds = 0; //*发送当前的时间给串口 HAL_RTC_GetTime(hrtc,&Time_RTC,RTC_FORMAT_BIN); HAL_RTC_GetDate(hrtc,&Date_RTC,RTC_FORMAT_BIN); sprintf(i_disp,"RTC:%02d-%02d-%02d\n",Time_RTC.Hours,Time_RTC.Minutes,Time_RTC.Seconds); HAL_UART_Transmit(&huart1,(uint8_t *)i_disp,strlen(i_disp),50); //*重新使能闹钟中断 HAL_RTC_SetAlarm_IT(hrtc, &Alarm_RTC, RTC_FORMAT_BIN); }
复制代码

问题:

在不当的设置条件下,明明我设置的是1s一次中断,但是发给串口却是两秒一次;

测试之后感觉有很多可能的原因,最后发现应该是秒中断的优先级不够,导致只有程序跳到执行语句(也就是不在systick的中断中时,才能响应rtc_alarm的中断)。

此时,问题很明显,怎么保证其他程序时序能正常运行呢?

一、sys中断不要那么高,尤其是在我的程序编写习惯下,sys的中断用来调节所有程序的进程,完全可以等一个程序运行ok了再考虑进入另一个外设的进程;

二、外设中断的服务函数不要执行太多任务,这样长期处于中断中,程序停摆了相当于。可以把任务放到外面,服务函数只留标志位。

回看火哥的书,才发现也是这样做的:RTC 的秒中断服务函数只是简单地对全局变量TimeDisplay 置1,在main 函数的while 循环中会检测这个标志,当标志为1 时,就调用Time_Display 函数显示一次时间,达到每秒钟更新当前时间的效果。——摘自P1012

最终结果:

 

参考:

(6条消息) stm32 RTC时钟配置_HardessGod的博客-CSDN博客_rtc时钟设置

(6条消息) STM32F030R8Tx HAL库实现RTC 1秒中断_仙剑情缘的博客-CSDN博客_hal库rtc秒中断

《野火——零死角玩转STM32》

posted @   charonplus  阅读(556)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
点击右上角即可分享
微信分享提示