PWM驱动
- PWM介绍
- TIM_OC1Init 配置通道
- TIM_OCStructInit 输出比较参数默认值
- 输出比较模式 TIM_OCInitstructure
- 输出比较极性 TIM_OCInitstructure
- 设置输出使能
- 以下三个决定了PWM的频率 占空比
- 初始化通道 TIM_OC1Init(TIM2, &TIM_OCInitstructure);
- GPIO复用 PWM通道
 
- 驱动LED
- 复用推挽输出
 
- 驱动舵机
- 驱动直流电机
PWM介绍
每个定时器有四个通道,每一个通道都有一个捕获比较寄存器,
 将寄存器值和计数器值比较,通过比较结果输出高低电平,实现PWM信号
如图为向上计数:
     定时器重装载值为ARR,比较值CCRx
     t时刻对计数器值和比较值进行比较
     如果计数器值小于CCRx值,输出低电平
     如果计数器值大于CCRx值,输出高电平
 
PWM的一个周期
    定时器从0开始向上计数
    当0-t1段,定时器计数器TIMx_CNT值小于CCRx值,输出低电平
    t1-t2段,定时器计数器TIMx_CNT值大于CCRx值,输出高电平
    当TIMx_CNT值达到ARR时,定时器溢出,重新向上计数...循环此过程
    至此一个PWM周期完成
 
影响因素
    ARR : 决定PWM周期(在时钟频率一定的情况下,当前为默认内部时钟CK_INT)
    CCRx : 决定PWM占空比(高低电平所占整个周期比例)
TIM_OC1Init 配置通道
配置比较函数 一个函数配置一个单元
 参数1 定时器 参数2 输出比较参数 ******
void TIM_OC1Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC2Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC3Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC4Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
TIM_OCStructInit 输出比较参数默认值
输出比较参数默认值
void TIM_OCStructInit(TIM_OCInitTypeDef* TIM_OCInitStruct);
用来配置强制输出模式 = 100%占空比
void TIM_ForcedOC1Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);
void TIM_ForcedOC2Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);
void TIM_ForcedOC3Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);
void TIM_ForcedOC4Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);
用来配置CCR寄存器预装功能
void TIM_OC1PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
void TIM_OC2PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
void TIM_OC3PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
void TIM_OC4PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
用来单独修改CCR寄存器值的函数(更改占空比)****
void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1);
void TIM_SetCompare2(TIM_TypeDef* TIMx, uint16_t Compare2);
void TIM_SetCompare3(TIM_TypeDef* TIMx, uint16_t Compare3);
void TIM_SetCompare4(TIM_TypeDef* TIMx, uint16_t Compare4);
再补充说明一下这个函数仅高级定时器使用在使用高级定时器输出PWM时需要调用这个函数使能主输出否则PWM将不能正常输出
void TIM_CtrlPWMOutputs(TIM_TypeDef* TIMx, FunctionalState NewState);
输出比较模式 TIM_OCInitstructure
TIM_OCInitstructure.TIM_OCMode = TIM_OCMode_PWM1;
 1 冻结模式
 2 相等时置有效电平
 3 相等时置无效电平
 4 相等时电平反转
 5 6 pwm1 pwm2
 
输出比较极性 TIM_OCInitstructure
TIM_OCInitstructure.TIM_OCPolarity = ;

 1高极性 极性不反转 REF波形直接输出 REF有效时 输出高电平
 2 REF有效时 输出低电平
设置输出使能
TIM_OCInitstructure.TIM_OutputNState = TIM_OutputNState_Enable;
以下三个决定了PWM的频率 占空比
TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1; //ARR in时基
 TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1; //PSC in 时基
 TIM_OCInitstructure.TIM_Pulse = ; //CCR 0000~FFFF
 
初始化通道 TIM_OC1Init(TIM2, &TIM_OCInitstructure);
GPIO复用 PWM通道

驱动LED
复用推挽输出


 我们可以看到 pa0的控制权 由输出数据寄存器 跳转到了片上外设
 所以 PA0的输出模式改为复用输出
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
分辨率1% 频率1k 占空比 50% 由公式可得
 ARR = 100 - 1
 CCR = 50
 PSC = 720 - 1
 后期可以调 CCR 来控制占空比
 0 - 100 分别代表占空比 0 - 100 %
#include "stm32f10x.h"                  // Device header
void PWM_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //打开时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
//	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
//	GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);
//	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	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);
	
	TIM_InternalClockConfig(TIM2);  //选择内部时钟
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; //初始化时基单元
	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 = 720 - 1;		//PSC
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
	
	TIM_OCInitTypeDef TIM_OCInitStructure;//配置输出比较单元
	TIM_OCStructInit(&TIM_OCInitStructure);
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
	TIM_OCInitStructure.TIM_Pulse = 50;		//CCR
	TIM_OC1Init(TIM2, &TIM_OCInitStructure);
	
	TIM_Cmd(TIM2, ENABLE);//启动定时器
}
void pwm_setcompare1(uint16_t Compare)  //实时修改CRR 用来控制PWM占空比
{
	TIM_SetCompare1(TIM2, Compare);
}
驱动舵机
驱动舵机不免驱动一个,多数情况下会驱动多个
 那么一个定时器应该如何驱动多个舵机呢
 我们可以开通多个通道
 

占空比要求一般 0.5ms - 2.5 ms
ARR+1 = 20k
 PSC + 1 = 72
 CRR = 500 -2500
这里通道设置为2 GPIOA pin2
 初始化代码基本不变
 main如下
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "sevo.h"
uint16_t i;
float angle=0;
int main(void)
{
	OLED_Init();//Apin 14 15
	sevo_init();//TIM2 ͨµÀ2 PA1
	
	while (1)
	{
		if(angle>180) angle=0;
		sevoangle(angle);
		OLED_ShowNum(1,1,angle,3);//
		angle+=30;
		Delay_ms(1000);
		
	}
}
sevo.c如下
#include "stm32f10x.h"                  // Device header
#include "pwm.h"
void sevo_init(void){
	PWM_Init();
}
void sevoangle(float angle){
	angle=angle/180 * 2000 + 500;
	TIM_SetCompare2(TIM2, angle);
}
实现了1s钟 舵机旋转30度
舵机接口   
1. 5V电压 
2. PWM通道2  这里接PA1 TIM2 通道 2
3. GND
驱动直流电机

频率越快 蜂鸣器杂音越小 20kHZ psc=32 预分频器= 32
 72M / 32 = 20KHZ
 CCR = -100 ~ +100 反转 和 正转
由于设备不齐全 只能理论以下了
VM 5v
VCC 3.3v
GND
AO1 正极
AO2 负极
PWMA 接A2 使用的TIM2 通道3
AIN2 GPIO A5
AIN1 GPIO A4
正传 A4=1 A5=0
反转 相反
STBY 3.3v
CCR用来控制速度
 0-100
 但我们输入参数的时候是-100 ~ +100  这是因为我们要区分正传还是反转
 如果为负数 我们就A4=0 A5=1 反之 相反
 如果为负值 我们需要取绝对值 否则CCR会错误
留个作业
旋转编码器来控制舵机 留在这



















