一、OC(Output Compare)输出比较
1、输出比较可以通过比较CNT(计数器)与CCR(捕获/比较寄存器)寄存器值的关系,来对输出电平进行置1、置0或翻转的操作,用于输出一定频率和占空比的PWM波形
2、每个高级定时器和通用定时器都拥有4个输出比较通道
3、高级定时器的前3个通道额外拥有死区生成和互补输出的功能,用于驱动三相无刷电机
二、PWM(Pulse Width Modulation)脉冲宽度调制
1、在具有惯性的系统中,可以通过对一系列脉冲的宽度进行调制,来等效地获得所需要的模拟参量,常应用于电机控速等领域
2、PWM参数:
频率 = 1/Ts 占空比 = Ton/Ts 分辨率 = 占空比变化步距
3、输出比较通道(通用)
4、PWM基本结构
参数计算
PWM频率: Freq = CK_PSC / (PSC + 1) / (ARR + 1) = 计数器的更新频率
PWM占空比: Duty = CCR / (ARR + 1)
PWM分辨率: Reso = 1 / (ARR + 1)
5、输出比较通道(高级)
三、PWM驱动LED呼吸灯
1、按照以下接线方式连接,并将STLINK插到电脑上
2、PWM驱动函数模块
(1)输出比较库函数功能
输出比较的配置
小功能和运行时更改参数的函数
(2)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
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_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=0; //设置CCR寄存器值
TIM_OC1Init(TIM2,&TIM_OCInitStructure);
TIM_Cmd(TIM2,ENABLE);
}
void PWM_SetCompare1(uint16_t Compare)
{
TIM_SetCompare1(TIM2,Compare);
}
(3)PWM.h
#ifndef __PWM_H
#define __PWM_h
void PWM_Init(void);
void PWM_SetCompare1(uint16_t Compare);
#endif
3、编写main.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);
}
}
}
4、实现效果
PWM驱动LED呼吸灯
5、引脚重映射和解除调试端口
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //打开AFIO时钟
GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2,ENABLE); //AFIO的复用功能引脚重映射 PA15
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE); //解除JTAG的复用,保留SWD的复用 关闭调试端口PA15、PB3、PB4
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_15; //引脚口改为PA15
SWJ就是SWD和JTAG这两种调试方式
GPIO_Remap_SWJ_NoJTRST 解除JTRST引脚的复用(PB4变为正常的GPIO接口,PA13、PA14、PA15、PB3还是调试端口)
GPIO_Remap_SWJ_JTAGDisable 解除JTAG调试端口的复用(PA15、PB3、PB4三个端口变回GPIO,PA13、PA14任为SWD的调试端口)
GPIO_Remap_SWJ_Disable 把SWD和JTAG的调试端口全部解除(PA13、PA14、PA15、PB3、PB4全变为普通的GPIO,没有调试功能了)
引脚重映射
四、PWM驱动舵机
1、外部设备——舵机
(1)舵机是一种根据输入PWM信号占空比来控制输出角度的装置
(2)输入PWM信号要求:周期为20ms,高电平宽度为0.5ms~2.5ms
频率=1/周期=50Hz
(3)硬件电路
2、按照以下接线方式连接,并将STLINK插到电脑上
3、驱动函数模块
(1)舵机驱动函数模块
Servo.c
#include "stm32f10x.h" // Device header
#include "PWM.h"
void Servo_Init(void)
{
PWM_Init();
}
void Servo_SetAngle(float Angle)
{
PWM_SetCompare2(Angle/180*2000+500); //将角度转化为CCR 0~180到500~2500的映射
}
Servo.h
#ifndef __SERVO_H
#define __SERVO_H
void Servo_Init(void);
void Servo_SetAngle(float Angle);
#endif
(2)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
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;//复用推挽输出
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;
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=20000 - 1; //ARR 舵机要求的周期是20ms,频率为1/20ms=50HZ
TIM_TimeBaseInitStructure.TIM_Prescaler=72 - 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=0; //设置CCR寄存器值 占空比
TIM_OC2Init(TIM2,&TIM_OCInitStructure);//通道2初始化对应引脚PA1
TIM_Cmd(TIM2,ENABLE);
}
void PWM_SetCompare2(uint16_t Compare)
{
TIM_SetCompare2(TIM2,Compare);
}
PWM.h
#ifndef __PWM_H
#define __PWM_H
void PWM_Init(void);
void PWM_SetCompare2(uint16_t Compare);
#endif
4、编写main.c代码
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Servo.h"
#include "button.h"
uint8_t KeyNum;
float Angle;
uint8_t i;
int main(void)
{
OLED_Init();
Servo_Init();
Button_Init();
OLED_ShowString(1,1,"Angle:");
while(1)
{
KeyNum=Button_GetNum();
if(KeyNum == 1)
{
Angle+=30;
if(Angle>180)
{
Angle=0;
}
}
Servo_SetAngle(Angle);
OLED_ShowNum(1,7,Angle,3);
}
}
5、实现效果
PWM驱动舵机
五、PWM驱动直流电机
1、外部设备——直流电机
(1)直流电机是一种将电能转换为机械能的装置,有两个电极,当电极正接时,电机正转,当电极反接时,电机反转
(2)直流电机属于大功率器件,GPIO口无法直接驱动,需要配合电机驱动电路来操作
(3)TB6612是一款双路H桥型的直流电机驱动芯片,可以驱动两个直流电机并且控制其转速和方向
(4)硬件电路
2、按照以下接线方式连接,并将STLINK插到电脑上
3、驱动函数模块
(1)电机驱动函数模块
Motor.c
#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); //函数输出应该是正数
}
}
Motor.h
#ifndef __MOTOR_H
#define __MOTOR_H
void Motor_Init(void);
void Motor_SetSpeed(int8_t Speed);
#endif
(2)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
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;//复用推挽输出
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_2; //通道3
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=36 - 1; //PSC 人耳听到的声音频率范围是20HZ到20kHZ
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=0; //设置CCR寄存器值
TIM_OC3Init(TIM2,&TIM_OCInitStructure); //通道3
TIM_Cmd(TIM2,ENABLE);
}
void PWM_SetCompare3(uint16_t Compare)
{
TIM_SetCompare3(TIM2,Compare);
}
PWM.h
#ifndef __PWM_H
#define __PWM_H
void PWM_Init(void);
void PWM_SetCompare3(uint16_t Compare);
#endif
4、编写main.c代码
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Motor.h"
#include "button.h"
uint8_t KeyNum;
int8_t Speed;
int main(void)
{
OLED_Init();
Motor_Init();
Button_Init();
OLED_ShowString(1,1,"Speed:");
while(1)
{
KeyNum=Button_GetNum();
if(KeyNum == 1)
{
Speed += 20;
if(Speed >100)
{
Speed=-100;
}
Motor_SetSpeed(Speed);
OLED_ShowSignedNum(1,7,Speed,3);
}
}
}
5、实现效果
PWM驱动直流电机