这一节主要学习STM32的输出比较功能,这个输出比较功能主要是用来输出PWM波形,其中PWM波形又是驱动电机的必要条件,所以如果想利用STM32做一些有电机的项目,比如智能车、机器人等,这个输出比较需要重点掌握。

一、基础概念的引入

1. TIM 输出比较的定义

  • TIM 输出比较是指利用 STM32 微控制器中的定时器(TIM)模块,通过比较定时器的计数器(cnt)与捕获 / 比较寄存器(ccr)的值,来控制输出引脚的电平状态。这种功能可以实现精确的时间控制和特定频率、占空比的脉冲宽度调制(PWM)信号输出。
  • 例如,当定时器的计数器值与捕获 / 比较寄存器的值相等时,可以触发特定的事件,如将输出引脚置为高电平或低电平、翻转输出引脚的电平状态等。这在控制电机转速、调节 LED 亮度、产生特定频率的信号等场景中非常有用。
  • 2. 相关术语缩写解释

    1. OC(output compare):代表输出比较。它的主要功能是输出比较可以通过比较CNT(计数器)与CCR(捕获/比较寄存器)寄存器值的关系,来对输出电平进行置1、置0或翻转的操作,用于输出一定频率和占空比的PVM波形。

    2. PWM(Pulse Width Modulation):脉冲宽度调制

    一、PWM 的定义与作用

    PWM 在具有惯性的系统中,可以通过对一系列脉冲的宽度行调制,来等效地获得所需要的模拟参量,常应用于电机控速等领域。在 STM32 中,PWM 主要用于实现对电机转速的控制、LED 亮度的调节等。例如,通过调整 PWM 信号的占空比,可以改变电机两端的平均电压,从而实现不同的转速控制;对于 LED,可以控制其导通时间,进而实现不同的亮度级别。也就是说使用PWM波形用来模拟等效实现一个模拟信号的输出。

    二、PWM 的参数

    1. 频率:PWM 信号的频率表示其周期变化的速度。不同的应用场景需要不同频率的 PWM 信号。例如,在控制电机转速时,通常需要较高频率的 PWM 信号以减少电机的噪声和振动;而在控制 LED 亮度时,频率可以相对较低。

    2. 占空比:占空比是指高电平在一个周期内所占的时间比例。通过改变占空比,可以实现对输出信号平均电压或功率的控制。占空比越大,那等效的模拟电压就越趋近于高电平,占空比越小,那等效的模拟电压就越趋近于低电平,这个等效关系一般来说是线性的。例如,占空比为 50% 时,输出信号的平均电压为电源电压的一半。

    3. 分辨率:分辨率体现了占空比变化的精细程度。较高的分辨率可以实现更精确的控制。在 STM32 中,可以通过设置定时器的相关参数来调整 PWM 的分辨率。

    使用PWM波形,就可以在数字系统等效输出模拟量,就能实现 LED 控制亮度、电机控速等功能了。

    在下图中,这种跳变的数字信号,可以等效为虚线所表示的模拟量的。当这个上面电平时间长一点,下面电平时间短一点的时候,那等效的模拟量就偏向于上面,反之则等效的模拟量就偏向于下面。

    二、定时器的输出比较模块产生PWM波形的过程

    输出比较电路的结构

    1. 通用定时器的输出比较部分电路/高级定时器的第四个通道的输出比较电路

    1. 计数器(cnt)

    2. 计数功能:通用定时器的计数器是输出比较通道的基础,它不断对输入的时钟信号进行计数。计数的方式可以根据需求设置为向上计数、向下计数或向上 / 向下计数模式。例如在向上计数模式下,计数器从 0 开始,每个时钟周期增加 1,直到达到设定的自动重装载值(ARR)后重新从 0 开始计数。

    3. 时钟源选择:计数器的时钟源可以灵活选择,通常来自内部时钟(CK_INT),也可以是外部输入引脚(TIX)、外部触发输入(ETR)或内部触发输入(ITRX)等5。通过对时钟源的选择和相关预分频器的设置,可以调整计数器的计数频率,从而影响输出比较的结果。

    4. 捕获 / 比较寄存器(ccr)

    5. 存储比较值:捕获 / 比较寄存器用于存储与计数器进行比较的参考值。每个输出比较通道都有独立的捕获 / 比较寄存器(CCR1、CCR2、CCR3、CCR4 等),通过设置这些寄存器的值,可以决定输出引脚的电平状态变化的时间点。

    6. 影响输出波形:改变 CCR 的值可以改变输出 PWM 波形的占空比。例如,在 PWM 模式下,如果 CCR 的值较小,那么在计数器计数到该值之前,输出引脚为高电平的时间就较短,从而导致占空比变小;反之,CCR 的值较大时,占空比会变大。

    7. 输出模式控制器

    8. 比较结果处理:当计数器的值与捕获 / 比较寄存器的值进行比较后,输出模式控制器会根据比较结果执行相应的操作。当计数器的值大于或等于 CCR 的值时,就会给这个输出模式控制器传一个信号。然后输出模式控制器就会改变它输出 OC1REF 的高低电平,这个REF是reference的缩写,意思是参考信号吗,接着这个 REF 信号可以前往主模式控制器,可以把这个 REF 映射到主模式的 TRGO 输出上去,不过REF主要还是前往极性选择。

    9. 多种输出模式:输出模式控制器可以通过寄存器配置为多种模式,如普通输出模式、强置输出模式、PWM 模式、单脉冲模式等。其中,PWM 模式是输出比较通道的常用模式,通过不断地比较计数器和 CCR 的值,产生具有特定频率和占空比的脉冲信号。

      1. 冻结模式

      2. 工作原理:当计数器的值(cnt)等于捕获 / 比较寄存器的值(ccr)时,输出参考信号(ocxref)保持为原状态。也就是说,在这种模式下,如果之前输出引脚为高电平,那么在 cnt = ccr 这个时刻及之后,输出引脚的电平状态会保持高电平不变;如果之前为低电平,则保持低电平不变。

      3. 应用场景:该模式适用于需要暂停输出比较操作,且保持当前输出电平的情况。例如,在某个特定的事件发生时,需要暂时停止 PWM 波形的输出,但又不想改变当前的输出电平状态,就可以将输出模式设置为冻结模式。

      4. 匹配时置有效电平模式

      5. 工作原理:当计数器的值等于捕获 / 比较寄存器的值时,将输出参考信号置为有效电平(通常为高电平)。在向上计数模式下,当 cnt 不断增加到与 ccr 的值相等时,输出引脚的参考信号会变为高电平;在向下计数模式下,当 cnt 不断减小到与 ccr 的值相等时,输出引脚的参考信号变为高电平。

      6. 应用场景:这种模式可用于在特定时刻触发一个高电平信号的输出。比如,在某个定时任务完成时,需要产生一个高电平的脉冲信号作为标志,就可以使用该模式。

      7. 匹配时置无效电平模式

      8. 工作原理:与匹配时置有效电平模式相反,当计数器的值等于捕获 / 比较寄存器的值时,将输出参考信号置为无效电平(通常为低电平)。在向上计数和向下计数模式下,其触发条件与匹配时置有效电平模式类似,只是输出的电平状态相反。

      9. 应用场景:常用于需要在特定时刻关闭某个信号的输出。例如,在一个控制系统中,当某个条件满足时,需要关闭一个正在输出的高电平信号,就可以将输出模式设置为匹配时置无效电平模式。

      10. 匹配时电平翻转模式

      11. 工作原理:当计数器的值等于捕获 / 比较寄存器的值时,输出参考信号的电平会发生翻转。例如,如果之前输出引脚为高电平,那么在 cnt = ccr 这个时刻,输出引脚的电平会变为低电平;反之,如果之前为低电平,则会变为高电平。

      12. 应用场景:该模式可以用于产生占空比为 50% 的方波信号,或者在需要周期性地改变输出电平状态的场合。比如,在一些通信协议中,需要发送特定频率的方波信号作为同步信号,就可以使用匹配时电平翻转模式。

      13. 强制为无效电平模式

      14. 工作原理:无论计数器的值与捕获 / 比较寄存器的值的关系如何,输出参考信号都被强制设置为无效电平。也就是说,输出引脚始终保持低电平状态。

      15. 应用场景:当需要完全关闭输出信号时,可以使用该模式。例如,在系统初始化阶段或者出现故障时,为了避免错误的输出信号对系统造成影响,就可以将输出模式设置为强制为无效电平模式。

      16. 强制为有效电平模式

      17. 工作原理:与强制为无效电平模式相反,无论计数器的值与捕获 / 比较寄存器的值的关系如何,输出参考信号都被强制设置为有效电平。即输出引脚始终保持高电平状态。

      18. 应用场景:该模式可用于需要持续输出高电平信号的情况,例如在某些电路中需要提供一个稳定的电源信号或者使能信号时,可以使用强制为有效电平模式。

      19. PWM 模式 1

      20. 工作原理(向上计数):在向上计数模式下,当计数器的值小于捕获 / 比较寄存器的值时,输出参考信号置为有效电平;当计数器的值大于或等于捕获 / 比较寄存器的值时,输出参考信号置为无效电平。

      21. 工作原理(向下计数):在向下计数模式下,当计数器的值大于捕获 / 比较寄存器的值时,输出参考信号置为无效电平;当计数器的值小于或等于捕获 / 比较寄存器的值时,输出参考信号置为有效电平。

      22. 应用场景:PWM 模式 1 常用于控制电机的速度、调节 LED 的亮度等需要精确控制占空比的场合。通过改变捕获 / 比较寄存器的值,可以调整输出信号的占空比,从而实现对被控对象的精确控制。

    蓝色线是CNT的值,红色线是CCR的值,黄色线是ARR的值,再根据PWM模式1的逻辑生成下方的占空比波形图。

    1. PWM 模式 2

    2. 工作原理(向上计数):与 PWM 模式 1 正好相反,在向上计数模式下,当计数器的值小于捕获 / 比较寄存器的值时,输出参考信号置为无效电平;当计数器的值大于或等于捕获 / 比较寄存器的值时,输出参考信号置为有效电平。

    3. 工作原理(向下计数):在向下计数模式下,当计数器的值大于捕获 / 比较寄存器的值时,输出参考信号置为有效电平;当计数器的值小于或等于捕获 / 比较寄存器的值时,输出参考信号置为无效电平。

    4. 应用场景:PWM 模式 2 也是用于产生 PWM 波形,其应用场景与 PWM 模式 1 类似,但由于输出的极性与 PWM 模式 1 相反,所以在一些需要反向控制的场合可能会使用到。

    1. 极性选择

    2. 电平极性控制:极性选择部分用于确定输出引脚的初始电平状态以及在特定条件下电平翻转的方向。通过对极性选择寄存器进行配置,可以选择输出信号的极性。例如,如果将极性设置为高电平有效(给寄存器写1),那么在满足输出条件时信号就会往下走,就是信号通过一个非门取反,那输出的信号就是输入信号高低电平反转的信号;如果设置为低电平有效(给寄存器写0),则满足条件时信号就会往上走,就是信号电平不翻转。

    3. 灵活应用:在一些需要反向输出或者特殊电平逻辑的应用场景中,极性选择功能非常有用。比如,在驱动某些需要反向控制的设备时,可以通过调整极性选择来适配设备的控制要求。

    4. 输出使能电路

    5. 输出控制:输出使能电路用于控制输出引脚是否有效输出信号。只有当输出使能被打开时,输出引脚才会根据输出模式控制器的控制输出相应的电平信号;如果输出使能关闭,即使计数器和 CCR 的比较结果满足条件,输出引脚也不会有信号输出。

    6. 安全保护和节能:输出使能电路可以在不需要输出信号时关闭输出,起到安全保护和节能的作用。例如,在设备的某些工作阶段不需要特定的 PWM 输出时,可以关闭输出使能,避免不必要的信号干扰或能量消耗。

    7. 引脚

    8. 信号输出:经过上述各个部分处理后的信号最终通过输出引脚输出到外部设备。通用定时器的输出比较通道可以连接到微控制器的 GPIO 引脚,以便将生成的 PWM 信号或其他输出信号传输到外部电路中,实现对外部设备的控制,如驱动电机、控制 LED 亮度等。

    9. 复用功能:为了充分利用微控制器的引脚资源,GPIO 引脚通常具有复用功能。在使用通用定时器的输出比较功能时,需要将对应的 GPIO 引脚配置为复用推挽输出模式,以便正确地输出定时器产生的信号。

    2. 高级定时器的前三个输出比较通道

    1. 基本组成部分

    2. 计数器(CNT):这是定时器的核心计数部件,不断对输入的时钟信号进行计数。随着时钟的每一个脉冲,计数器的值会按照设定的计数方式(如向上计数、向下计数或中心对齐计数等)进行增加或减少。它是输出比较的基础,用于和捕获 / 比较寄存器(CCR)的值进行比较,以确定输出的状态。

    3. 捕获 / 比较寄存器(CCR):该寄存器用于存储特定的比较值。高级定时器通常有多个 CCR,对应不同的输出比较通道。通过设置 CCR 的值,可以决定在计数器达到该值时触发特定的输出操作。例如,在 PWM 模式下,CCR 的值决定了脉冲的宽度。

    4. 比较器:将计数器的值与 CCR 的值进行实时比较。当计数器的值与 CCR 的值相等时,比较器会产生一个触发信号,该信号用于控制输出信号的状态变化。比较器的精度和速度对于输出比较的准确性和及时性非常重要。

    5. 输出控制逻辑:根据比较器的输出信号和配置的输出模式,输出控制逻辑决定最终的输出信号状态。它可以将输出信号置为高电平、低电平或进行翻转等操作,以实现不同的输出功能,如 PWM 输出、脉冲输出等。

    6. 高级定时器特有的部分

    7. 死区生成电路(DTG):在驱动三相无刷电机等应用中,为了防止上下两个功率开关器件同时导通导致短路,需要在控制信号之间插入一段死区时间。死区生成电路就是用于在输出比较通道的互补输出信号之间添加一定的延迟时间,确保一个开关器件完全关闭后,另一个开关器件才开始导通。这对于保证电路的安全运行和可靠性非常重要,特别是在高功率、高频率的应用场景中。

    8. 互补输出:高级定时器的前 3 个通道通常具有互补输出功能。这意味着对于每个通道,除了正常的输出信号(OCx)外,还有一个互补的输出信号(OCxN)。这两个信号的电平状态是相反的,适用于需要控制两个互补开关器件的应用,如 H 桥电路等。通过互补输出,可以方便地实现对电机的正反转控制、三相电机的驱动等功能。

    9. 输出极性控制:高级定时器的输出比较通道通常具有输出极性控制功能,可以通过相关的寄存器位或配置选项来设置12。常见的输出极性有高极性和低极性两种选择:

    10. 高极性:当选择高极性时,在输出比较事件发生后,输出信号为高电平(有效电平)。例如,在 PWM 模式 1 下,当计数器的值小于 CCR 的值时,输出为高电平;当计数器的值大于或等于 CCR 的值时,输出为低电平1。

    11. 低极性:选择低极性时,在输出比较事件发生后,输出信号为低电平(有效电平)。即与高极性相反的逻辑,这可以根据具体的应用需求来灵活配置,例如在某些需要反向控制的场景中使用。

    PWM波形的相关参数

    1. PWM 频率计算

    2. 公式:freq = ck_psc / (psc + 1) / (arr + 1)。其中,freq 表示 PWM 的频率,ck_psc 是定时器的输入时钟频率,psc 是预分频值,arr 是重装载值。

    3. 例如,如果 STM32 的内部时钟频率 ck_psc 为 72MHz,预分频值 psc 设置为 36,重装载值 arr 设置为 99,则根据公式计算 PWM 频率为:72000000 / (36 + 1) / (99 + 1) ≈ 1800Hz

    4. 始终对应计数器的一个溢出更新周期,所以 PWM 的频率就等于计数器的更新频率

    5. PWM 占空比计算

    6. 公式:duty = ccr / (arr + 1)。其中,duty 表示占空比,ccr 是捕获 / 比较寄存器的值,arr 是重装载值。

    7. 比如,重装载值 arr 为 100,捕获 / 比较寄存器的值 ccr 为 25,那么占空比为:25 / (100 + 1) ≈ 25%

    8. PWM 分辨率计算

    9. 公式:reso = 1 / (arr + 1)。其中,reso 表示分辨率2。

    10. 例如,重装载值 arr 为 255,则分辨率为:1 / (255 + 1) ≈ 0.4%,这意味着占空比的调节精度为 0.4%,即可以将占空比以 0.4% 的步长进行调整。分辨率越高,占空比的调节就越精确,但同时也会消耗更多的 STM32 性能。

    三、外部设备应用

    舵机:

    1. 基本定义

    2. 舵机也叫 RC 伺服器,通常用于机器人项目,在遥控汽车、飞机等航模中也较为常见。它是一种能够控制角度的电机,带有输出轴,当接收到控制信号时,输出轴会转到特定的位置,并且只要控制信号持续不变,舵机就会保持轴的角度位置不变。

    3. 结构组成

    4. 外壳:保护内部的零部件。

    5. 舵盘:是舵机输出轴连接的部件,其转动角度由输入信号控制。

    6. 直流电机:提供动力,驱动舵机的运转。

    7. 减速齿轮组:用于降低电机的转速,增加扭矩,使舵机能够输出较大的力量来驱动负载。

    8. 角度传感器(如电位计):不断采样测量电机输出轴的位置,并将该信息反馈给控制电路,以便与目标位置进行比较,形成闭环控制系统,确保输出轴的位置准确。

    9. 控制驱动电路:负责接收控制信号,并根据信号来控制电机的转动,以达到期望的角度位置。

    10. 接口线缆:用于连接舵机与控制系统,传输电源和控制信号。

    11. 控制原理

    12. PWM信号输入到控制板,给控制板一个指定的目标角度,然后,这个电位器检测输出轴的当前角度,如果大于目标角度,电机就会反转;如果小于目标角度,电机就会正转,最终使输出轴固定在指定角度。

    13. 舵机的角度控制是通过改变输入的脉冲信号的占空比来实现的。一般需要一个周期约为 20ms 的脉宽调制(PWM)信号,其中高电平的时间决定了舵机的角度。输入一个PWM波形,输出轴固定在一个角度。-90度~90度舵机的控制信号与角度的对应关系如下:

    14. 脉冲宽度为 0.5ms 时,舵机的输出轴将移至 0-90度位置;

    15. 脉冲宽度为 1ms 时,舵机的输出轴将移至 -45度位置;

    16. 脉冲宽度为 1.5ms 时,舵机的输出轴将移至 0 度位置;

    17. 脉冲宽度为 2ms 时,舵机的输出轴将移至 45度位置;

    18. 脉冲宽度为 2.5ms 时,舵机的输出轴将移至 90 度位置;

    19. 这里PWM波形,它其实是当做一个通信协议来使用的,与前文用PWM等效一个模拟输出,关系不大,把PWM当成一个通信协议,也是一个比较常见的应用。

    20. 硬件电路:

  • 黑色(电源负极接地):接地引脚,为舵机提供参考电位。

  • 红色(电源正极):连接 5V 电源,为舵机提供工作电压。一般电机都是大功率设备,它的驱动电源也必须是一个大功率的输出设备。如果能单独提供供电更好,如果不能,也要注意电源的功率是否能达标。

  • 黄色(信号线):接 PWM 信号,用于接收来自微控制器的控制信号。PWM 信号输入的控制板,给控制板一个指定的目标角度,然后电位器检测输出轴的当前角度,通过电机的正反转使输出轴固定在指定角度。

    1. 应用领域

    由于舵机具有角度控制精确、输出力大、能耗与机械负荷成正比等特点,在机器人、遥控模型等领域应用广泛3。例如在机器人项目中,可用于控制机器人的关节运动,使机器人能够完成各种动作;在遥控汽车、飞机等模型中,可用于控制方向舵、油门等部件,实现模型的精确操控3。

    直流电机

    一、 功能介绍

    1. 功能特点
    2. 直流电机是一种将电能转化为机械能的装置,通电就转。

    3. 有两个电极,当电极正接时电机正转,电极反接时电机反转。例如视频中套件里的 130 直流电机,有两个硬件接线端,一边接正,一边接负电机就朝一个方向转,对调正负极则电机朝另一个方向转。

    4. 功率特性
    5. 直流电机属于大功率器件,GPIO 口无法直接驱动,需要配合电机驱动电路来操作。电机这类器件基本上都属于大功率设备,必须要加驱动电路才能控制。

    二、电机驱动电路

    1. 市场选择多样
    2. 现在市面上有很多驱动电路可以选择,比如 TB6612、DRV8833、L9110、L298 等等,这些都是比较常见的电机驱动芯片,功率可以做得更大一些。

    3. 自主设计
    4. 也可以自己用 MOS 管设计电路,可根据实际需求进行研究选择。

    三、电机驱动模块的硬件电路

    1. 电源相关引脚
  • VM(驱动电压输入端):范围为 4.5 – 10V(与电机额定电压保持一致),为电机提供驱动电压。电机作为大功率器件,需要较高的电压来保证其正常运转。

  • VCC(逻辑电平输入端):范围为 2.7 – 5.5V(与控制器的电源保持一致),为电机驱动模块的控制电路提供合适的工作电压。这个电压主要用于逻辑控制部分,确保控制信号的稳定输入和处理。

  • GND(电源地端):为整个电路提供参考电位,保证电路中各个部分的电压稳定。所有的电路都需要接地,以确保电流能够正常回流,避免出现电压不稳定或电气干扰的问题。

  • 2. 控制引脚
  • STBY(正常工作 / 待机状态控制输入端):当该引脚为高电平时,电机驱动模块处于正常工作状态;当为低电平时,模块进入待机状态,此时可以降低功耗。这个引脚可以根据实际需求来控制电机驱动模块的工作状态,例如在不需要电机运行的时候,可以将其设置为低电平,进入待机状态,以节省能源。

  • PWMA 和 PWMB(PWM 信号输入端):分别是两路电机的 PWM 信号输入端。通过输入不同占空比的 PWM 信号,可以调节电机的转速。PWM 信号的占空比越大,电机转速越高;反之,占空比越小,电机转速越低。这两个引脚用于接收来自微控制器的 PWM 信号,以实现对电机的精确速度控制。

  • AIN1、AIN2、BIN1、BIN2(电机控制模式输入端):这些引脚是电机控制模式输入端。通过不同的输入组合,可以控制电机的正转、反转、制动和停止等状态。例如,当 IN1(对应 AIN1 或 BIN1)为高电平,IN2(对应 AIN2 或 BIN2)为高电平时,电机处于制动状态;当 IN1 为高电平,IN2 为低电平时,电机处于正转状态;当 IN1 为低电平,IN2 为高电平时,电机处于反转状态;当 IN1 和 IN2 都为低电平时,电机处于制动状态。

  • 3. 电机输出引脚
  • A01、A02 和 B01、B02(电机驱动输出端):分别对应两路电机。当控制信号输入正确时,这些引脚会输出相应的驱动信号,控制电机的运行。例如,在正转状态下,输出端会提供合适的电流和电压,使电机按照预定方向旋转。

  • 四、电机的正反转和转速控制

  • 电机正反转控制

    1. 直流电机正反转原理
    2. 直流电机有两个电极,当电极正接时电机正转,电极反接时电机反转。例如视频中套件里的 130 直流电机,有两个硬件接线端,一边接正,一边接负电机就朝一个方向转,对调正负极则电机朝另一个方向转。
    3. 电机驱动模块引脚与正反转控制
    4. 通过电机驱动模块的引脚可以控制电机的正反转。在电机驱动模块中,输入引脚(如 IN1、IN2、BIN1、BIN2)的不同组合可以控制电机的正转、反转、制动和停止等状态。

    5. 例如,当 IN1(对应 AIN1 或 BIN1)为高电平,IN2(对应 AIN2 或 BIN2)为高电平时,电机处于制动状态;当 IN1 为高电平,IN2 为低电平时,电机处于正转状态;当 IN1 为低电平,IN2 为高电平时,电机处于反转状态;当 IN1 和 IN2 都为低电平时,电机处于制动状态。

    6. 当IN1和IN2存在电压差时,已经满足了转动的前提,但是具体是否会发生旋转,还要看PWM的电平,只有在PWM处于高电平时才转动,处于低电平时不会发生转动。

  • 电机速度控制

    1. PWM 波形控制电机速度原理
    2. 通过改变 PWM(脉冲宽度调制)波形的占空比来控制电机的速度。在具有惯性的系统中,可以通过对一系列脉冲的宽度进行调制,来等效地获得所需要的模拟量。对于电机调速,以一个很快的频率给电机通电断电,当电机的通电时间比例不同时,电机的速度就能维持在不同的水平。

    3. 引脚与速度控制
    4. 在程序里,通过改变特定引脚的输入值来调整 PWM 波形的占空比,从而控制电机的速度。例如,按下按键可以改变电机的速度值,在 OLED 上显示的速度值变化对应着电机速度的改变。

    5. 具体来说,电机驱动模块的 PWM 信号输入端(如 PWMA 和 PWMB)接收来自微控制器的不同占空比的 PWM 信号,从而控制电机的转速。PWM 信号的占空比越大,电机转速越高;反之,占空比越小,电机转速越低。

    五、PWM驱动LED呼吸灯代码详解

    1. 所涉及的PWM的库函数

  • TIM_OCInit()

  • 一、函数作用

    TIM_OCInit()函数用于初始化定时器的输出比较通道。在视频中,这个函数在配置 PWM 驱动 LED 呼吸灯、PWM 驱动舵机和 PWM 驱动直流电机的过程中起到了关键作用。

    二、参数说明

    1. 第一个参数通常是定时器,比如在文中可能出现的TIM3,表示要配置的具体定时器。

    2. 第二个参数是一个指向TIM_OCInitTypeDef结构体的指针,这个结构体用于配置输出比较通道的具体参数。

    三、结构体配置示例

    以文中 PWM 驱动 LED 呼吸灯为例,可能有如下配置:

    展开过程

    这里解释一下各个参数的含义:

  • TIM_OCMode = TIM_OCMode_PWM1:设置输出比较模式为 PWM1 模式,这种模式下可以通过调整脉冲宽度来控制输出的占空比。

  • TIM_OutputState = TIM_OutputState_Enable:使能输出比较通道的输出状态,即允许该通道输出信号。

  • TIM_Pulse = 0:初始脉冲宽度为 0,在后续的程序中可能会根据需要动态调整这个值来实现不同的效果,比如控制 LED 的亮度、舵机的角度或直流电机的速度。

  • TIM_OCPolarity = TIM_OCPolarity_High:设置输出比较通道的极性为高电平有效,即当比较匹配时输出高电平。

  • 四、在文中的应用场景

    1. 在 PWM 驱动 LED 呼吸灯中,通过TIM_OCInit()函数配置定时器通道,使其产生合适的 PWM 信号,然后通过逐渐改变脉冲宽度(占空比)来实现 LED 亮度的渐变效果,模拟呼吸灯的过程。 

    2. 在 PWM 驱动舵机中,配置定时器通道以产生特定频率和占空比范围的 PWM 信号,从而控制舵机的角度。

    3. 在 PWM 驱动直流电机中,利用这个函数配置定时器通道产生 PWM 信号,通过调整占空比来控制直流电机的速度。

  • TIM_OCStructlnit()

  • 一、函数作用:用来给输出比较结构体赋一个默认值的

  • TIM_ForcedOCConfig()

  • 一、函数作用

    在视频中,TIM_ForcedOC1Config函数用于强制设置定时器输出比较通道 1的特定状态。这个函数可以在特定的情况下快速改变输出比较通道 1 的输出状态,以满足特定的控制需求。如果你在运行中想要暂停输出波形并且强制输出高或低电平,可以使用这个函数。

    二、补充

    不过一般使用不多,因为强制输出高电平和设置100%占空比一样,强制输出低电平和设置0%占空比也一样。        

  • TIM_OCPreloadConfig ()

  • 一、函数作用

    在文中,TIM_OCPreloadConfig()函数用于配置CCR寄存器的预装功能(影子寄存器:就是你写入的值不会立即生效,而是在更新事件才会生效)。这个功能在 PWM 驱动 LED 呼吸灯、PWM 驱动舵机和 PWM 驱动直流电机的应用中起着重要作用。

    二、参数及功能详解

    1. 通常,该函数接收两个参数,第一个参数是定时器的句柄,比如在视频中可能是TIM3这样的定时器。第二个参数是一个标志,用于确定预装载功能的状态,常见的可能有TIM_OCPreload_Enable(使能预装载)和TIM_OCPreload_Disable(禁用预装载)。

    2. 当设置为TIM_OCPreload_Enable时,在更新事件发生之前,新的比较值(即控制 PWM 占空比的值)被预先装载到一个暂存寄存器中,但不会立即生效。只有当更新事件发生时,暂存寄存器中的值才会被装载到实际的比较寄存器中,从而更新输出比较的结果。这在需要同步更新多个定时器参数或者确保在特定时刻进行参数更新时非常有用。例如在 LED 呼吸灯的应用中,可以在某个特定的时刻同步更新 PWM 的占空比,以实现更平滑的亮度变化效果。

    3. 在视频中的 PWM 驱动应用场景下,通过合理配置这个函数,可以确保 PWM 输出的稳定性和准确性,避免在参数更新过程中出现不稳定的输出状态。

  • TIM_OCFastConfig()

  • 一、函数功能:用配置快速使能。

  • TIM_CtrlPWMOutputs()

  • 一、函数功能

    仅高级定时器使用,在使用高级定时器输出 PWM 时需要调用这个函数,使能主输出,否则 PWM 将不能正常输出。

  • TIM SetComparel()

  • 一、函数功能

    TIM_SetCompare()可能用于设置定时器(CCR)的比较值,从而控制 PWM 的占空比,此函数并不直接控制占空比,占空比是由CCR和ARR+1共同决定的。实现对连接设备(如 LED、舵机或直流电机)的控制。例如在 LED 呼吸灯应用中,通过不断调整比较值可以改变 LED 的亮度,实现呼吸灯效果。在舵机和直流电机控制中,不同的比较值可以对应不同的角度或速度控制。

    2. PWM初始化函数的编写

  • 第一步,RCC开启时钟,把我们要用的TIM外设和GPIO外设的时钟打开。

  • 第二步,配置时基单元,包括这前面的时钟源选择,和时基单元的配置。

  • 以上两步在定时器定时中断那一部分有所说明,所以可以打开“6-1 定时器定时中断”复制其中的初始化代码,并在PWM初始化函数中复制粘贴,由于我们继续使用TIM2这个定时器故可以不用更改,但是下面这里打开中断和配置NVIC的部分,我们就不需要了。

  • 第三步,配置输出比较单元(输出比较通道),里面包括这个 CCR 的值、输出比较模式、极性选择、输出使能这些参数,在库函数里也是用结构体统一来配置的。

  • 在TIM.c中有四个初始化函数,对应四个输出比较单元(输出比较通道),使用你需要调用的的输出比较通道对应的函数,不同的通道对应的GPIO口也是不一样的,按照GPIO口的要求来,这里本文使用的PA0口,对应的就是第一个输出比较通道,故使用函数TIM_OC1lnit()函数。

    1. TIM_OCInitStructure结构体进行赋值:

    2. TIM_OCMode = TIM_OCMode_PWM1:选择 PWM1 模式。在这种模式下,当计数器值小于比较值时,输出为有效电平(具体电平状态由极性参数决定);当计数器值大于或等于比较值时,输出为无效电平。

    3. TIM_OutputState = TIM_OutputState_Enable:使能输出状态,允许定时器通道输出 PWM 信号。

    4. TIM_Pulse = 0:初始脉冲宽度设置为 0。这个值会在后续的程序中逐渐改变,以控制 LED 的亮度。

    5. TIM_OCPolarity = TIM_OCPolarity_High:设置输出极性为高电平有效。即当满足比较条件时,输出高电平;即有效电平为高电平。

    6. 补充:由于这个结构体中有许多不需要使用的结构体成员,但是我们也没有给他们赋初始值,那么对于这个结构体变量来说,他是一个局部变量那些没有赋初始值的成员,他们的值就是不确定的,这就可能导致一些问题。比如当你想把高级定时器当做通用定时器输出 PWM 时,那你自然就会把这里的 TIM2 改成 TIM1,这样的话,这个结构体原本用不到的成员,现在就需要用了,而这些成员也没有被赋值就会导致高级定时器输出 PWM 出现一些奇怪的问题。所以需要使用函数TIM_OCStructlnit()给结构体所有成员附一个初始值。

      TIM_OCStructInit (&TIM_OCInitstructure) ;

    7. 使用TIM_OC1Init(TIM2, &TIM_OCInitStructure)函数将配置好的输出比较参数应用到定时器 TIM3 的通道 1 上。

  • 第四步,配置GPIO,把PWM对应的GPIO口,初始化为复用推挽输出的配置。

    1. 定义结构体变量

    2. GPIO_InitTypeDef GPIO_InitStructure;定义了一个GPIO_InitTypeDef类型的结构体变量GPIO_InitStructure,这个结构体用于存储 GPIO 引脚的配置信息。

    3. 使能 GPIO 时钟

    4. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);这行代码使能了 GPIOA 的时钟。在 STM32 中,要对某个外设进行操作,首先需要使能该外设的时钟。这样才能确保后续对 GPIOA 引脚的配置操作有效。

    5. 配置引脚参数

    6. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;指定要配置的 GPIO 引脚为 GPIOA 的引脚 0。(或者根据重映射也可以配置为GPIO_Pin_15)

    7. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;设置引脚的模式为复用推挽输出(Alternate Function Push-Pull)。这种模式适用于连接到需要输出特定信号的外设,如定时器产生的 PWM 信号输出到 LED。复用推挽输出可以提供较强的驱动能力,并且在输出高电平和低电平时都有较好的性能。

    8. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;设置引脚的输出速度为 50MHz。较高的速度可以提供更快的信号变化,但也可能带来更多的电磁干扰和功耗,具体速度可以根据实际连接的 LED 和应用需求进行调整。

    9. 应用配置到引脚

    10. GPIO_Init(GPIOA, &GPIO_InitStructure);将前面配置好的参数应用到 GPIOA 上。通过这个函数调用,STM32 的硬件会根据配置参数对 GPIOA 的引脚 0 进行相应的设置,使其准备好接收来自定时器的 PWM 信号并输出到连接的 LED,从而实现对 LED 亮度的控制。

    补充:为什么初始化GPIO口时需要给引脚模式配置成复用推完输出模式?

    对于普通的开漏/推挽输出,引脚的控制权是来自于输出数据寄存器的,按如果想用定时器来控制引脚,那就需要使用复用开漏 / 推挽输出的模式,在这里输出数据寄存器将被断开,输出控制权将转移给片上外设,这里片上外设引脚连接的就是 TIM2 的 CH1 通道,所以,只有把GPIO设置成复用推挽输出,引脚的控制权才能交给片上外设,PWM波形才能通过引脚输出。

  • 第五步,运行控制,启动计数器,输出PWM

  • 最后一步 TIM_Cmd,ENABLE, 启动定时器,PWM 波形就能通过 PA0 输出了。

    补充:ARR、PSC 和 CCR 这三个参数的确定

    ARR(自动重装载寄存器值)、PSC(预分频器值)和 CCR(捕获 / 比较寄存器值)这三个参数的计算是为了产生特定频率、占空比和分辨率的 PWM 波形。

    /**
      * 函    数:PWM初始化
      * 参    数:无
      * 返 回 值:无
      */
    void PWM_Init(void)
    {
    	/*开启时钟*/
    	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);			//开启TIM2的时钟
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);			//开启GPIOA的时钟
    
    	/*GPIO初始化*/
    	GPIO_InitTypeDef GPIO_InitStructure;
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;		//GPIO_Pin_15;
    	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    	GPIO_Init(GPIOA, &GPIO_InitStructure);							//将PA0引脚初始化为复用推挽输出	
    																	//受外设控制的引脚,均需要配置为复用模式		
    	
    	/*配置时钟源*/
    	TIM_InternalClockConfig(TIM2);		//选择TIM2为内部时钟,若不调用此函数,TIM默认也为内部时钟
    	
    	/*时基单元初始化*/
    	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;				//定义结构体变量
    	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;     //时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
    	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数器模式,选择向上计数
    	TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;					//计数周期,即ARR的值
    	TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1;				//预分频器,即PSC的值
    	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;            //重复计数器,高级定时器才会用到
    	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);             //将结构体变量交给TIM_TimeBaseInit,配置TIM2的时基单元
    	
    	/*输出比较初始化*/
    	TIM_OCInitTypeDef TIM_OCInitStructure;							//定义结构体变量
    	TIM_OCStructInit(&TIM_OCInitStructure);							//结构体初始化,若结构体没有完整赋值
    																	//则最好执行此函数,给结构体所有成员都赋一个默认值
    																	//避免结构体初值不确定的问题
    	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;				//输出比较模式,选择PWM模式1
    	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;		//输出极性,选择为高,若选择极性为低,则输出高低电平取反
    	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;	//输出使能
    	TIM_OCInitStructure.TIM_Pulse = 0;								//初始的CCR值
    	TIM_OC1Init(TIM2, &TIM_OCInitStructure);						//将结构体变量交给TIM_OC1Init,配置TIM2的输出比较通道1
    	
    	/*TIM使能*/
    	TIM_Cmd(TIM2, ENABLE);			//使能TIM2,定时器开始运行
    }

    3. main.c函数的编写

    一、包含头文件和函数调用准备

    1. #include "pwm.h":包含了 PWM 模块的头文件,以便在主文件中使用 PWM 相关的函数和定义。

    2. 在主函数中调用pwm_init();:初始化 PWM 模块,这个函数在pwm.c文件中实现,用于配置定时器和相关参数以产生 PWM 信号。

    二、主函数中的循环实现呼吸灯效果

    1. 定义变量和循环:

    2. uint8_t i;:定义一个无符号 8 位整数变量i,用于控制占空比的变化。

    3. for (i = 0; i <= 100; i++)for (i = 100; i >= 0; i--):这两个循环分别实现了占空比从 0 增加到 100 和从 100 减小到 0 的过程,从而产生 LED 呼吸灯的效果。

    4. 调用函数改变占空比:

    5. pwm_set_compare1(i);:在循环中调用这个函数,传入当前的i值作为参数。这个函数在pwm.c文件中实现,用于设置定时器通道 1 的比较值(即 CCR 值),从而改变 PWM 的占空比。当i值从 0 增加到 100 时,占空比逐渐增大,LED 逐渐变亮;当i值从 100 减小到 0 时,占空比逐渐减小,LED 逐渐变暗。

    6. delay_ms(10);:在每次循环中调用这个函数,引入一个 10 毫秒的延迟,以控制呼吸灯变化的速度。如果没有这个延迟,呼吸灯变化会非常快,难以观察到效果。

             

    /**
      * 函    数:PWM设置CCR
      * 参    数:Compare 要写入的CCR的值,范围:0~100
      * 返 回 值:无
      * 注意事项:CCR和ARR共同决定占空比,此函数仅设置CCR的值,并不直接是占空比
      *           占空比Duty = CCR / (ARR + 1)
      */
    void PWM_SetCompare1(uint16_t Compare)
    {
    	TIM_SetCompare1(TIM2, Compare);		//设置CCR1的值
    }

    #include "stm32f10x.h"                  // Device header
    #include "Delay.h"
    #include "OLED.h"
    #include "PWM.h"
    
    uint8_t i;			//定义for循环的变量
    
    int main(void)
    {
    	/*模块初始化*/
    	OLED_Init();		//OLED初始化
    	PWM_Init();			//PWM初始化
    	
    	while (1)
    	{
    		for (i = 0; i <= 100; i++)
    		{
    			PWM_SetCompare1(i);			//依次将定时器的CCR寄存器设置为0~100,PWM占空比逐渐增大,LED逐渐变亮
    			Delay_ms(10);				//延时10ms
    		}
    		for (i = 0; i <= 100; i++)
    		{
    			PWM_SetCompare1(100 - i);	//依次将定时器的CCR寄存器设置为100~0,PWM占空比逐渐减小,LED逐渐变暗
    			Delay_ms(10);				//延时10ms
    		}
    	}
    }
    

    4. 有关重映射的知识

    一、重映射的功能与背景

    在 STM32 中,虽然外设与 GPIO 引脚有默认的复用关系,但 STM32 提供了重映射功能,允许更改外设复用的 GPIO 引脚位置,以避免两个外设引脚冲突等情况。

    例如,如果既要用 USART 的 TX 硬件,又要用 TM2 的特定通道(如 CH3),可能会出现引脚冲突,此时就可以通过重映射功能来调整引脚分配。

    二、重映射的查找与配置方法

    1. 查找重映射位置
    2. 通过打开引脚定义表,可以查看不同外设的端口和 GPIO 的连接关系。比如 TM2 的 CH1 通道默认复用在 PA0 引脚上,若要查找重映射的位置,可以在引脚定义表中找到相关外设的条目。

    3. 以 TM2 的 CH1 为例,在引脚定义表中可以看到有重映射的位置说明,如果使用重映射,它可以从 PA0 挪到 PA15 的硬件上。

    4. 配置重映射
    5. 使用 AFIO(复用功能 I/O)来完成重映射配置。首先要开启 AFIO 的时钟,例如在代码中复制开启时钟的代码行,并将其改为对应 AFIO 的时钟开启(这里改成 AFIO 是 AB2 的设备)。

    6. 使用GPIO_PinRemapConfig函数进行零角重新设配置。该函数的第一个参数有很多重映射的方式选项,每个方式对应的重映射关系可以在手册的 FO 这一节中查看,其中有很多表展示了重映射方式和引脚更改的关系。

    7. 以 TM2 的 CH1 通道重映射到 PA15 为例,在手册中找到 TIM2 复用功能重映射的表,选择部分重新设方式一或完全重新设的参数,将其放入GPIO_PinRemapConfig函数中,第二个参数设为 ENABLE。

    三、重映射需注意的问题

    1. 调试端口复用问题
    2. 当进行重映射到某些引脚时,可能会涉及到这些引脚原本作为调试端口的情况。例如,PA15 上电后默认复用为调试端口 JTDI。

    3. 如果想让这些引脚作为普通的 GPIO 或者复用定时器的通道,需要先关闭调试端口的复用。同样使用GPIO_PinRemapConfig函数来解除调试端口的复用。

    4. 该函数有三个参数用于解除调试端口的复用:

    5. Low_GPIO_RST参数解除 GTI 引脚(PB4)的复用,其他四个端口仍然是调试端口,不能当做 GPIO 来使用。

    6. GTag_Disable参数解除 JTAG 调试端口的复用,在引脚定义里对应 PA15、PB3、PB4 这三个端口变回 GPIO,PA13 和 PA14 仍为 SWD 的调试端口。

    7. SWG_Disable参数把 SWD 和 JTAG 的调试端口全部解除,五个引脚全部变成普通的 GPIO,但此参数千万不要随便调用,一旦调用且下载程序后,调试端口就没有了,只能使用串口下载新的未解除调试端口的程序才能恢复调试端口。

    8. 通常如果需要用 PA15、PB3、PB4 这三个硬件,选择GTag_Disable参数,保留 SWD 的复用。

    /*GPIO重映射*/
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);			//开启AFIO的时钟,重映射必须先开启AFIO的时钟
    GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);			//将TIM2的引脚部分重映射,具体的映射方案需查看参考手册
    GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);		//将JTAG引脚失能,作为普通GPIO引脚使用
    	

    总结:

    1.如果你想让PA15、PB3、PB4这三个引脚当做GPIO来使用的话,那就加一下这里的第一句和第三句,先打开AFIO时钟,再用AFIO将JTAG复用解除掉,这样就行了。

    2. 如果你想重映射定时器或者其他外设的复用引脚,那就加一下这里的第一句和第二句,先打开AFIO时钟,再用AFIO重映射外设复用的引脚,这样就行了。

    3. 如果你重映射的引脚又正好是调试端口,那这三句就都得加上,打开AFIO时钟,重映射引脚,解除调试端口,这样才行

    六、PWM驱动舵机

    1. 电路连接

    1. 舵机接线

    2. 舵机有三根线,分别是棕色线(接地,接面包板的接地)、红色线(5V 正极,需接在 ST-Link 的 5V 输出引脚,不能接面包板的 3.3V 正极,因为面包板输出功率不大带不动电机)、橙色线(PWM 信号,接在 PA1 的引脚,这里选择了 PA1 的通道二,上个代码用的是 PA0 的通道一,可根据需求选择不同通道)。

    3. 由于舵机接口是母口,使用跳线和杜邦线进行转接。棕色线用跳线转接,红色线用公对母的杜邦线转接至 ST-Link 的 5V 输出引脚,橙色线转接后接在 PA1 引脚。

    4. 最后在 PB1 接一个按键,用来控制舵机。

    2. 代码实现

    1. 工程准备

    2. 打开工程文件夹,复制上一个工程(PWM 驱动 LED 呼吸灯),改名为 “6-4 PWM 驱动舵机” 并打开工程。

    3. PWM 初始化修改

    4. 由于要驱动舵机,需要对 PWM 初始化部分进行修改。现在使用 PA1 口的通道二,所以要相应地调整代码中的通道设置。(将GPIO_Pin_0改为GPIO_Pin_1)

    5. pwm_init函数中,找到初始化输出比较单元的部分,根据使用的通道二进行调整。具体来说,使用TIM_OC2Init函数来初始化定时器 TIM2 的通道二,参数设置与通道一类似,包括选择定时器(TIM2)、配置输出比较结构体等。(将TIM_OC1Init改为TIM_OC2Init,将SetCompare1 改成 SetCompare2同时头文件也需要更改)

    6. 结构体成员设置:

    7. oc_mode设置输出比较模式,一般选择TIM_OCMode_PWM1

    8. oc_polarity设置输出比较极性,根据电路需求选择高电平或低电平有效。

    9. output_state设置输出使能,通常设置为使能状态。

    10. pulse设置 CCR 寄存器的值,这个值会影响 PWM 的占空比,对于舵机的控制,需要根据舵机的要求来设置合适的占空比范围。

    11. ARR、PSC 和 CCR 参数的确定:由于要求产生一个周期为20ms (频率为50Hz),高电平时间0.5ms~2.5ms,由于此时(PSC+1)和(ARR+1)不是一个固定的值,可以自己找一个满足要求而且好计算的值,经过计算得到(PSC+1)=72,(ARR+1)=20,此时CCR=500就是0.5ms,CCR=2500就是2.5ms是比较合适的

      TIM_TimeBaseInitStructure.TIM_Period = 20000 - 1;				//计数周期,即ARR的值
      TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;				//预分频器,即PSC的值
      TIM_OCInitStructure.TIM_Pulse = 0;								//初始的CCR值

    12. 舵机控制逻辑: 

    13. 通过调整 CCR 寄存器的值来控制 PWM 的占空比(利用函数TIM_SetCompare2()来控制),从而实现对舵机的控制。具体的占空比范围需要根据舵机的规格和要求来确定。

    14. 如果要实现按键控制舵机,可以在程序中添加按键检测的代码逻辑。当检测到按键按下时,改变 CCR 寄存器的值,从而改变 PWM 的占空比,进而控制舵机的角度或动作。

    15. 舵机模块的封装

    1. 继承PWM的功能,所以开头写上#include "PWM.h"

    2. 舵机初始化函数(void Servo Init(void))

    功能:将其PWM底层进行初始化

    /**
      * 函    数:舵机初始化
      * 参    数:无
      * 返 回 值:无
      */
    void Servo_Init(void)
    {
    	PWM_Init();									//初始化舵机的底层PWM
    }

    3. 舵机设置角度函数(void Servo_SetAngle(float Angle))

    功能:根据输入的角度值Angle来设置舵机的角度。通过调整输出到舵机的 PWM 信号的占空比,实现对舵机角度的精确控制。

    参数:这里0度对应500,180度对应2500,这里首先我们要对Angle进行缩放,0到180是180的范围,500到2500是2000的范围,所以这里Angle/180*2000,就得到目标比例了,再加一个偏移,+500,这样就完成了0~180到500~2500的映射

    /**
      * 函    数:舵机设置角度
      * 参    数:Angle 要设置的舵机角度,范围:0~180
      * 返 回 值:无
      */
    void Servo_SetAngle(float Angle)
    {
    	PWM_SetCompare2(Angle / 180 * 2000 + 500);	//设置占空比
    												//将角度线性变换,对应到舵机要求的占空比范围上
    }
    

    1. 主函数的修改

    不再调用PWM模块的函数这样不够直观,改成Servo模块。

    再添加KEY模块和OLED模块完善代码。

    #include "stm32f10x.h"                  // Device header
    #include "Delay.h"
    #include "OLED.h"
    #include "Servo.h"
    #include "Key.h"
    
    uint8_t KeyNum;			//定义用于接收键码的变量
    float Angle;			//定义角度变量
    
    int main(void)
    {
    	/*模块初始化*/
    	OLED_Init();		//OLED初始化
    	Servo_Init();		//舵机初始化
    	Key_Init();			//按键初始化
    	
    	/*显示静态字符串*/
    	OLED_ShowString(1, 1, "Angle:");	//1行1列显示字符串Angle:
    	
    	while (1)
    	{
    		KeyNum = Key_GetNum();			//获取按键键码
    		if (KeyNum == 1)				//按键1按下
    		{
    			Angle += 30;				//角度变量自增30
    			if (Angle > 180)			//角度变量超过180后
    			{
    				Angle = 0;				//角度变量归零
    			}
    		}
    		Servo_SetAngle(Angle);			//设置舵机的角度为角度变量
    		OLED_ShowNum(1, 7, Angle, 3);	//OLED显示角度变量
    	}
    }
    

    七、PWM驱动直流电机

    1. 工程准备

  • 打开工程文件夹,复制上一个工程(PWM 驱动 LED 呼吸灯),改名为 “6-5 PWM 驱动直流发电机” 并打开工程。
  • 2. PWM 初始化修改

  • 由于要驱动直流电机,需要对 PWM 初始化部分进行修改。现在目前这个代码是在通道1输出一个1KHz的PWM,电机接在通道3上,将GPIO_Pin_0 改成 GPIO_Pin_ 2。下面 TIM_OC1Init 改成TIM_OC3Init。然后下面 TIM_SetCompare1 改成TIM_Compare3,最后将头文件中的1也改成3。
  • 3. 直流电机模块的建立

  • 建立Motor的.C .H文件,并加入到路径Hardware中
  • 在Mortor.c中,我们同样是includePWM.h,继承PWM的模块
  • 直流电机初始化函数(void Motor Init(void)

    功能:在这里面调用它底层的PWM Init,初始化一下PWM(直接调用Init函数),额外初始化方向控制的两个脚(需要重新修改GPIO的参数进行初始化)

    /**
      * 函    数:直流电机初始化
      * 参    数:无
      * 返 回 值:无
      */
    void Motor_Init(void)
    {
    	/*开启时钟*/
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);		//开启GPIOA的时钟
    	
    	GPIO_InitTypeDef GPIO_InitStructure;
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5;
    	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    	GPIO_Init(GPIOA, &GPIO_InitStructure);						//将PA4和PA5引脚初始化为推挽输出	
    	
    	PWM_Init();													//初始化直流电机的底层PWM
    }

    直流电机设置速度函数(void Motor_Setspeed(int8_t Speed))

    功能:设置直流电机的速度。

    参数:参数要给一个带符号的速度变量,负数用来表示反转,这个速度值我们定为-100~100

    /**
      * 函    数:直流电机设置速度
      * 参    数:Speed 要设置的速度,范围:-100~100
      * 返 回 值:无
      */
    void Motor_SetSpeed(int8_t Speed)
    {
    	if (Speed >= 0)							//如果设置正转的速度值
    	{
    		GPIO_SetBits(GPIOA, GPIO_Pin_4);	//PA4置高电平
    		GPIO_ResetBits(GPIOA, GPIO_Pin_5);	//PA5置低电平,设置方向为正转
    		PWM_SetCompare3(Speed);				//PWM设置为速度值
    	}
    	else									//否则,即设置反转的速度值
    	{
    		GPIO_ResetBits(GPIOA, GPIO_Pin_4);	//PA4置低电平
    		GPIO_SetBits(GPIOA, GPIO_Pin_5);	//PA5置高电平,设置方向为反转
    		PWM_SetCompare3(-Speed);			//PWM设置为负的速度值,因为此时速度值为负数,而PWM只能给正数
    	}
    }

    4. 主函数的编写

    补充按键和OLED模块保证实现功能。

    #include "stm32f10x.h"                  // Device header
    #include "Delay.h"
    #include "OLED.h"
    #include "Motor.h"
    #include "Key.h"
    
    uint8_t KeyNum;		//定义用于接收按键键码的变量
    int8_t Speed;		//定义速度变量
    
    int main(void)
    {
    	/*模块初始化*/
    	OLED_Init();		//OLED初始化
    	Motor_Init();		//直流电机初始化
    	Key_Init();			//按键初始化
    	
    	/*显示静态字符串*/
    	OLED_ShowString(1, 1, "Speed:");		//1行1列显示字符串Speed:
    	
    	while (1)
    	{
    		KeyNum = Key_GetNum();				//获取按键键码
    		if (KeyNum == 1)					//按键1按下
    		{
    			Speed += 20;					//速度变量自增20
    			if (Speed > 100)				//速度变量超过100后
    			{
    				Speed = -100;				//速度变量变为-100
    											//此操作会让电机旋转方向突然改变,可能会因供电不足而导致单片机复位
    											//若出现了此现象,则应避免使用这样的操作
    			}
    		}
    		Motor_SetSpeed(Speed);				//设置直流电机的速度为速度变量
    		OLED_ShowSignedNum(1, 7, Speed, 3);	//OLED显示速度变量
    	}
    }
    

    作者:Tanecious.

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32–TIM输出比较

    发表回复