stm32-定时器输出比较PWM

目录

一、输出比较简介

二、PWM简介

三、输出比较模式实现

1.输出比较框图(以通用定时器为例)

2.PWM基本结构

四、固件库实现

1.程序1:PWM呼吸灯

2.程序2:PWM驱动直流电机

3.程序3:控制舵机


一、输出比较简介

死区生成和互补输出一般用于对电机的控制

二、PWM简介

惯性系统:即要能使人眼产生视觉停留的系统

三、输出比较模式实现

1.输出比较框图(以通用定时器为例)

高级定时器比通用定时器多了个互补输出和死区生成
如右图,两个mos管构成了推挽电路,上管导通下管关闭输出高电平,下管导通上管关闭输出低电平,两管都关闭为高阻态,两管都导通为短路,会对元器件造成损伤,所以两管不能同时导通

++互补输出++:当单片机要控制这个电路时,就需要两个输出端口,且二者电平要相反,即互补,而这里OC1和OC1N就是互补的两个端口,即互补输出

++死区发生器++:但是若在上管关闭的瞬间下管就导通,很可能由于器件的不理想而出现上下管都导通的情况,为了避免这种情况发生,于是又是死区发生器,即他可以在上管关闭后延迟一段时间再导通下管,避免同时导通

2.PWM基本结构

四、固件库实现

1.程序1:PWM呼吸灯

1.我使用的是TIM3的CH2的重定义引脚PB5,所以要打开AFIO时钟

2.开启TIM3和GPIO的时钟

3.初始化GPIO结构体。注意:要记得使用库函数进行重定义

4.选择TIM3的时钟输入,可以为内部时钟,外部时钟模式1和2,

外部时钟模式1:来源可以是ITR(其他定时器,多用于定时器级联),可以是ETR(外部时 钟),可以是CH1引脚的边沿,CH1引脚和CH2引脚(多用于输入捕获测频率)

外部时钟模式2:ETR的触发控制模式

5.初始化TIM3结构体--配置PSC,ARR,计数模式,等等

6.初始化OC结构体--PWM模式选择,CCR,输出ref的有效电平

7.定时器使能

cs 复制代码
//使用TIM3的通道2的重定义引脚PB5-指南者上面是红灯
//我们配置
void PWM_Config()
{
	//首先开启GPIO时钟
	//开启AFIO时钟,因为用到了重定义
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO,ENABLE);
	//开启定时器的时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
	
	//重定义引脚
	//选择部分重定义
	GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3,ENABLE);
	
	//初始化GPIO结构体-PB5-输出比较
	GPIO_InitTypeDef GPIO_InitStruct;
  GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_AF_PP;//复用推挽,手册可看
  GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5;
  GPIO_Init(GPIOB,&GPIO_InitStruct);
	
	//选择TIM3的时钟输入
	TIM_InternalClockConfig(TIM3);//我们直接使用内部时钟
	
	//配置TIM结构体
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
	TIM_TimeBaseInitStruct.TIM_Prescaler = 72-1;//PSC
	TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;//计数模式
	TIM_TimeBaseInitStruct.TIM_Period = 100-1;//ARR
	TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;//输入滤波器的分频
	TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;//重复计数器,只有高级定时器才有
	TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStruct);
	
	//初始化OC结构体
	TIM_OCInitTypeDef TIM_OCInitStruct;
	TIM_OCStructInit(&TIM_OCInitStruct);//先赋初值,因为我们没有把结构体配置完全
	TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;//模式选择-PWM1
	TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;//输出使能
	TIM_OCInitStruct.TIM_Pulse = 0;//CCR,这里我们不配置CCR的值,后面用一个库函数直接在主函数            
                                     里面配置,实现呼吸灯
	TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;//输出ref极性,选择有效电平,即ref有效时输出高电平
	//TIM_OCInitStruct.TIM_OCNPolarity = ;
	//TIM_OCInitStruct.TIM_OCIdleState = ;
	//TIM_OCInitStruct.TIM_OCNIdleState = ;
	//TIM_OCInitStruct.TIM_OutputNState = ;
	TIM_OC2Init(TIM3,&TIM_OCInitStruct);
	
	//启动定时器
	TIM_Cmd(TIM3,ENABLE);
	
}

void PWM_SetCompare2(uint16_t Compare)
{
	TIM_SetCompare2(TIM3,Compare);
}
cs 复制代码
main.c

#include "stm32f10x.h"   // Device header
#include "bsp_led.h"
#include ".\tim\bsp_tim.h"

extern uint16_t Num;//定时器都是16位的

int i;
void Delay(u32 i)
{
    u32 temp;
    SysTick->LOAD=9000*i;      //设置重装数值, 72MHZ时
    SysTick->CTRL=0X01;        //使能,减到零是无动作,采用外部时钟源
    SysTick->VAL=0;            //清零计数器
    do
    {
        temp=SysTick->CTRL;       //读取当前倒计数值
    }
    while((temp&0x01)&&(!(temp&(1<<16))));    //等待时间到达
    SysTick->CTRL=0;    //关闭计数器
    SysTick->VAL=0;        //清空计数器
}


//ARR=99-->PWM一个周期是100,那么分辨率为1%
//占空比 = CCR/(ARR+1) 
//频率 = 计数器溢出频率 = CK_PSC/(PSC+1)/(ARR+1) = 72M/720/100= 1000HZ ->1ms
int main()
{

	LED_GPIO_Config();
	PWM_Config();
	
	while(1)
	{
		for(i=0;i<=100;i++)
		{
			PWM_SetCompare2(i);
			Delay(10);
		}
		for(i=100;i>=0;i--)
		{
			PWM_SetCompare2(i);
			Delay(10);
		}
	}
}

2.程序2:PWM驱动直流电机

具体TIM的配置过程同呼吸灯一样,同样是输出不同的PWM占空比来实现电机的不同速度

我们需要三个引脚,一个输出PWM给电机,两个接电机的控制引脚

  • 使用PA2输出PWM,AIN1/2接到PA4/5
  • 频率设置为1KHZ(可以自己随便设置)
  • 定义一个八位有符号的变量Speed,+:正转 -:反转
  • 使用GPIO_SetBits/ResetBits();来设置AIN1/2的电平高低
  • 使用按键来改变转速
cs 复制代码
void PWM_Config()
{
	//开启时钟
	//使用TIM2的CH3的PA2
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//开启PWM引脚


	//初始化GPIO
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_AF_PP;//复用推挽,手册可看
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_4 | GPIO_Pin_5 ;
	GPIO_Init(GPIOA,&GPIO_InitStruct);
	
	//选择时基单元的时钟-为内部时钟--定时器上电后默认是内部时钟,故不写这一个也行
	TIM_InternalClockConfig(TIM2);
	//初始化时基单元
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
	
	//配置1KHZ的PWM
	TIM_TimeBaseInitStruct.TIM_Prescaler = 720-1;//PSC-预分频器
	TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;//向上计数 
	TIM_TimeBaseInitStruct.TIM_Period = 100-1;//ARR寄存器-重装载寄存器
	TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;/*不分频----滤波器的采样频率,可以由内部时钟直接提供,
																													也可以由内部时钟加一个时钟分频而来,
																													分频系数就是由TIM_ClockDivision决定*/
	TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;//重复计数器,只有高级定时器才有
	TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);
	
	//初始化OC-输出比较结构体
	TIM_OCInitTypeDef TIM_OCInitStruct;
	TIM_OCStructInit(&TIM_OCInitStruct);//因为结构体里面的成员有些是高级定时器采用得到,所以这里就先全部初始化一遍,然后再配置具体的值
	TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;//输出比较模式
	TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;
	//TIM_OCInitStruct.TIM_Pulse = 50;//CRR 
	TIM_OCInitStruct.TIM_Pulse = 0;//输出要求波型,这里的CRR就不需要了,用固件库的一个函数 TIM_SetCompare3 直接配置CRR
	TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;//输出比较极性
	TIM_OC3Init(TIM2,&TIM_OCInitStruct);//CH3通道
	
	
	//启动定时器
	TIM_Cmd(TIM2,ENABLE);

}
cs 复制代码
//电机控制函数

void Motor_SetSpeed(int8_t Speed)//+:正转 -:反转
{
	if(Speed >= 0)
	{
		GPIO_SetBits(GPIOA,GPIO_Pin_4);
		GPIO_ResetBits(GPIOA,GPIO_Pin_5);
		TIM_SetCompare3(TIM3,Speed);
	}
	else 
	{
		GPIO_ResetBits(GPIOA,GPIO_Pin_4);
		GPIO_SetBits(GPIOA,GPIO_Pin_5);
		TIM_SetCompare3(TIM3,-Speed);
	}
}
cs 复制代码
//主函数


#include "stm32f10x.h"   // Device header
#include ".\tim\bsp_tim.h"
#include ".\KEY\bsp_key.h"

uint8_t KeyNum;
int8_t Speed;

int main()
{
	KEY_GPIO_Config();
	PWM_Config();
	
	while(1)
	{
		KeyNum = Key_Scan();
		if(KeyNum == 0)
		{
			Speed += 20;
			if(Speed > 100)
			{
				Speed = -100;
			}
		}
		else if(KeyNum == 1)
		{
			Speed -= 20;
			if(Speed < -100)
			{
				Speed = 100;
			}
		}
		Motor_SetSpeed(Speed);
    }
}

3.程序3:控制舵机

-TB6612驱动板

VM->STLINK的5v

VCC->面包板3.3v

GND->面包板负极

AO1 AO2 接电机

STBY->待机控制引脚,这里不需要待机,接面包3.3v

AIN1/2-> 任意接两个引脚

PWMA->PWM输出控制引脚

驱动VM放在左下角使用PA2输出PWM,AIN1/2接到PA4/5

  • 要点:输出如上图右侧所示的PWM波型
  • 指南者的PA0引脚为按键1,所以使用TIM2的CH3通道的PA2
  • PWM要求频率为50HZ,即总时间20ms,高电平占0.5~2.5ms,这里我们可以给ARR配置20000-1,PSC配置72-1
  • 封装Angle转换函数0-50 180->2500 -->y=Angle/180*2500+50, Angle使用浮点型,利于计算
  • 使用按键来改变角度
cs 复制代码
#include "bsp_tim.h"

void PWM_Config()
{
	//开启时钟
	//使用TIM2的CH3的PA2
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//开启PWM引脚


	//初始化GPIO
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_AF_PP;//复用推挽,手册可看
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2;
	GPIO_Init(GPIOA,&GPIO_InitStruct);
	
	//选择时基单元的时钟-为内部时钟--定时器上电后默认是内部时钟,故不写这一个也行
	TIM_InternalClockConfig(TIM2);
	//初始化时基单元
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
	
	TIM_TimeBaseInitStruct.TIM_Prescaler = 72-1;//PSC-预分频器
	TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;//向上计数 
	TIM_TimeBaseInitStruct.TIM_Period = 20000-1;//ARR寄存器-重装载寄存器
	TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;/*不分频----滤波器的采样频率,可以由内部时钟直接提供,
																													也可以由内部时钟加一个时钟分频而来,
																													分频系数就是由TIM_ClockDivision决定*/
	TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;//重复计数器,只有高级定时器才有
	TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);
	
	//初始化OC-输出比较结构体
	TIM_OCInitTypeDef TIM_OCInitStruct;
	TIM_OCStructInit(&TIM_OCInitStruct);//因为结构体里面的成员有些是高级定时器采用得到,所以这里就先全部初始化一遍,然后再配置具体的值
	TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;//输出比较模式
	TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;
	//TIM_OCInitStruct.TIM_Pulse = 50;//CRR   --舵机要求设置频率50HZ-即总时间20ms,高电平时间在0.5~2.5ms之间的PWM波型即CCR->500~2500
	TIM_OCInitStruct.TIM_Pulse = 0;//输出要求波型,这里的CRR就不需要了,用固件库的一个函数 TIM_SetCompare3 直接配置CRR
	TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;//输出比较极性
	TIM_OC3Init(TIM2,&TIM_OCInitStruct);//CH3通道
	
	
	//启动定时器
	TIM_Cmd(TIM2,ENABLE);
}
//0.5ms-0度   2.5ms-180度
//0度   	CCR=500
//180度   CCR=2500
//舵机设置角度,范围0~180
void Servo_SetAngle(float Angle)
{
	TIM_SetCompare3(TIM2,Angle*2000/180+500);
}


void PWM_SetCompare3(uint16_t Compare)//设置CRR
{
	TIM_SetCompare3(TIM2,Compare);
}
cs 复制代码
#include "stm32f10x.h"   // Device header
#include ".\tim\bsp_tim.h"
#include ".\KEY\bsp_key.h"
uint8_t KeyNum;
float Angle;

int main()
{
	KEY_GPIO_Config();
	LED_GPIO_Config();
	PWM_Config();
	
	while(1)
	{
		KeyNum = Key_Scan();
		if(KeyNum == 0)
		{
			Angle+=30;
			if(Angle>180)
			{
				Angle = 0;
			}
		}
		else if(KeyNum == 1)
		{
			Angle-=30;
			if(Angle<0)
			{
				Angle = 180;
			}
		}
		Servo_SetAngle(Angle);
	}
}
相关推荐
智者知已应修善业2 小时前
【51单片机用数码管显示流水灯的种类是按钮控制数码管加一和流水灯】2022-6-14
c语言·经验分享·笔记·单片机·嵌入式硬件·51单片机
智商偏低8 小时前
单片机之helloworld
单片机·嵌入式硬件
青牛科技-Allen9 小时前
GC3910S:一款高性能双通道直流电机驱动芯片
stm32·单片机·嵌入式硬件·机器人·医疗器械·水泵、
森焱森11 小时前
无人机三轴稳定控制(2)____根据目标俯仰角,实现俯仰稳定化控制,计算出升降舵输出
c语言·单片机·算法·架构·无人机
白鱼不小白11 小时前
stm32 USART串口协议与外设(程序)——江协教程踩坑经验分享
stm32·单片机·嵌入式硬件
S,D12 小时前
MCU引脚的漏电流、灌电流、拉电流区别是什么
驱动开发·stm32·单片机·嵌入式硬件·mcu·物联网·硬件工程
芯岭技术15 小时前
PY32F002A单片机 低成本控制器解决方案,提供多种封装
单片机·嵌入式硬件
youmdt15 小时前
Arduino IDE ESP8266连接0.96寸SSD1306 IIC单色屏显示北京时间
单片机·嵌入式硬件
嘿·嘘16 小时前
第七章 STM32内部FLASH读写
stm32·单片机·嵌入式硬件
Meraki.Zhang16 小时前
【STM32实践篇】:I2C驱动编写
stm32·单片机·iic·驱动·i2c