PWM
OC(Output Compare)输出比较
输出比较可以通过比较CNT与CCR寄存器值的关系,来对输出电平进行置1、置0或翻转的操作,用于输出一定频率和占空比的PWM波形每个高级定时器和通用定时器都拥有4个输出比较通道高级定时器的前3个通道额外拥有死区生成和互补输出的功能。
PWM(Pulse Width Modulation)脉冲宽度调制
在具有惯性的系统中,可以通过对一系列脉冲的宽度进行调制,来等效地获得所需要的模拟参量,常应用于电机控速等领域。
PWM参数:
频率 = 1 / TS 占空比 = TON / TS 分辨率 = 占空比
高级输出比较通道

通用输出比较通道

输出比较模式

PWM基本结构

蓝色:CNT 红色:CCR 黄色:ARR
PSC一般为78MHz
参数计算
PWM频率: Freq = CK_PSC / (PSC + 1) / (ARR + 1)
PWM占空比: Duty = CCR / (ARR + 1)
PWM分辨率: Reso = 1 / (ARR + 1)
有关输出比较的函数
(1)结构体初始化输出比较单元
用来配置输出比较模块,位于tim.h第1056~1059
在PWM结构图中可知它有四个输出比较单元,对应四个函数
(选择定时器,输出比较参数)
1 2 3 4
| 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);
|
(2)使用高级定时器输出PWM使能主输出
位于tim.h第1068
1
| void TIM_CtrlPWMOutputs(TIM_TypeDef* TIMx, FunctionalState NewState);
|
*(3)配置强制输出模式,暂停输入波频强制输出高低电平
位于tim.h第1088~1091
1 2 3 4
| 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);
|
*(4)配置CCR寄存器预装功能(影子寄存器)
写入的值不会生效,在更新事件后才会
位于tim.h第1096~1099
1 2 3 4
| 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);
|
*(5)配置快速使能
位于tim.h第1101~1104
1 2 3 4
| void TIM_OC1FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast); void TIM_OC2FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast); void TIM_OC3FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast); void TIM_OC4FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast);
|
*(6)外部事件时清除REF信号
位于tim.h第1104~1107
1 2 3 4
| void TIM_ClearOC1Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear); void TIM_ClearOC2Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear); void TIM_ClearOC3Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear); void TIM_ClearOC4Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear);
|
(7)单独设置输出比较极性
位于tim.h第1108~1114
NPolarityConfig是高级定时器里互补通道的配置
1 2 3 4 5 6 7
| void TIM_OC1PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity); void TIM_OC1NPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCNPolarity); void TIM_OC2PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity); void TIM_OC2NPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCNPolarity); void TIM_OC3PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity); void TIM_OC3NPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCNPolarity); void TIM_OC4PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
|
(8)单独修改输出使能参数
位于tim.h第1115~1116
1 2
| void TIM_CCxCmd(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_CCx); void TIM_CCxNCmd(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_CCxN);
|
(9)单独更改输出比较模式
位于tim.h第1117
1
| void TIM_SelectOCxM(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_OCMode);
|
(10)单独更改CCR寄存器【重点】
位于tim.h第1127~1130
1 2 3 4
| TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1); TIM_SetCompare2(TIM_TypeDef* TIMx, uint16_t Compare2); TIM_SetCompare3(TIM_TypeDef* TIMx, uint16_t Compare3); TIM_SetCompare4(TIM_TypeDef* TIMx, uint16_t Compare4);
|
(11)给结构体赋初始值
1
| TIM_OCStructInit(TIM_OCInitTypeDef* TIM_OCInitStruct);
|
(2)结构体初始化输出比较单元
TIM_OCInitTypeDef…?
TIM_OCMode
1 2 3 4 5 6 7 8 9 10 11
| TIM_OCMode_Timing ((uint16_t)0x0000)
TIM_OCMode_Active ((uint16_t)0x0010)
TIM_OCMode_Inactive ((uint16_t)0x0020)
TIM_OCMode_Toggle ((uint16_t)0x0030)
TIM_OCMode_PWM1 ((uint16_t)0x0060) TIM_OCMode_PWM2
|
TIM_OCPolarity
1 2 3 4
| TIM_OCPolarity_High ((uint16_t)0x0000)
TIM_OCPolarity_Low ((uint16_t)0x0002)
|
TIM_OCInitStructure.TIM_Pulse
CCR
ARR,PSC,CCR共同决定输出PWM周期和占空比

要使用复用推挽输出,因为对于普通的开漏/推挽输出,引脚的控制权来自于输出数据寄存器,如果想让定时器来控制引脚,就要使用复用开漏/推挽输出模式。

PWM频率是1000 PWM占空比50% PWM分辨率1%
CK_PSC=72M
引脚重映射
(1)引脚定义

(2)引脚重定义
使用AFIO,AFIO指的是IO复用的意思 ,即一个IO口用在多个外设上的意思。
引脚重映射配置,先打开AFIO时钟
1 2
| RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState);
|
关闭调试端口的复用,SWJ=SWD+JTAG
1 2 3
| GPIO_Remap_SWJ_NoJTRST GPIO_Remap_SWJ_JTAGDisable GPIO_Remap_SWJ_Disable
|

同一个定时器的不同通道输出的PWM
因为不同通道共用一个计数器,所以他们的频率是一样的,相位同步。、
1 2 3 4
| TIM_OC1Init(TIM2, &TIM_OCInitStructure); TIM_OC2Init(TIM2, &TIM_OCInitStructure); TIM_OC3Init(TIM2, &TIM_OCInitStructure); TIM_OC4Init(TIM2, &TIM_OCInitStructure);
|
计算

PWM频率是1000 PWM占空比50% PWM分辨率1%
CK_PSC=72M
舵机要求的周期是20ms,那频率就是1/20ms=50MHz
舵机要求的高电平时间是0.5ms~2.5ms
(PSC+1)=72 CCR=500(0.5ms)
(ARR+1)=20K
舵机
舵机是一种根据输入PWM信号占空比来控制输出角度的装置输入PWM信号要求:周期为20ms,高电平宽度为0.5ms~2.5ms
注意:对PWM输出的数值有要求。


Servo.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #include "stm32f10x.h" #include "PWM.h"
void Servo_Init(void) { PWM_Init(); }
void Servo_SetAngle(float Angle) { PWM_SetCompare2(Angle / 180 * 2000 + 500);
}
|
对于PWM配置的数值:
1 2 3 4 5 6 7 8
| TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInitStructure.TIM_Period = 20000 - 1; TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1; TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
|
电机
直流电机是一种将电能转换为机械能的装置,有两个电极,当电极正接时,电机正转,当电极反接时,电机反转直流电机属于大功率器件,GPIO口无法直接驱动,需要配合电机驱动电路来操作TB6612是一款双路H桥型的直流电机驱动芯片,可以驱动两个直流电机并且控制其转速和方向。


Motor.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| #include "stm32f10x.h" #include "PWM.h"
void Motor_Init(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); PWM_Init() }
void Motor_SetSpeed(int8_t Speed) { if (Speed >= 0) { GPIO_SetBits(GPIOA, GPIO_Pin_4); GPIO_ResetBits(GPIOA, GPIO_Pin_5); PWM_SetCompare3(Speed); } else { GPIO_ResetBits(GPIOA, GPIO_Pin_4); GPIO_SetBits(GPIOA, GPIO_Pin_5); PWM_SetCompare3(-Speed); } }
|
main.c