PWM驱动舵机&PWM驱动直流电机

PWM驱动舵机&PWM驱动直流电机

PWM驱动舵机

  • 接线图如下:
    在这里插入图片描述
  • 复制后修改PWM驱动LED呼吸灯的PWM.c文件
    PWM.c代码:
#include "stm32f10x.h"                  // Device header

//初始化
void PWM_Init(void)
{
	//开启TIM2的时钟,TIM1(高级时钟)对频率要求高,连接在APB2;TIM2、TIM3、TIM4(通用时钟)连接在APB1
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);		//开启GPIOA的时钟
	
		
	/*GPIO初始化*/
	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);
	
	//启用STM32自带的内部时钟,系统默认配置,可以不写
	TIM_InternalClockConfig(TIM2);
	
	//配置时基单元
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	//选择分频数,TIM_CKD_DIV1表示分频数为1,即不分频
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	//选择计数器的计数模式,TIM_CounterMode_Up表示向上计数,即从0开始计数,到达预定值清零再次从零开始计数
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	/*设置自动重装器(ARR),因为预分频器设置为将7200个脉冲分频为1个脉冲,那么设置自动重装器为1000-1,
	  即1000HZ,7200*1000正好等于内部定时器频率72MHZ,此时一个脉冲信号72MHZ全部输入完毕,
	  正好过去1秒,自动重装器计时器就会清零*/
	TIM_TimeBaseInitStructure.TIM_Period = 20000-1;
	//设置预分频器(PSC),选择预分频数,7200-1表示对72MHZ进行7200次分频,即将7200个脉冲分频为1个脉冲
	TIM_TimeBaseInitStructure.TIM_Prescaler = 72-1;
	//重复计数器,高级计数器(TIM1)才有,本计数器TIM2用不到
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
	
	TIM_OCInitTypeDef TIM_OCInitTypeStructure;
	/*
	TIM_OCStructInit();将输出比较结构体函数赋一个默认值,因为在使用输出比较函数配置一个通用定时器(如TIM2),
	TIM_OC1Init结构体函数中有很多参数用不到(这些用不到的参数在高级定时器配置中要用到),不赋一个默认值,会出
	现一些莫名其妙的问题
	*/
	TIM_OCStructInit(&TIM_OCInitTypeStructure);
	//输出比较模式
	TIM_OCInitTypeStructure.TIM_OCMode = TIM_OCMode_PWM1;
	//极性,TIM_OCPolarity_High表示有限电平为高电平
	TIM_OCInitTypeStructure.TIM_OCPolarity = TIM_OCPolarity_High;
	TIM_OCInitTypeStructure.TIM_OutputState = TIM_OutputState_Enable;
	/*
	设置CCR的值,要结合自动重装器(ARR)和预分频器(PSC)的值来使用,当ARR=100-1,PSC=720-1,TIM_Pulse=50,
	可以输出一个频率是1000HZ,占空比为50%,分辨率为1%的PWM波形
	*/
	TIM_OCInitTypeStructure.TIM_Pulse = 0;
	//初始化输出比较函数,由于使用的是PA1,它对应的输出比较口是CH2 (引脚定义图查得)
	TIM_OC2Init(TIM2,&TIM_OCInitTypeStructure);
	//启动定时器
	TIM_Cmd(TIM2, ENABLE);


}

//独立设置CCR的值
void PWM_SetCompare2(uint16_t Compare)
{
	TIM_SetCompare2(TIM2,Compare);
}


与上一个工程PWM驱动LED呼吸灯的代码不同的是:
①根据接线图,舵机的PWM橙色线要接在GPIOA的PA1口,此口对应定时器通道是CH2(根据引脚定义图得到),所以要将初始化GPIO相应引脚口的代码修改为GPIO_Pin_1,将初试化输出比较函数修改为 CH2,即TIM_OC2Init(TIM2,&TIM_OCInitTypeStructure);
②将对应的设置CCR的函数修改名称为PWM_Compare2,将该函数里面调用的函数Tim_Compare1();修改为Tim_Compare2(); (Tim_Compare2();是官方函数,其中2对应的是CH2通道).

  • 新建一个Servo.c文件和Servo.h文件
    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);

}

值得注意的是:
①在Servo_Init();初始化函数中调用PWM.c文件中的PWM_Init();初始化函数来进行舵机函数的初始化
②在设置舵机角度的函数Servo_SetAngle(float Angle);调用了PWM.c的PWM_SetCompare2();来设置角度.
③为什么PWM_SetCompare2();的参数要写成Angle/180*2000 + 500?
答:驱动舵机的关键是将PWM波形变化为舵机所需要的波形,如图:
在这里插入图片描述

第一张图片显示舵机要求的周期是20ms,那么频率就是50hz,高电平宽度为0.5ms~2.5ms,也就是占空比的时间,最小占空比:0.5ms / 20ms = 2.5%,最大占空比:2.5ms / 20ms = 12.5%,根据公式:最小CCR:CCR_min = 2.5% × 20,000 = 500,最大CCR:CCR_max = 12.5% × 20,000 = 2,500。经过一系列变换可以变成我们熟知的0~180范围. ④因为PSC和ARR的值已经写死了,所以在使用PWM_SetCompare2();函数传参时也就在传输CCR的值,而CCR的值与占空比直接相关,所以舵机的角度是通过改变PWM波形的占空比来实现的.

main.c代码:

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "PWM.h"
#include "Key.h"
#include "Servo.h"

uint8_t KeyNum;
float Angle = 0;

int main(void)
{
	/*模块初始化*/
	OLED_Init();		//OLED初始化
	PWM_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驱动直流电机

  • 接线图如图:
    在这里插入图片描述
  • 复制后修改PWM驱动LED呼吸灯的PWM.c文件
    PWM.c代码:
#include "stm32f10x.h"                  // Device header

//初始化
void PWM_Init(void)
{
	//开启TIM2的时钟,TIM1(高级时钟)对频率要求高,连接在APB2;TIM2、TIM3、TIM4(通用时钟)连接在APB1
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);		//开启GPIOA的时钟

	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	//使用复用推挽输出
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	//启用STM32自带的内部时钟,系统默认配置,可以不写
	TIM_InternalClockConfig(TIM2);
	
	//配置时基单元
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	//选择分频数,TIM_CKD_DIV1表示分频数为1,即不分频
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	//选择计数器的计数模式,TIM_CounterMode_Up表示向上计数,即从0开始计数,到达预定值清零再次从零开始计数
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	/*设置自动重装器(ARR),因为预分频器设置为将7200个脉冲分频为1个脉冲,那么设置自动重装器为1000-1,
	  即1000HZ,7200*1000正好等于内部定时器频率72MHZ,此时一个脉冲信号72MHZ全部输入完毕,
	  正好过去1秒,自动重装器计时器就会清零*/
	TIM_TimeBaseInitStructure.TIM_Period = 100-1;
	//设置预分频器(PSC),选择预分频数,7200-1表示对72MHZ进行7200次分频,即将7200个脉冲分频为1个脉冲
	TIM_TimeBaseInitStructure.TIM_Prescaler = 36-1;
	//重复计数器,高级计数器(TIM1)才有,本计数器TIM2用不到
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
	
	TIM_OCInitTypeDef TIM_OCInitTypeStructure;
	/*
	TIM_OCStructInit();将输出比较结构体函数赋一个默认值,因为在使用输出比较函数配置一个通用定时器(如TIM2),
	TIM_OC1Init结构体函数中有很多参数用不到(这些用不到的参数在高级定时器配置中要用到),不赋一个默认值,会出
	现一些莫名其妙的问题
	*/
	TIM_OCStructInit(&TIM_OCInitTypeStructure);
	//输出比较模式
	TIM_OCInitTypeStructure.TIM_OCMode = TIM_OCMode_PWM1;
	//极性,TIM_OCPolarity_High表示有限电平为高电平
	TIM_OCInitTypeStructure.TIM_OCPolarity = TIM_OCPolarity_High;
	TIM_OCInitTypeStructure.TIM_OutputState = TIM_OutputState_Enable;
	/*
	设置CCR的值,要结合自动重装器(ARR)和预分频器(PSC)的值来使用,当ARR=100-1,PSC=720-1,TIM_Pulse=50,
	可以输出一个频率是1000HZ,占空比为50%,分辨率为1%的PWM波形
	*/
	TIM_OCInitTypeStructure.TIM_Pulse = 0;
	//初始化输出比较函数,由于使用的是PA0口,它对应的输出比较口是CH1(根据引脚定义图查得)
	TIM_OC3Init(TIM2,&TIM_OCInitTypeStructure);
	//启动定时器
	TIM_Cmd(TIM2, ENABLE);


}

//独立设置CCR的值
void PWM_Compare3(uint16_t Compare)
{
	TIM_SetCompare3(TIM2,Compare);
}


值得注意的是:
①接线图红色芯片是TB6612电机驱动模块,VM要接在STLink的5v引脚,AO1和AO2在连接电机时不分正反,AIN1和AIN2是控制引脚.
②因为电机驱动模块的PWMA是接在PA2引脚上的,所以在初始化GPIO参数的引脚一栏中应该填上GPIO_Pin_2参数.又因为PA2对应的定时器通道为CH3,所以初始化输出比较函数应该为TIM_OC3Init(TIM2,&TIM_OCInitTypeStructure);
所以在设置CCR值的函数void PWM_Compare3(uint16_t Compare);应该调用官方函数TIM_SetCompare3(TIM2,Compare);

  • 建立Motor.c和Motor.h文件:
    Motor.c代码:
#include "stm32f10x.h"                  // Device header
#include "PWM.h"

void Motor_Init(void)
{
	/*开启时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);		//开启GPIOA的时钟
	
	/*GPIO初始化*/
	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);						//将PA1和PA2引脚初始化为推挽输出
	
	PWM_Init();
}


//设置电机转速
void Motor_SetSpeed(uint8_t Speed)
{	
	//正转
	if(Speed >= 0)
	{
		GPIO_SetBits(GPIOA,GPIO_Pin_4);
		GPIO_ResetBits(GPIOA,GPIO_Pin_5);
		PWM_Compare3(Speed);
	}
	else
	{
		GPIO_ResetBits(GPIOA,GPIO_Pin_4);
		GPIO_SetBits(GPIOA,GPIO_Pin_5);
		//SetCompare3函数必须传入正数
		PWM_Compare3(-Speed);
	}
	
}

值得注意的是:
①控制电机正反转的是电机驱动模块的AIN1和AIN2,根据接线图连接到了STM32的PA4和PA5口,所以在初始化Motor函数中初始化GPIO的这两个引脚.
为什么在设置电机转速的函数里Speed要以100为分界线?
答:占空比 = CCR / (ARR + 1)​,在PWM.c文件里ARR值为100-1,所以:当你设置 CCR = 50时,占空比 = 50 / 100 = ​​50%​​。当你设置 CCR = 100时,占空比 = 100 / 100 = ​​100%​​(全速运转).

main.c代码:

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "PWM.h"
#include "Motor.h"
#include "Key.h"

int8_t Speed;
uint8_t KeyNum;
int main(void)
{
	/*模块初始化*/
	OLED_Init();		//OLED初始化
	Motor_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);
		
	}
}

### STM32 PWM信号生成与应用 STM32 微控制器通过其定时器模块可以生成 PWM(脉宽调制)信号,这种信号广泛应用于驱动舵机直流电机PWM 信号的特性包括频率、占空比等参数,可以通过配置定时器来调整这些参数以满足不同应用场景的需求。 #### 驱动舵机PWM 设置 舵机通常需要特定频率的 PWM 信号进行控制,常见的频率为50Hz。在这个频率下,一个完整的周期是20毫秒。对于大多数标准舵机来说,1.5毫秒的高电平持续时间对应于中间位置,而范围则从大约1毫秒到2毫秒不等,分别代表左转和右转的最大角度。 要使用 STM32 生成这样的 PWM 信号,可以配置定时器以达到所需的周期,并设置合适的比较值来改变占空比。以下是一个简化的示例代码片段,用于初始化 TIM3 通道1来产生适用于舵机控制的 PWM 信号: ```c #include "stm32f10x.h" // Device header void PWM_Init(void) { RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; // PB0 - TIM3 CH3 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_TimeBaseStructInit(&TIM_TimeBaseStructure); TIM_TimeBaseStructure.TIM_Prescaler = 72 - 1; // Prescaler = 72-1 for 1MHz timer clock TIM_TimeBaseStructure.TIM_Period = 20000 - 1; // Period = 20000-1 for 20ms period (50Hz) TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); TIM_OCInitTypeDef TIM_OCInitStructure; TIM_OCStructInit(&TIM_OCInitStructure); TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse = 1500; // Initial pulse width of 1.5ms TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OC3Init(TIM3, &TIM_OCInitStructure); TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable); TIM_ARRPreloadConfig(TIM3, ENABLE); TIM_Cmd(TIM3, ENABLE); } int main(void) { PWM_Init(); while (1) { // 可以在此处添加逻辑来动态调整TIM_OCInitStructure.TIM_Pulse // 从而改变输出PWM的占空比,进而控制舵机的角度 } } ``` #### 驱动直流电机PWM 设置 对于直流电机PWM 主要用来调节速度。通过改变占空比,可以在电机上获得不同的平均电压,这直接影响了电机的速度。同样地,我们可以利用 STM32 的定时器功能来实现这一点。下面给出的是基于上述 `PWM_Init` 函数基础上修改后的函数,用以支持直流电机的速度调节: ```c // 假设Motor_SetSpeed函数接收-100至+100之间的整数值作为速度设定 void Motor_SetSpeed(int8_t speed) { uint16_t pulseWidth; if(speed > 100) speed = 100; else if(speed < -100) speed = -100; // 将速度转换成相应的脉冲宽度(假设最大脉冲宽度为2000us) pulseWidth = ((uint16_t)(speed + 100)) * 10; // 简单线性映射 TIM_SetCompare3(TIM3, pulseWidth); // 更新比较寄存器值 } // 在main循环中调用Motor_SetSpeed函数 ... Motor_SetSpeed(Speed); // Speed变量由按键或其他方式更新 ... ``` 以上提供的代码仅作为起点,在实际项目中可能还需要考虑更多细节,比如确保正确的引脚配置、处理方向控制以及保护机制等。此外,根据所使用的具体型号(如STM32F1系列),可能需要对时钟配置和其他外设初始化步骤做出相应调整。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

必胜的思想钢印

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值