一、 相关概念
利用stm32的定时器模块可以实现方波的输出,该款单片机有两种模式可以实现这一功能,分别是输出比较(Output Compare,OC)模式及PWM模式。下图为输出比较部分的硬件结构图,其中捕获/比较寄存器(CCR,Capture/Compare Register)
-
输出比较
输出比较可以通过比较CNT和CCR值的关系,来对输出电平进行置1、置0或翻转的操作,用于输出一定频率的占空比的PWM波形。下图为输出比较模式下的工作原理,只有在CNT=CCR时输出电平极性才发生翻转,CNT=ARR时重置CNT,所以输出比较模式下ARR 决定输出频率,CCR决定每个通道的初始相位。
-
PWM(Pluse Width Modulation)脉冲宽度调制
在具有惯性的系统中,可以通过对一系列脉冲的宽度进行调制,来等效地获得所需要的模拟量,常用于电机控速等领域,也就是说,使用这个PWM波形,是用来等效地实现一个模拟信号的输出,也就是以一个很快的频率,给电机通电、断电。
常见参数:
频率=1/Ts
占空比=Ton/Ts
分辨率=占空比变化步距
下图为PWM模式下的工作原理,在时输出低电平, CCR≤CNT≤ARR极性发生翻转变为高电平,CNT=ARR时重置CNT。
PWM模式: ARR设置频率,CCR设置占空比,频率和占空比可以任意设置,起始相位不能设置。
输出比较模式:ARR设置频率,CCR设置相位,频率和起始相位可以任意设置,占空比不能设置。输出频率为理论计算值一半。
二、 基本结构
-
基本结构
如图为输出比较模式下的基本结构,左边是CNT计数器和CCR1 ,他俩进行比较,当CNT≥CCR1时就会给输出模式控制器传一个信号,然后输出模式控制器就会改变它输出OC1REF的高低电平, REF信号可以作为主模式控制器的输入,把这个REF映射到主模式的TRGO输出,但主要还是下面的极性选择部分,之后是输出使能电路控制输出信号的使能与失能,最后就是OC1引脚,这个引脚就是CH1的通道的引脚。
-
输出模式
模式 | 描述 |
---|---|
冻结 | CNT=CCR时,REF保持为原状态 |
匹配时置有效电平 | CNT=CCR时,REF置有效电平 |
匹配时置无效电平 | CNT=CCR时,REF置无效电平 |
匹配时电平翻转 | CNT=CCR时,REF电平翻转 |
强制为无效电平 | CNT与CCR无效,REF强制为无效电平 |
强制为有效电平 | CNT与CCR无效,REF强制为有效电平 |
PWM模式1向上计数 | CNT<CCR时,REF置有效电平,CNT≥CCR时,REF置无效电平 |
PWM模式1向下计数 | CNT>CCR时,REF置无效电平,CNT≤CCR时,REF置有效电平 |
PWM模式2向上计数 | CNT<CCR时,REF置无效电平,CNT≥CCR时,REF置有效电平 |
PWM模式2向下计数 | CNT>CCR时,REF置有效电平,CNT≤CCR时,REF置无效电平 |
冻结描述的是CNT=CCR时,REF保持原态,比如在输出PWM波,突然想暂停一会儿输出,就可以设置成这个模式,高低电平维持在暂停时刻;
匹配时值置有效电平,匹配时值置无效电平,匹配时值电平翻转,就是CNT=CCR时,REF分别置高电平,低电平,电平翻转,在匹配时值电平翻转模式下,可以产生一个占空比为50%的方波;
强制为无效电平和强制为有效电平两个模式和冻结模式相似,如果你想暂停波形输出,并且在暂停期间保持低电平或者高电平,那你就可以设置这两个模式;
PWM2就是PWM1的取反。
4. 参数计算
PWM频率: Freq = CK_PSC / (PSC + 1) / (ARR + 1)
PWM占空比: Duty = CCR / (ARR + 1)
PWM分辨率: Reso = 1 / (ARR + 1)
三、 代码分析
本文代码是对江科协的PWM呼吸灯程序的修改,使用了另外的通道对PWM的输出进行了控制。
- PWM.c
#include "stm32f10x.h" // Device header
void PWM_Init(void)
{
//第一个老朋友,外设时钟使能
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//第二个老朋友,设置外设接口,Pin0复用推挽输出,输出PWM波;Pin1下拉输入,用来控制PWM功能的有无
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);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_InternalClockConfig(TIM2);//TIM2参数表
//配置时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;//定义在定时器时钟(CK_INT)频率与数字滤波器(ETR,TIx)使用的采样频率之间的分频比例
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//向上计数
TIM_TimeBaseInitStructure.TIM_Period = 10 - 1; //ARR
TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1; //PSC 输出频率= 72MHz / (72 * 10)= 100KHz
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);//初始化TIM2
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; //设置CCR1初值,函数TIM_SetCompare1也是用来设置该寄存器的
TIM_OC1Init(TIM2, &TIM_OCInitStructure);//TIM2通道的输出比较初始化
TIM_Cmd(TIM2, DISABLE);//此处的第二个参数设置为DISABLE,是为了实现Pin1控制是否输出PWM波
}
void PWM_SetCompare1(uint16_t Compare)//设置占空比,取值在 0 ~ ARR+1 之间,占空比 = Compare / (ARR + 1)
{
TIM_SetCompare1(TIM2, Compare);
}
- Main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "PWM.h"
int main(void)
{
PWM_Init();
while (1)
{
if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1) == 1)
{
TIM_Cmd(TIM2, ENABLE);
PWM_SetCompare1(5);
}
else TIM_Cmd(TIM2, DISABLE);
}
}
四、 总结
本文对通用计时器的输出比较部分进行了总结,描述了基本原理和构成,并结合例程描述了PWM功能基本的使用方法。