分享

【青风带你学stm32f051系列教程】第8课 定时器PWM输出 | 爱板网

 weikong66 2013-03-28
【青风带你学stm32f051系列教程】第8课 定时器PWM输出
2013年01月07日 ? 教程 ? 评论数 1 ? 被围观 749+

Time定时器可以用于输出PWM波,通过定时器的比较模式,设定预装载值,可以设计输出不同频率的PWM波。并且我们设置不同的翻转量,则可以设置不同的PWM占空比,这些运用在驱动电机或者一些相关运用中非常有用。本实验我们采用TIM1来产生四路频率相同的,但是占空比不同的PWM波。 下面将从软硬件入手,分析如何通过STM32F0的定时器输出PWM波。首先是硬件方面:

硬件准备:

保证输出IO端口如下就可以:

- TIM1_CH1 pin (PA.08)

- TIM1_CH2 pin (PA.09)

- TIM1_CH3 pin (PA.10)

- TIM1_CH4 pin (PA.11)

软件准备:

打开keil编译环境,设置系统工程树如图所示:

如上图所示,在lib库函数调用了stm32f0xx.tim.c函数库,我们在驱动函数time.c中编写定时器输出的相关参量设置。

配置PWM波形输出的设置我们分成两个部分完成:

第一步:首先是输出管脚的IO口设置,PWM输出,自然会采用到IO口作为输出端口,在STM32F0系列中,IO端口可以复用为TIM定时器输出通道,如下表所示:

我们采用了PA0,,PA1,PA2,PA3的复用功能AF2做为TIM定时器的4路输出通道。那么配置IO复用的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void TIM_Config(void)
 {
 GPIO_InitTypeDef GPIO_InitStructure;
  
 /* 使能GPIO时钟 */
 RCC_AHBPeriphClockCmd( RCC_AHBPeriph_GPIOA, ENABLE);
  
 /* 配置GPIO管脚参数设置*/
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 |GPIO_Pin_11;
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP ;
 GPIO_Init(GPIOA, &GPIO_InitStructure);
  
 GPIO_PinAFConfig(GPIOA, GPIO_PinSource8, GPIO_AF_2); /* GPIO管脚复用设置*/
 GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_2);
 GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_2);
 GPIO_PinAFConfig(GPIOA, GPIO_PinSource11, GPIO_AF_2);
 }

完成了这一步,也就打开了PWM输出的通道。

第二步:设置定时器的参数,配置出频率为17.57 KHz的PWM波,并且输出四路的占空比分别为:50%,37.5%,25%,12.5%.下面来讨论下如何设置:

首先考虑time定时器的时钟频率。如果我们设置分频数为0,也就是说time定时器等于系统时钟,system_stm32f0xx.c中已经把系统频率设置在48MHZ,在startup_stm32f0xx.s中,首先运行了systemInit函数,因此可以确定time定时器运行在48MHZ。

定时器产生的PWM的频率可以按照下面的公式进行计算:

预定标的值TIM1_Period = (time定时器频率 / pwm的频率) - 1

预定标的值实际上就是定时器运行多少次算一个PWM周期,这个在设置pwm频率中重要的参数。这个参数在结构体中设置。在程序中调用如下:

1
2
TIM_TimeBaseStructure.TIM_Period = TimerPeriod;

这个TIM_TimeBaseStructure是定时器的基础设置参数,在stm32f0xx_tim.h中通过结构体给出:

1
2
3
4
5
6
7
8
9
typedef struct
 {
 uint16_t TIM_Prescaler; /*!指定用来划分TIM时钟预分频值*/
 uint16_t TIM_CounterMode; /*!指定的计数器模式*/
 uint32_t TIM_Period; /*设置时钟周期 */
 uint16_t TIM_ClockDivision; /*设定时钟分频 */
 uint8_t TIM_RepetitionCounter; /*指定重复计数器值 */
 } TIM_TimeBaseInitTypeDef;

上面的结构体参数是设置TIME的基础参数,但是输出PWM波的占空比我们采用了比较捕获模式,设置在什么情况下发生跳转,还需要使用结构体TIM_OCInitTypeDef ,如下面所示:

1
2
3
4
5
6
7
8
9
10
11
12
typedef struct
 {
 uint16_t TIM_OCMode; /*!指定的TIM模式 */
 uint16_t TIM_OutputState; /*指定的TIM输出比较状态 */
 uint16_t TIM_OutputNState; /*指定TIM互补的输出比较状态. */
 uint32_t TIM_Pulse; /*指定的脉冲值被装入到捕获比较寄存器*/
 uint16_t TIM_OCPolarity; /*指定的脉冲值被装入到捕捉比较寄存器 */
 uint16_t TIM_OCNPolarity; /*指定的互补输出极性 */
 uint16_t TIM_OCIdleState; /*指定在空闲状态下的TIM输出比较引脚的状态 */
 uint16_t TIM_OCNIdleState; /*指定在空闲状态下的互补TIM输出比较引脚的状态. */
 } TIM_OCInitTypeDef;

其中 TIM_Pulse装载比较寄存器,判断什么时候发生PWM翻转 :

1
2
TIM_OCInitStructure.TIM_Pulse = Channel1Pulse;

Channel1Pulse的值可以按照下面的公式进行计算:

ChannelxPulse = DutyCycle * (TIM1_Period - 1) / 100

其中DutyCycle/100为占空比的值,TIM1_Period 就是我们前面定义的预定标的值。那么四路PWM的装载值可以设置为:

1
2
3
4
5
6
7
8
9
10
11
/*计算预定标 的值,也就是多少个时钟计数为一个周期*/
TimerPeriod = (SystemCoreClock / 17570 ) - 1;
/*计算CCR1 跳转值 在占空比为50%时*/
Channel1Pulse = (uint16_t) (((uint32_t) 5 * (TimerPeriod - 1)) / 10);
*计算CCR2 跳转值 在占空比为37.5%时*/
Channel2Pulse = (uint16_t) (((uint32_t) 375 * (TimerPeriod - 1)) / 1000);
/*计算CCR3 跳转值 在占空比为25%时*/
Channel3Pulse = (uint16_t) (((uint32_t) 25 * (TimerPeriod - 1)) / 100);
*计算CCR4跳转值 在占空比为12.5%时*/
Channel4Pulse = (uint16_t) (((uint32_t) 125 * (TimerPeriod- 1)) / 1000);

各个数值指标设置好后,我们就按照结构体定义的参数来配置PWM的参数,整体的设置函数如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/* TIM1 时钟使能 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1 , ENABLE);
/* Time 定时基础设置*/
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; /* Time 定时设置为上升沿计算模式*/
TIM_TimeBaseStructure.TIM_Period = TimerPeriod;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
  
/* 频道1,2,3,4的PWM 模式设置 */
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;
TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Reset;
  
TIM_OCInitStructure.TIM_Pulse = Channel1Pulse;//使能频道1配置
TIM_OC1Init(TIM1, &TIM_OCInitStructure);
TIM_OCInitStructure.TIM_Pulse = Channel2Pulse;//使能频道2配置
TIM_OC2Init(TIM1, &TIM_OCInitStructure);
  
TIM_OCInitStructure.TIM_Pulse = Channel3Pulse;//使能频道3配置
TIM_OC3Init(TIM1, &TIM_OCInitStructure);
  
TIM_OCInitStructure.TIM_Pulse = Channel4Pulse;//使能频道4配置
TIM_OC4Init(TIM1, &TIM_OCInitStructure);
  
/* TIM1 计算器使能*/
TIM_Cmd(TIM1, ENABLE);
  
/* TIM1 主输出使能 */
TIM_CtrlPWMOutputs(TIM1, ENABLE);

主函数的编写就较为简单了,直接调用子函数输出:

1
2
3
4
5
6
7
8
</pre>
int main(void)
{
 TIM_Config();
 TIM_PWM_Config();
 while (1)
 {}
}

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约