细说STM32F407单片机RTC的备份寄存器原理及使用方法

news2025/2/15 11:29:12

目录

一、备份寄存器的功能

二、示例功能

三、项目设置

1、晶振、DEBUG、CodeGenerator、USART6

2、RTC

3、NVIC 

4、GPIO 及KEYLED

四、软件设计

1、main.h

2、main.c

3、rtc.c

4、keyled.c、keyled.h

五、运行调试


        本实例旨在介绍备份寄存器的作用。本实例继续使用旺宝红龙开发板STM32F407ZGT6 KIT V1.0。本实例将引用本文作者写的其他文章作为参考文献。

        参考项目:细说STM32F407单片机RTC的基本原理及闹钟和周期唤醒功能的使用方法-CSDN博客  https://wenchm.blog.csdn.net/article/details/145575366

一、备份寄存器的功能

        STM32F407的RTC有20个32位的备份寄存器,寄存器名称为RTC_BKP0R~RTC_BKP19R。这些备份寄存器由备用电源VBAT供电,在系统复位或主电源关闭时,只要VBAT有电,备份寄存器的内容就不会丢失。所以,备份寄存器可以用来存储一些用户数据

        文件stm32f4xx_hal_rtc_ex.h中有读写备份寄存器的功能函数,其原型定义如下:

uint32_t HAL_RTCEx_BKUPRead(RTC_HandleTypeDef *hrtc,uint32_t BackupReg);
void HAL_RTCEx_BKUPWrite(RTC_HandleTypeDef *hrtc,uint32_t BackupReg,uint32_t Data);

        其中,参数BackupReg是备份寄存器编号,文stm32f4xx_hal_rtc_ex.h定义了20个备份寄存器编号的宏,定义如下:

#define RTC_BKP_DR0		0x00000000U
#define RTC_BKP_DR1		0x00000001U
/*省略了中间的定义代码*/
#define RTC_BKP_DR18	0x00000012U
#define RTC_BKP_DR19	0x00000013U

        RTC也可以由VBAT供电,在系统复位或主电源关闭时,RTC的日历不受影响。但是在参考项目,因为在系统复位时调用了RTC初始化函数MX_RTC_Init(),而这个函数总是设置RTC的日期和时间,所以复位后,RTC的日期时间又从CubeMX里设置的初始日期时间开始了。

        可以使用备份寄存器对参考项目进行修改,使得系统在复位时不再自动设置RTC的初始日期时间,而是由备份寄存器RTC_BKP_DR0的内容决定,而且可以把RTC当前时间保存到备份寄存器。

二、示例功能

        本实例,将设计一个示例,演示备份寄存器的使用。示例具有如下的功能和操作流程。

  • 在RTC初始化函数MX_RTC_Init()中增加代码,读取备份寄存器RTC_BKP_DR0的内容,如果值为0,就设置RTC为初始时间和日期,如果值为1,就不设置RTC的日期和时间。
  • 在程序运行时,可以修改保存到备份寄存器RTC_BKP_DR0的值,可以保存0或1。
  • 将RTC当前时间的时、分、秒数据保存到RTC_BKP_DR2到RTC_BKP_DR4的3个寄存器里。RTC_BKP_DR1里的值为1时,表示保存了RTC时间。
  • 读取备份寄存器RTC_BKP_DR0到RTC_BKP_DR4的内容,显示到串口助手上。
  • 使用RTC周期唤醒中断,唤醒中断周期1s,在唤醒中断里读取时间后在串口助手上显示。本示例还用到4个按键和2个LED,所以继续引用作者发布的其它参考项目中的KEYLED文件中的程序文件。
  • 菜单设计:
[S2]KeyUp   = Reset RTC time on startup.    //按下S2键,用初始时间重置RTC
[S3]KeyDown = Read BKUP registers.          //按下S3键,读取备份寄存器
[S4]KeyLeft = Save current time to BKUP.    //按下S4键,把当前时间保存到RTC备份寄存器
[S5]KeyRight= Change RTC time from BKUP.    //按下S5键,用备份寄存器存储的时间重置RTC时间

三、项目设置

1、晶振、DEBUG、CodeGenerator、USART6

        与参考文章相同。 

2、RTC

        在RTC的模式设置中,启用时钟源和日历,设置WakeUp模式为Internal WakeUp。在参数设置部分,设置数据格式为Binary data format,设置唤醒时钟为1Hz信号,唤醒周期数为0,这样就会每秒产生一次唤醒中断。

3、NVIC 

        在NVIC中开启周期唤醒中断,设置RTC抢占优先级为1。

4、GPIO 及KEYLED

        4个按键和2个LED。引用文件夹KEYLED的方法请参考本文作者发布的其它文章。

程序标签

板上名称

引脚名称

引脚功能

GPIO模式

默认电平

上拉或下拉

LED1

D1

PA6

GPIO_Output

推挽输出

High

上拉

LED2

D2

PA4

GPIO_Output

推挽输出

High

上拉

KeyRight

S5

PF6

GPIO_Input

输入

上拉

KeyDown

S3

PD3

GPIO_Input

输入

上拉

KeyLeft

S4

PF7

GPIO_Input

输入

上拉

KeyUp

S2

PA0

GPIO Input

输入

上拉

 

四、软件设计

1、main.h

/* USER CODE BEGIN Private defines */
void RTC_ToggleReset();
void RTC_SaveToBKUP();
void RTC_LoadFromBKUP();
void RTC_ReadBKUP();
/* USER CODE END Private defines */

2、main.c

/* USER CODE BEGIN Includes */
#include "keyled.h"
#include <stdio.h>
/* USER CODE END Includes */
/* USER CODE BEGIN PV */
RTC_TimeTypeDef sTime;		//RTC读取时间和日期的结果数据
RTC_DateTypeDef sDate;

uint8_t RTC_isReading=0;	//RTC是否正在读日期时间数据
/* USER CODE END PV */
/* USER CODE BEGIN 2 */
  __HAL_RTC_WAKEUPTIMER_DISABLE(&hrtc); //禁止RTC周期唤醒

  printf("Demo11_2_RTC_Backup:Using Backup Registers.\r\n");
  uint32_t iniRTC = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR0);	//读取备份寄存DR0

  printf("Reset RTC time on startup: ");
  if ((iniRTC & 0x01)==0)
	  printf("Yes.\r\n");
  else
	  printf("No.\r\n");
  /* USER CODE END 2 */
 /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  //显示菜单,也可以放在USER CODE BEGIN 2里
  printf("[S2]KeyUp   = Reset RTC time on startup(LED2_ON).\r\n");
  printf("[S3]KeyDown = Read BKUP registers.\r\n");
  printf("[S4]KeyLeft = Save current time to BKUP.\r\n");
  printf("[S5]KeyRight= Change RTC time from BKUP.\r\n\r\n");

  __HAL_RTC_WAKEUPTIMER_ENABLE(&hrtc);	//重启RTC周期唤醒
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
	  KEYS curKey=ScanPressedKey(KEY_WAIT_ALWAYS);
	  switch(curKey)
	  {
	  case KEY_UP:		//[S2]KeyUp,改变保存到备份寄存器DR0的数值0 or 1
		  RTC_ToggleReset();
		  break;

	  case KEY_DOWN:	//[S3]KeyDown,读取5个备份寄存器的数据并显示
		  RTC_ReadBKUP();

	  case KEY_LEFT:	//[S4]KeyLeft,将RTC当前时间保存到备份寄存器
		  RTC_SaveToBKUP();
		  break;

	  case KEY_RIGHT:	//[S5]KeyRight,从备份寄存器读取时间,改变RTC时间
		  RTC_LoadFromBKUP();
		  break;

	  default:
	  	  break;
	  }
	  HAL_Delay(300);	//消抖
  }
  /* USER CODE END 3 */

        main()函数里在完成了外设初始化之后,执行__HAL_RTC_WAKEUPTIMER_DISABLE (&hrtc),禁止了RTC周期唤醒单元。在进入while死循环之前,再开启RTC的周期唤醒单元。

        调用函数HAL_RTCEx_BKUPRead()读取备份寄存器RTC_BKP_DR0的内容,如果寄存器的值为0就会在MX_RTC_Init()里用CubeMX设置的初始日期和时间设置RTC的日期时间;如果寄存器的值不为0在MX_RTC_Init()里就不会设置RTC的日期和时间。后一种情况下,在按复位键[S6]使系统复位时,RTC仍然保持连续的时间,因为RTC工作在备份域,不会在系统复位时复位。

        然后,在串口助手上显示了一个模拟菜单。

        在while循环里读取按键输入,用4个按键模拟菜单项的选择,按下某个按键后就执行与菜单项对应的功能。按键的响应代码封装为4个函数。

/* USER CODE BEGIN 4 */
//周期唤醒中断回调函数
void HAL_RTCEx_WakeUpTimerEventCallback(RTC_HandleTypeDef *hrtc)
{
	LED1_Toggle();

	RTC_isReading = 1;		//RTC正在读取数据,该变量的作用类似于RTOS中的二值信号量
	if (HAL_RTC_GetTime(hrtc, &sTime, RTC_FORMAT_BIN) == HAL_OK)
	{
		//调用HAL_RTC_GetTime()之后必须调用HAL_RTC_GetDate()以解锁数据,才能连续更新日期和时间
		HAL_RTC_GetDate(hrtc, &sDate, RTC_FORMAT_BIN);
		RTC_isReading = 0;	//RTC结束了读取数据

		//显示时间hh:mm:ss
		char str[40];
		sprintf(str,"RTC Time = %2d:%2d:%2d",sTime.Hours,sTime.Minutes,sTime.Seconds);
		printf(" %s\r\n",str);
	}
}

/* 翻转保存备份寄存器RTC_BKP_DR0的值 */
void RTC_ToggleReset()	//S2
{
	uint32_t iniRTC = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR0);
	iniRTC = !iniRTC;
	if ((iniRTC & 0x01)==0){ //存储值为0时,用CubeMX中的值复位日期时间
		printf("Reset RTC time on startup.\r\n");
		LED2_ON();
	}
	else{
		printf("Not reset RTC time on startup.\r\n");
		LED2_OFF();
	}

	//__HAL_RTC_WRITEPROTECTION_DISABLE(&hrtc);		//取消写保护
	HAL_RTCEx_BKUPWrite(&hrtc,RTC_BKP_DR0, iniRTC);	//写入备份寄存器DR0
	//__HAL_RTC_WRITEPROTECTION_ENABLE(&hrtc);		//开启写保护
}

/* RTC时间保存到备份寄存器 */
void RTC_SaveToBKUP()	//S4
{
	while (RTC_isReading)	//如果RTC的WakeUp中断里正在读取数据,就等待其读取完
		HAL_Delay(1);

	//__HAL_RTC_WRITEPROTECTION_DISABLE(&hrtc);				//取消写保护
	HAL_RTCEx_BKUPWrite(&hrtc,RTC_BKP_DR1, 0x01);			//0x01=保存了时间的标志
	HAL_RTCEx_BKUPWrite(&hrtc,RTC_BKP_DR2, sTime.Hours);	//使用全局变量sTime,不再读取当前时间
	HAL_RTCEx_BKUPWrite(&hrtc,RTC_BKP_DR3, sTime.Minutes);
	HAL_RTCEx_BKUPWrite(&hrtc,RTC_BKP_DR4, sTime.Seconds);
	//__HAL_RTC_WRITEPROTECTION_ENABLE(&hrtc);				//开启写保护

	char timeStr[30];
	sprintf(timeStr,"%2d:%2d:%2d",sTime.Hours,sTime.Minutes,sTime.Seconds);	//转换为字符串,自动添加"\0"
	printf("Current time %s is saved in BKUP.\r\n",timeStr);
}

/* 用保存的时间设置为RTC时间 */
void RTC_LoadFromBKUP()	//S5
{
	uint32_t isTimeSaved = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR1);	//读取备份寄存DR1
	if (isTimeSaved)
	{
		RTC_TimeTypeDef time;

		time.Hours = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR2);
		time.Minutes = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR3);
		time.Seconds = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR4);
		time.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;		//这两个参数还是有用的,如果不设置这2个参数,设置的小时数会自动减1
		time.StoreOperation = RTC_STOREOPERATION_SET;

		//HAL_Delay(10);		//必须加这个延时,否则设置的时间会错乱
		while(RTC_isReading)	//如果RTC正在WakeUp中断里读取数据,就等待其读完
			HAL_Delay(1);

		/*函数HAL_RTC_SetTime()内部在修改RTC的寄存器之前,会调用 __HAL_RTC_WRITEPROTECTION_DISABLE()取消写保护,
		  写完之后,会调用 __HAL_RTC_WRITEPROTECTION_ENABLE() 重新开启写保护 */
		if (HAL_RTC_SetTime(&hrtc, &time, RTC_FORMAT_BIN) == HAL_OK)
			printf("Load and set BKUP time, success.\r\n");
		else
			printf("Load and set BKUP time, failure.\r\n");
	}
	else
		printf("No BKUP time is saved.\r\n");
}

/* 读取5个备份寄存器内容并显示 */
void RTC_ReadBKUP()	//S3
{
	uint32_t regValue;
	char regStr[40];

	regValue = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR0);	//0=系统复位时复位RTC时间
	sprintf(regStr,"Reset RTC ,BKP_DR0= %lu",regValue);	//转换为字符串,自动添加"\0"
	printf(" %s\r\n",regStr);

	regValue=HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR1);	//1=有时间数值
	sprintf(regStr,"Time is saved,BKP_DR1= %lu",regValue);
	printf(" %s\r\n",regStr);

	regValue=HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR2);	//Hour
	sprintf(regStr,"Saved time(Hour) ,BKP_DR2= %lu",regValue);
	printf(" %s\r\n",regStr);

	regValue=HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR3);	//Minute
	sprintf(regStr,"Saved time(Min) , BKP_DR3= %lu",regValue);
	printf(" %s\r\n",regStr);

	regValue=HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR4);	//Second
	sprintf(regStr,"Saved time(Sec) , BKP_DR4= %lu",regValue);
	printf(" %s\r\n",regStr);
}

//串口打印
int __io_putchar(int ch)
{
	HAL_UART_Transmit(&huart6,(uint8_t*)&ch,1,0xFFFF);
	return ch;
}
/* USER CODE END 4 */

        回调函数HAL_RTCEx_WakeUpTimerEventCallback()处理RTC周期唤醒中断。

        其余的4个函数,是对4个菜单项的响应代码的封装。

3、rtc.c

/* USER CODE BEGIN Check_RTC_BKUP */
  uint32_t iniRTC = HAL_RTCEx_BKUPRead(&hrtc,RTC_BKP_DR0);	//读取备份寄存DR0
  if ((iniRTC & 0x01))  //非零,无需初始化RTC日期时间
  {
	if (HAL_RTCEx_SetWakeUpTimer_IT(&hrtc,0,RTC_WAKEUPCLOCK_CK_SPRE_16BITS) != HAL_OK)
		Error_Handler();
	return;  //提前退出函数
  }
  /* USER CODE END Check_RTC_BKUP */

        沙箱中添加的代码就是用函数HAL_RTCEx_BKUPRead()读取备份寄存器RTC_BKP_DR0的内容,如果寄存器的值不为0,就在执行完HAL_RTCEx_SetWakeUpTimer_IT()设置和启动周期唤醒后,退出函数;如果寄存器的值为0,就继续执行函数内后面的代码。

        这样,就在函数MX_RTC_Init()中加入了用户功能代码,使得在系统复位时,RTC不必设置初始日期和时间

4、keyled.c、keyled.h

        引用KEYLED文件夹下的 keyled.c、keyled.h,使用方法请参考本文作者发布的其他文章。

五、运行调试

        首次下载,或按S6键复位,显示系统菜单。重要的信息是显示当前从哪里重置RTC时间的信息。如果YES(LED2亮)则从RTC初始化值(15:30:0)重置RTC时间,否则不是;

        按S2键切换从哪里重置RTC时间。比如,下图,每次按下S6复位键,系统都从RTC初始化值重置时间,否则不重置时间,即当LED2灯不亮(DR0=1)时,按下复位键S6,不改变RTC时间,连续显示RTC时间。

 

         按S4键,存储当前时间到备份寄存器;按S3键,读取备份存储器内容并保存当前最新值到备份寄存器;按S5键,用备份寄存器内容重置RTC时间;

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2298567.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

spring 学习 (注解)

目录 前言 常用的注解 须知 1 Conponent注解 demo&#xff08;案例&#xff09; 2 ControllerServiceRepository demo(案例&#xff09; 3 ScopeLazyPostConstructPreDestroy demo(案例&#xff09; 4 ValueAutowiredQualifierResource demo(案例&#xff09; 5 Co…

【Linux】多线程 -> 从线程概念到线程控制

线程概念 在一个程序里的一个执行路线就叫做线程&#xff08;thread&#xff09;。更准确的定义是&#xff1a;线程是“一个进程内部的控制序列”。一切进程至少都有一个执行线程。线程在进程内部运行&#xff0c;本质是在进程地址空间内运行。在Linux系统中&#xff0c;在CPU眼…

mapbox 从入门到精通 - 目录

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;mapbox 从入门到精通 文章目录 一、&#x1f340;总目录1.1 ☘️ mapbox基础1.2 ☘️…

深度学习在半导体领域的创新点研究

摘要&#xff1a;本论文聚焦于深度学习在半导体领域的创新应用&#xff0c;全面剖析其为半导体产业带来的变革与机遇。通过深入探究深度学习在半导体设计、制造、测试及质量管控等多方面的创新实践&#xff0c;揭示其对提升半导体性能、降低成本及增强产业竞争力的关键作用。同…

谈谈云计算、DeepSeek和哪吒

我不会硬蹭热点&#xff0c;去分析自己不擅长的跨专业内容&#xff0c;本文谈DeepSeek和哪吒&#xff0c;都是以这两个热点为引子&#xff0c;最终仍然在分析的云计算。 这只是个散文随笔&#xff0c;没有严谨的上下游关联关系&#xff0c;想到哪里就写到哪里。 “人心中的成见…

分享 UniApp 实现列表长按删除功能

在移动应用开发中&#xff0c;列表是常见的展示形式&#xff0c;而长按删除列表项也是一个实用且常见的交互功能。今天就来和大家分享如何在 UniApp 中实现列表的长按删除功能&#xff0c;同时附上详细的代码。 效果预览 通过代码实现后&#xff0c;我们将得到一个带有红色边…

k8s集群搭建参考(by lqw)

文章目录 声明配置yum源安装docker安装 kubeadm&#xff0c;kubelet 和 kubectl部署主节点其他节点加入集群安装网络插件 声明 由于看了几个k8s的教程&#xff0c;都存在各种问题&#xff0c;自己搭建的时候&#xff0c;踩了不少坑&#xff0c;最后还是靠百度csdnchatGPT才搭建…

「前端面试宝典」 - 猿媛之家(21.06)

模拟面试是提高个人沟通技巧的最有效方式 请记住&#xff1a;思维的深度&#xff0c;决定你人生的高度。胸怀的广度&#xff0c;决定你事业的长度。 面试官关注的重点不是题目的答案&#xff0c;而是求职者解题的思路与方法. 以排序算法为例&#xff1a;时间利用是否高效&#…

C++算法竞赛基础语法-9

快速排序是一种高效的排序算法&#xff0c;由C. A. R. Hoare在1960年提出&#xff0c;基本思想是分治法&#xff08;Divide and Conquer&#xff09;策略&#xff0c;通过递归将一个大问题分解为若干个较小的子问题&#xff0c;然后合并这些子问题的解来解决原始问题 快速排序…

Mac安装JD-GUI

Mac安装反编译工具步骤如下&#xff1a; 打开官网https://java-decompiler.github.io/ 选择下载mac的安装包解压下载好的压缩包&#xff0c;点击JD-GUI安装 有可能会遇到如下错误。请先检查是否安装JDK&#xff0c;通过java -version命令查看是否是1.8版本的jdk如果jdk没问题&…

Nginx--日志(介绍、配置、日志轮转)

前言&#xff1a;本博客仅作记录学习使用&#xff0c;部分图片出自网络&#xff0c;如有侵犯您的权益&#xff0c;请联系删除 一、Nginx日志介绍 nginx 有一个非常灵活的日志记录模式&#xff0c;每个级别的配置可以有各自独立的访问日志, 所需日志模块 ngx_http_log_module 的…

QML 快捷键与Shortcut的使用

一、效果展示 二、源码分享 import QtQuick import QtQuick.Controls import Qt.labs.qmlmodels import QtQuick.Controls.Basic import QtQuick.Layouts import QtQuick.Effects import Qt.labs.platformApplicationWindow {id:rootwidth: 1000height: 730visible: truetitle…

制造业物联网的十大用例

预计到 2026 年&#xff0c;物联网制造市场价值将达到 4000 亿美元。实时收集和分析来自联网物联网设备与传感器的数据&#xff0c;这一能力为制造商提供了对生产流程前所未有的深入洞察。物联网&#xff08;IoT&#xff09;有潜力彻底改变制造业&#xff0c;使工厂能够更高效地…

考研操作系统----操作系统的概念定义功能和目标(仅仅作为王道哔站课程讲义作用)

目录 操作系统的概念定义功能和目标 操作系统的四个特征 操作系统的分类 ​编辑 操作系统的运行机制 系统调用 操作系统体系结构 操作系统引导 虚拟机 操作系统的概念定义功能和目标 什么是操作系统&#xff1a; 操作系统是指控制和管理整个计算机系统的软硬件资源&…

[极客大挑战 2019]Havefun1

[极客大挑战 2019]Havefun1 代码审计发现 根据代码逻辑&#xff0c;要求传入’cat’参数&#xff0c;值为’dog’时执行if的操作&#xff0c;所以构造参数: ?catdog获得flag

MG协议转换器:破解暖通设备通讯壁垒的智能钥匙

在智能化楼宇管理中&#xff0c;暖通空调系统&#xff08;HVAC&#xff09;的高效运行直接影响建筑的能耗控制与用户体验。然而&#xff0c;暖通设备品牌众多、协议不统一的问题长期困扰着运维人员&#xff1a;不同厂商的冷水机组、风机盘管、传感器等设备因采用Modbus、BACnet…

【赵渝强老师】Spark的容错机制:检查点

由于Spark的计算是在内存中完成&#xff0c;因此任务执行的生命周期lineage&#xff08;血统&#xff09;越长&#xff0c;执行出错的概念就会越大。Spark通过检查点Checkpoint的方式&#xff0c;将RDD的状态写入磁盘进行持久化的保存从而支持容错。如果在检查点之后有节点出现…

算法兵法全略(译文)

目录 始计篇 谋攻篇 军形篇 兵势篇 虚实篇 军争篇 九变篇 行军篇 地形篇 九地篇 火攻篇 用间篇 始计篇 算法&#xff0c;在当今时代&#xff0c;犹如国家关键的战略武器&#xff0c;也是处理各类事务的核心枢纽。算法的世界神秘且变化万千&#xff0c;不够贤能聪慧…

react传递函数与回调函数原理

为什么 React 允许直接传递函数&#xff1f; 回调函数核心逻辑 例子&#xff1a;父组件控制 Modal 的显示与隐藏 // 父组件 (ParentComponent.tsx) import React, { useState } from react; import { Modal, Button } from antd; import ModalContent from ./ModalContent;co…

多媒体术语扫盲备忘录

DRM DRM: Digital Rights Management, 数字版权保护。 广义上讲&#xff0c;能够保护数字版权(不单单是音视频)都可以叫做DRM。 国外主要分为三大类&#xff0c; Google的Widevine, MicroSoft的 PlayReady, 以及 Apple的 FairPlay. 更多细节请参考此文章. Google Widevine: …