当前位置:   article > 正文

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

jgb520

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算法进行闭环控制。

原理:

接收编码器的A、B相产生的正交信号,根据编码器产生的正交信号脉冲,自动控制CNT自增或自减,根据计数方向和编码器的信号关系来指示编码器的位置、旋转方向和旋转速度利用脉冲值来计算电机的转动位移

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

定时器的编码器接口模式

可以用来捕获上升沿和下降沿并计数。(Stm32中的定时器只有TIM1-5和TIM8才有编码器接口功能,而且只有CH1通道和CH2通道有用)。

4倍频计数:

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

线数:

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

5、相关代码的配置

电机的驱动代码如下:

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

  1. //moto1,moto2 左轮PWM、右轮PWM
  2. void Set_Pwm(int moto1,int moto2)
  3. {
  4. if(moto1<0) AIN2=1, AIN1=0;
  5. else AIN2=0, AIN1=1;
  6. PWMA=myabs(moto1);
  7. if(moto2<0) BIN1=1, BIN2=0;
  8. else BIN1=0, BIN2=1;
  9. PWMB=myabs(moto2);
  10. }
  11. int myabs(int a)
  12. {
  13. int temp;
  14. if(a<0) temp=-a;
  15. else temp=a;
  16. return temp;
  17. }

测速代码:

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

  1. /**************************************************************************
  2. 函数功能:单位时间读取编码器计数
  3. 入口参数:定时器
  4. 返回 值:速度值
  5. **************************************************************************/
  6. int Read_Encoder(u8 TIMX)
  7. {
  8. int Encoder_TIM;
  9. switch(TIMX)
  10. {
  11. case 2: Encoder_TIM= (short)TIM2 -> CNT; TIM2 -> CNT=0;break;
  12. case 3: Encoder_TIM= (short)TIM3 -> CNT; TIM3 -> CNT=0;break;
  13. default: Encoder_TIM=1;
  14. }
  15. return Encoder_TIM;
  16. }

PWM的配置

Motor_PWM_Init(7199,0);//不分频,初始化PWM 10KHZ,驱动电机

编码器模式的配置

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

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

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

  1. #define ENCODER_TIM_PERIOD (u16)(65535) //103的定时器是16位 2的16次方最大是65536
  2. TIM_TimeBaseStructure.TIM_Prescaler = 0x0; // 预分频器
  3. TIM_TimeBaseStructure.TIM_Period = ENCODER_TIM_PERIOD; //设定计数器自动重装值
  4. TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;//选择时钟分频:不分频
  5. 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

  1. #include "stdio.h"
  2. #include "led.h"
  3. #include "delay.h"
  4. #include "sys.h"
  5. #include "usart.h"
  6. #include "motor_pwm.h"
  7. #include "encoder.h"
  8. int main(void)
  9. {
  10. // u16 led0pwmval=0;
  11. // u8 dir=1;
  12. uart_init(9600); //串口1初始化
  13. delay_init(); //延时函数初始化
  14. LED_Init(); //初始化与LED连接的硬件接口
  15. NVIC_Configuration(); //中断优先级
  16. Motor_Init(); //
  17. Motor_PWM_Init(7199,0);//不分频,初始化PWM 10KHZ,驱动电机
  18. Encoder_Init_TIM2();//TIM2初始化为编码器接口模式
  19. Encoder_Init_TIM3();//TIM3初始化为编码器接口模式
  20. while(1)
  21. {
  22. // if(dir)led0pwmval++;
  23. // else led0pwmval--;
  24. // if(led0pwmval>300)dir=0;
  25. // if(led0pwmval==0)dir=1;
  26. // TIM_SetCompare1(TIM1,led0pwmval);
  27. Set_Pwm(6000,7000);
  28. printf("%d\t",Read_Encoder(2));
  29. printf("%d",Read_Encoder(3));
  30. printf("\r\n");
  31. delay_ms(1000);
  32. Set_Pwm(-5000,-4000);
  33. printf("%d\t",Read_Encoder(2));
  34. printf("%d",Read_Encoder(3));
  35. printf("\r\n");
  36. delay_ms(1000);
  37. }
  38. }

encoder.c

  1. #include "encoder.h"
  2. void Encoder_Init_TIM2(void)
  3. {
  4. TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
  5. TIM_ICInitTypeDef TIM_ICInitStructure;
  6. GPIO_InitTypeDef GPIO_InitStructure;
  7. RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);//使能定时器4的时钟
  8. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//使能PB端口时钟
  9. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1; //端口配置
  10. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
  11. GPIO_Init(GPIOA, &GPIO_InitStructure); //根据设定参数初始化GPIOB
  12. TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
  13. TIM_TimeBaseStructure.TIM_Prescaler = 0x0; // 预分频器
  14. TIM_TimeBaseStructure.TIM_Period = ENCODER_TIM_PERIOD; //设定计数器自动重装值
  15. TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;//选择时钟分频:不分频
  16. TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;TIM向上计数
  17. TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
  18. TIM_EncoderInterfaceConfig(TIM2, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);//使用编码器模式3
  19. TIM_ICStructInit(&TIM_ICInitStructure);
  20. TIM_ICInitStructure.TIM_ICFilter = 10;
  21. TIM_ICInit(TIM2, &TIM_ICInitStructure);
  22. TIM_ClearFlag(TIM2, TIM_FLAG_Update);//清除TIM的更新标志位
  23. TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
  24. //Reset counter
  25. TIM_SetCounter(TIM2,0);
  26. TIM_Cmd(TIM2, ENABLE);
  27. }
  28. /**************************************************************************
  29. 函数功能:把TIM3初始化为编码器接口模式
  30. 入口参数:无
  31. 返回 值:无
  32. **************************************************************************/
  33. void Encoder_Init_TIM3(void)
  34. {
  35. TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
  36. TIM_ICInitTypeDef TIM_ICInitStructure;
  37. GPIO_InitTypeDef GPIO_InitStructure;
  38. RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);//使能定时器4的时钟
  39. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//使能PB端口时钟
  40. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7; //端口配置
  41. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
  42. GPIO_Init(GPIOA, &GPIO_InitStructure); //根据设定参数初始化GPIOB
  43. TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
  44. TIM_TimeBaseStructure.TIM_Prescaler = 0x0; // 预分频器
  45. TIM_TimeBaseStructure.TIM_Period = ENCODER_TIM_PERIOD; //设定计数器自动重装值
  46. TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;//选择时钟分频:不分频
  47. TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;TIM向上计数
  48. TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
  49. TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);//使用编码器模式3
  50. TIM_ICStructInit(&TIM_ICInitStructure);
  51. TIM_ICInitStructure.TIM_ICFilter = 10;
  52. TIM_ICInit(TIM3, &TIM_ICInitStructure);
  53. TIM_ClearFlag(TIM3, TIM_FLAG_Update);//清除TIM的更新标志位
  54. TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
  55. //Reset counter
  56. TIM_SetCounter(TIM3,0);
  57. TIM_Cmd(TIM3, ENABLE);
  58. }
  59. /**************************************************************************
  60. 函数功能:单位时间读取编码器计数
  61. 入口参数:定时器
  62. 返回 值:速度值
  63. **************************************************************************/
  64. int Read_Encoder(u8 TIMX)
  65. {
  66. int Encoder_TIM;
  67. switch(TIMX)
  68. {
  69. case 2: Encoder_TIM= (short)TIM2 -> CNT; TIM2 -> CNT=0;break;
  70. case 3: Encoder_TIM= (short)TIM3 -> CNT; TIM3 -> CNT=0;break;
  71. default: Encoder_TIM=1;
  72. }
  73. return Encoder_TIM;
  74. }

encoder.h

  1. #ifndef __ENCODER_H
  2. #define __ENCODER_H
  3. #include <sys.h>
  4. #define ENCODER_TIM_PERIOD (u16)(65535) //103的定时器是16位 2的16次方最大是65536
  5. void Encoder_Init_TIM2(void);
  6. void Encoder_Init_TIM3(void);
  7. int Read_Encoder(u8 TIMX);
  8. #endif

motor_pwm.c

  1. #include "motor_pwm.h"
  2. #include "led.h"
  3. //PWM输出初始化
  4. //arr:自动重装值
  5. //psc:时钟预分频数
  6. void Motor_Init(void) //IN引脚初始化
  7. {
  8. GPIO_InitTypeDef GPIO_InitStructure;
  9. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能PB端口时钟
  10. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15; //端口配置
  11. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
  12. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //50MHZ
  13. GPIO_Init(GPIOB, &GPIO_InitStructure); //根据设定参数初始化GPIOB
  14. AIN1=0,AIN2=0;
  15. BIN1=0,BIN1=0;
  16. }
  17. void Motor_PWM_Init(u16 arr,u16 psc) //PWM引脚初始化
  18. {
  19. GPIO_InitTypeDef GPIO_InitStructure;
  20. TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
  21. TIM_OCInitTypeDef TIM_OCInitStructure;
  22. RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);//
  23. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE); //使能GPIO外设时钟使能
  24. //设置该引脚为复用输出功能,输出TIM1 CH1 CH4的PWM脉冲波形
  25. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_11; //TIM_CH1 //TIM_CH4
  26. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
  27. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  28. GPIO_Init(GPIOA, &GPIO_InitStructure);
  29. TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
  30. TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 不分频
  31. TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
  32. TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
  33. TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
  34. TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式1
  35. TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
  36. TIM_OCInitStructure.TIM_Pulse = 0; //设置待装入捕获比较寄存器的脉冲值
  37. TIM_OCInitStructure.TIM_Pulse = arr >> 1;
  38. TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
  39. TIM_OC1Init(TIM1, &TIM_OCInitStructure); //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
  40. TIM_OC4Init(TIM1, &TIM_OCInitStructure); //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
  41. TIM_CtrlPWMOutputs(TIM1,ENABLE); //MOE 主输出使能
  42. TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable); //CH1预装载使能
  43. TIM_OC4PreloadConfig(TIM1, TIM_OCPreload_Enable); //CH4预装载使能
  44. TIM_ARRPreloadConfig(TIM1, ENABLE); //使能TIMx在ARR上的预装载寄存器
  45. TIM_Cmd(TIM1, ENABLE); //使能TIM1
  46. }
  47. //moto1,moto2 左轮PWM、右轮PWM
  48. void Set_Pwm(int moto1,int moto2)
  49. {
  50. if(moto1<0) AIN2=1, AIN1=0;
  51. else AIN2=0, AIN1=1;
  52. PWMA=myabs(moto1);
  53. if(moto2<0) BIN1=1, BIN2=0;
  54. else BIN1=0, BIN2=1;
  55. PWMB=myabs(moto2);
  56. }
  57. int myabs(int a)
  58. {
  59. int temp;
  60. if(a<0) temp=-a;
  61. else temp=a;
  62. return temp;
  63. }

motor_pwm.h

  1. #ifndef __PWM_H
  2. #define __PWM_H
  3. #include "sys.h"
  4. #define PWMA TIM1->CCR1 //PA8 PWMA TIM1_CH1
  5. #define AIN2 PBout(15)
  6. #define AIN1 PBout(14)
  7. #define PWMB TIM1->CCR4 //PA11 PWMB TIM1_CH4
  8. #define BIN1 PBout(13)
  9. #define BIN2 PBout(12)
  10. void Motor_Init(void);
  11. void Motor_PWM_Init(u16 arr,u16 psc);
  12. int myabs(int a);
  13. void Set_Pwm(int moto1,int moto2);
  14. #endif

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/在线问答5/article/detail/797270
推荐阅读
相关标签
  

闽ICP备14008679号