STM32F103C8T6实时时钟RTC
目录
前言
一、RTC基本硬件结构
二、Unix时间戳
2.1 unix时间戳定义
2.2 时间戳与日历日期时间的转换
2.3 指针函数使用注意事项
三、RTC和BKP硬件结构
四、驱动代码解析
前言
STM32F103C8T6外部低速时钟LSE(一般为32.768KHz)用的引脚是PC14和PC15,所以这两个引脚一定不要再外接其它的电路,比如按键、LED灯之类的,会导致LSE时钟频率出错甚至不起振。
附上完整代码压缩包链接,包含所用到的用户手册和STM32实战手册:
https://jcnwdt8hb184.feishu.cn/wiki/AUXQwtZ6AipW16kUAn0cIi0anMe?form_wx_login=1
一、RTC基本硬件结构
具体的框图可以查看用户手册309页的图154。RTC的时钟输入源有三种选择,外部高速时钟(8MHz)128分频、外部低速时钟LSE(32.768KHz)、内部低速时钟LSI(40KHz)。只有选择LSE做时钟输入源,才能实现主电源掉电后,由电池(给VBAT供电)供电继续工作。
假设选择LSE做时钟输入源,预分频器系数PSC可以选择32767。DIV是一个向下递减的计数器,装载32767这个数值,每来一个时钟脉冲就减1,递减到0又重新装载32767,产生溢出事件,这样输出的时钟频率就是1Hz,对应周期1S。这个1S的时钟信号可以用来提供给32位的计数器CNT,每来一个1代表1S。所以CNT计数的数值就代表多少秒。
用户手册上的框图。RTCCLK就是上面说的三种时钟源之一,主要用来给CNT计数器计数用的;而PCLK1是用来,通过APB1接口获取以及写入寄存器数据用的。
以前用过的RTC时钟芯片DS1302,它是可以设置日历时间年月日等等的,而STM32F103C8T6的RTC却只有一个32位的CNT计数器。其实这个CNT计数器也可以理解为定时器,不过它和普通的定时器不同的是,当主电源掉电以后,它还能通过电池(给VBAT供电)工作。
下图是STM32F4系列的RTC框图,它就可以设置日历时间。
虽然STM32F103的RTC没有设置日历的功能,但是它便宜,那么,只有一个CNT计数器,如何把它转换为我们日常生活中需要的日历时间:年月日时分秒。
二、Unix时间戳
2.1 unix时间戳定义
RTC内的CNT计数器就可以用来存储时间戳,然后在软件内将时间戳转换为日历时间。
2.2 时间戳与日历日期时间的转换
时间戳转换为日历时间并不需要手撕代码,下面是C标准库提供的转换函数。划线的函数是裸机开发RTC常用的函数。
time_t是对uint32_t类型的重定义,struct tm是time.h头文件中定义的一个结构体,成员见下图,注意其中月的范围是0~11,所以写代码的时候要加1,年是从1900起的数值,所以年要加1900。
第一个函数time都是用在操作系统里面的,裸机开发用不了。
函数localtime能将时间戳也就是CNT秒计数器存的数值转换为日历时间,在内部已经自动写了闰年,大小月等等的判断。
2.3 指针函数使用注意事项
struct tm *localtime(const time_t *)是一个指针函数,对于指针函数,使用的时候要格外的注意,它返回的地址可能有下图中说明的三种情况,当然,基于高内聚低耦合原则,不会使用全局变量,那就只能是静态变量,或者用malloc,calloc在堆上申请的 内存空间。
如果这个函数用的是malloc申请的地址,那么在使用之后就必须使用free,否则会造成内存泄露。
如何知道函数使用的究竟是那种方法呢?由于没办法点开源文件,所以我们只能自己设法写代码验证。 可以看到,两次返回的地址都是一样的,说明函数使用的是静态变量。
也可以自己设计一种用malloc申请内存的方法,看看不同之处。可以看到,没有用free释放内存,导致两次打印的结果是不同的。
以后写代码越来越多,肯定会接触到很多指针函数,使用的时候都要小心,看看头文件里有没有说明,使用后需要用free释放内存,比如下面这个例子,这是ESP32的HAL开源库中的一个函数,他就使用了calloc申请地址,然后返回,这个时候就需要我们手动是否内存,否则就会造成内存泄露,而且这样的错误可能比较难排查(至少对我这种水平来说是这样)。
这种带creat的函数,要注意一般都是成对出现的,用delete就可以释放掉内存。
三、RTC和BKP硬件结构
下面是RTC硬件结构框图,可以看到,预分频器、计数器和闹钟都是位于后备区域,待机时维持供电。
下图是PN学堂GD32F303ZET6开发板上的RTC电路,当主电源3V3供电时,BAT54C内的2号二极管导通,1号截止,由3V3给VBAT供电;当主电源掉电时,1号导通2号截止,由3V的纽扣电池BT1给VBAT供电。
什么是后备区域呢 ?这就涉及到另一个片上外设BKP,在后备区域内,除了有之前提到的RTC的那些寄存器,还有42个2字节的寄存器用于存储并保护用户数据,比如说一些配置参数、系数可以放到这里面。
注意,这42个寄存器和内存一样是掉电丢失的,所以如果没有主电源供电了,那必须要有纽扣电池之类的给VBAT供电,它才可以工作。
该图是GD32F303ZET6的图。RTC信号输出和RTC校准:可以配置RTC信号输出寄存器,通过一个引脚(GD32F303ZET6是PC16)将RTC的时钟输出,然后去检测这个时钟信号,如果发现偏差较大,可以配置校准寄存器用来校准。
侵入检测寄存器作用,假如产品安全要求较高,不想让别人去拆、分析,就可以使用侵入检测。
四、驱动代码解析
就只有一个驱动函数,在rtc_drv.c文件中。我将基于寄存器,逐行分析。
#define MAGIC_CODE 0x5a5a//模码/**
***********************************************************
* @brief RTC驱动初始化
* @param
* @return
***********************************************************
*/
void RtcDrvInit(void)
(1)这个函数内部就两行代码,其实不写这个函数也行。
RCC_APB1PeriphResetCmd(RCC_APB1Periph_PWR, ENABLE);
RCC_APB1PeriphResetCmd(RCC_APB1Periph_PWR, DISABLE);
/*- - - - - - - -复位后备寄存器- - - - - - - - */
PWR_DeInit();
(2)开启时钟,就去用户手册找RCC_APB1ENR,就是把7.3.8 APB1 外设时钟使能寄存器(RCC_APB1ENR)的位28、27置1。
/*- - - - - - - -设置寄存器RCC_APB1ENR的PWREN和BKPEN位,使能电源和后备接口时钟- - - - - - - - */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE);
(3) 这个函数内部使用了位带操作,关于这部分的内容,在STM32实战手册中有详细说明,能看懂就行。
/*- - - - - - - -设置寄存器PWR_CR的DBP位,使能对后备寄存器和RTC的访问。- - - - - - - - */
PWR_BackupAccessCmd(ENABLE);/__________________下面是这个函数在库中的具体内容___________________________/
/**
* @brief Enables or disables access to the RTC and backup registers.
* @param NewState: new state of the access to the RTC and backup registers.
* This parameter can be: ENABLE or DISABLE.
* @retval None
*/
void PWR_BackupAccessCmd(FunctionalState NewState)
{
/* Check the parameters */
assert_param(IS_FUNCTIONAL_STATE(NewState));
*(__IO uint32_t *) CR_DBP_BB = (uint32_t)NewState;
}/__________________下面是这个函数中的某些变量具体内容_______________________/
//你必须要自己在keil中去查看才能看明白,这些内容只能用于辅助你理解
#define CR_DBP_BB (PERIPH_BB_BASE + (CR_OFFSET * 32) + (DBP_BitNumber * 4))
PERIPH_BB_BASE 就是位带别名区的首地址0x42000000,CR_OFFSET 就是我要配置的这个外设寄存器相当于位带区基地址的偏移量,DBP_BitNumber 就是我要配置外设寄存器中的第几位。
#define CR_OFFSET (PWR_OFFSET + 0x00)
#define PWR_OFFSET (PWR_BASE - PERIPH_BASE)
其中PWR_BASE就是要配置的外设寄存器的地址,去查看,地址是0x40007000,之后通过这个地址,在用户手册2.3存储器映像中去找对应的外设,发现是电源控制PWR。
通过#define DBP_BitNumber 0x08 可以知道配置的是第八位。也就是4.4.1 电源控制寄存器(PWR_CR)的第八位。
(4)打开LSE时钟,是配置用户手册7.3.9 备份域控制寄存器(RCC_BDCR)位0;
等待LSE稳定这部分库函数的代码写的很巧妙,值得仔细分析。
/*- - - - - - - -打开外部低速时钟LSE,并等待其稳定- - - - - - - - */
RCC_LSEConfig(RCC_LSE_ON);
while ( RCC_GetFlagStatus(RCC_FLAG_LSERDY) != SET );
/__________________下面是等待LSE稳定这个函数在库中的具体内容________________/
/**
* @brief Checks whether the specified RCC flag is set or not.
* @param RCC_FLAG: specifies the flag to check.
*
* For @b STM32_Connectivity_line_devices, this parameter can be one of the
* following values:
* @arg RCC_FLAG_HSIRDY: HSI oscillator clock ready
* @arg RCC_FLAG_HSERDY: HSE oscillator clock ready
* @arg RCC_FLAG_PLLRDY: PLL clock ready
* @arg RCC_FLAG_PLL2RDY: PLL2 clock ready
* @arg RCC_FLAG_PLL3RDY: PLL3 clock ready
* @arg RCC_FLAG_LSERDY: LSE oscillator clock ready
* @arg RCC_FLAG_LSIRDY: LSI oscillator clock ready
* @arg RCC_FLAG_PINRST: Pin reset
* @arg RCC_FLAG_PORRST: POR/PDR reset
* @arg RCC_FLAG_SFTRST: Software reset
* @arg RCC_FLAG_IWDGRST: Independent Watchdog reset
* @arg RCC_FLAG_WWDGRST: Window Watchdog reset
* @arg RCC_FLAG_LPWRRST: Low Power reset
*
* For @b other_STM32_devices, this parameter can be one of the following values:
* @arg RCC_FLAG_HSIRDY: HSI oscillator clock ready
* @arg RCC_FLAG_HSERDY: HSE oscillator clock ready
* @arg RCC_FLAG_PLLRDY: PLL clock ready
* @arg RCC_FLAG_LSERDY: LSE oscillator clock ready
* @arg RCC_FLAG_LSIRDY: LSI oscillator clock ready
* @arg RCC_FLAG_PINRST: Pin reset
* @arg RCC_FLAG_PORRST: POR/PDR reset
* @arg RCC_FLAG_SFTRST: Software reset
* @arg RCC_FLAG_IWDGRST: Independent Watchdog reset
* @arg RCC_FLAG_WWDGRST: Window Watchdog reset
* @arg RCC_FLAG_LPWRRST: Low Power reset
*
* @retval The new state of RCC_FLAG (SET or RESET).
*/
FlagStatus RCC_GetFlagStatus(uint8_t RCC_FLAG)
{
uint32_t tmp = 0;
uint32_t statusreg = 0;
FlagStatus bitstatus = RESET;
/* Check the parameters */
assert_param(IS_RCC_FLAG(RCC_FLAG));/* Get the RCC register index */
tmp = RCC_FLAG >> 5;
if (tmp == 1) /* The flag to check is in CR register */
{
statusreg = RCC->CR;
}
else if (tmp == 2) /* The flag to check is in BDCR register */
{
statusreg = RCC->BDCR;
}
else /* The flag to check is in CSR register */
{
statusreg = RCC->CSR;
}/* Get the flag position */
tmp = RCC_FLAG & FLAG_Mask;
if ((statusreg & ((uint32_t)1 << tmp)) != (uint32_t)RESET)
{
bitstatus = SET;
}
else
{
bitstatus = RESET;
}/* Return the flag status */
return bitstatus;
}
/_____________下面是分析,只是用于辅助理解,必须自己动手查看________________/
/** @defgroup RCC_Flag
* @{
*/#define RCC_FLAG_HSIRDY ((uint8_t)0x21)
#define RCC_FLAG_HSERDY ((uint8_t)0x31)
#define RCC_FLAG_PLLRDY ((uint8_t)0x39)
#define RCC_FLAG_LSERDY ((uint8_t)0x41)
#define RCC_FLAG_LSIRDY ((uint8_t)0x61)
#define RCC_FLAG_PINRST ((uint8_t)0x7A)
#define RCC_FLAG_PORRST ((uint8_t)0x7B)
#define RCC_FLAG_SFTRST ((uint8_t)0x7C)
#define RCC_FLAG_IWDGRST ((uint8_t)0x7D)
#define RCC_FLAG_WWDGRST ((uint8_t)0x7E)
#define RCC_FLAG_LPWRRST ((uint8_t)0x7F)
//分析
这些数字设计的十分巧妙,高3位用于区分要配置哪个寄存器,低五位用于识别是配置寄存器中的第几位。根据高三位,黄色部分配置RCC_CR寄存器、绿色配置RCC_BDCR寄存器、蓝色配置RCC_CSR寄存器。我们带入参数((uint8_t)0x41)分析,也就是说RCC_FLAG = ((uint8_t)0x41);
那么tmp = RCC_FLAG >> 5;结果是0x02,下面这个条件成立。
else if (tmp == 2) /* The flag to check is in BDCR register */
{
statusreg = RCC->BDCR;
}
语句 tmp = RCC_FLAG & FLAG_Mask;用于获取RCC_FLAG的低5位;用来识别是要配置寄存器中的第几位。也就是查看用户手册,7.3.9 备份域控制寄存器(RCC_BDCR)中的第1位是否被硬件置一了。if ((statusreg & ((uint32_t)1 << tmp)) != (uint32_t)RESET)
{
bitstatus = SET;
}
(5)设置时钟源为LSE就是配置用户手册,7.3.9 备份域控制寄存器(RCC_BDCR)的bit9:8
使能时钟这个函数,里面也是用了位带操作,用同样的方法可以知道是对 7.3.9 备份域控制寄存器(RCC_BDCR)的bit15进行配置。
/*- - - - - - - -设置RTC时钟源为外部低速时钟LSE,并使能- - - - - - - - */
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
RCC_RTCCLKCmd(ENABLE);
(6) 用户手册,16.4.2 RTC控制寄存器低位(RTC_CRL),查看位3是否被置1。
/*- - - - - - - -等待APB1接口时钟和RTC时钟同步- - - - - - - - */
RTC_WaitForSynchro();
(7)用户手册,16.4.2 RTC控制寄存器低位(RTC_CRL),查看位5是否被置1。
/*- - - - - - - -等待上次对 RTC 寄存器写操作完成- - - - - - - - */
RTC_WaitForLastTask();
(8)用户手册,16.4.3 RTC预分频装载寄存器(RTC_PRLH/RTC_PRLL),写入数值。
/*- - - - - - - -设置分频值32767- - - - - - - - */
RTC_SetPrescaler(32767);//32768-1
、、、、、、、、、函数具体内容
/**
* @brief Sets the RTC prescaler value.
* @param PrescalerValue: RTC prescaler new value.
* @retval None
*/
void RTC_SetPrescaler(uint32_t PrescalerValue)
{
/* Check the parameters */
assert_param(IS_RTC_PRESCALER(PrescalerValue));
RTC_EnterConfigMode();//16.4.2 RTC控制寄存器低位(RTC_CRL),位4置1
/* Set RTC PRESCALER MSB word */
RTC->PRLH = (PrescalerValue & PRLH_MSB_MASK) >> 16;//高16位写入0
/* Set RTC PRESCALER LSB word */
RTC->PRLL = (PrescalerValue & RTC_LSB_MASK);//低16位写入0x7fff
RTC_ExitConfigMode();//16.4.2 RTC控制寄存器低位(RTC_CRL),位4置0
}
(9)同之前
/*- - - - - - - -等待上次对 RTC 寄存器写操作完成- - - - - - - - */
RTC_WaitForLastTask();
(10)用户手册,16.4.5 RTC计数器寄存器 (RTC_CNTH / RTC_CNTL),写入数值。
/*- - - - - - - -设置时间1970-01-01 00:00:00- - - - - - - - */
RTC_SetCounter(0);
(11)向后备区域BKP的BKP_DR1寄存器中写入模码MAGIC_CODE,只要主电源供电或者VBAT有纽扣电池供电,那么即使复位,BKP寄存器中的内容也不会丢失。
BKP_WriteBackupRegister(BKP_DR1, MAGIC_CODE);
写入这个模码的作用是什么?在代码中,只有读取BKP_DR1中的内容与模码MAGIC_CODE相同时,才会执行上面讲述的所有代码。当设备第一次上电时,BKP_DR1中的内容肯定不是这个模码,就会执行这些初始化代码,而设备复位之后,由于BKP_DR1中已经有模码了,就不会再执行这些代码了。
有一个好处,如果复位后不执行这些代码,那么也就不会再初始化时间戳为0,我们CNT计数器中的时间戳就还是一直在计数的值。
if ( BKP_ReadBackupRegister(BKP_DR1) != MAGIC_CODE ){/*- - - - - - - -复位后备寄存器- - - - - - - - */PWR_DeInit();/*- - - - - - - -设置寄存器RCC_APB1ENR的PWREN和BKPEN位,使能电源和后备接口时钟- - - - - - - - */RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE);/*- - - - - - - -设置寄存器PWR_CR的DBP位,使能对后备寄存器和RTC的访问。- - - - - - - - */PWR_BackupAccessCmd(ENABLE);/*- - - - - - - -打开外部低速时钟LSE,并等待其稳定- - - - - - - - */RCC_LSEConfig(RCC_LSE_ON);while ( RCC_GetFlagStatus(RCC_FLAG_LSERDY) != SET );/*- - - - - - - -设置RTC时钟源为外部低速时钟LSE,并使能- - - - - - - - */RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);RCC_RTCCLKCmd(ENABLE);/*- - - - - - - -等待APB1接口时钟和RTC时钟同步- - - - - - - - */RTC_WaitForSynchro();/*- - - - - - - -等待上次对 RTC 寄存器写操作完成- - - - - - - - */RTC_WaitForLastTask();/*- - - - - - - -设置分频值32767- - - - - - - - */RTC_SetPrescaler(32767);//32768-1/*- - - - - - - -等待上次对 RTC 寄存器写操作完成- - - - - - - - */RTC_WaitForLastTask();/*- - - - - - - -设置时间1970-01-01 00:00:00- - - - - - - - */RTC_SetCounter(0);BKP_WriteBackupRegister(BKP_DR1, MAGIC_CODE);return;}
(12) 那么,复位后要执行的初始化代码是哪些呢?为什么是这些代码需要执行呢?
/*- - - - - - - -设置寄存器RCC_APB1ENR的PWREN和BKPEN位,使能电源和后备接口时钟- - - - - - - - */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE);
/*- - - - - - - -设置寄存器PWR_CR的DBP位,使能对后备寄存器和RTC的访问。- - - - - - - - */
PWR_BackupAccessCmd(ENABLE);
/*- - - - - - - -等待APB1接口时钟和RTC时钟同步- - - - - - - - */
RTC_WaitForSynchro();
/*- - - - - - - -等待上次对 RTC 寄存器写操作完成- - - - - - - - */
RTC_WaitForLastTask();
其它代码不用执行的原因可以参考下图
五、其它代码部分解析
/**
***********************************************************
* @brief 设置时间
* @param time,输入,日历时间
* @return
***********************************************************
*/
void SetRtcTime(RtcTime_t *time)
{time_t timeStamp;//时间戳struct tm timeInfo;memset(&timeInfo, 0, sizeof(timeInfo));//结构体初始化timeInfo.tm_year = time->year - 1900;timeInfo.tm_mon = time->month - 1;timeInfo.tm_mday = time->date;timeInfo.tm_hour = time->hour;timeInfo.tm_min = time->minute;timeInfo.tm_sec = time->second;timeStamp = mktime(&timeInfo) - 8 * 60 * 60;/*等待上次对 RTC 寄存器写操作完成*/RTC_WaitForLastTask();/*设置时间*/RTC_SetCounter(timeStamp);//因为这里面是基于零时区实现的,要想得到东八区即北京时间,时间戳就要减8*60*60S
}/**
***********************************************************
* @brief 获取时间
* @param time,输出,日历时间
* @return
***********************************************************
*/
void GetRtcTime(RtcTime_t *time)
{time_t timeStamp;struct tm* timeInfo;timeStamp = RTC_GetCounter() + 8 * 60 * 60;timeInfo = localtime(&timeStamp);time->year = timeInfo->tm_year + 1900;time->month = timeInfo->tm_mon + 1;time->date = timeInfo->tm_mday;time->hour = timeInfo->tm_hour;time->minute = timeInfo->tm_min;time->second = timeInfo->tm_sec;
}
在函数void SetRtcTime(RtcTime_t *time)中,有这样一行代码:
timeStamp = mktime(&timeInfo) - 8 * 60 * 60;
而在函数void GetRtcTime(RtcTime_t *time)中,却是这样一行代码:
timeStamp = RTC_GetCounter() + 8 * 60 * 60;
在函数void SetRtcTime(RtcTime_t *time)中,假如RtcTime_t *time成员的值为2001-9-9 9:46:40,那么通过mktime(&timeInfo)获得的零时区时间戳就是B,因为这些函数都是基于零时区的。我们希望东八区即北京时间的日历时间是通过零时区添加偏移得到的,那么时间戳B减去8个小时的偏移,就得到2001-9-9 9:46:40的东八区时间戳1000000000。
假设,我们已经在主函数中写了如下代码。
int main(void)
{DrvInit();AppInit();RtcTime_t time = {2001, 9, 9, 9, 46, 40};SetRtcTime(&time);while(1){TaskHandler();}
}
那么,在函数void GetRtcTime(RtcTime_t *time)中,RTC_GetCounter()得到的零时区时间戳就是1000000000。而localtime(&timeStamp);也是基于零时区进行转换的,如果timeStamp就是1000000000的话,转换的日历时间就是2001-9-9 1:46:40。但如果RTC_GetCounter()得到的时间戳加上8个小时的时区偏移量,那么得到的时间戳就是零时区时间戳B,timeInfo = localtime(&timeStamp);就得到零时区日历时间2001-9-9 9:46:40。
相关文章:

STM32F103C8T6实时时钟RTC
目录 前言 一、RTC基本硬件结构 二、Unix时间戳 2.1 unix时间戳定义 2.2 时间戳与日历日期时间的转换 2.3 指针函数使用注意事项 三、RTC和BKP硬件结构 四、驱动代码解析 前言 STM32F103C8T6外部低速时钟LSE(一般为32.768KHz)用的引脚是PC14和PC…...

Python Selenium:Web自动化测试与爬虫开发
Python Selenium:Web自动化测试与爬虫开发 Python Selenium:Web自动化测试与爬虫开发安装Selenium设置WebDriver基础示例页面元素交互处理JavaScript和Cookies浏览器控制屏幕截图Headless Mode结束会话错误处理与调试 ***本文由AI辅助生成*** Python Se…...

Java-07 深入浅出 MyBatis - 一对多模型 SqlMapConfig 与 Mapper 详细讲解测试
点一下关注吧!!!非常感谢!!持续更新!!! 大数据篇正在更新!https://blog.csdn.net/w776341482/category_12713819.html 目前已经更新到了: MyBatisÿ…...

用CAXA CAD电子图板导入图框、标题栏并导出pdf的方法
1.导入图框: 点击调入图框->出现读入图框文件 一个一个点击,选择合适的图框 然后点击导入 2.导入标题栏: 调入标题栏->出现读入标题栏文件 一个一个点击,选择合适的标题栏,然后点击导入 3.导出pdf&#x…...

深入了解 Linux htop 命令:功能、用法与示例
文章目录 深入了解 Linux htop 命令:功能、用法与示例什么是 htop?htop 的安装htop的基本功能A区:系统资源使用情况B区:系统概览信息C区:进程列表D区:功能键快捷方式 与 top 的对比常见用法与示例实际场景应…...

JDK1.8新增特性
新特性: Lambda表达式: (语法三要素:参数、箭头、代码) JDK1.8引入的一种新语法Lambda表达式,它简化了匿名内部类的使用和提高代码的可读性。 /**正常写法创建Runable**/ Runnable runnable new Runnable() {Overridepublic voi…...

环境背景文本到语音转换
目录 概述演示效果核心逻辑使用方式 概述 本文所涉及的所有资源的获取方式:https://www.aspiringcode.com/content?id100000000027&uid2f1061526e3a4548ab2e111ad079ea8c 论文标题: 本文提出了 VoiceLDM,这是一种旨在生成准确遵循两种…...

后端数据增删改查基于Springboot+mybatis mysql 时间根据当时时间自动填充,数据库连接查询不一致,mysql数据库连接不好用
目录 后端数据增删改查Springboot 实体(entity)类引进添加UserMapper接口 创建对用的UserController注意数据库查询不一致新增数据更新删除postman测试 后端数据增删改查 基于之前构建系统,实现用户数据的CRUD。 打开navicat16,…...

《Python编程实训快速上手》第九天--调试技巧
一、抛异常 异常类型分为两类,第一类是Python自带的异常类型(见《Python编程快速上手》第一天---前三章打基础),第二类是自定义异常。 面对自定义异常类型,使用raise抛异常,类型值默认为Exception&#x…...

html5复习一
目标 1、html5介绍及开发工具 2、html5标签 3、文本样式 4、图片标签和超链接标签 知识点: 万维网的构成: 1、url:统一资源定位器 2、http/https:超文本传输协议 3、html:超文本标记语言 html的后缀名: .html 和 .htm html基本…...

SSL/TLS,SSL,TLS分别是什么
SSL/TLS,SSL,TLS分别是什么 SSL(Secure Sockets Layer,安全套接层) 定义与发展历程: SSL 是一种早期的网络安全协议,旨在为网络通信提供保密性、数据完整性和身份验证等安全保障。它最初由网景…...

css iframe标签使用
<iframe> 标签用于在网页中嵌入另一个 HTML 页面。它非常灵活,可用于嵌入内容,比如其他网站、视频、地图等。以下是有关 <iframe> 的详细介绍及使用方法: 基本语法 <iframe src"URL" width"宽度" height…...

API的妙用
我们都知道,通过使用API可以快速开发部署应用,不需要从头开始收集处理数据。能够很好地提高效率。 一、加速应用程序开发和部署 通过调用API接口,可以快速获取数据、实现功能或整合其他服务,无需从零开始编写大量的代码…...

HTML5超酷响应式视频背景动画特效(六种风格,附源码)
文章目录 1.设计来源1.1 大气蓬勃动态背景界面效果1.2 星空闪闪动态背景界面效果1.3 眼神深眸动态背景界面效果1.4 星空银河动态背景界面效果1.5 花开花落动态背景界面效果1.6 海底世界动态背景界面效果 2.效果和源码2.1 动态效果2.2 源代码 源码下载万套模板,程序开…...

Spire.PDF for .NET【页面设置】演示:打开 PDF 时自动显示书签或缩略图
用户打开 PDF 文档时,他们会看到 PDF 的初始视图。默认情况下,打开 PDF 时不会显示书签面板或缩略图面板。在本文中,我们将演示如何设置文档属性,以便每次启动文件时都会打开书签面板或缩略图面板。 Spire.PDF for .NET 是一款独…...

算法中常用到的数学知识:埃拉托色尼筛法(获取质数)、欧几里得算法(求两个数最大公因数)
不管是在项目中还是面试时,一定的算法能力都是极其重要的。大多数算法只要有一定的基础,给足够的时间是可以写出来的,然而有一类算法,说难也不难,说简单也不简单,这种算法通常涉及到某种数学知识࿰…...

实战OpenCV之人脸识别
基础入门 随着计算机视觉技术和深度学习的发展,人脸识别已经成为一项广泛应用的技术,涵盖了从安全监控、身份验证、智能家居到大型公共安全项目等多个领域。 人脸识别技术通常包括以下几个主要步骤。 图像采集:通过摄像头或其他图像采集设备,捕获包含人脸的图像或视频帧。 …...

图像预处理之图像滤波
目录 图像滤波概览 均值滤波(Mean Filter) 中值滤波(Median Filter) 高斯滤波(Gaussian Filter) 双边滤波(Bilateral Filter) 方框滤波(Box Filter) S…...

【通俗理解】隐变量的变分分布探索——从公式到应用
【通俗理解】隐变量的变分分布探索——从公式到应用 关键词提炼 #隐变量 #变分分布 #概率模型 #公式推导 #期望最大化 #机器学习 #变分贝叶斯 #隐马尔可夫模型 第一节:隐变量的变分分布的类比与核心概念【尽可能通俗】 隐变量的变分分布就像是一场“捉迷藏”游戏…...

PyTorch 分布式并行计算
0. Abstract 使用 PyTorch 进行多卡训练, 最简单的是 DataParallel, 仅仅添加一两行代码就可以使模型在多张 GPU 上并行地计算. 但它是比较老的方法, 官方推荐使用新的 Distributed Data Parallel, 更加灵活与强大: 1. Distributed Data Parallel (DDP) 从一个简单的非分布…...

[cg] vulkan external_memory
最近在写硬件编码的代码,渲染器渲染出的RT需要给到编码器做硬编,有两种方法能做。 一是通过 map的方式,把显存里的数据读到cpu,拷贝一份cpu data给编码器,但这种方式会有内存拷贝的开销。所以,我们思考是否…...

如何使用Python代码实现给GPU预加热
如何使用Python代码实现给GPU预加热 一、引言二、使用深度学习框架进行预加热2.1 TensorFlow预加热2.2 PyTorch预加热三、使用CUDA进行预加热四、预加热的效果评估与优化五、结论与展望在高性能计算和深度学习领域,GPU(图形处理器)已经成为不可或缺的加速工具。然而,在实际…...

硬件知识 cadence16.6 原理图输出为pdf 网络名下划线偏移 (ORCAD)
1. cadence原理图输出为PDF网络名下划线偏移 生这种情况的原因 1. 设计的原理图图纸大小比正常的 A4图纸大。 2. 打印为PDF 的时候,打印机的设置有问题。 2.cadence原理图输出为 PDF网络名下划线偏移的情况 可以看到上图,网络名往上漂移。 3. 解决办法 …...

ffmpeg视频滤镜:提取缩略图-framestep
滤镜描述 官网地址 > FFmpeg Filters Documentation 这个滤镜会间隔N帧抽取一帧图片,因此这个可以用于设置视频的缩略图。总体上这个滤镜比较简单。 滤镜使用 滤镜参数 framestep AVOptions:step <int> ..FV....... set frame st…...

RecyclerView详解——(四)缓存复用机制
稍微看了下源码和部分文章,在此做个小小的总结 RecyclerView,意思为可回收的view,那么相对于listview,他的缓存复用肯定是一大优化。 具体而言,当一个列表项被移出屏幕后,RecyclerView并不会销毁其视图&a…...

进程 系统调用 中断
进程P通过执行系统调用从键盘接收一个字符的输入,已知此过程中与进程P相关的操作包括: ①将进程P插入就绪队列; ②将进程P插入阻塞队列; ③将字符从键盘控制器读入系统缓冲区; ④启动键盘中断处理程序; …...

演讲回顾丨杭州悦数 CTO 叶小萌:图数据库发展新航向——拥抱 GQL,融合 HTAP,携手 AI
本文为杭州悦数 CTO 叶小萌在“标准智能:新质生产力的原动力”悦数图数据库新产品发布会上的演讲回顾,主题为:《新标准、新期待:展望图数据库发展的关键方向》 各位嘉宾、悦数图数据库的用户以及线上的观众朋友们大家好࿰…...

Java安全—JNDI注入RMI服务LDAP服务JDK绕过
前言 上次讲到JNDI注入这个玩意,但是没有细讲,现在就给它详细地讲个明白。 JNDI注入 那什么是JNDI注入呢,JNDI全称为 Java Naming and Directory Interface(Java命名和目录接口),是一组应用程序接口&…...

C++:设计模式-单例模式
单例模式(Singleton Pattern)是一种设计模式,确保一个类只有一个实例,并且提供全局访问点。实现单例模式的关键是防止类被多次实例化,且能够保证实例的唯一性。常见的实现手法包括懒汉式、饿汉式、线程安全的懒汉式等。…...

Softing工业将OPC UA信息建模集成到边缘应用和安全集成服务器中
Softing工业宣布将OPC UA(统一架构)信息建模集成到其边缘产品系列及安全集成服务器(SIS)中,这一技术进步使得在工业物联网(IIoT)应用中的数据集成、交换与控制更加无缝、有效。 (OPC…...