type
slug
status
summary
icon
category
date
tags
password

0 时钟

notion image
时钟的设置可以通过CubeMax图形化页面准确的算出。
(下面默认的例子是以STM32F407为例,F401会额外给出)
 

0.1 什么是时钟?

在数字电路里,“时钟”是一个周期性的方波信号,它的作用是:
  • 控制处理器或外设中所有电路的运行节奏;
  • 相当于“节拍器”或“鼓点”,每跳动一下,就执行一条指令或传输一个数据
  • 数字电路没有“自发”动作,只有在时钟驱动下才运行。(所以可以理解为时钟驱动MCU运行,从而驱动外设)
  • 比如 1MHz 表示每秒100万次电平跳变。
在STM32这种微控制器中,有很多不一样的模块(CPU 核心、GPIO、USART、ADC、定时器等),每一个模块想要工作,都需要有对应的时钟信号输入
 

0.2 为什么使用外设前需要打开时钟?

这是一个节能和资源管理的机制。
STM32芯片内部的每个外设(比如 USART、ADC、TIM)都是连接在 AHB、APB1、APB2 等总线上的,而这些总线都从系统主时钟 SYSCLK 分出来专门供给外设模块的“时钟信号”
🔒 默认情况下,这些外设的时钟是“关闭的”(外设处于冻结状态),不工作也不耗电。
你可以把 STM32 想象成一个工厂:
  • 电源总线是“系统电源”;
  • 各个车间(外设)比如 UART、ADC、TIM 是“子工厂”;
  • 要让这些车间开始运转,就必须“打开他们的供电开关”——也就是打开“外设的时钟”!
 

0.3 时钟的种类

notion image
 

0.4 时钟的分发结构(STM32的时钟树)

STM32 的时钟系统采用“时钟树”结构,主时钟通过分频器分发到不同部分。
 
 

1 STM32 定时器概述

1.1 定时器的概述

  • 功能
    • 定时器的基本功能,但是STM32的定时器除了具有定时功能之外,也具有定时器中断功能还具有输入捕获(检测外部信号)以及输出比较功能(输出不同的脉冲),可以利用STM32定时器输出某种频率的脉冲信号来控制产品(控灯亮度、控制直流电机的转速、控制能机的角度....)
  • 定时器的种类
    • STM32由于资源丰富,所以提供了多种定时器,一共提供 14个定时器(不包含系统定时器、不包含看门狗定时器),分为高级定时器(TIM1和TIM8)、通用定时器(TIM2~TIM5+TIM9~TIM14,一共10个)、基本定时器(TIM6 和TIM7)。
    • 在F401中,定时器是TIM1,TIM2~TIM5,TIM9~TIM11,都是84MHZ
      • 没有基本定时器TIM6和TIM7,和高级定时器TIM8
      • 也没有TIM12~TIM14(辅助高级定时器)
 

1.2 基本定时器的使用

1.2.1 基本定时器框图

notion image
  • 基本定时器只能向上计数:0~2^16-1;
  • 在寄存器map里面看定时器挂载在哪里的总线上
    • notion image
      notion image
    • SYSCLK:168MHz
    • APB1总线频率(外设时钟):42MHz;APB2总线频率(外设频率):84MHz
    • APB1定时器时钟:84MHz;APB2定时器时钟:168MHz
    • STM32提供TIM6和TIM7作为基本定时器,属于STM32内部资源(没有通道就没有输入捕获和输出比较功能),都是16bit定时器,并没有和GPIO引脚连接,TIM6和TIM7都挂载在APB1总线下,而APB1 定时器时钟频率为84MHZ.
  • 基本定时器框图
    • notion image
    • 这里的内部时钟CK_INT:就是前面所说的APB1的定时器频率84MHz。
    • 如果不处理,那么84MHz即1s钟振荡84M次。
    • 分频:预分频器可对计数器时钟频率进行分频,分频系数介于1和65536之间。TIMX_PSC寄存器中的16位寄存器所控制的16位计数器。
      • (84-1),从0开始???(因为这个是基于16位寄存器控制的16位计数器,所以是从0开始计数,想要n分频,设置寄存器的值应该为n-1),时钟频率降为1MHz(1s振荡10^6次,振荡1次计时1us)
      • (8400-1),时钟将为10000Hz,即1s振荡10000次,即1ms振荡10次,100us1次。所以分频越高,时钟频率越低,周期更长,计时越慢
    • 基本定时器只能向上计数
    • 计数器寄存器(TIMx_CNT):记录当前计数值,从0开始向上+1
    • 预分频定时器(TIMx_PSC):设置计数一次的周期(单次时间)
    • 自动重载寄存器(TIMx_ARR):设置定时的次数(定时总时间)
    • 影子寄存器:如图所示有影子的寄存器即有影子寄存器。所以PSC和ARR有影子寄存器。
      • 自动重载寄存器是预装载的(即最初需要写入值)。每次尝试对自动重载寄存器执行读写操作时,都会访问预装载寄存器。预装载寄存器的内容既可以直接传送到影子寄存器,也可以在每次发生更新事件UEV时传送到影子寄存器,这取决于TIMxCR1寄存器中的自动乘载预装载使能位(ARPE)。当计数器达到上溢值并且TIMx_CR1寄存器中的UDIS位为0时,将发送更新事件。该更新事件也可由软件产生。
      • PSC寄存器有缓冲(即含有影子寄存器)。因此可以对预分频器进行实时更改。而新的预分频比将在下一场更新事件发生时被采用。
    • 所以想要对基木定时器的定时功能进行设置,只需要把TIM_CNT、TIM_PSC、TIM_ARR 寄存器设置正确即可。
      • 举个例子:TIM6的时钟频率为84MHZ,如果想要降低频率,公式:频率 = 84MHz /(PSC+1). 计数次数 = 定时时间/计数一次的时间 -1
      • 1MHZ=84MHZ/(83+1),频率就降为1MHZ,也就意味着TIM_CNT寄存器累计计数1次的时间是1us,如果打算利用TIM6定时1ms,也就是说TIM_ARR寄存器的值应该设置为1000-1,只要TIM_CNT==TIM_ARR,就说明时间到了.(PSC=84-1=83,ARR=1000-1=999).
      • 如果时间到了,TIM_CNT清0,重新从0计数到自动重载值(TIMX_ARR寄存器的内容),并生成计数器上溢事件。
    • 对时钟进行分频的目的就是为了延长定时时间(因为ARR是最高为65536,但是每次计数所对应的时间是可以更改的,所以可以延长单位计时长度来延长总计时时间),但是不是必须分频,由用户指定是否分频。
    • 计数值=自动重装值产生的中断,我们叫做更新中断,这个中断会通向NVIC,再配置好NVIC的定时器通道,就能得到CPU响应(即跳转到中断服务程序),编写中断服务程序就可以了。
    • 计数值=自动重装值产生的事件,我们叫做更新事件。更新事件不会触发中断,但是可以触发内部其他电路的工作。
    • 主从触发DAC的功能:内部硬件再不受程序控制的情况下实现自动运行
      • notion image
      • 定时器的更新事件映射到触发输出(TRGO:Trigger Output)的位置,然后TRGO直接接到DAC的触发转换引脚上,整个过程不需要软件的驱动,硬件自动。
  • 对于STMF401,说明书我们可以看到没有TIM8,TIM12,TIM13,TIM14,TIM6,TIM7.所以STM32F401没有一般定时器(TIM6和TIM7)
  • TIM1为高级定时器,TIM2~TIM5为通用定时器(TIM3和TIM4 16bit;TIM2和TIM5 32bit(定时范围更广)),TIM9~TIM11为补充定时器
定时器号
所属总线
定时器类型
位宽
主要功能
TIM1
APB2
高级定时器
16位
PWM、互补输出、死区、中心对齐等复杂功能
TIM2
APB1
通用定时器
32位
标准定时功能、输入捕获、PWM
TIM3
APB1
通用定时器
16位
标准定时功能、输入捕获、PWM
TIM4
APB1
通用定时器
16位
标准定时功能、输入捕获、PWM
TIM5
APB1
通用定时器
32位
标准定时功能、输入捕获、PWM
TIM9
APB2
补充定时器
16位
简化版高级定时器,2个通道
TIM10
APB2
补充定时器
16位
简化定时器,1个通道
TIM11
APB2
补充定时器
16位
简化定时器,1个通道
SysTick
内核专用
系统定时器
24位
Cortex-M4 内核自带定时器(常用于 OS)
notion image
notion image
由图和分频系数可知,AHB的频率是84MHz。APB1的总线频率是42MHz,APB2的总线频率是84MHz。APB1的定时频率就是84MHz,APB2的定时器时钟就是84MHz。
定时器
所属总线
总线频率
实际定时器时钟频率
TIM1
APB2
84 MHz
84 MHz
TIM9
APB2
84 MHz
84 MHz
TIM10
APB2
84 MHz
84 MHz
TIM11
APB2
84 MHz
84 MHz
TIM2–5
APB1
42 MHz
84 MHz
 
 

1.2.2 定时器的结构体配置

思考:那应该如何去配置基本定时器的相关参数??可以通过结构体进行配置,可以査阅stm32f4xx_tim.h头文件中包含了定时器的参数配置结构体,如下图
notion image
  • 设置定时器的分频系数 TIM _Prescaler 范围 0x0000 and 0xFFFF
notion image
  • 设置定时器的计数模式 TIM_CounterMode
    • notion image
    • 基本定时器只能设向上计数
    • 中心对齐模式123:关键寄存器:TIMx_CR1,STM32的中心对齐模式由TIMx_CR1寄存器的CMS[1:0]位控制:
      • STM32的中心对齐模式由 TIMx_CR1 寄存器的CMS[6:5] 位控制:
        notion image
      • 中心对齐模式1:首先从0开始向上计数,直到计数值等于自动重载寄存器(ARR)值,产生一个上溢事件,然后计数器开始向下计数,直到计数值为0时,产生一个下溢事件.随后计数器重新从0开始计数,如此循环往复。
      • 中心对齐模式2:其他同中心对齐模式1,更新事件发生在CNT=ARR时,产生一个上溢事件
      • 中心对齐模式3,上升下降都有更新事件。
      • notion image
  • 设置定时器计数周期 TIM_Period 指的是重载寄存器的值
    • 指定要加载到ARR寄存器的值。在下一次更新事件时自动重新加载寄存器。范围0x0000 ~ 0xFFFF(65536)
  • 设置定时器的时钟分频因子 TIM_ClockDivision 在输入捕获的采样使用 不需要设置
  • 设置定时器的重复计数器 TIM_RepetitionCounter 只有高级定时器需要设置
 

1.2.3 定时器编程

如何利用基本定时器实现定时中断的功能?流程可以参考stm32f4xx_tim,c中的代码注释
notion image
  • 打开基本定时器时钟 TIM6和TIM7 在APB1总线,所以打开APB1时钟
    • 设置定时器的相关参数(预分频、计数模式、计数周期)+初始化定时器
      • notion image
        notion image
      • 第一个函数是“真正初始化”定时器(TIMx)的时基(TimeBase)部分,也就是基本定时功能。它会根据你设置好的结构体 TIM_TimeBaseInitStruct 的内容,去配置对应定时器的这些寄存器:ARR,PSC,向上向下计数,CKD(时钟分频因子),计数器重复次数(仅限高级定时器)
      • 第二个函数是将传入的结构体初始化为默认值(不是配置定时器)!这个函数的作用是为了:避免你手动初始化每一个字段(比如忘记设置会有未定义行为);先用它设置默认值,再改你关心的字段,确保结构体内容完整合法。
      • 初始化定时器可以通过TIM_TimeBaseInit实现
      • 函数原型:void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
        • 参数一:TIMx 打算配置参数的定时器号 如TIM6
        • 参数二:TIM_TimeBaseInitStruct 配置时基单元的地址 记得&
    • 设置NVIC的相关参数(中断通道和中断优先级)初始化NVIC
      • notion image
    • 设置定时器中断源
      • notion image
        notion image
        函数原型:void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState)
      • 参数1:TIMx 打算产生中断的定时器号 如TIM6
      • 参数2:TIM_IT 指定定时器的中断源 一般为TIM_IT_Update 表示更新(数到了)中断
      • 参数3:NewState 打开和关闭中断源 ENABLE or DISABLE.
    • 打开定时器
      • 编写定时器中断服务函数 中断服务函数名字去汇编文件复制(检测状态和清除状态)
        • notion image
       
       
       
      练习:根据基本定时器的理解,编写代码实现利用TIM6 每隔 200ms 让 LED0 的电平翻转次,利用TIM7每隔400ms让LED1的电平翻转一次,并对比观察效果是否一致。
      main.c LED1对应9,LED2对应10

      1.2.4 出现的一些问题

      • NVIC初始化的时候,中断通道该怎么选?
        • 产生中断,那么就会有对应的中断服务程序,中断服务程序的名字在启动文件的向量表里面。即发生中断的时候会自动跳转到中断服务程序里面。所以需要打开中断通道之后才能发生中断时跳转。
          • notion image
            notion image
      • 中断服务程序的编写
        • 潜规则:默认要把所有中断服务程序写入stm32f4xx_it.c
          • 检测中断是否发生
            • notion image
            • 函数原型:ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT)
            • 第一个参数,选中的定时器
            • 第二个参数,中断源。
            • 返回值:SET(发生中断),RESET(没有发生中断)
          • 清除中断状态
            • notion image
            • 函数原型:void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT)
            • 第一个参数:选中的定时器
            • 第二个参数:中断源
          • 执行中断服务程序
      • 代码的优化
        • 定时器时间的设置
        •  

      1.3 通用定时器

      notion image
      • 区别在:多种计数模式,多个通道(可以实现定时器互联,GPIO关联),多个功能(输入捕获、输出比较、PWM)
      • 相比于基本定时器而言,通用定时器增加了输入捕获功能以及输出比较功能,并且通用定时器具有独立通道,就可以把GPIO引脚连接到某个通道,利用通道进行输入信号的检测以及脉冲信号的输出。
      • 对于通用定时器 TIM2~TIM5,都挂载在 APB1 定时总线下,定时器的频率为84MHZ,如下图
        • notion image
      • TIM2~TIM5的计数方式有多种可以选择,分别为递增计数、递减计数、递增/递減计数。
        • 递增计数:计数器从0计数到自动重载值(TIMx_ARR寄存器的内容),然后重新从0开始计数并生成计数器上溢事件。(所以填入ARR的实际值需要目标计数值-1)
        • 递减计数:计数器从自动重载值(TIMXARR寄存器的内容)开始递减计数到0,然后重新从自动重载值开始计数并生成计数器下溢事件。((所以填入ARR的实际值需要目标计数值-1))
        • 中心对齐:计数器从0开始计数到自动重载值(TIMxARR存器的内容),生成计数器上溢事件;然后从自动重载值开始向下计数到0并生成计数器下溢事件。
      notion image

      1.3.1 通用定时器框图

      • 内外时钟源选择
      • 滤波:过滤掉不稳定的不需要的波形
      • 边沿检测:比如说设置检测上升沿,信号就是周期信号,两个上升沿可以算出带宽,周期等等
      • 输入捕获的两种途径:第一个就是从通道产生,第二个就跟CNT自产信号比较
      • 输出比较:可以输出PWM(通过脉冲宽度调整得出),调整舵机转动角度
      notion image
      三种功能:
      • 定时功能:基本上使用流程和基本定时器一致,只不过通用定时器的计数方式更灵活
        • 开启定时器时钟(总线时钟)→配置定时器参数(结构体)→配置NVIC(开启中断)→编写中断服务程序→使能定时器
      • 输入捕获:可以把定时器的某个通道连接到GPIO引脚上,然后从外部输入脉冲信号,经过通道的滤波以及边沿检测之后,可以记录某个电平信号的脉冲宽度以及周期
      • 输出比较:可以把定时器的某个通道连接到GPIO引脚上,主动从引脚输出一个固定的脉冲,原理很简单,其实就是计数器(TIM_CNT)如果超过比较寄存器中的值,就可以输出一个电平信号(高电平或者低电平)。
       
      对于TIM9~TIM14而言,也可以进行定时功能,同样也具有输入捕获以及输出比较功能,但是只能采用向上计数的方式,并且相比于TIM2~TIM5,只有2个独立通道。
      notion image
      注意在f401中只有TIM9~TIM11
      notion image

      1.4 高级定时器

      notion image
      • STM32 中提供了TIM1和TIM8 高级定时器,相比于其他定时器而言,高级定时器增加了带可编程死区控制的互补输出、重复计数器、断路输入功能(一般用于控制工业中的高精度电机),但是如果作为定时功能来说,和其他的定时器没啥区别。
      • 高级级定时器TIM1和TIM8都挂载在APB2总线下,所以TIM1和TIM8的定时时钟频率为 168MHZ.
      • 这里需要插一嘴的是STM32F401
        • notion image
       

      1.5 回顾定时器通道

      定时器通道(Channel)是定时器用于“比较输出”或“捕获输入”的子模块,每个通道都能独立配置成PWM、输入捕获、中断等功能。
      notion image
      关键词
      解释
      通道
      定时器内的子功能模块(最多有 4 个)
      通道编号
      CH1、CH2、CH3、CH4
      每个通道功能
      都可以独立配置为 PWM、输入捕获、比较输出等
      配套引脚
      每个通道绑定一个或多个特定的 GPIO 引脚(AF 复用)
      TIM14_CH1
      意味着“TIM14 的通道1”,绑定在 GPIOF9 上(复用)
       
       

      2 PWM

      2.1 PWM的概述

      • PWM的概念
        • PWM 指的是脉冲宽度调制,是一种利用微处理器的数字输出能力控制模拟电路的技术。 PWM 技术的关键参数有两个,一个是频率,一个是占空比。
          • 频率指的是利用STM32的定时器通道输出脉冲的次数
          • 占空比指的是一个周期内高电平所占的比例(准确来说是占空比是“有效电平”在一个周期中所占的比例。默认为有效电平为高电平,所以默认的占空比是高电平时间占一个周期的比例)
          • notion image
        • PWM技术一般用在工业控制领域(控制电机的转速、控制舵机的角度…)
          • notion image
            notion image
       

      2.2 PWM的原理

      notion image
      • 这里的高低电平有效,通道有效和PWM模式有点懵
        • 高/低电平有效
          • 希望”在PWM高电平的时候是“有效”输出,还是低电平的时候为“有效”输出。本质是“有效”状态用什么电平来表示。这里的“有效”不是逻辑 true/false。
          • 极性设置决定“有效电平是高电平还是低电平”;
          • 这个设置定义了当通道处于“有效”状态时,输出什么电平
            • 高电平有效 (CCxP = 0): 当通道有效时,输出高电平 (1)。当通道无效时,输出低电平 (0)。
            • 低电平有效 (CCxP = 1): 当通道有效时,输出低电平 (0)。当通道无效时,输出高电平 (1)。
        • 通道有效/通道无效
          • 输出比较通道是否启用输出,通道有效并输出比较事件生效时,才会有波形输出
          • 通道是否使能决定是否输出波形
          • “通道有效” 表示输出应该驱动为有效电平所定义的那个电平。
          • “通道无效” 表示输出应该驱动为无效电平所定义的那个电平(即有效电平的反)
        • PWM模式
          • PWM模式决定“输出有效电平出现在哪段时间”;
      notion image
      模式
      有效电平极性(OCxPolarity)
      有效区间
      输出波形(向上计数)
      备注
      PWM1
      高电平有效
      先有效再无效
      起始为高,数到 CCR 后为低
      占空比 = CCR / ARR
      PWM1
      低电平有效
      先有效再无效
      起始为低,数到 CCR 后为高
      波形反相
      PWM2
      高电平有效
      先无效再有效
      起始为低,数到 CCR 后为高
      和 PWM1+低极性 等价
      PWM2
      低电平有效
      先无效再有效
      起始为高,数到 CCR 后为低
      和 PWM1+高极性 等价
      • 思考:外设到底是和哪个定时器的哪个通道连接在一起的? 参考芯片数据手册和原理图
        • notion image
        • 可以看到PF9引脚和TIM4_CH1是相关联的,所以就需要把PF9引脚的功能设置为复用功能,复用为定时器功能。
        • notion image
      notion image
      • 函数原型:void GPIO_PinAFConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_PinSource, uint8_t GPIO_AF);
        • 参数一:GPIOx 要复用的引脚端口
        • 参数二:GPIO_PinSource 要复用的引脚编号 GPIO_PinSourcex(x为0~15)
        • 参数三:GPIO_AF 指的是要复用的引脚功能 如GPIO_AF_TIM14
      • 这里是一些寄存器的说明
        • notion image
          notion image
          notion image

      2.3 PWM的使用

      • PWM 的使用流程可以参考 stm32f4xx_tim.c源文件的注释部分,如下图所示
      notion image
      • 打开GPIO引脚的时钟 + 定时器的时钟
        • 配置GPIO引脚(引脚模式需要设置为复用模式)+初始化GPIO
          • 把GPIO引脚功能复用为对应的定时器 配置GPIO为复用模式,但你还需要告诉它“复用成哪个外设”。
            • 配置定时器的定时时间(预分频+自动重装载值)+初始化TIM (PWM周期)
              • 配置定时器通道的参数(输出极性、PWM模式)+初始化定时器通道 TIM_OCxInit()
                • (设置PWM占空比)
                  notion image
                  notion image
                • 这里为什么是50()而不是50-1.因为在说明书里面可以知道<50时候为有效状态,那么就是0~49为有效状态即高电平输出,
                  • notion image
              notion image
              • 使能预装载寄存器 调用TIM_OCxPreloadConfig
                • 意思是:当你用软件修改 CCR1(占空比)时,不会立即生效,而是要等到下一个计数周期开始。这样可防止 PWM 输出中间跳变,保证波形稳定
              • 使能自动重载、预装载寄存器的ARPE位
                • 和上面一样,让 ARR 修改也是等下个周期开始才生效
                • notion image
                  notion image
                • 函数原型:void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx,Functionalstate Newstate)
              • 打开定时器TIM_cmd(ENABLE)
                • 注意:PWM技术使用的是定时器通道的输出比较功能,所以不需要配置NVIC,也不需要编写中断服务函数。
                  • notion image
                  • 前面我们设置了有效电平是高电平,使用的模式是PWM1(CNT<CCR1时输出有效电平)。然后设置PSC=83999和Period(ARR)=100-1,即一次pwm周期为100ms.CNT从0数到99.设置占空比为50%,即有效电平(高电平为0~49),即前0~49的时候输出高电平,后面50~99的时候输出低电平。
                  • 这个函数的本质就是设置CCR1寄存器的值,一张寄存器的图如下。这里TIM_SetCompare1(TIM14, 30); // 设置 CCR1 = 30。所以CNT<30的时候为高电平
                    • 寄存器名称
                      中文解释
                      作用
                      ARR
                      自动重装寄存器
                      控制一个 PWM 周期的总时间(最大计数值)
                      CCR1
                      捕获/比较寄存器1
                      控制 PWM 中高电平持续的时间 → 决定占空比=CCR1/(ARR+1)
                      CNT
                      计数器
                      实时计数值,从 0 递增到 ARR,或递减(根据模式)
                      PSC
                      预分频器
                      控制定时器的计数速度(时钟分频)
                      notion image
                完整代码如下:

                2.4 回顾GPIO

                2.4.1 什么是GPIO

                GPIO(General Purpose Input/Output)是通用输入输出引脚,是你与外设通信、控制电平状态、读取信号的最基础接口。
                STM32 的每一个 GPIO 引脚都是可配置的,并具有多种功能:
                功能模式
                说明
                输入模式
                读取外部信号(按钮、电平、传感器)
                输出模式
                输出高/低电平(控制LED、继电器等)
                复用功能
                用作其他外设(比如 TIM/PWM、USART、SPI)输入输出
                模拟功能
                用于 ADC/DAC 模拟输入输出

                2.4.2 GPIO配置结构体GPIO_InitTypeDef

                stm32使用一个结构体来配置引脚
                字段
                作用
                GPIO_Mode
                设置工作模式(输入/输出/复用/模拟)
                GPIO_OType
                输出类型(推挽 / 开漏)
                GPIO_PuPd
                上拉 / 下拉 / 无
                GPIO_Speed
                输出速度(驱动能力)
                GPIO_Pin
                要配置的具体引脚(如 GPIO_Pin_9)
                • GPIO_OType —— 设置输出类型(仅当为输出或复用模式时有意义)
                  • 类型
                    说明
                    典型用途
                    推挽
                    可输出高或低电平,驱动能力强
                    PWM、普通IO
                    开漏
                    只能输出低电平,高电平靠上拉
                    I2C总线
                • GPIO_PuPd —— 内部上拉下拉电阻配置
                  • 类型
                    说明
                    用途举例
                    引脚悬空时电平不确定(容易漂浮)
                    外部电路有自己电平控制
                    上拉
                    默认高电平适合捕获下降沿
                    按键接地、I2C
                    下拉
                    默认低电平,适合捕获上升沿
                    捕获高电平脉冲
                • GPIO_Speed —— 输出速度(实际上是驱动能力)
                  • 并不是“波形切换频率”,而是驱动速率电磁干扰控制有关。
                    速度
                    用途
                    低速
                    慢速IO、LED、蜂鸣器
                    高速/非常高速
                    PWM、SPI、USART、频繁变化的信号线

                2.4.3 GPIO配置流程

                步骤
                内容
                1
                开启 GPIO 时钟(RCC_AHB1PeriphClockCmd
                2
                配置结构体 GPIO_InitTypeDef 的各项参数
                3
                调用 GPIO_Init() 应用配置
                4
                如需复用功能,还需 GPIO_PinAFConfig() 设置引脚与外设的连接关系

                2.4.4 为什么需要配置GPIO?

                • 大多数引脚都是多功能复用引脚(比如一个引脚可以做GPIO(普通IO输入输出),也可以当作UART、PWM、ADC等外设)。你想让某个引脚用于定时器输出PWM信号(就是定时器产生PWM波,但是输出靠GPIO口),或者捕获输入信号的高电平宽度,那它就不能当作普通GPIO使用,必须:
                notion image
                • 回顾一下推挽输出:内部同时有高电平驱动和低电平下拉能力
                  • 在“推挽”模式下,引脚可以主动输出高电平(推)或低电平(拉),通过控制两个开关器件(如上下MOS管)交替导通,实现稳定、强驱动能力的电平输出。
                • 回顾一下下拉:内部下拉电阻,在引脚悬空时自动拉低,常用于输入捕获模式,比如用来捕获一个高电平脉冲宽度,低电平默认有助于识别高电平跳变。
                  • “下拉”是一种电气属性,在 输入模式或复用模式下,当引脚悬空或未被外部驱动时,会通过内部连接一个电阻到 GND,把引脚电平拉低
                • GPIO和定时器的关联
                  • 定时器负责生成PWM波形,GPIO引脚负责把波形“搬运”到芯片外部。
                  • 必须通过GPIO的“复用功能”把定时器的输出通道映射到某个具体的引脚上,二者才能打通。
                   
                   

                  3 PWM驱动舵机

                  notion image
                  • 注意:需要20ms的脉冲宽度
                  • 按照这里的预分频和单次计时周期可以得出总计时时长20ms计数2000次(写入Period(ARR寄存器))
                  • 根据角度对应的高电平算出对应计数的次数(写入一度对应的次数,这样可能会得到小数,往误差小的上取和下取),写入CCR寄存器,使用SetCompare
                  • 实现顺时针和逆时针调节(延时观察现象)
                  • 一些有点遗忘的知识点
                    • 名称
                      中文名
                      控制内容
                      ARR (Auto-Reload Register)
                      自动重装载寄存器
                      控制 PWM 的周期(计数最大值)
                      CCRx (Capture/Compare Register)
                      捕获比较寄存器
                      控制 PWM 的占空比(什么时候翻转)
                    • 预装载寄存器机制”:你写进去的新值暂时放在一个影子寄存器中等当前计数周期结束(更新事件UEV到来)时再统一加载。
                    • 启动CCR1的预装载功能:TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);
                      • 如果不开启预装载,你每次调用 TIM_SetCompare1() 改占空比,立即生效,可能会导致 PWM 信号瞬间跳变。
                      • 如果开启预装载(推荐),那么改完占空比之后,会在下一个更新事件(计数周期结束)时统一生效,平滑安全。
                    • 启动ARR的预装载功能:TIM_ARRPreloadConfig(TIM3, ENABLE);
                  • (了解一下)时钟分频因子的知识点:TIM_BaseStruct.TIM_ClockDivision = TIM_CKD_DIV1
                    • TIM_ClockDivision用于“数字滤波器”相关功能(例如输入捕获、编码器模式等)时,对定时器时钟的进一步分频。它不影响基本的 PWM 输出节拍。
                      • 它控制的是定时器的内部采样时钟(tDTS),不是主时钟频率(你设定的 prescaler)。
                      • 主要用于数字滤波器的采样节拍,比如输入捕获时用于消除抖动。
                        • notion image
                  • 在我们配置TIM的PWM模式时
                    • TIM_OCStruct.TIM_Pulse = 1500; // 设置1.5ms为中间值,一次为1us
                      • 设置了PWM输出的“高电平持续时间”
                        • 更直白一些是:在每个PWM周期内,输出高电平持续N个计数器周期。
                        • 更底层一些:设置每个周期里的CCR为1500.(ARR=20000-1)
                        • 占空比为CCR/(ARR+1)
                      • 这个地方是初始化就固定好了占空比
                      • VS:TIM_SetCompare1(TIM3, new_value);
                        • 前者初始化使用,在结构体里面
                        • 后者运行时使用,实时修改占空比,即实时修改CRR。new_value=CRR

                  4 PWM驱动呼吸灯

                  • 呼吸灯需要实现的效果:从暗到亮再到暗再到亮…
                    • 占空比:从小到大再从大到小,占空比就对应pwmVal这个变量
                    • dir:控制方向:1,正在变亮pwmVal++;0正在变暗,pwmVal—
                   
                   

                  5 小组资料与补充

                  5.1 PWM概念

                  • Pulse Width Modulation,脉冲宽度调制
                    • 一种利用微处理器的数字输出来对模拟电路进行控制的技术
                      • notion image
                      • 通过物理可以知道高低电平跳变可以等效电压(图一中虚线部分)不同,这样亮度/转速都不同。可以调节高电平时间来确定。
                      • 一般等效是线性的即50%占空比,高电平是5V,低电平是0V,那么等效电压就是2.5V。使用PWM波形就可以在数字系统等输出等效模拟量

                  5.2 配置时基单元

                  notion image
                  • 预分频器(Prescaler)
                    • 接收一个输入时钟信号CK_PSC),并将这个信号分频(降低为原来的1/N),产生一个频率较低的输出时钟信号CK_CNT
                    • 实际分频系数 = 预分频值(PSC) + 1
                      • CK_CNT = CK_PSC / (PSC+1)
                      notion image
                  • 计数器(CNT)
                    • 计数时钟每来一个上升沿,(在向上计数模式中)计数器的值加一,不断自增直至达到目标值
                    • 通用和高级定时器还支持向下计数模式和中央对齐模式。
                  • 自动重装寄存器(ARR)
                    • 存储计数目标值(计时总时长)

                  5.3 输出比较(OC:Output Compare)

                  输出比较:Output Compare 输入捕获:Input Capture
                  输入捕获/输出比较单元:Capture/Compare
                  • 输出比较可以通过比较CNT与CCR寄存器值的关系,来对输出电平进行置1、置0或翻转的操作,用于输出一定频率和占空比的PWM波形
                    • CNT就是时基单元的计数器,CCR就是捕获/比较寄存器(输入捕获和输出比较共用),当使用输入捕获时就是捕获就是捕获寄存器,当输出比较的时候就是比较寄存器
                    • 以输出比较为例,当CNT<>CCR的时候,就会输出相应的0,1电平,从而形成周期性波形。
                  • 每个高级定时器和通用定时器都拥有4个输出比较通道
                  • 高级定时器的前3个通道额外拥有死区生成和互补输出的功能,这个是用来驱动三相无刷电机的
                  • 通用定时器的输出比较电路
                    • notion image
                      左边是CCR和CNT比较的结果,右边就是输出比较电路,最后通过TIM_CH1输出到GPIO上。
                      notion image
                    • REF:参考信号,此时为一个频率、占空比均可调的PWM波形
                    • 极性选择:寄存器写0时信号电平不翻转,写1信号通过一个非门取反,输出信号就是一个高低电平反转的信号。
                    • 输出使能电路。OC1引脚这个就是CH1通道的引脚,引脚定义表里就可以知道具体是哪个GPIO口了。
                    • 什么时候给RED高/低电平?输出模式控制器:输入时CNT和CCR的比较结果,输出是REF的高低电平。通过寄存器OC1M[2:0]配置
                      • notion image
                        匹配时电平翻转,可以输出一个50%的PWM波形。更新事件两次为一个周期。
                        这里可以看到PWM模式1和PWM模式2是相反的两个波形。然后再后面的极性选择的地方又可以选择一次输出。所以PWM模式1的正极性输出和PWM模式2的反极性输出最终输出结果是一样的。
                        notion image
                        CCR是我们自己设置的,CNT每次+1都在不断与CCR进行比较,蓝色线是CNT的值,黄色线是ARR的值,红色线是CCR的值。绿色线就是输出。REF就是一个频率可调,占空比可调的PWM波形。
                  • 高级定时器输出比较电路
                    • notion image
                    • REF:参考信号,此时为一个频率、占空比均可调的PWM波形
                    • 极性选择:寄存器写0时信号电平不翻转,写1信号通过一个非门取反,输出信号就是一个高低电平反转的信号。
                    • 外部会接一个电路:MOS管(大功率开关),上接高电平,下接地。左边给电平信号,例如给高电平,MOS管导通,低电平MOS管断开,即最基本的推挽输出。中间是输出
                      • 上管导通下管断开,输出为高电平。上管断开,下管导通,输出为低电平。如果上下管都导通,电源短路。
                      • 两个这样的推挽电路就构成了H桥电路,可以控制直流电机正反转。三个推挽电路就可以驱动三相无刷电机。
                    • OC1和OC1N是两个互补的输出端口,控制推挽输出中上下管的导通和关闭
                    • 死区发生器:上下管导通的时候可能会因为器件反应不理想,推挽输出上下导通的时候短路。某一个管关闭的时候,延迟一小段时间再导通另一个管
                  • 计算公式
                    • notion image
                    • 占空比(Duty)=CCR/(ARR+1)
                      • 因为CNT<CCR的时候有效,比如说CCR=50,那么0~49就是有效的,共50个节拍,就是CCR的值。但是ARR填入的时候需要-1,因为是从0开始计数。
                    • PWM频率(Freq)(向上计数且为PWM模式1)
                    • PWM周期(注意这里的CCR:为占空比所对应的计数次数。而真正的周期为ARR的值
                      • 实际频率CK_CNT=CK_PSC/(PSC+1),即1s多少次
                      • 周期T=(ARR+1)/CK_CNT→多少秒
                      • Freq=CK_CNT/(ARR+1)=CK_PSC/(PSC+1)/(ARR+1)
                    • 分辨率:占空比的最小变化单元(CCR能调的步长)
                      • 分辨率=1/(ARR+1)

                  5.4 电机和电调

                  • 基本的电磁知识
                    • 电流的磁效应:通有电流的导线会在其周围产生磁场
                    • 安培右手定则:可判断磁场的方向
                    • 磁场可以产生“力”:洛伦兹力。通电导线+磁场=会转动
                    • 磁铁:能产生并改变磁场方向,与永磁体有相同的性质(同极相斥,异极相吸)->在有刷电机中,电磁铁充当转子,不断转动;在无刷电机中,电磁铁作为定子,不会转动。

                  5.4.1 电机

                  • 有刷电机
                    • “通电线圈”变成转子(能动的部分),再放进一个磁铁结构(固定的部分,叫定子)中。
                    • 主要结构:定子+转子+电刷,通过旋转磁场获得转动力矩,从而输出动能。电刷与换向器不断接触摩擦,在转动中起到导电和换向作用。
                      • notion image
                        notion image
                    • 为什么需要电刷和换向器
                      • 导线在磁场中,电流方向决定了力的方向。如果我们不改变电流方向,它转一圈就会卡住。所以需要:电刷 + 换向器的组合:在转动过程中不断“切换电流方向”,让磁力持续产生扭矩 ➜ 就能一直转下去
                    • 有刷电机采用机械换向,磁极不动,线圈旋转。电机工作时,电磁铁作为转子,在其两侧各放置一块弧形永磁体,分别使他们的N极和S极指向转子,固定不动,称为定子。给转子通入电流,产生磁场,由于同极相斥,转子就会开始转动,转到一定位置时,就会由于异极相吸而停住。如果要让转子继续转动,就需要改变线圈中的电流方向,来切换转子中的磁极方向。
                    • 如何改变线圈中的电流方向:使用换向片和电刷。换向片固定在转子中轴上,与线圈相连,而电刷在弹簧的作用下紧贴换向片,且位置固定不动,并与电源相连。当线圈磁场方向需要反转时,两个电刷就各自切换所接触的换向片,线圈中的电流方向就发生了反转,磁场方向也随之反转。
                    • 缺点:相互滑动,会摩擦碳刷,造成损耗,需要定期更换碳刷;碳刷与线圈接线头之间通断交替,会发生电火花,产生电磁破,干扰电子设备。
                  • 无刷电机
                    • notion image
                    • 与有刷电机刚好相反,无刷电机使用永磁铁作为转子,线铁芯制成的电磁铁作为定子。采取电子换向线圈不动,磁极旋转使用控制电路不断改变线圈的通电情况,同极相斥驱动转子不断地转动下去。(三相电路+霍尔传感器+MCU做电子换向)“线圈不动,磁铁动;电流不自己换方向,由MCU控制。”
                    • 结构对比:
                      • 项目
                        有刷电机
                        无刷电机
                        转子(会转)
                        线圈(电磁铁)
                        永磁铁(通常是多极磁钢)
                        定子(固定)
                        永磁铁
                        多相线圈(ABC三相绕组)
                        换向方式
                        物理换向:电刷 + 换向器
                        电子换向:MCU + 霍尔传感器
                    • MCU + 霍尔传感器 + 三相驱动 ➜ 电子换向系统组成
                      • 部件
                        作用
                        MCU 或驱动芯片
                        控制哪一相线圈什么时候通电
                        霍尔传感器
                        检测转子位置,告诉MCU“磁铁转哪了”
                        三相电路
                        负责将电流送入 A、B、C 三组线圈
                    • 工作过程详细分解(以三相120°驱动为例子)
                      • 三相驱动工作
                      • notion image
                      • 霍尔传感器工作
                        • notion image
                    • 无刷电机如何检测转子所处的位置:
                      • ①应用反电动势效应,电调并没有让所有的线圈都同时导通,当转子磁铁经过没有导通的线圈时,就会给这个线圈感应出电压,然后被控制器检测到,这个过程就像是在发电,电调会一直检测绕组,看它何时供上了电,何时发出了电,从而确定转子的相对位置。
                      • ②使用霍尔传感器来检测转子相对于定子的位置。检测到转子相对于定子的位置之后就能适时切换线圈中电流的方向,保证产生正确方向的磁力,来驱动电机。消除了有刷电机的缺点。

                  5.4.2 电调

                  • 电调(ESC):Electronic Speed Controller,电子调速器
                    • 电调(ESC)是“给无刷电机喂电”的大脑和双手——把直流电变成能让电机旋转的“节奏感强”的三相交流电
                  • ESC功能如下:
                    • 功能
                      解释
                      💡 整流/变换电流
                      将输入的直流电(电池)转换为三相交流电供电给无刷电机。
                      🧠 控制电机转速
                      根据接收到的控制信号(通常是PWM),改变三相电的频率、占空比,实现电机调速。
                      👂 接受外部指令
                      比如来自飞控、舵机控制器、遥控器接收模块的信号。
                      🛡️ 保护功能
                      如欠压保护、过流保护、堵转保护等,保护电机和电池。
                    • 无刷电机工作必须要有电调,否则是不能转动的。必须通过无刷电调将直流电转化为三相交流电,输给无刷电机才能转动随时改变着固定线圈内部电流的方向,保证它跟永磁体之间的作用力是相互排斥,持续保持转动
                  • 无刷电调的输入和输出
                    • 电调分为有刷电调和无刷电调,无刷电机配合无刷电调使用。
                    • 输入:直流电,两根线,红正黑负,可以接锂电池,或者稳压电路。具体供电需要参考说明书,我们使用的好盈20A电调的供电是2-3节锂电池。输出线中,白色是信号线,用于接受PWM信号,红色接5V,黑色线接地。
                    • 输入端(电源和控制信号)
                      • 引脚
                        说明
                        🔴 红线(VCC)
                        正极供电,一般来自锂电池,如 2S(7.4V)、3S(11.1V)等
                        ⚫ 黑线(GND)
                        地线,和主控板共地
                        ⚪ 白线(Signal)
                        信号线,用来接收来自MCU的PWM信号,比如STM32
                        输入信号是一种“伪舵机信号”:50Hz,周期20ms,高电平宽度控制转速
                        高电平时间
                        意义(一般)
                        1ms
                        停止或最慢转速
                        1.5ms
                        中间速度
                        2ms
                        最大转速
                    • 输出端(接无刷电机)
                      • 线数
                        含义
                        3 根粗线
                        三相交流输出:U、V、W(接到电机任意三根线即可,转向不对就换两根)
                        这些线不是正负极,而是交替输出不同相位的“伪正弦波”或“方波”电流。
                    • 实际使用中的接法(好盈20A)
                      • 类型
                        说明
                        电调型号
                        好盈(Hobbywing)20A 无刷电调
                        电源输入
                        2-3节锂电池(7.4~11.1V)
                        信号输入
                        白色为信号线,控制PWM占空比
                        控制方式
                        舵机 PWM,20ms 周期,控制 0~100% 转速
                        MCU 接法
                        信号线接 TIMx_PWM 输出引脚,GND 共地,电调自带供电不接VCC
                        注意GND共地。
                    • 电调是如何驱动无刷电机的?
                      • 本质上:电调在模拟“旋转磁场”,吸引转子转动,类似你用手轮流推动一个陀螺。
                        • notion image
                      • 电调和PWM占空比的关系
                        • PWM高电平宽度
                          电调反应
                          0.5ms
                          停止、最慢、反转等初始化位置(取决于设置)
                          1ms
                          最低转速
                          1.5ms
                          中间速度
                          2ms
                          最大转速
                        • 这和舵机的控制方式完全一样,兼容舵机控制器。
                    • 注意事项:
                      • notion image
                      • 电调 = 给无刷电机“跳舞打节拍”的节拍器 + 电流放大器。它根据 PWM 信号的节奏不断调节三相电输出模拟旋转磁场,从而驱动永磁体转子不断旋转
                  • 电调的接法:
                    • notion image
                  • 输入:
                    • 第一幅图含香蕉头的两根线是的电源线
                    • 第三幅图是信号线:其中三根一起的线是信号线,接到接收机(?油门控制线)(转接板开了四个口),然后那一根黄色线是(可接入另外一个通道)
                  • 输出:
                    • 第二幅图的电机线和电机随便接。
                  • 关于我们无人机的结构是
                    • 将四个电调电源线的香蕉头剪掉,通过焊接在无人机的金属板上。同时将电池电源线也焊接在板子上,实现电池对四个电调供电。
                    • 这是我们的机架图就明白走线了
                      • 蓝色部分为电池的焊接,红色部分为电调线的连接。
                      • 即实现了电池正极接接电调的四个正极,电池负极接四个电调的负极。
                      • 同时还有转接板的正负极也和电池相连。
                      • 实现了所有元件共地共电源
                  notion image
                  • 设置反推刹车:
                    • 油门最大,上电。听到电调响第一声,油门拉到最低,听到电调第二声、第三声,第三声为反推刹车设置完毕。再推大最大油门,即设置保存好了反推刹车。油门拉到最小后,断电。
                    • 反推刹车就是让电调控制电机“短时间反转”或“短接绕组”,从而实现快速减速或刹停的功能。(适合于小车)
                  • 油门行程校准
                    • 油门推到最大,上电,滴滴两声后,油门拉到低。电机开始最低速转动,则油门行程校准完成。此时不动油门,拨动反推开关,电机反转。关闭反推开关,电机正转。
                   
                  notion image
                  notion image
                  • 电调解锁逻辑与正常启动流程
                  电调接上电池,鸣叫提示音“123”,表示上电正常
                  自检OK,发出长鸣音“哔——”
                  N声短鸣音,表示锂电池节数
                  推油门可随时起飞
                  • 电调解锁:
                  • 初始化信号
                    • STM32上电后,立即输出最低油门PWM信号(脉宽为1000us~2000us)
                  • 接通电调电源
                    • 保持最低油门信号的同时,给电调供电(接电池)。
                      • 电调鸣叫 123 音阶 → 表示检测到最低油门信号
                      • 随后发出 N声短鸣 → 表示检测到的锂电池节数(如3S电池鸣3声)
                      • 最后发出 长鸣"哔——" → 系统准备就绪(解锁完成)

                  5.5 修改寄存器直接驱动电机

                  在 STM32 微控制器中,通过操作寄存器完成电机驱动:
                  是因为这是硬件与软件交互的基础方式。寄存器是 MCU 内部的特定存储单元,直接映射到硬件功能上,通过操作寄存器可以控制硬件的行为。
                   

                  5.5.1 寄存器是什么

                  寄存器是微控制器内部的小型存储器,用于存储特定信息(通常是状态或控制信息)。
                  每个外设模块(如 RCC、GPIO、TIM 等)都通过寄存器暴露功能接口。
                  • 寄存器分类
                    • 控制寄存器:设置硬件的工作模式(如启用/禁用外设、配置时钟源等)。
                    • 状态寄存器:存储硬件当前的状态信息(如中断标志位)。
                    • 数据寄存器:用于存储传输中的数据。

                  5.5.2 操作寄存器的优点

                  1. 高效直接:
                    1. 寄存器操作是直接与硬件交互,速度快且资源消耗低。
                    2. 没有中间抽象层,硬件响应时间最短。
                  1. 标准化接口:
                    1. STM32 的所有外设都通过寄存器暴露功能,统一操作方式便于开发和调试。
                  1. 深入了解硬件:
                    1. 操作寄存器需要阅读数据手册,能更好地理解硬件工作原理。
                    2. 在调试或需要非常精细的控制时,直接操作寄存器更加可靠。

                  5.5.3 代码思路

                  notion image

                  5.6 知识点

                  5.6.1启用GPIOA和TIM2时钟

                  5.6.1.1 内存映射

                  5.6.1.2 GPIO

                  RCC->AHB1ENR |= (1 << 0);
                  //使能 GPIOA 外设的时钟,只有时钟信号被使能后,GPIOA 的寄存器才可以被正确访问和操作。
                  //RCC (Reset and Clock Control)RCC 是 STM32 的时钟管理模块,用于控制所有外设的时钟开关。各个外设的时钟通过 RCC 的寄存器来配置。
                  //GPIO 外设挂载在AHB1总线,(AHB1ENR)AHB1外设时钟使能寄存器,每一位对应一个AHB1上的外设,置 1 代表使能该外设的时钟。
                  //GPIOA 时钟使能对应 AHB1ENR 的第 0 位。置 1 代表使能GPIOA时钟。
                  //位移运算符是 C 语言中的一种二进制操作符:
                  左移运算符 (<<): 将一个数的二进制位向左移动指定的位数。
                  右移运算符 (>>): 将一个数的二进制位向右移动指定的位数。
                  Value<<n:被操作数左移n位
                  notion image
                  按位或操作(|):
                  两个操作数对应位有一个是1,结果就是1;只有两个位都是0,结果才是0;
                  当需要在寄存器的某些位上设置为 1,但不改变其他位的值时,使用按位或。
                  按位与操作(&):
                  两个操作数对应位都为1,结果为1,只要有一个是0,结果就是0;
                  当需要在寄存器中将某些位清零,使用按位与。
                  按位与和取反结合使用:通过对目标位取反,再与原值按位与,就可清楚特定位,同时保护其他位。
                  Q:为什么需要保护其他位?
                  A:在硬件寄存器中,多个位可能控制不同的功能。例如,MODER 寄存器的每两位决定一个 GPIO 引脚的模式。如果直接将目标位清零,而不小心影响了其他位,可能导致:临近引脚的配置被意外更改。不相关的外设功能被误触发。
                  因此,安全的位操作是:仅修改目标位,而不影响其他位。
                  //构造掩码
                  什么是掩码:掩码是一个二进制数,它的每一位对应寄存器中的某一位:
                  • 掩码中值为 1 的位: 表示目标位(我们关心的位)。
                  • 掩码中值为 0 的位: 表示非目标位(我们希望忽略的位)。
                  • 改为二进制码和掩码的优劣
                  为什么需要掩码:掩码可以确保只操纵目标位,避免对非目标位造成干扰,从而达到安全操作寄存器的功能。
                  notion image
                  +4,32位。。。。
                  p137
                  notion image
                  notion image
                  notion image

                  5.7 库函数

                  pwm.c
                  main.c
                  delay.c
                   

                  5.8 寄存器

                  pwm.c
                  pwm.h
                  main.c

                  5.9 汇编

                  1-4-1 USART串口协议2-1 用Keil实现μC/OS-II工程搭建
                  Loading...
                  🐟🐟
                  🐟🐟
                  在坚冰还盖着北海的时候,我看到了怒放的梅花
                  最新发布
                  1-4-1 USART串口协议
                  2025-6-27
                  2-6-1 uC/OSII 在STM32上的移植
                  2025-6-23
                  1-6 定时器与PWM
                  2025-6-21
                  一些工具
                  2025-6-21
                  第5章:链路层和局域网
                  2025-6-19
                  第4章:网络层
                  2025-6-19
                  公告
                  🎉 欢迎来到鱼鱼的博客~ 🎉
                  --- 很高兴认识你~ ---
                  👏一起成为理想中的自己吧!👏