STM32F103学习笔记-16-RCC(第3节)-使用HSE配置系统时钟并使用MCO输出监控系统时钟

在前两节中,我们学习了时钟树的理论和系统时钟配置函数。本节我们将亲手编写代码,对于调试和验证时钟配置非常重要,通过使用HSE(外部高速时钟)配置系统时钟,并利用MCO(微控制器时钟输出)功能,来监控时钟信号。

一、创建驱动文件与工程配置

在STM32开发中,我们经常需要根据项目需求自定义时钟配置。STM32标准库提供了丰富的函数来简化这个过程。今天,我们将编写一个名为 HSE_SetSysClk的函数,它使用HSE晶振作为时钟源,并通过PLL倍频到所需频率(包括超频)。我们还将通过MCO引脚输出时钟信号,用示波器测量验证。

步骤:

1.新建驱动文件

  • 在工程中创建两个文件:

    bsp_rccclkconfig.c:包含函数实现。bsp_rcc_clock_config.c(源文件)和 bsp_rcc_clock_config.h(头文件)。文件名中的 bsp表示"板级支持包",这是一种常见的驱动命名约定。

    bsp_rccclkconfig.h:包含函数声明和必要的头文件。在 bsp_rcc_clock_config.c中,我们将实现时钟配置函数;在头文件中声明这些函数,以便其他文件调用。

  • bsp_rcc_clock_config.c中,我们将实现时钟配置函数;在头文件中声明这些函数,以便其他文件调用。

2. 工程配置

  • 将这两个文件添加到你的MDK(Keil)或IDE工程中。

  • 在需要使用时钟配置的文件(如 main.c)中包含头文件:

    复制代码
    #include "bsp_rcc_clock_config.h"
  • 确保编译器能找到头文件:在IDE中设置头文件路径(如Keil中在Options for Target -> C/C++ -> Include Paths中添加路径)。

3. 防止头文件重复包含

  • 在头文件中使用条件编译指令,避免多次包含导致的错误。这是编程的良好习惯:

    复制代码
    #ifndef __BSP_RCC_CLOCK_CONFIG_H
    #define __BSP_RCC_CLOCK_CONFIG_H
    
    /* 头文件内容 */
    
    #endif
  • 头文件中需要包含STM32的标准库头文件,以便使用固件库函数和寄存器定义:

    复制代码
    #include "stm32f10x.h"

4. 编译验证

  • 完成以上步骤后,先编译工程,确保没有语法错误和路径问题。如果编译成功,说明工程配置正确。

原理说明 :创建独立的驱动文件有助于代码模块化,提高可重用性。条件编译防止头文件被多次包含,避免重复定义错误。包含 stm32f10x.h是必须的,因为它提供了STM32固件库的核心函数和寄存器映射。

二、编写 bsp_rcclkconfig.c 时要参考 system_stm32f10x.c 里的 static void SetSysClockTo72(void)

1. 背景:system_stm32f10x.c 是官方的"时钟初始化模板"

这个文件是 STM32 官方提供的系统初始化文件,在程序启动时自动执行,用于配置:

  • 系统时钟源(HSI/HSE/PLL)

  • 各总线分频(AHB、APB1、APB2)

  • Flash等待周期

  • 启动时钟稳定检测等。

换句话说,它是整个STM32上电后第一份初始化时钟的"参考蓝图"

例如,在该文件中定义的:

复制代码
static void SetSysClockTo72(void)

就是 ST 官方配置 系统时钟为 72MHz(HSE × 9) 的标准做法。

它完整展示了:

  • HSE 启动检测;

  • PLL 倍频计算;

  • 各总线分频;

  • Flash 延时配置;

  • 切换系统时钟源的正确顺序。

2. bsp_rcclkconfig.c 是在"重写"这个功能

写的函数:

复制代码
void HSE_SetSysClk(uint32_t RCC_PLLMul_x)

本质上与 SetSysClockTo72(void) 作用相同:配置系统时钟为某个频率(可选倍频)

区别在于:

对比项 system_stm32f10x.c bsp_rcclkconfig.c
函数名 SetSysClockTo72() HSE_SetSysClk(uint32_t RCC_PLLMul_x)
调用方式 系统启动时由 SystemInit() 自动调用 用户在 main() 中手动调用
倍频系数 固定为 9(72MHz) 可通过参数灵活指定(如 ×9, ×10, ×12)
编写方式 直接操作寄存器 使用固件库函数(更直观、安全)

因此,你需要参考 SetSysClockTo72() 的逻辑顺序与配置要点,因为:

  • 它保证了正确的时钟切换流程;

  • 它包含了所有必须的寄存器设置;

  • 任何少配置或顺序错误都可能导致系统跑飞或时钟错误。

简言之:

参考 SetSysClockTo72() 是为了在"自主编写固件库版本的时钟初始化函数"时,确保流程正确、配置完整、时序安全。

3. 为什么不是直接用 SetSysClockTo72()

因为:

  • 它是 static 函数,作用域仅限 system_stm32f10x.c 文件,外部无法直接调用;

  • 它只支持固定频率(72MHz),无法动态调整倍频;

  • 要做"裸机编程实验与超频实验",因此需要自己写一个可传参的版本,比如:

三、void HSE_SetSysClk(uint32_t RCC_PLLMul_x) 的函数参数语法作用

1. 参数类型说明

复制代码
uint32_t RCC_PLLMul_x
  • uint32_t 表示一个 32位无符号整数

  • RCC_PLLMul_x 是一个 枚举常量,它来自固件库定义:

stm32f10x_rcc.h 文件中,可以找到类似的定义:

复制代码
#define RCC_PLLMul_2   ((uint32_t)0x00000000)
#define RCC_PLLMul_3   ((uint32_t)0x00040000)
#define RCC_PLLMul_4   ((uint32_t)0x00080000)
...
#define RCC_PLLMul_9   ((uint32_t)0x001C0000)
#define RCC_PLLMul_16  ((uint32_t)0x003C0000)

这些宏值代表 PLL 的倍频系数,用于函数:

复制代码
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_x);

这个函数会根据你传入的倍频参数,自动配置寄存器 RCC->CFGR 中的 PLLMULL 位域。

2. 参数的语法功能(重点理解)

当你调用:

复制代码
HSE_SetSysClk(RCC_PLLMul_9);

实质上就是告诉 MCU:

"以 HSE 为输入时钟源(8MHz),倍频9倍,配置系统主频为 8MHz × 9 = 72MHz。"

如果改成:

复制代码
HSE_SetSysClk(RCC_PLLMul_10);

则为:

"以 HSE 为输入时钟源,倍频10倍,系统主频 = 8MHz × 10 = 80MHz。"

所以:

  • RCC_PLLMul_x 就是倍频选择参数

  • 函数根据它来动态生成不同主频的系统时钟;

  • 可用于测试超频、验证时钟输出。

3. 举例说明参数的使用流程

main.c 中:

复制代码
int main(void)
{
  HSE_SetSysClk(RCC_PLLMul_10); // 设置系统时钟 = 8MHz × 10 = 80MHz
  LED_GPIO_Config();

  while(1)
  {
    LED_G(OFF);
    Delay(0xFFFFF);
    LED_G(ON);
    Delay(0xFFFFF);
  }
}

你会发现:

  • 如果改成 RCC_PLLMul_9(72MHz),LED闪烁频率为标准;

  • 如果改成 RCC_PLLMul_10(80MHz),LED闪烁变快;

  • 如果改成 RCC_PLLMul_16(128MHz),超频效果更明显。

这就是参数的灵活性与教学意义所在。

四、HSE系统时钟配置函数详解

现在,我们来编写 HSE_SetSysClk函数。这个函数接受一个参数 RCC_PLLMul_x,用于设置PLL的倍频因子,从而实现不同频率的输出(包括超频)。以下是函数的步骤分解:

步骤0: 函数框架与复位操作

复制代码
void HSE_SetSysClk(uint32_t RCC_PLLMul_x)
{
  ErrorStatus HSEStatus;  // 定义状态变量
  // 复位RCC寄存器到默认值
  RCC_DeInit();

原理说明RCC_DeInit()将RCC寄存器恢复为复位值,确保从已知状态开始配置,避免之前的配置干扰。

步骤1: 使能HSE并等待就绪

HSE是外部晶振,通常为8MHz。我们需要启动它并等待稳定。

复制代码
RCC_HSEConfig(RCC_HSE_ON);  // 使能HSE
HSEStatus = RCC_WaitForHSEStartUp();  // 等待HSE启动
if (HSEStatus == SUCCESS) {
    // HSE启动成功,继续后续操作(步骤2~5)
} else {
    // HSE启动失败,可以在这里处理错误(例如,切换到HSI或报错)
}

原理说明

  • RCC_HSEConfig()是库函数,用于启用HSE。

  • RCC_WaitForHSEStartUp()等待HSE稳定,返回 SUCCESSERROR。HSE起振需要时间(通常几毫秒),所以必须等待,否则后续操作可能失败。

  • 如果HSE失败,系统可能无法达到预期频率,因此错误处理很重要(例如,使用默认HSI)。

步骤2: 配置Flash预取指和等待周期

当系统时钟频率提高时,CPU访问Flash需要更长时间,否则可能读取出错。我们必须配置Flash的等待周期。

复制代码
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);  // 启用预取指缓冲区
FLASH_SetLatency(FLASH_Latency_2);  // 设置2个等待周期

原理说明

  • 预取指缓冲区是Flash的缓存机制,能提前加载指令,提高效率。

  • 等待周期设置依据SYSCLK频率:≤24MHz时无需等待,24-48MHz时设1周期,>48MHz时设2周期。由于我们目标是72MHz或更高,所以设2周期。

  • 如果不设置,系统在高速下可能运行不稳定或崩溃。

步骤3: 配置总线分频因子

系统时钟SYSCLK通过分频分配给AHB、APB1和APB2总线,确保外设时钟不超限。

复制代码
RCC_HCLKConfig(RCC_SYSCLK_Div1);  // AHB分频 = 1, HCLK = SYSCLK
RCC_PCLK1Config(RCC_HCLK_Div2);   // APB1分频 = 2, PCLK1 = HCLK/2 (最高36MHz)
RCC_PCLK2Config(RCC_HCLK_Div1);   // APB2分频 = 1, PCLK2 = HCLK (最高72MHz)

原理说明

  • AHB总线连接内核和高速外设(如DMA),1分频让HCLK跑在SYSCLK的全速。

  • APB1用于低速外设(如USART2、I2C),最高36MHz,所以2分频。

  • APB2用于高速外设(如GPIO、ADC),支持72MHz,1分频即可。

  • 重要:如果APB1设成1分频,可能超频导致外设异常!务必遵循手册限值。

步骤4: 配置并使能PLL

PLL将HSE倍频到更高频率。我们使用HSE作为PLL输入,并设置倍频因子。

复制代码
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_x);  // PLL源 = HSE不分频, 倍频因子由参数指定
RCC_PLLCmd(ENABLE);  // 使能PLL
while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);  // 等待PLL锁定

原理说明

  • RCC_PLLConfig()设置PLL的输入源和倍频因子。RCC_PLLSource_HSE_Div1表示HSE不分频(直接8MHz输入),RCC_PLLMul_x是参数,范围2-16(例如,9倍频得72MHz)。

  • PLL需要时间锁定频率,等待 RCC_FLAG_PLLRDY标志位确保输出稳定。

  • 超频提示:倍频因子可设为16,得到128MHz(但超频可能不稳定,需测试)。

步骤5: 选择系统时钟源并等待切换完成

最后,将系统时钟源切换到PLL输出。

复制代码
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);  // 选择PLL作为SYSCLK源
while (RCC_GetSYSCLKSource() != 0x08);       // 等待切换完成(0x08表示PLL已作为源)

原理说明

  • RCC_SYSCLKConfig()选择时钟源(HSI、HSE或PLL)。

  • RCC_GetSYSCLKSource()返回当前源状态:0x00表示HSI,0x04表示HSE,0x08表示PLL。等待直到返回0x08,确认切换成功。

  • 切换后,SYSCLK = PLL输出频率(如72MHz),系统正式运行在高速模式。

完整函数代码

结合以上步骤,HSE_SetSysClk函数的完整代码如下(在 bsp_rccclkconfig.c中):

复制代码
#include "bsp_rcclkconfig.h"

void HSE_SetSysClk(uint32_t RCC_PLLMul_x)
{
  ErrorStatus HSEStatus;
  //把RCC 寄存器复位成复位值
  RCC_DeInit;
  
  //使能HSE
  RCC_HSEConfig(RCC_HSE_ON);
  
  HSEStatus = RCC_WaitForHSEStartUp();
  
  if(HSEStatus == SUCCESS)
  {
    //使能预取指
    FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
    FLASH_SetLatency(FLASH_Latency_2);
    
    //设置分频因子
    RCC_HCLKConfig(RCC_SYSCLK_Div1);
    RCC_PCLK1Config(RCC_HCLK_Div2);
    RCC_PCLK2Config(RCC_HCLK_Div1);
    
    //配置锁相环
    //配置PLLCLK = HSE * RCC_PLLMul_x
    RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_x);
    //使能PLL
    RCC_PLLCmd(ENABLE);
    //等待PLL稳定
    while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
    
    //选择系统时钟
    RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
    while(RCC_GetSYSCLKSource() != 0x08);
    
  }
  else
  { 
  /*如果HSE启动失败,应用程序将会有错误的时钟配置,用户可以在这里添加代码来处理这个错误*/
  }
}

五、bsp_rcclkconfig.c 调用固件库的函数映射关系

1. 逻辑结构概览:三层关系图

层次 文件 作用 说明
应用层(用户代码) bsp_rcclkconfig.c / .h 调用固件库API封装特定功能(如配置HSE、PLL) 这是你编写的外层接口函数,比如 HSE_SetSysClk()
固件库接口层 stm32f10x_rcc.h 声明函数、定义宏、参数枚举 提供开发者调用API的"词典"和参数选项
底层驱动实现层 stm32f10x_rcc.c 实现具体函数操作RCC寄存器 直接读写 RCC->CR, RCC->CFGR 等寄存器,改变时钟设置

2. 图示理解:

复制代码
main.c
   ↓ 调用
bsp_rcclkconfig.c     ← 用户自定义逻辑
   ↓ 使用固件库函数
stm32f10x_rcc.h/.c    ← 固件库层(标准外设驱动)
   ↓ 操作寄存器
RCC 寄存器组(硬件层)

3. 对照表

层次 文件 内容类型 示例
用户层 bsp_rcclkconfig.c 应用逻辑(调用固件库) 调用 RCC_PLLConfig()
接口层 stm32f10x_rcc.h 函数声明 + 宏定义 #define RCC_PLLMul_9 0x001C0000
底层层 stm32f10x_rcc.c 函数实现(操作寄存器) `RCC->CFGR

三者之间的关系可以理解为:

  • bsp_rcclkconfig.c 像是一个"工程师调度中心",

  • stm32f10x_rcc.h 是它的"功能目录",

  • stm32f10x_rcc.c 是它的"实际执行员",

  • 最终通过修改 RCC 寄存器来驱动 MCU 的时钟系统运行。

实操编程时,"光标"对准"函数名"、"鼠标单击右键"选择"Go To Definition of 'XXX' "。

六、超频实验 与 MCO 输出监控系统时钟

1. 目标(本节只做这些)

  1. 在 STM32F103 上用 HSE+PLL 实现可参数化的超频(通过传入不同 PLL 倍频)。

  2. 用 MCO(PA8)把所选时钟源输出到探头,用示波器测频率与波形来验证实际系统时钟。

  3. 设计并执行一套稳健的验收 / 稳定性测试流程(避免盲目超频导致器件损伤)。

本节不重复 RCC、PLL 的基本概念和 RCC_* API 的说明(这些已在前面讲过)。下面直接给出实践要点和操作细则。


2. 实验前准备(硬件与软件)

  • 开发板:STM32F103(确保晶振为 8 MHz HSE)。

  • 示波器、探头(注意带宽须大于测量频率;例如要测 128 MHz,应使用 ≥200 MHz 带宽探头/示波器)。

  • 若开发板 PA8 连有蜂鸣器或其他外设:先断开该外设(拔跳帽、断线),否则 MCO 输出会驱动外设,导致测量失真或噪声。

  • USB 或稳压电源:供电稳定,最好带有电流限制/监控。

  • 将代码仓库/工程准备好:包含 bsp_rcclkconfig.c/.hmain.c、LED 例程等。

软件检查项(必须):

  • 头文件原型声明采用 ANSI 风格(void MCO_GPIO_Config(void);)。

  • bsp_rcclkconfig.cRCC_DeInit(); 必须带括号(实际调用)。

  • main.c 中调用 MCO_GPIO_Config(); 时不要写 void 或错放空格在 include 名称。

  • 在成功切换 SYSCLK 后调用 SystemCoreClockUpdate();,以便 SystemCoreClock 变量与实际频率一致(影响延时与外设时序)。

3. 实验步骤(简洁、可复制)

3.1 代码讲解

main() 里先设置系统主频(HSE_SetSysClk(...)),然后配置 PA8 为 MCO 输出(MCO_GPIO_Config()),接着用 RCC_MCOConfig(...) 选择要输出的时钟源(通常选 RCC_MCO_SYSCLK),用示波器测 PA8 波形并判定超频是否成功。同时注意更新 SystemCoreClock、设置 Flash 延迟和总线分频以保证系统稳定。

复制代码
/* main.c (节选:关键部分) */
//......
int main(void)
{
    /* 假设上电启动时为默认 SystemInit 状态(HSI 或 boot 设置),
       现在我们想把系统时钟设为 HSE * PLLMUL */
    HSE_SetSysClk(RCC_PLLMul_10);   /* 尝试 8MHz * 10 = 80MHz(示例) */

    /* 切换系统时钟后,必须更新库中的 SystemCoreClock 变量 */
    SystemCoreClockUpdate();

    /* 配置 PA8 引脚为 MCO 输出(复用推挽) */
    MCO_GPIO_Config();              /* 注意:这里是函数调用,不是函数声明 */

    /* 选择 MCO 输出源:输出当前系统时钟(SYSCLK)到 PA8 */
    RCC_MCOConfig(RCC_MCO_SYSCLK);

    /* 初始化 LED 引脚(用于直观观察) */
    LED_GPIO_Config();

    while (1)
    {
        LED_G(OFF);
        Delay(0xFFFFF);
        LED_G(ON);
        Delay(0xFFFFF);
    }
}

注意事项(代码层面):

  • MCO_GPIO_Config();函数调用 ,不要写成 void MCO_GPIO_Config();(那是函数声明,会被当成声明语句)。

  • HSE_SetSysClk() 成功后要调用 SystemCoreClockUpdate(),否则基于 SystemCoreClock 的延时/串口波特率计算会不正确。

  • RCC_MCOConfig(RCC_MCO_SYSCLK) 是选择输出 SYSCLK 到 PA8;如果你想输出 PLL 的直接输出而不是 SYSCLK,可使用 RCC_MCO_PLLCLK_Div2(注意 PLL 输出一般是 PLLCLK/2)。

    /* bsp_rcclkconfig.c 中的 PA8 配置函数(节选:关键部分) */
    //......
    void MCO_GPIO_Config(void)
    {
    GPIO_InitTypeDef GPIO_InitStruct;

    复制代码
      /* 使能 GPIOA 时钟 */
      RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    
      /* 配置 PA8 为 AF 推挽输出(MCO) */
      GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8;
      GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
      GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
      GPIO_Init(GPIOA, &GPIO_InitStruct);

    }

说明:

  • 这段代码把 PA8 置为复用推挽(AF_PP),这样内部 MCO 信号才能驱动到引脚。

  • 必须先使能 GPIOA 时钟,否则 GPIO_Init() 无效。

    /* bsp_rcclkconfig.h 中 增加函数声明*/
    #ifndef __BSP_RCCLKCONFIG_H
    #define __BSP_RCCLKCONFIG_H

    #include "stm32f10x.h"

    void HSE_SetSysClk(uint32_t RCC_PLLMul_x);
    void MCO_GPIO_Config(void);

    #endif /* __BSP_RCCLKCONFIG_H*/

举个生活类比:

  • .h 文件 = 告诉别人"我有这个功能"。

  • .c 文件 = 真正实现这个功能的细节。

  • main.c = 使用这个功能的人。

就像你告诉别人「我会修电脑(声明)」,而你真正修电脑的过程(实现)在别处完成。

别人听说你会修电脑(包含头文件)之后,就敢放心地让你来干活(调用函数)。

3.2 编译与基础校验

  • 编译工程,确保无警告/错误(特别注意函数声明警告、RCC_DeInit 未调用警告等)。

  • 在未超频前(例如使用 RCC_PLLMul_9 得到 72 MHz)加载并验证 LED、串口等外设是否正常。

3.3 配置 MCO 引脚

  • 在代码里调用:

    MCO_GPIO_Config(); // 配置 PA8 为 AF Push-Pull
    RCC_MCOConfig(RCC_MCO_SYSCLK); // 选择输出 SYSCLK 到 MCO(PA8)

  • 硬件上:断开 PA8 上的蜂鸣器跳帽(若有),接好示波器探头并确认共地。

3.4 切换不同 PLL 倍频(逐步上升)

  • main() 或调试 shell 中,分别尝试传入 RCC_PLLMul_9RCC_PLLMul_10、......、RCC_PLLMul_16(或你板子/芯片允许的值)。

  • 每次变更后,观察:

    • 示波器测到的频率是否与预期(HSE * PLLMUL)相符;

    • 波形的占空比 /上升沿陡峭程度(太差说明驱动/布线/负载问题);

    • 板上 LED 闪烁是否与预期延时对应(注意:若使用延时函数依赖 SystemCoreClock,必须 SystemCoreClockUpdate())。

3.5 验证并记录(必做)

  • 用示波器读取频率并拍照或保存波形(记录实际值)。

  • 记录每个倍频下的:

    • 测得频率(Hz)

    • 波形形状(方波、失真)

    • 供电电流(观察是否显著上升)

    • 板上温升(高频运行后检测芯片温度)

    • 功能异常(外设是否失效、系统是否重启)

4. 观测要点与判读指南

4.1 频率是否正确

  • 预期频率 = HSE(通常 8 MHz) × PLL 倍频。测量值须在小偏差范围内(示波器与探头本身有误差)。

  • 若误差显著(例如 128 MHz 读成 115 MHz),先检查:示波器采样设定、探头补偿/带宽、是否有分频/探头 10× 模式设置错误。

4.2 波形质量

  • 理想:近似对称方波,边沿清晰。

  • 波形差的常见原因:PA8 上外设未断开、探头接地回路过大、PCB 路径阻抗或驱动能力限制、示波器带宽不足。

  • HSE 直出(8 MHz)时波形往往比经 PLL 后差(PLL 输出驱动能力、内部时钟网络有差异),但通常可接受。

  • 过度失真或噪声说明该频率下 PCB/走线/驱动受限,不建议继续上调。

4.3 系统稳定性

  • 仅凭 LED 变快不能判定稳定:必须运行 CPU/外设负载测试(UART/ADC/TIMER)并长时间(例如 10~60 分钟)验证。

  • 超频对 Flash 等等待周期有严格要求:若未设置足够的 Flash Latency,会出现指令错误或系统崩溃。

  • APB1 上外设(例如 I2C、UART)最大时钟不能超过 36 MHz,若超出必须改分频。


相关推荐
摇滚侠2 小时前
Vue 项目实战《尚医通》,医院详情菜单与子路由,笔记17
前端·vue.js·笔记
CarmenHu2 小时前
IBM RAG挑战赛冠军方案学习笔记
笔记·学习
赶飞机偏偏下雨2 小时前
【MySQL笔记】索引 (非常重要)
笔记
jzhwolp2 小时前
从nginx角度看数据读写,阻塞和非阻塞
c语言·nginx·性能优化
朱嘉鼎2 小时前
GPIO中断编程
单片机·嵌入式硬件
小叮当⇔2 小时前
树莓派4B使用指南
学习·树莓派
straw_hat.2 小时前
32HAL——万年历
stm32·单片机·学习
snakecy2 小时前
二叉树、动态规划与链表学习
学习·链表·动态规划
星星20253 小时前
MBSE与数字孪生:五大行业案例
笔记