江科大STM32学习笔记——TIM输出比较

本文详细介绍了STM32中的高级定时器如何通过输出比较功能生成PWM波形,涉及占空比调整、输出通道配置、极性选择、死区生成以及与GPIO接口配合驱动舵机和电机的应用实例,展示了编程代码实现过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

输出比较(Output Compare)

  • 通过比较CNT与CCR寄存器值的关系,来对输出电平进行置1、置0或翻转的操作。
    用于输出一定频率和占空比的PWM波形。
  • 每个高级定时器和通用定时器都拥有4个输出比较通道。
  • 高级定时器的前3个通道额外拥有死区生成和互补输出的功能。

 

 PWM波形

 

例:以一个很快的频率让电机通、断、通、断电,那么电机就能都呈现出一个中等速度。

  • 占空比决定了PWM等效出的模拟电压的大小,占空比越高,模拟电压越接近高电压输出。
  • 分辨率就是占空比变化的精细程度

输出比较通道

当CNT计数器和CCR1第一路的捕获/比较寄存器进行比较,当CNT>/=CCR1时,输出模式控制器收到信号,然后改变输出oc1ref信号的高低电平。

然后这个信号一路到主模式控制器去(可以映射到TRGO输出上)

一路通过极性选择电路经过输出使能电路,最后输出到OC1引脚。

可以通过TIMx_CCER寄存器的值来决定是否将oc1ref输出的信号翻转。

输出比较模式

1. 冻结可以用于要暂停一段时间的输出情况

2.匹配时置有/无效电平:可以理解为高/低电平,用于一次性输出,不适合连续波形。

3.输出占空比50%的PWM波形

4.强制:可以用于暂停期间需要保持高/低电平

5.PWM模式1/2主要是极性的区别。可以就选模式1。

参数计算

外接设备(舵机和电机)

给个PWM,输出轴就会固定在一个角度。

上面红色三个引脚控制下面AO的电机,PWMA接PWM输出,而IN引脚可以接GPIO输出,给一个低功率控制信号就能控制电机转动。

输入与输出状态对应关系如右下角所示。

如当IN1置低,IN2置高,PWM为高电平则电机翻转,低电平则电机不转。

代码

1. PWM驱动呼吸灯

OLED部分与之前一样接,灯正极接PA0,负极接GND。

首先初始化PWM,即把输出比较模块打通。

//配置输出比较单元的函数
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);

//配置强制输出模式
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);

//单独数值输出比歼的极性(单独修改输出极性)
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);

//单独更改CCR寄存器的值(运行时改变占空比)
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);

//以下不常用
//配置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);

//在使用高级定时器时,需调用使能主输出,否则PWM不能正常输出
void TIM_CtrlPWMOutputs(TIM_TypeDef* TIMx, FunctionalState NewState);

1. RCC开启时钟,把要用的TIM外设和GPIO口的时钟打开

2. 配置时基单元,包括时钟源选择。

3. 配置输出比较单元,包括输出比较模式、极性选择、输出使能

4. 配置GPIO,把PWM输出对应的GPIO口初始化为复用推挽输出

查看引脚定义,TIM2的通道1和ETR复用在了PA0端口上。

5. 运行控制,启动计数器输出PWM

main.c

#include "stm32f10x.h"// Device header
#include "Delay.h"
#include "OLED.h"
#include "PWM.h"

uint8_t i;

int main(void)
{
	OLED_Init();
	PWM_Init();

	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);
		}			
	}	
	
}

PWM.c

#include "stm32f10x.h"                  // Device header


void PWM_Init(void)
{
	//打开时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽,将引脚控制权交与片上外设
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);

	//配置TIM,时基单元
	TIM_InternalClockConfig(TIM2);
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
	TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;	
	TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInitStruct.TIM_Period = 100-1;		//ARR的值		
	TIM_TimeBaseInitStruct.TIM_Prescaler = 720-1;	//PSC的值	
	TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);
	
	//初始化输出比较
	TIM_OCInitTypeDef TIM_OCInitStruct;
	TIM_OCStructInit(&TIM_OCInitStruct);
	TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;//输出模式PWM1
	TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;//高极性
	TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;//
	TIM_OCInitStruct.TIM_Pulse = 0;	//CCR的值
	TIM_OC1Init(TIM2,&TIM_OCInitStruct);
	
	TIM_Cmd(TIM2,ENABLE);//启动定时器
	
}

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

2. PWM驱动舵机

根据舵机所需高电平时间及相应公式计算出各个寄存器的值。

	TIM_TimeBaseInitStruct.TIM_Period = 2000-1;		//ARR的值		
	TIM_TimeBaseInitStruct.TIM_Prescaler = 72-1;	//PSC的值

	TIM_OCInitStruct.TIM_Pulse = 0;	//CCR的值

CCR:500->0°,2500->180°,根据线性关系可计算。

//main.c

#include "stm32f10x.h"// Device header
#include "Delay.h"
#include "OLED.h"
#include "Servo.h"

uint8_t i;
float Angle;

int main(void)
{
	OLED_Init();
	Servo_Init();
	
	Servo_SetAngle(90);
	OLED_ShowString(1,1,"Angle:");
	while(1)
	{
		
		Angle += 30;
		if(Angle > 180)
			Angle = 0;
		Delay_s(5);
		Servo_SetAngle(Angle);
		OLED_ShowNum(2,1,Angle,7);
	}	
	
}

#include "stm32f10x.h"                  // Device header


void PWM_Init(void)
{
	//打开时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽,将引脚控制权交与片上外设
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);

	//配置TIM,时基单元
	TIM_InternalClockConfig(TIM2);
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
	TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;	
	TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInitStruct.TIM_Period = 2000-1;		//ARR的值		
	TIM_TimeBaseInitStruct.TIM_Prescaler = 72-1;	//PSC的值	
	TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);
	
	//初始化输出比较
	TIM_OCInitTypeDef TIM_OCInitStruct;
	TIM_OCStructInit(&TIM_OCInitStruct);
	TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;//输出模式PWM1
	TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;//高极性
	TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;//
	TIM_OCInitStruct.TIM_Pulse = 0;	//CCR的值
	TIM_OC2Init(TIM2,&TIM_OCInitStruct);
	
	TIM_Cmd(TIM2,ENABLE);//启动定时器
	
}

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

3.  PWM驱动电机

TB6612硬件电路如下,根据引脚定义接线。

 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_Pin = GPIO_Pin_4 | GPIO_Pin_5;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	PWM_Init();
	
}

//设置速度函数
void Motor_SetSpeed(int8_t Speed)  //带符号的,负号表示反转
{
	if(Speed > 0)
	{
		GPIO_SetBits(GPIOA,GPIO_Pin_4);
		GPIO_ResetBits(GPIOA,GPIO_Pin_5);
		PWM_SetCompare3(Speed);
	}
	else
	{
		GPIO_ResetBits(GPIOA,GPIO_Pin_4);
		GPIO_SetBits(GPIOA,GPIO_Pin_5);
		PWM_SetCompare3(-Speed);
	}
	
}

 main.c

#include "stm32f10x.h"// Device header
#include "Delay.h"
#include "OLED.h"
#include "Motor.h"

uint8_t i;
float Angle;

int main(void)
{
	OLED_Init();
	Motor_Init();
	
	Motor_SetSpeed(-50);
	while(1)
	{

	}	
	
}

### 关于科大 STM32 输入捕获 学习笔记 教程 #### 什么是输入捕获? 输入捕获是一种通过定时器捕捉外部信号变化的功能。它通常用于测量脉冲宽度、频率或其他时间相关的参数。STM32 的定时器模块提供了强大的输入捕获功能,可以通过配置通道来检测特定事件并记录对应的时间戳。 在科大STM32 学习笔记中提到,STM32 的定时器支持多种高级特性,其中包括 **输入捕获** 功能[^3]。该功能允许用户利用定时器的输入通道捕获外部信号的变化时刻,并将其转换为计数值以便进一步处理。 --- #### 配置输入捕获的关键步骤 以下是实现输入捕获的主要技术要点: 1. **选择合适的定时器** - 使用具有输入捕获功能的通用定时器(如 TIM2, TIM3 等)。这些定时器具备多个通道,可作为输入捕获使用。 2. **设置时钟源** - 定时器的工作依赖内部或外部时钟源。对于输入捕获应用,可以选择外部时钟源以同步外部信号[^3]。 3. **配置输入捕获模式** - 设置定时器通道为输入捕获模式(IC Mode),并通过滤波器减少噪声干扰。 - 配置极性(上升沿、下降沿或双边沿触发)以及捕获预分频系数。 4. **启用中断或 DMA** - 当捕获到指定事件时,可通过中断服务程序读取捕获寄存器中的值,或者借助 DMA 将数据传输至内存缓冲区。 5. **计算实际时间间隔** - 利用捕获寄存器存储的计数值和定时器时基频率,可以推导出两次捕获之间的时间差。 --- #### 示例代码:基于 STM32 的输入捕获实现 以下是一个简单的输入捕获示例代码片段,展示如何配置 TIM2 进行输入捕获操作: ```c #include "stm32f10x.h" void TIM2_InputCapture_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_ICInitTypeDef TIM_ICInitStructure; // 启用相关外设时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // 配置 PA0 (TIM2_CH1) 为复用输入模式 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入 GPIO_Init(GPIOA, &GPIO_InitStructure); // 初始化 TIM2 时间基准结构体 TIM_TimeBaseStructInit(&TIM_TimeBaseStructure); TIM_TimeBaseStructure.TIM_Period = 65535; // 自动重装载值 TIM_TimeBaseStructure.TIM_Prescaler = 72 - 1; // 预分频器,假设系统时钟为 72 MHz TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); // 初始化 TIM2 输入捕获结构体 TIM_ICStructInit(&TIM_ICInitStructure); TIM_ICInitStructure.TIM_Channel = TIM_Channel_1; // 选择通道 1 TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; // 上升沿捕获 TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; // 直接映射 TI1 TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; // 不分频 TIM_ICInitStructure.TIM_ICFilter = 0xF; // 数字滤波器 TIM_ICInit(TIM2, &TIM_ICInitStructure); // 开启 TIM2 中断 TIM_ITConfig(TIM2, TIM_IT_CC1, ENABLE); // 启动定时器 TIM_Cmd(TIM2, ENABLE); } // 中断服务函数 void TIM2_IRQHandler(void) { if (TIM_GetITStatus(TIM2, TIM_IT_CC1) != RESET) { // 检查 CC1 中断标志 uint16_t capture_value = TIM_GetCapture1(TIM2); // 获取捕获值 TIM_ClearITPendingBit(TIM2, TIM_IT_CC1); // 清除中断标志 } } ``` 此代码展示了如何初始化 TIM2 并配置其通道 1 为输入捕获模式。当检测到上升沿时,会触发中断并将捕获值保存下来。 --- #### 参考资源推荐 除了上述内容之外,还可以参考以下资源深入学习: - STMicroelectronics 提供的《STM32 Reference Manual》详细描述了定时器架构及其工作原理[^1]。 - `Delay.h` 文件定义了一些基础延时函数,虽然不直接涉及输入捕获,但可以帮助理解硬件延迟的概念[^2]。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值