6-5 PWM驱动LED、舵机、直流电机


title: 6-5 PWM驱动LED、舵机、直流电机
tags:

  • STM32
    categories:
  • STM32学习

[[6-4TIM输出比较]]

PWM呼吸灯

  1. RCC开启时钟,把TIM外设和GPIO外设的时钟打开
  2. 配置时基单元,包括前面的时钟源和时基单元
  3. 配置输出比较单元
    • CCR的值、输出比较模式、极性选择、输出使能(在结构体同一配置)
  4. 配置GPIO,将PWM对应的GPIO口,初始化为复用推挽输出的配置
  5. 运行控制

库函数

  • 配置4个比较单元 TIM_OCxInit()
  • TIM_ForcedOCxConfig 用于配置强制输出模式
  • TIM_OC1PreloadConfig用于配置CCR寄存器的预装功能,也就是影子寄存器
  • TIM_SetComparex用于更改PWM占空比

代码


#include "stm32f10x.h"                  // Device header

void PWM_Init(void)
{
//前面这些初始化TIM2时钟的代码都不需要删除
	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;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;   
	//为什么输出模式选择复用推挽输出?
			//因为引脚的控制权来自于输出数据寄存器
			//如果想让定时器来控制引脚,那么就使用复用推挽输出,将控制权转移到片上外设
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;		//此处改为PIN0 
	//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);
//以上这些都是在6-2定时器就已经配置过

//  注意在结构体中有些变量是只在高级定时器才有
//  在变量前拿带有‘N’
	TIM_OCInitTypeDef TIM_OCInitStructure;  //这个结构体初始化的函数是给定时器赋初始值,防止在使用通用定时器时出现没有赋值的结构体变量导致的结构体的值出现混乱
	TIM_OCStructInit(&TIM_OCInitStructure);
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;  
	//输出比较的模式
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;  //High,选择输出的有效值是高电平
	//设置输出比较的极性
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
	//设置输出使能
	TIM_OCInitStructure.TIM_Pulse = 0;  //占空比	
	//设置CCR
	TIM_OC1Init(TIM2, &TIM_OCInitStructure);
	
	TIM_Cmd(TIM2, ENABLE);   //启动定时器,PWM波形就能通过引脚输出了
}

void PWM_SetCompare1(uint16_t Compare)
{
	TIM_SetCompare1(TIM2, Compare);   //用于更改占空比的值
}

为什么输出模式选择复用推挽输出?

因为引脚的控制权来自于输出数据寄存器
如果想让定时器来控制引脚,那么就使用复用推挽输出,将控制权转移到片上外设


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);
		}
	}
}

引脚重映射

如果我们既要使用USART2的TX引脚,又要使用TIM2的CH3引脚,此时引脚冲突了,那么我们就可以在重映射列表中寻找这个引脚的重映射对应的引脚
比方说这里的PA2可以用PB10来重映射

  • GPIO_PinRemapConfig引脚重映射配置

解除调试端口

如果想让调试端口作为普通的GPIO或者复用定时通道,需要先关闭调试复用端口
也是用GPIO_PinRemapConfig函数
这里的三个参数,就是用来解除调试端口的复用的

  1. SWJ就是SWD和JTAG这两种调试方式
  2. NoJTRST是接触JTRST引脚的复用,也就是PB4
  3. JTAGDisable是解除JTAG,也就是PA15、PB3、PB4三个端口变回GPIO,上面PA13、PA14仍为SWD的调试端口
  4. SWJ_Disable把SWD和JTAG的调试端口全部解除,此时不能使用STLINK下载程序了,只能使用串口下载

所以我们可以使用AFIO寄存器来重映射引脚

  1. 开启AFIO的时钟
  2. 选择重映射方式
  3. 解除
//开启时钟	
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
//重映射其他端口:如定时器或其他外设,才需要加这一句
GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);
//解除调试端口的代码:解除JTAG的调试端口	
	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);

PWM舵机

  • 电机驱动需要5v供电,而面包板上只有3.3v,所以需要接到STLINK的5v供电口

代码

需要输出这样的波形

#include "stm32f10x.h"                  // Device header


//大体上和上一个代码一样。只是改成了TIM2定时器
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)
{
	TIM_SetCompare2(TIM2, Compare);
}

主函数👇

#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,他们的频率是一致的,
因为使用的是同一个计数器,他们的占空比由各自CCR决定,
他们的相位,由于计数器的更新,所有的PWM同时跳变,所以他们的相位是同步的

封装函数

舵机驱动`servo

#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);   //此处为PWM对应的角度映射
	//0°时是500
	//180°时是2500
}

PWM驱动直流电机

注意:前面所配置的时钟频率都是10kHZ
而直流电机在堵转的时候会发出类似蜂鸣器的声音,原因是定时器的频率太低了
所以在此历程中,我们将时钟频率提高到了20KHz,人耳就听不到了

#include "stm32f10x.h"                  // Device header
#include "PWM.h"

void Motor_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);     //开启PA4、PA5
	
	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);
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值