一、什么是PWM
脉冲宽度调制(PWM),是英文“Pulse Width Modulation”的缩写,简称脉宽调制,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术,广泛应用在从测量、通信到功率控制与变换的许多领域中。
PWM占空比:
PWM信号保持高电平的时间百分比称为占空比。
如果信号始终为高电平,则它处于100%占空比,如果它始终处于低电平,则占空比为0%。如图1所示,T1为占空比,T为一个PWM周期。
二、PWM产生方式
通过STM32控制板,有两种方式能产生PWM,
第一是利用普通IO口输出PWM,第二种是利用定时器的PWM的IO口或复用IO口。
⛵普通IO口与PWM口
1)PWM端口
STM32 的定时器除了 TIM6 和 7。其他的定时器都可以用来产生 PWM 输出。其中高级定时器 TIM1 和 TIM8 可以同时产生多达 7 路的 PWM 输出。而通用定时器也能同时产生多达 4路的 PWM 输出,这样,STM32 最多可以同时产生 30 路 PWM 输出。
(2)普通IO口
一般能够输出PWM的端口都会在主要功能那一栏出现CHx的标志,而普通定时器没有出现这种标志。如图所示,上面的红框就是普通的定时器,不是专用的PWM端口。
(3)两者区别
1)一般而言,尽量选用PWM口进行PWM输出,因为普通IO口模拟PWM的输出频率越高,进入定时器中断的次数就越快,中断间隔的时间越短,如果再有其他类型的中断也要处理时,会因为中断的优先级嵌套等待响应,影响控制精度,PWM输出误差增大,也会影响其他如ADC等中断处理,甚至会较出现单片机逻辑出错,死机或者跑飞的情况。
- 2)普通IO也可以输出PWM,只是产生PWM一般用转用芯片(开关电源上用的较多)或者单片机的PWM内置模块如定时器,很小直接用MCU的IO口线直接输出因为那样太耗MCU资源了。
控制 PWM 的输出寄存器:CCMR1/2、CCER、CCR1~4、BDTR
(1)捕获/比较模式寄存器( TIMx_CCMR1/2)
(2)捕获/比较使能寄存器( TIMx_CCER)
(3)捕获/比较寄存器( TIMx_CCR1~4)
(4)刹车和死区寄存器( TIMx_BDTR)[注]刹车和死区寄存器( TIMx_BDTR)仅在高级定时器中使用,普通定时器只需要配置前三个寄存器即可
三、程序实现---舵机控制
这里用的是F4来演示,舵机接的是PA1--定时器2通道2
将通用定时器分为四个部分:
- 1,选择时钟
- 2,时基电路
- 3,输入捕获
- 4,输出比较
1,TIMx_CCMR1寄存器的OC1M[2:0]位,设置输出模式控制器 110:PWM模式1 111:PWM模式2 2,计数器值TIMx_CNT与通道1捕获比较寄存器CCR1进行比较,通过比较结果输出有效电平和无效电平 OC1REF=0 无效电平 OC1REF=1 无效电平 3,通过输出模式控制器产生的信号 TIMx_CCER寄存器的CC1P位,设置输入/捕获通道1输出极性 0:高电平有效 1:低电平有效 4,TIMx_CCER:CC1E位控制输出使能电路,信号由此输出到对应引脚 0:关闭 1:开启
舵机
占空比 = t / T 相关参数如下:
t = 0.5ms——————-舵机会转到 0 °
t = 1.0ms——————-舵机会转到 45°
t = 1.5ms——————-舵机会转到 90°
t = 2.0ms——————-舵机会转到 135°
t = 2.5ms——————-舵机会转到 180°
因为:PWM周期为20ms = (8400*200)/84000000=0.02
#include "pwm.h"
/*******************************************
*@函数名 : PWM_Tim2_Init
*@函数功能 : PWM初始化--TIM2
*@函数参数 : psc--接口时钟 arr--重装载值
*@Data 2023-09-18
*@函数返回值: None
*@函数描述 : 周期20ms; 0.5ms脉宽时舵臂角度为0度; 2.5ms脉宽时舵臂角度为180度。
*PA0--接舵机---定时器2通道2
*********************************************/
void PWM_Tim2_Init(u32 psc, u32 arr)
{
/** 使能相关时钟 **/
RCC_AHB1PeriphClockCmd (RCC_AHB1Periph_GPIOA, ENABLE); // 开启相关的GPIO外设时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // 使能定时器时钟
/** 配置所用TIM的时基 **/
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; // 定义时基结构体
TIM_TimeBaseStructure.TIM_Prescaler = psc-1; // 分频; 把接口时钟分频后给计数器使用, 即多少个接口脉冲,才产生一次计数器脉冲; 简单理解:计算每一计数器脉冲的时长;
TIM_TimeBaseStructure.TIM_Period = arr-1; // ARR, 自动重载值; 多少个计数器脉冲作为一周期; 注意:TIM2和5是32位的,其它TIM是16位的;
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; // 采样时钟分频
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; // 计数方式
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); // 初始化定时器,把配置写入寄存器
/** 配置通道2所用引脚 **/
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; // 引脚编号
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; // 工作模式:复用模式
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; // 输出类型:推挽模式
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; // 上下拉选择:浮空
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; // 端口速率:100MHz
GPIO_Init(GPIOA, &GPIO_InitStructure); // 初始化,把结构体数据写入到芯片寄存器
/** 配置通道2所用引脚的复用功能 **/
GPIO_PinAFConfig(GPIOA, GPIO_PinSource1, GPIO_AF_TIM2); // 定时器通道引脚复用
/** 配置通道2的工作模式 **/
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; // 比较输出方式; 简单理解: PWM1_CNT<CCR时产生有效电平、PWM2_CNT>CCR时产生有效电平;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; // 通道输出开关;
TIM_OCInitStructure.TIM_Pulse = 0; // 比较值; 简单理解:计数器每次脉冲均把CNT值与此CCR值比较大小,根据设置的工作模式TIM_OCMode值,用于产生有效电平(脉宽);
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; // 输出极性; 可配置为:高、低电平; 简单理解:有效电平,需配合上面的TIM_OCMode选项来理解;
TIM_OC2Init(TIM2, &TIM_OCInitStructure); // 初始化,把上面参数写到寄存器
/** 配置通道2预装载 **/
TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable); // 配置通道预装载; 理解:Enable_修改CCR值(占空比)后等下个周期再生效、Disable_修改CCR值(占空比)后立即生效;
/** 使能定时器 **/
TIM_Cmd(TIM2, ENABLE);
}
看看结构体 TIM_OCInitTypeDef--通道选择
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;
我们来看看里面的参数:
参数 TIM_OCMode 设置模式是 PWM 还是输出比较,这里我们是 PWM 模式。
参数 TIM_OutputState 用来设置比较输出使能,也就是使能 PWM 输出到端口。
参数 TIM_OCPolarity 用来设置极性是高还是低。
其他的参数 TIM_OutputNState, TIM_OCNPolarity, TIM_OCIdleState 和 TIM_OCNIdleState 是
高级定时器 TIM1 和 TIM8 才用到的。
四、修改 TIM2_CCR4来控制占空比。
TIM_SetCompare2(TIM2,1000);---主函数执行