江科大STM32视频学习笔记:PWM驱动LED呼吸灯、舵机和直流电机
目录
一、PWM驱动LED呼吸灯(灯接在PA0)
1、PWM波和GPIO的对应关系参考引脚定义表
2、计数器的计算
3、TIM输出PWM波使用步骤编辑
4、代码
(1)输出化比较单元
(2) PWM.c
(3)main.c
5、重映射更换成PA15亮灯
二、PWM驱动舵机(舵机接在PA1、按键在PB1)
1、电路图
2、参数计算
3、代码
(1) PWM.c修改的地方
(2)PWM.c完整代码
(3)Servo.c
(4)main.c
三、PWM驱动直流电机
1、原理图
2、代码
(1)PWM.c中改的地方
(2)Motor.c
(3)main.c
四、基础知识
一、PWM驱动LED呼吸灯(灯接在PA0)
1、PWM波和GPIO的对应关系参考引脚定义表
(1)TIM2的引脚复用在了PA0引脚上,故要是要TIM2的OC1也就是CH1通道,输出PWM,那它只能在PA0的引脚上输出,而不能任意选择引脚输出
(2)同样,若选择 TIM2的是CH2通道,那只能在PA1端口输出,以此类推
(3)但还是有一次重映射的机会,可以在其他引脚上输出
2、计数器的计算
PWM的频率=计数器的更新频率
若要产生一个频率为1kHz,占空比为50%,分辨率为1%的PWM波形
带入公式:72M/(PSC+1)/(ARR+1)=1000
CCR/(ARR+1) = 50%
1/(ARR+1)=1%
则计算出:ARR+1=100,CCR=50,PSC+1=720
则等以1kHZ的频率闪烁,但是我们看不出来
TIM_TimeBaseInitStructure.TIM_Period = 100 – 1; //ARR
TIM_TimeBaseInitStructure.TIM_Prescaler = 720 – 1; //PSC
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
3、TIM输出PWM波使用步骤
第一步:RCC开启时钟,打开TIM外设和GPIO外设的时钟
第二步:配置时基单元,包括时基单元和时基单元前的时钟源选择
第三步:配置输出比较单元,里面包括CCR的值、输出比较模式、极性选择、输出使能(结构体配置)
第四步:配置GPIO口,初始化为复用推挽输出的配置
第五步:运行控制,启动计数器,就能输出PWM波
4、代码
(1)输出化比较单元
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的值0-ffff
TIM_OC1Init(TIM2, &TIM_OCInitStructure);//用结构体初始化
(2) 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);
// GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);
// GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
//对于普通的开漏推挽输出,引脚的控制权是来自于输出数据寄存器,
//若想要定时器控制引脚,需要使用复用开漏/推挽输出的模式
//此时,输出数据寄存器将被断开,输出控制权将转移到片上外设
//本实验中片上外设引脚连接的就是TIM2的CH1通道,所以只有把GPIO设置程复用推挽输出
//引脚的控制权才能交给片上外设,PWM波形才能通过引脚输出
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);
//时基单元初始化
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);
//初始化输出比较单元
//PA0口对应1通道
TIM_OCInitTypeDef TIM_OCInitStructure;
//不想把所有成员都列出来赋一个初始值,就用TIM_OCStructInit初始化值,防止出现奇怪的错误
TIM_OCStructInit(&TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//PWM模式1和PWM模式2
//输出比较极性:
//TIM_OCPolarity_High高级性,就是极性不翻转,REF波形直接输出,或者说有效电平是高电平
//TIM_OCPolarity_Low低极性,就是REF波形电平取反,有效电平为低电平
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 0; //设置CCR的值0-ffff
TIM_OC1Init(TIM2, &TIM_OCInitStructure);//用结构体初始化
TIM_Cmd(TIM2, ENABLE);//启动定时器
}
void PWM_SetCompare1(uint16_t Compare)
{
TIM_SetCompare1(TIM2, Compare);//设置占空比
}
(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);
}
}
}
5、重映射更换成PA15亮灯
想要把PA0改成PA15,就可以选重映射方式1或者完全重映射
在"GPIO_InitTypeDef GPIO_InitStructure;"前面添加
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); (1)
GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2,ENABLE); (2)
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE) (3)
PA15是调试端口,所以要解除调试端口 GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE)
改变引脚:
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
改为
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;
此时PA0的灯不亮,而PA15的灯亮了
二、PWM驱动舵机(舵机接在PA1、按键在PB1)
1、电路图
2、参数计算
3、代码
(1) PWM.c修改的地方
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
TIM_TimeBaseInitStructure.TIM_Period = 20000 - 1; //ARR
TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1; //PSC
void PWM_SetCompare2(uint16_t Compare)//从通道1改为使用通道2
{
TIM_SetCompare2(TIM2, Compare);
}
(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_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
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);
TIM_Cmd(TIM2, ENABLE);
}
void PWM_SetCompare2(uint16_t Compare)//使用通道2
{
TIM_SetCompare2(TIM2, Compare);
}
(3)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);
}
(4)main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Servo.h"
#include "Key.h"
uint8_t KeyNum;
float Angle;
int main(void)
{
OLED_Init();
Servo_Init();
Key_Init();
OLED_ShowString(1, 1, "Angle:");
while (1)
{
KeyNum = Key_GetNum();
if (KeyNum == 1)
{
Angle += 30;
if (Angle > 180)
{
Angle = 0;
}
}
Servo_SetAngle(Angle);
OLED_ShowNum(1, 7, Angle, 3);
}
}
三、PWM驱动直流电机
1、原理图
红色的是TB6612电机驱动模块 连接电机的两根线不分正反,对调知识反过来转动,AIN1和AIN2是方向控制,任意连接两个GPIO就行(此处接PA4、PA5),PWMA是速度控制,需要接PWM的输出脚(PA2,PA2对应的是TIM2的通道3,到时候初始化通道三即可)
2、代码
(1)PWM.c中改的地方
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;//(0改为2)
TIM_OC3Init(TIM2, &TIM_OCInitStructure);//(1改为3)
TIM_TimeBaseInitStructure.TIM_Period = 1000 - 1; //ARR
TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1; //PSC
//改频率,将1kHZ改为20kHz
TIM_TimeBaseInitStructure.TIM_Period = 100 - 1; //ARR
TIM_TimeBaseInitStructure.TIM_Prescaler = 36 - 1; //PSC
void PWM_SetCompare3(uint16_t Compare)//(1改为3)
{
TIM_SetCompare3(TIM2, Compare);
}
(2)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);
}
}
(3)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);
}
}
四、基础知识
void TIM_CtrlPWMOutputs(TIM_TypeDef* TIMx, FunctionalState NewState)
仅高级定时器使用,在使用高级定时器输出PWM时,需要调用这个函数,使能主输出,否则PWM将不能正常输出
void TIM_CtrlPWMOutputs(TIM_TypeDef* TIMx, FunctionalState NewState)
仅高级定时器使用,在使用高级定时器输出PWM时,需要调用这个函数,使能主输出,否则PWM将不能正常输出