搬份大一假期的课设报告上来,是基于德州仪器msp430单片机开发的循迹小车。那学期学的还写了一份用三个红外灰度传感器和与或非门做的小车报告,懒得搬上来了。感谢在机器人学院通过这项目课学了相应的一些数电模电知识,还学会了Multisim、Proteus等电路仿真软件的简单使用,不然谁能想到原专业应数的人居然会学这个呢。

课程设计任务及评分标准

  1. 竞赛得分为基本任务分和“表演项”得分,小车按要求完成指定各项功能的,最高可得分为144 分。
  2. 小车循迹基本任务分:小车从蓝色起点出发,按顺序经过 Figure1 标注蓝色 1 号点得10 分、蓝色2号点再得10分,蓝色3号点再得15分、蓝色4号点再得 15分、蓝色5号点再得15分、蓝色6号点再得10分、蓝色起点再得15分,一共 90 分;
  3. “表演项”得分:“表演项”得分为可选得分,每完成一项,可得18分,没完成的项目不扣分也不得分,每项只能得一次分,且可以不按顺序得分。
    “表演项”内容如下:
    1. 当小车运动到绿色标注点①时,小车停止运动保持 5s 或以上;
    2. 当小车运动到绿色标注点②时,小车原地顺时针旋转360度或者以上;
    3. 当小车运动到绿色标注点④时,小车沿直线倒退到绿色标注点③。
      “表演项”内容开始时,参赛队员必须提醒裁判“‘表演项’开始”,同时,小车必须点亮一颗LED 灯,直至该“表演项”结束,并将 LED 灯熄灭。否则,该“表演项”不得分。
  4. 若为完成指定循迹,需在轨道上面做特殊标记的,可向核心教师团队申请,批准后可以在轨道上面做出特殊标记(每个小组仅允许作出一处特殊标记,标记不得使用总长度超过250mm 的黑胶布)。对于在指定轨迹上做出标记的小组,小车成绩扣 25 分。
  5. 每组在竞赛时,均有两次测试机会,以最好的一次成绩作为最终成绩。每轮比赛的用时为120秒,若超出120秒小车还没走完路线,则强制停止,以该停止点作为小车的终点并算分。
  6. 小车在循迹过程中应跟随轨道,若脱离轨道,必须在5秒内返回到脱离轨道前的位置,否则判定此次竞赛结束;并以小车脱离轨道点为最终得分点计算得分。
  7. 若小车在循迹过程中进入非“表演项”指定岔道, 则此次比赛成绩扣5分。每进入一个岔道扣5分,直到此次比赛结束。
  8. 小车须以轨道标记顺序依次经过各得分点,否则判定此次竞赛结束;只计算按顺序经过的得分点分数。课程设计轨道

课程设计任务解决方案

任务分析

循迹路线主要包括以下关卡:(细线、平滑曲线、Y字型分岔、垂直岔道、断口、直角拐弯)

细线 平滑曲线
Y字型分岔 垂直岔道
断口 直角拐弯

总体方案

本设计提出通过
用5个红外灰度传感器对路线进行识别,通过msp430单片机处理输入信号再进行输出控制调整电机正反转以完成前进及转向功能,使小车能够按照规定的轨迹(黑线)完成直线、直角、弯道和岔道的转向运动并最终到达终点。

核心逻辑

  1. 将三个红外灰度传感器如图排布:

  2. 方法:分析关卡,探究在各关卡时ABCD四个红外灰度传感器分别的状态。依照各状态列出真值表并设计相应程序。E传感器用作判断岔路进行中断使能,在一号表演点进行中断5秒表演。

  3. 真值表:(列出所有状况,1为检测到白色,0为检测到黑色)

    传感器A传感器B传感器C传感器D左轮右轮左轮状态右轮状态小车状态
    000000快速正转快速正转快速前进
    000100快速正转快速正转快速前进
    001000快速正转快速正转快速前进
    001110快速反转快速正转左转
    010000快速正转快速正转快速前进
    010100快速正转快速正转快速前进
    011000快速正转快速正转快速前进
    011110快速反转快速正转左转
    100000快速正转快速正转快速前进
    100100快速正转快速正转快速前进
    101000快速正转快速正转快速前进
    101110快速反转快速正转左转
    110001快速正转快速反转右转
    110101快速正转快速反转右转
    111001快速正转快速反转右转
    111100快速正转快速正转快速前进
  4. 卡诺图
    左:

    cd\ab00011110
    000000
    010000
    111101
    100000
    右:
    cd\ab00011110
    000011
    010000
    110000
    100001
  5. 通过卡诺图化简后的逻辑:
    左轮信号逻辑:$ L=(\bar{AB}+\bar{CD})*CD $
    右轮信号逻辑:$ R=(\bar{AB}+\bar{CD})*AB $
    逻辑电路:
    逻辑电路.png

    逻辑运算代码:(传感器ABCDE分别接p1.1、p1.2、p1.4、p1.5、p1.7,用A1A2A3A4A5来表示传感器的电平状态,1为高电平,0为低电平)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    int logic()
    {
    if(P1IN&BIT1)
    A1=1;
    else A1=0;
    if(P1IN&BIT2)
    A2=1;
    else A2=0;
    if(P1IN&BIT4)
    A3=1;
    else A3=0;
    if(P1IN&BIT5)
    A4=1;
    else A4=0;
    if(P1IN&BIT7)
    A5=1;
    else A5=0;
    U1=A1&&A2;
    U2=~U1;
    U3=A3&&A4;
    U4=~U3;
    U5=U2 || U4;
    U6=U5 && U3;
    U7=U5 && U1;
    OUT1= U6;
    UT2= U7;
    return 0;
    }
  6. 通过逻辑电路对关卡的解决情况:

    1. 在此点位时红外灰度传感器E将是0的状态,对中断进行使能,能对接下来的一号表演点作出反应。但要设置相应的延时用以判断这粗线,否则感应到地板的缝隙也会使能影响程序的运行。
      6197321431fde291fc3baa955d8b596e.png

      1
      2
      3
      4
      5
      6
      7
      8
      9
      if(A5==0){
      delay_us(80000);
      if(P1IN&BIT7)
      A5=1;
      else A5=0;
      if(A5==0){
      initGPIO(); // Initialize GPIO
      _BIS_SR(GIE); // Enable global interrupt
      }
    2. 在此点位时红外灰度传感器ABCD将是1001的状态,小车保持直线行走,且当B或C摆出黑线(右偏:1101,左偏1011)会往中间回摆保持始终在黑线内,同时对细线亦有相同效果,虽然摆动会加剧,但依然能完成行走。
      930502bb1d6066b749d1aa33f2bea7e4.png

    3. 在此点位时红外灰度传感器ABCD将是0111的状态,小车左转,通过左直角弯。(同理1110时可转右直角弯)
      2ba4cd84b111c456645a58620644f27c.png

    4. 在断口处红外灰度传感器ABCD将是1111的状态,小车保持直走,一直到接驳进下一段直线。
      b2f7aea328a62967abe08e70acf62a75.png

中断设置

用switch语句配合中断函数,可设置每次进入中断执行不同的语句。
1
2
3
4
5
6
7
void initGPIO(void){
P1OUT |= BIT1; // P1.3 set, else reset
P1REN &=~ BIT1; // P1.3 pullup
P1IE |= BIT1; // P1.3 interrupt enabled
P1IES |= BIT1; // P1.3 Hi-low edge
P1IFG &= ~BIT1;
}
1. 经岔道进行中断使能后,到达一号表演点传感器A感应黑线输出低电平触发第一次中断。此时b=0,进行亮灯停步5秒的表演。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#pragma vector=PORT1_VECTOR
__interrupt void Port_1 (void)
{
switch(b) {
case 0:
P2OUT |= BIT0;
P1OUT &= ~BIT0;
P1OUT &= ~BIT6;
TA1CCR1=0;
TA1CCR2=0;
delay_us(3000000);
TA1CCR1=200;
TA1CCR2=500;
P1OUT &=~ BIT6;
P1OUT &=~ BIT0;
delay_us(500000);
TA1CCR1=500;
TA1CCR2=500;
P2OUT &=~ BIT0;
__bic_SR_register( GIE );
P1IE &=~ BIT1;
break; // None
case 2:
TA1CCR1=200;
TA1CCR2=700;
P1OUT &=~ BIT6;
P1OUT &=~ BIT0;
delay_us(900000);
TA1CCR1=500;
TA1CCR2=500;
break; // None
}
b++;
P1IFG &= ~BIT1; // P1.3 IFG cleared
}
2. 当小车继续前进再经过一个岔道,b自增变为1,再到下一个岔道即变为2,然后执行中断中switch语句中的case2两轮差速左转弯90°进入下一段路线。
1
2
3
4
5
6
7
8
9
case 2:
TA1CCR1=200;
TA1CCR2=700;
P1OUT &=~ BIT6;
P1OUT &=~ BIT0;
delay_us(900000);
TA1CCR1=500;
TA1CCR2=500;
break; // None
3. 设置一个变量c=0,当b\>=2、c=0是,改用另一套逻辑,在原逻辑程序的基础上加入以下代码,使小车能差速右转弯90°拐入细线。
1
2
3
4
5
6
7
8
9
10
if(A4==0){
TA1CCR1=700;
TA1CCR2=200;
P1OUT &=~ BIT6;
P1OUT &=~ BIT0;
delay_us(550000);
TA1CCR1=500;
TA1CCR2=500;
c=1;
}

小车各部分电路

电机驱动电路:
b9bf15bc9fb3743d65e33400c81e7b11.png

搭建测试中遇到的问题及解决方案

  1. 两电机转速不一,无法正常直行,走线时摇摆剧烈,而且在关键关卡节点有较大几率出问题卡死或乱走。
    解决方法:设置定时器用两路方波控制两个马达,通过调节占空比使两个马达转速达到一致。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
        void initTimer1()
    {
    BCSCTL1=CALBC1_1MHZ;
    DCOCTL=CALDCO_1MHZ;
    TA1CTL=TASSEL_2+MC_1;
    TA1CCR0=1000;
    TA1CCTL1=OUTMOD_7;
    TA1CCTL2=OUTMOD_7;
    DCOCTL=0;
    TA1CCR1=500;
    TA1CCR2=550;
    }
  2. 电源电压不足,小车速度太慢无法在规定时间内完成所有表演及路线。
    解决方法:
    1. 购入更高电压的稳压电源。(但由于时间关系无法落实)
    2. 放弃2号及3号表演点的表演,刚好够时间让小车跑完全程。

参考文献:

[1]张宝荣.数字电子技术基础(第二版)[M].北京:电子工业出版社,2015:1-38.
[2]初永丽,王雪琪,范丽杰. 模拟电子技术基础 [M].西安: 西安电子科技大学出版社,2016
[3]MSP430G2553 Datasheet
[4]MSP430x2xx Family User’s guide

附录(源码)

展开源代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
#include <msp430.h> 
#define CPU_F ((double)1000000)
#define delay_us(x) __delay_cycles((long)(CPU_F*(double)x/1000000.0))
volatile int OUT1=0; //输出变量
volatile int OUT2=0; //输出变量
volatile int U1,U2,U3,U4,U5,U6,U7;//输入变量
volatile int A1,A2,A3,A4,A5;//输入变量
long int i=0,b=0,c=0;
volatile int a=0;
void initGPIO(void);
/*
* main.c
*/
void main(void) {
void IO_DIR();
int logic();
int logic_1();
void OUT_1();
void initTimer1();
WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer
BCSCTL1 = CALBC1_1MHZ;
DCOCTL = CALDCO_1MHZ; //主时钟频率1mhz
initTimer1();
IO_DIR();
P2OUT &=~ BIT0;
for(;;){
for(;b<=2;){
logic();
OUT_1();
}
for(;b>2&&c==0;){
logic_1();
OUT_1();
}
for(;c==1;){
logic();
OUT_1();
}


}
}
void IO_DIR()
{
P1DIR &=~ BIT1+BIT2+BIT7+BIT4+BIT5;
P2DIR |= BIT0;
P1DIR |= BIT0+BIT6;
P2DIR |= (BIT2+BIT4);
P2SEL |= BIT2;
P2SEL |= BIT4;

}
void OUT_1()
{

if(OUT1)
P1OUT |= BIT0;
else
P1OUT &= ~BIT0;

if(OUT2)
P1OUT |= BIT6;
else
P1OUT &= ~BIT6;
}


int logic()
{
if(P1IN&BIT1)
A1=1;
else A1=0;
if(P1IN&BIT2)
A2=1;
else A2=0;
if(P1IN&BIT4)
A3=1;
else A3=0;
if(P1IN&BIT5)
A4=1;
else A4=0;
if(P1IN&BIT7)
A5=1;
else A5=0;

U1=A1&&A2;
U2=~U1;
U3=A3&&A4;
U4=~U3;
U5=U2 || U4;
U6=U5 && U3;
U7=U5 && U1;
OUT1= U6;
OUT2= U7;

if(A5==0){
delay_us(80000);
if(P1IN&BIT7)
A5=1;
else A5=0;
if(A5==0){
initGPIO(); // Initialize GPIO
_BIS_SR(GIE); // Enable global interrupt
}
}
return 0;
}
int logic_1()
{
if(P1IN&BIT1)
A1=1;
else A1=0;
if(P1IN&BIT2)
A2=1;
else A2=0;
if(P1IN&BIT4)
A3=1;
else A3=0;
if(P1IN&BIT5)
A4=1;
else A4=0;
if(P1IN&BIT7)
A5=1;
else A5=0;

U1=A1&&A2;
U2=~U1;
U3=A3&&A4;
U4=~U3;
U5=U2 || U4;
U6=U5 && U3;
U7=U5 && U1;
OUT1= U6;
OUT2= U7;

if(A1&A2&A3&A4&A5){
TA1CCR1=500;
TA1CCR2=570;
OUT1= 0;
OUT2= 0;
}
if(A5==0){
delay_us(80000);
if(P1IN&BIT7)
A5=1;
else A5=0;
if(A5==0){
initGPIO(); // Initialize GPIO
_BIS_SR(GIE); // Enable global interrupt
}
}
if(A4==0){
TA1CCR1=700;
TA1CCR2=200;
P1OUT &=~ BIT6;
P1OUT &=~ BIT0;
delay_us(550000);
TA1CCR1=500;
TA1CCR2=500;
c=1;
}
return 0;
}
void initTimer1()
{
BCSCTL1=CALBC1_1MHZ;
DCOCTL=CALDCO_1MHZ;
TA1CTL=TASSEL_2+MC_1;
TA1CCR0=1000;
TA1CCTL1=OUTMOD_7;
TA1CCTL2=OUTMOD_7;
DCOCTL=0;
TA1CCR1=500;
TA1CCR2=550;
}

// Initialize GPIO

void initGPIO(void){
P1OUT |= BIT1; // P1.3 set, else reset
P1REN &=~ BIT1; // P1.3 pullup
P1IE |= BIT1; // P1.3 interrupt enabled
P1IES |= BIT1; // P1.3 Hi-low edge
P1IFG &= ~BIT1;

}

// Port 1 interrupt service routine
#pragma vector=PORT1_VECTOR
__interrupt void Port_1 (void)
{
switch(b) {
case 0:
P2OUT |= BIT0;
P1OUT &= ~BIT0;
P1OUT &= ~BIT6;
TA1CCR1=0;
TA1CCR2=0;
delay_us(3000000);
TA1CCR1=200;
TA1CCR2=500;
P1OUT &=~ BIT6;
P1OUT &=~ BIT0;
delay_us(500000);
TA1CCR1=500;
TA1CCR2=500;
P2OUT &=~ BIT0;
__bic_SR_register( GIE );
P1IE &=~ BIT1;
break; // None
case 2:
TA1CCR1=200;
TA1CCR2=700;
P1OUT &=~ BIT6;
P1OUT &=~ BIT0;
delay_us(900000);
TA1CCR1=500;
TA1CCR2=500;

break; // None
}
b++;
P1IFG &= ~BIT1; // P1.3 IFG cleared
}

本文用到工具

一个能将EXCEL表格转化成HTML格式的在线网址

吐槽

没想到从word搬运成markdown这么累,图都丢都图床上去了。
还好借这次搬运熟悉回markdown、html还有Latex的一些用法。