PWM(Pulse Width Modulation)脉冲宽度调制
在具有惯性的系统中,可以通过对一系列脉冲的宽度进行调制,来等效地获得所需要的模拟参量,常应用于电机控速等领域。
PWM参数: 频率 = 1 / TS 占空比 = TON / TS 分辨率 = 占空比变化步距
PWM的初始化与定时器的初始化有着相同的基础,不过PWM在定时器初始化中加入了有关PWM的内容,就是如图中的输入/捕获寄存器。
从其运行流程上看
这是上一组定时器中断的实验,PWM实验则是在其中去除中断,通过输出比较寄存器CCR来控制输出的电平,如下图:
其输出比较通道的电路图如下图:
当计数器的值没有超过CCR寄存器的值的时候其输出的值与超过后的不同,如上图一路直通OC1 。其中输出模式控制器的模式可以更改,其模式如下图:
本次实验选择PWM模式1, 接下来在程序中将PWM的通路打通。
实验程序PWM.c文件:
#include "stm32f10x.h" // Device header
void PWM_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_InternalClockConfig(TIM2);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_Period = 100 - 1;
TIM_TimeBaseInitStruct.TIM_Prescaler = 720 -1;
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStruct);
TIM_OCInitTypeDef TIM_OCInitStruct;
TIM_OCStructInit(&TIM_OCInitStruct);
TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStruct.TIM_Pulse = 0; // CCR
TIM_OC1Init(TIM2, &TIM_OCInitStruct);
TIM_Cmd(TIM2, ENABLE);
}
void PWM_change(uint16_t compare)
{
TIM_SetCompare1(TIM2, compare); // change CCR value
}
与定时中断实验程序的区别不大,去除中断程序配置,结构化OC输出比较单元,其中先将OC模块初始化配置使用TIM_OCStructInit(),因为其中还包含了一部分为用到的配值,但为了其不影响实验的运行,将其初始化配置后,自行更改需要的配置。其中包含了模式的选择,高低电平的选择,使能输出,然后是CCR的值,此实验中CCR的值又下面的函数更改。
PWM.h文件:
#ifndef __PWM_H
#define __PWM_H
void PWM_Init(void);
void PWM_change(uint16_t compare);
#endif
至此完成了PWM的模块化初始配置。
接下来做第一个实验,PWM驱动的呼吸灯。
上面的程序中PWM波由PA0口输出,在此加上LED灯珠。
主函数main.c文件如下:
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "PWM.h"
uint16_t i;
int main(void)
{
OLED_Init();
PWM_Init();
OLED_ShowString(2, 5, "Flying!");
while (1)
{
for(i = 0; i <= 100; i++)
{
PWM_change(i);
Delay_ms(10);
}
for(i = 0; i <= 100; i++)
{
PWM_change(100 - i);
Delay_ms(10);
}
}
}
其中循环中加入了一定的延时,至此才能够看到灯珠的呼吸态,这是由于PWM调制波的频率过高。
第二个实验是PWM驱动舵机,选用的舵机是SG90,其具体要求如下:
那么就要按照这个信号要求来输出PWM信号, PWM的参数计算如下:
知晓了其周期,以及其占空比就可以来推算出合适的参数来输出控制舵机的PWM波。
本次实验的ARR取2000-1,PSC取720,这样他的周期就是20ms,至于其占空比,可用角度angle/180 *200 +50,由此来确定其CCR的值。具体看如下程序。其中PWM文件不更改,新建sensor文件。
sensor.c文件如下:
#include "stm32f10x.h" // Device header
#include "PWM.h"
void sensor_Init(void)
{
PWM_Init();
}
void sensor_angle(float angle)
{
PWM_change(angle / 180 * 200 +50);
}
就是加了一个按照要求更改占空比的函数。
sensor.h文件:
#ifndef __SENSOR_H
#define __SENSOR_H
void sensor_Init(void);
void sensor_angle(float angle);
#endif
本程序还用到了按键读取,具体程序看附件完整工程。
主函数main.c如下:
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "sensor.h"
#include "Key.h"
uint16_t key_num;
float angle;
int main(void)
{
OLED_Init();
sensor_Init();
Key_Init();
OLED_ShowString(1, 1, "Angle:");
while (1)
{
key_num = Key_GetNum();
if(key_num == 1)
{
angle += 30;
if(angle > 180)
{
angle = 0;
}
}
sensor_angle(angle);
OLED_ShowNum(1, 7, angle, 3);
}
}
至此完成PWM驱动舵机的实验。