文章目录
- 一、TIM输出比较
- 输出比较简介
- PWM简介
- 输出比较通道(高级)
- 输出比较通道(通用)
- 输出比较模式控制器工作原理
- PWM基本结构
- 参数计算
- 三、PWM驱动LED呼吸灯
- 电路设计
- 关键代码
- 关键函数与参数
- 引脚重映射
- 取消默认调试功能函数
- 极性选择
- 决定占空比,周期的三个函数
- 四、PWM驱动舵机
- 硬件介绍
- 电路设计
- 关键代码
- 五、PWM驱动直流电机
- 硬件介绍
- 电路设计
- 关键代码
- 六、定时器库函数(tim.h)
一、TIM输出比较
输出比较简介
- OC(Output Compare)输出比较
- 输出比较可以通过
比较CNT与CCR寄存器值的关系
,来对输出电平进行置1、置0或翻转的操作
,用于输出一定频率和占空比的PWM波形 - 每个高级定时器和通用定时器都拥有
4个输出比较通道
,每个输出比较通道对应一个引脚 - 高级定时器的前3个通道额外拥有死区生成和互补输出的功能【用于三相无刷电机】
- CCR捕获/比较寄存器是输入捕获和输出比较共用的
PWM简介
-
PWM(Pulse Width Modulation)脉冲宽度调制
-
在具有惯性的系统中,可以通过对一系列脉冲的宽度进行调制,
来等效地获得所需要的模拟参量
,常应用于电机控速等领域,测量,通信,功率调试
-
PWM参数:
- 频率 = 1 / TS 与等效的模拟量信号的平稳相关,性能开销相关
- 占空比 = TON / TS 决定模拟电压的大小
- 分辨率 = 占空比变化步距
输出比较通道(高级)
- 高级定时器框图
- 通道的输出部分
输出比较通道(通用)
- 通用定时器框图
- 通道的输出部分
- oc1ref:输出高低电平(实际上就是REF信号)
- 可以将REF映射到主模式的TRGO输出上
- cc1p:级性选择:是否需要翻转,0是不翻转,1是非门,翻转
输出比较模式控制器工作原理
- 有效电平理解为高电平,无效电平理解为低电平,和高级定时器里面的关断,刹车功能配合
- pwm模式1和模式2类似,只是极性相反
- 在输出模式控制器中可以设置极性,同时,在cc1p也可以设置极性,极性的设置较为灵活
PWM基本结构
- 图中参考的输出比较模式选定为PWM模式1
参数计算
-
PWM频率(HZ):Freq = CK_PSC(默认72MHZ) / (PSC + 1) / (ARR + 1)=计数器更新频率=计数器溢出频率
-
PWM占空比(%):Duty = CCR / (ARR + 1)
-
PWM分辨率(%):Reso = 1 / (ARR + 1),ARR值越大,分辨率越好,CRR值越大越好
-
PWM周期=定时时长=计数器溢出频率的倒数
三、PWM驱动LED呼吸灯
电路设计
- 正极接A0引脚,输出高电平时点亮,占空比越大则越亮
关键代码
功能:产生一个频率为1KHZ的呼吸灯
原理:占空比由小到到再由大到小
pwm.c
#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);//AFIO管理引脚重映射
//GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);重映射有部分重映射和完全重映射,需要查看手册配置相应的模式,此处使用部分重映射1模式
//GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);//PA15默认为调试端口,需要关闭调试端口复用,再设置为普通IO或复用定时器通道。调试端口有5个,通过参数可以将五个都复用,但是会导致只能使用串口下载
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);//PA0功能为TIM2_CH1_ETR
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;//输出比较模式为PWM模式1
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//输出比较极性,该极性表示不翻转
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//设置输出使能
TIM_OCInitStructure.TIM_Pulse = 0; //CCR寄存器初始值
TIM_OC1Init(TIM2, &TIM_OCInitStructure);//OC1通道初始化
TIM_Cmd(TIM2, ENABLE);
}
void PWM_SetCompare1(uint16_t Compare)
{
TIM_SetCompare1(TIM2, Compare);//由于ARR等于100,此时CCR的值可以看做占空比
}
mian.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "PWM.h"
uint8_t i;
int main(void)
{
OLED_Init();
PWM_Init();
while (1)
{
for (i = 0; i <= 100; i++)
{
PWM_SetCompare1(i);
Delay_ms(10);
}
for (i = 0; i <= 100; i++)
{
PWM_SetCompare1(100 - i);
Delay_ms(10);
}
}
}
关键函数与参数
当使用输出比较通道1时,GPIO只能使用功能TIMx_CH1的PA0和PA15引脚(需要引脚重映射),根据用户手册配置模式为推挽复用输出。采用复用推挽输出,输出控制权将由输出寄存器转移到片上外设(定时器TIM2的CH1通道)
- 用户手册
- 推挽复用输出时,控制信号来自片上外设:
引脚重映射
引脚重映射函数
GPIO_PinRemapConfig()
将PA15映射到定时器输出模式的参数
@arg GPIO_PartialRemap1_TIM2 : TIM2 Partial1 Alternate Function mapping//部分重映射1
@arg GPIO_PartialRemap2_TIM2 : TIM2 Partial2 Alternate Function mapping//部分重映射2
@arg GPIO_FullRemap_TIM2 : TIM2 Full Alternate Function mapping//完全重映射
对应的用户手册中PA15引脚重映射函数的参数值
取消默认调试功能函数
引脚重映射函数
GPIO_PinRemapConfig()
取消PA15默认的调试功能的函数参数
@arg GPIO_Remap_SWJ_NoJTRST : Full SWJ Enabled (JTAG-DP + SW-DP) but without JTRST//解除JTRST复用,即PB4可以复用,其他还是调试端口
@arg GPIO_Remap_SWJ_JTAGDisable : JTAG-DP Disabled and SW-DP Enabled//解除JTAG端口复用,即PA15,PB3,PB4可以复用
@arg GPIO_Remap_SWJ_Disable : Full SWJ Disabled (JTAG-DP + SW-DP)//解除SWJ端口复用,即PA13,PA14,PA15,PB3,PB4可以复用
对应的用户手册中取消PA15默认的调试功能的函数参数
引脚功能图
极性选择
- TIM_OCPolarity_High,高极性,不翻转
- TIM_OCPolarity_Low,低极性,翻转电平
决定占空比,周期的三个函数
TIM_TimeBaseInitStructure.TIM_Period=100 - 1;//ARR
TIM_TimeBaseInitStructure.TIM_Prescaler=720 - 1;//PSC
TIM_OCInitStructure.TIM_Pulse=0;//CCR此时为占空比
四、PWM驱动舵机
硬件介绍
- 舵机是一种根据输入PWM信号占空比来控制输出角度的装置
- 输入PWM信号要求:pwm周期为20ms(50hz),高电平宽度为0.5ms~2.5ms
电路设计
关键代码
实现功能:按下按键旋转30度,到达180度则返回原点
为了满足Freq = 72MHZ / (PSC + 1) / (ARR + 1) = 50HZ,以及Duty = CCR / (ARR + 1),可以将ARR设置为20000 - 1,PSC设置诶72-1,此时CCR 根据公式可得为500-2500,对应的信号周期的范围是0.5ms-2.5ms。
PWM.c文件和PWM驱动LED呼吸灯类似
servo.c文件中角度与CCR值的关系转换函数
void Servo_SetAngle(float Angle){
PWM_SetCompare2(Angle / 180 * 2000 + 500);
}
在main.c文件中调用
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "KEY.h"
#include "Servo.h"
uint8_t KeyNum;
float Angle;
int main(void)
{
OLED_Init();
Key_Init();
Servo_Init(); //函数内部完成pwm初始化
OLED_ShowString(1, 1, "Angle:");
while (1)
{
KeyNum = Key_GetNum();
if(KeyNum == 1){
Angle+=30;
if(Angle >180){
Angle = 0;
}
}
//该函数完成从角度到CCR的转换
Servo_SetAngle(Angle);
OLED_ShowNum(1, 7, Angle, 3);
}
}
五、PWM驱动直流电机
硬件介绍
- 直流电机是一种将电能转换为机械能的装置,有两个电极,当电极正接时,电机正转,当电极反接时,电机反转
- 直流电机属于大功率器件,GPIO口无法直接驱动,需要配合电机驱动电路来操作
- TB6612是一款双路H桥型的直流电机驱动芯片,可以驱动两个直流电机并且控制其转速和方向
图三的H桥电路:
- 四个开关元器件Q1,Q2,Q3,Q4,另外还有一个直流电机M,D1,D2,D3,D4是MOS-FET的续流二极管
- 两路推挽电路,正转和反转是人为规定的方向
- 左上角与右下角导通,则电流从Q1流到Q4
- 右上角与左下角导通,则电流从Q3流到Q2
电路设计
直流电机驱动芯片连接图
-
电机的电流从VM端输入,一般和电机的额定电压一致,完成低功率的输入到高功率的输出,可以选5V
-
VCC与控制器电源保持一致,可以选3.3V
-
直流电机驱动芯片引脚功能图
关键代码
Motor.c
#include "stm32f10x.h" // Device header
#include "PWM.h"
void Motor_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//pin5和pin4为方向控制脚
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
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Motor.h"
#include "Key.h"
uint8_t KeyNum;
int8_t Speed;
int main(void)
{
OLED_Init();
Motor_Init();
Key_Init();
OLED_ShowString(1, 1, "Speed:");
while (1)
{
KeyNum = Key_GetNum();
if (KeyNum == 1)
{
Speed += 20;
if (Speed > 100)
{
Speed = -100;
}
}
Motor_SetSpeed(Speed);
OLED_ShowSignedNum(1, 7, Speed, 3);
}
}
六、定时器库函数(tim.h)
//配置输出比较模块的四个通道
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);
void TIM_OCStructInit(TIM_OCInitTypeDef* TIM_OCInitStruct);//结构体默认值
//配置强制输出模式
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);
//配置快速使能
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);
//清除REF信号
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);
//单独设置输出比较极性,带N表示高级定时器的互补通道配置,通道4没有互补配置
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);
//单独修改输出使能参数
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);
//选择输出比较模式
void TIM_SelectOCxM(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_OCMode);
//设置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);