前言: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

#include <msp430.h>
#include "oled.h"
#include "type.h"
#include "bmp.h"
//#include "motor.h"  这是我自己写的电机控制代码,无关紧要,对应的是下面 调试电机转向//num代表每个定时器周期内(也就是0.4619989秒内)的脉冲数,X代表从单片机开始工作时记录的脉冲总数
int num1=0;   //对应P2.0引脚
int num2=0;   //对应P2.4
int X_1=0;    //对应P2.0
int X_2=0;    //对应P2.4float PID_calc(float Set_Temp , float V_1) //PID算法子程序   输入:(目标值,当前值)
{float P=0.800,I=0.15,D=800.0;//初始化P,I,D,当前值,设置值 常数float PID_OUT=0,PWM_Duty=0;    //PID输出float P_OUT=0,I_OUT=0,D_OUT=0;    //比例输出,积分输出,微分输出float Current_Error=0, Last_Error=0;    //当前误差  最后误差float Sum_Error=0,Prev_Error=0;     //误差积分float Gain=1.2,PID_I_MAX=100.0,PID_I_MIN=-100.0,V_DATA_MAX=100,V_DATA_MIN=0;float Rate;//误差变化率Current_Error = Set_Temp - V_1;//当前误差Sum_Error +=Current_Error;//误差积分Prev_Error = Last_Error;//存储误差积分Last_Error = Current_Error;//存储误差分析Rate = Current_Error-Last_Error;//变化速率计算if(Rate>10)//不让ta大于5也不让ta小于5Rate = 10;if(Rate<-10)Rate = -10;P_OUT = P*Gain*Current_Error;//比列项I_OUT = I*Gain*Sum_Error;//积分项//积分限幅处理if( I_OUT>PID_I_MAX )  I_OUT = PID_I_MAX;//不能超过最大值不能低于最小值if( I_OUT<PID_I_MIN )  I_OUT = PID_I_MIN;//微分输出处理D_OUT = D*Gain*Rate;PID_OUT =  P_OUT  +  I_OUT  +  D_OUT ;//if ( PID_OUT >= V_DATA_MAX )  PID_OUT = V_DATA_MAX;//if ( PID_OUT <= V_DATA_MIN )  PID_OUT = V_DATA_MIN;return PID_OUT;
}float speed1(int num)//金属电机  下面的0.000556906就是我测得我电机 m/脉 的参数
{float v;//v = (num * 0.000556906) / 0.4619989;//这里把测得的 m/脉 取代0.000556906算出来的是m/sv = num * 1.2054271124;//mm/s 这句我为了方便单片机计算,自己已经算好了系数然后转化为mm/sreturn v;
}float speed2(int num)//小蓝电机   如果你只是做一辆小车,就写一个speed计算函数就好了
{float v;//v = (num * 0.00037109091) / 0.4619989;v = num * 0.8032289903;//mm/sreturn v;
}void TIME()//配置编码电机接口
{TA2CTL   |= TASSEL_2+MC_2+TAIE+TACLR+ ID_3;//SMCLK,连续计数,中断允许,计数器清零TA1CTL   |= TASSEL_2+MC_2+TAIE+TACLR+ ID_3;//SMCLK,连续计数,中断允许,计数器清零TA2CCTL1 |= CAP+CM_1+CCIS_0+CCIE;  //捕获模式,上升沿捕获,CCI1A输入,同步捕获,中断允许TA1CCTL1 |= CAP+CM_1+CCIS_0+CCIE;  //捕获模式,上升沿捕获,CCI1A输入,同步捕获,中断允许P2DIR &=~ BIT4;               //初始化捕获IO口P2SEL |=  BIT4;P2DIR &=~ BIT0;               //初始化捕获IO口P2SEL |=  BIT0;
}int main(void)
{WDTCTL = WDTPW | WDTHOLD;   // stop watchdog timerOLED_Init();    //初始化OLED_Clear();   //清屏TIME();//motor_IO_int();__bis_SR_register(GIE);float v1;float v2;//X_1  p2.0//X_2  p2.4//front();float a1=0, b1=0;int a2=0, b2=0;while(1){   /*v1=speed2(num1);v2=speed2(num2);a1 = v1 ;  //左电机 接的是p2.0b1 = v2 ;  //右a1 = a1 + PID_calc(294 , a1);b1 = b1 + PID_calc(303 , b1);*/OLED_ShowNum(1,1,num1,3,20);//oled第一行显示P2.0引脚捕获的脉冲数OLED_ShowNum(1,2,num2,3,20);//第二行显示p2.4//__delay_cycles(1600000);//1s延时//a2 = a1 * 0.3;//b2 = b1 * 0.3;//mv_go(a2,b2);//front();//OLED_ShowNum(1,1,v1,3,20);//OLED_ShowNum(1,2,v2,3,20);}/*调试电机转向mv_go(90,90);__delay_cycles(1600000*2);mv_back(90,90);__delay_cycles(1600000*2);mv_L(90,90);__delay_cycles(1600000*2);mv_R(90,90);*/
}#pragma vector=TIMER2_A1_VECTOR  //Timer_A捕获中断向量 p2.4
__interrupt void Timer_A2(void)
{switch(TA2IV){case 2 : X_2++; break;default:num2=X_2 ; //X_2=0; 注释这里,代码只能实现测 m/脉 去掉注释则只能测速度break;}
}#pragma vector=TIMER1_A1_VECTOR  //Timer_A捕获中断向量 p2.0
__interrupt void Timer_A1(void)
{switch(TA1IV){case 2 : X_1++; break;default:num1=X_1 ; //X_1=0; 同X_2break;}
}

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

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

main.c

#include <msp430.h>
#include "oled.h"
#include "type.h"
#include "bmp.h"
#include "motor.h"  //num代表每个定时器周期内(也就是0.4619989秒内)的脉冲数,X代表从单片机开始工作时记录的脉冲总数
int num1=0; //对应P2.0引脚
int num2=0; //对应P2.4
int X_1=0;    //对应P2.0
int X_2=0;    //对应P2.4float PID_calc(float Set_Temp , float V_1) //匀速PID算法子程序   输入:(目标值,当前值)
{float P=0.800,I=0.15,D=800.0;//初始化P,I,D,当前值,设置值 常数float PID_OUT=0,PWM_Duty=0;    //PID输出float P_OUT=0,I_OUT=0,D_OUT=0;    //比例输出,积分输出,微分输出float Current_Error=0, Last_Error=0;    //当前误差  最后误差float Sum_Error=0,Prev_Error=0;     //误差积分float Gain=1.2,PID_I_MAX=100.0,PID_I_MIN=-100.0,V_DATA_MAX=100,V_DATA_MIN=0;float Rate;//误差变化率Current_Error = Set_Temp - V_1;//当前误差Sum_Error +=Current_Error;//误差积分Prev_Error = Last_Error;//存储误差积分Last_Error = Current_Error;//存储误差分析Rate = Current_Error-Last_Error;//变化速率计算if(Rate>10)//不让ta大于5也不让ta小于5Rate = 10;if(Rate<-10)Rate = -10;P_OUT = P*Gain*Current_Error;//比列项I_OUT = I*Gain*Sum_Error;//积分项//积分限幅处理if( I_OUT>PID_I_MAX )  I_OUT = PID_I_MAX;//不能超过最大值不能低于最小值if( I_OUT<PID_I_MIN )  I_OUT = PID_I_MIN;//微分输出处理D_OUT = D*Gain*Rate;PID_OUT =  P_OUT  +  I_OUT  +  D_OUT ;//if ( PID_OUT >= V_DATA_MAX )  PID_OUT = V_DATA_MAX; 如果你有加速度存在限制的需求,则启用这两句//if ( PID_OUT <= V_DATA_MIN )  PID_OUT = V_DATA_MIN;return PID_OUT;
}float speed1(int num)//金属电机
{float v;//v = (num * 0.000556906) / 0.4619989;v = num * 1.2054271124;//mm/sreturn v;
}float speed2(int num)//小蓝电机
{float v;//v = (num * 0.00037109091) / 0.4619989;v = num * 0.8032289903;//mm/sreturn v;
}void TIME()//配置编码电机接口
{TA2CTL   |= TASSEL_2+MC_2+TAIE+TACLR+ ID_3;//SMCLK,连续计数,中断允许,计数器清零TA1CTL   |= TASSEL_2+MC_2+TAIE+TACLR+ ID_3;//SMCLK,连续计数,中断允许,计数器清零TA2CCTL1 |= CAP+CM_1+CCIS_0+CCIE;  //捕获模式,上升沿捕获,CCI1A输入,同步捕获,中断允许TA1CCTL1 |= CAP+CM_1+CCIS_0+CCIE;  //捕获模式,上升沿捕获,CCI1A输入,同步捕获,中断允许P2DIR &=~ BIT4;               //初始化捕获IO口P2SEL |=  BIT4;P2DIR &=~ BIT0;               //初始化捕获IO口P2SEL |=  BIT0;
}int main(void)
{WDTCTL = WDTPW | WDTHOLD;   // stop watchdog timerOLED_Init();    //初始化OLED_Clear();   //清屏TIME();//接编码电机引脚初始化motor_IO_int();//接线l298n的引脚初始化__bis_SR_register(GIE);float v1;float v2;//X_1  p2.0//X_2  p2.4front();float a1=0, b1=0;int a2=0, b2=0;while(1){v1=speed2(num1); //自己写哪个speed函数就改成哪个v2=speed2(num2);a1 = v1 ;  //左电机b1 = v2 ;a1 = a1 + PID_calc(300 , a1);//这里的用法是我希望速度在300mm/s,用增量式计算出a1(pwm输出)当前的值/*但是这里的a1还不是pwm输出值,具体看下方a2,由于摩擦力的存在,设300mm/s不一定就是0.3m/s,具体看oled上,如果显示290,那可以设成(310,a1)具体多少自己调*/b1 = b1 + PID_calc(300 , b1);//与a1同理OLED_ShowNum(1,3,a1,3,20);OLED_ShowNum(1,4,b1,3,20);//__delay_cycles(1600000);//1s延时a2 = a1 * 0.3;//设置好速度和pwm的比例转换/*经实验,我的motor.c中TA0CCR0设置为100,我的电机在pwm输出90时速度大致为0.3m/s,90(下方调试电机转向的第一句)与300(mm/s)数字比例为0.3*/b2 = b1 * 0.3;mv_go(a2,b2);//front();OLED_ShowNum(1,1,v1,3,20);OLED_ShowNum(1,2,v2,3,20);}/*调试电机转向mv_go(90,90);__delay_cycles(1600000*2);mv_back(90,90);__delay_cycles(1600000*2);mv_L(90,90);__delay_cycles(1600000*2);mv_R(90,90);*/
}#pragma vector=TIMER2_A1_VECTOR  //Timer_A捕获中断向量 p2.4
__interrupt void Timer_A2(void)
{switch(TA2IV){case 2 : X_2++; break;default:num2=X_2 ; X_2=0;break;}
}#pragma vector=TIMER1_A1_VECTOR  //Timer_A捕获中断向量 p2.0
__interrupt void Timer_A1(void)
{switch(TA1IV){case 2 : X_1++; break;default:num1=X_1 ; X_1=0;break;}
}

motor.c

#include <motor.h>
/*430与l298n接线GPIO P4.0 -> IN2GPIO P4.3 -> IN1GPIO P4.1 -> IN3GPIO P4.2 -> IN4PWM  P1.4 -> ENAPWM  P1.5 -> ENB*/
void motor_IO_int() //初始化
{P1DIR |=(BIT4+BIT5);  //pwmP4DIR |=(BIT0+BIT1+BIT2+BIT3);  //gpioP1SEL |=(BIT4+BIT5);//TA0CCR3 = 50;  //定时器装载值,周期50us,即频率1*10^6/50=20kHzTA0CCR0 = 100;TA0CCTL3 = OUTMOD_7;TA0CCTL4 = OUTMOD_7;TA0CTL= TASSEL_2 +MC_1;//    P6DIR = 0x00; //左右寻迹传感器.
//    P6SEL = 0x00;
}void mv_back(pwmL,pwmR)
{PWM(pwmL,pwmR);   //占空比:32*100/50= 64%,即2倍关系P4OUT &= ~BIT0;P4OUT |= BIT3;P4OUT &= ~BIT2;P4OUT |= BIT1;
}void mv_L(pwmL,pwmR)
{PWM(pwmL,pwmR);P4OUT |= BIT3;P4OUT &= ~BIT0;P4OUT |= BIT2;P4OUT &= ~BIT1;
}
void mv_R(pwmL,pwmR)
{PWM(pwmL,pwmR);P4OUT |= BIT0;P4OUT &= ~BIT3;P4OUT |= BIT1;P4OUT &= ~BIT2;
}void front()
{PWM(90,90);   //90是0.3m/sP4OUT |= BIT0;P4OUT &= ~BIT3;P4OUT |= BIT2;P4OUT &= ~BIT1;
}void mv_go(pwmL,pwmR)
{PWM(pwmL,pwmR);   //占空比:32*100/50= 64%,即2倍关系P4OUT |= BIT0;P4OUT &= ~BIT3;P4OUT |= BIT2;P4OUT &= ~BIT1;
}void stop()
{PWM(0,0);
}void PWM(int pwm1,int pwm2)   //左,右电机pwm
{TA0CCR3 = pwm1;   // CCR1 PWM 占空比定义TA0CCR4 = pwm2;   // CCR2 PWM 占空比定义
}void left()
{mv_L(83,83);
}

motor.h

#ifndef _motor_h
#define _motor_h
#include <msp430.h>void motor_IO_int();
void front();
void slowgo();
void stop();
void back();
void turn_L();
void turn_R();
void PWM(int pwm1,int pwm2);#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小车

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

  1. 树莓派4b:PWM调速风扇+DIY亚克力板外壳的定制降温方案

    宿舍里的校园网只能连接两台设备,于是我就给树莓派4B装上了openwrt,加上淘宝上买的1200M无限网卡(!买的时候注意型号和宽度大小)作为路由器.但在官方外壳里,树莓派温度稳定在60~70度,所以 ...

  2. STM32 学习笔记1-智能小车-基于PWM 调速 的电机设置

    目录 本文章主要介绍 STM32 电机相关软件的配置,PWM的相关介绍,csdn上面有很多资料 硬件: PWM(Pulse Width Modulation) 介绍: 基本定时器(TIM6/7) 通用 ...

  3. 伺服驱动器接收脉冲数,齿轮比,发送电机脉冲数关系

    公式一: U:脉冲当量(单位:mm/pulse) A:电子齿轮比分子 B:电子齿轮比分母 L:电机转一圈,实际位移距离(螺距)(单位:mm) E:编码器分辨率(单位:pulse) 公式二: S:驱动器 ...

  4. 基于51单片机直流电机PWM调速设计

    具体实现功能 系统由STC89C52单片机.LCD1602液晶显示屏.霍尔测速传感器.3V直流电机.按键模块构成. 具体功能: 1.采用霍尔传感器非接触式测电机转速: 2.LCD1602液晶显示当前的 ...

  5. 51单片机-PWM调速(直流电机,智能小车的电机调速)

    文章目录 前言 一.PWM 二.产生PWM的方法 使用定时器产生一个恒定的周期 中断服务子程序里设置比较值 三.实验 使用PWM对直流电机调速 使用PWM对智能小车的电机调速 总结 前言 这次来对PW ...

  6. 基于STM32单片机的直流电机PWM调速(Proteus仿真+程序)

    编号:9 基于STM32单片机的直流电机PWM调速 功能描述: 由 STM32单片机+液晶1602显示模块+键盘模块+L298N电机驱动模块+直流电机 1.采用STM32F103单片机为主控制器 2. ...

  7. [问题解决]不使用PWM调速系统,彻底解决一个L298N带动两个电机却转速不同的问题

    [问题解决]不使用PWM调速系统,彻底解决一个L298N带动两个电机却转速不同的问题 参考文章: (1)[问题解决]不使用PWM调速系统,彻底解决一个L298N带动两个电机却转速不同的问题 (2)ht ...

  8. 基于STM32使用PWM调速控制直流电机

    PWM调速控制直流电机 网上找的历程,实现可以,但是无法随我自己的意愿改变速度,还是有问题. 代码如下: #include "delay.h" #include "sys ...

  9. h桥控制电机刹车_51单片机H桥电路控制电机正反转和PWM调速

    桥电路,控制电机的正反转和 PWM 调速, 程序是网上的, 改改引脚就能用,电路和 源程序如下: 功能: P1.1 按键停止, P1.2 左转, P1.3 右转, P1.0 调速 #include # ...

最新文章

  1. nginx反向代理、负载均衡以及分布式下的session保持
  2. Dom onload和jQuery document ready的区别
  3. 中国IT行业薪资:与销售相比,程序员真得很“穷”
  4. 探索解析微服务下的RabbitMQ
  5. 学了N年英语,你学会翻译了吗?——最基本的数据库连接
  6. KVM通过qemu实现USB重定向
  7. 《手把手教你学DSP-基于TMS320F28335》书中的错误
  8. 剑三重制版怎么同步插件_剑网三重制版怎么导入插件 | 手游网游页游攻略大全...
  9. mpg文件怎么转换成mp4
  10. 编程游戏开发:如何开发制作一款游戏?你需要做哪些准备?
  11. 如何生成微信小程序二维码(uniapp开发)
  12. 积木报表JimuReport支持的15种数据库类型介绍
  13. java对象的内存分配流程
  14. express图片上传
  15. mybatis入门学习之环境的搭建——helloworld
  16. Python开发(基础):列表List
  17. 李宏毅 线性代数 Introduction
  18. 重整网站。。。。。。。。。
  19. mysql主从同步部分表_Mysql入门MySQL 主从同步部分数据表
  20. 竞业协议了解多少?如何应对?

热门文章

  1. 【sass】安装及入门教程
  2. 查看PCIE带宽的信息
  3. [MOP]出轨札记【原创】(又名:断翅的蝴蝶)
  4. java视频教程千峰_java基础教程-Java开发入门视频教程-千锋教育视频资源库
  5. wxpython动态实时绘图_wxPython 画图板
  6. 2022熔化焊接与热切割考试模拟100题及在线模拟考试
  7. Web开发者十大必备网站资源任你挑
  8. ES5新增方法、函数调用、this、严格模式、高阶函数(11th)
  9. 网络爬虫-爬取顶点小说网指定小说
  10. Android快速开发之appBase——(6).HttpReq和APICloudSDK