04:TIM定时器功能---输出比较功能(PWM)

目录

一:TIM输出比较功能

1: 简历

2:PWM波形

 3:输出比较模式

4:参数计算

5:PWM基本结构

 6:输出比较功能的实际应用

A:PWM驱动LED呼吸灯

1:连接图

2:步骤

 3:函数介绍

4:代码

B:PWM驱动舵机

1:连接图

2:舵机介绍

3:步骤

4:代码

C:PWM驱动直流电机 

1:连接图

 2:电机与驱动电路

3:代码


一:TIM输出比较功能

1: 简历

        OC(Output Compare)输出比较

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

         每个高级定时器和通用定时器都拥有4个输出比较通道

        高级定时器的前3个通道额外拥有死区生成和互补输出的功能

2:PWM波形

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

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

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

 

 3:输出比较模式

 配置比较单元时使用TIM_OCXInit()

    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择PWM1的模式

4:参数计算

5:PWM基本结构

 6:输出比较功能的实际应用

A:PWM驱动LED呼吸灯

1:连接图

2:步骤

1 :RCC开启时钟,(TIM外设---RCC_APB1PeriphClockCmd和GPIO外设----RCC_APB2PeriphClockCmd的时钟打开)

2: 配置GPIO----GPIO_Init    (最后写第二步)

3: 时钟选择----TIM_InternalClockConfig(内部)

4: 配置时基单元-----TIM_TimeBaseInit()

5: 配置输出比较单元----TIM_OC1Init()

6:启动定时器-----TIM_Cmd

 3:函数介绍

在stm32f10x tim.h文件中的函数-----配置输出比较单元函数

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

TIM_OCXInit : 配置输出比较单元 

在stm32f10x tim.h文件中的函数

void TIM_OCStructInit(TIM_OCInitTypeDef* TIM_OCInitStruct);

 TIM_OCStructInit : 给输出比较结构体赋一个默认的值

在stm32f10x tim.h文件中的函数-----改变占空比函数

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

 TIM_SetCompareX :  来单独更改CCR寄存器值的函数,可以更改占空比

4:代码

每次调用修改它的占空比,以达到呼吸灯的作用


#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "PWM.h"
void PWM_init(){
	//第一步,RCC开启时钟,把我们要用的TIM外设和GPIO外设的时钟打开
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	//第二步,配置GPIO
	GPIO_InitTypeDef GPIO_INITStruct;
	GPIO_INITStruct.GPIO_Mode=GPIO_Mode_AF_PP;  //复用推挽输出
	GPIO_INITStruct.GPIO_Pin=GPIO_Pin_0 ;
	GPIO_INITStruct.GPIO_Speed= GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_INITStruct);
	//第三步,时钟源选择(内部时钟)
	TIM_InternalClockConfig(TIM2);
	//第四步,配置时基单元
	TIM_TimeBaseInitTypeDef TIM_INITStruct;
	TIM_INITStruct.TIM_ClockDivision=TIM_CKD_DIV1;
	TIM_INITStruct.TIM_CounterMode=TIM_CounterMode_Up;  向上计数
	TIM_INITStruct.TIM_Period=100-1;     ///自动重装载寄存器ARR
	TIM_INITStruct.TIM_Prescaler=720-1;    // //预分频器PSC
	TIM_INITStruct.TIM_RepetitionCounter=0;
	TIM_TimeBaseInit(TIM2,&TIM_INITStruct);
	//第五步,配置输出比较单元
	TIM_OCInitTypeDef TIM_OCInitStructure;
	TIM_OCStructInit(&TIM_OCInitStructure);//给输出比较结构体赋一个默认的值
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择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);
}

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



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


为什么选择TIM_OC1Init

 在这里可以看到,有TM2-CH1_ETR它是在这个PAO这一行的 , 这就说明,TIM2的ETR引脚和通道1的引脚都是借用了PAO这个引脚的位置的.  换句话说就是TM2的引脚复用在了PAO引脚上 , 所以说如果我们要使用TIM2的OC1也就是CH1通道,输出PWM, 那它就只能在PA0的引脚上输出一而不能任意选择引脚输出;    PA0输出PWM波形

为什么选择TIM_SetCompare1

因为使用的是TIM_OC1Init  CH1通道

 TIM_SetCompareX :  来单独更改CCR寄存器值的函数,可以更改占空

 引脚重定义

 

那首先,要使用AFIO,就要开启AFIO的时钟

RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);

引脚重映射

void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState);

   GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2,ENABLE);//部分重映射1

 将PA0改为PA15

因为PA15在它上电后已经默认复用为了调试端回TDIT,如果想让他作为普通的GPIO或者复用定时器的通道, 需要先关闭调试端口的复用(需要谨慎)

    //PA15、PB3、PB4这三个引脚当做GPIO来使用的话
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);//解除JTAG复用(解除重映射端口)
	//想重映射定时器或者其他外设的复用引脚
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
	GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2,ENABLE);//GPIO_PartialRemap1_TIM2部分重映射1
	//如果重新映射的使调式端口使用
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
	GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2,ENABLE);//GPIO_PartialRemap1_TIM2部分重映射1
	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);解除JTAG复用(解除重映射端口)
	

 GPIO调式 选择复用推挽输出

GPIO_INITStruct.GPIO_Mode=GPIO_Mode_AF_PP;  //复用推挽输出
输出控制权将转移给片上外设,PWM波形才能通过引脚输出

计算 

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

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

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

CK_PSC=72MHZ           PSC=TIM_INITStruct.TIM_Prescaler=;    // //预分频器PSC

ARR=    TIM_INITStruct.TIM_Period=;     ///自动重装载寄存器ARR

CRR=    TIM_OCInitStructure.TIM_Pulse = ;        //CCR

分辨率 = 占空比变化步距

频率为1KHz,占空比为50%,分辨率为1%的PWM波形

 

B:PWM驱动舵机

1:连接图

 红色-----接USE5V(3v带不动)

橙色-----信号线              棕色---地线

2:舵机介绍

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

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

 高电平宽度=某电频的持续时间

CCR=高电频

舵机的控制一般需要一个20ms的时基脉冲(周期),该脉冲的高电平部分一般为0.5ms~2.5ms范围内的角度控制脉冲部分。以180度角度舵机为例,那么对应的控制关系是这样的:
0.5ms--------------0度;
1.0ms------------45度;
1.5ms------------90度;
2.0ms-----------135度;
2.5ms-----------180度;

3:步骤

1 :RCC开启时钟,(TIM外设---RCC_APB1PeriphClockCmd和GPIO外设----RCC_APB2PeriphClockCmd的时钟打开)

2: 配置GPIO----GPIO_Init    (最后写第二步)

3: 时钟选择----TIM_InternalClockConfig(内部)

4: 配置时基单元-----TIM_TimeBaseInit()

5: 配置输出比较单元----TIM_OC2Init()

6:启动定时器-----TIM_Cmd

4:代码

这个使用的是CH2通道, 上面的LED使用的是CH2通道

使用: TIM_SetCompareX和TIM_OCXInit 函数都有换改为相对应的X值

X应为2.见下面的引脚定义表

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Servo.h"
#include "Key.h"
void PWM_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
	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);
	
	TIM_InternalClockConfig(TIM2);
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	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;
	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);
}


void Servo_Init(void)
{
	PWM_Init();
}

void Servo_SetAngle(float Angle)
{
	PWM_SetCompare2(Angle / 180 * 2000 + 500);
}


void Key_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_11;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
}

uint8_t Key_GetNum(void)
{
	uint8_t KeyNum = 0;
	if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
	{
		Delay_ms(20);
		while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0);
		Delay_ms(20);
		KeyNum = 1;
	}
	if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0)
	{
		Delay_ms(20);
		while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0);
		Delay_ms(20);
		KeyNum = 2;
	}
	
	return KeyNum;
}

uint8_t KeyNum;
float Angle;

int main(void)
{
	OLED_Init();
	Servo_Init();
	Key_Init();
	
	OLED_ShowString(1, 1, "Angle:");
	
	while (1)
	{
		KeyNum = Key_GetNum();
		if (KeyNum == 1)
		{
			Angle += 30;
			if (Angle > 180)
			{
				Angle = 0;
			}
		}
		Servo_SetAngle(Angle);
		OLED_ShowNum(1, 7, Angle, 3);
	}
}

计算

 TIM_SetCompareX :  来单独更改CCR寄存器值的函数,可以更改占空比

 

引脚定义表

 舵机的信号线与PA1连接所以使用的是CH2通道2,使用X为2;     PA1输出PWM波形

C:PWM驱动直流电机 

1:连接图

 2:电机与驱动电路

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

        当电极反接时,电机反转直流电机属于大功率器件,GPIO口无法直接驱动,需要配合电机驱动电路来操作
        TB6612是一款双路H桥型的直流电机驱动芯片,可以驱动两个直流电机并且控制其转速和方向

 

 电机驱动详情见: 51:电机(ULN2003D)这一篇

3:代码
#include "stm32f10x.h"                  // Device header
#include "OLED.h"
#include "PWM.h"
#include "motor.h"
#include "Key.h"
void PWM_init(){
	//第一步,RCC开启时钟,把我们要用的TIM外设和GPIO外设的时钟打开
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	

	//第二步,配置GPIO
	GPIO_InitTypeDef GPIO_INITStruct;
	GPIO_INITStruct.GPIO_Mode=GPIO_Mode_AF_PP;  //复用推挽输出
	GPIO_INITStruct.GPIO_Pin=GPIO_Pin_2 ;
	GPIO_INITStruct.GPIO_Speed= GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_INITStruct);
	//第三步,时钟源选择(内部时钟)
	TIM_InternalClockConfig(TIM2);
	//第四步,配置时基单元
	TIM_TimeBaseInitTypeDef TIM_INITStruct;
	TIM_INITStruct.TIM_ClockDivision=TIM_CKD_DIV1;
	TIM_INITStruct.TIM_CounterMode=TIM_CounterMode_Up;  向上计数
	TIM_INITStruct.TIM_Period=100-1; 
	TIM_INITStruct.TIM_Prescaler=720-1;
	TIM_INITStruct.TIM_RepetitionCounter=0;
	TIM_TimeBaseInit(TIM2,&TIM_INITStruct);
	//第五步,配置输出比较单元
	TIM_OCInitTypeDef TIM_OCInitStructure;
	TIM_OCStructInit(&TIM_OCInitStructure);//给输出比较结构体赋一个默认的值
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择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);
}
void motor_init(){
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	GPIO_InitTypeDef GPIO_INITstruct;
	GPIO_INITstruct.GPIO_Mode=GPIO_Mode_IPU;//GPIO_Mode_Out_PP  //GPIO_Mode_IPU
	GPIO_INITstruct.GPIO_Pin=GPIO_Pin_4|GPIO_Pin_5;
	GPIO_INITstruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_INITstruct);
	PWM_init();
}

void motor_serspeed(int8_t speed){
	if(speed >=0){
		GPIO_SetBits(GPIOA,GPIO_Pin_5);  //高电频
		GPIO_ResetBits(GPIOA,GPIO_Pin_4); //低电频
		PWM_SetCompare3(speed);
	}
	else
	{	
		GPIO_SetBits(GPIOA,GPIO_Pin_4);  //高电频
		GPIO_ResetBits(GPIOA,GPIO_Pin_5); //低电频
		//传穿过来的speed为负数,要变为正,所以要加一个负号;
		PWM_SetCompare3(-speed);
	}
}
void Key_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_11;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
}

uint8_t Key_GetNum(void)
{
	uint8_t KeyNum = 0;
	if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
	{
		Delay_ms(20);
		while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0);
		Delay_ms(20);
		KeyNum = 1;
	}
	if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0)
	{
		Delay_ms(20);
		while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0);
		Delay_ms(20);
		KeyNum = 2;
	}
	
	return KeyNum;
}


uint8_t key_num;
int8_t speed;  //速度必须是有符号的
int main(void)
{
	Key_Init();
	OLED_Init();
	motor_init();
	
	OLED_ShowString(1,1,"speed:");
	
	while (1)
		
	{	
		key_num=Key_GetNum();
		if (key_num==1){
			speed+=20;
			if(speed>100){
				speed=-100;
			}
		}
			motor_serspeed(speed);
			OLED_ShowSignedNum(1, 7, speed, 3);
	}
	
	
	}
	

  • 4
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
void BLDC_PHASE_CHANGE(uint8_t step) { #if 0 // H_PWM_L_ON switch(step) { case 4: //B+ C- /* Next step: Step 2 Configuration -------------------------------------- */ TIM_CCxCmd(BLDC_TIMx,TIM_Channel_1,TIM_CCx_Disable); TIM_CCxNCmd(BLDC_TIMx,TIM_Channel_1,TIM_CCxN_Disable); /* Channel1 configuration */ /* Channel2 configuration */ TIM_SetCompare2(BLDC_TIMx,BLDC_TIM_PERIOD*speed_duty/1000); TIM_CCxCmd(BLDC_TIMx,TIM_Channel_2,TIM_CCx_Enable); /* Channel3 configuration */ TIM_SetCompare3(BLDC_TIMx,BLDC_TIM_PERIOD); TIM_CCxNCmd(BLDC_TIMx,TIM_Channel_3,TIM_CCxN_Enable); break; case 5: //B+ A- /* Next step: Step 3 Configuration -------------------------------------- */ TIM_CCxCmd(BLDC_TIMx,TIM_Channel_3,TIM_CCx_Disable); TIM_CCxNCmd(BLDC_TIMx,TIM_Channel_3,TIM_CCxN_Disable); /* Channel1 configuration */ TIM_SetCompare1(BLDC_TIMx,BLDC_TIM_PERIOD); TIM_CCxNCmd(BLDC_TIMx,TIM_Channel_1,TIM_CCxN_Enable); /* Channel2 configuration */ TIM_SetCompare2(BLDC_TIMx,BLDC_TIM_PERIOD*speed_duty/1000); TIM_CCxCmd(BLDC_TIMx,TIM_Channel_2,TIM_CCx_Enable); /* Channel3 configuration */ break; case 1: //C+ A- /* Next step: Step 4 Configuration -------------------------------------- */ TIM_CCxCmd(BLDC_TIMx,TIM_Channel_2,TIM_CCx_Disable); TIM_CCxNCmd(BLDC_TIMx,TIM_Channel_2,TIM_CCxN_Disable); /* Channel1 configuration */ TIM_SetCompare1(BLDC_TIMx,BLDC_TIM_PERIOD); TIM_CCxNCmd(BLDC_TIMx,TIM_Channel_1,TIM_CCxN_Enable); /* Channel2 configuration */ /* Channel3 configuration */ TIM_SetCompare3(BLDC_TIMx,BLDC_TIM_PERIOD*speed_duty/1000); TIM_CCxCmd(BLDC_TIMx,TIM_Channel_3,TIM_CCx_Enable); break; case 3: //C+ B- /* Next step: Step 5 Configuration -------------------------------------- */ TIM_CCxCmd(BLDC_TIMx,TIM_Channel_1,

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值