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。

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

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

相关文章

【JavaEE初阶】多线程初阶下部

文章目录 前言一、volatile关键字volatile 能保证内存可见性 二、wait 和 notify2.1 wait()方法2.2 notify()方法2.3 notifyAll()方法2.4 wait 和 sleep 的对比&#xff08;面试题&#xff09; 三、多线程案例单例模式 四、总结-保证线程安全的思路五、对比线程和进程总结 前言…

【人工智能】Python在机器学习与人工智能中的应用

Python因其简洁易用、丰富的库支持以及强大的社区&#xff0c;被广泛应用于机器学习与人工智能&#xff08;AI&#xff09;领域。本教程通过实用的代码示例和讲解&#xff0c;带你从零开始掌握Python在机器学习与人工智能中的基本用法。 1. 机器学习与AI的Python生态系统 Pyth…

“iOS profile文件与私钥证书文件不匹配”总结打ipa包出现的问题

目录 文件和证书未加载或特殊字符问题 证书过期或Profile文件错误 确认开发者证书和私钥是否匹配 创建证书选择错误问题 申请苹果 AppId时勾选服务不全问题 ​总结 在上线ios平台的时候&#xff0c;在Hbuilder中打包遇见了问题&#xff0c;生成ipa文件时候&#xff0c;一…

element-ui 中el-calendar 日历插件获取显示的第一天和最后一天【原创】

需要获取el-calendar 日历组件上的第1天和最后一天。可以通过document.querySelector()方法进行获取dom元素中的值&#xff0c;这样避免计算问题。 获取的过程中主要有两个难点&#xff0c;第1个是处理上1月和下1月的数据&#xff0c;第2个是跨年的数据。 直接贴代码&#xff…

JavaScript的基础数据类型

一、JavaScript中的数组 定义 数组是一种特殊的对象&#xff0c;用于存储多个值。在JavaScript中&#xff0c;数组可以包含不同的数据类型&#xff0c;如数字、字符串、对象、甚至其他数组。数组的创建有两种常见方式&#xff1a; 字面量表示法&#xff1a;let fruits [apple…

5.5 W5500 TCP服务端与客户端

文章目录 1、TCP介绍2、W5500简介2.1 关键函数socketlistensendgetSn_RX_RSRrecv自动心跳包检测getSn_SR 1、TCP介绍 TCP 服务端&#xff1a; 创建套接字[socket]&#xff1a;服务器首先创建一个套接字&#xff0c;这是网络通信的端点。绑定套接字[bind]&#xff1a;服务器将…

Android 15 版本更新及功能介绍

Android 15版本时间戳 Android 15,代号Vanilla Ice Cream(香草冰淇淋),是当下 Android 移动操作系统的最新主要版本。 开发者预览阶段:2024年2月,谷歌发布了Android 15的第一个开发者预览版本(DP1),这标志着新系统开发的正式启动。随后,在3月和4月,谷歌又相继推出了D…

第02章_MySQL环境搭建(基础)

1. MySQL 的卸载 1.1 步骤1&#xff1a;停止 MySQL 服务 在卸载之前&#xff0c;先停止 MySQL8.0 的服务。按键盘上的 “Ctrl Alt Delete” 组合键&#xff0c;打开“任务管理器”对话 框&#xff0c;可以在“服务”列表找到“MySQL8.0” 的服务&#xff0c;如果现在“正在…

红队笔记--W1R3S、JARBAS、SickOS、Prime打靶练习记录

W1R3S(思路为主) 信息收集 首先使用nmap探测主机&#xff0c;得到192.168.190.147 接下来扫描端口&#xff0c;可以看到ports文件保存了三种格式 其中.nmap和屏幕输出的一样&#xff1b;xml这种的适合机器 nmap -sT --min-rate 10000 -p- 192.168.190.147 -oA nmapscan/ports…

学习笔记|MaxKB对接本地大模型时,选择Ollma还是vLLM?

在使用MaxKB开源知识库问答系统的过程中&#xff0c;除了对接在线大模型&#xff0c;一些用户出于资源配置、长期使用成本、安全性等多方面考虑&#xff0c;还在积极尝试通过Ollama、vLLM等模型推理框架对接本地离线大模型。而在用户实践的过程中&#xff0c;经常会对候选的模型…

计算机网络八股整理(一)

计算机网络八股文整理 一&#xff1a;网络模型 1&#xff1a;网络osi模型和tcp/ip模型分别介绍一下 osi模型是国际标准的网络模型&#xff0c;它由七层组成&#xff0c;从上到下分别是&#xff1a;应用层&#xff0c;表示层&#xff0c;会话层&#xff0c;传输层&#xff0c;…

Spring Boot教程之五:在 IntelliJ IDEA 中运行第一个 Spring Boot 应用程序

在 IntelliJ IDEA 中运行第一个 Spring Boot 应用程序 IntelliJ IDEA 是一个用 Java 编写的集成开发环境 (IDE)。它用于开发计算机软件。此 IDE 由 Jetbrains 开发&#xff0c;提供 Apache 2 许可社区版和商业版。它是一种智能的上下文感知 IDE&#xff0c;可用于在各种应用程序…

单片机学习笔记 9. 8×8LED点阵屏

更多单片机学习笔记&#xff1a;单片机学习笔记 1. 点亮一个LED灯单片机学习笔记 2. LED灯闪烁单片机学习笔记 3. LED灯流水灯单片机学习笔记 4. 蜂鸣器滴~滴~滴~单片机学习笔记 5. 数码管静态显示单片机学习笔记 6. 数码管动态显示单片机学习笔记 7. 独立键盘单片机学习笔记 8…

vue 预览pdf 【@sunsetglow/vue-pdf-viewer】开箱即用,无需开发

sunsetglow/vue-pdf-viewer 开箱即用的pdf插件sunsetglow/vue-pdf-viewer, vue3 版本 无需多余开发&#xff0c;操作简单&#xff0c;支持大文件 pdf 滚动加载&#xff0c;缩放&#xff0c;左侧导航&#xff0c;下载&#xff0c;页码&#xff0c;打印&#xff0c;文本复制&…

Css—实现3D导航栏

一、背景 最近在其他的网页中看到了一个很有趣的3d效果&#xff0c;这个效果就是使用css3中的3D转换实现的&#xff0c;所以今天的内容就是3D的导航栏效果。那么话不多说&#xff0c;直接开始主要内容的讲解。 二、效果展示 三、思路解析 1、首先我们需要将这个导航使用一个大…

重新定义社媒引流:AI社媒引流王如何为品牌赋能?

在社交媒体高度竞争的时代&#xff0c;引流已经不再是单纯追求流量的数字游戏&#xff0c;而是要找到“对的用户”&#xff0c;并与他们建立真实的连接。AI社媒引流王通过技术创新和智能策略&#xff0c;重新定义了社媒引流的方式&#xff0c;帮助品牌在精准触达和高效互动中脱…

Docker1:认识docker、在Linux中安装docker

欢迎来到“雪碧聊技术”CSDN博客&#xff01; 在这里&#xff0c;您将踏入一个专注于Java开发技术的知识殿堂。无论您是Java编程的初学者&#xff0c;还是具有一定经验的开发者&#xff0c;相信我的博客都能为您提供宝贵的学习资源和实用技巧。作为您的技术向导&#xff0c;我将…

Centos 8, add repo

Centos repo前言 Centos 8更换在线阿里云创建一键更换repo 自动化脚本 华为Centos 源 , 阿里云Centos 源 华为epel 源 , 阿里云epel 源vim /centos8_repo.sh #!/bin/bash # -*- coding: utf-8 -*- # Author: make.han

《硬件架构的艺术》笔记(五):低功耗设计

介绍 能量以热量形式消耗&#xff0c;温度升高芯片失效率也会增加&#xff0c;增加散热片或风扇会增加整体重量和成本&#xff0c;在SoC级别对功耗进行控制就可以减少甚至可能消除掉这些开支&#xff0c;产品也更小更便宜更可靠。本章描述了减少动态功耗和静态功耗的各种技术。…

Matlab 深度学习工具箱 案例学习与测试————求二阶微分方程

clc clear% 定义输入变量 x linspace(0,2,10000);% 定义网络的层参数 inputSize 1; layers [featureInputLayer(inputSize,Normalization"none")fullyConnectedLayer(10)sigmoidLayerfullyConnectedLayer(1)sigmoidLayer]; % 创建网络 net dlnetwork(layers);% 训…