当前位置: 代码网 > it编程>软件设计>软件测试 > stm32平衡小车--(1)JGB-520减速电机+tb6612(附测试代码)

stm32平衡小车--(1)JGB-520减速电机+tb6612(附测试代码)

2024年08月06日 软件测试 我要评论
那么转一圈会计数器值为30*4,如果我设置重装载值为120,那是不是电机每转一圈定时器就会溢出一次,我们再在定时器的溢出中断中加上一个计数器cnt,每次进中断,cnt的值就++,那在采样的时候计数器的值就为:cnt*重装载值+计数器的值。电机每转动一圈,他们会产生相差九十度的相同数量的脉冲,然后会有一个计数器去捕获脉冲的上升沿和下降沿,然后每隔一段时间去读取计数器的值(转动的圈数=计数器的数值/一圈产生的脉冲数),这样就能算出这一段时间转了多少圈,从而算出转速。当AIN或BIN产生电势差,电机就会转动。

1、jgb-520编码器减速直流电机

                                   

编码器

这是我用的电机,红色框框中的就是编码器

这是官方给出的,但我们只需要知道这个编码器可以通过电机的转动产生一定的脉冲,从而通过脉冲测出电机的转速和转动方向。后面我们会介绍这个。

jgb-520电机

这种电机一般有6个引脚,有的引脚会与图中顺序不同(我的就是┭┮﹏┭┮)但只要连线正确就行。

这么大一个电机,我们要怎么让它动起来呢?没错,我们肯定需要一个驱动模块——tb6612

2、tb6612电机驱动模块

就是长这个样子:

                                       

这是它的引脚:

                            

3、电机的驱动

tb6612和电机的连接

tb6612c8t6
stby高电平(+3.3v)
ain1a电机输入端1(接板子)
ain2a电机输入端2(接板子)
pwmaa电机控制信号输入端(pwm输入)
ao1a电机电源m+
ao2a电机电源m-
vm12v
vcc3.3v
gnd和单片机共地

这是一个电机的引脚连接,另一个电机与此类似。如果只用于电机驱动而不用测速的话,只用接电机的m+和m-即可

当ain或bin产生电势差,电机就会转动。

4、转速的测量

如果我们要让我们的平衡车真的实现平衡,就肯定需要使用pwm驱动电机,然后再使用编码器测量速度,再使用pid算法进行闭环控制。

原理:

大概就是编码器上会有两个用来产生脉冲的东西,电机每转动一圈,他们会产生相差九十度的相同数量的脉冲,然后会有一个计数器去捕获脉冲的上升沿和下降沿,然后每隔一段时间去读取计数器的值(转动的圈数=计数器的数值/一圈产生的脉冲数),这样就能算出这一段时间转了多少圈,从而算出转速。

定时器的编码器接口模式

可以用来捕获上升沿和下降沿并计数。(stm32中的定时器只有tim1-5和tim8才有编码器接口功能,而且只有ch1通道和ch2通道有用)。

4倍频计数:

采用的是编码器模式3,在ti1和ti2边沿都计数,也就是在一个周期内对a相和b相的上升沿下降沿都计数一个周期内计4次,所以采用这种模式后,相应的计数值(cnt)就会变成4倍。

线数:

电机转动一圈产生的脉冲数就是线数。比如我的电机线束是30,那么我的电机转一圈就会产生30个脉冲。

5、相关代码的配置

电机的驱动代码如下:

这个没有什么好讲的,大家应该都能看懂。

//moto1,moto2  左轮pwm、右轮pwm
void set_pwm(int moto1,int moto2)
{
	if(moto1<0)			ain2=1,			ain1=0;
	else 	          ain2=0,			ain1=1;
	pwma=myabs(moto1);
	if(moto2<0)	bin1=1,			bin2=0;
	else        bin1=0,			bin2=1;
	pwmb=myabs(moto2);	
}

int myabs(int a)
{ 		   
	  int temp;
		if(a<0)  temp=-a;  
	  else temp=a;
	  return temp;
}

测速代码:

我左右轮分别使用的tim2和tim3,这段代码就是读取计数器的值。

/**************************************************************************
函数功能:单位时间读取编码器计数
入口参数:定时器
返回  值:速度值
**************************************************************************/
int read_encoder(u8 timx)
{
    int encoder_tim;    
   switch(timx)
	 {
	   case 2:  encoder_tim= (short)tim2 -> cnt;  tim2 -> cnt=0;break;
		 case 3:  encoder_tim= (short)tim3 -> cnt;  tim3 -> cnt=0;break;	
		 default: encoder_tim=1;
	 }
		return encoder_tim;
}


pwm的配置

motor_pwm_init(7199,0);//不分频,初始化pwm 10khz,驱动电机

编码器模式的配置

预分频(psc)一般就为‘0’就好;

重装载值(arr)我这里配置的重装载值为65535,是最大值。这样定时器不会溢出,方便计算。也可以使用别的重装载值:

比如我的电机线数为30,即一圈产生30个脉冲,那么我如果使用编码器模式3的4倍频计数,那么转一圈会计数器值为30*4,如果我设置重装载值为120,那是不是电机每转一圈定时器就会溢出一次,我们再在定时器的溢出中断中加上一个计数器cnt,每次进中断,cnt的值就++,那在采样的时候计数器的值就为:cnt*重装载值+计数器的值

#define encoder_tim_period (u16)(65535)   //103的定时器是16位 2的16次方最大是65536


 
  tim_timebasestructure.tim_prescaler = 0x0; // 预分频器 
  tim_timebasestructure.tim_period = encoder_tim_period; //设定计数器自动重装值
  tim_timebasestructure.tim_clockdivision = tim_ckd_div1;//选择时钟分频:不分频
  tim_timebasestructure.tim_countermode = tim_countermode_up;tim向上计数  

6、硬件连接

电机a

m+——ao1

m- ——ao2

a   ——pa6

b   ——pa7

vcc——3.3v

gnd——gnd

电机b

m+——bo1

m- ——bo2

a   ——pa0

b   ——pa1

vcc——3.3v

gnd——gnd

tb6612

vm——12v

vcc——3.3v

gnd——gnd

pwma——pa8

ain2——pb15

ain1——pb14

stby——3.3v

bin1——pb13

bin2——pb12

pwmb——pa11

7、完整代码

main.c

#include "stdio.h"
#include "led.h"
#include "delay.h"
#include "sys.h"
#include "usart.h"	
#include "motor_pwm.h"
#include "encoder.h"


 int main(void)
 {	
//	u16 led0pwmval=0;    
//	u8 dir=1;	
    uart_init(9600);	     //串口1初始化
	delay_init();	    	 //延时函数初始化	  
	led_init();		  	//初始化与led连接的硬件接口
    nvic_configuration();	//中断优先级
    motor_init();       //
	motor_pwm_init(7199,0);//不分频,初始化pwm 10khz,驱动电机
   	encoder_init_tim2();//tim2初始化为编码器接口模式
    encoder_init_tim3();//tim3初始化为编码器接口模式
     
    while(1)
	{ 
//		if(dir)led0pwmval++;
//		else led0pwmval--;	 
// 		if(led0pwmval>300)dir=0;
//		if(led0pwmval==0)dir=1;
//		tim_setcompare1(tim1,led0pwmval);
        set_pwm(6000,7000);
        printf("%d\t",read_encoder(2));
        printf("%d",read_encoder(3));
        printf("\r\n");
        delay_ms(1000);
        set_pwm(-5000,-4000);
        printf("%d\t",read_encoder(2));
        printf("%d",read_encoder(3));
        printf("\r\n");
        delay_ms(1000);
        
	} 
}

encoder.c

#include "encoder.h"

void encoder_init_tim2(void)
{
	tim_timebaseinittypedef tim_timebasestructure;  
  tim_icinittypedef tim_icinitstructure;  
  gpio_inittypedef gpio_initstructure;
  rcc_apb1periphclockcmd(rcc_apb1periph_tim2, enable);//使能定时器4的时钟
  rcc_apb2periphclockcmd(rcc_apb2periph_gpioa, enable);//使能pb端口时钟
	
  gpio_initstructure.gpio_pin = gpio_pin_0|gpio_pin_1;	//端口配置
  gpio_initstructure.gpio_mode = gpio_mode_in_floating; //浮空输入
  gpio_init(gpioa, &gpio_initstructure);					      //根据设定参数初始化gpiob
  
  tim_timebasestructinit(&tim_timebasestructure);
  tim_timebasestructure.tim_prescaler = 0x0; // 预分频器 
  tim_timebasestructure.tim_period = encoder_tim_period; //设定计数器自动重装值
  tim_timebasestructure.tim_clockdivision = tim_ckd_div1;//选择时钟分频:不分频
  tim_timebasestructure.tim_countermode = tim_countermode_up;tim向上计数  
  tim_timebaseinit(tim2, &tim_timebasestructure);
  tim_encoderinterfaceconfig(tim2, tim_encodermode_ti12, tim_icpolarity_rising, tim_icpolarity_rising);//使用编码器模式3
  tim_icstructinit(&tim_icinitstructure);
  tim_icinitstructure.tim_icfilter = 10;
  tim_icinit(tim2, &tim_icinitstructure);
  tim_clearflag(tim2, tim_flag_update);//清除tim的更新标志位
  tim_itconfig(tim2, tim_it_update, enable);
  //reset counter
  tim_setcounter(tim2,0);
  tim_cmd(tim2, enable); 
}
/**************************************************************************
函数功能:把tim3初始化为编码器接口模式
入口参数:无
返回  值:无
**************************************************************************/
void encoder_init_tim3(void)
{
	tim_timebaseinittypedef tim_timebasestructure;  
  tim_icinittypedef tim_icinitstructure;  
  gpio_inittypedef gpio_initstructure;
  rcc_apb1periphclockcmd(rcc_apb1periph_tim3, enable);//使能定时器4的时钟
  rcc_apb2periphclockcmd(rcc_apb2periph_gpioa, enable);//使能pb端口时钟
	
  gpio_initstructure.gpio_pin = gpio_pin_6|gpio_pin_7;	//端口配置
  gpio_initstructure.gpio_mode = gpio_mode_in_floating; //浮空输入
  gpio_init(gpioa, &gpio_initstructure);					      //根据设定参数初始化gpiob
  
  tim_timebasestructinit(&tim_timebasestructure);
  tim_timebasestructure.tim_prescaler = 0x0; // 预分频器 
  tim_timebasestructure.tim_period = encoder_tim_period; //设定计数器自动重装值
  tim_timebasestructure.tim_clockdivision = tim_ckd_div1;//选择时钟分频:不分频
  tim_timebasestructure.tim_countermode = tim_countermode_up;tim向上计数  
  tim_timebaseinit(tim3, &tim_timebasestructure);
  tim_encoderinterfaceconfig(tim3, tim_encodermode_ti12, tim_icpolarity_rising, tim_icpolarity_rising);//使用编码器模式3
  tim_icstructinit(&tim_icinitstructure);
  tim_icinitstructure.tim_icfilter = 10;
  tim_icinit(tim3, &tim_icinitstructure);
  tim_clearflag(tim3, tim_flag_update);//清除tim的更新标志位
  tim_itconfig(tim3, tim_it_update, enable);
  //reset counter
  tim_setcounter(tim3,0);
  tim_cmd(tim3, enable); 
}

/**************************************************************************
函数功能:单位时间读取编码器计数
入口参数:定时器
返回  值:速度值
**************************************************************************/
int read_encoder(u8 timx)
{
    int encoder_tim;    
   switch(timx)
	 {
	   case 2:  encoder_tim= (short)tim2 -> cnt;  tim2 -> cnt=0;break;
		 case 3:  encoder_tim= (short)tim3 -> cnt;  tim3 -> cnt=0;break;	
		 default: encoder_tim=1;
	 }
		return encoder_tim;
}


encoder.h

#ifndef __encoder_h
#define __encoder_h
#include <sys.h>	 


#define encoder_tim_period (u16)(65535)   //103的定时器是16位 2的16次方最大是65536


void encoder_init_tim2(void);
void encoder_init_tim3(void);
int read_encoder(u8 timx);
#endif

motor_pwm.c

#include "motor_pwm.h"
#include "led.h"



//pwm输出初始化
//arr:自动重装值
//psc:时钟预分频数
void motor_init(void)	//in引脚初始化
{
    gpio_inittypedef gpio_initstructure;
    rcc_apb2periphclockcmd(rcc_apb2periph_gpiob, enable); //使能pb端口时钟
    gpio_initstructure.gpio_pin = gpio_pin_12|gpio_pin_13|gpio_pin_14|gpio_pin_15;	//端口配置
    gpio_initstructure.gpio_mode = gpio_mode_out_pp;      //推挽输出
    gpio_initstructure.gpio_speed = gpio_speed_50mhz;     //50mhz
    gpio_init(gpiob, &gpio_initstructure);					      //根据设定参数初始化gpiob 
	ain1=0,ain2=0;
	bin1=0,bin1=0;
}
void motor_pwm_init(u16 arr,u16 psc)	//pwm引脚初始化
{
    gpio_inittypedef gpio_initstructure;
	tim_timebaseinittypedef  tim_timebasestructure;
	tim_ocinittypedef  tim_ocinitstructure;
	rcc_apb2periphclockcmd(rcc_apb2periph_tim1, enable);// 
 	rcc_apb2periphclockcmd(rcc_apb2periph_gpioa , enable);  //使能gpio外设时钟使能
    //设置该引脚为复用输出功能,输出tim1 ch1 ch4的pwm脉冲波形
	gpio_initstructure.gpio_pin = gpio_pin_8|gpio_pin_11; //tim_ch1 //tim_ch4
	gpio_initstructure.gpio_mode = gpio_mode_af_pp;  //复用推挽输出
	gpio_initstructure.gpio_speed = gpio_speed_50mhz;
	gpio_init(gpioa, &gpio_initstructure);
	
	tim_timebasestructure.tim_period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值	 
	tim_timebasestructure.tim_prescaler =psc; //设置用来作为timx时钟频率除数的预分频值  不分频
	tim_timebasestructure.tim_clockdivision = 0; //设置时钟分割:tdts = tck_tim
	tim_timebasestructure.tim_countermode = tim_countermode_up;  //tim向上计数模式
	tim_timebaseinit(tim1, &tim_timebasestructure); //根据tim_timebaseinitstruct中指定的参数初始化timx的时间基数单位

 
	tim_ocinitstructure.tim_ocmode = tim_ocmode_pwm1; //选择定时器模式:tim脉冲宽度调制模式1
	tim_ocinitstructure.tim_outputstate = tim_outputstate_enable; //比较输出使能
	tim_ocinitstructure.tim_pulse = 0;                            //设置待装入捕获比较寄存器的脉冲值
	tim_ocinitstructure.tim_pulse = arr >> 1;
	tim_ocinitstructure.tim_ocpolarity = tim_ocpolarity_high;     //输出极性:tim输出比较极性高
	tim_oc1init(tim1, &tim_ocinitstructure);  //根据tim_ocinitstruct中指定的参数初始化外设timx
	tim_oc4init(tim1, &tim_ocinitstructure);  //根据tim_ocinitstruct中指定的参数初始化外设timx

    tim_ctrlpwmoutputs(tim1,enable);	//moe 主输出使能	

	tim_oc1preloadconfig(tim1, tim_ocpreload_enable);  //ch1预装载使能	 
	tim_oc4preloadconfig(tim1, tim_ocpreload_enable);  //ch4预装载使能	 
	
	tim_arrpreloadconfig(tim1, enable); //使能timx在arr上的预装载寄存器
	
	tim_cmd(tim1, enable);  //使能tim1
}
//moto1,moto2  左轮pwm、右轮pwm
void set_pwm(int moto1,int moto2)
{
	if(moto1<0)			ain2=1,			ain1=0;
	else 	          ain2=0,			ain1=1;
	pwma=myabs(moto1);
	if(moto2<0)	bin1=1,			bin2=0;
	else        bin1=0,			bin2=1;
	pwmb=myabs(moto2);	
}

int myabs(int a)
{ 		   
	  int temp;
		if(a<0)  temp=-a;  
	  else temp=a;
	  return temp;
}

motor_pwm.h

#ifndef __pwm_h
#define __pwm_h
#include "sys.h"	 

#define pwma   tim1->ccr1  //pa8  pwma  tim1_ch1
#define ain2   pbout(15)
#define ain1   pbout(14)

#define pwmb   tim1->ccr4  //pa11 pwmb  tim1_ch4
#define bin1   pbout(13)
#define bin2   pbout(12)

void motor_init(void);
void motor_pwm_init(u16 arr,u16 psc);
int myabs(int a);
void set_pwm(int moto1,int moto2);

#endif

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com