×

MSP430线路跟随器开源分享

消耗积分:0 | 格式:zip | 大小:0.37 MB | 2022-11-15

张波

分享资料个

描述

介绍

对于熟悉 Arduino IDE 的digitalRead()digitalWrite()的人来说,德州仪器的 Code Composer Studio 的端口方向和按位操作可能看起来有点陌生。但是请相信我,一旦您习惯了它,您将立即使用 PIC 和 MSP430 等廉价 MCU 制作史诗般的项目!

我们将使用MSP430 G2553 超值系列 MCU 随附的MSP430G2 LaunchPad 开发套件( http://www.ti.com/tool/MSP-EXP430G2)制作带有 PD 控制器的线路跟随机器人。如果您有不同的德州仪器 MCU,代码应该可以工作,但您必须检查其数据表以确保引脚支持分配给它们的某些特殊功能。(如定时器和中断)

 
pYYBAGNxieWABWFEAACofqb-xiw830.png
图 1:MSP430G2 Launchpad 套件
 

代码编写器工作室

我们将在本教程中使用 CCS。你可以在这里下载。

安装时,请确保您正在安装 MSP430 设备的所有组件。安装完成后,打开应用程序并使用 File > New创建一个新的CCS 项目。

 
poYBAGNxieeAIzaGAADxDFmxxso534.png
图 2:CCS 项目创建
 

确保选择正确的目标设备。您可以在插入电路板后使用识别按钮让 CCS 找出电路板。为您的项目命名并点击完成。

 
pYYBAGNxiemAYttcAABpBeY9cHc331.png
图 3:main.c
 

您将看到一个包含如上所示小代码片段的 main.c 文件。主循环内的乱码用于停止看门狗计时器(呃!它旁边的评论中这么说)提示:尝试突出显示该行的一个单词并按 F3。它将在单独的文件中打开 msp430g2553.h 文件,并为您提供有关该寄存器的更多信息。

设置和清除位

我们将使用 DIR 寄存器作为如何设置和清除位的示例。

MSP430 使用 DIR(方向)寄存器将引脚设置为输入或输出。此外,G2553 有两个端口,分别是 PORT1 和 2。看看你的 Launchpad。您会看到标记为 P1.0、P1.1、P2.0、P2.1 的引脚。他们的意思是什么?简单!P1.0 - 端口 1,引脚 0P2.3 - 端口 2,引脚 3

这些端口通常为 8 位长,这就是为什么每个端口只能看到 8 个引脚的原因。两个端口有两个 DIR 寄存器,它们遵循与引脚相同的命名约定。P1DIR - 端口 1 方向寄存器 P2DIR - 端口 2 方向寄存器

这些寄存器也是 8 位长,每个位对应于相关端口中的一个引脚。P1DIR的BIT1对应P1.1(Port 1 Pin 1)

正确的。那么我们如何将一个引脚设置为输入或输出呢?如果将 DIR 寄存器中某个引脚的相应位设置为 1,则该引脚为输出。同样,如果您将该位设置为 0,则该引脚将成为输入。与 Arduino 的 pinMode() 函数相同。例如 - 为了将 P2.1 设置为输出,我们必须将 P2DIR 的 BIT1 设为 1。CCS 允许我们通过使用 BIT0、BIT1、BIT2 轻松访问寄存器中的位。 ..BIT6 关键字。

尝试在 CCS 中输入“ BIT4 ”,突出显示它并按 F3 键。您将能够看到它们是如何在 msp430g2553.h 标头中定义的。

 
pYYBAGNxieyAXqBtAACVMFUxX3s607.png
图 4:标准钻头
 

您可以使用这些关键字和二进制 OR 运算符 ('|') 将寄存器中的各个位设置为 1。

P1DIR |= BIT2; //Set BIT2 of P1DIR to 1 (Sets pin 1.2 as an output)

我们正在使用 or 运算符,以便我们只对所需的位进行更改。

重置一个位是棘手的。这涉及使用二进制 AND 运算符 (' & ') 和二进制补码运算符 (' ~ ')

P1DIR &= ~BIT2; //Set BIT2 of P1DIR to 0 (Sets pin 1.2 as an input)

接线组件

现在我们知道了基础知识,让我们开始做生意。首先,使用下面的图片作为组装组件的指南。(我还附上了fritzing项目,你可以在本页底部下载)

 
pYYBAGNxie-ACd7-AAJqyAwR2ms542.png
图 5:接线
 

注意:我使用了 TCRT5000 和比较器附带的 IR 线跟随传感器。这些东西相当便宜。你可以花 2 美元买到其中的 5 个。epid=911730611&hash=item488ae89f86:g:TDUAAOSwlzZbIiiiI:rk:3: pf:0

声明和全局变量

我们将首先声明我们将使用的函数和全局变量的名称。

#include  
void MotorSetup();
void SetLeftMotorSpeed();
void SetRightMotorSpeed();
void SetSpeeds(int lspeed,int rspeed);
void IRSensorSetup();
void SonarSetup();
void ReadSonar();
int readLine();
void lineFollow();
void SetBrakes();
/**
* main.c
*/
unsigned int up_counter;
unsigned int distance_cm;
int val = 0;
int sensorpanelVal = 0;
int lastval = 0;
//PID Values
int error = 0;
int lasterror = 0;
#define BaseSpeed 160
#define Kp 18
#define Kd 30

引脚设置

让我们首先设置引脚以使用我们的声纳。我正在使用单独的函数,我将在主函数中调用一次。

void SonarSetup(){
   /* set P1.2 (TRIG)to output direction */
   P1DIR |= BIT2;
   P1OUT &= ~BIT2;                 // keep trigger at low
   /* Set P1.1 to input direction (echo)
   P1.1 is an input for Timer A0 - Compare/Capture input */
   P1DIR &= ~BIT1;
   // Select P1.1 as timer trigger input select (echo from sensor)
   P1SEL = BIT1;
   /* Timer A0 configure to read echo signal:
   Timer A Capture/Compare Control 0 =>
   capture mode: 1 - both edges +
   capture sychronize +
   capture input select 0 => P1.1 (CCI1A) +
   capture mode +
   capture compare interrupt enable */
   CCTL0 |= CM_3 + SCS + CCIS_0 + CAP + CCIE;
   /* Timer A Control configuration =>
   Timer A clock source select: 1 - SMClock +
   Timer A mode control: 2 - Continous up +
   Timer A clock input divider 0 - No divider */
   TA0CTL |= TASSEL_2 + MC_2 + ID_0;
   // Global Interrupt Enable
   _BIS_SR(GIE);
}

如果您想了解有关所用寄存器的更多信息,可以在此处查看 MSP430 G2553 数据表。

现在让我们设置控制 L298N 电机驱动器所需的引脚。

void MotorSetup(){
   P2DIR |= BIT1+BIT5;//Pin 2.1 -> left motor speed Pin 2.5 -> Right Motor Speed
   P2SEL |= BIT1+BIT5;
   P1DIR |= BIT3+BIT4; // Set Pins 1.3 and 1.4 as outputs to control left motor direction
   P1DIR |= BIT5+BIT0; // Set Pins 1.5 and 1.6 as outputs to control right motor direction
   P1OUT &= ~(BIT1+BIT4+BIT5+BIT0); //set all pins to low
   /*** Timer1_A Set-Up ***/
   TA1CCR0 |= 200 - 1;
   TA1CCTL1 |= OUTMOD_7;
   TA1CCTL2 |= OUTMOD_7;
   TA1CCR1 |= 0;
   TA1CCR2 |= 0;
   TA1CTL |= TASSEL_2 + MC_1;
}

最后,读取 5 个红外传感器的引脚。

void IRSensorSetup(){
   //Set IR sensor pins as inputs
   P2DIR &= ~(BIT0+BIT2+BIT3+BIT4); //Set pins 2.0,2.2,2.3,2.4 as inputs
   P1DIR &= ~(BIT6); //1.7 as inputs
}

辅助函数

让我们创建一些帮助函数,使我们能够轻松地控制我们的组件。这比把所有东西都塞进 main 函数要容易得多。

读取声纳的辅助功能 -

void ReadSonar(){
   P1OUT ^= BIT2;              // assert
   __delay_cycles(10);         // 10us wide
   P1OUT ^= BIT2;              // deassert
   __delay_cycles(60000);      // 60ms measurement cycle
}

控制电机的功能 -

void SetLeftMotorSpeed(int speed){
   if (speed >0){
       P1OUT &= ~BIT3; // Pin 1.3 Low
       P1OUT |= BIT4; //Pin 1.4 High
       if(speed >199){
           speed = 199;//prevent CCR2 from being negative
       }
       TA1CCR1 = speed;
   }else if(speed <0){
       P1OUT |= BIT3; //1.3 High
       P1OUT &= ~BIT4; // Pin 1.4 Low
       speed = -speed;
       if(speed >199){
           speed = 199;//prevent CCR1 from being negative
       }
       TA1CCR1 = speed;
   }
}
void SetRightMotorSpeed(int speed){
   if (speed >0){
       P1OUT &= ~BIT5;//Pin 1.5 Low
       P1OUT |= BIT0; // Pin 1.3 Low
       if(speed >199){
           speed = 199;//prevent CCR2 from being negative
       }
       TA1CCR2 = speed;
   }else if(speed <0){
       P1OUT |= BIT0; //1.5 High
       P1OUT &= ~BIT6;//Pin 1.6 Low
       speed = -speed;
       if(speed >199){
           speed = 199; //prevent CCR2 from being negative
       }
       TA1CCR2 = speed;
   }
}
void SetSpeeds(int lspeed,int rspeed){
   SetLeftMotorSpeed(lspeed);SetRightMotorSpeed(rspeed);
}
void SetBrakes(){
   P1OUT &= ~BIT5;//Pin 1.5 Low
   P1OUT &= ~BIT0; // Pin 1.3 Low
   P1OUT &= ~BIT3; // Pin 1.3 Low
   P1OUT &= ~BIT4; //Pin 1.4 Low
   TA1CCR1 = 199;
   TA1CCR2 = 199;
}

声纳中断处理程序

在触发声纳后,您将需要这个时间让 Echo 引脚变低。

#pragma vector=TIMER0_A0_VECTOR
__interrupt void Timer_A (void)
{
   if (CCTL0 & CCI)            // Raising edge
   {
       up_counter = CCR0;      // Copy counter to variable
   }
   else                        // Falling edge
   {
       // Formula: Distance in cm = (Time in uSec)/58
       distance_cm = (CCR0 - up_counter)/58;
   }
   TA0CTL &= ~TAIFG;           // Clear interrupt flag - handled
}

线路跟随

首先,我们将创建一个函数来根据行的位置返回一个值。例如,如果黑线低于第一个传感器,则函数将返回 1,如果黑线低于传感器 2 和传感器 3,则返回 2.5,依此类推。

int readLine()
{
   //from left to right. Sensor output is high when white space is detected. Since we're seeking a black line,inputs are inverted
   int sensor1 = !(P2IN&BIT0);
   int sensor2 = !(P2IN&BIT2);
   int sensor3 = !(P2IN&BIT3);
   int sensor4 =!(P1IN&BIT6);
   int sensor5 =!(P2IN&BIT4);
   int sum = 0;
   sensorpanelVal = (sensor1 * 1)+(sensor2* 2)+(sensor3 * 3)+(sensor4 *4)+(sensor5*5);
   sum = (sensor1+sensor2+sensor3+sensor4+sensor5);
   if (sum ==0){
       return lastval;
   }else{
   lastval = sensorpanelVal/sum;
   return lastval;
   }
}

现在我们有了一个函数,可以根据黑线的位置给我们一个数值,我们可以开发一个比例和微分控制器来将位置保持在指定的设定点。

void lineFollow(){
   val = readLine();
   error = 3-val;
   int delta = error-lasterror;
   int change = Kp*error + Kd*delta;
   lasterror = error;
   int leftMotorPWM = BaseSpeed -change;
   //constrain PWM
   if(leftMotorPWM >199){
       leftMotorPWM = 199;
   }else if(leftMotorPWM <0){
       leftMotorPWM = 0;
   }
   //constrain PWM
   int rightMotorPWM = BaseSpeed + change;
   if(rightMotorPWM >199){
           rightMotorPWM = 199;
   }else if(leftMotorPWM <0){
           rightMotorPWM = 0;
   }
   SetSpeeds(leftMotorPWM,rightMotorPWM);
}

就是这样!现在您所要做的就是设置您的主要功能以运行引脚设置,并连续运行 linefollow() 功能。

int main(void)
{
   WDTCTL = WDTPW | WDTHOLD;   // stop watchdog timer
   MotorSetup();
   IRSensorSetup();
   SonarSetup();
   while(1){
       lineFollow();
       if(sensorpanelVal ==15){
           SetBrakes();
           while(1){
           }
       }
   }
}

在这里,如果机器人的所有 5 个传感器都在黑色表面上,我将无限期地停止机器人,但您可以使用声纳读数或任何其他 IR 传感器值组合来触发不同的功能。(可以使用全局变量 sensorpanelVal 访问)

结论

您可以修改 linefollow() 和 readline() 函数以适合您的机器人和您的表面。如果您需要在黑色表面上跟随白线,请将 readline() 中的线替换为这些线,

int sensor1 = (P2IN&BIT0); //For white line on black surface
   int sensor2 = (P2IN&BIT2);
   int sensor3 = (P2IN&BIT3);
   int sensor4 = (P1IN&BIT6);
   int sensor5 = (P2IN&BIT4);

您还必须调整 Kp 和 Kd 值,以使您的机器人沿着线走而不会丢失它。


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

评论(0)
发评论

下载排行榜

全部0条评论

快来发表一下你的评论吧 !

'+ '

'+ '

'+ ''+ '
'+ ''+ ''+ '
'+ ''+ '' ); $.get('/article/vipdownload/aid/'+webid,function(data){ if(data.code ==5){ $(pop_this).attr('href',"/login/index.html"); return false } if(data.code == 2){ //跳转到VIP升级页面 window.location.href="//m.lene-v.com/vip/index?aid=" + webid return false } //是会员 if (data.code > 0) { $('body').append(htmlSetNormalDownload); var getWidth=$("#poplayer").width(); $("#poplayer").css("margin-left","-"+getWidth/2+"px"); $('#tips').html(data.msg) $('.download_confirm').click(function(){ $('#dialog').remove(); }) } else { var down_url = $('#vipdownload').attr('data-url'); isBindAnalysisForm(pop_this, down_url, 1) } }); }); //是否开通VIP $.get('/article/vipdownload/aid/'+webid,function(data){ if(data.code == 2 || data.code ==5){ //跳转到VIP升级页面 $('#vipdownload>span').text("开通VIP 免费下载") return false }else{ // 待续费 if(data.code == 3) { vipExpiredInfo.ifVipExpired = true vipExpiredInfo.vipExpiredDate = data.data.endoftime } $('#vipdownload .icon-vip-tips').remove() $('#vipdownload>span').text("VIP免积分下载") } }); }).on("click",".download_cancel",function(){ $('#dialog').remove(); }) var setWeixinShare={};//定义默认的微信分享信息,页面如果要自定义分享,直接更改此变量即可 if(window.navigator.userAgent.toLowerCase().match(/MicroMessenger/i) == 'micromessenger'){ var d={ title:'MSP430线路跟随器开源分享',//标题 desc:$('[name=description]').attr("content"), //描述 imgUrl:'https://'+location.host+'/static/images/ele-logo.png',// 分享图标,默认是logo link:'',//链接 type:'',// 分享类型,music、video或link,不填默认为link dataUrl:'',//如果type是music或video,则要提供数据链接,默认为空 success:'', // 用户确认分享后执行的回调函数 cancel:''// 用户取消分享后执行的回调函数 } setWeixinShare=$.extend(d,setWeixinShare); $.ajax({ url:"//www.lene-v.com/app/wechat/index.php?s=Home/ShareConfig/index", data:"share_url="+encodeURIComponent(location.href)+"&format=jsonp&domain=m", type:'get', dataType:'jsonp', success:function(res){ if(res.status!="successed"){ return false; } $.getScript('https://res.wx.qq.com/open/js/jweixin-1.0.0.js',function(result,status){ if(status!="success"){ return false; } var getWxCfg=res.data; wx.config({ //debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。 appId:getWxCfg.appId, // 必填,公众号的唯一标识 timestamp:getWxCfg.timestamp, // 必填,生成签名的时间戳 nonceStr:getWxCfg.nonceStr, // 必填,生成签名的随机串 signature:getWxCfg.signature,// 必填,签名,见附录1 jsApiList:['onMenuShareTimeline','onMenuShareAppMessage','onMenuShareQQ','onMenuShareWeibo','onMenuShareQZone'] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2 }); wx.ready(function(){ //获取“分享到朋友圈”按钮点击状态及自定义分享内容接口 wx.onMenuShareTimeline({ title: setWeixinShare.title, // 分享标题 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享给朋友”按钮点击状态及自定义分享内容接口 wx.onMenuShareAppMessage({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 type: setWeixinShare.type, // 分享类型,music、video或link,不填默认为link dataUrl: setWeixinShare.dataUrl, // 如果type是music或video,则要提供数据链接,默认为空 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享到QQ”按钮点击状态及自定义分享内容接口 wx.onMenuShareQQ({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享到腾讯微博”按钮点击状态及自定义分享内容接口 wx.onMenuShareWeibo({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享到QQ空间”按钮点击状态及自定义分享内容接口 wx.onMenuShareQZone({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); }); }); } }); } function openX_ad(posterid, htmlid, width, height) { if ($(htmlid).length > 0) { var randomnumber = Math.random(); var now_url = encodeURIComponent(window.location.href); var ga = document.createElement('iframe'); ga.src = 'https://www1.elecfans.com/www/delivery/myafr.php?target=_blank&cb=' + randomnumber + '&zoneid=' + posterid+'&prefer='+now_url; ga.width = width; ga.height = height; ga.frameBorder = 0; ga.scrolling = 'no'; var s = $(htmlid).append(ga); } } openX_ad(828, '#berry-300', 300, 250);