江科大STM32学习记录
一、输出比较理论介绍
输出比较可以通过比较CNT与CCR寄存器值的关系,来对输出电平进行置1、置0或翻转的操作,用于输出一定频率和占空比的PWM波形每个高级定时器和通用定时器都拥有4个输出,比较通道高级定时器的前3个通道额外拥有死区生成和互补输出的功能输出比较的作用主要是生成PWM(脉冲宽度调制):
1.PWM作用原理
PWM是一种通过快速开启和关断,来生成所需要的电压调制技术,在电力电子技术中,PWM主要通过在想要的调制波上添加载波,调制波大于载波,开关打开,调制波小于载波,开关关断。而在STM32中,主要通过输出比较来实现,比较的两个参数为:CNT、CCR
2.基本参数
PWM通常包括频率、分辨率和占空比三个参数。
(1)频率:主要用于决定脉冲开启的频次,频率越高,生成的调制波效果越好
(2)分辨率:PWM可调节的最小参数,即占空比变化的步距
(3)占空比:PWM的最主要参数,通过在一个周期内改变开启时间和关断时间所占的比例调节占空比,其计算方式为:占空比=开启时间/一个周期时间
PWM参数计算:
(1)PWM频率:Freq = CK_PSC / (PSC +1) / (ARR +1)
(2)PWM占空比:Duty=CCR/(ARR+1)
(3)PWM分辨率:Reso=1/(ARR+1)
3.实现方式
输出比较STM32的实现:
基本结构:
上图中,红线为CCR,蓝线为CNT,黄线为ARR
补充:高级定时器中死区生成的作用是防止上下开关管同时导通造成电路短路
二、输出比较PWM驱动呼吸灯
初始化及占空比封装与引脚重印射相关代码:
void PWM_Init(void)
{
//设定TIM2的CH1通道口A0的GPIO
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
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);
/*
******************************注意*******************************
若要选择引脚重印射,以TIM2的CH1为例,在进行解除原来PA15引脚功能时
通常解除JTAG的复用,保留SWD的复用,否则会导致st_link无法使用
重印射GPIO代码示例:
RCC_APB2PeriphClockCmd (RCC_APB2Periph_AFIO, ENABLE) ;
GPIO_PinRemapConfig (GPIO_PartialRemap1_TIM2, ENABLE);
GPIO_PinRemapConfig (GPIO_Remap_SWJ_JTAGDisable, ENABLE) ;
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_15;
GPIO_Init(GPIOA, &GPIO_InitStructure);
*****************************************************-**********
*/
//TIM初始化
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
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 计时=(ARR-1)*(PSC-1)/主频=1S
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//重复计数器,只在高级计数器中使用
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
//输出比较通道配置
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCStructInit(&TIM_OCInitStructure); //给结构体TIM_OCInitStructure赋初始值
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //输出比较模式选择为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);
}
主函数代码:
#include "stm32f10x.h" // Device header
#include "OLED.h"
#include "LED.h"
#include "Delay.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);
}
}
}
三、输出比较PWM驱动舵机SG90
1.舵机SG90简单介绍
舵机是一种根据输入PWM信号占空比来控制输出角度的装置输入PWM信号要求:周期为20ms,高电平宽度为0.5ms~2.5ms
选取PA10引脚对应TIM1的CH2输出比较,PWM.c初始化函数为:
#include "stm32f10x.h"
//PWM信号要求:周期为20ms,高电平宽度为0.5ms~2.5ms
//周期为20ms则设置频率为50hz,设定PSC为72,ARR为20000
//高电平宽度为0.5ms~2.5ms,当CCR为500为0.5ms,CCR为2500为2.5ms
//(1)PWM频率:Freq = CK_PSC / (PSC +1) / (ARR +1)
//(2)PWM占空比:Duty=CCR/(ARR+1)
//(3)PWM分辨率:Reso=1/(ARR+1)
void PWM_Init(void)
{
// 1. GPIO初始化
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
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_10;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 2. 定时器初始化
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
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; // 72MHz/72*20000 = 50Hz = 20ms
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseInitStructure);
// 3. 输出比较配置
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;
TIM_OC3Init(TIM1, &TIM_OCInitStructure);
// 4. 使能定时器
TIM_Cmd(TIM1, ENABLE);
// 5. 特别重要:对于高级定时器TIM1,需要使能主输出
TIM_CtrlPWMOutputs(TIM1, ENABLE);
}
void PWM_SetCompare(uint16_t Compare)
{
TIM_SetCompare3(TIM1, Compare);
}
舵机配置代码为:
#include "stm32f10x.h" // Device header
#include "PWM.h"
void Servo_Init(void)
{
PWM_Init();
}
// 0 180 D Angle
// 0+500 2000+500 Angle/180*2000+500
void Servo_SetAngle(float Angle)
{
PWM_SetCompare(Angle/180*2000+500);
}
主函数设定两个按键角度增加和减少,代码为
#include "stm32f10x.h" // Device header
#include "OLED.h"
#include "Matrix_Key.h"
#include "LED.h"
#include "Delay.h"
#include "Servo.h"
uint8_t Keynum;
float Angle;
int main(void)
{
OLED_Init();
Servo_Init();
Matrix_Key_Pin_Init();
Servo_SetAngle(90);
OLED_ShowString(1,1,"Angle:");
while(1)
{
Keynum = Matrix_Key_Scan();
if(Keynum == 1)
{
Angle = Angle+30;
if(Angle > 180)
{
Angle = 180;
}
}
if(Keynum == 2)
{
Angle = Angle-30;
if(Angle < 0)
{
Angle = 0;
}
}
Servo_SetAngle(Angle);
OLED_ShowNum(1,7,Angle,3);
}
}
四、输出比较PWM驱动有刷直流电机(TB6612)
1.TB6612
TB6612是一款双路H桥型的直流电机驱动芯片,可以驱动两个直流电机并且控制其转速和方向,其引脚定义如下:
PWM配置:
#include "stm32f10x.h"
void PWM_Init(void)
{
// 1. GPIO初始化
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
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_10;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 2. 定时器初始化
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
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; // 72MHz/720 = 100kHz
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseInitStructure);
// 3. 输出比较配置
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 = 50; // 初始占空比50%
TIM_OC3Init(TIM1, &TIM_OCInitStructure);
// 4. 使能定时器
TIM_Cmd(TIM1, ENABLE);
// 5. 特别重要:对于高级定时器TIM1,需要使能主输出
TIM_CtrlPWMOutputs(TIM1, ENABLE);
}
void PWM_SetCompare(uint16_t Compare)
{
TIM_SetCompare3(TIM1, Compare);
}
电机状态函数:
#include "stm32f10x.h" // Device header
#include "PWM.h"
void Moter_Init(void)
{
PWM_Init();
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA ,&GPIO_InitStructure);
}
void Moter_SetSpeed(int8_t Speed)
{
if(Speed >= 0)
{
GPIO_SetBits(GPIOA,GPIO_Pin_8);
GPIO_ResetBits(GPIOA,GPIO_Pin_9);
PWM_SetCompare(Speed);
}
else
{
GPIO_SetBits(GPIOA,GPIO_Pin_9);
GPIO_ResetBits(GPIOA,GPIO_Pin_8);
PWM_SetCompare(-Speed);
}
}
主函数为:
#include "stm32f10x.h" // Device header
#include "OLED.h"
#include "LED.h"
#include "Delay.h"
#include "Moter.h"
#include "Matrix_Key.h"
uint8_t Keynum;
int8_t Speed;
int main(void)
{
OLED_Init();
Moter_Init();
Matrix_Key_Pin_Init();
Moter_SetSpeed(0);
OLED_ShowString(1,1,"Speed:");
while(1)
{
Keynum = Matrix_Key_Scan();
//转速增大
if(Keynum == 1)
{
Speed = Speed + 10;
if(Speed > 100)
{
Speed = 100;
}
}
//转速减小
if(Keynum == 2)
{
Speed = Speed - 10;
if(Speed < -100)
{
Speed = -100;
}
}
Moter_SetSpeed(Speed);
OLED_ShowSignedNum(1,7,Speed,3);
}
}