脉冲宽度调制PWM(Pulse Width Modulation)的原理示意图如下:
图中,假定定时器工作在向上计数PWM2模式,定时器从0开始计数到ARR,t时刻比较计数值CNT和CCRx,当CNT<CCRx时,IO输出低;CNT>=CCRx时,IO出高。当CNT=ARR,重新计数。
显然:改变CCRx可以改变占空比,改变ARR可以改变输出频率。
STM32F103 的定时器除了 TIM6 和 7。其他的定时器都可以用来产生 PWM 输出。其中高级定时器 TIM1 和 TIM8 可以同时产生多达 7 路的 PWM 输出,高级定时器尚未研究,以后再议。用定时器也能同时产生多达 4路的 PWM 输出。
首先大致了解PWM相关的三个寄存器:捕获/比较模式寄存器(TIMx_CCMR1/2)、捕获/比较使能寄存器(TIMx_CCER)、捕获/比较寄存器(TIMx_CCR1~4)。
1.捕获/比较模式寄存器(TIMx_CCMR1/2):
该寄存器总共有 2 个, TIMx _CCMR1和 TIMx _CCMR2。 TIMx_CCMR1 控制 CH1 和 2,而 TIMx_CCMR2 控制 CH3 和 4。
通道可用于输入(捕获模式)或输出(比较模式),通道的方向由相应的CCxS定义。
该寄存器其它位的作用在输入和输出模式下不同:
OCxx描述了通道在输出模式下的功能,ICxx描述了通道在输出模式下的功能。
当OC1M为:
110:PWM模式1。
111: PWM模式2。
两个模式输出电平极性相反。
2.捕获/比较使能寄存器(TIMx_CCER)
CC1E:输入/捕获1输出使能 (Capture/Compare 1 output enable) CC1通道配置为输出: 0: 关闭- OC1禁止输出。 1: 开启- OC1信号输出到对应的输出引脚。 CC1通道配置为输入: 该位决定了计数器的值是否能捕获入TIMx_CCR1寄存器。 0:捕获禁止; 1:捕获使能。 |
CCxE中的x对应通道定时器的4个通道CH1~4。
3.捕获/比较寄存器(TIMx_CCR1~4)
该寄存器总共有 4 个,对应 4 个通道 CH1~4。
CCR1[15:0]: 捕获/比较1的值 (Capture/Compare 1 value)
|
再看代码中的配置:
初始化PWM相关的结构体:
typedef struct
{
uint16_t TIM_OCMode; //选择定时器模式,输出PWM选择PWM1/2
写捕获/比较模式寄存器(TIMx_CCMR1/2)中的OC1M。
PWM1:在向上计数时,一旦TIMx_CNT<TIMx_CCR1时通道1为有效电平,否则为无效电平;在向下计数时,一旦TIMx_CNT>TIMx_CCR1时为无效电平(OC1REF=0),否则为有效电平(OC1REF=1)。
PWM2:在向上计数时,一旦TIMx_CNT<TIMx_CCR1时通道1为无效电平,否则为有效电平(如文章开头图片);在向下计数时,一TIMx_CNT>TIMx_CCR1时为有效电平,否则为无效电平。
所以向上向下计数是什么?
向上计数:计数器从0计到自动加载值(ARR)。TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
uint16_t TIM_OutputState; //比较输出使能,即使能PWM输出到端口
uint16_t TIM_OutputNState; /*用于TIM1 and TIM8. */
uint16_t TIM_Pulse; /*设置占空比,也就是设置CCR*/
库函数代码中可使用 TIM_SetComparen(TIMx,num) 来设置和修改占空比,n可为1~4,代表1~4个输出通道,num为数字
uint16_t TIM_OCPolarity; /*设置极性高低 */
设置比较输出的有效电平,要与计数器自动装载值、PWM模式、向上向下计数的配置相配合。举个栗子:
TIM_TimeBaseStructure.TIM_Period=899; //计数器自动装载值
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//PWM模式1
TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High; //PWM输出有效电平为高
TIM_OCInitStructure.TIM_Pulse=300; //PWM输出的占空比(CCR)
则计数0-300为高,301-899为低。理论示意图可对应文章开头图片。主要就是设置这几项来获取想要的PWM波形。
uint16_t TIM_OCNPolarity; /*用于TIM1 and TIM8. */
uint16_t TIM_OCIdleState; /*用于TIM1 and TIM8. */
uint16_t TIM_OCNIdleState; /*用于TIM1 and TIM8. */
} TIM_OCInitTypeDef;
以上大致解释了代码中的配置的意思,那么每句配置操作了最开始提到的三个寄存器中的哪个呢?举个栗子:
使用STM32F103VET6里的定时器3通道2来输出PWM的初始化代码库函数版本:
void TIM3_PWM_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //定时器3时钟打开
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; //TIM3_CH2
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_TimeBaseStructure.TIM_Period=899; //自动重装载值(ARR)
TIM_TimeBaseStructure.TIM_Prescaler =0; //预分频值
TIM_TimeBaseStructure.TIM_ClockDivision=0; //时钟分割
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure); //
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //捕获/比较模式寄存器(TIMx_CCMR1)中的OC1M为110
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能:捕获/比较使能寄存器(TIMx_CCER)中的CC2E为1
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //低电平为有效电平:捕获/比较使能寄存器(TIMx_CCER)中的CC2P为1
TIM_OC2Init(TIM3, &TIM_OCInitStructure); //OCn中的n对应4个通道
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); //OCn中的n对应4个通道
TIM_Cmd(TIM3, ENABLE); //
}
可使用TIM_SetCompare2(TIM3,num) 来设置和修改占空比,也可设置TIM_Pulse。
PWM的知识暂且整理到这里,供自己复习梳理之用,待有新的收获再更新。
参考到的文章有:原子的stm32开发指南,stm32中文参考手册和网络上的一些博文。
1