目录
一、输出比较(OC)
二、PWM:
1、简介
2、基本结构
3、参数计算
三、PWM驱动LED呼吸灯
1、代码
四、PWM驱动Sg90舵机
1、工作原理
2、完整代码
五、PWM驱动直流电机
1、TB6612芯片模块
2、完整代码:
一、输出比较(OC)
OC(Output Compare)输出比较
- 可以通过比较CNT与CCR寄存器值的关系,来对输出电平进行置1、置0或翻转的操作,用于输出一定频率和占空比的PWM波形
- 每个高级定时器和通用定时器都拥有4个输出比较通道
- 高级定时器的前3个通道额外拥有死区生成和互补输出的功能
捕获/比较通道的输出部分(通道一)如下:
当CNT>=CCR1时,输出模式控制器会输出一个OC1ref,也就是一个输出比较的参考信号,可以映射到至主模式控制器,最常用的是走下面的那一条路到达CC1P极性选择,给这个寄存器写0,信号往上走,信号电平不翻转;给这个寄存器写1,信号往下走,经过一个非门取反,信号电平会发生翻转,原来的参考信号中高低电平会相反。输出使能电路就是要不要输出,OC1引脚就是CH1通道的引脚。
输出模式控制器中的输出比较8种模式,如下图:
图中置有效电平不可以直接理解为就是高电平,置无效电平也不可以直接理解为就是低电平。因为有效电平与输出极性(TIMx_CCER寄存器中的 CCxP位)有直接关系,TIMx_CCER寄存器中置CCxP = 0,选择极性为高电平有效,这时候置有效电平就为高电平;TIMx_CCER寄存器中置CCxP = 1,选择极性为低电平有效,这时候置有效电平就为低电平。
假如我们用PWM模式1,定时器设置为向上计数模式,TIMx_CCER寄存器中置CCxP = 0(在编程库函数是这个参数TIM_OCInitStructure.TIM_OCPolarityTIM_OCPolarity_High)。CNT<CCR时REF置有效电压为高电平输出,CNT>=CCR时,REF置无效电压为低电平输出。相反,如果TIMx_CCER寄存器中置CCxP = 1,CNT<CCR时REF置有效电压为低电平输出,CNT>=CCR时,REF置无效电压为高电平输出
冻结模式:当使用时,状态为暂停时的状态。
匹配时置有效电平/无效电平:是高级定时器里面用的。它俩都是一次性的置完高电平/低电平后,就不管事了,所以这俩模式不适合输出连续变化的波形。
匹配时电平翻转:可以输出一个连续的波形,输出频率=定时器频率/2,如下,会输出一个占空比为50%的波形。
强制为无效电平/无效电平:把REF强制设置为高电平/低电平。
PWM模式1/模式2:可以输出频率和占空比可调的PWM波形,功能如上图,注意一点的是定时器计数方式。
二、PWM:
1、简介
必须在具有惯性系统下应用,如灯亮灭、电机调速等,所谓的惯性,如灯灭掉,不会立马熄灭,而是慢慢的熄灭;电机也是慢慢的停止。
2、基本结构
3、参数计算
三、PWM驱动LED呼吸灯
1、代码
- 配置时钟外设
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
- GPIO初始化
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode =GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_Init(GPIOA, &GPIO_InitStructure);
如下图,当IO口作为普通IO口时使用输出数据寄存器进行输出数据;当我们使用复用功能输出时,输出数据寄存器会断开,从片上外设进行输出,片上外设如片上集成的PWM等等。复用功能输出就是从A0口进行输出PWM波形,使用的是复用推挽输出,可以输出高低平。所谓复用就是某个引脚具有多个功能。
- 内部时钟配置
TIM_InternalClockConfig(TIM2);
- 配置时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1; //滤波频率
TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up; //计数方式,向上
TIM_TimeBaseInitStruct.TIM_Period=100-1; //自动重装载寄存器ARR
TIM_TimeBaseInitStruct.TIM_Prescaler=720-1; //预分频器
TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);
- 输出比较初始化
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCStructInit(&TIM_OCInitStructure); //要给结构体赋一个初始值,否则有可能出现错误
TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1; //输出模式
TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High ; //CC1P输出极性,这里的High指的是REF有效电平为高电平输出
TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable; //输出使能
TIM_OCInitStructure.TIM_Pulse=0; //用来设置CCR
TIM_OC1Init(TIM2,&TIM_OCInitStructure);
- 启动定时器
TIM_Cmd(TIM2,ENABLE);
- 配置CCR
CCR可以通过可以库函数TIM_SetCompare1()进行动态配置。
void PWM_SetCompare1(uint16_t Compare)
{
TIM_SetCompare1(TIM2,Compare);
}
完整代码:
PWM.c:
#include "stm32f10x.h" // Device header
void PWM_Init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_OCInitTypeDef TIM_OCInitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
//1.配置时钟,用那个外设
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// //重映射功能
// RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //先打开AFIO,让AFIO去重映射
// GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2,ENABLE);
// GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE); //如果要使用PA15、B4、B3当作GPIO,首先需要先解除调式端口
GPIO_InitStructure.GPIO_Mode =GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//2.内部时钟配置
TIM_InternalClockConfig(TIM2);
//3.配置时基单元
TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1; //滤波频率
TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up; //计数方式,向上
TIM_TimeBaseInitStruct.TIM_Period=100-1; //自动重装载寄存器ARR
TIM_TimeBaseInitStruct.TIM_Prescaler=720-1; //预分频器
TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);
//4.输出比较初始化
TIM_OCStructInit(&TIM_OCInitStructure); //要给结构体赋一个初始值,否则有可能出现错误
TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1; //输出模式
TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High ; //CC1P输出极性,这里的High指的是REF有效电平为高电平输出
TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable; //输出使能
TIM_OCInitStructure.TIM_Pulse=0; //用来设置CCR
TIM_OC1Init(TIM2,&TIM_OCInitStructure);
//5.启动定时器
TIM_Cmd(TIM2,ENABLE);
}
void PWM_SetCompare1(uint16_t Compare)
{
TIM_SetCompare1(TIM2,Compare);
}
PWM.h:
#ifndef _PWM_H
#define _PWM_H
void PWM_SetCompare1(uint16_t Compare);
void PWM_Init(void);
#endif
main.c:
#include "stm32f10x.h" // Device header
#include "OLED.h"
#include "delay.h"
#include "PWM.h"
uint16_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);
}
}
}
四、PWM驱动Sg90舵机
1、工作原理
- 舵机是一种根据输入PWM信号占空比来控制输出角度的装置
- 输入PWM信号要求:频率为50HZ,周期为20ms,高电平宽度为0.5ms~2.5ms
角度范围为0~180度:
以0度和135度为例:
2、完整代码
黄色的那根线接的是PA1,用的CH2输出通道。
PWM.c:
#include "stm32f10x.h" // Device header
void PWM_Init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_OCInitTypeDef TIM_OCInitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
//1.配置时钟,用那个外设
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// //重映射功能
// RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //先打开AFIO,让AFIO去重映射
// GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2,ENABLE);
// GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE); //如果要使用PA15、B4、B3当作GPIO,首先需要先解除调式端口
GPIO_InitStructure.GPIO_Mode =GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//2.内部时钟配置
TIM_InternalClockConfig(TIM2);
//3.配置时基单元
TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1; //滤波频率
TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up; //计数方式
TIM_TimeBaseInitStruct.TIM_Period=20000-1; //自动重装载寄存器ARR
TIM_TimeBaseInitStruct.TIM_Prescaler=72-1; //预分频器
TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);
//TIM_ClearFlag(TIM2,TIM_FLAG_Update);
//配置输出比较初始化
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);
//4.启动定时器
TIM_Cmd(TIM2,ENABLE);
}
void PWM_SetCompare2(uint16_t Compare)
{
TIM_SetCompare2(TIM2,Compare);
}
PWM.h:
#ifndef _PWM_H
#define _PWM_H
void PWM_SetCompare2(uint16_t Compare);
void PWM_Init(void);
#endif
Sg90.c:
#include "stm32f10x.h" // Device header
#include "PWM.h"
void Sg90_Init(void)
{
PWM_Init();
}
void Sg90_SetAngle(float Angle) //angle 角度
{
PWM_SetCompare2(Angle/180*2000+500);
}
Sg90.h:
#ifndef _SG90_H
#define _SG90_H
void Sg90_Init(void);
void Sg90_SetAngle(float Angle); //angle 角度
#endif
main.c:
#include "stm32f10x.h" // Device header
#include "OLED.h"
#include "delay.h"
#include "Sg90.h"
float i;
int main(void)
{
OLED_Init();
Sg90_Init();
Sg90_SetAngle(0);
OLED_ShowString(1,1,"Angle:");
while(1)
{
for(i=0;i<=180;i+=0.5)
{
Sg90_SetAngle(i);
OLED_ShowNum(1,7,(uint16_t)i,3);
Delay_ms(20);
}
Delay_ms(10);
for(i=0;i<=180;i+=0.5)
{
Sg90_SetAngle(180-i);
OLED_ShowNum(1,7,(uint16_t)(180-i),3);
Delay_ms(20);
}
}
}
五、PWM驱动直流电机
1、TB6612芯片模块
驱动直流电机使用的是TB6612芯片模块,TB6612是一款双路H桥型的直流电机驱动芯片,可以驱动两个直流电机并且控制其转速和方向。
硬件电路和功能:
2、完整代码:
接线图:
PWM.c:
#include "stm32f10x.h" // Device header
void PWM_Init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_OCInitTypeDef TIM_OCInitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
//1.配置时钟,用那个外设
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// //重映射功能
// RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //先打开AFIO,让AFIO去重映射
// GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2,ENABLE);
// GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE); //如果要使用PA15、B4、B3当作GPIO,首先需要先解除调式端口
GPIO_InitStructure.GPIO_Mode =GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//2.内部时钟配置
TIM_InternalClockConfig(TIM2);
//3.配置时基单元
TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1; //滤波频率
TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up; //计数方式
TIM_TimeBaseInitStruct.TIM_Period=100-1; //自动重装载寄存器ARR
TIM_TimeBaseInitStruct.TIM_Prescaler=18-1; //预分频器
TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);
//TIM_ClearFlag(TIM2,TIM_FLAG_Update);
//配置输出比较初始化
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);
//4.启动定时器
TIM_Cmd(TIM2,ENABLE);
}
void PWM_SetCompare3(uint16_t Compare)
{
TIM_SetCompare3(TIM2,Compare);
}
PWM.h:
#ifndef _PWM_H
#define _PWM_H
void PWM_SetCompare3(uint16_t Compare);
void PWM_Init(void);
#endif
Motor.c:
#include "stm32f10x.h" // Device header
#include "PWM.h"
void Motor_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure; //如果有错误,就把这个函数放在最前面
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
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(int16_t Speed)
{
if(Speed>=0)
{
GPIO_SetBits(GPIOA,GPIO_Pin_4);
GPIO_ResetBits(GPIOA,GPIO_Pin_5);
PWM_SetCompare3(Speed);
}
else{
GPIO_SetBits(GPIOA,GPIO_Pin_5);
GPIO_ResetBits(GPIOA,GPIO_Pin_4);
PWM_SetCompare3(-Speed);
}
}
Motor.h:
#ifndef _PWM_H
#define _PWM_H
void Motor_Init(void);
void Motor_SetSpeed(int16_t Speed);
#endif
main.c:
#include "stm32f10x.h" // Device header
#include "OLED.h"
#include "delay.h"
#include "Motor.h"
uint16_t i;
int main(void)
{
OLED_Init();
Motor_Init();
Motor_SetSpeed(0);
while(1)
{
for(i=0;i<=100;i++)
{
Motor_SetSpeed(i);
Delay_ms(10);
}
Delay_ms(100);
for(i=0;i<=100;i++)
{
Motor_SetSpeed(100-i);
Delay_ms(10);
}
}
}