为什么80%的码农都做不了架构师?>>>
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复用的代码如下:
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);
主函数的编写就较为简单了,直接调用子函数输出:
int main(void)
{
TIM_Config();
TIM_PWM_Config();
while (1)
{}
}