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

这篇具有很好参考价值的文章主要介绍了STM32单片机入门学习笔记——定时器TIM第二部分。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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

大致内容

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

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

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

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

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

输出比较简介

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

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

STM32单片机入门学习笔记——定时器TIM第二部分,单片机入门学习笔记,单片机,stm32,学习,c语言,嵌入式硬件,Powered by 金山文档

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

PWM简介

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

STM32单片机入门学习笔记——定时器TIM第二部分,单片机入门学习笔记,单片机,stm32,学习,c语言,嵌入式硬件,Powered by 金山文档

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

STM32单片机入门学习笔记——定时器TIM第二部分,单片机入门学习笔记,单片机,stm32,学习,c语言,嵌入式硬件,Powered by 金山文档

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

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

STM32单片机入门学习笔记——定时器TIM第二部分,单片机入门学习笔记,单片机,stm32,学习,c语言,嵌入式硬件,Powered by 金山文档

PWM基本结构

STM32单片机入门学习笔记——定时器TIM第二部分,单片机入门学习笔记,单片机,stm32,学习,c语言,嵌入式硬件,Powered by 金山文档

参数计算

STM32单片机入门学习笔记——定时器TIM第二部分,单片机入门学习笔记,单片机,stm32,学习,c语言,嵌入式硬件,Powered by 金山文档

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的范围就越大,对应的分辨率就越大

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

STM32单片机入门学习笔记——定时器TIM第二部分,单片机入门学习笔记,单片机,stm32,学习,c语言,嵌入式硬件,Powered by 金山文档

死区生成和互补输出

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

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

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

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

舵机简介

STM32单片机入门学习笔记——定时器TIM第二部分,单片机入门学习笔记,单片机,stm32,学习,c语言,嵌入式硬件,Powered by 金山文档

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

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

STM32单片机入门学习笔记——定时器TIM第二部分,单片机入门学习笔记,单片机,stm32,学习,c语言,嵌入式硬件,Powered by 金山文档

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

直流电机及驱动简介

STM32单片机入门学习笔记——定时器TIM第二部分,单片机入门学习笔记,单片机,stm32,学习,c语言,嵌入式硬件,Powered by 金山文档

硬件电路

STM32单片机入门学习笔记——定时器TIM第二部分,单片机入门学习笔记,单片机,stm32,学习,c语言,嵌入式硬件,Powered by 金山文档

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

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信号

STM32单片机入门学习笔记——定时器TIM第二部分,单片机入门学习笔记,单片机,stm32,学习,c语言,嵌入式硬件,Powered by 金山文档

单独设置输出比较的极性

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

STM32单片机入门学习笔记——定时器TIM第二部分,单片机入门学习笔记,单片机,stm32,学习,c语言,嵌入式硬件,Powered by 金山文档

单独修改输出使能参数

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

  • 【重要】

STM32单片机入门学习笔记——定时器TIM第二部分,单片机入门学习笔记,单片机,stm32,学习,c语言,嵌入式硬件,Powered by 金山文档

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

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

第一步——RCC开启时钟

STM32单片机入门学习笔记——定时器TIM第二部分,单片机入门学习笔记,单片机,stm32,学习,c语言,嵌入式硬件,Powered by 金山文档

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口重定义的功能

STM32单片机入门学习笔记——定时器TIM第二部分,单片机入门学习笔记,单片机,stm32,学习,c语言,嵌入式硬件,Powered by 金山文档

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

GPIO_PinRemapConfig——引脚重映射配置

STM32单片机入门学习笔记——定时器TIM第二部分,单片机入门学习笔记,单片机,stm32,学习,c语言,嵌入式硬件,Powered by 金山文档

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

GPIO_PartialRemap1_TIM2——部分重映像1

GPIO_PartialRemap2_TIM2——部分重映像2

GPIO_FullRemap_TIM2——完全重映像

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

STM32单片机入门学习笔记——定时器TIM第二部分,单片机入门学习笔记,单片机,stm32,学习,c语言,嵌入式硬件,Powered by 金山文档

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

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

STM32单片机入门学习笔记——定时器TIM第二部分,单片机入门学习笔记,单片机,stm32,学习,c语言,嵌入式硬件,Powered by 金山文档

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

STM32单片机入门学习笔记——定时器TIM第二部分,单片机入门学习笔记,单片机,stm32,学习,c语言,嵌入式硬件,Powered by 金山文档

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

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

STM32单片机入门学习笔记——定时器TIM第二部分,单片机入门学习笔记,单片机,stm32,学习,c语言,嵌入式硬件,Powered by 金山文档

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

STM32单片机入门学习笔记——定时器TIM第二部分,单片机入门学习笔记,单片机,stm32,学习,c语言,嵌入式硬件,Powered by 金山文档

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

总结

如果想要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);
}

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

STM32单片机入门学习笔记——定时器TIM第二部分,单片机入门学习笔记,单片机,stm32,学习,c语言,嵌入式硬件,Powered by 金山文档
STM32单片机入门学习笔记——定时器TIM第二部分,单片机入门学习笔记,单片机,stm32,学习,c语言,嵌入式硬件,Powered by 金山文档
STM32单片机入门学习笔记——定时器TIM第二部分,单片机入门学习笔记,单片机,stm32,学习,c语言,嵌入式硬件,Powered by 金山文档

感谢抽出宝贵时间阅读的各位小读者们,创作不易,如果感觉有帮助的话,帮忙点个赞再走吧!你的支持是我创作的动力,希望能带给大家更多优质的文章。文章来源地址https://www.toymoban.com/news/detail-719428.html

到了这里,关于STM32单片机入门学习笔记——定时器TIM第二部分的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【单片机】STM32单片机的各个定时器的定时中断程序,标准库

    高级定时器和普通定时器的区别(https://zhuanlan.zhihu.com/p/557896041): TIM1是高级定时器,使用的时钟总线是RCC_APB2Periph_TIM1,和普通定时器不一样。 timer.c timer.h 调用 timer.c timer.h 调用 timer.c timer.h 调用 timer.c timer.h 调用 timer.c timer.h 调用

    2024年02月11日
    浏览(18)
  • 【单片机】STM32单片机读取旋转编码器,TIM定时器编码器模式捕获,程序

    旋转编码器简单来说,就是会输出2个PWM,依据相位可以知道旋转方向,依据脉冲个数可以知道旋转的角度。一般旋转一圈有一个固定数值的脉冲个数。 旋转编码器广泛用于电机、或者角度传感器,STM32的定时器可以直接接入这两个波形获取到信息。 前两个引脚(接地和Vcc)

    2024年02月13日
    浏览(20)
  • 学习笔记|定时器|STC中断|定时器时间计算|STC32G单片机视频开发教程(冲哥)|第十一集:定时器的作用和意义

    什么是定时器:定时器-与非网 上节课的一段代码: TimeCount++然后一个延时1毫秒,每运行1ms,变量就会加一。 系统已经运行了多少个毫秒。 实际使用时的代码如下, 判断按键有沿有按下的时候,我们等待按键松开,还有一个while循环。 如果没有松开,会一直死在这一行。所以,

    2024年02月09日
    浏览(26)
  • 【单片机】STM32单片机,定时器,多路PWM,TIM1、TIM2、TIM3、TIM4,STM32F103

    下图是定时器相关引脚: 在《STM32中文参考手册V10.pdf》有写: TIM1 的PWM是带互补输出的,较为高级和复杂,有兴趣可以参考其他介绍文章,这里的代码让7个引脚输出PWM。 调用: 这里没有重映射,注意不能把PA9 PA10 初始化成串口去了。 timer.c timer.h 调用: 这里没有重映射。

    2024年02月11日
    浏览(30)
  • STM32单片机(六)TIM定时器 -> 第三节:TIM输出比较

    ❤️ 专栏简介:本专栏记录了从零学习单片机的过程,其中包括51单片机和STM32单片机两部分;建议先学习51单片机,其是STM32等高级单片机的基础;这样再学习STM32时才能融会贯通。 ☀️ 专栏适用人群 :适用于想要从零基础开始学习入门单片机,且有一定C语言基础的的童鞋

    2024年02月09日
    浏览(23)
  • STM32单片机(六)TIM定时器 -> 第五节:TIM输入捕获

    ❤️ 专栏简介:本专栏记录了从零学习单片机的过程,其中包括51单片机和STM32单片机两部分;建议先学习51单片机,其是STM32等高级单片机的基础;这样再学习STM32时才能融会贯通。 ☀️ 专栏适用人群 :适用于想要从零基础开始学习入门单片机,且有一定C语言基础的的童鞋

    2024年02月09日
    浏览(18)
  • 洋桃电子STM32F407单片机入门教程笔记九:低功耗模式

      此文档作为对洋桃电子STM32F407单片机视频的整理,B站链接: 第28集)低功耗模式_哔哩哔哩_bilibili         在之前的程序里应用程序在while(1)主循环中反复执行,ARM内核以100%的功率工作。这样的设计简单稳定,内核全速运行的功耗也只有几十毫安,对于外接电源的设备

    2024年01月23日
    浏览(28)
  • 洋桃电子STM32F407单片机入门教程笔记一:RCC时钟设置

    此文档作为对洋桃电子STM32F407单片机视频的整理,B站链接:2023更新 STM32入门F4= STM32F407单片机入门教程=WIFI连接阿里云物联网+CubeMX+HAL库+TFT彩屏+杜洋主讲_哔哩哔哩_bilibili 由于单片机内部的时钟结构都是相互关联的,一个时钟源出发可以供给多个功能,类似于树根、树杆、树

    2024年02月04日
    浏览(20)
  • STM32F103RCT6开发板M3单片机教程06--定时器中断

    除非特别说明,本章节描述的模块应用于整个 STM32F103xx 微控制器系列,因为我们使用是 STM32F103RCT6开发板是mini最小系统板。 本教程使用是( 光明谷SUN_STM32mini开发板 )   首先了解一下是STM32F10X定时器(Timer)   注: 小容量产品是指闪存存储器容量在16K至32K字节之间的STM32F101

    2024年02月04日
    浏览(23)
  • [蓝桥杯单片机]学习笔记——555定时器与频率测量

    目录 一、原理部分 1、什么是555定时器 2、频率测量  3、频率测量相关配置 二、程序案例 555定时器是一种集成电路芯片,常被用于定时器、脉冲产生器和振荡电路。555可被作为电路中的延时器件、触发器或起振元件。而在蓝桥杯的板子上,555定时器是一个信号发生电路,通

    2023年04月12日
    浏览(28)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包