【STM32】TIM定时器输出比较

1 输出比较

1.1 输出比较简介

  1. OC(Output Compare)输出比较;IC(Input Capture)输入捕获;CC(Capture/Compare)输入捕获和输出比较的单元
  2. 输出比较可以通过比较CNTCCR寄存器值(CCR捕获/比较寄存器)的关系,来对输出电平进行置1、置0或翻转的操作,用于输出一定频率和占空比的PWM波形
  3. 每个高级定时器和通用定时器都拥有4个输出比较通道
  4. 高级定时器的前3个通道额外拥有死区生成和互补输出的功能

主要是用来输出PWM波形的,PWM波形又是驱动电机的必要条件。

这个CCR是共用的,当使用输入捕获时,它就是捕获寄存器;当使用输出比较时,它就是比较寄存器。在输出比较这里,这块电路会比较CNT和CCR的值,CNT计数自增,CCR是我们给定的一个值,当CNT大于CCR、小于CCR、等于CCR时,输出就会置1,置0,置1,置0,这样就可以输出一个电平不断跳变的PWM波形了。

1.2 PWM简介

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

在具有惯性的系统中,可以通过对一系列脉冲的宽度进行调制,来等效地获得所需要的模拟参量,常应用于电机控速等领域

PWM参数:

     频率 = 1 / TS            占空比 = TON / TS           分辨率 = 占空比变化步距

1.3 输出比较通道

通用定时器的输出比较部分电路

对应的是

 最后通过TIMx_CH1输出到GPIO引脚上。

左边是CNT计数器和CCR1第一路的捕获/比较寄存器;它俩比较,当CNT > CCR1 或者 CNT = CCR1时,就会给输出模式控制器传一个信号,然后输出模式控制器就会改变它输出OC1REF(reference参考信号)的高低电平;接下来可以把OC1REF映射到主模式的TRGO输出上去;不过REF的主要去向还是走下面。

这是一个极性选择,给这个寄存器写0,信号就会往上走,就是信号电平不反转;写1的话,信号就会往下走,信号会通过一个非门取反,输出的信号就会发生反转。最后就是OC1引脚,这个引脚是CH1通道的引脚,在引脚定义中就可以具体知道是哪个GPIO了。

输出模式控制器的工作:输出比较模式,通过寄存器来配置。

模式

描述

冻结

CNT=CCR时(无效),REF保持为原状态

匹配时置有效电平

CNT=CCR时,REF置有效电平(高电平),一次性的

匹配时置无效电平

CNT=CCR时,REF置无效电平(低电平),一次性的

匹配时电平翻转

CNT=CCR时,REF电平翻转

强制为无效电平

CNTCCR无效,REF强制为无效电平。在暂停期间保持高电平

强制为有效电平

CNTCCR无效,REF强制为有效电平。在暂停期间保持低电平

PWM模式1

向上计数:CNT<CCR时,REF置有效电平,CNTCCR时,REF置无效电平

向下计数:CNT>CCR时,REF置无效电平,CNTCCR时,REF置有效电平

PWM模式2

向上计数:CNT<CCR时,REF置无效电平,CNTCCR时,REF置有效电平

向下计数:CNT>CCR时,REF置有效电平,CNTCCR时,REF置无效电平

(1)冻结模式:输出暂停;

(2)匹配时置有效电平、匹配时置无效电平、匹配时电平翻转:有效/无效电平一般是高级定时器的说法;简单理解有效电平是高电平,无效电平是低电平。

(3)PWM模式

PWM模式2是PWM模式1的取反。

1.4 PWM基本结构

 左上角是时基单元和控制部分,输出PWM暂时不需要中断。下面就是输出比较单元了,总共有4路。输出比较单元的最开始,是CCR捕获/比较寄存器,CCR是我们自己设定的,CNT不断自增运行,同时它俩还在不断比较,后面是输出模式控制器(PWM模式1)。

蓝色线是CNT的值,黄色线是ARR的值,CNT(蓝色线)从0开始自增,一直增到ARR的值,之后清零继续自增。在这个过程中再设置一条红线(CCR的值),之后再执行【CNT<CCR时,REF置有效电平;CNT≥CCR时,REF置无效电平】,下面绿色部分是输出。

CNT<CCR时,REF置有效电平;CNT≥CCR时,REF置无效电平。并且它的占空比是受CCR值调控的;如果CCR设置高一些,输出占空比就大一些。

1.5 参数计算

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

对应着计数器的一个溢出更新周期,PWM的频率等于计数器的更新频率。

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

PWM分辨率:  Reso = 1 / (ARR + 1)

输出一个频率为1KHz,占空比可以任意调控,切分辨率为1%的PWM波形

Reso = 1 / (ARR + 1) = 1% =====》ARR = 99

Duty = CCR / (ARR + 1) = CCR / 100  =====》 CCR = [0, 100]

Freq = CK_PSC / (PSC + 1) / (ARR + 1) = 1000 =====》 CK_PSC / (PSC + 1) = 100000

1.6 舵机简介

舵机是一种根据输入PWM信号占空比来控制输出角度的装置

输入PWM信号要求:周期为20ms,高电平宽度为0.5ms~2.5ms

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

1.6.1 舵机硬件电路

1.7 直流电机

直流电机是一种将电能转换为机械能的装置,有两个电极,当电极正接时,电机正转,当电极反接时,电机反转

直流电机属于大功率器件,GPIO口无法直接驱动,需要配合电机驱动电路来操作

TB6612是一款双路H桥型的直流电机驱动芯片,可以驱动两个直流电机并且控制其转速和方向

1.7.1 硬件电路

看图和引脚说明,很清晰。

STBY引脚是待机控制引脚。如果接GND,芯片就不工作,处于待机状态;如果接逻辑电源VCC,芯片就正常工作。

看手册

强置输出模式:CNT和CCR无效,REF强制为高和低的那两种模式

输出比较模式:CNT=CCR时,REF冻结、置高、置低、反转那四种模式

PWM 模式:CNT > CCR或者CNT < CCR时,REF置高或者置低的那两种模式。

2 TIM输出比较之PWM驱动LED呼吸灯

2.1 接线图

注意这里:高电平点亮,低电平熄灭。即占空比越大,LED越亮;占空比越小,LED越暗。

2.2 封装模块

按这个流程图进行初始化

先看输出比较相关的函数

// 这4个函数是用来配置输出比较的
void TIM_OC1Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC2Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC3Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC4Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);// 用来给输出比较结构体赋值一个默认值的
void TIM_OCStructInit(TIM_OCInitTypeDef* TIM_OCInitStruct);// 仅高级定时器使用,在使用高级定时器输出PWM时,需要用这个函数使能主输出;否则PWM将不能正常输出
void TIM_CtrlPWMOutputs(TIM_TypeDef* TIMx, FunctionalState NewState);// 用来配置强制输出模式的。强制输出高电平和输出设置100%占空比一样
void TIM_ForcedOC1Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);
void TIM_ForcedOC2Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);
void TIM_ForcedOC3Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);
void TIM_ForcedOC4Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);// 配置CCR寄存器的预装功能,就是影子寄存器;写入的值不会立即生效,在更新事件才会生效
void TIM_OC1PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
void TIM_OC2PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
void TIM_OC3PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
void TIM_OC4PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);// 配置快速使能的
void TIM_OC1FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast);
void TIM_OC2FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast);
void TIM_OC3FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast);
void TIM_OC4FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast);// 外部事件时清除ref信号
void TIM_ClearOC1Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear);
void TIM_ClearOC2Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear);
void TIM_ClearOC3Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear);
void TIM_ClearOC4Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear);// 单独设置输出比较极性的
void TIM_OC1PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
void TIM_OC1NPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCNPolarity);
void TIM_OC2PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
void TIM_OC2NPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCNPolarity);
void TIM_OC3PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
void TIM_OC3NPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCNPolarity);
void TIM_OC4PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);// 单独修改输出使能参数的
void TIM_CCxCmd(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_CCx);
void TIM_CCxNCmd(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_CCxN);// 选择输出比较模式
void TIM_SelectOCxM(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_OCMode);// 单独更改RCC寄存器的值
void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1);
void TIM_SetCompare2(TIM_TypeDef* TIMx, uint16_t Compare2);
void TIM_SetCompare3(TIM_TypeDef* TIMx, uint16_t Compare3);
void TIM_SetCompare4(TIM_TypeDef* TIMx, uint16_t Compare4);

要掌握的

// 这4个函数是用来配置输出比较的
void TIM_OC1Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC2Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC3Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC4Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);// 单独更改RCC寄存器的值
void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1);
void TIM_SetCompare2(TIM_TypeDef* TIMx, uint16_t Compare2);
void TIM_SetCompare3(TIM_TypeDef* TIMx, uint16_t Compare3);
void TIM_SetCompare4(TIM_TypeDef* TIMx, uint16_t Compare4);

看这个图

有TIM2_CH1_ETR在PA0这一行,说明TIM2的ETR引脚和通道1的引脚都是借用了PA0引脚,换句话,TIM2的引脚复用到了PA0引脚上,所以如果要使用TIM2的OC1也就是CH1t通道,输出PWM那它就只能在PA0的引脚输出,不能任意接。同样,TIM2的CH2对应PA1。。。。。。

还可以修改

计算:频率1kHz,占空比50% ,分辨率为1%。

PWM分辨率:    Reso = 1 / (ARR + 1) = 1%                                         ==> ARR= 100 - 1

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

PWM频率:    Freq = CK_PSC(72M) / (PSC + 1) / (ARR + 1) = 1K      ==> PSC = 720 - 1

// 2配置时基单元(预分频器、自动重装器、计数模式等)// ...TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;				// ARR自动重装器的值 两个合起来计数0.1秒TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1;			// PSC预分频器的值,720分频,得到100k计数TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);// 3配置输出比较单元(CCR的值、输出比较模式、极性选择、输出使能)TIM_OCInitTypeDef TIM_OCInitStructure;TIM_OCStructInit(&TIM_OCInitStructure);               					 // 先初始化,后面再按需修改// ...TIM_OCInitStructure.TIM_Pulse = 0;            							 // CCR的值w为50,先设置为0,后面变化// 这个函数的选择按照GPIO口需求来,PA0口对应的是第一个输出比较通道TIM_OC1Init(TIM2, &TIM_OCInitStructure);

PWM.c

#include "stm32f10x.h"                  // Device header// PWM初始化函数
void PWM_Init(void)
{// 1RCC开启时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);TIM_InternalClockConfig(TIM2);// 2配置时基单元(预分频器、自动重装器、计数模式等)TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;		// 时钟分频,影响不大TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;	// 计数模式,向上计数TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;			// 重复计数器的值// 关键参数TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;				// ARR自动重装器的值 两个合起来计数0.1秒TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1;			// PSC预分频器的值,720分频,得到100k计数TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);// 3配置输出比较单元(CCR的值、输出比较模式、极性选择、输出使能)TIM_OCInitTypeDef TIM_OCInitStructure;TIM_OCStructInit(&TIM_OCInitStructure);               					 // 先初始化,后面再按需修改TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;     					 // 输出比较模式,PWM模式1TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;                // 输出比较极性,高级性,极性不反转,有效电平是高电平TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;            // 输出状态输出使能TIM_OCInitStructure.TIM_Pulse = 0;            							 // CCR的值w为50,先设置为0,后面变化// 这个函数的选择按照GPIO口需求来,PA0口对应的是第一个输出比较通道TIM_OC1Init(TIM2, &TIM_OCInitStructure);// 频率1kHz,占空比50% ,分辨率为1%。 CCR = 50 ,/**PWM分辨率:	Reso = 1 / (ARR + 1) = 1%      						==> ARR= 100 - 1PWM占空比:	Duty = CCR / (ARR + 1) = 50%   						==> CCR = 50PWM频率:	Freq = CK_PSC(72M) / (PSC + 1) / (ARR + 1) = 1K  	==> PSC = 720 - 1*/// 4配置GPIO(初始化为复用推挽输出,参考引脚定义表)RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);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);// 5运行控制,启动计数器TIM_Cmd(TIM2, ENABLE);
}// 更改通道1的CCR值
void PWM_SetCompare1(uint16_t Compare)
{TIM_SetCompare1(TIM2, Compare);
}

2.3 主函数

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "PWM.h"uint8_t i;int main()
{OLED_Init();					// 初始化OLEDPWM_Init();						// PWM初始化OLED_ShowString(1, 1, "Hello");while (1){// 点亮for (i = 0; i <= 100; i++){PWM_SetCompare1(i);Delay_ms(10);}// 熄灭for (i = 0; i <= 100; i++) {PWM_SetCompare1(100 - i);Delay_ms(10);}}
}

现象:接在PA0号口的灯如呼吸一般亮灭

复用到PA15

PWM.c

#include "stm32f10x.h"                  // Device header// PWM初始化函数
void PWM_Init(void)
{// 1RCC开启时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);TIM_InternalClockConfig(TIM2);RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);      // 重映射使用AFIOGPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);     // 部分重映射PA15GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);  // 解除调试端口// 2配置时基单元(预分频器、自动重装器、计数模式等)TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;		// 时钟分频,影响不大TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;	// 计数模式,向上计数TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;			// 重复计数器的值// 关键参数TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;				// ARR自动重装器的值 两个合起来计数0.1秒TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1;			// PSC预分频器的值,720分频,得到100k计数TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);// 3配置输出比较单元(CCR的值、输出比较模式、极性选择、输出使能)TIM_OCInitTypeDef TIM_OCInitStructure;TIM_OCStructInit(&TIM_OCInitStructure);               					 // 先初始化,后面再按需修改TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;     					 // 输出比较模式,PWM模式1TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;                // 输出比较极性,高级性,极性不反转,有效电平是高电平TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;            // 输出状态输出使能TIM_OCInitStructure.TIM_Pulse = 0;            							 // CCR的值w为50,先设置为0,后面变化// 这个函数的选择按照GPIO口需求来,PA0口对应的是第一个输出比较通道TIM_OC1Init(TIM2, &TIM_OCInitStructure);// 频率1kHz,占空比50% ,分辨率为1%。 CCR = 50 ,/**PWM分辨率:	Reso = 1 / (ARR + 1) = 1%      						==> ARR= 100 - 1PWM占空比:	Duty = CCR / (ARR + 1) = 50%   						==> CCR = 50PWM频率:	Freq = CK_PSC(72M) / (PSC + 1) / (ARR + 1) = 1K  	==> PSC = 720 - 1*/// 4配置GPIO(初始化为复用推挽输出,参考引脚定义表)RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;				// 复用推挽输出。定时器控制引脚,输出控制权转移给片上外设
//	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);// 5运行控制,启动计数器TIM_Cmd(TIM2, ENABLE);
}// 更改通道1的CCR值
void PWM_SetCompare1(uint16_t Compare)
{TIM_SetCompare1(TIM2, Compare);
}

3 TIM输出比较之PWM驱动舵机

3.1 接线图

注意颜色

3.2 模块封装

输入PWM信号要求:周期为20ms,高电平宽度为0.5ms~2.5ms

	// 输入PWM信号要求:周期为20ms(50Hz),高电平宽度为0.5ms~2.5ms,占空比[2.5%, 12.5%]/**PWM分辨率:	Reso = 1 / (ARR + 1)PWM占空比:	Duty = CCR / (ARR + 1)PWM频率:	Freq = CK_PSC(72M) / (PSC + 1) / (ARR + 1)PSC = 72 - 1ARR = 20000 - 1CCR的范围是[500, 2500]*/

现在使用的是PA1的通道2

PWM.c

#include "stm32f10x.h"                  // Device header// PWM初始化函数
void PWM_Init(void)
{// 1RCC开启时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);TIM_InternalClockConfig(TIM2);// 2配置时基单元(预分频器、自动重装器、计数模式等)TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;		// 时钟分频,影响不大TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;	// 计数模式,向上计数TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;			// 重复计数器的值// 关键参数TIM_TimeBaseInitStructure.TIM_Period = 20000 - 1;				// ARR自动重装器的值 两个合起来计数0.1秒TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;				// PSC预分频器的值,72分频,得到100k计数TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);// 3配置输出比较单元(CCR的值、输出比较模式、极性选择、输出使能)TIM_OCInitTypeDef TIM_OCInitStructure;TIM_OCStructInit(&TIM_OCInitStructure);               					 // 先初始化,后面再按需修改TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;     					 // 输出比较模式,PWM模式1TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;                // 输出比较极性,高级性,极性不反转,有效电平是高电平TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;            // 输出状态输出使能TIM_OCInitStructure.TIM_Pulse = 0;            							 // CCR的值w为50,先设置为0,后面变化// 这个函数的选择按照GPIO口需求来,PA1口对应的是第二个输出比较通道TIM_OC2Init(TIM2, &TIM_OCInitStructure);// 输入PWM信号要求:周期为20ms(50Hz),高电平宽度为0.5ms~2.5ms,占空比[2.5%, 12.5%]/**PWM分辨率:	Reso = 1 / (ARR + 1)PWM占空比:	Duty = CCR / (ARR + 1)PWM频率:	Freq = CK_PSC(72M) / (PSC + 1) / (ARR + 1)PSC = 72 - 1ARR = 20000 - 1CCR的范围是[500, 2500]*/// 4配置GPIO(初始化为复用推挽输出,参考引脚定义表)RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;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);// 5运行控制,启动计数器TIM_Cmd(TIM2, ENABLE);
}// 更改通道2的CCR值
void PWM_SetCompare2(uint16_t Compare)
{TIM_SetCompare2(TIM2, Compare);
}

Servo.c

#include "stm32f10x.h"                  // Device header
#include "PWM.h"// 舵机模块
void Servo_Init(void)
{PWM_Init();   // 初始化PWM模块
}// 设置舵机角度。即改CCR的值
void Servo_SetAngle(float angle)
{// 0°   500// 180  2500PWM_SetCompare2(angle / 180 * 2000 + 500);
}

3.3 主函数

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "Key.h"
#include "OLED.h"
#include "Servo.h"uint8_t keyNum;
float angle;int main()
{OLED_Init();       						// 初始化OLED_ShowString(1, 1, "angle:");   		// 显示字符串Servo_Init();KEY_Init();while (1){keyNum = KEY_GetNum();				// 读按键if (keyNum == 1){angle += 30;if (angle > 180){angle = 0;}}Servo_SetAngle(angle);				// 设置舵机角度(设置CCR的值)OLED_ShowNum(1, 7, angle, 3);}
}

现象:按按键,舵机从0°开始,每次转动30°,当大于180°时,回到0°

输入PWM信号要求:周期为20ms,高电平宽度为0.5ms~2.5ms

通过修改CCR的值,改变占空比,进而输出不同占空比的PWM波形。

4 TIM输出比较之PWM驱动直流电机

4.1 接线图

4.2 模块封装

电机接到了通道3上

PWM.c

#include "stm32f10x.h"                  // Device header// PWM初始化函数
void PWM_Init(void)
{// 1RCC开启时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);TIM_InternalClockConfig(TIM2);// 2配置时基单元(预分频器、自动重装器、计数模式等)TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;		// 时钟分频,影响不大TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;	// 计数模式,向上计数TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;			// 重复计数器的值// 关键参数TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;				// ARR自动重装器的值 两个合起来计数0.1秒TIM_TimeBaseInitStructure.TIM_Prescaler = 36 - 1;			// PSC预分频器的值,36分频TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);// 3配置输出比较单元(CCR的值、输出比较模式、极性选择、输出使能)TIM_OCInitTypeDef TIM_OCInitStructure;TIM_OCStructInit(&TIM_OCInitStructure);               					 // 先初始化,后面再按需修改TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;     					 // 输出比较模式,PWM模式1TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;                // 输出比较极性,高级性,极性不反转,有效电平是高电平TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;            // 输出状态输出使能TIM_OCInitStructure.TIM_Pulse = 0;            							 // CCR的值w为50,先设置为0,后面变化// 这个函数的选择按照GPIO口需求来,PA2口对应的是第一个输出比较通道TIM_OC3Init(TIM2, &TIM_OCInitStructure);// 频率1kHz,占空比50% ,分辨率为1%。 CCR = 50 ,/**PWM分辨率:	Reso = 1 / (ARR + 1) = 1%      						==> ARR= 100 - 1PWM占空比:	Duty = CCR / (ARR + 1) = 50%   						==> CCR = 50PWM频率:	Freq = CK_PSC(72M) / (PSC + 1) / (ARR + 1) = 1K  	==> PSC = 720 - 1*/// 4配置GPIO(初始化为复用推挽输出,参考引脚定义表)RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;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);// 5运行控制,启动计数器TIM_Cmd(TIM2, ENABLE);
}// 更改通道1的CCR值
void PWM_SetCompare3(uint16_t Compare)
{TIM_SetCompare3(TIM2, Compare);
}

Motor.c

#include "stm32f10x.h"                  // Device header
#include "PWM.h"// 电机初始化
void Motor_Init(void)
{// 还有两个角需要初始化(控制电机方向)RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);// 配置端口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);// 初始化PWMPWM_Init();
}// 设置速度函数
void Motor_Speed(int8_t speed)
{// 正转if (speed >= 0){// 设置方向GPIO_SetBits(GPIOA, GPIO_Pin_4);GPIO_ResetBits(GPIOA, GPIO_Pin_5);// 速度,RCC的值,即调节占空比PWM_SetCompare3(speed);}else{// 设置方向GPIO_ResetBits(GPIOA, GPIO_Pin_4);GPIO_SetBits(GPIOA, GPIO_Pin_5);// 速度,RCC的值,即调节占空比PWM_SetCompare3(-speed);}}

4.3 主函数

#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()
{OLED_Init();					// 初始化OLEDOLED_ShowString(1, 1, "Speed:");Motor_Init();KEY_Init();while (1){keyNum = KEY_GetNum();if (keyNum == 1){speed += 20;if (speed >= 100){speed = -100;}}Motor_Speed(speed);OLED_ShowSignedNum(1, 7, speed, 3);}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/206635.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JavaWeb-HTTP协议

1. 什么是HTTP协议 HTTP超文本传输协(Hyper Text transfer protocol)&#xff0c;是一种用于用于分布式、协作式和超媒体信息系统的应用层协议。它于1990年提出&#xff0c;经过十几年的使用与发展&#xff0c;得到不断地完善和扩展。HTTP 是为 Web 浏览器与 Web 服务器之间的…

AI自动生成代码工具

AI自动生成代码工具是一种利用人工智能技术来辅助或自动化软件开发过程中的编码任务的工具。这些工具使用机器学习和自然语言处理等技术&#xff0c;根据开发者的需求生成相应的源代码。以下是一些常见的AI自动生成代码工具&#xff0c;希望对大家有所帮助。北京木奇移动技术有…

HCIP —— BGP 基础 (上)

BGP --- 边界网关协议 &#xff08;路径矢量协议&#xff09; IGP --- 内部网关协议 --- OSPF RIP ISIS EGP --- 外部网关协议 --- EGP BGP AS --- 自治系统 由单一的组织或者机构独立维护的网络设备以及网络资源的集合。 因 网络范围太大 需 自治 。 为区分不同的AS&#…

vim常见操作

vim常见操作 文章目录 vim常见操作1. 回退/前进2. 搜索3. 删除4. 定位到50行5. 显示行号6. 复制粘贴7. 剪贴8. 替换9. vim打开文件的时候出现 1. 回退/前进 1.esc进入命令模式 2.ctrlr 前进 u 回退2. 搜索 1&#xff09; esc进入命令模式 2&#xff09; /text  查找text&am…

【STM32】TIM定时器输入捕获

1 输入捕获 1.1 输入捕获简介 IC&#xff08;Input Capture&#xff09;输入捕获 输入捕获模式下&#xff0c;当通道输入引脚出现指定电平跳变时&#xff08;上升沿/下降沿&#xff09;&#xff0c;当前CNT的值将被锁存到CCR中&#xff08;把CNT的值读出来&#xff0c;写入到…

类风湿性关节炎口腔黏膜破裂引发抗瓜氨酸细菌和人蛋白抗体反应

今天给同学们分享一篇实验文章“Oral mucosal breaks trigger anti-citrullinated bacterial and human protein antibody responses in rheumatoid arthritis”&#xff0c;这篇文章发表在Sci Transl Med期刊上&#xff0c;影响因子为17.1。 结果解读&#xff1a; 口腔黏膜破…

Redis主从复制的配置和实现原理

Redis的持久化功能在一定程度上保证了数据的安全性&#xff0c;即便是服务器宕机的情况下&#xff0c;也可以保证数据的丢失非常少。通常&#xff0c;为了避免服务的单点故障&#xff0c;会把数据复制到多个副本放在不同的服务器上&#xff0c;且这些拥有数据副本的服务器可以用…

如何快速构建知识服务平台,打造个人或企业私域流量

随着互联网的快速发展&#xff0c;传统的知识付费平台已经不能满足用户的需求。而SaaS知识付费小程序平台则是一种新型的知识付费方式&#xff0c;具有灵活、便捷、高效等特点&#xff0c;为用户提供了更加优质的付费知识服务。本文将介绍如何搭建自己的SaaS知识付费小程序平台…

如何掌握构建 LMS 网站的艺术

目录 什么是学习管理系统 (LMS) 在线课程和 LMS 网站的好处 为什么 WordPress 对于 LMS 网站很重要 统一学习中心 多功能性和可扩展性 提高教育参与度 简化管理和监控 节省时间和费用 技能评估和绩效监督 持续学习和技能提升 使用 WordPress 插件构建成功的 LMS 课程 专注于您的…

sparkc程序idea调试提示内存不足

报错如下&#xff1a; Exception in thread "main" java.lang.IllegalArgumentException: System memory 259522560 must be at least 471859200. Please increase heap size using the --driver-memory option or spark.driver.memory in Spark configuration. 测…

自动驾驶:传感器初始标定

手眼标定 机器人手眼标定AxxB&#xff08;eye to hand和eye in hand&#xff09;及平面九点法标定 Ax xB问题求解&#xff0c;旋转和平移分步求解法 手眼标定AXXB求解方法&#xff08;文献总结&#xff09; 基于靶的方法 相机标定 (1) ApriTag (2) 棋盘格&#xff1a;cv::f…

富时中国A50指数暴跌

近年来&#xff0c;中国股市的波动一直备受关注&#xff0c;而富时中国A50指数更是其中一项备受瞩目的指标之一。然而&#xff0c;近期却出现了一场引人瞩目的暴跌&#xff0c;引发了广泛的关注和讨论。 富时中国A50指数简介 富时中国A50指数&#xff0c;作为富时罗素指数系列…

全新UI彩虹外链网盘系统源码V5.5/支持批量封禁+优化加载速度+用户系统与分块上传

源码简介&#xff1a; 全新UI彩虹外链网盘系统源码V5.5&#xff0c;它可以支持批量封禁优化加载速度。新增用户系统与分块上传。 彩虹外链网盘&#xff0c;作为一款PHP网盘与外链分享程序&#xff0c;具备广泛的文件格式支持能力。它不仅能够实现各种格式文件的上传&#xff…

CLASS60 DM蓝牙5.2双模热插拔PCB

键盘使用说明索引&#xff08;均为出厂默认值&#xff09; 软件支持&#xff08;驱动的详细使用帮助&#xff09;一些常见问题解答&#xff08;FAQ&#xff09;首次使用步骤蓝牙配对规则&#xff08;重要&#xff09;蓝牙和USB切换键盘默认层默认触发层0的FN键配置的功能默认功…

使用word中的VBA 批量设置Word中所有图片大小

在VBA编辑器中&#xff0c;你可以创建、编辑和运行VBA宏代码&#xff0c;以实现自动化任务和自定义Word 功能。如果你是VBA编程初学者&#xff0c;可以在VBA编辑器中查看Word VBA宏代码示例&#xff0c;以便更好地了解如何使用VBA编写代码。 要打开VBA编辑器&#xff0c;你可以…

【Vue】修改组件样式并动态添加样式

文章目录 目标修改样式动态添加/删除样式样式不生效 注意&#xff1a;类似效果el-step也可以实现&#xff0c;可以不用手动实现。这里只是练习。 目标 使用组件库中的组件&#xff0c;修改它的样式并动态添加/删除样式。 修改样式 组件中的一些类可能添加样式无法生效。如Ele…

[java学习日记]反射、动态代理

目录 一.反射的简单解释与获取字节码文件对象 二.获取构造方法对象Constructor 三.反射获取字节码文件中的成员变量Field 四.反射获取字节码文件中的成员方法&#xff1a;Method 五.反射练习&#xff1a;保存信息 六.反射练习&#xff1a;利用配置文件&#xff08;存储类名…

第21章:网络通信

21.1 网络程序设计基础 21.1.1 局域网与互联网 为了实现两台计算机的通信&#xff0c;必须用一个网络线路连接两台计算机。如下图所示 21.1.2 网络协议 1.IP协议 IP是Internet Protocol的简称&#xff0c;是一种网络协议。Internet 网络采用的协议是TCP/IP协议&#xff0…

Google Bard vs. ChatGPT 4.0:文献检索、文献推荐功能对比

在这篇博客中&#xff0c;我们将探讨和比较四个不同的人工智能模型——ChatGPT 3.5、ChatGPT 4.0、ChatGPT 4.0插件和Google Bard。我们将通过三个问题的测试结果来评估它们在处理特定任务时的效能和响应速度。 导航 问题 1: 统计自Vehicle Routing Problem (VRP)第一篇文章发…

netty源码:(4)ServerBootstrap

ServerBootstrap的group方法用来给成员变量赋值&#xff0c;如下图 AbstractBootstrap为ServerBootstrap的父类。 ServerBootstrap的channel方法用来设置channelFactory成员变量(在父类AbstractBootstrap里&#xff09; ServerBootstrap的childHandler方法用来给本类的成员…