当前位置:   article > 正文

基于MSP430f5529 编码电机测速 接收脉冲数 PWM调速 CCS编译器 代码分析_msp430电机测速

msp430电机测速

前言:2022年TI杯大学生电子设计竞赛,小车跟随行驶系统(C题)要求:设计一套小车跟随行驶系统,采用TI的MCU,由一辆领头小车和一辆跟随小车组成,要求小车具有循迹功能,且速度在0.3~1m/s可调......本文着重介绍速度在0.3~1m/s可调的一种实现方式。

正文

一、首先了解编码电机测速的原理(移步下方链接,不过多赘述)

霍尔增量式编码器左右车轮线速度的计算_许你一世阳光yyds的博客-CSDN博客

二、获取关键参数,及oled使用

       由此我们得知,我们需要获取的关键数据为编码电机  轮子转动一个脉冲走过的距离(m/脉)  这个参数可以是 1. 从你所购买电机的店铺获得;2. 自己做实验测出来。下面我们从2展开。

       要测这个参数最起码,你的单片机要能够读取到编码电机A相或B相在旋转时所发出的脉冲,然后通过使用CCS debug功能看变量的值,或者直接显示在oled上。

四针oled显示:把下面链接的文件除了main.c( main.c用 三、里的 )都复制到你的工程文件里面

msp430f5529——OLED屏显示文字与图片_会动的栗子的博客-CSDN博客_msp430oled显示

三、获取 脉冲数 代码(注释的内容可完全删除也不会影响)

原理:定时器中断(连续计数模式),配置上升沿捕获,我在完整代码上注释了 X_1=0;X_2=0; (将这两个变量定时清0)的语句,此时的代码只能进行获取 脉冲总数 的实验,并不能获得当前速度。

main.c

  1. #include <msp430.h>
  2. #include "oled.h"
  3. #include "type.h"
  4. #include "bmp.h"
  5. //#include "motor.h" 这是我自己写的电机控制代码,无关紧要,对应的是下面 调试电机转向
  6. //num代表每个定时器周期内(也就是0.4619989秒内)的脉冲数,X代表从单片机开始工作时记录的脉冲总数
  7. int num1=0; //对应P2.0引脚
  8. int num2=0; //对应P2.4
  9. int X_1=0; //对应P2.0
  10. int X_2=0; //对应P2.4
  11. float PID_calc(float Set_Temp , float V_1) //PID算法子程序 输入:(目标值,当前值)
  12. {
  13. float P=0.800,I=0.15,D=800.0;//初始化P,I,D,当前值,设置值 常数
  14. float PID_OUT=0,PWM_Duty=0; //PID输出
  15. float P_OUT=0,I_OUT=0,D_OUT=0; //比例输出,积分输出,微分输出
  16. float Current_Error=0, Last_Error=0; //当前误差 最后误差
  17. float Sum_Error=0,Prev_Error=0; //误差积分
  18. float Gain=1.2,PID_I_MAX=100.0,PID_I_MIN=-100.0,V_DATA_MAX=100,V_DATA_MIN=0;
  19. float Rate;//误差变化率
  20. Current_Error = Set_Temp - V_1;//当前误差
  21. Sum_Error +=Current_Error;//误差积分
  22. Prev_Error = Last_Error;//存储误差积分
  23. Last_Error = Current_Error;//存储误差分析
  24. Rate = Current_Error-Last_Error;//变化速率计算
  25. if(Rate>10)//不让ta大于5也不让ta小于5
  26. Rate = 10;
  27. if(Rate<-10)
  28. Rate = -10;
  29. P_OUT = P*Gain*Current_Error;//比列项
  30. I_OUT = I*Gain*Sum_Error;//积分项
  31. //积分限幅处理
  32. if( I_OUT>PID_I_MAX ) I_OUT = PID_I_MAX;//不能超过最大值不能低于最小值
  33. if( I_OUT<PID_I_MIN ) I_OUT = PID_I_MIN;
  34. //微分输出处理
  35. D_OUT = D*Gain*Rate;
  36. PID_OUT = P_OUT + I_OUT + D_OUT ;
  37. //if ( PID_OUT >= V_DATA_MAX ) PID_OUT = V_DATA_MAX;
  38. //if ( PID_OUT <= V_DATA_MIN ) PID_OUT = V_DATA_MIN;
  39. return PID_OUT;
  40. }
  41. float speed1(int num)//金属电机 下面的0.000556906就是我测得我电机 m/脉 的参数
  42. {
  43. float v;
  44. //v = (num * 0.000556906) / 0.4619989;//这里把测得的 m/脉 取代0.000556906算出来的是m/s
  45. v = num * 1.2054271124;//mm/s 这句我为了方便单片机计算,自己已经算好了系数然后转化为mm/s
  46. return v;
  47. }
  48. float speed2(int num)//小蓝电机 如果你只是做一辆小车,就写一个speed计算函数就好了
  49. {
  50. float v;
  51. //v = (num * 0.00037109091) / 0.4619989;
  52. v = num * 0.8032289903;//mm/s
  53. return v;
  54. }
  55. void TIME()//配置编码电机接口
  56. {
  57. TA2CTL |= TASSEL_2+MC_2+TAIE+TACLR+ ID_3;//SMCLK,连续计数,中断允许,计数器清零
  58. TA1CTL |= TASSEL_2+MC_2+TAIE+TACLR+ ID_3;//SMCLK,连续计数,中断允许,计数器清零
  59. TA2CCTL1 |= CAP+CM_1+CCIS_0+CCIE; //捕获模式,上升沿捕获,CCI1A输入,同步捕获,中断允许
  60. TA1CCTL1 |= CAP+CM_1+CCIS_0+CCIE; //捕获模式,上升沿捕获,CCI1A输入,同步捕获,中断允许
  61. P2DIR &=~ BIT4; //初始化捕获IO口
  62. P2SEL |= BIT4;
  63. P2DIR &=~ BIT0; //初始化捕获IO口
  64. P2SEL |= BIT0;
  65. }
  66. int main(void)
  67. {
  68. WDTCTL = WDTPW | WDTHOLD; // stop watchdog timer
  69. OLED_Init(); //初始化
  70. OLED_Clear(); //清屏
  71. TIME();
  72. //motor_IO_int();
  73. __bis_SR_register(GIE);
  74. float v1;
  75. float v2;
  76. //X_1 p2.0
  77. //X_2 p2.4
  78. //front();
  79. float a1=0, b1=0;
  80. int a2=0, b2=0;
  81. while(1)
  82. {
  83. /*v1=speed2(num1);
  84. v2=speed2(num2);
  85. a1 = v1 ; //左电机 接的是p2.0
  86. b1 = v2 ; //右
  87. a1 = a1 + PID_calc(294 , a1);
  88. b1 = b1 + PID_calc(303 , b1);*/
  89. OLED_ShowNum(1,1,num1,3,20);//oled第一行显示P2.0引脚捕获的脉冲数
  90. OLED_ShowNum(1,2,num2,3,20);//第二行显示p2.4
  91. //__delay_cycles(1600000);//1s延时
  92. //a2 = a1 * 0.3;
  93. //b2 = b1 * 0.3;
  94. //mv_go(a2,b2);
  95. //front();
  96. //OLED_ShowNum(1,1,v1,3,20);
  97. //OLED_ShowNum(1,2,v2,3,20);
  98. }
  99. /*
  100. 调试电机转向
  101. mv_go(90,90);
  102. __delay_cycles(1600000*2);
  103. mv_back(90,90);
  104. __delay_cycles(1600000*2);
  105. mv_L(90,90);
  106. __delay_cycles(1600000*2);
  107. mv_R(90,90);*/
  108. }
  109. #pragma vector=TIMER2_A1_VECTOR //Timer_A捕获中断向量 p2.4
  110. __interrupt void Timer_A2(void)
  111. {
  112. switch(TA2IV)
  113. {
  114. case 2 : X_2++; break;
  115. default:
  116. num2=X_2 ;
  117. //X_2=0; 注释这里,代码只能实现测 m/脉 去掉注释则只能测速度
  118. break;
  119. }
  120. }
  121. #pragma vector=TIMER1_A1_VECTOR //Timer_A捕获中断向量 p2.0
  122. __interrupt void Timer_A1(void)
  123. {
  124. switch(TA1IV)
  125. {
  126. case 2 : X_1++; break;
  127. default:
  128. num1=X_1 ;
  129. //X_1=0; 同X_2
  130. break;
  131. }
  132. }

通过上面的代码,自己用手转动车轮一周记录数值变化,多测几次取平均值,然后依据 一、自己算 m/脉 然后替换掉speed1或2里的公式.

四、匀速0.3m/s总代码 main.c + motor.h + motor.c 参考

main.c

  1. #include <msp430.h>
  2. #include "oled.h"
  3. #include "type.h"
  4. #include "bmp.h"
  5. #include "motor.h"
  6. //num代表每个定时器周期内(也就是0.4619989秒内)的脉冲数,X代表从单片机开始工作时记录的脉冲总数
  7. int num1=0; //对应P2.0引脚
  8. int num2=0; //对应P2.4
  9. int X_1=0; //对应P2.0
  10. int X_2=0; //对应P2.4
  11. float PID_calc(float Set_Temp , float V_1) //匀速PID算法子程序 输入:(目标值,当前值)
  12. {
  13. float P=0.800,I=0.15,D=800.0;//初始化P,I,D,当前值,设置值 常数
  14. float PID_OUT=0,PWM_Duty=0; //PID输出
  15. float P_OUT=0,I_OUT=0,D_OUT=0; //比例输出,积分输出,微分输出
  16. float Current_Error=0, Last_Error=0; //当前误差 最后误差
  17. float Sum_Error=0,Prev_Error=0; //误差积分
  18. float Gain=1.2,PID_I_MAX=100.0,PID_I_MIN=-100.0,V_DATA_MAX=100,V_DATA_MIN=0;
  19. float Rate;//误差变化率
  20. Current_Error = Set_Temp - V_1;//当前误差
  21. Sum_Error +=Current_Error;//误差积分
  22. Prev_Error = Last_Error;//存储误差积分
  23. Last_Error = Current_Error;//存储误差分析
  24. Rate = Current_Error-Last_Error;//变化速率计算
  25. if(Rate>10)//不让ta大于5也不让ta小于5
  26. Rate = 10;
  27. if(Rate<-10)
  28. Rate = -10;
  29. P_OUT = P*Gain*Current_Error;//比列项
  30. I_OUT = I*Gain*Sum_Error;//积分项
  31. //积分限幅处理
  32. if( I_OUT>PID_I_MAX ) I_OUT = PID_I_MAX;//不能超过最大值不能低于最小值
  33. if( I_OUT<PID_I_MIN ) I_OUT = PID_I_MIN;
  34. //微分输出处理
  35. D_OUT = D*Gain*Rate;
  36. PID_OUT = P_OUT + I_OUT + D_OUT ;
  37. //if ( PID_OUT >= V_DATA_MAX ) PID_OUT = V_DATA_MAX; 如果你有加速度存在限制的需求,则启用这两句
  38. //if ( PID_OUT <= V_DATA_MIN ) PID_OUT = V_DATA_MIN;
  39. return PID_OUT;
  40. }
  41. float speed1(int num)//金属电机
  42. {
  43. float v;
  44. //v = (num * 0.000556906) / 0.4619989;
  45. v = num * 1.2054271124;//mm/s
  46. return v;
  47. }
  48. float speed2(int num)//小蓝电机
  49. {
  50. float v;
  51. //v = (num * 0.00037109091) / 0.4619989;
  52. v = num * 0.8032289903;//mm/s
  53. return v;
  54. }
  55. void TIME()//配置编码电机接口
  56. {
  57. TA2CTL |= TASSEL_2+MC_2+TAIE+TACLR+ ID_3;//SMCLK,连续计数,中断允许,计数器清零
  58. TA1CTL |= TASSEL_2+MC_2+TAIE+TACLR+ ID_3;//SMCLK,连续计数,中断允许,计数器清零
  59. TA2CCTL1 |= CAP+CM_1+CCIS_0+CCIE; //捕获模式,上升沿捕获,CCI1A输入,同步捕获,中断允许
  60. TA1CCTL1 |= CAP+CM_1+CCIS_0+CCIE; //捕获模式,上升沿捕获,CCI1A输入,同步捕获,中断允许
  61. P2DIR &=~ BIT4; //初始化捕获IO口
  62. P2SEL |= BIT4;
  63. P2DIR &=~ BIT0; //初始化捕获IO口
  64. P2SEL |= BIT0;
  65. }
  66. int main(void)
  67. {
  68. WDTCTL = WDTPW | WDTHOLD; // stop watchdog timer
  69. OLED_Init(); //初始化
  70. OLED_Clear(); //清屏
  71. TIME();//接编码电机引脚初始化
  72. motor_IO_int();//接线l298n的引脚初始化
  73. __bis_SR_register(GIE);
  74. float v1;
  75. float v2;
  76. //X_1 p2.0
  77. //X_2 p2.4
  78. front();
  79. float a1=0, b1=0;
  80. int a2=0, b2=0;
  81. while(1)
  82. {
  83. v1=speed2(num1); //自己写哪个speed函数就改成哪个
  84. v2=speed2(num2);
  85. a1 = v1 ; //左电机
  86. b1 = v2 ;
  87. a1 = a1 + PID_calc(300 , a1);
  88. //这里的用法是我希望速度在300mm/s,用增量式计算出a1(pwm输出)当前的值
  89. /*但是这里的a1还不是pwm输出值,具体看下方a2,由于摩擦力的存在,设300mm/s不一定就是0.3m/s,具体看oled上,如果显示290,那可以设成(310,a1)具体多少自己调*/
  90. b1 = b1 + PID_calc(300 , b1);//与a1同理
  91. OLED_ShowNum(1,3,a1,3,20);
  92. OLED_ShowNum(1,4,b1,3,20);
  93. //__delay_cycles(1600000);//1s延时
  94. a2 = a1 * 0.3;//设置好速度和pwm的比例转换
  95. /*经实验,我的motor.c中TA0CCR0设置为100,我的电机在pwm输出90时速度大致为0.3m/s,90(下方调试电机转向的第一句)与300(mm/s)数字比例为0.3*/
  96. b2 = b1 * 0.3;
  97. mv_go(a2,b2);
  98. //front();
  99. OLED_ShowNum(1,1,v1,3,20);
  100. OLED_ShowNum(1,2,v2,3,20);
  101. }
  102. /*
  103. 调试电机转向
  104. mv_go(90,90);
  105. __delay_cycles(1600000*2);
  106. mv_back(90,90);
  107. __delay_cycles(1600000*2);
  108. mv_L(90,90);
  109. __delay_cycles(1600000*2);
  110. mv_R(90,90);*/
  111. }
  112. #pragma vector=TIMER2_A1_VECTOR //Timer_A捕获中断向量 p2.4
  113. __interrupt void Timer_A2(void)
  114. {
  115. switch(TA2IV)
  116. {
  117. case 2 : X_2++; break;
  118. default:
  119. num2=X_2 ; X_2=0;
  120. break;
  121. }
  122. }
  123. #pragma vector=TIMER1_A1_VECTOR //Timer_A捕获中断向量 p2.0
  124. __interrupt void Timer_A1(void)
  125. {
  126. switch(TA1IV)
  127. {
  128. case 2 : X_1++; break;
  129. default:
  130. num1=X_1 ; X_1=0;
  131. break;
  132. }
  133. }

motor.c

  1. #include <motor.h>
  2. /*430与l298n接线
  3. GPIO P4.0 -> IN2
  4. GPIO P4.3 -> IN1
  5. GPIO P4.1 -> IN3
  6. GPIO P4.2 -> IN4
  7. PWM P1.4 -> ENA
  8. PWM P1.5 -> ENB
  9. */
  10. void motor_IO_int() //初始化
  11. {
  12. P1DIR |=(BIT4+BIT5); //pwm
  13. P4DIR |=(BIT0+BIT1+BIT2+BIT3); //gpio
  14. P1SEL |=(BIT4+BIT5);
  15. //TA0CCR3 = 50; //定时器装载值,周期50us,即频率1*10^6/50=20kHz
  16. TA0CCR0 = 100;
  17. TA0CCTL3 = OUTMOD_7;
  18. TA0CCTL4 = OUTMOD_7;
  19. TA0CTL= TASSEL_2 +MC_1;
  20. // P6DIR = 0x00; //左右寻迹传感器.
  21. // P6SEL = 0x00;
  22. }
  23. void mv_back(pwmL,pwmR)
  24. {
  25. PWM(pwmL,pwmR); //占空比:32*100/50= 64%,即2倍关系
  26. P4OUT &= ~BIT0;
  27. P4OUT |= BIT3;
  28. P4OUT &= ~BIT2;
  29. P4OUT |= BIT1;
  30. }
  31. void mv_L(pwmL,pwmR)
  32. {
  33. PWM(pwmL,pwmR);
  34. P4OUT |= BIT3;
  35. P4OUT &= ~BIT0;
  36. P4OUT |= BIT2;
  37. P4OUT &= ~BIT1;
  38. }
  39. void mv_R(pwmL,pwmR)
  40. {
  41. PWM(pwmL,pwmR);
  42. P4OUT |= BIT0;
  43. P4OUT &= ~BIT3;
  44. P4OUT |= BIT1;
  45. P4OUT &= ~BIT2;
  46. }
  47. void front()
  48. {
  49. PWM(90,90); //900.3m/s
  50. P4OUT |= BIT0;
  51. P4OUT &= ~BIT3;
  52. P4OUT |= BIT2;
  53. P4OUT &= ~BIT1;
  54. }
  55. void mv_go(pwmL,pwmR)
  56. {
  57. PWM(pwmL,pwmR); //占空比:32*100/50= 64%,即2倍关系
  58. P4OUT |= BIT0;
  59. P4OUT &= ~BIT3;
  60. P4OUT |= BIT2;
  61. P4OUT &= ~BIT1;
  62. }
  63. void stop()
  64. {
  65. PWM(0,0);
  66. }
  67. void PWM(int pwm1,int pwm2) //左,右电机pwm
  68. {
  69. TA0CCR3 = pwm1; // CCR1 PWM 占空比定义
  70. TA0CCR4 = pwm2; // CCR2 PWM 占空比定义
  71. }
  72. void left()
  73. {
  74. mv_L(83,83);
  75. }

motor.h

  1. #ifndef _motor_h
  2. #define _motor_h
  3. #include <msp430.h>
  4. void motor_IO_int();
  5. void front();
  6. void slowgo();
  7. void stop();
  8. void back();
  9. void turn_L();
  10. void turn_R();
  11. void PWM(int pwm1,int pwm2);
  12. #endif

五、总共使用的引脚

430与l298n接线:
 GPIO P4.0 -> IN2
 GPIO P4.3 -> IN1
 GPIO P4.1 -> IN3
 GPIO P4.2 -> IN4
 PWM  P1.4 -> ENA
 PWM  P1.5 -> ENB

430与编码电机:

P2.0 -> 左电机的编码器脉冲输出(A,B相随便选)

P2.4 -> 右电机的编码器脉冲输出(A,B相随便选)

430与oled:

P3.5 -> SCL

P3.6 -> SDA

oled上显示内容:

第一行:左电机转速(mm/s)

第二行:右电机转速(mm/s)

第三行:左电机的PID输出值 (mm/s)

第四行:右电机的PID输出值 (mm/s)

参考学习自 化作尘大佬 :MSP430项目设计:2020年TI杯大学生电子设计竞赛 坡道行驶电动小车(C题)循迹小车(分享项目展示视频与源码)_化作尘的博客-CSDN博客_msp430小车

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

闽ICP备14008679号