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

【普中STM32精灵开发攻略】--第 9 章 STM32时钟系统

(1)实验平台:普中STM32精灵开发板​https://item.taobao.com/item.htm?id=739076227953(2)资料下载:普中科技-各型号产品资料下载链接


        本章将向大家介绍 STM32 的时钟系统,重点分析时钟树,只要理解好时钟树,STM32 一切时钟的来龙去脉会非常清楚。通过介绍 STM32 时钟配置过程,让大家学会如何修改系统时钟频率,本章最后通过一个简单的LED 闪烁程序来讲述如何自定义系统时钟。学习本章可以参考“STM32F1xx 中文参考手册”“复位和时钟控制(RCC)”章节内容。本章分为如下几部分内容:

 

9.1 STM32 时钟树

9.2 时钟配置函数

9.2.1 时钟初始化配置函数

9.2.2 时钟使能配置函数

9.3 自定义系统时钟

9.4 实验现象


9.1 STM32 时钟树

        时钟对于单片机来说是非常重要的,它为单片机工作提供一个稳定的机器周期从而使系统能够正常运行。时钟系统犹如人的心脏,一旦有问题整个系统就崩溃。我们知道 STM32 属于高级单片机,其内部有很多的外设,但不是所有外设都使用同一时钟频率工作,比如内部看门狗和 RTC,它只需30 几KHz 的时钟频率即可工作,所以内部时钟源就有多种选择。在前面章节的介绍中,我们知道STM32系统复位后首先进入 SystemInit 函数进行时钟的设置,将STM32F1 系统时钟设置为 72MHz,然后进入主函数。那么这个系统时钟大小如何得来,其他外设的时钟又如何划分,这些问题都可以通过一张时钟树图找到答案,只要理解好时钟树,STM32 一切时钟的来龙去脉就会非常清楚。下面就来了解下时钟树,如下图所示,我们把时钟树拆分逐个介绍。

        在 STM32 时钟系统中,有 5 个重要的时钟源,分别是LSI、LSE、HSI、HSE、PLL。按照时钟频率分可分为高速时钟源和低速时钟源,在这5 个中HSI,HSE以及 PLL 属于高速时钟,LSI 和 LSE 属于低速时钟。按照时钟来源可分为外部时钟源和内部时钟源,外部时钟源就是在 STM32 晶振管脚处接入外部晶振的方式获取时钟源,其中 HSE 和 LSE 是外部时钟源,其他的是内部时钟源。下面我们就按照上图中数字顺序来介绍。

        (1)图标 1 HSI 是内部高速时钟,RC 振荡器,频率为8MHz。可作为系统时钟或 PLL 锁相环的输入。

        (2)图标 2 HSE 是外部高速时钟,芯片的 23 和 24 引脚即为外部高速晶振管脚。可通过外接一个频率范围是 4-16MHz 的时钟或者晶振,我们开发板上接的是一个 8MHz 的外部晶振。HSE 可以作为系统时钟和 PLL 锁相环输入,还可以经过 128 分频后输入给 RTC。

        (3)图标 3 LSI 是内部低速时钟,RC 振荡器,频率大约为40K,可供独立看门狗和 RTC 使用,并且独立看门狗只能使用 LSI 时钟。

        (4)图标 4 LSE 是外部低速时钟,我们开发板上 STM32 芯片的PC14 和PC15即为外部低速时钟管脚。通常在此管脚上外接一个 32.768KHz 的晶振,供RTC使用。我们开发板上已经外接了一个 32.768K 的晶振。

        (5)图标 5 PLL 是锁相环,用于倍频输出,因为开发板外部高速晶振也只有 8M,而我们这块芯片的最大时钟频率是 72M,因此可通过PLL 锁相环来倍频。从图标 5 中可以看到,PLL 时钟输入源可选择为 HSI/2、HSE 或者HSE/2,时钟源经过 2-16 倍频后输入给 PLLCLK,如果系统时钟选择由PLLCLK 提供,则PLLCLK最大值不要超过 72M。

        那么它是怎么倍频产生 72MHz 系统时钟的呢?我们看到在主PLL 内有倍频器和分频器,如下图所示。

        从上图可以看出,PLL 时钟源的输入信号要先经过一个PLLMUL 倍频器,将HSE 或 HSI 倍频(2-16)后输入给 PLLCLK,如果系统时钟源SYSCLK 选择PLLCLK作为它的来源,则最大值不能超过 72M。虽然可以做超频处理,但会打破系统的稳定性,这个是不划算的。假如 PLLSRC 的时钟来源由HSE 提供,我们开发板使用的 HSE 是 8M 晶振,经过 PLLMUL 9 倍频后可以输出72M 时钟频率给PLLCLK。总结:如果我们选择 HSE 是 PLL 的时钟源,PLL 是 SYSCLK 的时钟源,即SYSCLK为 72MHz,这个也是我们库函数模板中 SystemInit 所配置的最终系统时钟。

        上面我们简单介绍了下 STM32 的 5 个时钟源,那么它们是怎么给其他外设和系统提供时钟的呢?在上图时钟树图中我们把常用的时钟用字母框起来,按照它们顺序依次介绍。

        (A)MCO 是 STM32 的一个时钟输出 IO(PA8),它可以选择一个时钟信号输出,可以选择为 PLL 输出的 2 分频、 HSI、 HSE 或者系统时钟。这个时钟可以用来给外部其他系统提供时钟源。

        (B)RTC 时钟。从图中线的流向可知,RTC 时钟来源可以是内部低速的LSI时钟,外部低速 LSE 时钟(32.768K),还可以通过 HSE 128 分频后得到。

        (C)USB 时钟。STM32 中有一个全速功能的 USB 模块,其串行接口引擎需要一个频率为 48MHz 的时钟源,该时钟源只能从 PLL 输出端获取,可以选择为1.5 分频或者 1 分频,也就是当需要使用 USB 模块时,PLL 必须使能,并且PLLCLK 时钟频率配置为 48MHz 或 72MHz。

        (D)SYSCLK 系统时钟。它是 STM32 中绝大部分部件工作的时钟源。它的时钟来源可以由 HSI、HSE、PLLCLK 提供,相信大家选择STM32F1 这种高级芯片,都希望有一个比较大的时钟频率,因此选择 PLLCLK 作为系统时钟。PLLCLK 又是从 HSE 或 HSI 经过 PLL 倍频得到。根据前面 PLL 计算关系大家就可以算出系统时钟是多少。

        (E)其他所有外设。从时钟图上可以看出,其他所有外设的时钟最终来源都是 SYSCLK。SYSCLK 通过 AHB 分频器分频后送给各模块使用。这些模块包括:

        ①、AHB 总线、内核、内存和 DMA 使用的 HCLK 时钟。

        ②、通过 8 分频后送给 Cortex 系统定时器时钟,即SysTick。

        ③、直接送给 Cortex 的空闲运行时钟 FCLK。

        ④、送给 APB1 分频器。 APB1 分频器输出一路供APB1 外设使用(PCLK1,最大频率 36MHz),另一路送给定时器(Timer)1、2 倍频使用。

        ⑤、送给 APB2 分频器。 APB2 分频器分频输出一路供APB2 外设使用(PCLK2,最大频率 72MHz),另一路送给定时器(Timer)1 倍频器使用。

        ⑥、送给 ADC 分频器。ADC 分频器经过 2、4、6、8 分频后送给ADC1/2/3使用,ADC 最大频率为 14M。

        ⑦、二分频后送给 SDIO 使用。

        其中需要理解的是 APB1 和 APB2 的区别,APB1 上面连接的是低速外设,包括电源接口、备份接口、 CAN、 USB、 I2C1、 I2C2、UART2、UART3 等,APB2上面连接的是高速外设包括 UART1、 SPI1、 Timer1、ADC1、ADC2、GPIO等。大家可以简单这样记忆:2>1,所以 APB2 的速度大于 APB1 的速度。

        在时钟树图中我们还可以得到一个重要信息,大多数有关时钟输出部分都有一个使能控制,比如 AHB 总线、APB1 外设、APB2 外设、内核时钟等。当需要使用某个时钟的时候一定要开启它的使能,否则将不工作。在前面我们介绍库函数点亮第一个 LED 实验的时候就使能了 GPIO 的外设时钟,如果不开启,LED 将不工作。

9.2 时钟配置函数

9.2.1 时钟初始化配置函数

        在前面章节的介绍中,我们知道 STM32 系统复位后首先进入SystemInit函数进行时钟的设置,然后进入主函数 main。那么我们就来看下SystemInit()函数到底做了哪些操作,首先打开我们前面使用库函数编写的LED 程序,在system_stm32f10x.c 文件中可以找到 SystemInit()函数,如果不想找的可以直接打开其头文件,通过前面教大家的快速进入函数的方法进入到SystemInit()内。SystemInit()代码如下:

/*** @brief  Setup the microcontroller system*         Initialize the Embedded Flash Interface, the PLL and update the *         SystemCoreClock variable.* @note   This function should be used only after reset.* @param  None* @retval None*/
void SystemInit (void)
{/* Reset the RCC clock configuration to the default reset state(for debug purpose) *//* Set HSION bit */RCC->CR |= (uint32_t)0x00000001;/* Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits */
#ifndef STM32F10X_CLRCC->CFGR &= (uint32_t)0xF8FF0000;
#elseRCC->CFGR &= (uint32_t)0xF0FF0000;
#endif /* STM32F10X_CL */   /* Reset HSEON, CSSON and PLLON bits */RCC->CR &= (uint32_t)0xFEF6FFFF;/* Reset HSEBYP bit */RCC->CR &= (uint32_t)0xFFFBFFFF;/* Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE bits */RCC->CFGR &= (uint32_t)0xFF80FFFF;#ifdef STM32F10X_CL/* Reset PLL2ON and PLL3ON bits */RCC->CR &= (uint32_t)0xEBFFFFFF;/* Disable all interrupts and clear pending bits  */RCC->CIR = 0x00FF0000;/* Reset CFGR2 register */RCC->CFGR2 = 0x00000000;
#elif defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)/* Disable all interrupts and clear pending bits  */RCC->CIR = 0x009F0000;/* Reset CFGR2 register */RCC->CFGR2 = 0x00000000;      
#else/* Disable all interrupts and clear pending bits  */RCC->CIR = 0x009F0000;
#endif /* STM32F10X_CL */#if defined (STM32F10X_HD) || (defined STM32F10X_XL) || (defined STM32F10X_HD_VL)#ifdef DATA_IN_ExtSRAMSystemInit_ExtMemCtl(); #endif /* DATA_IN_ExtSRAM */
#endif /* Configure the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers *//* Configure the Flash Latency cycles and enable prefetch buffer */SetSysClock();#ifdef VECT_TAB_SRAMSCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM. */
#elseSCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH. */
#endif 
}

        SystemInit 函数开始通过条件编译,先复位 RCC 寄存器,同时通过设置CR寄存器的 HSI 时钟使能位来打开 HSI 时钟。默认情况下如果CR 寄存器复位,是选择 HSI 作为系统时钟,这点大家可以查看 RCC->CR 寄存器相关位描述可以得知,当低两位配置为 00 的时候(复位之后),会选择HSI 振荡器为系统时钟。也就是说,调用 SystemInit 函数之后,首先是选择HSI 作为系统时钟。在设置完相关寄存器后才换成 HSE 作为系统时钟,接下来SystemInit 函数内部会调用 SetSysClock()函数。这个函数内部是根据宏定义设置系统时钟频率。函数如下:

/*** @brief  Configures the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers.* @param  None* @retval None*/
static void SetSysClock(void)
{
#ifdef SYSCLK_FREQ_HSESetSysClockToHSE();
#elif defined SYSCLK_FREQ_24MHzSetSysClockTo24();
#elif defined SYSCLK_FREQ_36MHzSetSysClockTo36();
#elif defined SYSCLK_FREQ_48MHzSetSysClockTo48();
#elif defined SYSCLK_FREQ_56MHzSetSysClockTo56();  
#elif defined SYSCLK_FREQ_72MHzSetSysClockTo72();
#endif

        在 system_stm32f10x.c 文件的开头就有对此宏定义,系统默认的宏定义是72MHz,如下:

#define SYSCLK_FREQ_72MHz  72000000

        如果你要设置为 36MHz,只需要注释掉上面代码,然后加入下面代码即可:

#define SYSCLK_FREQ_36MHz  36000000

        根据该函数内部实现过程可知,直接调用 SetSysClockTo72()函数,此函数功能是将系统时钟 SYSCLK 设置为 72M,AHB 总线时钟设置为72M,APB2 总线时钟设置为 72M,APB1 总线时钟设置为 36M,PLL 时钟设置为72M。函数具体实现大家可以打开库函数查看,这里我们就不截取出来。如果SystemInit 内实现过程看不懂没有关系,大家只要知道 SystemInit 函数执行完,时钟大小设置如下:

        SYSCLK(系统时钟) =72MHz

        AHB 总线时钟(HCLK=SYSCLK) =72MHz

        APB1 总线时钟(PCLK1=SYSCLK/2) =36MHz

        APB2 总线时钟(PCLK2=SYSCLK/1) =72MHz

        PLL 主时钟 =72MHz

        这些时钟值大家要记住。

9.2.2 时钟使能配置函数

        上一节我们说到,当使用一个外设时,必须先使能它的时钟。那么怎么通过库函数使能时钟呢?如需了解寄存器配置时钟,可以参考《STM32F10x 中文参考手册》“复位和时钟控制(RCC)”章节内容,里面有详细寄存器的介绍。固件库已经把时钟相关寄存器的使能配置都封装好,放在stm32f10x_rcc.c和stm32f10x_rcc.h 中。只需要打开 stm32f10x_rcc.h 文件,会发现有很多的宏定义和时钟使能函数的声明。这些时钟函数可大致分为三类。一类是外设时钟使能函数,一类是时钟源和倍频因子配置函数,还有一类是外设复位函数。当然还有几个获取时钟源配置的函数。下面就来简单介绍下这些函数的使用。

        首先我们看下时钟使能函数,时钟使能函数包括外设时钟使能和时钟源使能。首先我们看下外设时钟使能相关函数,如下:

void RCC_AHBPeriphClockCmd(uint32_t RCC_AHBPeriph, FunctionalState NewState);
void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);
void RCC_APB1PeriphClockCmd(uint32_t RCC_APB1Periph, FunctionalState NewState);

        上面 3 个时钟使能函数也正是 STM32 的 3 条总线(这个在前面介绍存储器与寄存器章节讲过)。由于 STM32 的外设都是挂接在 AHB 和APB 总线上的,所以要使能外设时钟,也就是使能对应外设所挂接的总线时钟。比如GPIO 外设它是挂接在 APB2 总线上的,如果使用 GPIO 外设,就需要先调用:

void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);

        函数使能 APB2 时钟。有的朋友就会问:我怎么知道哪个外设挂接在哪个总线上呢?很简单,可以通过 STM32 中文参考手册查找,还可以在固件库stm32f10x_rcc.h 文件中查找。其实这些知识在存储器与寄存器章节已经介绍,大家回过头看下即可。

        外设时钟使能函数有两个形参,第一个是你所使用的外设所挂接的时钟,第二个是选择你用的外设时钟使能还是失能。比如我们要使能端口GPIOB,那么第一个传递的参数是:RCC_APB2Periph_GPIOB 宏,第二个传递的参数是ENABLE使能。从第一个参数名来看也非常好理解,RCC 表示复位和时钟控制器,APB2 表示GPIOB 是挂接在 APB2 总线上,Periph 表示外设,后面的GPIOB 表示我们使能的是 GPIOB 端口。第二个参数 ENABLE 表示使能。假如使能GPIOA 端口时钟,那么只需要修改第一个参数值即可,按照刚才介绍的名意义,可以无需查找即可写出RCC_APB2Periph_GPIOA。其他的外设初始化方法类似。

        下面我们介绍下时钟源使能函数,通过前面的讲解,知道STM32 有5 大类时钟源,这里我们只挑几个重要的时钟源使能函数介绍,如下

void RCC_HSICmd(FunctionalState NewState);
void RCC_LSICmd(FunctionalState NewState);
void RCC_PLLCmd(FunctionalState NewState);
void RCC_RTCCLKCmd(FunctionalState NewState);

        这些函数都是用来使能相应的时钟源,比如我们要使能PLL 时钟,那么就调用 RCC_PLLCmd 函数,函数有一个形参,和前面外设时钟的第二个参数一样,如果为 ENABLE 表示使能,DISABLE 表示失能。

        我们再来介绍下另外一类时钟函数——时钟源和倍频因子配置函数。这类函数主要用来选择相应的时钟源和配置时钟倍频因子,比如系统时钟,它可以由HSE、HSI 或者 PLLCLK 作为它的时钟源,具体选择哪个,就是通过时钟源配置函数实现。比如我们设置 HSE 作为系统时钟源,那么调用的函数就是:

RCC_SYSCLKConfig(RCC_SYSCLKSource_HSE);//配置时钟源为 HSE

        在前面也介绍了 APB1 的时钟频率是 HCLK 的 2 分频。那么可以调用下面这个函数来实现:

RCC_PCLK1Config(RCC_HCLK_Div2);//设置低速 APB1 时钟(PCLK1)

        时钟倍频因子配置函数主要用来修改系统的时钟频率。在本章最后一节我们会通过一个简单 LED 闪烁程序来说明修改倍频因子后时钟的变化。

        最后介绍下另外一类时钟函数——外设复位函数。其函数如下:

void RCC_APB2PeriphResetCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);
void RCC_APB1PeriphResetCmd(uint32_t RCC_APB1Periph, FunctionalState NewState);

        在 STM32F10x 高容量的芯片中没有 RCC_AHBPeriphResetCmd 函数。这类函数与前面讲解的外设时钟使能函数用法一样,只不过外设时钟使能函数是用于使能外设时钟,而这类函数是用于外设复位,从函数名也可以区分出来。

        其他的函数大家可以自行查找其功能和用法。

9.3 自定义系统时钟

        在时钟树的讲解中我们知道,通过修改 PLLMUL 中的倍系数值(2-16)可以改变系统的时钟频率。在库函数中也有对时钟倍频因子配置的函数,如下:

void RCC_PLLConfig(uint32_t RCC_PLLSource, uint32_t RCC_PLLMul);

        第一个参数是 PLL 时钟源选择,我们例程中采用的都是HSE 作为PLL 的时钟源,可以设置为 RCC_PLLSource_HSE_Div1/RCC_PLLSource_HSE_Div2。第二个参数就是倍频因子值(RCC_PLLMul_2~RCC_PLLMul_16)。

        为了方便朋友们能够修改系统时钟,我们这里自定义一个系统时钟初始化函数,我们将函数放在对应实验程序的 main.c 中。具体代码如下:

/*******************************************************************************
* 函 数 名 : RCC_HSE_Config
* 函数功能 : 自定义系统时钟,可以通过修改 PLL 时钟源和倍频系数实现时钟调整 
* 输    入 : div:RCC_PLLSource_HSE_Div1/RCC_PLLSource_HSE_Div2pllm:RCC_PLLMul_2-RCC_PLLMul_16
* 输 出 : 无
*******************************************************************************/
void RCC_HSE_Config(u32 div,u32 pllm) //自定义系统时间(可以修改时钟)
{RCC_DeInit(); //将外设 RCC 寄存器重设为缺省值RCC_HSEConfig(RCC_HSE_ON);//设置外部高速晶振(HSE)if(RCC_WaitForHSEStartUp()==SUCCESS) //等待 HSE 起振{RCC_HCLKConfig(RCC_SYSCLK_Div1);//设置 AHB 时钟(HCLK)RCC_PCLK1Config(RCC_HCLK_Div2);//设置低速 AHB 时钟(PCLK1)RCC_PCLK2Config(RCC_HCLK_Div1);//设置高速 AHB 时钟(PCLK2)RCC_PLLConfig(div,pllm);//设置 PLL 时钟源及倍频系数RCC_PLLCmd(ENABLE); //使能或者失能 PLLwhile(RCC_GetFlagStatus(RCC_FLAG_PLLRDY)==RESET);//检查指定的RCC 标志位设置与否,PLL就绪RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);//设置系统时钟(SYSCLK)while(RCC_GetSYSCLKSource()!=0x08);//返回用作系统时钟的时钟源,0x08:PLL作为系统时钟}
}

        函数具体实现过程在程序中已经注释,大家可以参考注释。在函数中设置倍频因子时,我们给他传递了形参中的变量,这样做的好处是当你调用此函数时,只需要修改传递给函数形参内的值即可修改系统时钟,无需修改函数内部程序。在未修改系统时钟时,系统初始化后的时钟是 72M,对应着此函数参数设置如下:

RCC_HSE_Config(RCC_PLLSource_HSE_Div1,RCC_PLLMul_9);

        如果现在我们想让系统时钟为 36M,只需要将参数值修改即可,如下:

RCC_HSE_Config(RCC_PLLSource_HSE_Div2,RCC_PLLMul_9);

        此时修改的是 div 这个参数值,此参数用来对 HSE 时钟分频系数设置,从时钟树可知,HSE 可以直接流入到 PLLSRC,还可以经过2 分频后给PLLSRC。它的取值为 RCC_PLLSource_HSE_Div1 或 RCC_PLLSource_HSE_Div2。

        最后我们可以通过一个 LED 指示灯闪烁速度来反映系统时钟修改后的效果。主函数代码如下:

        

/*******************************************************************************
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
int main()
{RCC_HSE_Config(RCC_PLLSource_HSE_Div2,RCC_PLLMul_9); //36MLED_Init();while(1){GPIO_ResetBits(LED0_PORT,LED0_PIN);//点亮 LEDdelay(6000000);GPIO_SetBits(LED0_PORT,LED0_PIN);delay(6000000);}
}

        如果将 div 原先的 1 值修改为 2,此时系统时钟即为36M,相当于速度慢了一倍。LED 闪烁的速度也就慢了一倍。注意:不要把 STM32 系统时钟设置超过72M使用,否则很容易崩溃

9.4 实验现象

        将“\4--实验程序\1--基础实验\3-STM32 时钟系统实验”中的程序下载到开发板内,核心板中的 D2 指示灯闪烁速度明显变慢。

http://www.dtcms.com/a/317055.html

相关文章:

  • 17.9 ChatGLM3-6B开源!32K长文本+推理提速45%,多任务性能飙升29.4%
  • 【概念学习】什么是深度学习
  • 希赛《华为 HCIA-Datacom 》核心考点之 NAT 技术解析
  • STM32F407VET6学习笔记11:smallmodbus_(多从机)创建新的slave从机
  • GPU 优化-用 tensor core实现5G Massive MIMO 64x64
  • 无人机开发分享——基于行为树的无人机集群机载自主决策算法框架搭建及开发
  • 【嵌入式电机控制#26】BLDC:三相模拟采集
  • springboot项目前后端通用下载方法、问题和解决方案
  • [滑动窗口]904. 水果成篮
  • Maven入门到精通
  • Linux网络编程基础-简易TCP服务器框架
  • Unity笔记(一)——生命周期函数、Inspector面板、MonoBehavior、GameObject
  • Go语言版JSON转TypeScript接口生成器:支持智能递归解析与命名优化
  • 超细整理,接口测试基础+流程,真实环境下怎么测...
  • [GESP202309 四级] 2023年9月GESP C++四级上机题题解,附带讲解视频!
  • 解锁音频创作新可能:AI 人声伴奏分离神器 Replay 深度解析
  • Python 进行点云ICP(lterative Closest Point)配准(精配准)
  • 【Java String】类深度解析:从原理到高效使用技巧
  • 数论手机辅助:打造便捷高效的移动应用交互体验
  • Wisdom SSH:数据库自动化运维的坚固基石
  • WARNING: Illegal reflective access by org.apache.ibatis.reflection.Reflector
  • 八股——IM项目
  • 多端同步新解法:Joplin+cpolar联合通过开源设计实现跨平台无缝协作?
  • 2025年测绘程序设计模拟赛一--地形图图幅编号及图廓点经纬度计算
  • Python日志记录库——logaid
  • 磁悬浮转子振动控制:主动电磁力如何成为高速旋转的“振动克星”
  • 数据集相关类代码回顾理解 | sns.distplot\%matplotlib inline\sns.scatterplot
  • LeetCode 刷题【31. 下一个排列】
  • Golang 基本数据类型
  • 【vibe coding】Kubernetes + Nginx Ingress 实现云端Workspace容器分配与域名访问方案