STM32入门之定时器输出比较部分

        OC输出比较功能,可以通过比较CNT于CCR的值来实现对电平的翻转,置1,置0功能,用于输出一定频率和占空比的PWM波形。

        在这里需要注意的是,这部分功能只有高级定时器和通用定时器具有在输出比较功能,分别有四个通道可以用。

        PWM脉冲宽度调制可以输出一个介于高低电平之间的电压值,我们都知道一般单片机输出电平不是高电平就是低电平,假设我们想要调节LED的亮度呢?或者想要调节电机的速度呢?这里我们就可以利用PWM波来实现模拟电压输出。但是PWM波调节仅仅针对具有惯性力的系统,我们熄灭LED,它并不会立马就灭,而是有一个时间间隔。形容PWM波有三个重要的参数,频率,占空比,分辨率。频率就是周期(由一段高电平到低电平组成)的倒数,占空比就是高电平所占整个周期的比例,分辨率即占空比的变化步距。

a353509579c949aeaa8390ca39f76d57.jpeg

        由上图可以看出,占空比越大,等效出来的电压也就越大,通过调节占空比,可以实现调节亮度/速度。

ac596f564d364abe8e1ea2eac3a5b059.jpeg

        通过以上框图可知,当CNT大于或者等于CCR时,输出模式控制器会输出一个参考电压OCref(0或者1),这里的电压型信号可以分两路,一路是把REF映射到主模式的TRGO上,一路是通过极性选择(0或1)选择是否翻转电平,最后使能一下电路就可以在OC1通道上输出一个PWM波型了。

        那么如何确定OCref到底是置1还是置0还是翻转呢?主要还是看输出模式控制器是如何工作的。一共可以配置为8种模式。

  1. 冻结(CNT=CCR,REF保持为原状态)
  2. 匹配时置有效电平(CNT=CCR,REF置有效/高电平)
  3. 匹配时置无效电平(CNT=CCR,REF置无效/低电平)
  4. 匹配时电平翻转(CNT=CCR,REF电平翻转),实际上也就是一个占空比为50%的信号
  5. 强制为无效电平(CNT与CCR均无效,REF强制为无效/低电平)
  6. 强制为有效电平(CNT与CCR均无效,REF强制为有效/高电平)
  7. PWM模式
  • 当CNT向上计数时,CNT<CCR时,REF置有效电平,CNT>=CCR时,REF置无效电平。
  • 当CNT向下计数时,CNT<=CCR时,REF置有效电平,CNT>CCR时,REF置无效电平。
  1. PWM模式2
  • 当CNT向上计数时,CNT<CCR时,REF置无效电平,CNT>=CCR时,REF置有效电平。

    当CNT向下计数时,CNT<=CCR时,REF置无效电平,CNT>CCR时,REF置有效电平。

 

        我们利用IO口输出PWM波,就是利用PWM模式1与模式2。我们以PWM模式1为例,主要分析这种模式是怎么输出频率和占空比均可调的PWM波形的。

        在右上角部分,黄色线是ARR的值,蓝色线是CNT的值,红色线是CCR的值,当CNT小于CCR时,为高电平,当CNT大于CCR时,为低电平,通过设置ARR与CCR的值,可以调节PWM的频率以及占空比。

18985ebfc94b4e36a9ef6c47a261e693.jpeg

        经过时钟源选择,时基单元初始化后,就接到了CCR比较寄存器,这里不用通向NVIC中断了,因为PWM波不需要中断也可以输出。通过输出模式控制器来配置OCref的参考电压,再选择一下极性,使能一下输出,就打通了这条输出通道。


        接下来我们就用PWM波形来实现呼吸灯的控制吧,这里我们可以选择高电平驱动LED更为直观,因为占空比就是LED的亮度,我们把LED接在PA0口,也就是TIM2的通道1,这里可以通过查询引脚定义表得知。

00e7945bc6e049b7b38d26db79201b14.jpeg

  1. 首先还是老规矩,先开启GPIOA与定时器的时钟,先选择时钟源,然后初始化GPIO与时基单元,输出比较单元。初始化GPIO时要注意,引脚的模式要配置为复用推挽输出(AF_PP),因为对于普通的开漏/推挽输出,引脚的控制器是来自于输出数据寄存器的,想用片上外设(例如定时器)来控制输出引脚,就要用复用开漏/推挽输出模式。
  2. 初始化输出比较单元这里我们使用通道1,也就是PA0口,函数用TIM_OC1Init(TIM2,&TIM_OCInitStructure);这里输出比较单元的成员参数有许多,我们可以使用TIM_OCStructInit(TIM_OCInitStructure)将所有参数都初始化一遍,再修改我们需要用到的参数即可,首先就是输出比较的模式,我们设置为PWM模式1,其次是输出比较的极性选择,然后配置一下输出使能,设置一下CCR的值。

        最后使能计数器,就可以利用定时器输出PWM波了。

133060edaf4941d39971187781d827e9.jpeg

        如果我们要利用定时器输出一个频率为1KHZ,占空比为50%,分辨率为1%的pwm波,就可以根据公式算一下,设置PSC+1=720,ARR+1=100,CCR=50即可。

        为了更加直观地设置占空比,我们还可以利用一个函数来封装一下修改CCR的值,修改CCR的值我们可以利用TIM_SetCompare1(TIM2,Compare1)函数来实现那么初始化部分的代码如下。

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_0;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	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_OCInitStructure;
	TIM_OCStructInit(&TIM_OCInitStructure);
	TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;/*PWM模式1*/
	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 Compare1)
{
	TIM_SetCompare1(TIM2,Compare1);
}

        在主函数中,我们可以利用按键控制修改LED的亮灭,本质上就是通过修改CCR的值来实现输出不同占空比的PWM波来实现对亮度的调节的。

int main(void)
{
	
	OLED_Init();
	PWM_Init();
	key_Init();
	OLED_ShowString(1, 1, "light:");
	OLED_ShowString(1, 10, "%");
	
	while(1)
	{
		KEYNUM=key_GetNum();/*获取按键的键码*/
	 
		if(KEYNUM==1)/*按键1调高亮度*/
		{
			light+=20;
			if(light>100)
			{
				light=0;
			}
			PWM_SetCompare1(light);
			
		}
		if(KEYNUM==2)/*按键2调低亮度*/
		{
			light-=20;
			if(light<0)
			{
				light=100;
			}
			PWM_SetCompare1(light);
			
		}
		  OLED_ShowNum(1, 7, light,3);/*不断刷新light值*/
	}
}

        其实很简单的对吧,这里我们还可以顺带学习一下之前介绍的AFIO的引脚重映射功能具体是怎么使用的吧。引脚重映射,顾名思义就是把某个引脚的功能赋予到另外的引脚上。在什么时候会用到这个功能呢?就是当我们所需要的功能恰好都被开发员安排在同一个引脚上,比如根据引脚定义表我们可得知,一个引脚是会被赋予多个功能通道1的,我们可以将某个引脚的功能重映射到其他引脚上,但这也不是随便映射的,要根据引脚定义表,只有映射到某些特定的引脚,而且并不是所有的功能都可以重映射的,只有引脚定义表里面查询得到的,才可以映射。例如像PA0引脚的TIM2_CH1功能,就可以映射到PA15,但是这里要注意,PA15是一个调试端口,我们先要解除PA15的调试功能才可以用重映射。

ed94ada4076448bfb9a0196fdccd1f0c.jpeg

        那么假设这里我们要将PA0的CH1通道进行重映射,就可以先开启AFIO的时钟,然后再选择一下重映射的模式。以TIM2的重映射来举例,这里有四种模式

  1. 没有重映射(引脚功能未发生改变,相当于未使用重映射功能)
  2. 部分重映射1PartialRemap1(CH1_ETR映射到PA15,CH2映射到PB3,CH3与CH4未发生改变)
  3. 部分重映射2PartialRemap2(CH3映射到PB10,CH4映射到PB11,CH1_ETR与CH2未发生改变)
  4. 完全重映射FullRemap_TIM2(CH1_ETR映射到PA15,CH2映射到PB3,CH3映射到PB10,CH4映射到PB11)

        这里选择部分重映射1,把PA0口TIM2_CH1映射到PA15后,然后关闭PA15的调试功能,利用GPIO_PinRemapConfig();这里关于解除调试端口的参数一共有三个,SWJ指的就是SWD与JTAG这两种调试模式,将参数设置为GPIO_Remap_SWJ_JTAGODisable即可。

  1. GPIO_Remap_SWJ_NOJTRST(解除JTRST,对应为PB4)
  2. GPIO_Remap_SWJ_JTAGODisable(解除JTAGO调试端口的使用,对应为PA15——JTDI,PB3——JTDO,PB4——NJTRST)
  3. GPIO_Remap_SWJ_Disable(把SWD与JTAG的调试端口全部解除)
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
	GPIO_PinRemapConfig( GPIO_PartialRemap1_TIM2, ENABLE);
	GPIO_PinRemapConfig( GPIO_Remap_SWJ_JTAGDisable, ENABLE);

        接下来就是驱动舵机转轴的部分了。

        首先来介绍一下舵机,舵机是一种根据PWM信号占空比来控制输出角度的装置,根据舵机型号的不同,驱动它转动角度所需要的占空比也不同,例如SG90型号的舵机,需要我们输入周期为20ms(50HZ),占空比为2.5-12.5%的PWM波才可驱动舵机转轴,当占空比是2.5%时,舵机输出角度就是-90°,当占空比是5%时,舵机输出角度就是-45°,当占空比是7.5%时,舵机输出角度就是0°......以此类推舵机有三个输入线,一根接电源,一跟接地,一根是信号线输入PWM波。

fe69a44cbcda44a5bf66f088b00f0ed6.jpeg

        因为舵机属于大功率器件,要用5V才可以驱动,所以这里就利用单片机上的5V引脚给舵机供电即可。

        这里我们只需要更改一下PSC,ARR,CCR这些参数,就可以输出标准对应占空比的PWM波,因为驱动SG90型号的舵机需要50HZ,所以根据公式设置PSC+1为72,ARR+1为20K,CCR分别设置为500,1000,1500,2000,2500对应不同的占空比。这里原理讲清楚了,代码部分与LED是类似的,不再赘述。


        接下来介绍一下直流电机。

27df60710af84db8b60b513967a3900d.jpeg

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

        电机也是属于大功率器件,需要搭配驱动电路来驱动。通过舵机拆解图可知,舵机内部其实也是一个直流电机,区别就在于舵机内部集成了一个控制电路,控制电路已经将驱动模块包含在里面了,所以舵机不需要我们外部再使用驱动电路了。

        我们驱动电机有许多芯片可以选择,像TB6612就是一款双路H桥型的直流电机驱动芯片,可以驱动两个直流电机并控制转速与方向。

bfa6d302b7c4463a9f967416f88f8376.jpeg

        上图是TB6612芯片的引脚接线示意图,VM是驱动电压输入端,就接5V电源,这里电源注意与单片机共地,VCC接逻辑电平3.3V,与单片机保持一致即可,GND内部是连通的,只用接一个引脚即可。PWMA/B就接我们输入的PWM信号,AIN1/BIN1与AIN2/BIN2分别置高低电平,这里具体要看左下角的使用手册。AO1与AO2接电机1,BO1与BO2接电机2。STBY是待机接口,需要使用就接VCC,芯片进入工作模式。不需要就接GND,进入待机模式。如果需要使用待机模式的话,可以接一个GPIO口,置高低电平就可以配置是否待机了。

        那我们仔细学习一下使用手册,当我们PWM波为低电平时,电机是处于制动模式,也就是不转动,还有一种情况电机也是不转动的,就是两个输入口(IN1与IN2)都是高电平或者都是低电平时,电机都是不转动的。只有当PWM波为高电平,且IN口1高1低时,才会转动。

  1. PWM为高电平,IN1为低电平,IN2为高电平。(反转)
  2. PWM为高电平,IN1为高电平,IN2为低电平。(正转)

        当电机不断处于转动,停止,转动,停止时,那速率多大呢?就要看PWM占空比了,这里其实就是模拟了一个电压信号,通过改变这个模拟电压的大小,来实现调速的。

        同样的,这里我们把VM电源驱动电压接到单片机的5V引脚,然后IN1与IN2分别接到PA4,PA5。STBY接到VCC,GND接地,O1与O2接电机两极即可,不分正负极。

        这里需要改变的也就只有初始化模块,把PA4,PA5初始化一下即可。

        我们可以写一个设置电机速度的函数,速度变量就用带符号字符型,正数代表正转,负数代表反转。正转时,给PA4置高电平,PA5置低电平。反转时,给PA4置低电平,PA5置高电平。

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_SetBits(GPIOA,GPIO_Pin_5);
		GPIO_ResetBits(GPIOA,GPIO_Pin_4);
		PWM_SetCompare3(-speed);
	}
}

        最后在主函数中完善代码即可。

OLED_Init();
	Motor_Init();
	key_Init();
	OLED_ShowString(1,1,"Speed:");
	
	while(1)
	{
		KeyNum=key_GetNum();
		if(KeyNum==1)
		{
			Speed+=20;
			if(Speed>100)
			{
				Speed=-100;
			}
			Motor_SetSpeed(Speed);
			OLED_ShowSignedNum(1,7,Speed,3);
		}
		

图均源自网络

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值