STM32定时器输出PWM波
1. STM32的定时器的结构
STM32的T1和T8是高级定时器,T2-T5是通用定时器,他们都可以输出PWM信号,用于驱动电机等负载。其中高级定时器的结构框图如图所示,其可以输出7路PWM信号;而通用定时器可以输出4个通道的PWM信号。

图1 高级定时器的PWM信号
每个定时器的通道通过STM32的GPIO向外输出PWM信号,其映射关系如下:

图2 定时器的通道和GPIO的映射关系
2. 实验设计
为了验证STM32输出PWM信号的使用方法,我们分别使用高级定时器T1和通用定时器T2的通道1,输出PWM信号。并通过按键改变所产生PWM信号的占空比。当按下按键时,示波器上的波形的占空比跟着变化。如图所示

图3 实验波形
3. 实验代码
基于ST公司的标准库配置芯片的寄存器,关键代码如下:
3.1 main函数代码
#include "OLED.h"
#include "Key.h"
#include "PWM.h"
#include "PWMt1.h"int duty =100;int main()
{
int duty_cur =0;//定义实时的占空比
OLED_Init();
PWM_Init();
Key_Init();
TIM1_PWM_Init();OLED_ShowString(1, 1, "Duty:00%");
while(1){duty_cur=Key_GetNum();//读取按键状态,获取占空比PWM_SetCompare1(duty_cur);//设置占空比OLED_ShowNum(1, 6, duty_cur, 2);//显示到OLED上}
}
3.2 按键代码
#include "stm32f10x.h" // Device header
#include "public.h"extern int duty;/*** 函 数:按键初始化* 参 数:无* 返 回 值:无*/
void Key_Init(void)
{GPIO_InitTypeDef GPIO_InitStructure;/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); //开启GPIOB的时钟/*GPIO初始化*/GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_13;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOC, &GPIO_InitStructure); //将PB1和PB11引脚初始化为上拉输入
}/*** 函 数:按键获取键码* 参 数:无* 返 回 值:按下按键的键码值,范围:0~2,返回0代表没有按键按下* 注意事项:此函数是阻塞式操作,当按键按住不放时,函数会卡住,直到按键松手*/
uint8_t Key_GetNum(void)
{if (GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_1) == 0) //读PB1输入寄存器的状态,如果为0,则代表按键1按下{Delay_ms(20); //延时消抖while (GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_1) == 0); //等待按键松手Delay_ms(20); //延时消抖duty=duty+10; //置键码为1if (duty>=90)duty=90;}if (GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_13) == 0) //读PB11输入寄存器的状态,如果为0,则代表按键2按下{Delay_ms(20); //延时消抖while (GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_13) == 0); //等待按键松手Delay_ms(20); //延时消抖duty=duty-10; //置键码为1if (duty<0)duty=0;}return duty; //返回键码值,如果没有按键按下,所有if都不成立,则键码为默认值0
}
3.3 定时器的配置方法
3.3.1 高级定时器的配置方法
#include "PWMt1.h"void TIM1_PWM_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_BDTRInitTypeDef TIM_BDTRStructure;// 1. 使能TIM1和GPIOA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1 | RCC_APB2Periph_GPIOA, ENABLE); // 2. 配置PA8为复用推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure); // 3. 配置时基单元
TIM_TimeBaseStructure.TIM_Period = 99; // ARR = 1000 - 1,PWM频率 = 72MHz / (72 * 1000) = 1kHz
TIM_TimeBaseStructure.TIM_Prescaler = 7199; // PSC = 7200 - 1,分频后计数器时钟为1MHz
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); // 4. 配置输出比较通道1
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; // PWM模式1
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; // 使能输出
TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Disable; // 互补输出不使能(因为只用通道1)
TIM_OCInitStructure.TIM_Pulse = 500; // CCR1 = 500,占空比50%
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; // 高电平有效
TIM_OC1Init(TIM1, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable); // 使能预装载 // 5. 配置刹车和死区(高级定时器需要)
TIM_BDTRStructure.TIM_OSSRState = TIM_OSSRState_Disable;
TIM_BDTRStructure.TIM_OSSIState = TIM_OSSIState_Disable;
TIM_BDTRStructure.TIM_DeadTime = 0; // 死区时间设为0
TIM_BDTRStructure.TIM_Break = TIM_Break_Disable;
TIM_BDTRStructure.TIM_BreakPolarity = TIM_BreakPolarity_High;
TIM_BDTRStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Disable;
TIM_BDTRConfig(TIM1, &TIM_BDTRStructure); // 6. 使能TIM1和主输出
TIM_Cmd(TIM1, ENABLE);
TIM_CtrlPWMOutputs(TIM1, ENABLE); // 必须使能MOE
}3.3.2 通用定时器的配置方法
#include "stm32f10x.h" // Device header/*** 函 数:PWM初始化* 参 数:无* 返 回 值:无*/
void PWM_Init(void)
{GPIO_InitTypeDef GPIO_InitStructure;TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; //定义结构体变量TIM_OCInitTypeDef TIM_OCInitStructure; //定义结构体变量/*开启时钟*/RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //开启TIM2的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟/*GPIO重映射*/
// RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //开启AFIO的时钟,重映射必须先开启AFIO的时钟
// GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE); //将TIM2的引脚部分重映射,具体的映射方案需查看参考手册
// GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE); //将JTAG引脚失能,作为普通GPIO引脚使用/*GPIO初始化*/GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //GPIO_Pin_15;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure); //将PA0引脚初始化为复用推挽输出 //受外设控制的引脚,均需要配置为复用模式 /*配置时钟源*/TIM_InternalClockConfig(TIM2); //选择TIM2为内部时钟,若不调用此函数,TIM默认也为内部时钟/*时基单元初始化*/TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数器模式,选择向上计数TIM_TimeBaseInitStructure.TIM_Period = 100 - 1; //计数周期,即ARR的值TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1; //预分频器,即PSC的值TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //重复计数器,高级定时器才会用到TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure); //将结构体变量交给TIM_TimeBaseInit,配置TIM2的时基单元/*输出比较初始化*/TIM_OCStructInit(&TIM_OCInitStructure); //结构体初始化,若结构体没有完整赋值//则最好执行此函数,给结构体所有成员都赋一个默认值//避免结构体初值不确定的问题TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //输出比较模式,选择PWM模式1TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性,选择为高,若选择极性为低,则输出高低电平取反TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //输出使能TIM_OCInitStructure.TIM_Pulse = 0; //初始的CCR值TIM_OC1Init(TIM2, &TIM_OCInitStructure); //将结构体变量交给TIM_OC1Init,配置TIM2的输出比较通道1/*TIM使能*/TIM_Cmd(TIM2, ENABLE); //使能TIM2,定时器开始运行
}/*** 函 数:PWM设置CCR* 参 数:Compare 要写入的CCR的值,范围:0~100* 返 回 值:无* 注意事项:CCR和ARR共同决定占空比,此函数仅设置CCR的值,并不直接是占空比* 占空比Duty = CCR / (ARR + 1)*/
void PWM_SetCompare1(uint16_t Compare)
{TIM_SetCompare1(TIM1, Compare); //设置CCR1的值
}/*** 函 数:PWM设置PSC* 参 数:Prescaler 要写入的PSC的值,范围:0~65535* 返 回 值:无* 注意事项:PSC和ARR共同决定频率,此函数仅设置PSC的值,并不直接是频率* 频率Freq = CK_PSC / (PSC + 1) / (ARR + 1)*/
void PWM_SetPrescaler(uint16_t Prescaler)
{TIM_PrescalerConfig(TIM2, Prescaler, TIM_PSCReloadMode_Immediate); //设置PSC的值
}
3.3.3 关于定时器配置方法的小结
无论是通用定时器还是高级定时器都需要做以下几件事情:
(1)开启时钟
(2)配置GPIO
(3)配置时间基准单元
主要配置分频寄存器PSC,重装寄存器ARR,这决定了PWM的周期,CCR决定占空比
(4)配置输出比较通道
(5)使能定时器
