您的位置:首页 > 编程语言

12.PWM—基于CT117E开发板的STM32库函数编程

2017-01-26 11:12 423 查看
1.PWM 简介STM32 的定时器除了 TIM6 和 7。其他的定时器都可以用来产生 PWM 输出。其中高级定时器 TIM1 和 TIM8 可以同时产生多达 7 路的 PWM 输出。而通用定时器也能同时产生多达4路的 PWM 输出,这样,STM32 最多可以同时产生 30 路 PWM 输出!要使 STM32 的高级定时器 TIM2产生 PWM 输出,除了上一章介绍的几个寄存器(ARR、PSC、CR1 等)外,我们还会用到 4 个寄存器(通用定时器则只需要 3 个) ,来控制 PWM 的输出。这四个寄存器分别是:捕获/比较模式寄存器(TIMx_CCMR1/2) 、捕获/比较使能寄存器(TIMx_CCER)、捕获/比较寄存器(TIMx_CCR1~4)以及刹车和死区寄存器(TIMx_BDTR) 。首先是捕获/比较模式寄存器(TIMx_CCMR1/2),该寄存器总共有 2 个,TIMx _CCMR1和 TIMx _CCMR2。TIMx_CCMR1 控制 CH1 和 2,而 TIMx_CCMR2 控制 CH3 和 4。该寄存器的各位描述如图:于该寄存器的详细说明,请参考《STM32 参考手册》第 240 页,13.4.7 一节。设置位 OCxM,此部分由 3 位组成。总共可以配置成 7 种模式,我们使用的是 PWM 模式,这 3 位必须设置为110/111。这两种PWM 模式的区别就是输出电平的极性相反。另外 CCxS 用于设置通道的方向(输入/输出)默认设置为 0,就是设置通道作为输出使用。接下来,我们介绍捕获/比较使能寄存器(TIMx_CCER),该寄存器控制着各个输入输出通道的开关。该寄存器的各位描述如图:该寄存器比较简单,我们这里只用到了 CC1E 位,该位是输入/捕获 1 输出使能位,要想PWM 从 IO 口输出,这个位必须设置为 1,所以我们需要设置该位为 1。该寄存器更详细的介绍了,请参考《STM32 参考手册》第 244 页,13.4.9 这一节。最后,我们介绍一下捕获/比较寄存器(TIMx_CCR1~4),该寄存器总共有 4 个,对应 4 个输通道 CH1~4。因为这 4 个寄存器都差不多,我们仅以 TIMx_CCR1 为例介绍,该寄存器的各位描述如图 :在输出模式下,该寄存器的值与 CNT 的值比较,根据比较结果产生相应动作。利用这点我们通过修改这个寄存器的值,就可以控制 PWM 的输出脉宽了。我们使用的是 TIM2的通道2和3,所以我们需要修改 TIM2_CCR2和TIM2_CCR3 以实现脉宽控制。如果是通用定时器,则配置以上三个寄存器就够了,但是如果是高级定时器,则还需要配置:刹车和死区寄存器(TIMx_BDTR),该寄存器各位描述如图 :该寄存器,我们只需要关注最高位:MOE 位,要想高级定时器的 PWM 正常输出,则必须设置 MOE 位为 1,否则不会有输出。注意:通用定时器不需要配置这个。其他位我们这里就不详细介绍了,请参考《STM32 参考手册》第 248 页,13.4.18 这一节。2.配置步骤1 ) 开启 TIM2 时钟,配置 PA1、2为复用输出。要使用 TIM2, 我们必须先开启 TIM2的时钟,这里我们还要配置 PA1、2 为复用输出(当然还要时能 PORTA 的时钟) ,这是因为 TIM2_CH2 通道将使用 PA1的复用功能作为输出。库函数使能 TIM2 时钟的方法是:
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //使能定时器 3 时钟
设置 PA1、2 为复用功能输出的方法在前面的几个实验都有类似的讲解, 相信大家很明白,这里简单列出 GPIO 初始化的一行代码即可:
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
2 )设置 TIM2的ARR 和 PSC 。在开启了 TIM1 的时钟之后, 我们要设置 ARR 和 PSC 两个寄存器的值来控制输出 PWM 的周期。当 PWM 周期太慢(低于 50Hz)的时候,我们就会明显感觉到闪烁了。因此,PWM 周期在这里不宜设置的太小。这在库函数是通过 TIM_TimeBaseInit 函数实现的,在上一节定时器中断章节我们已经有讲解,这里就不详细讲解,调用的格式为:
TIM_TimeBaseStructure.TIM_Period = arr; //设置自动重装载值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数模式
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); //根据指定的参数初始化 TIMx 的
3 )设置 TIM2_CH2、3的 PWM 模式 及通道方向, 使能 TIM2的 CH2、3输出 接下来,我们要设置 TIM2_CH2、3 为 PWM 模式(默认是冻结的) 要通过配置 TIM1_CCMR1 的相关位来控制 TIM2_CH2 、3的模式。在库函数中,PWM 通道设置是通过函数 TIM_OC1Init()~TIM_OC4Init()来设置的,不同的通道的设置函数不一样,这里我们使用的是通道2,所以使用的函数是TIM_OC2Init()。
void TIM_OC2Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
结构体 TIM_OCInitTypeDef的定义:
typedef struct
{
uint16_t TIM_OCMode;
uint16_t TIM_OutputState;
uint16_t TIM_OutputNState;
uint16_t TIM_Pulse;
uint16_t TIM_OCPolarity;
uint16_t TIM_OCNPolarity;
uint16_t TIM_OCIdleState;
uint16_t TIM_OCNIdleState;
} TIM_OCInitTypeDef;
我们要求相关的几个成员变量:参数 TIM_OCMode 设置模式是 PWM 还是输出比较,这里我们是 PWM 模式。参数 TIM_OutputState 用来设置比较输出使能,也就是使能 PWM 输出到端口。参数 TIM_OCPolarity 用来设置极性是高还是低。其他的参数 TIM_OutputNState,TIM_OCNPolarity,TIM_OCIdleState 和 TIM_OCNIdleState 是高级定时器 TIM1 和 TIM8 才用到的。要实现我们上面提到的场景,方法是:
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;//选择 PWM 模式 2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//输出极性高
TIM_OC1Init(TIM1, &TIM_OCInitStructure); //初始化 TIM1 OC1
4 ) 使能 TIM2。在完成以上设置了之后,我们需要使能 TIM2。使能 TIM2的方法前面已经讲解过:
TIM_Cmd(TIM2, ENABLE); //使能 TIM2
5 ) 设置 MOE  输出,使能 PWM  输出 。普通定时器在完成以上设置了之后,就可以输出 PWM 了,但是高级定时器,我们还需要使能刹车和死区寄存器(TIM2_BDTR)的 MOE 位,以使能整个 OCx(即 PWM)输出。库函数的设置函数为:
TIM_CtrlPWMOutputs(TIM2,ENABLE); // MOE 主输出使能
6 ) 修改 TIM1_CCR1  来控制占空比。最后,在经过以上设置之后,PWM 其实已经开始输出了,只是其占空比和频率都是固定的,而我们通过修改 TIM2_CCR1 则可以控制 CH2 的输出占空比。在库函数中,修改 TIM2_CCR2 占空比的函数是:
void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1)
理所当然,对于其他通道,分别有一个函数名字,函数格式为TIM_SetComparex(x=1,2,3,4)。通过以上 6 个步骤,我们就可以控制 TIM2 的 CH2、3 输出 PWM 波了。3.软件设计初始化函数如下:
void TIM_Config(uint16_t Channel2Pulse, uint16_t Channel3Pulse)
{
TIM_TimeBaseInitTypeDefTIM_TimeBaseStructure;
TIM_OCInitTypeDefTIM_OCInitStructure;
/*1.1使能 tim1 时钟*/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
/*2.定时器配置*/
TIM_TimeBaseStructure.TIM_Period = 999;//设置在下一个更新事件装入活动的自动重装载寄存器周期的值1K
TIM_TimeBaseStructure.TIM_Prescaler = 0;//设置用来作为 TIM2 时钟频率除数的预分频值 不分频
TIM_TimeBaseStructure.TIM_ClockDivision = 0;//设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;//向上计数
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);//初始化 TIMx
//TIM2预分频设置:1MHZ,APB1分频系数2,TIM2时钟为36MHzx2 = 72MHz 
TIM_PrescalerConfig(TIM2,71, TIM_PSCReloadMode_Immediate);
/*3.Channel 2 and 3 Configuration in PWM mode */
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;//脉宽调制模式 2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//比较输出使能
TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;//互补输出
比较输出使能
TIM_OCInitStructure.TIM_Pulse = Channel2Pulse; //设置待装入捕获比较寄存器的脉冲值
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;//TIM 输出比较极性低
TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;//TIM2 输出比较 N 极性低
TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;//当 MOE=0 设置 TIM2输出比较空闲状态
TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Reset;//当 MOE=0 重置 TIM1 输出比较N空闲状态
TIM_OC2Init(TIM2, &TIM_OCInitStructure);//4.初始化外设 TIM2
TIM_OCInitStructure.TIM_Pulse = Channel3Pulse;//设置了待装入捕获比较寄存器的脉冲值.它的取值必须在0x0000和 0xFFFF之间。
TIM_OC3Init(TIM2, &TIM_OCInitStructure);
//初始化外设 TIM2
TIM_Cmd(TIM2, ENABLE);//使能 TIM1
TIM_CtrlPWMOutputs(TIM2, ENABLE);//5.MOE 主输出使能
}
/*
1.2设置该引脚为复用输出功能,输出 TIM2 CH2 3 的 PWM 脉冲波形*/
void PWM_IO_Config(void)
{
GPIO_InitTypeDefGPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
在主函数中如下使用
TIM_Config(998/2,998*7/10);
补充:STM32定时器与端口对应关系
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  库函数 C语言 stm32