少女祈祷中...

PWM

OC(Output Compare)输出比较

​ 输出比较可以通过比较CNT与CCR寄存器值的关系,来对输出电平进行置1、置0或翻转的操作,用于输出一定频率和占空比的PWM波形 每个高级定时器和通用定时器都拥有4个输出比较通道高级定时器的前3个通道额外拥有死区生成和互补输出的功能。

PWM(Pulse Width Modulation)脉冲宽度调制

​ 在具有惯性的系统中,可以通过对一系列脉冲的宽度进行调制,来等效地获得所需要的模拟参量,常应用于电机控速等领域。

PWM参数:

频率 = 1 / TS 占空比 = TON / TS 分辨率 = 占空比

高级输出比较通道

image.png

通用输出比较通道

image.png

输出比较模式

image.png

PWM基本结构

image.png

蓝色: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)
//PWM输出比较模式
TIM_OCMode_PWM1 ((uint16_t)0x0060)
TIM_OCMode_PWM2

TIM_OCPolarity

1
2
3
4
//极性不翻转,REF波形直接输出,有效电平为高电平
TIM_OCPolarity_High ((uint16_t)0x0000)
//极性翻转,REF电平取反,有效电平为低电平
TIM_OCPolarity_Low ((uint16_t)0x0002)

TIM_OCInitStructure.TIM_Pulse

CCR

ARR,PSC,CCR共同决定输出PWM周期和占空比

image.png

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

image.png

PWM频率是1000 PWM占空比50% PWM分辨率1%

CK_PSC=72M

引脚重映射

(1)引脚定义

image.png

(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 //解除JTRST引脚复用(PB4变回GPIO)
GPIO_Remap_SWJ_JTAGDisable //解除JTAG调试端口复用(PA15,PB3,PB4变回GPIO)
GPIO_Remap_SWJ_Disable //把SWD和JTAG的调试端口全部解除

image.png

同一个定时器的不同通道输出的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);

计算

image.png

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输出的数值有要求。

image.png

image.png

Servo.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include "stm32f10x.h"                  // Device header
#include "PWM.h"

void Servo_Init(void)
{
PWM_Init(); //初始化舵机的底层PWM
}

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;//ARR
TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;//PSC
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //重复计数器
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);

电机

直流电机是一种将电能转换为机械能的装置,有两个电极,当电极正接时,电机正转,当电极反接时,电机反转直流电机属于大功率器件,GPIO口无法直接驱动,需要配合电机驱动电路来操作TB6612是一款双路H桥型的直流电机驱动芯片,可以驱动两个直流电机并且控制其转速和方向。

image.png

image.png

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"                  // Device header
#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

1
Motor_SetSpeed(Speed);