当前位置: 首页 > news >正文

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&#xff08;一般为32.768KHz&#xff09;用的引脚是PC14和PC…...

springboot获取配置文件中的值

概括 在开发过程中&#xff0c;对于一些通用的配置&#xff0c;通常会定在一个配置文件中。通常为application.properties或者application.yml文件中。我们只需要在需要使用的地方通过注解直接获取即可。 使用 在springboot中可以通过使用value注解来读取配置文件中的属性。…...

Python 爬虫从入门到(不)入狱学习笔记

爬虫的流程&#xff1a;从入门到入狱 1 获取网页内容1.1 发送 HTTP 请求1.2 Python 的 Requests 库1.2 实战&#xff1a;豆瓣电影 scrape_douban.py 2 解析网页内容2.1 HTML 网页结构2.2 Python 的 Beautiful Soup 库 3 存储或分析数据&#xff08;略&#xff09; 一般爬虫的基…...

STM32C011开发(1)----开发板测试

STM32C011开发----1.开发板测试 概述硬件准备视频教学样品申请源码下载参考程序生成STM32CUBEMX串口配置LED配置堆栈设置串口重定向主循环演示 概述 STM32C011F4P6-TSSOP20 评估套件可以使用户能够无缝评估 STM32C0 系列TSSOP20 封装的微控制器功能&#xff0c;基于 ARM Corte…...

【GAMES101笔记速查——Lecture 19 Cameras,Lenses and Light Fields】

本章节内容&#xff1a;相机、棱镜、光场 计算机图形学的两种成像方法&#xff1a; 1.合成方法&#xff1a;光栅化、光线追踪&#xff08;展示出现实没有的东西&#xff09; 2.捕捉方法&#xff1a;相机&#xff08;捕捉现实已有的东西&#xff09; 目录 1 相机 1.1 针孔相…...

记录两次Unity编辑器和真机表现不符的情况,引用丢失等

如题&#xff0c;问题是在编辑器和打包在真机测试上的效果不一致。 首先&#xff0c;第一次遇到的问题是编辑器和真机上大量资源不符和丢失&#xff0c;多次对比表现为&#xff0c;异常和丢失内容都是两个版本之间变更的资源&#xff0c;判定为资源引用异常&#xff0c;尝试删…...

【Win】user32.SetWindowsHookExW - Notes

user32.SetWindowsHookExW user32.SetWindowsHookExW(idHook, lpfn, hMod, dwThreadId)用于在系统中安装钩子函数&#xff0c;以监视某些系统事件或消息。 Parameters idHook: int - 安装钩子的类型标识符&#xff1b;对应于不同的系统事件或消息类别。lpfn: callable - 指向…...

泷羽sec-linux

基础之linux 声明&#xff01; 学习视频来自B站up主 泷羽sec 有兴趣的师傅可以关注一下&#xff0c;如涉及侵权马上删除文章&#xff0c;笔记只是方便各位师傅的学习和探讨&#xff0c;文章所提到的网站以及内容&#xff0c;只做学习交流&#xff0c;其他均与本人以及泷羽sec团…...

二叉树oj题解析

二叉树 二叉树的最近公共祖先什么是最近公共祖先&#xff1f;leetcode中求二叉树中最近公共祖先解题1.解题2. 根据二叉树创建字符串 二叉树的最近公共祖先 什么是最近公共祖先&#xff1f; 最近的公共祖先指的是这一棵树中两个节点中深度最大的且公共的祖先节点就是最近祖先节…...

python画图|无坐标轴自由划线操作fig.add_artist(lines.Line2D()函数

【1】引言 新发现了一种自由划线操作函数&#xff0c;和大家共享。 【2】官网教程 点击下述代码&#xff0c;直达官网&#xff1a; https://matplotlib.org/stable/gallery/misc/fig_x.html#sphx-glr-gallery-misc-fig-x-py 官网代码非常简洁&#xff0c;我进行了解读。 …...

Flutter封装Coap

前言 我们根据Coap数据通信流程写一个公共组件&#xff0c;用户只要在原本的组件外嵌套这个公共组件就可以使用Coap的功能&#xff0c;这样做更加的方便便捷。 具体步骤 封装一个udp函数 创建一个工厂函数&#xff0c;工厂函数初始化时监听广播数据发送广播函数&#xff1a…...

openharmony napi调试笔记

一、动态库的编译 第一种openharmony交叉编译链配置方法 使用的编译环境是ubuntu20.04 1、使用vscode配置openharmony sdk交叉编译环境 首先下载openharmony的sdk&#xff0c;如native-linux-x64-4.1.7.5-Release.zip 解压后native目录下就是交叉编译用的sdk 在要编译的源…...

C++数据结构与算法

C数据结构与算法 1.顺序表代码模版 C顺序表模版 #include <iostream> using namespace std; // 可以根据需要灵活变更类型 #define EleType intstruct SeqList {EleType* elements;int size;int capacity; };// Init a SeqList void InitList(SeqList* list, int capa…...

MATLAB深度学习(六)——LSTM长短期神经网络原理与应用

LSTM的应用可以参见一个相当好的视频&#xff1a;小车倒立摆最优控制教程 - Part1 Simulink Simscape Multibody仿真建模_哔哩哔哩_bilibili 6.1 序列建模——循环神经网络 循环神经网络RNN是一类专门用于处理序列性数据x&#xff0c;&#xff0c;xn的神经网络结构&#xff0c…...

利用Python爬虫获得1688按关键字搜索商品:技术解析

在电商领域&#xff0c;1688作为中国领先的B2B电商平台&#xff0c;其商品搜索功能对于商家来说具有极高的价值。通过获取搜索结果&#xff0c;商家可以更好地了解市场趋势&#xff0c;优化产品标题&#xff0c;提高搜索排名。本文将介绍如何使用Python编写爬虫&#xff0c;以获…...

Ajax学习笔记,第一节:语法基础

Ajax学习笔记&#xff0c;第一节&#xff1a;语法基础 一、概念 1、什么是Ajax 使用浏览器的 XMLHttpRequest 对象 与服务器通信2、什么是axios Axios是一个基于Promise的JavaScript库&#xff0c;支持在浏览器和Node.js环境中使用。相较于Ajax&#xff0c;Axios提供了更多…...

java基础知识(常用类)

目录 一、包装类(Wrapper) (1)包装类与基本数据的转换 (2)包装类与String类型的转换 (3)Integer类和Character类常用的方法 二、String类 (1)String类介绍 1)String 对象用于保存字符串,也就是一组字符序列 2)字符串常量对象是用双引号括起的字符序列。例如:&quo…...

修改bag的frame_id的工具srv_tools

在使用数据集导航或者建图时&#xff0c;bag中的点云或者其他话题的frame_id没有和需要的对应 1.创建工作空间 2.cd xxxx/src 3.git clone https://github.com/srv/srv_tools.git cd .. catkin_make source ./devel/setup.bash rosrun bag_tools change_frame_id.py -t /要改…...

浅谈丨功能安全测试,汽车的守护者

随着新能源汽车迅猛的发展&#xff0c;各类车型频频面世&#xff0c;同时辅助驾驶/自动驾驶等智驾功能也在不断迭代&#xff0c;使得整个汽车系统的复杂性越来越高&#xff0c;最终导致消费者不得不对如今的汽车质量和安全性提出质疑。 如何打破质疑&#xff1f; 那就不得不搬…...

了解M有SQL索引

目录 索引介绍 索引的优缺点 索引底层数据结构选型 Hash表 二叉查找树(BST) AVL树 红黑树 B 树& B树 索引类型总结 主键索引(Primary Key) 二级索引 聚簇索引与非聚簇索引 聚簇索引(聚集索引) 聚簇索引介绍 聚簇索引的优缺点 非聚簇索引(非聚集索引) 非聚簇…...

进程间通信5:信号

引入 我们之前学习了信号量&#xff0c;信号量和信号可不是一个东西&#xff0c;不能混淆。 信号是什么以及一些基础概念 信号是一种让进程给其他进程发送异步消息的方式 信号是随时产生的&#xff0c;无法预测信号可以临时保存下来&#xff0c;之后再处理信号是异步发送的…...

Excel把其中一张工作表导出成一个新的文件

excel导出一张工作表 一个Excel表里有多个工作表&#xff0c;怎么才能导出一个工作表&#xff0c;让其生成新的Excel文件呢&#xff1f; 第一步&#xff1a;首先打开Excel表格&#xff0c;然后选择要导出的工作表的名字&#xff0c;比如“Sheet1”&#xff0c;把鼠标放到“She…...

python oa服务器巡检报告脚本的重构和修改(适应数盾OTP)有空再去改

Two-Step Vertification required&#xff1a; Please enter the mobile app OTPverification code: 01.因为巡检的服务器要双因子认证登录&#xff0c;也就是登录堡垒机时还要输入验证码。这对我的巡检查服务器的工作带来了不便。它的机制是每一次登录&#xff0c;算一次会话…...

微信小程序下拉刷新与上拉触底的全面教程

微信小程序下拉刷新与上拉触底的全面教程 引言 在微信小程序的开发中,用户体验至关重要。下拉刷新和上拉触底是提高用户交互体验的重要功能,能够让用户轻松获取最新数据和内容。本文将详细介绍这两个功能的实现方式,结合实际案例、代码示例和图片展示,帮助开发者轻松掌握…...

第三十九章:Grafana 概述、Docker安装与验证指南

Grafana 概述、Docker安装与验证指南 一、Grafana 概述 Grafana 是一个跨平台的开源可视化分析工具,是目前网络架构和应用分析中最流行的时序数据展示工具。它主要用于大规模指标数据的可视化展示,并支持多种数据源和丰富的可视化插件。Grafana 使用Go语言开发,具备数据监…...

使用go实现流式输出

流式输出的深度剖析 之前一直在调用openai的key&#xff0c;只是照着文档进行流式调用&#xff0c;也只知其确是流式与api有所不同&#xff0c;而未成体系深究其实现原理。 就以openai的官方流式输出为切入。 概述 流式输出&#xff08;Streaming Output&#xff09;是 HTT…...

WebSocket详解、WebSocket入门案例

目录 1.1 WebSocket介绍 http协议&#xff1a; webSocket协议&#xff1a; 1.2WebSocket协议&#xff1a; 1.3客户端&#xff08;浏览器&#xff09;实现 1.3.2 WebSocket对象的相关事宜&#xff1a; 1.3.3 WebSOcket方法 1.4 服务端实现 服务端如何接收客户端发送的请…...

元组部分介绍

元组部分 元组的基本格式与特点 #1.元组 #基本格式&#xff1a; 元组名&#xff08;元素1&#xff0c;元素2&#xff0c;元素3&#xff09; #注意&#xff1a;所有元素包含在小括号内&#xff0c;元素与元素之间用逗号隔开&#xff0c;可以是不同的元素类型 #注意&#xff1a…...

mfc100u.dll是什么?分享几种mfc100u.dll丢失的解决方法

mfc100u.dll 是一个动态链接库&#xff08;DLL&#xff09;文件&#xff0c;属于 Microsoft Foundation Classes (MFC) 库的一部分。MFC 是微软公司开发的一套用于快速开发 Windows 应用程序的 C 类库。mfc100u.dll 文件包含了 MFC 库中一些常用的函数和类的定义&#xff0c;这…...

企业数字化转型现状

国家数字经济战略背景 2018年以来&#xff0c;国家政府不断出台政策规范我国企业数字化治理市场。2018年9月颁布《关于发展数字经济稳定并扩大就业的指导意见》&#xff0c;支持建设一批数字经济创新创业孵化机构。积极推进供应链创新与应用&#xff0c;支持构建以企业为主导。…...

数据治理:在企业数据管理中的关键角色与实现路径——《DAMA 数据管理知识体系指南》读书笔记- 第 3 章

文章目录 1. 数据治理的核心内涵与战略价值2. 数据治理的驱动因素&#xff1a;不仅仅是合规3. 数据治理的组织模型&#xff1a;选择适合企业结构的运营模式4. 实施数据治理的关键步骤&#xff1a;战略、制度和文化5. 数据治理工具的选择&#xff1a;支持业务与流程的高效管理6.…...

树莓派2装FreeBSD14.1 Raspberry Pi2 install FreeBSD14.1 00000121:error:0A000086:SSL

树莓派2代的Model B采用Broadcom BCM2836 900MHz的四核SoC&#xff0c;1GB内存&#xff0c;是新一代开拓者&#xff0c;兼容1代B。相比之下&#xff0c;树莓派2的性能比1代提升6倍&#xff0c;内存翻了一番。Raspberry Pi 2不仅能跑全系列ARM GNU/Linux发行版&#xff0c;而且支…...

uniop触摸屏维修eTOP40系列ETOP40-0050

在现代化的工业与商业环境中&#xff0c;触摸屏设备已成为不可或缺的人机交互界面。UNIOP&#xff0c;作为一个知名的触摸屏品牌&#xff0c;以其高性能、稳定性和用户友好性&#xff0c;广泛应用于各种自动化控制系统、自助服务终端以及高端展示系统中。然而&#xff0c;即便如…...

uniapp+vue2重新进入小程序就清除缓存,设备需要重新扫码

代码 app.vue页面 <script>export default {onLaunch: function() {uni.removeStorageSync(equiId)}} </script>...

视频推拉流EasyDSS互联网直播点播平台技术特点及应用场景剖析

在数字科技日新月异的今天&#xff0c;视频直播和点播已经成为互联网内容传播的重要方式之一。而互联网直播点播平台EasyDSS作为功能强大的流媒体直播点播视频能力平台&#xff0c;提供了一站式的视频推拉流、转码、直播、点播、时移回放、存储等视频服务&#xff0c;广泛应用于…...

论文笔记 网络安全图谱以及溯源算法

​ 本文提出了一种网络攻击溯源框架&#xff0c;以及一种网络安全知识图谱&#xff0c;该图由六个部分组成&#xff0c;G <H&#xff0c;V&#xff0c;A&#xff0c;E&#xff0c;L&#xff0c;S&#xff0c;R>。 1|11.知识图 ​ 网络知识图由六个部分组成&#xff0c…...

抓住鸿蒙生态崛起的机遇,拥抱未来开发挑战

随着华为鸿蒙&#xff08;HarmonyOS&#xff09;的持续发展&#xff0c;鸿蒙生态正在迅速崛起&#xff0c;逐步在智能手机、智能穿戴、车载、家居等领域形成完整闭环。它不仅为开发者带来了新的机遇&#xff0c;还带来了技术上的挑战。如何抓住这些机遇并应对挑战&#xff0c;是…...

STM32WB55RG开发(5)----监测STM32WB连接状态

STM32WB55RG开发----5.生成 BLE 程序连接手机APP 概述硬件准备视频教学样品申请源码下载参考程序选择芯片型号配置时钟源配置时钟树RTC时钟配置RF wakeup时钟配置查看开启STM32_WPAN条件配置HSEM配置IPCC配置RTC启动RF开启蓝牙LED配置设置工程信息工程文件设置参考文档SVCCTL_A…...

ArcGIS应用指南:ArcGIS制作局部放大地图

在地理信息系统&#xff08;GIS&#xff09;中&#xff0c;制作详细且美观的地图是一项重要的技能。地图制作不仅仅是简单地将地理数据可视化&#xff0c;还需要考虑地图的可读性和美观性。局部放大图是一种常见的地图设计技巧&#xff0c;用于展示特定区域的详细信息&#xff…...

浅谈网络 | 传输层之TCP协议

目录 TCP 包头格式TCP 的三次握手TCP 的四次挥手TCP 的可靠性与"靠谱"的哲学TCP流量控制TCP拥塞控制 上一章我们提到&#xff0c;UDP 就像我们小时候一样简单天真&#xff0c;它相信“网之初&#xff0c;性本善&#xff0c;不丢包&#xff0c;不乱序”&#xff0c;因…...

‌Kotlin中的?.和!!主要区别

目录 1、?.和!!介绍 2、使用场景和最佳实践 3、代码示例和解释 1、?.和!!介绍 ‌Kotlin中的?.和!!主要区别在于它们对空指针的处理方式。‌ ‌?.&#xff08;安全调用操作符&#xff09;‌&#xff1a;当变量可能为null时&#xff0c;使用?.可以安全地调用其方法或属性…...

【漏洞复现】代付微信小程序系统 read.php 任意文件读取漏洞

免责声明: 本文旨在提供有关特定漏洞的信息,以帮助用户了解潜在风险。发布此信息旨在促进网络安全意识和技术进步,并非出于恶意。读者应理解,利用本文提到的漏洞或进行相关测试可能违反法律或服务协议。未经授权访问系统、网络或应用程序可能导致法律责任或严重后果…...

Python人工智能项目报告

一、实践概述 1、实践计划和目的 在现代社会&#xff0c;计算机技术已成为支撑社会发展的核心力量&#xff0c;渗透到生活的各个领域&#xff0c;应关注人类福祉&#xff0c;确保自己的工作成果能够造福社会&#xff0c;同时维护安全、健康的自然环境&#xff0c;设计出具有包…...

PyQt6+pyqtgraph折线图绘制显示

1、实现效果 2、环境&#xff1a; 确认已经安装pyqtgraph的模块&#xff0c;如果没有安装&#xff0c;使用命令安装&#xff1a; pip install pyqtgraph 3、代码实现&#xff1a; 绘制折线函数&#xff1a; import sys import random from PySide6.QtWidgets import QAppl…...

1-golang_org_x_crypto_bcrypt测试 --go开源库测试

1.实例测试 package mainimport ("fmt""golang.org/x/crypto/bcrypt" )func main() {password : []byte("mysecretpassword")hashedPassword, err : bcrypt.GenerateFromPassword(password, bcrypt.DefaultCost)if err ! nil {fmt.Println(err)…...

C语言菜鸟入门·关键字·union的用法

目录 1. 简介 2. 访问成员 2.1 声明 2.2 赋值 3. 共用体的大小 4. 与typedef联合使用 5. 更多关键字 1. 简介 共用体&#xff08;union&#xff09;是一种数据结构&#xff0c;它允许在同一内存位置存储不同的数据类型&#xff0c;但每次只能存储其中一种类型的…...

leetcode hot100【LeetCode 238.除自身以外数组的乘积】java实现

LeetCode 238.除自身以外数组的乘积 题目描述 给你一个整数数组 nums&#xff0c;返回 数组 answer &#xff0c;其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 …...

【测试工具JMeter篇】JMeter性能测试入门级教程(一)出炉,测试君请各位收藏了!!!

一、前言 Apache JMeter是纯Java的开源软件&#xff0c;最初由Apache软件基金会的Stefano Mazzocchi开发&#xff0c;旨在加载测试功能行为和测量性能。可以使用JMeter进行性能测试&#xff0c;即针对重负载、多用户和并发流量测试Web应用程序。 我们选择JMeter原因 是否测试过…...

嵌入式驱动开发详解3(pinctrl和gpio子系统)

文章目录 前言pinctrl子系统pin引脚配置pinctrl驱动详解 gpio子系统gpio属性配置gpio子系统驱动gpio子系统API函数与gpio子系统相关的of函数 pinctrl和gpio子系统的使用设备树配置驱动层部分用户层部分 前言 如果不用pinctrl和gpio子系统的话&#xff0c;我们开发驱动时需要先…...

使用 OpenCV 进行视频中的行人检测

在计算机视觉领域&#xff0c;行人检测是一个重要的研究方向&#xff0c;它在视频监控、自动驾驶、人机交互等领域都有着广泛的应用。本文将介绍如何使用 OpenCV 库来实现视频中的行人检测。 环境准备 首先&#xff0c;我们需要安装 OpenCV 库。可以通过以下命令来安装&#…...