首先熟悉一下定时器的PWM相关部分。看图最明白:
其实PWM就是定时器的一个比较功能而已。
CNT里的值不断++,一旦加到与CCRX寄存器值相等,那么就产生相应的动作。这点和AVR单片机很类似。既然这样,我们要产生需要的PWM信号,就需要设定PWM的频率和PWM的占空比。
首先说频率的确定。由于通用定时器的时钟来源是PCLK1,而我又喜欢用固件库的默认设置,那么定时器的时钟频率就这样来确定了,如下:
AHB(72MHz)→APB1分频器(默认2)→APB1时钟信号(36MHz)→倍频器(*2倍)→通用定时器时钟信号(72MHz)。
这里为什么是这样,在RCC模块学习记录里有详细记载,不多说。
因此图中的CK_PSC就是72MHz了。
下面的资料也是网上一搜一大把,我就罗列了:
STM32的PWM输出有两种模式,模式1(PWM1)和模式2(PWM2),由TIMx_CCMRx寄存器中的OCxM位确定的(“110”为模式1,“111”为模式2)。模式1和模式2的区别如下:
110:PWM模式1-在向上计数时,一旦TIMx_CNT=TIMx_CCR1时通道1为无效电平(OC1REF=0),否则为有效电平(OC1REF=1)。
111:PWM模式2-在向上计数时,一旦TIMx_CNT=TIMx_CCR1时通道1为有效电平,否则为无效电平。
由此看来,模式1和模式2正好互补,互为相反,所以在运用起来差别也并不太大。我用的是模式一,因此后面的设定都是按照模式一来设定的。
PWM的周期是就是由定时器的自动重装值和CNT计数频率决定的。而CNT的计数时钟是CK_PSC经分频器PSC得到,因此CNT的时钟就是CK_PSC/分频系数。这个分频系数在TIM_TimeBaseStructure.TIM_Prescaler确定。成都网站设计我设置的值是72,因此CNT的计数频率也就是CK_CNT的频率为1MHz。
下一步就是确定定时器自动重装值。因为CNT每自加到ARR寄存器的值时就会自动清零,当然前提是设定为为向上计数模式,而就是根据这个溢出事件来改变PWM的周期。所以PWM信号的频率由ARR的值来确定。我设置的值是1000-1,即TIM_TimeBaseStructure.TIM_Period = 1000-1;因此PWM的周期是1MHz/1000=1KHz。
接下来就要确定PWM的占空比了。因为CNT在自加到ARR值的过程中会不断和CRRX的值相比较,一旦二者相等就产生匹配事件,但要注意CNT不会理会这件事,它会继续++直到等于ARR。而CRRX的值我设定为400-1,那么占空比就随之确定为40%。
好了,下面就是库函数的配置了。
TIMER输出PWM实现步骤
1.设置RCC时钟;
2.设置GPIO;
3.设置TIMx定时器的相关寄存器;
4.设置TIMx定时器的PWM相关寄存器。
首先是main函数和全局变量申明,很简单,不作说明
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TimOCInitStructure;
int main(void)
{
rcc_cfg();
gpio_cfg();
tim2_cfg();
pwm_cfg();
//
while (1)
{
GPIO_WriteBit(GPIOA, GPIO_Pin_8, Bit_SET);
delay();
GPIO_WriteBit(GPIOA, GPIO_Pin_8, Bit_RESET);
delay();
}
}
下面是IO口的配置:
void gpio_cfg()
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
此处要注意的是PWM输出口要配置为复用推挽输出,原因我也不知道,反正照搬就是了。
下面是TIM配置函数,注释很清楚了,不作说明:
void tim2_cfg()
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
TIM_DeInit(TIM2);
TIM_InternalClockConfig(TIM2);
//预分频系数为72,这样计数器时钟为72MHz/72 = 1MHz
TIM_TimeBaseStructure.TIM_Prescaler = 72;
//设置时钟分割
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
//设置计数器模式为向上计数模式
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
//设置计数溢出大小,每计1000个数就产生一个更新事件
TIM_TimeBaseStructure.TIM_Period = 1000-1;
//将配置应用到TIM2中
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStructure);
//禁止ARR预装载缓冲器
TIM_ARRPreloadConfig(TIM2, DISABLE);
TIM_Cmd(TIM2, ENABLE);//使能TIMx外设
}
接下来是关键的PWM的配置函数:
void pwm_cfg()
{
//设置缺省值
TIM_OCStructInit(&TimOCInitStructure);
//PWM模式1输出
TimOCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
//设置占空比,占空比=(CCRx/ARR)*100%或(TIM_Pulse/TIM_Period)*100%
TimOCInitStructure.TIM_Pulse = 400-1;
//TIM输出比较极性高
TimOCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
//使能输出状态
TimOCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
//TIM2的CH2输出
TIM_OC2Init(TIM2, &TimOCInitStructure);
//设置TIM2的PWM输出为使能
TIM_CtrlPWMOutputs(TIM2,ENABLE);
}
stm32固件库的输出比较单元结构体与定时器的时基单元是分开定义的,而PWM模式只是输出比较结构体成员TimOCInitStructure.TIM_OCMode的一个取值,当把此结构体填充完后,还要映射到某个定时器,用TIM_OCXInit函数实现,我用了一个X,说明不止一个这样的函数,事实上,stm32的通用定时器都有四个通道,每个通道对应一个初始化函数,这里真够纠结的!最后还要使能该定时器的PWM输出功能,TIM_CtrlPWMOutputs(TIM2,ENABLE)函数要注意,是outputs而不是output,说明TIM2不止一个通道嘛!够复杂,够繁琐的!
下面是输出比较单元的结构体原型:
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;
其中没有加色的成员是高级定时器才有的,通用定时器就不用管了。
这里还有个TimOCInitStructure.TIM_OCPolarity成员需要注意,它有什么作用呢?在网上查的资料,如下图:
前面说到pwm有pwm1和pwm2两种模式,这两种模式只能控制到OCXREF为止,TIM_OCPolarity 能控制OC1是直接等于OCXREF,还是取反极性!OC1才是最终的PWM信号。
这里有个小插曲,我用示波器去测量PWM信号,发现信号居然是双极性的,然后改变TIM_OCPolarity ,再测,还是双极性,只是倒了个跟头。还真以为stm32单片机能输出两极性的PWM,后面把示波器改为直流档(之前用的是交流档),波形才从零电位一下纵向移上去。以后要注意!
上一篇:基于STM32的红外遥控重点解析
下一篇:基于ZigBee和STM32的室内智能照明系统的设计
推荐阅读最新更新时间:2024-11-09 08:37
设计资源 培训 开发板 精华推荐
- CY4605 EZ-USB HX2LP 高速低功耗 USB 2.0 兼容 4 端口集线器参考设计套件
- LT6656ACS6-1.25、1.25V 升压输出电流电压基准的典型应用
- USB一体化扬声器
- 电子CAD图3-1811571106
- #第六届立创电赛#TPA3116数字功放
- ADR443B 3 Vout 超低噪声、LDO XFET 电压基准的典型应用,具有电流吸收器和电流源
- USBHub
- 超小两路光敏电阻传感器模块(GL5616 & LM393)
- 使用 Analog Devices 的 LT1086CT-5 的参考设计
- 使用 ROHM Semiconductor 的 BD46382 的参考设计
- 下载有礼:2017年泰克亚太专家大讲堂第四期: 如何应对新型半导体材料表征测试挑战
- Microchip 喊你快来打造你的理想型单片机,智能门铃、百元京东卡等【80份】好礼等你赢!
- EEWORLD 示波器问卷有奖大调查
- 2024 DigiKey 应用说:树莓派 5 在视频直播中的应用
- 【评论有礼】大话CC2650,从资料到例程、从应用到生态全方位讲解!
- 购买TI store MSP432P401R LaunchPad 晒单就送礼!
- TI原装MSP430 LaunchPad千人大团购!
- 开启Altera SoC体验之旅,玩转Altera Cyclone V系列SoC
- 双重好礼等你来!2022年,让英飞凌更懂你!
- 阅读 TI工业应用方案(电器、电力传输)精彩专题,挑战你的记忆力!好礼相送