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编译环境,设置系统工程树如图所示:
<a href="http://www.eeboard.com/wp-content/uploads/2013/01/image001.png" class="cboxElement" rel="example4" 24235"="" style="text-decoration: none; color: rgb(1, 150, 227);">
如上图所示,在lib库函数调用了stm32f0xx.tim.c函数库,我们在驱动函数time.c中编写定时器输出的相关参量设置。
配置PWM波形输出的设置我们分成两个部分完成:
第一步:首先是输出管脚的IO口设置,PWM输出,自然会采用到IO口作为输出端口,在STM32F0系列中,IO端口可以复用为TIM定时器输出通道,如下表所示:
<a href="http://www.eeboard.com/wp-content/uploads/2013/01/image002.png" class="cboxElement" rel="example4" 24235"="" style="text-decoration: none; color: rgb(1, 150, 227);">
我们采用了PA0,,PA1,PA2,PA3的复用功能AF2做为TIM定时器的4路输出通道。那么配置IO复用的代码如下:
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频率中重要的参数。这个参数在结构体中设置。在程序中调用如下:
TIM_TimeBaseStructure.TIM_Period = TimerPeriod;
这个TIM_TimeBaseStructure是定时器的基础设置参数,在stm32f0xx_tim.h中通过结构体给出:
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 ,如下面所示:
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翻转 :
TIM_OCInitStructure.TIM_Pulse = Channel1Pulse;
Channel1Pulse的值可以按照下面的公式进行计算:
ChannelxPulse = DutyCycle * (TIM1_Period - 1) / 100
其中DutyCycle/100为占空比的值,TIM1_Period 就是我们前面定义的预定标的值。那么四路PWM的装载值可以设置为:
/*计算预定标 的值,也就是多少个时钟计数为一个周期*/ 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的参数,整体的设置函数如下所示:
/* 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);
主函数的编写就较为简单了,直接调用子函数输出:
</pre> int main(void) { TIM_Config(); TIM_PWM_Config(); while (1) {} }