【普中STM32F1xx开发攻略--标准库版】-- 第 12 章 STM32 时钟系统

(1)实验平台:

普中STM32F103朱雀开发板https://item.taobao.com/item.htm?id=620302685024普中STM32F103玄武开发板https://item.taobao.com/item.htm?id=603479028876(2)资料下载:普中科技-各型号产品资料下载链接


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

[12.1 STM32 时钟树](#12.1 STM32 时钟树)

[12.2 时钟配置函数](#12.2 时钟配置函数)

[12.2.1 时钟初始化配置函数](#12.2.1 时钟初始化配置函数)

[12.2.2 时钟使能配置函数](#12.2.2 时钟使能配置函数)

[12.3 自定义系统时钟](#12.3 自定义系统时钟)

[12.4 实验现象](#12.4 实验现象)

课后作业


12.1 STM32 时钟树

时钟对于单片机来说是非常重要的, 它为单片机工作提供一个稳定的机器周期从而使系统能够正常运行。 时钟系统犹如人的心脏, 一旦有问题整个系统就崩溃。 我们知道 STM32 属于高级单片机, 其内部有很多的外设, 但不是所有外设都使用同一时钟频率工作, 比如内部看门狗和 RTC, 它只需 30 几 KHz 的时钟频率即可工作, 所以内部时钟源就有多种选择。 在前面章节的介绍中, 我们知道 STM32系统复位后首先进入 SystemInit 函数进行时钟的设置, 将 STM32F1 系统时钟设置为 72MHz(我们开发板上使用的 STM32F103ZET6 最大可达到 72M(超频除外) ),然后进入主函数。 那么这个系统时钟大小如何得来, 其他外设的时钟又如何划分,这些问题都可以通过一张时钟树图找到答案, 只要理解好时钟树, 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 个时钟源, 那么它们是怎么给其他外设和系统提供时钟的呢? 在上图 11.1.1 时钟树图中我们把常用的时钟用字母框起来, 按照它们顺序依次介绍。

(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 将不工作。

12.2 时钟配置函数

12.2.1 时钟初始化配置函数

在前面章节的介绍中, 我们知道 STM32 系统复位后首先进入 SystemInit 函数进行时钟的设置, 然后进入主函数 main。 那么我们就来看下 SystemInit()函数到底做了哪些操作, 首先打开我们前面使用库函数编写的 LED 程序, 在

system_stm32f10x.c 文件中可以找到 SystemInit()函数, 如果不想找的可以直接打开其头文件, 通过前面教大家的快速进入函数的方法进入到 SystemInit()内。 SystemInit()代码如下:

cpp 复制代码
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_CL
  RCC->CFGR &= (uint32_t)0xF8FF0000;
#else
  RCC->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_ExtSRAM
    SystemInit_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_SRAM
  SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM. */
#else
  SCB->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()函数。 这个函数内部是根据宏定义设置系统时钟频率。 函数如下:

cpp 复制代码
static void SetSysClock(void)
{
#ifdef SYSCLK_FREQ_HSE
  SetSysClockToHSE();
#elif defined SYSCLK_FREQ_24MHz
  SetSysClockTo24();
#elif defined SYSCLK_FREQ_36MHz
  SetSysClockTo36();
#elif defined SYSCLK_FREQ_48MHz
  SetSysClockTo48();
#elif defined SYSCLK_FREQ_56MHz
  SetSysClockTo56();  
#elif defined SYSCLK_FREQ_72MHz
  SetSysClockTo72();
#endif
 
 /* If none of the define above is enabled, the HSI is used as System clock
    source (default after reset) */ 
}

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

cpp 复制代码
#define SYSCLK_FREQ_72MHz 72000000

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

cpp 复制代码
#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

这些时钟值大家要记住。

12.2.2 时钟使能配置函数

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

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

cpp 复制代码
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 外设, 就需要先调用:

cpp 复制代码
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 大类时钟源, 这里我们只挑几个重要的时钟源使能函数介绍, 如下

cpp 复制代码
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 作为系统时钟源, 那么调用的函数就是:

cpp 复制代码
RCC_SYSCLKConfig(RCC_SYSCLKSource_HSE);//配置时钟源为 HSE

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

cpp 复制代码
RCC_PCLK1Config(RCC_HCLK_Div2);//设置低速 APB1 时钟(PCLK1)

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

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

cpp 复制代码
void RCC_APB1PeriphResetCmd(uint32_t RCC_APB1Periph, FunctionalState NewState);
void RCC_APB2PeriphResetCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);

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

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

12.3 自定义系统时钟

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

cpp 复制代码
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 中。 具体代码如下:

cpp 复制代码
/*******************************************************************************
* 函 数 名         : RCC_HSE_Config
* 函数功能		   : 自定义系统时钟,可以通过修改PLL时钟源和倍频系数实现时钟调整
* 输    入         : div:RCC_PLLSource_HSE_Div1/RCC_PLLSource_HSE_Div2
					 pllm: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); //使能或者失能PLL
		while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY)==RESET);//检查指定的RCC标志位设置与否,PLL就绪
		RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);//设置系统时钟(SYSCLK)
		while(RCC_GetSYSCLKSource()!=0x08);//返回用作系统时钟的时钟源,0x08:PLL作为系统时钟
	}
}

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

cpp 复制代码
RCC_HSE_Config(RCC_PLLSource_HSE_Div1,RCC_PLLMul_9);

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

cpp 复制代码
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 指示灯闪烁速度来反映系统时钟修改后的效果。主函数代码如下:

cpp 复制代码
/*******************************************************************************
* 函 数 名         : main
* 函数功能		   : 主函数
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
int main()
{
	RCC_HSE_Config(RCC_PLLSource_HSE_Div2,RCC_PLLMul_9);   //36M
	LED_Init();
	while(1)
	{
		GPIO_ResetBits(LED1_PORT,LED1_PIN);//点亮D1
		delay(6000000);
		GPIO_SetBits(LED1_PORT,LED1_PIN);
		delay(6000000);
	}
}

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

12.4 实验现象

将"\4--实验程序\1--基础实验\5-STM32 时钟系统实验" 中的程序下载到开发板内, LED 模块中的 DS0 指示灯闪烁速度明显变慢。

课后作业

(1) 通过修改系统时钟调节 LED 闪烁速度

(2) 可以尝试调节系统时钟超过 72M, 看看效果是什么样(这里仅推荐大家尝试, 不推荐后面使用)

相关推荐
奋斗的牛马5 小时前
FPGA—ZYNQ学习GPIO-EMIO,MIO,AXIGPIO(五)
单片机·嵌入式硬件·学习·fpga开发·信息与通信
Shylock_Mister5 小时前
ESP32事件组替代全局变量:高效控制任务循环
c语言·单片机·物联网
武文斌776 小时前
PCB画板:电阻、电容、电感、二极管、三极管、mos管
单片机·嵌入式硬件·学习
齐落山大勇6 小时前
STM32的ADC(遥杆的控制)
stm32·单片机·嵌入式硬件
EVERSPIN6 小时前
国产MCU-灵动微MM32F0050系列微控制器的简单分享
单片机·微控制器·mm32f0050系列
huaijin6226 小时前
ESP32在arduino环境下的离线安装 -- 理论上多个版本都有效
stm32·单片机·嵌入式硬件
Shylock_Mister7 小时前
FreeRTOS事件组全解析:多任务同步核心技巧
c语言·单片机·物联网
三佛科技-134163842127 小时前
SI13213L/H,SI13215L/H 非隔离降压恒压芯片5V/3.3V典型应用资料
单片机·嵌入式硬件·智能家居·pcb工艺
云山工作室11 小时前
基于单片机的牧场奶牛养殖系统设计(论文+源码)
stm32·单片机·嵌入式硬件·毕业设计·毕设