首先了解输出比较的概念:输出比较(OC)的英文全称:Output Compare,通过设置计数器CNT和CCR(捕获/输出比较寄存器),来输出PWM波形。

STM32学习笔记_定时器输出比较(PWM)(3)_STM32学习笔记


PWM波形的一些参数:

1:频率 = 1 / Ts(周期)

2:占空比:指高电平的时间在整个高低电平周期中的时间占比,Ton / Ts

3:分辨率:指占空比变化的最低精度(最小幅度)

STM32学习笔记_定时器输出比较(PWM)(3)_STM32学习笔记_02


捕获/比较通道的输出部分:

CNT与CCR进行比较,比较值进入输出模式控制器,输出模式控制器有8种模式可以配置,配置后可以设置OC1REF的输出电平信号,REF输出电平信号在CC1P寄存器可以设置频率和翻转,然后配置输出使能电路完成PWM的输出。

STM32学习笔记_定时器输出比较(PWM)(3)_STM32学习笔记_03

STM32学习笔记_定时器输出比较(PWM)(3)_STM32 PWM_04


PMW的内部结构和参数设置:形成对周期、占空比、CCR、AAR的基本概念。

STM32学习笔记_定时器输出比较(PWM)(3)_STM32 呼吸灯_05

STM32学习笔记_定时器输出比较(PWM)(3)_STM32学习笔记_06


这个实验中,我们要有道SG90舵机和130直流电机,我们了解下舵机和直流电机模块的基本知识:

舵机输入信号周期位20ms,也就是50Hz的信号,在20ms周期内,占空比0.5ms舵机为-90°,占空比1ms舵机-45°,占空比1.5ms舵机0°。

STM32学习笔记_定时器输出比较(PWM)(3)_STM32 PWM_07


130直流电机:

直流电机需要配合驱动电路或者驱动芯片来工作,本次实验使用的是TB6612直流电机驱动芯片

STM32学习笔记_定时器输出比较(PWM)(3)_STM32 PWM_08

TB6612直流电机驱动芯片的硬件电路和驱动方法如下,通过设置IN1、IN2的电平状态,控制01、02脚的电平状态,达到正转、反转、制动的效果。

STM32学习笔记_定时器输出比较(PWM)(3)_STM32 PWM_09



PWM驱动LED呼吸灯的实验:

1.在PA0引脚接LED(正极接PA0,负极接地),在此引脚输出PWM实现呼吸灯的效果。

2.新建PWM的模块代码。

3.编写PWM的初始化代码:

①.RCC开启时钟,把TIM外设和GPIO外设的时钟打开

②.时钟源选择配置和时基单元配置。

③.配置输出比较单元,包括CCR(捕获/比较寄存器)的值,输出比较模式、极性选择、输出使能等参数。

④.配置PWM对应的GPIO,初始化为复用推挽输出的配置。

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

编写代码前,我们先熟悉TIM库函数中关于PWM对应的函数:

STM32学习笔记_定时器输出比较(PWM)(3)_STM32 呼吸灯_10

STM32学习笔记_定时器输出比较(PWM)(3)_STM32 呼吸灯_11

#include "stm32f10x.h"                  // Device header

/**
  * 函    数:PWM初始化
  * 参    数:无
  * 返 回 值:无
  */
void PWM_Init(void)
{
	/*开启时钟*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);			//开启TIM2的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);			//开启GPIOA的时钟
	
	/*GPIO重映射*/
//	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);			//开启AFIO的时钟,重映射必须先开启AFIO的时钟
//	GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);			//将TIM2的引脚部分重映射,具体的映射方案需查看参考手册
//	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);		//将JTAG引脚失能,作为普通GPIO引脚使用
	
	/*GPIO初始化*/		//将PA0引脚初始化为复用推挽输出
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //只有把GPIO设置成复用推挽输出,引脚的控制权才能交给片上外设定时器
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	/*配置时钟源*/
	TIM_InternalClockConfig(TIM2);		//选择TIM2为内部时钟,若不调用此函数,TIM默认也为内部时钟
	
	/*时基单元初始化*/
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;		//定义结构体变量
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;     //时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数器模式,选择向上计数
	TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;					//计数周期,即ARR的值
	TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1;				//预分频器,即PSC的值
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;            //重复计数器,高级定时器才会用到
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);             //将结构体变量交给TIM_TimeBaseInit,配置TIM2的时基单元
	
	/*输出比较初始化*/
	TIM_OCInitTypeDef TIM_OCInitStructure;							//定义结构体变量
	TIM_OCStructInit(&TIM_OCInitStructure);		//结构体初始化,若结构体没有完整赋值则执行此函数,给结构体成员都赋一个默认值
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;				//输出比较模式,选择PWM模式1
	TIM_OCInitStructure.TIM_Pulse = 0;								//初始的CCR值
	TIM_OCInitStructure.TIM_OCPolarity =TIM_OCPolarity_High;		//输出极性,选择为高,若选择极性为低,则输出高低电平取反
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;	//输出使能
	
	TIM_OC1Init(TIM2, &TIM_OCInitStructure);						//将结构体变量交给TIM_OC1Init,配置TIM2的输出比较通道1

	/*TIM使能*/
	TIM_Cmd(TIM2,ENABLE);			//使能TIM2,定时器开始运行
}

/**
  * 函    数:PWM设置CCR
  * 参    数:Compare 要写入的CCR的值,范围:0~100
  * 返 回 值:无
  * 注意事项:CCR和ARR共同决定占空比,此函数仅设置CCR的值,并不直接是占空比
  *           占空比Duty = CCR / (ARR + 1)
  */
void PWM_SetCompare1(uint16_t Compare)
{
	TIM_SetCompare1(TIM2, Compare);		//设置CCR1的值
}


TIM外设引脚和GPIO引脚的复用关系:

STM32学习笔记_定时器输出比较(PWM)(3)_STM32学习笔记_12


只有把GPIO设置成复用推挽输出,引脚的控制权才能交给片上外设定时器:

STM32学习笔记_定时器输出比较(PWM)(3)_STM32 呼吸灯_13


TIM_SetCompare1函数设置写入CCR的值,注意CCR和ARR共同决定占空比,此函数仅设置CCR的值,并不直接是占空比:

STM32学习笔记_定时器输出比较(PWM)(3)_STM32学习笔记_14


接着在主函数编写代码,调用PWM,实现呼吸灯的效果:

#include "stm32f10x.h"    //添加头文件
#include "Delay.h"
#include "LED.h"
#include "KEY.h"
#include "Timer.h"
#include "OLED.h"
#include "PWM.h"

uint8_t i;

int main(void)
{
	/*模块初始化*/
	OLED_Init();		//OLED初始化
	PWM_Init();		//PWM初始化
	
	OLED_ShowString(1, 1, "PWM:");		//OLED显示字符
	
	while(1)
	{	
		for(i = 0; i < 100; i++)
		{
			PWM_SetCompare1(i);			//依次将定时器的CCR寄存器设置为0~100,PWM占空比逐渐增大,LED逐渐变亮
			OLED_ShowNum(2, 1, i, 3);
			Delay_ms(10);
		}
		for(i = 100; i > 0; i--)
		{
			PWM_SetCompare1(i);			//依次将定时器的CCR寄存器设置为100~0,PWM占空比逐渐减小,LED逐渐变暗
			OLED_ShowNum(2, 1, i, 3);
			Delay_ms(10);
		}
	}
}



接下来介绍引脚重映射的方法:

①.查看引脚配置表可知引脚复用关系:

STM32学习笔记_定时器输出比较(PWM)(3)_STM32 呼吸灯_15

②.接下来开启AFIO复用IO口的时钟,AFIO是APB2的外设:

STM32学习笔记_定时器输出比较(PWM)(3)_STM32 PWM_16

③.接下来配置引脚重映射函数:

STM32学习笔记_定时器输出比较(PWM)(3)_STM32学习笔记_17

引脚重映射的第一个参数根据手册的指导来填写,选择部分重映射1就是将TIM2的部分引脚进行重映射;第2个参数填写ENABLE;

GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);

STM32学习笔记_定时器输出比较(PWM)(3)_STM32学习笔记_18

STM32学习笔记_定时器输出比较(PWM)(3)_STM32 呼吸灯_19

④.使用重映射将PA15的JTAG复用解除掉:PA15才可以作为一个普通的IO脚使用:

STM32学习笔记_定时器输出比较(PWM)(3)_STM32学习笔记_20

简单来说,重映射复用IO口需要2步:

1.开启AFIO复用IO口的时钟,

2.配置引脚重映射函数.

如果此IO口用作了其他功能,那么还需要使用重映射函数先解除该引脚的原复用功能。

以上就是定时器输出PWM的笔记。