单片机系统 课 程 设 计
成绩评定表
设计课题 : 自动避障小车 学院名称 : 电气工程学院 专业班级 : 自动1105 学生姓名 : 学 号 : 指导教师 : 设计地点 : 31-630 设计时间 :
单片机系统 课 程 设 计
课程设计名称:
专 业 班 级 : 自动1105 学 生 姓 名 : 学 号 : 指 导 教 师 : 课程设计地点: 31-630 课程设计时间:
目 录
1概述 --------------------------------------------------------------------------------------------- 4
1.1研究背景 -------------------------------------------------------------------------------- 4 1.2设计思想及基本功能 ----------------------------------------------------------------- 4 2总体方案设计 --------------------------------------------------------------------------------- 4
2.1方案论证 -------------------------------------------------------------------------------- 4 2.2系统框图 -------------------------------------------------------------------------------- 5 2.3总体方案设计 -------------------------------------------------------------------------- 6 3硬件电路设计 --------------------------------------------------------------------------------- 7
3.1电源电路 -------------------------------------------------------------------------------- 7 3.2晶振电路 -------------------------------------------------------------------------------- 8 3.3复位电路 -------------------------------------------------------------------------------- 8 3.4键盘电路 -------------------------------------------------------------------------------- 8 3.5显示电路 -------------------------------------------------------------------------------- 9 3.6超声波测距电路 ---------------------------------------------------------------------- 10 3.7舵机电路 ------------------------------------------------------------------------------- 11 3.8电机驱动电路 ------------------------------------------------------------------------- 12 3.9电机转速测量电路 ------------------------------------------------------------------- 13 3.10设计PCB和腐蚀电路板 ---------------------------------------------------------- 14 4系统软件设计 -------------------------------------------------------------------------------- 16
4.1分模块程序设计 ---------------------------------------------------------------------- 18 4.2主程序设计 ---------------------------------------------------------------------------- 20 5系统调试 -------------------------------------------------------------------------------------- 20 6总结 -------------------------------------------------------------------------------------------- 22 参考文献: -------------------------------------------------------------------------------------- 23 附录A硬件电路图 ---------------------------------------------------------------------------- 24 附录B 源程序 --------------------------------------------------------------------------------- 25
1概述
1.1研究背景
科技的发展趋势之一就是让几乎所有的东西具有一定的智能。这样的智能一方面可以避免人的复杂性带来的错误,另一方面,作为人的能力的延伸,快速、便捷地适应环境。本文研究的超声波自动避障小车,就是让小车具有一定的智能,可以作为有人驾驶车辆的一部分,帮助司机及早发现司机还未觉察的危险。另外,可以应用在无人控制系统里,代替人类完成信息接收、处理和判断。那么这样它还有更广阔的应用和发展空间。这个月我国嫦娥三号载着玉兔号月球车踏上了月球,已经开始探索活动。玉兔号月球车可以自动寻找有价值的目标,自动避开障碍物,自动寻找最优路径等等,这些功能在原理上都差不多,只是所用仪器以及控制算法的不同。
1.2设计思想及基本功能
小车设计的基本思路是:不断扫描前方180°左右的水平面内的物体,根据反馈的距离和方位信息确定前方的地形是开阔还是多障碍。然后根据这些信息通过路径最优算法选择前进方向。
小车的基本功能是识别空旷区和多障区;自动避开障碍物;能够在空旷区快速行驶。
2总体方案设计
2.1方案论证
首先是测距方案: 方案一:激光测距
优点:测速快,适应范围广,精确;不过相对于自动避障小车系统,有点大材小用。激光测距是点对点的测距,一般进行远距离测距比较精确。如是近距离
激光测距,必须要涉及到微小时间测量,51MCU无法胜任。因此激光测距方案否定。
方案二:超声波测距
优点:结构简单,成本低,便于与MCU联机工作。测量范围从5CM到几百米的距离内精度在毫米级。尽管它测量的是点到面的距离,但是定向性满足本小车系统的要求。而且,数据处理简单。可以考虑。
方案三:雷达测距
不太现实,受到本人技术水平限制,无法驾驭雷达测距系统,此外,雷达应用于较大范围的测量和监控,如果用于本小车,同样涉及到微小时间测量,以及雷达数据处理,51MCU难以胜任。否定!
方案四:红外线测距
优点:技术难度低,数据处理简单,但是测量开关信号比较好,测量连续信号不够准确。测量的精度有点低。因此不是一种较好的测距方法。
综合各因素,选用超声波测距。 其次是微控制器的选择:
可选的微控制器有:51系列的STC89C52RC、STC12C5A60S2;TI公司的MSP430G2553;飞思卡尔的MC9S12XS128MAL;意法的STM32F407-Discovery;(这些玩儿过一点,而且最小系统板手边都有)
根据本系统的需求,需要两路同频率(50hz)的PWM控制两个舵机、一路PWM频率约几千赫兹控制电机、三个个定时器做脉冲捕捉、至少两个外部中断输入、一个并行6800总线接口。除了51系列的两款,其他一片足以胜任。而且对于意法的F407显得大材小用。飞思卡尔的S12比较合适;TI的G2553的引脚有点少(20个),外围电路设计的会复杂一点,成本也会高一点。60S2用两片比较合适; 用89C52比较麻烦,两片也不够。根据个人能力,以及时间和进度方面考虑选用60S2,60S2呢,它的资源有:完全兼容51 的两个定时器T0、T1和两个外部中断;两路拥有独立波特率的串行口;一个PCA定时器,可产生两路同频的PWM;八个10位精度的AD 转换通道; 一个硬件SPI接口。
2.2系统框图
系统框图如图2.2所示
图2.2系统框图
2.3 总体方案设计
两片60S2一个做主机,一个做从机,通过串行口通信,传递数据。 主机用到的功能有:
(1)、定时器T1和外部中断INT1,作用是:测脉宽,用到的IO是P3^3;P2^0作为超声波测距模块的触发功能引脚;
(2)、定时器T0和PCA产生两路同频率的PWM,作用是:T0的溢出率作为PCA的计数脉冲产生50HZ的PWM控制两路舵机。使用T0作为PCA的时钟源,可以通过改变T0的溢出率改变输出的频率。用到的IO是P1^3和P1^4;
(3)、外部中断INT0接一个按键。
(4)、串口一使用独立波特率发生器,与从机通P3^0和P3^1;
(5)、并行数据接口:P0口作为6800并行总线的数据总线P2^7、P2^6和P2^5作为控制总线,分别为:EN(总使能)、R\W(读写控制)、C\D(命令数据控制);地址总线CS接地,一直处于选通状态。 从机用到的功能:
(1)、串行口一使用独立波特率发生器P3^0和P3^1与主机通讯; (2)、PCA产生一路PWM,P1^2共同控制电机速度。
(3)、定时器T0和T1作为编码器脉冲计数和测脉宽使用,测量电机的转速。
3硬件电路设计
首先是单片机的最小系统如图3.1、3.2所示
图3.1 单片机的最小系统
图3.2 单片机的最小系统
然后是各个模块电路:
3.1电源电路
*
本系统由7.5V可充电电池供电,由开关电源LM2596转换成5V电压给单片机
和测距模块供电。出于对系统的稳定性、可靠性和成本的考虑,选用了开关电源模块作为转换电路。电源部分电路如图3.3
图3.3 电源部分电路
图3.4 电源模块实物
3.2晶振电路
虽然60S2内部有RC振荡电路作为时钟电路的输入源,但是外部连接一个无源晶振电路,可以使单片机的时钟频率更稳定,因而工作更可靠。电路参数:四个电容均为22PF,作为石英晶振频率的微调。石英晶振的频率为12MHZ。
3.3复位电路
复位电路是单片机必不可少的基本电路,在调试阶段,可以按下复位按键,使复位引脚保持两个机器周期的低电平,就可以使单片机内部所有电路初始化。电路参数:
电容
1UF只要保证电容的时间常数大于两个机器周期即可。电阻值为1K欧。
3.4键盘电路
键盘电路如图3.5所示
法检测。
图3.5键盘电路
可以利用外部中断法或查询法检测按键是否按下。这里都采用外部中断的方
3.5显示电路
显示电路如图3.6所示
图3.6显示电路
采用1603液晶屏显示数字和字符。1603与1602的硬件接口完全一样。不同之处在于1603的RAM区的内容可以全部同时显示在屏幕上,没有屏幕内容的
位移切换。可以显示四行。状态指示灯如图3.7所示
图3.7 状态指示灯
这几个LED灯是作为状态指示用的,方便调试。
3.6超声波测距电路
超声波测距模块如图3.8所示
图3.8超声波测距模块
这是超声波模块的接口电路。这个超声波的工作原理是: (1)采用 IO 触发测距,给至少 10us 的高电平信号;
(2)模块自动发送 8 个 40khz 的方波,自动检测是否有信号返回; (3)有信号返回,通过 IO 输出一高电平,高电平持续的时间就是
(4)超声波从发射到返回的时间.测试距离=(高电平时间*声速(340M/S))/2;
超声波模块测距工作时序图:
触发信号(由单片机产生一个宽度大于10US的高脉冲,触发模块工作一次):
_|¯¯¯¯|_______________________________________________|¯¯¯¯ 模块内部发出的信号(循环发出8个40KHZ的脉冲):
______|¯|_|¯|_|¯|_|¯|_|¯|_|¯|_|¯|_|¯|___________________ 输出回响信号(回响信号的高电平持续时间正比于检测的距离):
____________________________________|¯¯¯¯¯¯¯¯¯¯¯¯¯¯|____
3.7舵机电路
舵机电路如图3.9所示
图3.9舵机电路
舵机驱动电路。舵机接口是信号线、电源正、地。信号线一般可以直接接到单片机的IO上,但是接多了就不行了,电流太大。这里用了一级三极管驱动。通常用的这种舵机,控制信号是一个周期为20ms占空比在2.5%—12.5%之间的PWM信号,分别对应舵机的控制角为0°—180°之间。舵机本身有反馈调节,因此,单片机给出一个占空比,舵机就到某一个角度。
3.8电机驱动电路
电机驱动电路如图3.10所示
图3.10电机驱动模块
电机的控制采用的是L298N全桥驱动电路。 驱动电路如图3.11所示
电机正反转原理:
3.9电机转速测量电路
电机转速测量电路如图3.12所示
图3.11 驱动电路
PWM: |_|¯|_|¯|_|¯|_|¯|_|¯|_|¯|_|¯|_|¯|_|¯|_|¯|_|¯|_
电流流向:¦↑¦↑¦¦↑¦¦↑¦¦↑¦ ¦↓¦¦↓¦¦↓¦¦↓¦¦↓¦¦↓
IO: _____________________|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
|←假设此时电机正转→|←‐ 该时刻电机反转‐→|
图3.12电机转速测量电路
编码器输出信号:AB两相输出矩形正交脉冲,电路输出为NPN集电极开路输出型,此种输出类型可以和带内部上拉电阻的单片机。
接线方式:黄=A相,蓝=B相,红=Vcc正电源,黑=V0地。 编码器输出信号图如图3.13所示
图3.13信号图
3.10设计PCB和腐蚀电路板
这是原始的PCB图和单面的腐蚀电路板,红色的TOPLAYER层作为跳线。PCB如图3.14所示
图3.15 打印PCB负片图
图3.16 腐蚀效果图
4系统软件设计
主程序的流程图如图4.1所示。
图4.1主程序流程图
主程序流程说明:
电路主要分为以下几个部分,分别是电源部分、显示部分、按键部分、步进电机控制部分、超声波检测模块、舵机模块、单片机主控器件部分,各部分具有不同的子程序。
系统初始化流程如图4.2所示:
图4.2 系统初始化流程图
4.1分模块程序设计 4.1.1键盘程序
小车系统需要的按键不多,有两个,采用中断法。在系统中的作用是启动小车。
4.1.2舵机驱动程序和电机驱动程序
控制原理部分在叙述硬件时已介绍。这里说说具体怎么实现。
产生50HZ的PWM控制舵机:时钟频率Fosc=11059200HZ,定时器T0的溢出率Ft0作为PCA定时器的时钟输入。60S2工作在51模式下。设TO的初值为X,自动重装模式,T0、PCA均无中断。计算方法为:
X*(12/Fosc)*256=0.02s
可以得出X=72,TH0=0xB7。
再计算占空比:PCA的中心计数器每(72*12/Fosc)秒记一个数,记256个数(即一个八位计数器)溢出再从零开始。比较寄存器的取值是:256*0.025和256*0.125之间,对应的转角在0到180度。在实际情况下,舵机不够精密,最大旋转角没有180度,而且比较寄存器只能比较写入整数。实际的取值为7—29,控制方向的舵机取值为17——21(分级明显,转向不够舒畅啊)。
电机的频率没有严格的要求,因此,选用简单的处理:对时钟进行 256*12分频,是3.6KHZ。
4.1.3超声波测距程序
超声波测距实现原理就是测量高电平脉冲持续的时间。用到了51定时器启停由外部控制的功能。GATE=1,TRn=1,定时器由外部中断引脚电平控制,上升沿之后开始计数,下降沿之后停止计数。同时打开相应的外部中断,在下降沿到来后进入外部中断服务程序,记录停止计数时的数值。
这里应当注意一个问题是:如果测量的距离较远,定时器可能就溢出了,对这个不处理,结果将是错误的。因此同时开启定时器中断,在溢出时,记下溢出的次数。距离的计算方法是:
距离=【Number*(12/Fosc)+N*65536*(12/Fosc)】/2 void Int1_Routine(void) interrupt 2 {
T=(TH1*256+TL1); TH1=0; TL1=0;
juli=((float)T1_Count)*11141.12+0.1844618*((float)T); LCD_Write_Number(0,2,juli,5); T1_Count=0; T1_F=1;
}
4.2主程序设计
主程序里主要是避障程序。我的思路是,把区域三部分:近距离20CM以内,适中区域(20CM—150CM),远距离(150CM以外)。得到22组数据后首先是根据区域分类,然后记各区域的角度大小,当远距离的角度范围最大时,判断为空旷区,其他是多障碍区。然后根据角度大小排序,当适中区最大时,走的方向为适中区集中的方向。当最大区域为近距离或远距离时,走远距离区,具体方向为最远点方向。
速度总是由最近的障碍物距离计算得出。
5系统调试
首先是分模块调试。把各个模块的功能分开实现。然后是部分的组合到一起使用,如超声波测距和1603显示、两路舵机同时控制。
在软件调试的时候,出现了RAM区不够用。原因出在避障算法上。定义的数组多,而且长。用户128RAM不够用。可以有两个解决办法:一是改进代码,使用指针;二是扩展RAM区。考虑到代码优化不是一天两天就弄好的,而且我的C语言编程水平实在浅薄,指针依然是用不好,一用就报错。那只好扩展RAM。查查60S2的数据手册,它取消了外部扩展RAM和ROM的功能,但是内部增加了1KB的RAM,用法是在定义变量时,加前缀“XDATA”,告诉编译器把后面的变量定义在扩展RAM区。此外我还了解到“DATA”、“PDATA”、“IDATA”都是51系统编译器允许的关键字,具体的解释是:
dATa: 固定指前面0x00-0x7f的128个RAM,可以用acc直接读写的,速度最快,生成的代码也最小。 idATa: 固定指前面0x00-0xff的256个RAM,其中前128和dATa的128完全相同,只是因为访问的方式不同。idATa是用类似C中的指针方式 访问的。汇编中的语句为:mox ACC,@Rx.(不重要的补充:c中idATa做指针式的访问效果很好) xdATa: 外部扩展RAM,一般指外部0x0000-0xffff空间,用DPTR访问。 pdATa: 外部扩展RAM的低256个字节,地址出现在A0-A7的上时读写,用movx ACC,@Rx读写。
硬件方面的调试主要在模块调试方面。因为基本上都用的现有的模块电路,自己设计的电路基本没有(自己还差的很远唉!),没有什么难的。
小车主板:
图5.1 小车主板
整车图片:
图5.2 整车鸟瞰图
6总结
两周的单片机课程设计很快就结束了,但是在进军单片机领域,这仅仅是个开始。学习单片机最重要的是实践。要投入必要的成本。一块开发板必不可少,还有一大堆配套的硬件软件。
作为有志于今后继续在单片机方面发展的我,学会使用51单片机,远远不够,世界上51系列占的市场份额并不多。流行的单片机,其功能远远好于51,因此,学习51单片机重点在于利用它简单易学的特点掌握单片机的各种通用性的东西,深刻理解和掌握它的工作原理和使用方式,这样可以快捷的掌握其他单片机的使用。把单片机作为改变生活的万能工具。
学习单片机,离不开编程,离不开软件。C语言作为一种高级语言,与硬件联系紧密。因此在单片机领域,基本都用的是C语言。灵活运用C语言,对单片机学习带来极大的便利。今后还得继续深入学习C语言。还有一点,非常重要,把软件工程的思想融入到单片机的编程中。比如分层和封装思想。这个课程设计中所有的程序,并不是都在这两周写的,有一些以前玩过类似的模块,当然有可靠地例程,在这里,修改一下硬件接口,修改一下工作方式,就可以用了。由此看出,单片机学习是可以积累的。除了掌握大量的知识,还有个人的单片机例程、软件、电路设计库,这样会越来越容易。
参考文献:
【1】《单片微型计算机原理、应用及接口技术》(第2版)张迎新.[M].北京:国防工业出版社,2004
【2】 夏路易 石宗义.Protel 99se电路原理图与电路板设计教程.北京:北京希望电子出版社,2004
【3】《单片微型计算机原理、应用及接口技术》(第2版)张迎新.[M].北京:国防工业出版社,2004
【4】宏晶STC12单片机技术手册
附录A硬件电路图
图A.1 整车鸟瞰图
附录B 源程序
工程文件结构:
图B.1 整车鸟瞰图
STC12C5A60S2增加的特殊功能寄存器与地址的映射:
#ifndef __REG52_H__
#define __REG52_H__
/* BYTE Registers */
sfr AUXR = 0x8E;/*stc12c5a60s2*/
sfr WAKE_CLK0=0x8F;/*stc12c5a60s2*/
sfr P1M1 = 0x91;/*stc12c5a60s2*/
sfr P1M0 = 0x92;/*stc12c5a60s2*/
sfr P0M1 = 0x93;/*stc12c5a60s2*/
sfr P0M0 = 0x94;/*stc12c5a60s2*/
sfr P2M1 = 0x95;/*stc12c5a60s2*/
sfr P2M0 = 0x96;/*stc12c5a60s2*/
sfr CLK_DIV=0x97;/*stc12c5a60s2*///Clock Divder
sfr S2CON = 0x9A;/*stc12c5a60s2*/
sfr S2BUF = 0x9B;/*stc12c5a60s2*/
sfr BRT = 0x9C;/*stc12c5a60s2*/
sfr BUS_SPEED=0xA1;/*stc12c5a60s2*/
sfr AUXR1 = 0xA2;/*stc12c5a60s2*/
sfr IE2 = 0xAF;/*stc12c5a60s2*/
sfr P3M1 = 0xB1;/*stc12c5a60s2*/
sfr P3M0 = 0xB2;/*stc12c5a60s2*/
sfr P4M1 = 0xB3;/*stc12c5a60s2*/
sfr P4M0 = 0xB4;/*stc12c5a60s2*/
sfr IP2 = 0xB5;/*stc12c5a60s2*/
sfr IP2H = 0xB6;/*stc12c5a60s2*/
sfr IPH = 0xB7;/*stc12c5a60s2*///Interrupt Priority High
sfr P4SW = 0xBB;/*stc12c5a60s2*///P4 I/O Switch
sfr ADC_CONTR=0xBC;/*stc12c5a60s2*/
sfr ADC_RES=0xBD;/*stc12c5a60s2*/
sfr ADC_RESL=0xBE;/*stc12c5a60s2*///A/D Result Low
sfr P4 = 0xC0;/*stc12c5a60s2*///P4 I/O
sfr WDT_CONTR=0xC1;/*stc12c5a60s2*///Watch-Dog-Timer Cotrol Register sfr IAP_DATA=0xC2;/*stc12c5a60s2*///ISP/IAP Flash Data Register sfr IAP_ADDRH=0xC3;/*stc12c5a60s2*///ISP/IAP Flash Address High sfr IAP_ADDRL=0xC4;/*stc12c5a60s2*///ISP/IAP Flash Address Low sfr IAP_CMD= 0xC5;/*stc12c5a60s2*///ISP/IAP Flash Command Register sfr IAP_TRIG=0xC6;/*stc12c5a60s2*///ISP/IAP Flash Command Trigger sfr IAP_CONTR=0xC7;/*stc12c5a60s2*///ISP/IAP Control Register sfr P5 = 0xC8;/*stc12c5a60s2*///P5 I/O
sfr P5M1 = 0xC9;/*stc12c5a60s2*/
sfr P5M0 = 0xCA;/*stc12c5a60s2*/
sfr TL2 = 0xCC; /*8052*/
sfr SPSTAT= 0xCD;/*stc12c5a60s2*///SPI Control Register
sfr SPCTL = 0xCE;/*stc12c5a60s2*///SPI Status Register
sfr SPDAT = 0xCF;/*stc12c5a60s2*///SPI Data Register
sfr CCON = 0xD8;/*stc12c5a60s2*///PCA
sfr CMOD = 0xD9;/*stc12c5a60s2*///PCA
sfr CCAPM0= 0xDA;/*stc12c5a60s2*///PCA
sfr CCAPM1= 0xDB;/*stc12c5a60s2*///PCA
sfr CL = 0xE9;/*stc12c5a60s2*/
sfr CCAP0L= 0xEA;/*stc12c5a60s2*/
sfr CCAP1L= 0xEB;/*stc12c5a60s2*/
sfr PAC_PWM0=0xF2;/*stc12c5a60s2*///PCA PWM
sfr PAC_PWM1=0xF3;/*stc12c5a60s2*///PCA PWM
sfr CH = 0xF9;/*stc12c5a60s2*/
sfr CCAP0H= 0xFA;/*stc12c5a60s2*/
sfr CCAP1H= 0xFB;/*stc12c5a60s2*/
/* BIT Registers */
/*CCON*/
sbit CF =CCON^7;
sbit CR =CCON^6;
sbit CCF1 =CCON^1;
sbit CCF0 =CCON^0;
#endif
LCD1603显示程序文件:
#include"reg52.h"
#include"define.h"
#include"delay.h"
#include"1603.h"
bit LCD_Check_Busy(void)
{
u8 result;
EN=0;
DataPort= 0xFF;
D_C=0;
R_W=1;
delay_ms(1);
EN=1;
result=(DataPort & 0x80);
EN=0;
return result;
}
void LCD_Write_Comm(u8 comm)
{
while(LCD_Check_Busy());
EN=0;
D_C=0;
R_W=0;
EN=1;
delay_ms(1);
DataPort=comm;
EN=0;
}
void LCD_Write_Data(u8 dat)
{
while(LCD_Check_Busy());
EN=0;
D_C=1;
R_W=0;
EN=1;
} delay_ms(1); DataPort=dat; EN=0;
void LCD_Write_Char(u8 x,u8 y,u8 Data)
{
}
void LCD_Write_String(u8 x,u8 y,u8 *s)
{
}
void LCD_Write_Number(u8 x,u8 y,u32 number,u8 Nbit) {
u8 dat,ten,hun,thu,tenthu,hunthu; hunthu=48+(number/100000);
switch(y) { case 1:LCD_Write_Comm(0x80+x); break; break; break; break; case 2:LCD_Write_Comm(0xC0+x); case 3:LCD_Write_Comm(0x90+x); case 4:LCD_Write_Comm(0xD0+x); } LCD_Write_Data(Data); switch(y) { case 1:LCD_Write_Comm(0x80+x); break; break; break; break; case 2:LCD_Write_Comm(0xC0+x); case 3:LCD_Write_Comm(0x90+x); case 4:LCD_Write_Comm(0xD0+x); } while(*s) { } LCD_Write_Data(*s); s++;
tenthu=48+(number%100000/10000); thu=48+(number%10000/1000); hun=48+(number%1000/100); ten=48+(number%100/10); dat=48+(number%10); switch(y) { } { case 6: LCD_Write_Data(hunthu); LCD_Write_Data(tenthu); LCD_Write_Data(thu); LCD_Write_Data(hun); LCD_Write_Data(ten); LCD_Write_Data(dat); break; LCD_Write_Data(tenthu); LCD_Write_Data(thu); LCD_Write_Data(hun); LCD_Write_Data(ten); LCD_Write_Data(dat); break; LCD_Write_Data(thu); LCD_Write_Data(hun); LCD_Write_Data(ten); LCD_Write_Data(dat); break; LCD_Write_Data(hun); LCD_Write_Data(ten);
case 1:LCD_Write_Comm(0x80+x); break; break; break; break; case 2:LCD_Write_Comm(0xC0+x); case 3:LCD_Write_Comm(0x90+x); case 4:LCD_Write_Comm(0xD0+x); switch (Nbit) case 5: case 4: case 3:
}
{
} } LCD_Write_Data(dat); break; LCD_Write_Data(ten); LCD_Write_Data(dat); break; LCD_Write_Data(dat); break; case 2: case 1: void LCD_Clear(void) LCD_Write_Comm(0x01); delay_ms(1);
void LCD_Init(void)
{
}
#ifndef _1603_H_
LCD_Write_Comm(0x01); delay_ms(1); LCD_Write_Comm(0x02); delay_ms(1); LCD_Write_Comm(0x38); delay_ms(1); LCD_Write_Comm(0x0C); delay_ms(1); LCD_Write_Comm(0x06); delay_ms(1); LCD_Write_Comm(0x80); delay_ms(1); LCD_Write_Data(0xFF); delay_ms(1); LCD_Write_Data(0xFF); delay_ms(1); LCD_Write_Comm(0x8E); delay_ms(1); LCD_Write_Data(0xFF); delay_ms(1); LCD_Write_Data(0xFF); delay_ms(1);
#define _1603_H_
sbit buzz=P2^3;
sbit D_C= P1^0;
sbit R_W= P1^1;
sbit EN = P2^5;
#define DataPort P0
bit LCD_Check_Busy(void);
void LCD_Write_Comm(u8 comm);
void LCD_Write_Data(u8 dat);
void LCD_Write_Char(u8 x,u8 y,u8 Data);
void LCD_Write_String(u8 x,u8 y,u8 *s);
void LCD_Write_Number(u8 x,u8 y,u32 number,u8 Nbit); void LCD_Clear(void);
void LCD_Init(void);
串行口程序文件:
#endif
#include"reg52.h"
#include"define.h"
#include"delay.h"
u8 receive;
u16 n;
u8 flagR;
u8 stopF;
void COMInit(u16 bps)
{
u8 s; EA=0; if(bps>=19200) { } else { } SCON=0x50; TMOD=0x20; TH1=256-(28800*(s+1))/bps; TL1=256-(28800*(s+1))/bps; REN=1;
PCON|=0x80; s=1; PCON&=0x7F; s=0;
ES=1; EA=1; TR1=1;
}
void serial() interrupt 4
{
if(TI!=0)
{
TI=0;
flagR=1;
}
else
{
if(RI!=0)
{
receive=SBUF;
P1=receive;
if(receive==0xFF)
{
stopF=1;
}
else
{
if(receive==0x5A)
{
stopF=0;
}
}
RI=0;
}
}
}
#ifndef _COM_H_
#define _COM_H_
void COMInit(u16 bps);
#endif
自定义变量类型文件:
#ifndef _DEFINE_H_
#define _DEFINE_H_
#define u8 unsigned char
#define u16 unsigned int
#define u32 unsigned long
#endif
各种延时函数文件:
#include"define.h"
#include"delay.h"
void delay_us(u16 xus)
{
xus=2*xus;
while(xus--);
}
void delay_ms(u16 xms)
{
int i,j;
for(i=xms;i>0;i--)
{
for(j=110;j>0;j--);
}
}
#ifndef _DELAY_H_
#define _DELAY_H_
void delay_us(u16 xus);
void delay_ms(u16 xms)
各个外设中断服务函数文件:
#ifndef _IRQ_H_
#define _IRQ_H_
#include"define.h"
#include"1603.h"
u8 key_flag=0;
u8 receive=0;
float juli;
u16 T;
u8 T1_Count=0;
bit T1_F=0;
void Int0_Routine(void) interrupt 0
{
if(IE0)
{
delay_ms(800);
if(IE0)
{
key_flag++;
}
}
}
void Int1_Routine(void) interrupt 2
{
T=(TH1*256+TL1); TH1=0; TL1=0; juli=((float)T1_Count)*11141.12+0.1844618*((float)T); LCD_Write_Number(0,2,juli,5);
T1_Count=0;
T1_F=1;
}
void Timer1_Routine(void) interrupt 3
{
T=(TH1*256+TL1);
TH1=0;
TL1=0;
T1_Count++;
}
void UART_Routine(void) interrupt 4
{
if(TI)
{
TI=0;
}
else
{
if(RI)
{
receive=SBUF;
RI=0;
}
}
}
#endif
脉宽测量初始化文件:
#include
#include"define.h"
#include"delay.h"
#include"1603.h"
#include"capture.h"
void capture_init(void)
{
} TMOD|=0x90; TH1=0; TL1=0;// TR1=1; EX1=1; IT1=1;
#ifndef _CAPTURE_H_
#define _CAPTURE_h_
sbit chufa=P2^1;
void capture_init(void);
#endif
舵机控制文件:
#include"delay.h"
#include"1603.h"
#include"externIRQ.h"
#include"steer.h"
void Init_SteerPWM(void)
{
}
void steer(u8 Mod,u8 Num,int degree) {
double duty; if(Mod)//½Ç¶È { duty=19.2+0.14222*((double)degree); if(duty>29) {
TMOD|=0x02; TH0=0xB7; TL0=0xB7; TR0=1; CMOD=0x04; CL=0x00; CH=0x00; CCAPM0=0x42; CCAPM1=0x42; CCAP0H=home; CCAP0L=home; CCAP1H=home; CCAP1L=home; CR=1;//Æô¶¯PCA
} } duty=29; else { } if(Num==1) { } if(Num==2) { } CCAP1H=(int)duty; CCAP1L=(int)duty; LCD_Write_Number(4,3,duty,3); CCAP0H=(int)duty; CCAP0L=(int)duty; LCD_Write_Number(0,3,duty,3); if(duty29) { } else { } if(Num==1) { } if(Num==2)
degree=29; if(degree
{ CCAP1H=degree; CCAP1L=degree; LCD_Write_Number(4,3,duty,3); }
}
}
#ifndef _STEER_H_
#define _STEER_H_
#include"define.h"
#define CryOsc 11059200
#define SteerCycle 50
#define home 0x13
void Init_SteerPWM(void);
void steer(u8 Mod,u8 Num,int degree); #endif
电机控制文件:
#include"REG52.h"
#include"define.h"
#include"delay.h"
#include"PWM.h"
sbit motorIO=P1^2;
void Init_PWM(void)
{
CMOD=0x08;
CL=0x00;
CH=0x00;
CCAPM0=0x42;
CCAPM1=0x42;
CR=1;
}
void forward(u8 duty)
{
CCAP0H=duty;
motorIO=0;
}
void stop(void)
{
CCAP0H=0;
motorIO=0;
}
void back(u8 duty)
{
}
#ifndef _PWM_H_
#define _PWM_H_
void Init_PWM(void);
void forward(u8 duty);
void stop(void);
void back(u8 duty);
#endif
主机的主函数文件:
#include"reg52.h"
#include"define.h"
#include"delay.h"
#include"1603.h"
#include"com.h"
#include"externIRQ.h"
#include"capture.h"
#include"steer.h"
#include"IRQ.h"
void system_init(void)
{
}
void main(void)
{
u8 i,closeN=0,fitN=0,remoteN=0; float xdata tem[5]; u32_l xdata distance[22]; u8 xdata close[22],xdata fit[22],xdata remote[22]; system_init(); while(1) { if(key_flag) {
CCAP0H=256-duty; motorIO=1; EA=0; LCD_Init(); COMInit(); IRQ_Init(); capture_init(); Init_SteerPWM(); EA=1;
buzz=0;
delay_ms(500);
buzz=1;
delay_ms(500);
buzz=0;
delay_ms(500);
buzz=1;
delay_ms(500);
buzz=0;
delay_ms(500);
buzz=1;
delay_ms(500);
buzz=0;
delay_ms(500);
buzz=1;
delay_ms(500);
break;
}
}
while(1)
{
closeN=0;
fitN=0;
remoteN=0;
for(i=7;i
{
steer(0,1,i);
chufa=1;
while(!T1_F);
tem[0]=juli;
chufa=1;
while(!T1_F);
tem[1]=juli;
chufa=1;
while(!T1_F);
tem[2]=juli;
chufa=1;
while(!T1_F);
tem[3]=juli;
chufa=1;
while(!T1_F);
tem[4]=juli;
} distance[i-7]=(tem[0]+tem[1]+tem[2]+tem[3]+tem[4])/5; for(i=0;i=closeN) { } else { if(remoteN
40 if(distance[i]1500) { } else { } fit[fitN]=i; fitN++; remote[remoteN]=i; remoteN++; close[closeN]=i; closeN++; LCD_Write_String(0,4,"open area!"); for(i=1;iremote[i]) { } remote[0]=remote[i];
} } } } { } steer(0,2,(remote[0]*5/22+17)); send_UART(0x88); if(remote[0]>remote[i]) { } remote[0]=remote[i]; else { } LCD_Write_String(0,4,"barrier area!"); for(i=1;ifit[i]) { } fit[0]=fit[i];
从机的主函数:
#include"IRQ.h"
#include"PWM.h"
#include"com.h"
#include"capture.h"
void system_init(void)
{
}
void main(void)
{
system_init(); while(1)
41 EA=0; COMInit(); capture_init(); Init_PWM(); EA=1;
{ if(R_Flag) { R_Flag=0; forward(receive);
}
}
}
附小车实物图:
小车正面照:
图B.2小车正面照
图B.3小车侧面照
42
图B.4小车所用微控制器(STC12C5A60S2)特写
图B.5小车编码器测速特写(机械部分费了好大劲才连上的)
43
单片机系统 课 程 设 计
成绩评定表
设计课题 : 自动避障小车 学院名称 : 电气工程学院 专业班级 : 自动1105 学生姓名 : 学 号 : 指导教师 : 设计地点 : 31-630 设计时间 :
单片机系统 课 程 设 计
课程设计名称:
专 业 班 级 : 自动1105 学 生 姓 名 : 学 号 : 指 导 教 师 : 课程设计地点: 31-630 课程设计时间:
目 录
1概述 --------------------------------------------------------------------------------------------- 4
1.1研究背景 -------------------------------------------------------------------------------- 4 1.2设计思想及基本功能 ----------------------------------------------------------------- 4 2总体方案设计 --------------------------------------------------------------------------------- 4
2.1方案论证 -------------------------------------------------------------------------------- 4 2.2系统框图 -------------------------------------------------------------------------------- 5 2.3总体方案设计 -------------------------------------------------------------------------- 6 3硬件电路设计 --------------------------------------------------------------------------------- 7
3.1电源电路 -------------------------------------------------------------------------------- 7 3.2晶振电路 -------------------------------------------------------------------------------- 8 3.3复位电路 -------------------------------------------------------------------------------- 8 3.4键盘电路 -------------------------------------------------------------------------------- 8 3.5显示电路 -------------------------------------------------------------------------------- 9 3.6超声波测距电路 ---------------------------------------------------------------------- 10 3.7舵机电路 ------------------------------------------------------------------------------- 11 3.8电机驱动电路 ------------------------------------------------------------------------- 12 3.9电机转速测量电路 ------------------------------------------------------------------- 13 3.10设计PCB和腐蚀电路板 ---------------------------------------------------------- 14 4系统软件设计 -------------------------------------------------------------------------------- 16
4.1分模块程序设计 ---------------------------------------------------------------------- 18 4.2主程序设计 ---------------------------------------------------------------------------- 20 5系统调试 -------------------------------------------------------------------------------------- 20 6总结 -------------------------------------------------------------------------------------------- 22 参考文献: -------------------------------------------------------------------------------------- 23 附录A硬件电路图 ---------------------------------------------------------------------------- 24 附录B 源程序 --------------------------------------------------------------------------------- 25
1概述
1.1研究背景
科技的发展趋势之一就是让几乎所有的东西具有一定的智能。这样的智能一方面可以避免人的复杂性带来的错误,另一方面,作为人的能力的延伸,快速、便捷地适应环境。本文研究的超声波自动避障小车,就是让小车具有一定的智能,可以作为有人驾驶车辆的一部分,帮助司机及早发现司机还未觉察的危险。另外,可以应用在无人控制系统里,代替人类完成信息接收、处理和判断。那么这样它还有更广阔的应用和发展空间。这个月我国嫦娥三号载着玉兔号月球车踏上了月球,已经开始探索活动。玉兔号月球车可以自动寻找有价值的目标,自动避开障碍物,自动寻找最优路径等等,这些功能在原理上都差不多,只是所用仪器以及控制算法的不同。
1.2设计思想及基本功能
小车设计的基本思路是:不断扫描前方180°左右的水平面内的物体,根据反馈的距离和方位信息确定前方的地形是开阔还是多障碍。然后根据这些信息通过路径最优算法选择前进方向。
小车的基本功能是识别空旷区和多障区;自动避开障碍物;能够在空旷区快速行驶。
2总体方案设计
2.1方案论证
首先是测距方案: 方案一:激光测距
优点:测速快,适应范围广,精确;不过相对于自动避障小车系统,有点大材小用。激光测距是点对点的测距,一般进行远距离测距比较精确。如是近距离
激光测距,必须要涉及到微小时间测量,51MCU无法胜任。因此激光测距方案否定。
方案二:超声波测距
优点:结构简单,成本低,便于与MCU联机工作。测量范围从5CM到几百米的距离内精度在毫米级。尽管它测量的是点到面的距离,但是定向性满足本小车系统的要求。而且,数据处理简单。可以考虑。
方案三:雷达测距
不太现实,受到本人技术水平限制,无法驾驭雷达测距系统,此外,雷达应用于较大范围的测量和监控,如果用于本小车,同样涉及到微小时间测量,以及雷达数据处理,51MCU难以胜任。否定!
方案四:红外线测距
优点:技术难度低,数据处理简单,但是测量开关信号比较好,测量连续信号不够准确。测量的精度有点低。因此不是一种较好的测距方法。
综合各因素,选用超声波测距。 其次是微控制器的选择:
可选的微控制器有:51系列的STC89C52RC、STC12C5A60S2;TI公司的MSP430G2553;飞思卡尔的MC9S12XS128MAL;意法的STM32F407-Discovery;(这些玩儿过一点,而且最小系统板手边都有)
根据本系统的需求,需要两路同频率(50hz)的PWM控制两个舵机、一路PWM频率约几千赫兹控制电机、三个个定时器做脉冲捕捉、至少两个外部中断输入、一个并行6800总线接口。除了51系列的两款,其他一片足以胜任。而且对于意法的F407显得大材小用。飞思卡尔的S12比较合适;TI的G2553的引脚有点少(20个),外围电路设计的会复杂一点,成本也会高一点。60S2用两片比较合适; 用89C52比较麻烦,两片也不够。根据个人能力,以及时间和进度方面考虑选用60S2,60S2呢,它的资源有:完全兼容51 的两个定时器T0、T1和两个外部中断;两路拥有独立波特率的串行口;一个PCA定时器,可产生两路同频的PWM;八个10位精度的AD 转换通道; 一个硬件SPI接口。
2.2系统框图
系统框图如图2.2所示
图2.2系统框图
2.3 总体方案设计
两片60S2一个做主机,一个做从机,通过串行口通信,传递数据。 主机用到的功能有:
(1)、定时器T1和外部中断INT1,作用是:测脉宽,用到的IO是P3^3;P2^0作为超声波测距模块的触发功能引脚;
(2)、定时器T0和PCA产生两路同频率的PWM,作用是:T0的溢出率作为PCA的计数脉冲产生50HZ的PWM控制两路舵机。使用T0作为PCA的时钟源,可以通过改变T0的溢出率改变输出的频率。用到的IO是P1^3和P1^4;
(3)、外部中断INT0接一个按键。
(4)、串口一使用独立波特率发生器,与从机通P3^0和P3^1;
(5)、并行数据接口:P0口作为6800并行总线的数据总线P2^7、P2^6和P2^5作为控制总线,分别为:EN(总使能)、R\W(读写控制)、C\D(命令数据控制);地址总线CS接地,一直处于选通状态。 从机用到的功能:
(1)、串行口一使用独立波特率发生器P3^0和P3^1与主机通讯; (2)、PCA产生一路PWM,P1^2共同控制电机速度。
(3)、定时器T0和T1作为编码器脉冲计数和测脉宽使用,测量电机的转速。
3硬件电路设计
首先是单片机的最小系统如图3.1、3.2所示
图3.1 单片机的最小系统
图3.2 单片机的最小系统
然后是各个模块电路:
3.1电源电路
*
本系统由7.5V可充电电池供电,由开关电源LM2596转换成5V电压给单片机
和测距模块供电。出于对系统的稳定性、可靠性和成本的考虑,选用了开关电源模块作为转换电路。电源部分电路如图3.3
图3.3 电源部分电路
图3.4 电源模块实物
3.2晶振电路
虽然60S2内部有RC振荡电路作为时钟电路的输入源,但是外部连接一个无源晶振电路,可以使单片机的时钟频率更稳定,因而工作更可靠。电路参数:四个电容均为22PF,作为石英晶振频率的微调。石英晶振的频率为12MHZ。
3.3复位电路
复位电路是单片机必不可少的基本电路,在调试阶段,可以按下复位按键,使复位引脚保持两个机器周期的低电平,就可以使单片机内部所有电路初始化。电路参数:
电容
1UF只要保证电容的时间常数大于两个机器周期即可。电阻值为1K欧。
3.4键盘电路
键盘电路如图3.5所示
法检测。
图3.5键盘电路
可以利用外部中断法或查询法检测按键是否按下。这里都采用外部中断的方
3.5显示电路
显示电路如图3.6所示
图3.6显示电路
采用1603液晶屏显示数字和字符。1603与1602的硬件接口完全一样。不同之处在于1603的RAM区的内容可以全部同时显示在屏幕上,没有屏幕内容的
位移切换。可以显示四行。状态指示灯如图3.7所示
图3.7 状态指示灯
这几个LED灯是作为状态指示用的,方便调试。
3.6超声波测距电路
超声波测距模块如图3.8所示
图3.8超声波测距模块
这是超声波模块的接口电路。这个超声波的工作原理是: (1)采用 IO 触发测距,给至少 10us 的高电平信号;
(2)模块自动发送 8 个 40khz 的方波,自动检测是否有信号返回; (3)有信号返回,通过 IO 输出一高电平,高电平持续的时间就是
(4)超声波从发射到返回的时间.测试距离=(高电平时间*声速(340M/S))/2;
超声波模块测距工作时序图:
触发信号(由单片机产生一个宽度大于10US的高脉冲,触发模块工作一次):
_|¯¯¯¯|_______________________________________________|¯¯¯¯ 模块内部发出的信号(循环发出8个40KHZ的脉冲):
______|¯|_|¯|_|¯|_|¯|_|¯|_|¯|_|¯|_|¯|___________________ 输出回响信号(回响信号的高电平持续时间正比于检测的距离):
____________________________________|¯¯¯¯¯¯¯¯¯¯¯¯¯¯|____
3.7舵机电路
舵机电路如图3.9所示
图3.9舵机电路
舵机驱动电路。舵机接口是信号线、电源正、地。信号线一般可以直接接到单片机的IO上,但是接多了就不行了,电流太大。这里用了一级三极管驱动。通常用的这种舵机,控制信号是一个周期为20ms占空比在2.5%—12.5%之间的PWM信号,分别对应舵机的控制角为0°—180°之间。舵机本身有反馈调节,因此,单片机给出一个占空比,舵机就到某一个角度。
3.8电机驱动电路
电机驱动电路如图3.10所示
图3.10电机驱动模块
电机的控制采用的是L298N全桥驱动电路。 驱动电路如图3.11所示
电机正反转原理:
3.9电机转速测量电路
电机转速测量电路如图3.12所示
图3.11 驱动电路
PWM: |_|¯|_|¯|_|¯|_|¯|_|¯|_|¯|_|¯|_|¯|_|¯|_|¯|_|¯|_
电流流向:¦↑¦↑¦¦↑¦¦↑¦¦↑¦ ¦↓¦¦↓¦¦↓¦¦↓¦¦↓¦¦↓
IO: _____________________|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
|←假设此时电机正转→|←‐ 该时刻电机反转‐→|
图3.12电机转速测量电路
编码器输出信号:AB两相输出矩形正交脉冲,电路输出为NPN集电极开路输出型,此种输出类型可以和带内部上拉电阻的单片机。
接线方式:黄=A相,蓝=B相,红=Vcc正电源,黑=V0地。 编码器输出信号图如图3.13所示
图3.13信号图
3.10设计PCB和腐蚀电路板
这是原始的PCB图和单面的腐蚀电路板,红色的TOPLAYER层作为跳线。PCB如图3.14所示
图3.15 打印PCB负片图
图3.16 腐蚀效果图
4系统软件设计
主程序的流程图如图4.1所示。
图4.1主程序流程图
主程序流程说明:
电路主要分为以下几个部分,分别是电源部分、显示部分、按键部分、步进电机控制部分、超声波检测模块、舵机模块、单片机主控器件部分,各部分具有不同的子程序。
系统初始化流程如图4.2所示:
图4.2 系统初始化流程图
4.1分模块程序设计 4.1.1键盘程序
小车系统需要的按键不多,有两个,采用中断法。在系统中的作用是启动小车。
4.1.2舵机驱动程序和电机驱动程序
控制原理部分在叙述硬件时已介绍。这里说说具体怎么实现。
产生50HZ的PWM控制舵机:时钟频率Fosc=11059200HZ,定时器T0的溢出率Ft0作为PCA定时器的时钟输入。60S2工作在51模式下。设TO的初值为X,自动重装模式,T0、PCA均无中断。计算方法为:
X*(12/Fosc)*256=0.02s
可以得出X=72,TH0=0xB7。
再计算占空比:PCA的中心计数器每(72*12/Fosc)秒记一个数,记256个数(即一个八位计数器)溢出再从零开始。比较寄存器的取值是:256*0.025和256*0.125之间,对应的转角在0到180度。在实际情况下,舵机不够精密,最大旋转角没有180度,而且比较寄存器只能比较写入整数。实际的取值为7—29,控制方向的舵机取值为17——21(分级明显,转向不够舒畅啊)。
电机的频率没有严格的要求,因此,选用简单的处理:对时钟进行 256*12分频,是3.6KHZ。
4.1.3超声波测距程序
超声波测距实现原理就是测量高电平脉冲持续的时间。用到了51定时器启停由外部控制的功能。GATE=1,TRn=1,定时器由外部中断引脚电平控制,上升沿之后开始计数,下降沿之后停止计数。同时打开相应的外部中断,在下降沿到来后进入外部中断服务程序,记录停止计数时的数值。
这里应当注意一个问题是:如果测量的距离较远,定时器可能就溢出了,对这个不处理,结果将是错误的。因此同时开启定时器中断,在溢出时,记下溢出的次数。距离的计算方法是:
距离=【Number*(12/Fosc)+N*65536*(12/Fosc)】/2 void Int1_Routine(void) interrupt 2 {
T=(TH1*256+TL1); TH1=0; TL1=0;
juli=((float)T1_Count)*11141.12+0.1844618*((float)T); LCD_Write_Number(0,2,juli,5); T1_Count=0; T1_F=1;
}
4.2主程序设计
主程序里主要是避障程序。我的思路是,把区域三部分:近距离20CM以内,适中区域(20CM—150CM),远距离(150CM以外)。得到22组数据后首先是根据区域分类,然后记各区域的角度大小,当远距离的角度范围最大时,判断为空旷区,其他是多障碍区。然后根据角度大小排序,当适中区最大时,走的方向为适中区集中的方向。当最大区域为近距离或远距离时,走远距离区,具体方向为最远点方向。
速度总是由最近的障碍物距离计算得出。
5系统调试
首先是分模块调试。把各个模块的功能分开实现。然后是部分的组合到一起使用,如超声波测距和1603显示、两路舵机同时控制。
在软件调试的时候,出现了RAM区不够用。原因出在避障算法上。定义的数组多,而且长。用户128RAM不够用。可以有两个解决办法:一是改进代码,使用指针;二是扩展RAM区。考虑到代码优化不是一天两天就弄好的,而且我的C语言编程水平实在浅薄,指针依然是用不好,一用就报错。那只好扩展RAM。查查60S2的数据手册,它取消了外部扩展RAM和ROM的功能,但是内部增加了1KB的RAM,用法是在定义变量时,加前缀“XDATA”,告诉编译器把后面的变量定义在扩展RAM区。此外我还了解到“DATA”、“PDATA”、“IDATA”都是51系统编译器允许的关键字,具体的解释是:
dATa: 固定指前面0x00-0x7f的128个RAM,可以用acc直接读写的,速度最快,生成的代码也最小。 idATa: 固定指前面0x00-0xff的256个RAM,其中前128和dATa的128完全相同,只是因为访问的方式不同。idATa是用类似C中的指针方式 访问的。汇编中的语句为:mox ACC,@Rx.(不重要的补充:c中idATa做指针式的访问效果很好) xdATa: 外部扩展RAM,一般指外部0x0000-0xffff空间,用DPTR访问。 pdATa: 外部扩展RAM的低256个字节,地址出现在A0-A7的上时读写,用movx ACC,@Rx读写。
硬件方面的调试主要在模块调试方面。因为基本上都用的现有的模块电路,自己设计的电路基本没有(自己还差的很远唉!),没有什么难的。
小车主板:
图5.1 小车主板
整车图片:
图5.2 整车鸟瞰图
6总结
两周的单片机课程设计很快就结束了,但是在进军单片机领域,这仅仅是个开始。学习单片机最重要的是实践。要投入必要的成本。一块开发板必不可少,还有一大堆配套的硬件软件。
作为有志于今后继续在单片机方面发展的我,学会使用51单片机,远远不够,世界上51系列占的市场份额并不多。流行的单片机,其功能远远好于51,因此,学习51单片机重点在于利用它简单易学的特点掌握单片机的各种通用性的东西,深刻理解和掌握它的工作原理和使用方式,这样可以快捷的掌握其他单片机的使用。把单片机作为改变生活的万能工具。
学习单片机,离不开编程,离不开软件。C语言作为一种高级语言,与硬件联系紧密。因此在单片机领域,基本都用的是C语言。灵活运用C语言,对单片机学习带来极大的便利。今后还得继续深入学习C语言。还有一点,非常重要,把软件工程的思想融入到单片机的编程中。比如分层和封装思想。这个课程设计中所有的程序,并不是都在这两周写的,有一些以前玩过类似的模块,当然有可靠地例程,在这里,修改一下硬件接口,修改一下工作方式,就可以用了。由此看出,单片机学习是可以积累的。除了掌握大量的知识,还有个人的单片机例程、软件、电路设计库,这样会越来越容易。
参考文献:
【1】《单片微型计算机原理、应用及接口技术》(第2版)张迎新.[M].北京:国防工业出版社,2004
【2】 夏路易 石宗义.Protel 99se电路原理图与电路板设计教程.北京:北京希望电子出版社,2004
【3】《单片微型计算机原理、应用及接口技术》(第2版)张迎新.[M].北京:国防工业出版社,2004
【4】宏晶STC12单片机技术手册
附录A硬件电路图
图A.1 整车鸟瞰图
附录B 源程序
工程文件结构:
图B.1 整车鸟瞰图
STC12C5A60S2增加的特殊功能寄存器与地址的映射:
#ifndef __REG52_H__
#define __REG52_H__
/* BYTE Registers */
sfr AUXR = 0x8E;/*stc12c5a60s2*/
sfr WAKE_CLK0=0x8F;/*stc12c5a60s2*/
sfr P1M1 = 0x91;/*stc12c5a60s2*/
sfr P1M0 = 0x92;/*stc12c5a60s2*/
sfr P0M1 = 0x93;/*stc12c5a60s2*/
sfr P0M0 = 0x94;/*stc12c5a60s2*/
sfr P2M1 = 0x95;/*stc12c5a60s2*/
sfr P2M0 = 0x96;/*stc12c5a60s2*/
sfr CLK_DIV=0x97;/*stc12c5a60s2*///Clock Divder
sfr S2CON = 0x9A;/*stc12c5a60s2*/
sfr S2BUF = 0x9B;/*stc12c5a60s2*/
sfr BRT = 0x9C;/*stc12c5a60s2*/
sfr BUS_SPEED=0xA1;/*stc12c5a60s2*/
sfr AUXR1 = 0xA2;/*stc12c5a60s2*/
sfr IE2 = 0xAF;/*stc12c5a60s2*/
sfr P3M1 = 0xB1;/*stc12c5a60s2*/
sfr P3M0 = 0xB2;/*stc12c5a60s2*/
sfr P4M1 = 0xB3;/*stc12c5a60s2*/
sfr P4M0 = 0xB4;/*stc12c5a60s2*/
sfr IP2 = 0xB5;/*stc12c5a60s2*/
sfr IP2H = 0xB6;/*stc12c5a60s2*/
sfr IPH = 0xB7;/*stc12c5a60s2*///Interrupt Priority High
sfr P4SW = 0xBB;/*stc12c5a60s2*///P4 I/O Switch
sfr ADC_CONTR=0xBC;/*stc12c5a60s2*/
sfr ADC_RES=0xBD;/*stc12c5a60s2*/
sfr ADC_RESL=0xBE;/*stc12c5a60s2*///A/D Result Low
sfr P4 = 0xC0;/*stc12c5a60s2*///P4 I/O
sfr WDT_CONTR=0xC1;/*stc12c5a60s2*///Watch-Dog-Timer Cotrol Register sfr IAP_DATA=0xC2;/*stc12c5a60s2*///ISP/IAP Flash Data Register sfr IAP_ADDRH=0xC3;/*stc12c5a60s2*///ISP/IAP Flash Address High sfr IAP_ADDRL=0xC4;/*stc12c5a60s2*///ISP/IAP Flash Address Low sfr IAP_CMD= 0xC5;/*stc12c5a60s2*///ISP/IAP Flash Command Register sfr IAP_TRIG=0xC6;/*stc12c5a60s2*///ISP/IAP Flash Command Trigger sfr IAP_CONTR=0xC7;/*stc12c5a60s2*///ISP/IAP Control Register sfr P5 = 0xC8;/*stc12c5a60s2*///P5 I/O
sfr P5M1 = 0xC9;/*stc12c5a60s2*/
sfr P5M0 = 0xCA;/*stc12c5a60s2*/
sfr TL2 = 0xCC; /*8052*/
sfr SPSTAT= 0xCD;/*stc12c5a60s2*///SPI Control Register
sfr SPCTL = 0xCE;/*stc12c5a60s2*///SPI Status Register
sfr SPDAT = 0xCF;/*stc12c5a60s2*///SPI Data Register
sfr CCON = 0xD8;/*stc12c5a60s2*///PCA
sfr CMOD = 0xD9;/*stc12c5a60s2*///PCA
sfr CCAPM0= 0xDA;/*stc12c5a60s2*///PCA
sfr CCAPM1= 0xDB;/*stc12c5a60s2*///PCA
sfr CL = 0xE9;/*stc12c5a60s2*/
sfr CCAP0L= 0xEA;/*stc12c5a60s2*/
sfr CCAP1L= 0xEB;/*stc12c5a60s2*/
sfr PAC_PWM0=0xF2;/*stc12c5a60s2*///PCA PWM
sfr PAC_PWM1=0xF3;/*stc12c5a60s2*///PCA PWM
sfr CH = 0xF9;/*stc12c5a60s2*/
sfr CCAP0H= 0xFA;/*stc12c5a60s2*/
sfr CCAP1H= 0xFB;/*stc12c5a60s2*/
/* BIT Registers */
/*CCON*/
sbit CF =CCON^7;
sbit CR =CCON^6;
sbit CCF1 =CCON^1;
sbit CCF0 =CCON^0;
#endif
LCD1603显示程序文件:
#include"reg52.h"
#include"define.h"
#include"delay.h"
#include"1603.h"
bit LCD_Check_Busy(void)
{
u8 result;
EN=0;
DataPort= 0xFF;
D_C=0;
R_W=1;
delay_ms(1);
EN=1;
result=(DataPort & 0x80);
EN=0;
return result;
}
void LCD_Write_Comm(u8 comm)
{
while(LCD_Check_Busy());
EN=0;
D_C=0;
R_W=0;
EN=1;
delay_ms(1);
DataPort=comm;
EN=0;
}
void LCD_Write_Data(u8 dat)
{
while(LCD_Check_Busy());
EN=0;
D_C=1;
R_W=0;
EN=1;
} delay_ms(1); DataPort=dat; EN=0;
void LCD_Write_Char(u8 x,u8 y,u8 Data)
{
}
void LCD_Write_String(u8 x,u8 y,u8 *s)
{
}
void LCD_Write_Number(u8 x,u8 y,u32 number,u8 Nbit) {
u8 dat,ten,hun,thu,tenthu,hunthu; hunthu=48+(number/100000);
switch(y) { case 1:LCD_Write_Comm(0x80+x); break; break; break; break; case 2:LCD_Write_Comm(0xC0+x); case 3:LCD_Write_Comm(0x90+x); case 4:LCD_Write_Comm(0xD0+x); } LCD_Write_Data(Data); switch(y) { case 1:LCD_Write_Comm(0x80+x); break; break; break; break; case 2:LCD_Write_Comm(0xC0+x); case 3:LCD_Write_Comm(0x90+x); case 4:LCD_Write_Comm(0xD0+x); } while(*s) { } LCD_Write_Data(*s); s++;
tenthu=48+(number%100000/10000); thu=48+(number%10000/1000); hun=48+(number%1000/100); ten=48+(number%100/10); dat=48+(number%10); switch(y) { } { case 6: LCD_Write_Data(hunthu); LCD_Write_Data(tenthu); LCD_Write_Data(thu); LCD_Write_Data(hun); LCD_Write_Data(ten); LCD_Write_Data(dat); break; LCD_Write_Data(tenthu); LCD_Write_Data(thu); LCD_Write_Data(hun); LCD_Write_Data(ten); LCD_Write_Data(dat); break; LCD_Write_Data(thu); LCD_Write_Data(hun); LCD_Write_Data(ten); LCD_Write_Data(dat); break; LCD_Write_Data(hun); LCD_Write_Data(ten);
case 1:LCD_Write_Comm(0x80+x); break; break; break; break; case 2:LCD_Write_Comm(0xC0+x); case 3:LCD_Write_Comm(0x90+x); case 4:LCD_Write_Comm(0xD0+x); switch (Nbit) case 5: case 4: case 3:
}
{
} } LCD_Write_Data(dat); break; LCD_Write_Data(ten); LCD_Write_Data(dat); break; LCD_Write_Data(dat); break; case 2: case 1: void LCD_Clear(void) LCD_Write_Comm(0x01); delay_ms(1);
void LCD_Init(void)
{
}
#ifndef _1603_H_
LCD_Write_Comm(0x01); delay_ms(1); LCD_Write_Comm(0x02); delay_ms(1); LCD_Write_Comm(0x38); delay_ms(1); LCD_Write_Comm(0x0C); delay_ms(1); LCD_Write_Comm(0x06); delay_ms(1); LCD_Write_Comm(0x80); delay_ms(1); LCD_Write_Data(0xFF); delay_ms(1); LCD_Write_Data(0xFF); delay_ms(1); LCD_Write_Comm(0x8E); delay_ms(1); LCD_Write_Data(0xFF); delay_ms(1); LCD_Write_Data(0xFF); delay_ms(1);
#define _1603_H_
sbit buzz=P2^3;
sbit D_C= P1^0;
sbit R_W= P1^1;
sbit EN = P2^5;
#define DataPort P0
bit LCD_Check_Busy(void);
void LCD_Write_Comm(u8 comm);
void LCD_Write_Data(u8 dat);
void LCD_Write_Char(u8 x,u8 y,u8 Data);
void LCD_Write_String(u8 x,u8 y,u8 *s);
void LCD_Write_Number(u8 x,u8 y,u32 number,u8 Nbit); void LCD_Clear(void);
void LCD_Init(void);
串行口程序文件:
#endif
#include"reg52.h"
#include"define.h"
#include"delay.h"
u8 receive;
u16 n;
u8 flagR;
u8 stopF;
void COMInit(u16 bps)
{
u8 s; EA=0; if(bps>=19200) { } else { } SCON=0x50; TMOD=0x20; TH1=256-(28800*(s+1))/bps; TL1=256-(28800*(s+1))/bps; REN=1;
PCON|=0x80; s=1; PCON&=0x7F; s=0;
ES=1; EA=1; TR1=1;
}
void serial() interrupt 4
{
if(TI!=0)
{
TI=0;
flagR=1;
}
else
{
if(RI!=0)
{
receive=SBUF;
P1=receive;
if(receive==0xFF)
{
stopF=1;
}
else
{
if(receive==0x5A)
{
stopF=0;
}
}
RI=0;
}
}
}
#ifndef _COM_H_
#define _COM_H_
void COMInit(u16 bps);
#endif
自定义变量类型文件:
#ifndef _DEFINE_H_
#define _DEFINE_H_
#define u8 unsigned char
#define u16 unsigned int
#define u32 unsigned long
#endif
各种延时函数文件:
#include"define.h"
#include"delay.h"
void delay_us(u16 xus)
{
xus=2*xus;
while(xus--);
}
void delay_ms(u16 xms)
{
int i,j;
for(i=xms;i>0;i--)
{
for(j=110;j>0;j--);
}
}
#ifndef _DELAY_H_
#define _DELAY_H_
void delay_us(u16 xus);
void delay_ms(u16 xms)
各个外设中断服务函数文件:
#ifndef _IRQ_H_
#define _IRQ_H_
#include"define.h"
#include"1603.h"
u8 key_flag=0;
u8 receive=0;
float juli;
u16 T;
u8 T1_Count=0;
bit T1_F=0;
void Int0_Routine(void) interrupt 0
{
if(IE0)
{
delay_ms(800);
if(IE0)
{
key_flag++;
}
}
}
void Int1_Routine(void) interrupt 2
{
T=(TH1*256+TL1); TH1=0; TL1=0; juli=((float)T1_Count)*11141.12+0.1844618*((float)T); LCD_Write_Number(0,2,juli,5);
T1_Count=0;
T1_F=1;
}
void Timer1_Routine(void) interrupt 3
{
T=(TH1*256+TL1);
TH1=0;
TL1=0;
T1_Count++;
}
void UART_Routine(void) interrupt 4
{
if(TI)
{
TI=0;
}
else
{
if(RI)
{
receive=SBUF;
RI=0;
}
}
}
#endif
脉宽测量初始化文件:
#include
#include"define.h"
#include"delay.h"
#include"1603.h"
#include"capture.h"
void capture_init(void)
{
} TMOD|=0x90; TH1=0; TL1=0;// TR1=1; EX1=1; IT1=1;
#ifndef _CAPTURE_H_
#define _CAPTURE_h_
sbit chufa=P2^1;
void capture_init(void);
#endif
舵机控制文件:
#include"delay.h"
#include"1603.h"
#include"externIRQ.h"
#include"steer.h"
void Init_SteerPWM(void)
{
}
void steer(u8 Mod,u8 Num,int degree) {
double duty; if(Mod)//½Ç¶È { duty=19.2+0.14222*((double)degree); if(duty>29) {
TMOD|=0x02; TH0=0xB7; TL0=0xB7; TR0=1; CMOD=0x04; CL=0x00; CH=0x00; CCAPM0=0x42; CCAPM1=0x42; CCAP0H=home; CCAP0L=home; CCAP1H=home; CCAP1L=home; CR=1;//Æô¶¯PCA
} } duty=29; else { } if(Num==1) { } if(Num==2) { } CCAP1H=(int)duty; CCAP1L=(int)duty; LCD_Write_Number(4,3,duty,3); CCAP0H=(int)duty; CCAP0L=(int)duty; LCD_Write_Number(0,3,duty,3); if(duty29) { } else { } if(Num==1) { } if(Num==2)
degree=29; if(degree
{ CCAP1H=degree; CCAP1L=degree; LCD_Write_Number(4,3,duty,3); }
}
}
#ifndef _STEER_H_
#define _STEER_H_
#include"define.h"
#define CryOsc 11059200
#define SteerCycle 50
#define home 0x13
void Init_SteerPWM(void);
void steer(u8 Mod,u8 Num,int degree); #endif
电机控制文件:
#include"REG52.h"
#include"define.h"
#include"delay.h"
#include"PWM.h"
sbit motorIO=P1^2;
void Init_PWM(void)
{
CMOD=0x08;
CL=0x00;
CH=0x00;
CCAPM0=0x42;
CCAPM1=0x42;
CR=1;
}
void forward(u8 duty)
{
CCAP0H=duty;
motorIO=0;
}
void stop(void)
{
CCAP0H=0;
motorIO=0;
}
void back(u8 duty)
{
}
#ifndef _PWM_H_
#define _PWM_H_
void Init_PWM(void);
void forward(u8 duty);
void stop(void);
void back(u8 duty);
#endif
主机的主函数文件:
#include"reg52.h"
#include"define.h"
#include"delay.h"
#include"1603.h"
#include"com.h"
#include"externIRQ.h"
#include"capture.h"
#include"steer.h"
#include"IRQ.h"
void system_init(void)
{
}
void main(void)
{
u8 i,closeN=0,fitN=0,remoteN=0; float xdata tem[5]; u32_l xdata distance[22]; u8 xdata close[22],xdata fit[22],xdata remote[22]; system_init(); while(1) { if(key_flag) {
CCAP0H=256-duty; motorIO=1; EA=0; LCD_Init(); COMInit(); IRQ_Init(); capture_init(); Init_SteerPWM(); EA=1;
buzz=0;
delay_ms(500);
buzz=1;
delay_ms(500);
buzz=0;
delay_ms(500);
buzz=1;
delay_ms(500);
buzz=0;
delay_ms(500);
buzz=1;
delay_ms(500);
buzz=0;
delay_ms(500);
buzz=1;
delay_ms(500);
break;
}
}
while(1)
{
closeN=0;
fitN=0;
remoteN=0;
for(i=7;i
{
steer(0,1,i);
chufa=1;
while(!T1_F);
tem[0]=juli;
chufa=1;
while(!T1_F);
tem[1]=juli;
chufa=1;
while(!T1_F);
tem[2]=juli;
chufa=1;
while(!T1_F);
tem[3]=juli;
chufa=1;
while(!T1_F);
tem[4]=juli;
} distance[i-7]=(tem[0]+tem[1]+tem[2]+tem[3]+tem[4])/5; for(i=0;i=closeN) { } else { if(remoteN
40 if(distance[i]1500) { } else { } fit[fitN]=i; fitN++; remote[remoteN]=i; remoteN++; close[closeN]=i; closeN++; LCD_Write_String(0,4,"open area!"); for(i=1;iremote[i]) { } remote[0]=remote[i];
} } } } { } steer(0,2,(remote[0]*5/22+17)); send_UART(0x88); if(remote[0]>remote[i]) { } remote[0]=remote[i]; else { } LCD_Write_String(0,4,"barrier area!"); for(i=1;ifit[i]) { } fit[0]=fit[i];
从机的主函数:
#include"IRQ.h"
#include"PWM.h"
#include"com.h"
#include"capture.h"
void system_init(void)
{
}
void main(void)
{
system_init(); while(1)
41 EA=0; COMInit(); capture_init(); Init_PWM(); EA=1;
{ if(R_Flag) { R_Flag=0; forward(receive);
}
}
}
附小车实物图:
小车正面照:
图B.2小车正面照
图B.3小车侧面照
42
图B.4小车所用微控制器(STC12C5A60S2)特写
图B.5小车编码器测速特写(机械部分费了好大劲才连上的)
43