STM32输出PWM驱动直流电机

        本文是主控芯片采用STM32F103C8T6,电机驱动用L298N,驱动直流有刷电机的学习笔记。

L298N模块介绍

        L298N是比较常用的电机驱动模块,可以同时驱动两个电机,控制正反转和调速,其接口说明如下:

①、输出A和输出B的两个引脚直接连两个电机的GND和VCC即可。

②、板载5V使能需要接高电平,驱动板才能工作,可以将其接到按键以控制电机的启停。

③、供电部分:GND接单片机的GND,可以选择12V或5V供电。

④、通道使能部分:两个通道使能引脚分别控制两个电机,一般接单片机的PWM输出,ENA控制输出A,ENB控制输出B,如果只是想让电机转起来,不用PWM的话,通道使能引脚直接接高电平即可。

⑤、逻辑输入部分:这部分一共有四个引脚,IN1、IN2、IN3、IN4,这四个引脚用于控制电机正反转和制动,IN1和IN2控制输出A,IN3和IN4控制输出B,直接接到单片机的四个IO口即可,控制IO口输出高低电平即可控制电机正反转,具体逻辑如下:

ENA/ENBIN1/IN3IN2/IN4电机状态
低电平任意电平任意电平停止
PWM或高电平高电平低电平正转
PWM或高电平低电平低电平制动
PWM或高电平低电平高电平反转
PWM或高电平高电平高电平制动

直流有刷电机

        直流有刷电机(BDC)是内含电刷装置的将直流电能转换成机械能的电动机,直流有刷电机由定子、转子、电刷和换向器这四个结构组成。拆解后的直流有刷电机如图所示:

下面我们来分析一下图中各个结构的作用:

① 定子:用于产生固定的磁场,通常由永磁体或电磁绕组制成。

② 转子:由一个或多个铜线绕组构成,通电后可以在磁场中受力运动。

③ 电刷:将外部电流输入到转子绕组上。

④ 换向器:改变转子绕组中电流的流向,是电机可以持续转动的关键结构。

        在对电机输出扭矩(即输出的力)有高要求的场景,我们会给直流有刷电机加上减速齿轮组,以增大输出扭矩,这一类电机就是直流有刷减速电机。其原理为:在电机输出功率一定的条件下,转速和扭矩成反比例关系,我们通过减速齿轮组降低电机的转速,即可提高电机的输出扭矩。

左手定则

        直流有刷电机原理的本质是:通电导线在磁场中受力运动。左手定则是判断通电导线处于磁场中时,所受安培力 F (或运动)的方向、磁感应强度 B的方向以及通电导体棒的电流 I 三者方向之间的关系的定律。左手定则的具体内容:将左手四指并拢伸直,使拇指与其他四指在平面内垂直,手掌方向代表磁场的方向(从 N 级到 S 级),四指代表电流的方向(从正极到负极),那拇指所指的方向就是受力的方向。我们可以借助示意图对左手定则进行理解,示意图如下:

 直流有刷电机基本工作原理

        为了方便分析,我们先把直流有刷电机的结构简化, A 和 B 代表的是两块电刷,C 和 D 代表两片换向器,E 代表简化后的单匝转子线圈,S 和 N 为两块定子磁极,磁场方向从 N 极到 S 极。 简化后的结构如图:

         直流有刷电机的工作原理可分为以下几步:第一步,电流从电池正极流出,进入电刷 A,经过换向器 C,输入到转子线圈 E 的左侧导线,此时已经知道电流的方向(从 a1 到 a2)和磁场的方向(N 极到 S 极),根据左手定则,可以判断出线圈 E 左侧导线的受力 F1 是垂直于导线向上的。第二步,电流从转子线圈 E 的左侧流向右侧(从 b2 到 b1),经过换向器 D 和电刷 B,流回电池负极,同理,我们根据左手定则,就可以判断出线圈 E 右侧导线的受力 F2 是垂直于导线向下的。结合转子线圈 E 左右两侧导线的受力情况,可得知线圈会沿顺时针方向转动,如下图所示:

         第三步,当转子线圈 E 沿顺时针方向转过一定角度(在简化模型上就是 90°,实际应用中并不一定),换向器 C 和 D 的位置会改变,此时电刷和换向器的接触关系就改变了,电流的方向发生了变化,此时电流从电刷 A 流入换向器 D,经过转子线圈E,再流出到换向器 C 和电刷 B。这个时候,虽然转子线圈 E 中电流的方向发生了改变,但是从转子线圈 E 的整体受力来看,左右两侧导线受力的方向并没有改变,所以转子线圈 E 会继续沿顺时针方向旋转下去,如下图所示:

        注意:单匝转子线圈运动到磁场的不同位置时,其受力是不均匀的,因此,在实际的应用中,转子通常都会有三匝或以上的线圈,它的受力情况会复杂很多,但是基本的原理是相通的。 

实验代码

pwm原理

         图中,我们假定定时器工作在向上计数PWM 模式,当 CNT 值小于 CCRx 的时候,IO 输出低电平(0),当 CNT 值大于等于 CCRx 的时候,IO 输出高电平(1),当 CNT 达到 ARR 值的时候,重新归零,然后重新向上计数,依次循环。改变 CCRx 的值,就可以改变 PWM 输出的占空比,改变 ARR 的值,就可以改变 PWM输出的频率,这就是 PWM 输出的原理。

PWM模式1和PWM模式2

        PWM模式1:CNT 值小于 CCRx 的时候为有效电平,否则为无效电平。

        PWM模式2:CNT 值大于 CCRx 的时候为有效电平,否则为无效电平。

PWM配置流程

①、使能定时器和相关IO口时钟

②、初始化PWM输出通道对应的IO口为复用推挽输出

③、初始化定时器

④、初始化PWM输出比较参数

⑤、使能预装载,使能定时器

电机配置代码如下:

#include "motor_ctl.h"

//PWM输出初始化,采用定时器3的通道4,在PB1
//arr:自动重装值
//psc:时钟预分频数
void TIM3_PWM_Init(u16 arr,u16 psc)
{  
	GPIO_InitTypeDef GPIO_InitStructure; //定义一个引脚初始化的结构体
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStrue; //定义一个定时中断的结构体	
	TIM_OCInitTypeDef TIM_OCInitTypeStrue; //定义一个PWM输出的结构体
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能GPIOB时钟,GPIOB挂载在APB2时钟下,在STM32中使用IO口前都要使能对应时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //使能通用定时器3时钟
	
	//PWM输出引脚
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;//引脚PB1
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; //复用推挽输出模式,定时器功能为PB1引脚复用功能
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; //定义该引脚输出速度为50MHZ
	GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化引脚PB1
	//定时器结构体初始化
	TIM_TimeBaseInitStrue.TIM_Period=arr; //计数模式为向上计数时,定时器从0开始计数,计数超过到arr时触发定时中断服务函数
	TIM_TimeBaseInitStrue.TIM_Prescaler=psc; //预分频系数,决定每一个计数的时长
	TIM_TimeBaseInitStrue.TIM_CounterMode=TIM_CounterMode_Up; //计数模式:向上计数
	TIM_TimeBaseInitStrue.TIM_ClockDivision=TIM_CKD_DIV1; //一般不使用,默认TIM_CKD_DIV1
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStrue); //根据TIM_TimeBaseInitStrue的参数初始化定时器TIM3
	
	TIM_OCInitTypeStrue.TIM_OCMode=TIM_OCMode_PWM1; //PWM模式1,当定时器计数小于TIM_Pulse时,定时器对应IO输出有效电平
	TIM_OCInitTypeStrue.TIM_OCPolarity=TIM_OCNPolarity_High; //输出有效电平为高电平
	TIM_OCInitTypeStrue.TIM_OutputState=TIM_OutputState_Enable; //使能PWM输出
	TIM_OCInitTypeStrue.TIM_Pulse = 0; //设置待装入捕获比较寄存器的脉冲值
	TIM_OC4Init(TIM3, &TIM_OCInitTypeStrue); //根TIM_OCInitTypeStrue参数初始化定时器3通道4

	TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Disable); //CH4预装载 失能后改变TIM_Pulse(即PWM)的值立刻生效,使能则下个周期生效
	
	TIM_ARRPreloadConfig(TIM3, ENABLE); //TIM3预装载使能,使能后改变arr立即生效,不使能则下个周期生效
	
	TIM_Cmd(TIM3, ENABLE); //使能定时器TIM3
}
//设置L298N的IN1,IN2引脚为PB12和PB13,控制电机转向
void Dir_GPIO_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure; //定义一个引脚初始化的结构体
	
	
	//L298N控制方向引脚
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_12|GPIO_Pin_13; 
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP; //引脚输入输出模式为推挽输出模式
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; //引脚输出速度为50MHZ
	GPIO_Init(GPIOB, &GPIO_InitStructure); //根据上面设置好的GPIO_InitStructure参数,初始化引脚
	
	GPIO_ResetBits(GPIOB, GPIO_Pin_12|GPIO_Pin_13); //初始化设置引脚低电平

	
}
//电机初始化
void Motor_Init(void)
{
	TIM3_PWM_Init(7199,0);//pwm初始化
	Dir_GPIO_Init();//方向控制引脚初始化
}
//电机方向控制,pwm为正就是正转,为负就是反转
void Dir_Ctl(int pwm)
{
	if(pwm>=0)//pwm>=0 (BIN2, BIN2)=(0, 1) 正转 顺时针
  {
		PBout(13)=0; //BIN2=0
		PBout(12)=1; //BIN1=1
		TIM_SetCompare4(TIM3, pwm);//设置PWM值
  }
  else if(pwm<0)//pwm<0 (BIN2, BIN1)=(1, 0) 反转 逆时针
  {
		PBout(13)=1; //BIN2=1
		PBout(12)=0; //BIN1=0
		TIM_SetCompare4(TIM3, -pwm);//设置PWM值
  }
}

        PB1为定时器3的通道4,用作PWM输出,接到L298N的通道A使能,PB12和PB13为普通IO口,控制电机正反转,接到L298N的IN1和IN2。

main.c测试函数如下:

#include "led.h"
#include "delay.h"
#include "sys.h"
#include "motor_ctl.h"
int PWM=0;          //PWM控制变量
int Step=500;   //速度渐变速率 相当于加速度
 int main(void)
 {		
	delay_init();	    	 //延时函数初始化	  
	Motor_Init();		  	//初始化电机
   	while(1)
	{
            //速度循环变化 
				if(PWM<=-7000)Step=500;      //减速到反转最大速度后加速
				else if(PWM>=7000)Step=-500; //加速到正转最大速度后减速
				PWM=PWM+Step; //速度渐变
				Dir_Ctl(PWM);  //设置PWM
	} 

}

关于MOE主输出使能

        上述定时器输出PWM配置针对的是通用定时器,如果要用高级定时器输出PWM还要使能刹车和死区寄存器(TIM1_BDTR)的 MOE 位,以使能整个 OCx(即 PWM)输出。

TIM_CtrlPWMOutputs(TIM1,ENABLE);    //MOE 主输出使能    

        如果不是高级定时器不能加这句话,会卡在这里。

  • 30
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对于使用STM32F103C8T6驱动直流电机,您可以使用PWM信号来控制电机的速度。下面是一个简单的步骤来实现这个功能: 1. 初始化TIM定时器:选择一个合适的定时器(如TIM2),配置定时器的时钟和计数周期,并启动定时器。 2. 配置GPIO引脚:选择一个合适的GPIO引脚作为PWM输出引脚,并将其设置为复用功能模式。 3. 配置PWM模式:选择PWM模式,设置PWM的频率和占空比。 4. 启动PWM输出:启动PWM输出,使定时器开始生成PWM信号。 下面是一个示例代码,用于配置TIM2为PWM输出引脚PA0控制直流电机的速度: ```c #include "stm32f10x.h" void PWM_init(void) { // 使能TIM2时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // 初始化TIM2定时器 TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct; TIM_TimeBaseInitStruct.TIM_Period = 1000; // 设置计数周期为1000 TIM_TimeBaseInitStruct.TIM_Prescaler = 720; // 设置时钟预分频为720(72MHz / 720 = 100kHz) TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStruct); // 配置TIM2通道1为PWM模式 TIM_OCInitTypeDef TIM_OCInitStruct; TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStruct.TIM_Pulse = 500; // 设置占空比为50%(1000 * 50% = 500) TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OC1Init(TIM2, &TIM_OCInitStruct); // 配置GPIO引脚为复用功能模式 GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStruct); // 启动TIM2定时器 TIM_Cmd(TIM2, ENABLE); } int main(void) { // 初始化PWM PWM_init(); while (1) { // 可以在这里通过改变占空比来改变电机的速度 TIM2->CCR1 = 250; // 设置占空比为25%(1000 * 25% = 250) } } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值