STM32单片机入门学习笔记——定时器TIM第二部分

笔记整理自B站UP主江科大自化协教程《STM32入门教程-2023持续更新中》,所用单片机也为教程推荐单片机。

大致内容

第一部分:定时器基本定时的功能,定时器每隔这个时间产生一个中断,来实现每隔一个固定时间执行一段程序的目的,比如要做一个时钟、秒表或者使用一些程序算法的时候都需要用到定时中断这个功能

第二部分:定时器输出比较的功能,最常见的用途就是产生PWM波形,用于驱动电机等设备

第三部分:定时器输入捕获的功能,使用输入buhuo这个模块来实现测量方波频率的例子

第四部分:定时器的编码器接口,使用编码器接口能够更加方便地读取正交编码器的输出波形,在编码电机测速中,应用广泛

使用定时器的外部时钟,可以提供一个更加精准的时钟来计时或者也可以把外部时钟当做一个计数器,用来统计引脚上电平翻转的次数。

输出比较简介

OC(Output Compare)输出比较;IC(Input Compare)输入捕获;CC(Capture/Compare)输入捕获和输出比较的单元

输出比较可以通过比较CNT(计数器)与CCR捕获/比较寄存器值的关系,来对输出电平进行置1、置0或翻转的操作,用于输出一定频率和占空比的PWM波形

每个高级定时器和通用定时器都拥有4个输出比较通道,高级定时器的前3个通道额外拥有死区生成和互补输出的功能

PWM简介

PWM的频率越快,那它等效模拟的信号就越平稳,不过同时性能开销就越大,一般来说PWM的频率都在几K~几十KHz

输出比较通道(通用定时器)

输出比较模式(输出模式控制器执行逻辑)

冻结模式:比如正在输出PWM波,想暂停一会儿输出,就可以设置成这个模式,一旦切换为冻结模式后,输出就暂停了,并且高低电平也维持为暂停时刻的状态,保持不变。

PWM基本结构

参数计算

PWM频率=计数器的更新频率CK_PSC / (PSC + 1) / (ARR + 1)第一部分提到过

PWM频率:Freq = CK_PSC / (PSC + 1) / (ARR + 1)

PWM占空比:Duty = CCR / (ARR + 1)

PWM分辨率:Reso = 1 / (ARR + 1)——占空比最小的变化步距,值越小越好

ARR越大,CCR的范围就越大,对应的分辨率就越大

输出比较通道(高级定时器)

死区生成和互补输出

右边红色部分是一个普通的推挽电路,如果有两个这样的推挽电路就构成了H桥电路,可以控制直流电机正反转;如果有三个这样的推挽电路就可以用于驱动三相无刷电机。

外围电路就是类似红色部分这样的,输出一高一低(互补)

OC1和OC1N两个互补的输出端口,分别控制上管和下管的导通和关闭

在切换上下管导通状态时,如果在上管关断的瞬间,下管立刻就打开,那可能会因为器件的不理想,上管还没有完全关断,下管就已经导通了,出现了短暂的上下管同时导通的现象,这会导致功率损耗,引起器件发热。所以为了避免这个问题,就有了死区生成电路,它会在上管关闭的时候延迟一小段时间再导通下管,这样就可以避免上下管同时导通的现象了。

舵机简介

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

这里其实把PWM当成了一种通信协议

信号线也有其他颜色,要注意区分

直流电机及驱动简介

硬件电路

问题:模式状态的制动和停止有什么区别?

PWM驱动LED呼吸灯代码讲解(选择PA0TIM2通道1)

第一步:RCC开启时钟

第二步:配置时基单元(时钟源选择)

第三步:配置输出比较单元

第四步:配置GPIO(复用推挽输出)

第五步:运行控制,启动计数器

先了解一下TIM外设对应的库函数,第一部分也提到过一些

  • 【重要】TIM_OC1Init、TIM_OC2Init、TIM_OC3Init、TIM_OC4Init——结构体配置输出比较模块

OC:Output Compare输出比较

TIM_ForcedOC1Config、TIM_ForcedOC2Config、TIM_ForcedOC3Config、TIM_ForcedOC4Config——配置强制输出模式

TIM_OC2PreloadConfig、TIM_OC2PreloadConfig、TIM_OC3PreloadConfig、TIM_OC4PreloadConfig——配置CCR寄存器的预装功能(影子寄存器第一部分也介绍过:写入的值不会立即生效,而是在更新事件才会生效)

TIM_OC1FastConfig、TIM_OC2FastConfig、TIM_OC3FastConfig、TIM_OC4FastConfig——配置快速使能的

TIM_ClearOC1Ref、TIM_ClearOC2Ref、TIM_ClearOC3Ref、TIM_ClearOC4Ref——外部事件时清除REF信号

单独设置输出比较的极性

带N的就是高级定时器互补通道的配置,OC4没有互补通道

单独修改输出使能参数

TIM_SelectOCxM——选择输出比较模式,单独更改输出比较模式的函数

  • 【重要】

单独更改CCR寄存器值的函数,更改占空比

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

第一步——RCC开启时钟

TIM2_CH1是复用在PA0引脚上

主体代码
//第一步开启RCC时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

第二步——配置GPIO

对于普通的开漏/推挽输出,引脚的控制权是来自于输出数据寄存器的,如果想让定时器来控制引脚就需要使用复用开漏/推挽输出的模式,输出数据寄存器将被断开,输出控制权将转移给片上外设。

这里片上外设连接的就是TIM2的CH1通道

主体代码
//配置GPIO
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;               //复用推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);

第三步——时钟源选择及配置时基单元

时钟源选择:选择内部时钟

主体代码
//选择内部时钟
TIM_InternalClockConfig(TIM2);

初始化时基单元

  • 关于ARR/PSC/CCR这三个参数应该如何配置?

假设现在要产生一个频率为1KHz,占空比为50%,分辨率为1%的PWM波形

代入公式Freq = CK_PSC / (PSC + 1) / (ARR + 1)得

PWM频率 = 计数器的更新频率 = 72MHz / (PSC + 1) / (ARR + 1) = 1KHz

占空比 = CCR / (ARR + 1) = 0.5 = 50%

分辨率 = 1 / (ARR + 1) = 1%

解得

ARR + 1 = 100 -> ARR = 100 - 1

CCR = 50-> CCR = 50

PSC + 1 = 720-> PSC = 720 - 1

主体代码
//初始化时基单元
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);

第四步——配置输出比较单元

主体代码

这里注意我们是先给结构体赋初始值,然后再更改其中我们需要的参数。

CCR初始给0即可,后面会变化,产生呼吸灯的效果

//初始化输出比较单元
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCStructInit(&TIM_OCInitStructure);  //给结构体赋初始值,再更改需要改的值
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
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_Cmd(TIM2, ENABLE);

第六步——设置CCR的值

TIM_SetCompare1——用来单独更改通道1的CCR的值

主体代码
void PWM_SetCompare1(uint16_t Compare)
{
    TIM_SetCompare1(TIM2, Compare);
}

源代码

#include "stm32f10x.h"                  // Device header

void PWM_Init(void)
{
    //第一步开启RCC时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    
    //配置GPIO
    GPIO_InitTypeDef GPIO_InitStructure;
    //对于普通的开漏/推挽输出,引脚的控制权是来自于输出数据寄存器的,如果想让定时器来控制引脚
    //就需要使用复用开漏/推挽输出的模式,输出数据寄存器将被断开,输出控制权将转移给片上外设
    //这里片上外设连接的就是TIM2的CH1通道,
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;               //复用推挽输出
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    //选择内部时钟
    TIM_InternalClockConfig(TIM2);
    
    //初始化时基单元
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
    TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
    //对72M进行7200分频,得到10K的计数频率,计10000个数,1s的时间
    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_OCInitTypeDef TIM_OCInitStructure;
    TIM_OCStructInit(&TIM_OCInitStructure);  //给结构体赋初始值,再更改需要改的值
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
    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_Cmd(TIM2, ENABLE);
}

/*
关于ARR/PSC/CCR这三个参数应该如何配置?
现在要产生一个频率为1KHz,占空比为50%,分辨率为1%的PWM波形
代入公式Freq = CK_PSC / (PSC + 1) / (ARR + 1)得
PWM频率 = 计数器的更新频率 = 72MHz / (PSC + 1) / (ARR + 1) = 1KHz
占空比 = CCR / (ARR + 1) = 0.5 = 50%
1 / (ARR + 1) = 1%
解得
ARR + 1 = 100   ->   ARR = 100 - 1
CCR = 50        ->     CCR = 50
PSC + 1 = 720    ->     PSC = 720 - 1
*/

/*
TIM_SetCompare1——用来单独更改通道1的CCR的值
*/

void PWM_SetCompare1(uint16_t Compare)
{
    TIM_SetCompare1(TIM2, Compare);
}

下面我们再讲一下IO口重定义的功能

通过上图可知TIM2_CH1重定义在PA15引脚上

GPIO_PinRemapConfig——引脚重映射配置

如果想把PA0改到PA15,可以选择部分重映像1或者完全重映像

GPIO_PartialRemap1_TIM2——部分重映像1

GPIO_PartialRemap2_TIM2——部分重映像2

GPIO_FullRemap_TIM2——完全重映像

还有一个问题要注意PA15上电后已经默认复用为调试端口JTDI,如果想让它作为普通的GPIO或者复用定时器的通道,需要先关闭调试端口的复用

SWJ:SWD和JTAG这两种调试方式

GPIO_Remap_SWJ_NoJTRST(PB4)——解除JTRST引脚的复用

GPIO_Remap_SWJ_JTAGDisable——解除JTAG调试端口的复用

配置之后这三个端口PA15、PB3、PB4变为普通GPIO

GPIO_Remap_SWJ_Disable——把SWD和JTAG的调试端口全部解除

配置之后这五个端口变为GPIO,这个不要乱用,我们就是用STLINK下载程序的

这张表也详细说明了上述几种情况

总结

如果想要PA15、PB3、PB4这三个引脚当做GPIO来使用

主体代码
//开启AFIO时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
//解除JTAG调试端口的复用功能
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);

如果想要重映射定时器或者其他外设的复用引脚

主体代码
//开启AFIO时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
//引脚重映射配置
GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);

如果想要重映射的引脚正好是调试端口

主体代码
//开启AFIO时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
//引脚重映射配置
GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);
//解除JTAG调试端口的复用功能
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);

源代码

#include "stm32f10x.h"                  // Device header

如果需要重映射到PA15引脚,该如何配置?代码如下
void PWM_Init(void)
{
    //第一步开启RCC时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
    
    //引脚重映射配置
    GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);
    //因为PA15正好是JTAG调试端口,所以需要解除JTAG调试端口的复用功能
    GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
    
    //配置GPIO
    GPIO_InitTypeDef GPIO_InitStructure;
    //对于普通的开漏/推挽输出,引脚的控制权是来自于输出数据寄存器的,如果想让定时器来控制引脚
    //就需要使用复用开漏/推挽输出的模式,输出数据寄存器将被断开,输出控制权将转移给片上外设
    //这里片上外设连接的就是TIM2的CH1通道,
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;               //复用推挽输出
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    //选择内部时钟
    TIM_InternalClockConfig(TIM2);
    
    //初始化时基单元
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
    TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
    //对72M进行7200分频,得到10K的计数频率,计10000个数,1s的时间
    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_OCInitTypeDef TIM_OCInitStructure;
    TIM_OCStructInit(&TIM_OCInitStructure);  //给结构体赋初始值,再更改需要改的值
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
    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_Cmd(TIM2, ENABLE);
}

/*
关于ARR/PSC/CCR这三个参数应该如何配置?
现在要产生一个频率为1KHz,占空比为50%,分辨率为1%的PWM波形
代入公式Freq = CK_PSC / (PSC + 1) / (ARR + 1)得
PWM频率 = 计数器的更新频率 = 72MHz / (PSC + 1) / (ARR + 1) = 1KHz
占空比 = CCR / (ARR + 1) = 0.5 = 50%
1 / (ARR + 1) = 1%
解得
ARR + 1 = 100   ->   ARR = 100 - 1
CCR = 50        ->     CCR = 50
PSC + 1 = 720    ->     PSC = 720 - 1
*/

/*
TIM_SetCompare1——用来单独更改通道1的CCR的值
*/

void PWM_SetCompare1(uint16_t Compare)
{
    TIM_SetCompare1(TIM2, Compare);
}

PWM驱动舵机代码讲解(选择PA1TIM2通道2)

对于同一个定时器的不同通道输出的PWM,它们的频率因为不同通道是共用一个计数器的,所以它们的频率必须是一样的,占空比由各自的CCR决定,所以占空比可以各自设定,由于计数器更新,所有PWM同时跳变,所以它们的相位是同步的。

  • 关于ARR/PSC/CCR这三个参数应该如何配置?

舵机要求的周期是20ms,频率为1/20ms=50Hz

PWM频率:Freq = CK_PSC / (PSC + 1) / (ARR + 1)

72MHz / (PSC + 1) / (ARR + 1) = 50Hz

可以设置PSC + 1 = 72

ARR + 1 = 20K

PWM占空比:Duty = CCR / (ARR + 1)

CCR = 500对应0.5ms

CCR = 2500对应2.5ms

第一步——开启RCC时钟

选择的引脚是PA1

主体代码
//第一步开启RCC时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

第二步——配置GPIO

主体代码
//配置GPIO
GPIO_InitTypeDef GPIO_InitStructure;
//对于普通的开漏/推挽输出,引脚的控制权是来自于输出数据寄存器的,如果想让定时器来控制引脚
//就需要使用复用开漏/推挽输出的模式,输出数据寄存器将被断开,输出控制权将转移给片上外设
//这里片上外设连接的就是TIM2的CH2通道,
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;               //复用推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);

第三步——时钟源选择及配置时基单元

主体代码
//选择内部时钟
TIM_InternalClockConfig(TIM2);

//初始化时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
//对72M进行7200分频,得到10K的计数频率,计10000个数,1s的时间
TIM_TimeBaseInitStructure.TIM_Period = 20000 - 1;   //ARR自动重装器的值
TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;   //PSC预分频器的值
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;   //重复计数器的值
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);

第四步——配置输出比较单元

这里通道选择的是CH2,所以最后函数选择TIM_OC2Init

主体代码
//初始化输出比较单元
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCStructInit(&TIM_OCInitStructure);  //给结构体赋初始值,再更改需要改的值
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
//CCR取值范围500~2500对应0.5ms~2.5ms
TIM_OCInitStructure.TIM_Pulse = 0;     //CCR
TIM_OC2Init(TIM2, &TIM_OCInitStructure);

第五步——开启定时器

主体代码
//开启定时器
TIM_Cmd(TIM2, ENABLE);

第六步——设置CCR的值

主体代码
void PWM_SetCompare2(uint16_t Compare)
{
    TIM_SetCompare2(TIM2, Compare);
}

源代码

#include "stm32f10x.h"                  // Device header

void PWM_Init(void)
{
    //第一步开启RCC时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    
    //配置GPIO
    GPIO_InitTypeDef GPIO_InitStructure;
    //对于普通的开漏/推挽输出,引脚的控制权是来自于输出数据寄存器的,如果想让定时器来控制引脚
    //就需要使用复用开漏/推挽输出的模式,输出数据寄存器将被断开,输出控制权将转移给片上外设
    //这里片上外设连接的就是TIM2的CH2通道,
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;               //复用推挽输出
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    //选择内部时钟
    TIM_InternalClockConfig(TIM2);
    
    //初始化时基单元
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
    TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
    //对72M进行7200分频,得到10K的计数频率,计10000个数,1s的时间
    TIM_TimeBaseInitStructure.TIM_Period = 20000 - 1;   //ARR自动重装器的值
    TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;   //PSC预分频器的值
    TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;   //重复计数器的值
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
    
    //初始化输出比较单元
    TIM_OCInitTypeDef TIM_OCInitStructure;
    TIM_OCStructInit(&TIM_OCInitStructure);  //给结构体赋初始值,再更改需要改的值
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    //CCR取值范围500~2500对应0.5ms~2.5ms
    TIM_OCInitStructure.TIM_Pulse = 0;     //CCR
    TIM_OC2Init(TIM2, &TIM_OCInitStructure);
    
    //开启定时器
    TIM_Cmd(TIM2, ENABLE);
}

void PWM_SetCompare2(uint16_t Compare)
{
    TIM_SetCompare2(TIM2, Compare);
}

可对比之前PWM驱动LED呼吸灯的代码,可见只有PSC/ARR/CCR、引脚、通道相关的地方改了一下,大部分都没变。

PWM驱动直流电机代码讲解(选择PA2TIM2通道3)

PWM和驱动LED部分代码类似,只是通道和引脚不同,这里我们直接给出源代码。

#include "stm32f10x.h"                  // Device header

void PWM_Init(void)
{
    //第一步开启RCC时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    
    //配置GPIO
    GPIO_InitTypeDef GPIO_InitStructure;
    //对于普通的开漏/推挽输出,引脚的控制权是来自于输出数据寄存器的,如果想让定时器来控制引脚
    //就需要使用复用开漏/推挽输出的模式,输出数据寄存器将被断开,输出控制权将转移给片上外设
    //这里片上外设连接的就是TIM2的CH3通道,
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;               //复用推挽输出
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    //选择内部时钟
    TIM_InternalClockConfig(TIM2);
    
    //初始化时基单元
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
    TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
    //对72M进行7200分频,得到10K的计数频率,计10000个数,1s的时间
    TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;   //ARR自动重装器的值
    TIM_TimeBaseInitStructure.TIM_Prescaler = 36 - 1;   //PSC预分频器的值
    TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;   //重复计数器的值
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
    
    //初始化输出比较单元
    TIM_OCInitTypeDef TIM_OCInitStructure;
    TIM_OCStructInit(&TIM_OCInitStructure);  //给结构体赋初始值,再更改需要改的值
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_Pulse = 0;     //CCR
    TIM_OC3Init(TIM2, &TIM_OCInitStructure);
    
    //开启定时器
    TIM_Cmd(TIM2, ENABLE);
}

void PWM_SetCompare3(uint16_t Compare)
{
    TIM_SetCompare3(TIM2, Compare);
}

不同的地方如下(头文件不要忘记改):

感谢抽出宝贵时间阅读的各位小读者们,创作不易,如果感觉有帮助的话,帮忙点个赞再走吧!你的支持是我创作的动力,希望能带给大家更多优质的文章。

  • 12
    点赞
  • 55
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
STM32单片机是一种高性能、低功耗的32位微控制器,广泛应用于嵌入式系统的开发中。其原理是基于ARM Cortex-M系列的核心,具有丰富的外设和高性能的计算能力,可以实现多种功能。 STM32单片机的应用非常广泛。其可以应用于智能家居、医疗仪器、工业自动化等领域。它可以用来控制各种传感器,如温度传感器、湿度传感器等,实现对环境的监测和控制。同时,它也可以用于控制各种执行机构,如电机和执行器,实现对设备的控制和运动。此外,STM32单片机还可以应用于通信领域,如无线模块和网络模块的控制,实现设备之间的数据传输和通信。 基于Proteus的虚拟仿真可以帮助开发者在进行STM32单片机的开发过程中,不需要实际搭建硬件电路,即可进行软件开发和调试。Proteus是一款功能强大的虚拟仿真软件,可以模拟STM32单片机和外设的工作过程。它可以提供丰富的模型库和仿真环境,方便开发者进行程序的编写、调试和验证。 通过Proteus的虚拟仿真,开发者可以在电脑上实现对STM32单片机的全面测试,包括外设的连接和数据交互。开发者可以使用Proteus提供的虚拟示波器、虚拟显示器等工具,模拟实际硬件的工作状态,及时查看和调试程序。 总而言之,STM32单片机的原理和应用非常广泛,通过Proteus的虚拟仿真可以帮助开发者在进行STM32单片机的开发过程中,提高开发效率和减少成本,并且可以更好地进行软件开发和调试。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AoXin_TechJZ.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值