基于51的避障循迹重力感应遥控的智能小车设计(C语言)
基于51的避障/循迹/重力感应遥控的智能小车设计
1 绪论
1.1 选题背景
随着汽车工业的迅速发展,关于汽车的研究也就越来越受人关注。全国电子大赛和省内电子大赛几乎每次都有智能小车这方面的题目,全国各高校也都很重视该题目的研究。可见其研究意义很大。本设计就是在这样的背景下提出的,指导教师已经有充分的准备。本题目是结合科研项目而确定的设计类课题。设计的智能电动小车应该能够实现适应能力,能自动避障,可以智能规划路径。
智能化作为现代社会的新产物,是以后的发展方向,他可以按照预先设定的模式在一个特定的环境里自动的运作,无需人为管理,便可以完成预期所要达到的或是更高的目标。同遥控小车不同,遥控小车需要人为控制转向、启停和进退,比较先进的遥控车还能控制器速度。常见的模型小车,都属于这类遥控车;智能小车,则可以通过计算机编程来实现其对行驶方向、启停以及速度的控制,无需人工干预。操作员可以通过修改智能小车的计算机程序来改变它的行驶方向。因此,智能小车具有再编程的特性,是机器人的一种。
中国自1978年把“智能模拟”作为国家科学技术发展规划的主要研究课题,开始着力研究智能化。从概念的引进到实验室研究的实现,再到现在高端领域(航天航空、军事、勘探等)的应用,这一过程为智能化的全面发展奠定基石。智能化全面的发展是实现其对资源的合理充分利用,以尽可能少的投入得到最大的收益,大大提高工业生产的效率,实现现有工业生产水平从自动化向智能化升级,实现当今智能化发展由高端向大众普及。从先前的模拟电路设计,到数字电路设计,再到现在的集成芯片的应用,各种能实现同样功能的元件越来越小为智能化产物的生成奠定了良好的物质基础。
智能小车,是一个集环境感知、规划决策,自动行驶等功能于一体的综合系统,它集中地运用了计算机、传感、信息、通信、导航、人工智能及自动控制等技术,是典型的高新技术综合体。
1.2 智能小车研究现状
智能车辆作为智能交通系统的关键技术,是许多高新技术综合集成的载体。智能车辆驾驶是一种通用性术语,指全部或部分完成一项或多项驾驶任务的综合车辆技术。智能车辆的一个基本特征是在一定道路条件下实现全部或者部分的自动驾驶功能,下面简单介绍一下国内外智能小车研究的发展情况。
1.2.1 国外智能车辆研究现状
国外智能车辆的研究历史较长,始于上世纪50年代。它的发展历程大体可以分成三个阶段:
第一阶段 20世纪50年代是智能车辆研究的初始阶段。1954年美国Barrett Electronics 公司研究开发了世界上第一台自主引导车系统AGVS(Automated Guided Vehicle System)。该系统只是一个运行在固定线路上的拖车式运货平台,但它却具有了智能车辆最基本得特征即无人驾驶。早期研制AGVS的目的是为了提高仓库运输的自动化水平,应用领域仅局限于仓库内的物品运输。随着计算机的应用和传感技术的发展,智能车辆的研究不断得到新的发展。
第二阶段 从80年代中后期开始,世界主要发达国家对智能车辆开展了卓有成效的研究。在欧洲,普罗米修斯项目于1986年开始了在这个领域的探索。在美洲,美国于1995年成立了国家自动高速公路系统联盟(NAHSC),其目标之一就是研究发展智能车辆的可能性,并促进智能车辆技术进入实用化。在亚洲,日本于1996年成立了高速公路先进巡航/辅助驾驶研究会,主要目的是研究自动车辆导航的方法,促进日本智能车辆技术的整体进步。进入80年代中期,设计和制造智能车辆的浪潮席卷全世界,一大批世界著名的公司开始研制智能车辆平台。
第三阶段 从90年代开始,智能车辆进入了深入、系统、大规模研究阶段。最为突出的是,美国卡内基.梅隆大学(Carnegie Mellon University)机器人研究所一共完成了Navlab系列的10台自主车(Navlab1—Navlab10)的研究,取得了显著的成就。
目前,智能车辆的发展正处于第三阶段。这一阶段的研究成果代表了当前国外智能车辆的主要发展方向。在世界科学界和工业设计界中,众多的研究机构研发的智能车辆具有代表性的有:
德意志联邦大学的研究 1985年,第一辆VaMoRs智能原型车辆在户外高速公路上以100km/h的速度进行了测试,它使用了机器视觉来保证横向和纵向的车辆控制。1988年,在都灵的PROMRTHEUS项目第一次委员会会议上,智能车辆维塔(VITA,7t)进行了展示,该车可以自动停车、行进,并可以向后车传送相关驾驶信息。这两种车辆都配备了UBM视觉系统。这是一个双目视觉系统,具有极高的稳定性。
荷兰鹿特丹港口的研究 智能车辆的研究主要体现在工厂货物的运输。荷兰的Combi road系统,采用无人驾驶的车辆来往返运输货物,它行驶的路面上采用了磁性导航参照物,并利用一个光阵列传感器去探测障碍。荷兰南部目前正在讨论工业上利用这种系统的问题,政府正考虑已有的高速公路新建一条专用的车
道,采用这种系统将货物从鹿特丹运往各地。
日本大阪大学的研究 大阪大学的Shirai实验室所研制的智能小车,采用了航位推测系统(Dead Reckoning System),分别利用旋转编码器和电位计来获取智能小车的转向角,从而完成了智能小车的定位。
另外,斯特拉斯堡实验中心、英国国防部门的研究、美国卡内基梅隆大学、奔驰公司、美国麻省理工学院、韩国理工大学对智能车辆也有较多的研究。 1.2.2 国内智能车辆研究现状
相比于国外,我国开展智能车辆技术方面的研究起步较晚,开始于20世纪80年代。而且大多数研究处在于针对某个单项技术研究的阶段。虽然我国在智能车辆技术方面的研究总体上落后于发达国家,并且存在一定得技术差距,但是我们也取得了一系列的成果,主要有:
(1)中国第一汽车集团公司和国防科技大学机电工程与自动化学院与2003年研制成功我国第一辆自主驾驶轿车。该自主驾驶轿车在正常交通情况下的高速公路上,行驶的最高稳定速度为13km/h,最高峰值速度达170km/h,并且具有超车功能,其总体技术性能和指标已经达到世界先进水平。
(2)南京理工大学、北京理工大学、浙江大学、国防科技大学、清华大学等多所院校联合研制了7B.8军用室外自主车,该车装有彩色摄像机、激光雷达、陀螺惯导定位等传感器。计算机系统采用两台Sun10完成信息融合、路径规划,两台PC486完成路边抽取识别和激光信息处理,8098单片机完成定位计算和车辆自动驾驶。其体系结构以水平式结构为主,采用传统的“感知-建模-规划-执行”算法,其直线跟踪速度达到20km/h,避障速度达到5-10km/h。
智能车辆研究也是智能交通系统ITS的关键技术。目前,国内的许多高校和科研院所都在进行ITS关键技术、设备的研究。随着ITS研究的兴起,我国已形成一支ITS技术研究开发的技术专业队伍。并且各交通、汽车企业越来越加大了对ITS及智能车辆技术研发的投入,整个社会的关注程度在不断提高。交通部已将ITS研究列入“十五”科技发展
计划
项目进度计划表范例计划下载计划下载计划下载课程教学计划下载
和2010年长期规划。相信经过相关领域的共同努力,我国ITS及智能车辆的技术水平一定会得到很大提高。
可以预计,我国飞速发展的经济实力将为智能车辆的研究提供一个更加广阔的前景。我们要结合我国国情,在某一方面或某些方面,对智能车进行深入细致的研究,为它今后的发展及实际应用打下坚实的基础。
1.3 主要
内容
财务内部控制制度的内容财务内部控制制度的内容人员招聘与配置的内容项目成本控制的内容消防安全演练内容
本课题要开发一个能自动循迹自动避障同时可以遥控的智能小车控制系统,系统分小车和遥控器两部分,主要以简易智能机器人为开发平台,选择通用、价廉的51单片机为控制平台,选择常见的电机模型车为机械平台,通过细化设
计要求,结合传感器技术和电机控制技术相关知识实现小车的各种功能。设计完成以由红外线对管的自动寻迹、红外线自动避障、重力遥控组成的硬件模块结合软件设计组成多功能智能小车,共同实现小车的前进倒退、转向行驶,自动根据地面黑线寻迹导航,检测障碍物后停止等功能,实现智能控制,达到设计目标。
2
方案
气瓶 现场处置方案 .pdf气瓶 现场处置方案 .doc见习基地管理方案.doc关于群访事件的化解方案建筑工地扬尘治理专项方案下载
设计及论证
2.1 总体设计
本课题设计主要是制作一款能进行智能判断并能做出正确反应的小车。小车具有以下几个功能:自动避障功能;寻迹功能(按路面的黑色轨道行驶);基于重力感应的遥控(通过倾斜方向和角度控制小车运动方向和速度)。
小车端以两直流电动机为主驱动,通过各类传感器件来采集各类信息,送入主控单元89C52单片机处理数据后完成相应动作,以达到自身控制。电机驱动电路采用H桥驱动模块--双L298步进/直流电机驱动板 ,能同时驱动4个直流电机和2个步进电机;避障采用漫反射式光电开关来完成,自动寻迹采用红外发射管和接收管光电对管寻迹传感器完成,最后由控制单元处理数据后通过编程有序合理的将各模块信号整合在一起并完成相应动作,实现了智能控制,相当于简易机器人。
遥控端由MPU0605陀螺仪和无线模块、按键模块、LCD显示模块组成,通过检测按键和陀螺仪数据送入89C52单片机处理后判断用户的指令,然后通过NRF24L01无线模块把指令发送到小车端,同时在LCD12864显示当前工作模式和小车的状态.
2.2 主控单元方案比较与选择
按照题目要求,控制器主要用于控制电机,通过相关传感器对路面的轨迹信息进行处理,并将处理信号传输给控制器,然后控制器做出相应的处理,实现小车的自动循迹和自动避障。
方案一:可以采用ARM为系统的控制器,优点是该系统功能强大,片上外设集成度搞密度高,提高了稳定性,系统的处理速度也很高,适合作为大规模实时系统的控制核心。
方案二:采用AT89S52作为系统控制的方案。AT89S52单片机算术运算功能强,软件编程灵活、自由度大,功耗低、体积小、技术成熟,成本也比ARM低。
考虑到性价比问题,本设计选择 用AT89S52单片机做控制器。
2.3 电机单元方案比较与选择
方案一:采用直流电机,配合LM293驱动芯片组合。优点在于硬件电路的设计简单。当外加额定直流电压时,转速几乎相等。这类电机用于录音机、录相机、唱机或激光唱机等固定转速的机器或设备中,也用于变速范围很宽的驱动装置,但容易受到外部因素干扰,影响稳定的转速和转矩输出。
方案二:采用直流减速电机。直流减速电机转动力矩大,体积小,重量轻,装配简单,使用方便,小车电机内部装有减速齿轮组,所以并不需要考虑调速功能,很方便的就可以实现通过单片机对直流减速电机前进、后退、停止等操作。
综合以上考虑我们选择方案二的直流减速电机作为智能小车的驱动电机。 2.4 电源单元方案比较与选择
方案一:采用单电源供电,通过单电源同时对单片机和直流电机进行供电,此方案的优点是,减少机身的重量,操作简单,其缺点是,这样会使单片机的波动变大,影响单片机的性能,稳定性比较弱。
方案二:采用双电源供电,通过两个独立的电源分别对单片机和直流减速电机进行供电,此方案的优点是,减少波动,稳定性比较好,可以让小车更好的运作起来,唯一的缺点就是会增加小车的重量。
综合以上的优缺点,本设计决定采用第二种方案。
2.5 避障单元方案比较与选择
方案一:用超声波传感器进行避障。超声波传感器的原理是:超声波由压电陶瓷超声波传感器发出后,遇到障碍物便反射回来,再被超声波传感器接收。但使用超声波模块的成本比较高。因此我们考虑其它的方案,超声波传感器实物图如下图2所示:
图2 超声波传感器
方案二:用漫反射式光电开关进行避障。光电开关的工作原理是根据光线发射头发出的光束,被物体反射,其接收电路据此做出判断反应,物体对红外光由同步回路选通而检测物体的有无。当有光线反射回来时,输出低电平。当没有光线反射回来时,输出高电平。光电开关的是物图如下图3所示:
图3 光电开关
考虑到超声波测量的范围宽,使用非常灵活,帮助智能小车顺利绕过障碍,可以适应十分复杂的环境,我们最终选择了方案一。
2.6 寻迹单元方案比较与选择
方案一: 利用寻迹来引导小车到达用户所指定的地点。采用红外发射管和接收管光电对管寻迹传感器。红外发射管发出红外线,当发出的红外线照射到白色的平面后反射,若红外接收管能接收到反射回的光线则检测出白线继而输出低电平,若接收不到发射管发出的光线则检测出黑线继而输出高电平。此方案存在的缺陷是对光线的亮度要求较高,在夜间难以正常工作。
方案二: 通过超声波定位模块来实时定位小车的位置。超声波定位的基本原
在小车上加入一个发射理是通过接收几个固定位置的发射点的超声波接收器,
器,通过无线模块计算各模块接收到超声波的时间差,通过集成模块的内部算法得出小车所在位置和原设定位置的偏差情况,从而得到主体到这几个发射点的距离, 实现了超声波的定位,由于超声波在空气中的衰减较大, 它只适用于较小的范围,而且使用此方案还将面临着在家中的超声波各通讯线的布局,使用很不方便。
经实测发现方案一中的红外对管型寻迹模块只要给进行一定的改善,对环境的适应能力还是比较强的例如可以在晚上行进,这样就可以用低成本来实现我们你所需要的功能,所以就排除了方案二,以方案一作为小车在家庭中的行进方式。
3 硬件系统的设计
3.1 单片机控制模块
STC89C52 是一种低功耗、高性能CMOS8位微控制器,具有 8K 在系统可编
程Flash 存储器。在单芯片上,拥有灵巧的8 位CPU 和在线系统可编程Flash,使得STC89C52为众多嵌入式控制应用系统提供高灵活、超有效的解决方案。 STC89C52具有以下标准功能: 8k字节Flash,256字节RAM, 32 位I/O 口线,看门狗定时器,2 个数据指针,三个16 位 定时器/计数器,一个6向量2级中断结构,全双工串行口,片内晶振及时钟电路。另外,STC89C52可降至0Hz静态逻辑操作,支持2种软件可选择节电模式。空闲模式下,CPU 停止工作,允许RAM、定时器/计数器、串口、中断继续工作。掉电保护方式下,RAM内容被保存,振荡器被冻结,单片机一切工作停止,直到下一个中断或硬件复位为止。如图4是较为常见的单片机最小系统图。
图4 单片机最小系统
3.1.1 时钟电路
单片机的时钟产生有两种方法:内部时钟方式和外部时钟方式。
系统的时钟电路设计是采用的内部方式,即利用芯片内部的振荡电路。AT89单片机内部有一个用于构成振荡器的高增益反相放大器。引脚XTAL1和XTAL2分别是此放大器的输入端和输出端。这个放大器与作为反馈元件的片外晶体谐振器一起构成一个自激振荡器。外接晶体谐振器以及电容C1和C2构成并联谐振电路,接在放大器的反馈回路中。对外接电容的值虽然没有严格的要求,但电容的大小会影响震荡器频率的高低、震荡器的稳定性、起振的快速性和温度的稳定性。因此,此系统电路的晶体振荡器的值为12MHz,电容应尽可能的选择陶瓷电容,电容值通常取30PF。在焊接刷电路板时,晶体振荡器和电容应尽可能安装得与单片机芯片靠近,以减少寄生电容,更好地保证震荡器稳定和可靠地工作。 3.1.2 复位电路
位是由外部的复位电路来实现的。片内复位电路是复位引脚RST通过一个触
发器与复位电路相连,触发器用来抑制噪声,它的输出在每个机器周期中由复位电路采样一次。
复位电路通常采用上电自动复位和按钮复位两种方式。所谓上电复位,是指计算机加电瞬间,要在RST引脚出现大于10MS的正脉冲,使单片机进入复位状态。按钮复位是指用户按下“复位”按钮,使单片机进入复位状态。如上图3是按钮电平复位的一种实用电路。
3.2 电机驱动模块的设计
电机驱动模块采用专用芯片L298N 作为电机驱动芯片,L298N 是一个具有高电压大电流的全桥驱动芯片,其响应频率高,一片L298N可以分别控制两个直流电机。
图5 电机驱动原理简图
3.2.1 L298N芯片的介绍
L298N是ST公司生产的一种高电压、大电流电机驱动芯片。该芯片采用15脚封装。主要特点是:工作电压高,最高工作电压可达46V;输出电流大,瞬间峰值电流可达3A,持续工作电流为2A;额定功率25W。内含两个H桥的高电压大电流全桥式驱动器,可以用来驱动直流电动机和步进电动机、继电器线圈等感性负载;采用标准逻辑电平信号控制;具有两个使能控制端,在不受输入信号影响的情况下允许或禁止器件工作有一个逻辑电源输入端,使内部逻辑电路部分在低电压下工作;可以外接检测电阻,将变化量反馈给控制电路。使用L298N芯片
驱动电机,该芯片可以驱动一台两相步进电机或四相步进电机,也可以驱动两台直流电机,实物图及外围电路如下图6、7所示。
图6 L298N芯片
图7 L298N外围电路
接口说明如下示:
+5V:芯片电压5V。
VCC:电机电压,最大可接50V。
GND:共地接法。
A-~D-:输出端,接电机。
A~D+ :为步进电机公共端,模块上接了VCC。
EN1、EN2:高电平有效,EN1、EN2分别为 IN1和IN2、IN3和IN4的使能端。
IN1~ IN4:输入端,输入端电平和输出端电平是对应的。
L298N 的5、7、10、12 四个引脚接到单片机上,通过对单片机的编程就可实现两个直流电机的PWM调速控制,图8是L298N功能逻辑图。
图8 L298N功能逻辑图
L298N可接受标准TTL逻辑电平信号V,V可接4(5,7 V电压。4脚V接电源SSSSS电压,V电压范围V为,2(5,46 V。输出电流可达2(5 A,可驱动电感性负SIH
载。1脚和15脚下管的发射极分别单独引出以便接入电流采样电阻,形成电流传感信号。L298可驱动2个电动机,OUT1,OUT2和OUT3,OUT4之间可分别接电动机,本实验装置我们选用驱动一台电动机。5,7,10,12脚接输入控制电平,控制电机的正反转。E,E接控制使能端,控制电机的停转。In3,In4的逻辑nAnB
图与上图相同。由上图可知E为低电平时,输入电平对电机控制起作用,当EnAnA为高电平,输入电平为一高一低,电机正或反转。同为低电平电机停止,同为高电平电机刹停。
3.3 红外避障电路的原理与设计
用漫反射式光电开关进行避障。光电开关实际发射头与接收头于一体的检测开关,其工作原理是根据发射头发出的光束,被物体反射,接收头据此做出判断是否有障碍物。当有光线反射回来时,输出低电平。当没有光线反射回来时,输出高电平。单片机根据接收头电平的高低做出相应控制,避免小车碰到障碍物。由于接收管输出TTL电平,有利于单片机对信号的处理。小车采用漫反射式传感器进行避障的电路原理图如下图9所示:
图9 光电开关避障电路原理图
3.3.1 光电传感器简介
光电传感器在机器人中有着广泛的应用,可以用来检测地面明暗和颜色的变化,也可以探测有无接近的物体。光电传感器是靠红外发射管和红外接收管组成的传感器,对于小车循迹场地的黑白两种颜色,发射管发出同样的光强,接收管接收到的光强不同,因此输出的电压值也不同;给定一个基准电压,通过对不同输出电压值进行比较,则电路的输出为高低电平。当检测到黑白线时分别输出为高低电平,这样不仅系统硬件电路简单,而且信号处理速度快。原理如下图10、图11所示。
图10 白色反射面下的红外反射
图11 黑色反射面下的红外吸收
红外发射管发射的红外线具有一定得方向性,当红外线照射到白色表面上时会有较大的反射,如果距离D1取值合适,红外接收管可接收到反射回的红外线,再利用红外接收管的电气特性,在电路中处理红外线的接受信息;如果反射表面为黑色,红外光会被表面将其大部分吸收,红外接收管就难以收到红外线。这样,就可以利用红外收发管组成的光电传感器检测赛道黑线,实现智能车的循迹方案。
3.3.2 比较器LM324简介
LM324为四运放集成电路,采用14脚双列直插塑料封装。内部有四个运算放大器,有相位补偿电路。电路功耗很小,工作电压范围宽,可用正电源3,30V,或正负双电源?1(5V,?15V工作。
在黑线检测电路中用来确定红外接收信号电平的高低,以电平高低判定黑线有无。在电路中,LM324的一个输入端需接滑动变阻器,通过改变滑动变阻器的阻值来提供合适的比较电压,图12为LM324的管脚图。
图12 LM324的管教图
3.4 寻迹模块的硬件设计
红外对管循迹模块,五路寻迹TCR5000的模块,采用红外发射管和接收管光电对管寻迹传感器。红外发射管发出红外线,当发出的红外线照射到白色的平面后反射,若红外接收管能接收到反射回的光线则检测出白线继而输出低电平,若接收不到发射管发出的光线则检测出黑线继而输出高电,图13为红外对管黑线检测电路。
图13 红外对管黑线检测电路
3.4.1 红外传感器TCRT5000简介
TCRT5000光电传感器模块是基于TCRT5000红外光电传感器设计的一款红外反射式光电开关。传感器采用高发射功率红外光电二极管和高灵敏度光电晶体管组成,输出信号经施密特电路整形,稳定可靠。传感器的红外发射二极管不断发射红外线,当发射出的红外线没有被反射回来或被反射回来但强度不够大时,光敏三极管一直处于关断状态,此时模块的输出端为低电平,指示二极管一直处于熄灭状态;被检测物体出现在检测范围内时,红外线被反射回来且强度足够大,光敏三极管饱和,此时模块的输出端为高电平,指示二极管被点亮。TCRT5000反射式光电传感器是经常使用的传感器,这个系列的传感器种类齐全、价格便宜、体积小、使用方便、质量可靠、用途广泛。此传感器含一个反射模块(发光二极管)和一个接收模块(光敏三极管)。通过发射红外信号,看接收信号变化判断检测物体状态的变化,图14为TCRT5000传感器模块电路原理图,图15为它的实物图 。
图14 TCRT5000传感器模块电路原理图
图15 TCRT5000的实物图
基本参数如下:
外形尺寸:长 32mm~37 mm;宽 7.5mm;厚 2mm
工作电压:DC 3V~5.5V,推荐工作电压为5V
检测距离:1mm~8mm适用,焦点距离为2.5mm
3.5 无线模块的硬件设计
无线模块的硬件设计采用两块NRF24L01模块实时接收遥控发送的新指令.
3.6 重力感应模块的硬件设计
重力感应模块采用MPU-6050模块(三轴陀螺仪 + 三轴加速度)
MPU-6000为全球首例整合性6轴运动处理组件,相较于多组件方案,免除了组合陀螺仪与加速器时之轴间差的问题,减少了大量的包装空间。MPU-6000整合了3轴陀螺仪、3轴加速器,并含可藉由第二个I2C端口连接其他厂牌之加速器、磁力传感器、或其他传感器的数位运动处理(DMP: Digital Motion Processor)硬件加速引擎,由主要I2C端口以单一数据流的形式,向应用端输出完整的9轴融合演算技术
InvenSense的运动处理资料库,可处理运动感测的复杂数据,降低了运动处理运算对操作系统的负荷,并为应用开发提供
架构
酒店人事架构图下载公司架构图下载企业应用架构模式pdf监理组织架构图免费下载银行管理与it架构pdf
化的API。
MPU-6000的角速度全格感测范围为?250、?500、?1000与?2000?/sec (dps),可准确追緃快速与慢速动作,并且,用户可程式控制的加速器全格感测范围为?2g、?4g?8g与?16g。产品传输可透过最高至400kHz的I2C或最高达20MHz的SPI。
MPU-6000可在不同电压下工作,VDD供电电压介为2.5V?5%、3.0V?5%或3.3V?5%,逻辑接口VVDIO供电为1.8V? 5%。MPU-6000的包装尺寸4x4x0.9mm(QFN),在业界是革命性的尺寸。其他的特征包含内建的温度感测器、包含在运作环境中仅有?1%变动的振荡器。 应用
运动感测游戏
现实增强
电子稳像 (EIS: Electronic Image Stabilization) 光学稳像(OIS: Optical Image Stabilization) 行人导航器
“零触控”手势用户接口
姿势快捷方式
认证
市场
智能型手机
平板装置设备
手持型游戏产品
?蜗坊?
3D遥控器
可携式导航设备
特征
以数字输出6轴或9轴的旋转矩阵、四元数(quaternion)、欧拉角格式(Euler Angle forma)的融合演算数据。
具有131 LSBs/?/sec 敏感度与全格感测范围为?250、?500、?1000与?2000?/sec 的3轴角速度感测器(陀螺仪)。
可程式控制,且程式控制范围为?2g、?4g、?8g和?16g的3轴加速器。 移除加速器与陀螺仪轴间敏感度,降低设定给予的影响与感测器的飘移。 数字运动处理(DMP: Digital Motion Processing)引擎可减少复杂的融合演算数据、感测器同步化、姿势感应等的负荷。
运动处理数据库支持Android、Linux与Windows
内建之运作时间偏差与磁力感测器校正演算技术,免除了客户须另外进行校正的需求。 以数位输出的温度传感器
以数位输入的同步引脚(Sync pin)支援视频电子影相稳定技术与GPS
可程式控制的中断(interrupt)支援姿势识别、摇摄、画面放大缩小、滚动、快速下降中断、
high-G中断、零动作感应、触击感应、摇动感应功能。
VDD供电电压为2.5V?5%、3.0V?5%、3.3V?5%;VDDIO为1.8V? 5% 陀螺仪运作电流:5mA,陀螺仪待命电流:5μA;加速器运作电流:350μA,加速器省电模式电流: 20μA@10Hz
高达400kHz快速模式的I2C,或最高至20MHz的SPI串行主机接口(serial host interface)
内建频率产生器在所有温度范围(full temperature range)仅有?1%频率变化。 使用者亲自测试
10,000 g 碰撞容忍度
为可携式产品量身订作的最小最薄包装 (4x4x0.9mm QFN) 符合RoHS及环境标准
6 效果图
6软件设计
4.1 编译语言的选取
目前,STC89C52单片机的开发多为支持两种语言,一种是汇编语言,另一种是C语言,而这两种语言各有其优缺点。汇编语言:效率高,对硬件的可操控性更强,体积小,但不易维护,可移植性很差。C语言:效率比较低,硬件可操控性比较差,目标代码体积大,但容易维护,可移植性很好。
而在本设计里面,程序需要接近底层,但程序要解决的问题繁多,逻辑关系也比较复杂,代码量也比较大,又考虑到产品以后需要升级,各方面综合考虑,主要以C51语言来编写本设计的程序是最佳选择。
4.2 软件调试平台
Keil for C51是美国Keil Software公司出品的C语言软件开发系统,与汇编相比,C语言在功能上、结构性、可读性、可维护性上有明显的优势,因而易学易用。Keil C51软件提供丰富的库函数和功能强大的集成开发调试工具,全Windows界面。另外重要的一点,只要看一下编译后生成的汇编代码,就能体会到Keil for C51生成的目标代码效率非常之高,多数语句生成的汇编代码很紧凑,容易理解。在开发大型软件时更能体现高级语言的优势。下面详细介绍Keil for C51开发系统各部分功能和使用。
C51开发中除必要的硬件外,同样离不开软件,我们写的源程序要变为C51可以执行的机器码有两种方法,一种是手工汇编,另一种是机器汇编,目前已极少使用手工汇编的方法了。随着C51开发技术的不断发展,从普遍使用汇编语言到逐渐使用高级语言开发,单片机的开发软件也在不断发展,Keil软件除了致力于单片机的编程开发平台外,还针对目前最流行C51开发项目出品了Keil for 51软件平台以及支持在线调试的串口烧写。
从近年来各仿真机厂商纷纷宣布全面支持Keil即可看出。Keil提供了包括C编译器、宏汇编、连接器、库管理和一个功能强大的仿真调试器等在内的完整开发方案,通过一个集成开发环境(uVision2)将这些部份组合在一起。
4.2 小车端程序
文件一main.c
#include
#include #include "..\header\init.h" #include "..\header\NRF24L01.h"
void R_S_Byte(uchar R_Byte) {
SBUF = R_Byte;
while( TI == 0 ); //查询法
TI = 0;
}
/**********定时器初始化程序***************/
void T0T1_init()
{
EA=1;
ET1=1;
ET0=1;
TMOD=0x11;//定时器0负责小车速度控制 定时器1负责超声波测距和舵机控制
TH0=(65536-500)/256;
TL0=(65536-500)%256;
TH1=(65536-500)/256;
TL1=(65536-500)%256;
TL1=0;
TH1=0;
TR0=1;
TR1=1;
}
void StartModule(void)//超声波测距子函数
{
TX=1; //启动一次模块
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
TX=0;
}
void Conut(void)//通过超声波测量的数据计算距离 {
if(csflag==0)
{
time=TH1*256+TL1;
TH1=0;
TL1=0;
S=(time*1.7)/10; //算出来是 mm
// if(S>499) ;
}
else
{
csflag=0;
S=600;//如果定时器溢出则距离S=600
}
if(jd==3) {middleS=S; }// 测量正前方
else if(jd==2) {leftS=S;}//测量左前方
else if(jd==4) {rightS=S;}//测量右前方 }
/**********************接收遥控器的状态标志**********************/
void RX_STATE(void) {
uchar RxBuf[4];
uchar SIGN=0;
if(nRF24L01_RxPacket(RxBuf))//如果收到遥控器的数据则进入
{
led2=~led2;
R_S_Byte(RxBuf[0]);
SIGN=RxBuf[2];//判断倾斜的方向 X_SIAN=0:前方 X_SIAN=1:下方
STATE=RxBuf[3];
LED_FLAG=(SIGN&0x04)>>2;//判断小车灯光控制位
CONTROL_MODE_FLAG=(STATE&0xf0)>>4;//读取当前状态标志
if(LED_FLAG) LED1=1;
else if(LED_FLAG==0) LED1=0;
}
}
void delay_RX(uint z)//避障模式下当出现while循环时用的延时函数, {
uint x,y;
for(x=110;x>0;x--)
{
for(y=z;y>0;y--);
RX_STATE();//扫描无线模块接收到的新指令
}
}
void measured(unsigned char fs)//测距函数jd=3测量正前方jd=2测量左前方jd=4测量右前方
{
TH1 = (65536-500)/256;
TL1 = (65536-500)%256; //12MZ晶振,0.5ms
mode=0;
jd=fs;
count2=0;
TR1=1;
delay_RX(500);
TR1=0;
mode=1;
TH1=0;
TL1=0;
StartModule();
while(!RX); //当RX为零时等待
TR1=1; //开启计数
while(RX); //当RX为1计数并等待
TR1=0; //关闭计数
Conut();
}
/********************避障模式子函数***********************/
void csbmode(void)
{
unsigned char temp;
TR1=0;
mode=1;
TH1=0;
TL1=0;
StartModule();
while(!RX); //当RX为零时等待
TR1=1; //开启计数
while(RX); //当RX为1计数并等待
TR1=0; //关闭计数
Conut();//测量前方距离
delay_RX(80);
speed1=16;speed2=16;
while(middleS>500 &&CONTROL_MODE_FLAG==3) //当前方空间大于300mm
时保持
{
IN1_1=0;IN1_2=1;IN2_1=0;IN2_2=1;//小车前进
TR1=0;
mode=1;
TH1=0;
TL1=0;
StartModule();
while(!RX); //当RX为零时等待
TR1=1; //开启计数
while(RX); //当RX为1计数并等待
TR1=0; //关闭计数
Conut();
delay_RX(2);
}
IN1_1=0;IN1_2=0;IN2_1=0;IN2_2=0;//小车停止
speed1=0;speed2=0;
measured(2);
measured(4);
TH1 = (65536-500)/256; //舵 机 复位
TL1 = (65536-500)%256; //12MZ晶振,0.5ms
mode=0;
jd=3;
count2=0;
TR1=1;
delay_RX(500);
TR1=0;
if(leftS<250||rightS<250)
{
temp=middleS;
speed1=20;speed2=20;
IN1_1=1;IN1_2=0;IN2_1=1;IN2_2=0;//小车后退
while((middleS-temp)<200&&CONTROL_MODE_FLAG==3)
{
speed1=20;speed2=20;
delay_RX(2);
TR1=0;
mode=1;
TH1=0;
TL1=0;
StartModule();
while(!RX); //当RX为零时等待
TR1=1; //开启计数
while(RX); //当RX为1计数并等待
TR1=0; //关闭计数
Conut();
}
speed1=20;speed2=20;
if(leftS>2;//判断小车灯光控制位
CONTROL_MODE_FLAG=(STATE&0xf0)>>4;//读取当前状态标志
KEY_VALUE=STATE&0x0f;//读取键值
if(LED_FLAG) LED1=1;
else if(LED_FLAG==0) LED1=0;
X_SIGN= SIGN&0x01;
Y_SIGN= (SIGN&0x02)>>1;
if(X_ANGLE>110) X_ANGLE=105;//全速时:if(X_ANGLE>160) X_ANGLE=155;
if(Y_ANGLE>110) Y_ANGLE=110;//全速时:if(X_ANGLE>160) X_ANGLE=160;
if(Y_ANGLE-X_ANGLE<30)//如果主要是往纵向倾斜则进入
{
FLAG_ANGLE=1;
if(Y_SIGN)//同时也往左方倾斜
{speed1=40-X_ANGLE/4;speed2=40-(X_ANGLE/4-Y_ANGLE/4);}//左轮
else if(Y_SIGN==0)
{speed1=40-(X_ANGLE/4-Y_ANGLE/4);speed2=40-X_ANGLE/4;}
}
else
{
speed1=40-Y_ANGLE/4;speed2=40-Y_ANGLE/4;
}
if(X_SIGN) X_ANGLE=-X_ANGLE;
if(Y_SIGN) Y_ANGLE=-Y_ANGLE;
if(FLAG_ANGLE)
{
FLAG_ANGLE=0;
if(X_ANGLE>20){IN1_1=1;IN1_2=0;IN2_1=1;IN2_2=0; }
//小车前进
else
if(X_ANGLE<-20&&middleS>200){IN1_1=0;IN1_2=1;IN2_1=0;IN2_2=1;} //小车后退
else
{
IN1_1=0;IN1_2=0;IN2_1=0;IN2_2=0;
//小车停止
}
}
else
{
if(Y_ANGLE>20){IN1_1=1;IN1_2=0;IN2_1=0;IN2_2=1;}
//小车左转
else if(Y_ANGLE<-20){IN1_1=0;IN1_2=1;IN2_1=1;IN2_2=0;}
//小车右转
else
{
IN1_1=0;IN1_2=0;IN2_1=0;IN2_2=0;;
//小车停止
}
}
}
}
break;
case 2:
// speed1=15;
// speed2=15;
while(CONTROL_MODE_FLAG==2)
{
xunji();
}
IN1_1=0;IN1_2=0;IN2_1=0;IN2_2=0; //
小车停止
break;
case 3:
// TR0=0;
while(CONTROL_MODE_FLAG==3)
{
csbmode();
}
// TR0=1;
IN1_1=0;IN1_2=0;IN2_1=0;IN2_2=0; //
小车停止
break;
}
}
}
//定时器中断
void zhongduan(void) interrupt 1
{
TH0 = (65536-500)/256;
TL0 = (65536-500)%256;
aa++;
if(aa==100)
{
led0=~led0;
aa=0;t0++;
}
/******************************************/
//大于则输出低电平
/******************************************/
if(count typedef unsigned char uchar;
typedef unsigned char uint;
uchar t0;
uchar aa;
unsigned char count; //0.5ms次数标识 unsigned char count2; //0.5ms次数标识 unsigned char speed1; unsigned char speed2;
unsigned int GS;//脉冲个数
unsigned char GS_H;//脉冲个数高8位
unsigned char GS_L;//脉冲个数低8位
//#define L 15 //#define S 20 sbit led0=P2^6;
sbit led2= P2^7;
//sbit led3= P2^5; //sbit led4= P2^4; uchar STATE=0; //小车状态
uchar KEY_VALUE=0; //键值
sbit LED1= P1^0;//小车灯光控制位
uchar LED_FLAG=0;//小车灯光标志 0:关闭 1:开启 uchar CONTROL_MODE_FLAG=0; //控制模式标志 0:按键模式 1:重力模式 2:循迹模式
3:避障模式
uchar SFLAG=1;
bit mode=0;//定时器工作状态标志。0: 舵机控制 1: 超声波测距
sbit IN1_1=P2^1;
sbit IN1_2=P2^0;
sbit EN1=P2^2;
sbit IN2_1=P2^4;
sbit IN2_2=P2^3;
sbit EN2=P2^5;
//sbit MP1=P2^6;
//sbit MP2=P2^7;
sbit irL1=P1^1;
sbit irL2=P1^2;
sbit irR1=P3^2;
sbit irR2=P3^3;
sbit CE =P3^6;
sbit CSN =P1^7;
sbit SCK =P1^6;
sbit MOSI =P3^4;
sbit MISO =P3^5;
sbit IRQ =P3^7;
sbit pwm =P1^5 ; //P舵机WM信号输出
sbit TX=P1^4;
sbit RX=P1^3;
unsigned char jd; //角度标识
unsigned int S;
unsigned int time;
unsigned int leftS,middleS,rightS;
bit csflag=0; //超声波测距接收超时标志 1: 表示没有接收到超声返回信号
//*********************************************NRF24L01***********************
**************
uchar TxBuf2[4]={0,0,0,0};
//uchar RxBuf[5];
#define TX_ADR_WIDTH 5 // 5 uints TX address width
#define RX_ADR_WIDTH 5 // 5 uints RX address width
#define TX_PLOAD_WIDTH 10 // 20 uints TX payload #define RX_PLOAD_WIDTH 10 // 20 uints TX payload uint const TX_ADDRESS[TX_ADR_WIDTH]= {0x34,0x43,0x10,0x10,0x01}; //本地地址 uint const RX_ADDRESS[RX_ADR_WIDTH]= {0x34,0x43,0x10,0x10,0x01}; //接收地址 //***************************************NRF24L01寄存器指令
*******************************************************
#define READ_REG 0x00 // 读寄存器指令
#define WRITE_REG 0x20 // 写寄存器指令
#define RD_RX_PLOAD 0x61 // 读取接收数据指令
#define WR_TX_PLOAD 0xA0 // 写待发数据指令
#define FLUSH_TX 0xE1 // 冲洗发送 FIFO指令
#define FLUSH_RX 0xE2 // 冲洗接收 FIFO指令
#define REUSE_TX_PL 0xE3 // 定义重复装载数据指令
#define NOP 0xFF // 保留
//*************************************SPI(nRF24L01)寄存器地址****************************************************
#define CONFIG 0x00 // 配置收发状态,CRC校验模式以及收发状态响应方式 #define EN_AA 0x01 // 自动应答功能设置
#define EN_RXADDR 0x02 // 可用信道设置
#define SETUP_AW 0x03 // 收发地址宽度设置
#define SETUP_RETR 0x04 // 自动重发功能设置
#define RF_CH 0x05 // 工作频率设置
#define RF_SETUP 0x06 // 发射速率、功耗功能设置
#define STATUS 0x07 // 状态寄存器
#define OBSERVE_TX 0x08 // 发送监测功能
#define CD 0x09 // 地址检测 #define RX_ADDR_P0 0x0A // 频道0接收数据地址
#define RX_ADDR_P1 0x0B // 频道1接收数据地址
#define RX_ADDR_P2 0x0C // 频道2接收数据地址
#define RX_ADDR_P3 0x0D // 频道3接收数据地址
#define RX_ADDR_P4 0x0E // 频道4接收数据地址
#define RX_ADDR_P5 0x0F // 频道5接收数据地址
#define TX_ADDR 0x10 // 发送地址寄存器
#define RX_PW_P0 0x11 // 接收频道0接收数据长度
#define RX_PW_P1 0x12 // 接收频道0接收数据长度
#define RX_PW_P2 0x13 // 接收频道0接收数据长度
#define RX_PW_P3 0x14 // 接收频道0接收数据长度
#define RX_PW_P4 0x15 // 接收频道0接收数据长度
#define RX_PW_P5 0x16 // 接收频道0接收数据长度
#define FIFO_STATUS 0x17 // FIFO栈入栈出状态寄存器设置 //*****************************************************************************
*********
void Delay(unsigned int s);
void inerDelay_us(unsigned char n); void init_NRF24L01(void);
uint SPI_RW(uint uchar);
uchar SPI_Read(uchar reg);
uint SPI_RW_Reg(uchar reg, uchar value); uint SPI_Read_Buf(uchar reg, uchar *pBuf, uchar uchars);
uint SPI_Write_Buf(uchar reg, uchar *pBuf, uchar uchars);
unsigned char nRF24L01_RxPacket(unsigned char* rx_buf);
void delay(uint z)
{
uint x,y;
for(x=110;x>0;x--)
for(y=z;y>0;y--);
}
uint bdata sta; //状态标志
sbit RX_DR =sta^6;
sbit TX_DS =sta^5;
sbit MAX_RT =sta^4;
void inerDelay_us(unsigned char n)
{
for(;n>0;n--)
_nop_();
}
文件三< NRF24L01.h>
void init_RX(void)
{
inerDelay_us(100);
CE=0; // chip enable
CSN=1; // Spi disable
SCK=0;
SPI_RW_Reg(WRITE_REG + CONFIG, 0x0f); // Set PWR_UP bit, enable CRC(2 bytes) &
SPI_RW_Reg(WRITE_REG + EN_AA, 0x01);
SPI_RW_Reg(WRITE_REG + EN_RXADDR, 0x01); // Enable Pipe0
SPI_RW_Reg(WRITE_REG + SETUP_AW, 0x02); // Setup address width=5 bytes
SPI_RW_Reg(WRITE_REG + SETUP_RETR, 0x1a); // 500us + 86us, 10 retrans...
SPI_RW_Reg(WRITE_REG + RF_CH, 0);
SPI_RW_Reg(WRITE_REG + RF_SETUP, 0x07); // TX_PWR:0dBm, Datarate:1Mbps,
SPI_RW_Reg(WRITE_REG + RX_PW_P0, RX_PLOAD_WIDTH);
SPI_Write_Buf(WRITE_REG + TX_ADDR, TX_ADDRESS, TX_ADR_WIDTH);
SPI_Write_Buf(WRITE_REG + RX_ADDR_P0, TX_ADDRESS, TX_ADR_WIDTH); //CE=1; //
}
uint SPI_RW(uint uchar)
{
uint bit_ctr;
for(bit_ctr=0;bit_ctr<8;bit_ctr++) // output 8-bit
{
MOSI = (uchar & 0x80); // output 'uchar', MSB to MOSI
uchar = (uchar << 1); // shift next bit into MSB..
SCK = 1; // Set SCK high..
uchar |= MISO; // capture current MISO bit
SCK = 0; // ..then set SCK low again
}
return(uchar); // return read uchar }
uchar SPI_Read(uchar reg)
{
uchar reg_val;
CSN = 0; // CSN low, initialize SPI communication...
SPI_RW(reg); // Select register to read from..
reg_val = SPI_RW(0); // ..then read registervalue
CSN = 1; // CSN high, terminate SPI communication
return(reg_val); // return register value }
uint SPI_RW_Reg(uchar reg, uchar value)
{
uint status;
CSN = 0; // CSN low, init SPI transaction
status = SPI_RW(reg); // select register
SPI_RW(value); // ..and write value to it..
CSN = 1; // CSN high again
return(status); // return nRF24L01 status uchar }
uint SPI_Read_Buf(uchar reg, uchar *pBuf, uchar uchars) {
uint status,uchar_ctr;
CSN = 0; // Set CSN low, init SPI tranaction
status = SPI_RW(reg); // Select register to write to and read status uchar
for(uchar_ctr=0;uchar_ctr
#include
#include //Keil library #include //Keil library #include
#include
#include "..\header\init.h" #include "..\header\MPU6050.h" #include "..\header\lcdinit.h" #include "..\header\NRF24L01.h"
#include "..\header\keyscan.h"
/**********定时器初始化程序***************/
void T0_init()
{
TMOD=0x01;
TH0=(65535-50000)/256;
TL0=(65535-50000)%256;
EA=1;
ET0=1;
TR0=1;
}
void init_system() {
BUZZER=0;//关闭蜂鸣器
LED_FLAG=0;//关闭小车灯光
CONTROL_MODE_FLAG=0;//开机默认重力模式
init_TX() ;//初始化NRF24L01 ,接收模式
lcdinit(); //初始化LCd
T0_init();//初始化定时器
InitMPU6050(); //初始化MPU6050
lcd_wcmd(0x01);
delay(1);
play(0,2,dis1);//显示按键模式
lcd_pos(2,0);
lcd_wdat(76);//显示"L"
lcd_wdat(58);
lcd_pos(3,0);
lcd_wdat(82);//显示"R"
lcd_wdat(58);
// lcd_wcmd(0x01); //清除LCD的显示内容
delay(1);
lcd_pos(1,5);
lcd_wdat(30);
lcd_pos(2,4);
lcd_wdat(17);
lcd_pos(2,6);
lcd_wdat(16);
lcd_pos(3,5);
lcd_wdat(31);
lcd_pos(2,5);
lcd_wdat(9);
}
void main(void)
{
uchar X_SIGN=0;//小车转弯标志 1:小车左转弯 0:小车右转弯
uchar Y_SIGN=0;
uchar SIGN=0;
uchar FLAG_ANGLE=0; // 1 :小车前进或后退(可差速转弯) 0 :原地打转
uchar speed1,speed2;
uchar flag_key=0;
uchar bai,shi,ge;
unsigned int bb;
uchar BACK_LED_FLAG=0;
int X_ANGLE;
int Y_ANGLE;
uchar a=0;
BACK_LED_FLAG=1;
BACK_LED=1;
init_system();//系统初始化
while(1)
{
/***********获取MPU0605坐标数据,处理后发送至小车端************/
if(aa%2==0)
{
X_ANGLE=GetData(ACCEL_XOUT_H);
Y_ANGLE=GetData(ACCEL_YOUT_H);
/************将X轴倾斜数据转化成十进制****************/
X_ANGLE/=64;
if(X_ANGLE<0)
{
X_SIGN=1;
X_ANGLE=-X_ANGLE;
}
else X_SIGN=0;
bai =X_ANGLE/100;
X_ANGLE=X_ANGLE%100; //取余运算
shi =X_ANGLE/10;
X_ANGLE=X_ANGLE%10; //取余运算
ge =X_ANGLE;
X_ANGLE=bai*100+shi*10+ge;
/***********将Y轴倾斜数据转化成十进制**********/
Y_ANGLE/=64;
if(Y_ANGLE<0)
{
Y_SIGN=1;
Y_ANGLE=-Y_ANGLE;
}
else Y_SIGN=0;
bai =Y_ANGLE/100;
Y_ANGLE=Y_ANGLE%100; //取余运算
shi =Y_ANGLE/10;
Y_ANGLE=Y_ANGLE%10; //取余运算
ge =Y_ANGLE;
Y_ANGLE=bai*100+shi*10+ge;
/********************************/
SIGN=(X_SIGN)|(Y_SIGN<<1)|(LED_FLAG<<2);
STATE=((P1&0x3c)>>2)|(CONTROL_MODE_FLAG<<4);
/******************************/
TxBuf2[0]=X_ANGLE;
TxBuf2[1]=Y_ANGLE;
TxBuf2[2]=SIGN;
TxBuf2[3]=STATE;
nRF24L01_TxPacket(TxBuf2);
// delay(1);
// init_RX();
// Display10BitData(GetData(ACCEL_ZOUT_H),2,1); //显示Z轴加速度
// Display10BitData(GetData(GYRO_XOUT_H),0,5); //显示X轴角速度
// Display10BitData(GetData(GYRO_YOUT_H),1,5); //显示Y轴角速度
// Display10BitData(GetData(GYRO_ZOUT_H),2,5); //显示Z轴角速度
}
/**************************************************************************/
/*********通过MPU0605的数据判断小车当前的状态刷新液晶显示*******/
if(aa%5==0)
{
if(X_ANGLE>160) X_ANGLE=155;
if(X_ANGLE<20) X_ANGLE=0;
if(Y_ANGLE>160) Y_ANGLE=160;
if(Y_ANGLE<20) Y_ANGLE=0;
if(X_ANGLE>Y_ANGLE)//如果主要是往纵向倾斜则进入
{
FLAG_ANGLE=1;
if(Y_SIGN)//同时也往左方倾斜
{speed1=X_ANGLE/1.6;speed2=X_ANGLE/1.6-Y_ANGLE/1.6;}//左轮
else if(Y_SIGN==0)
{speed1=X_ANGLE/1.6-Y_ANGLE/1.6;speed2=X_ANGLE/1.6;}
}
else
{
speed1=Y_ANGLE/1.6;speed2=Y_ANGLE/1.6;
}
lcd_pos(2,1);
if(speed1/100==0) lcd_wdat(32);
else lcd_wdat(table1[speed1/100]);
if(speed1%100/10==0) lcd_wdat(32);
else lcd_wdat(table1[speed1%100/10]);
lcd_wdat(table1[speed1%10]);
lcd_wdat(37);
lcd_pos(3,1);
if(speed2/100==0) lcd_wdat(32);
else lcd_wdat(table1[speed2/100]);
if(speed2%100/10==0) lcd_wdat(32);
else lcd_wdat(table1[speed2%100/10]);
lcd_wdat(table1[speed2%10]);
lcd_wdat(37);
}
if(aa==10&&(X_ANGLE>10||Y_ANGLE>10))//箭头部分显示
{
if(FLAG_ANGLE)
{
if(X_SIGN)
{
lcd_pos(1,5);
lcd_wdat(32);
lcd_pos(3,5);
lcd_wdat(31);
if(speed1>speed2) {lcd_pos(2,4); lcd_wdat(32);lcd_pos(2,6);
lcd_wdat(16);}
else if(speed1speed2) {lcd_pos(2,6); lcd_wdat(32);lcd_pos(2,4);
lcd_wdat(17);}
}
}
}
/******************按键扫描*********************/ /**********************************************************************/
if(KEY_LED==0) //扫描灯光控制按键
{
delay(20);
if(KEY_LED==0)
{
//LED_FLAG=~LED_FLAG;
if(LED_FLAG) LED_FLAG=0;//如果按键按下标志的值取反
else if(LED_FLAG==0) LED_FLAG=1;
}
while(KEY_LED==0);
delay(20);
while(KEY_LED==0);
}
/***************控制模式**************/
switch(CONTROL_MODE_FLAG)
{
case 0:
if(KEY_CONTROL_MODE==0)
{
delay(20);
if(KEY_CONTROL_MODE==0)
{
CONTROL_MODE_FLAG=1;
play(0,2,dis2);//显示按键模式
}
while(KEY_CONTROL_MODE==0);
delay(20);
while(KEY_CONTROL_MODE==0);
}
break;
case 1:
if(KEY_CONTROL_MODE==0)
{
delay(20);
if(KEY_CONTROL_MODE==0)
{
CONTROL_MODE_FLAG=0;
play(0,2,dis1);//显示重力模式
}
while(KEY_CONTROL_MODE==0);
delay(20);
while(KEY_CONTROL_MODE==0);
}
if(KEY_UP==0)
{
delay(20);
if(KEY_UP==0)
{
CONTROL_MODE_FLAG=2;
play(0,2,dis3);//显示循迹模式
}
while(KEY_UP==0);
delay(20);
while(KEY_UP==0);
}
if(KEY_DOWN==0)
{
delay(20);
if(KEY_DOWN==0)
{
CONTROL_MODE_FLAG=3;
play(0,2,dis4);//显示避障模式
}
while(KEY_DOWN==0);
delay(20);
while(KEY_DOWN==0);
}
break;
case 2:
if(KEY_CONTROL_MODE==0)
{
delay(20);
if(KEY_CONTROL_MODE==0)
{
CONTROL_MODE_FLAG=0;
play(0,2,dis1);//显示按键模式
}
while(KEY_CONTROL_MODE==0);
delay(20);
while(KEY_CONTROL_MODE==0);
}
break;
case 3:
if(KEY_CONTROL_MODE==0)
{
delay(20);
if(KEY_CONTROL_MODE==0)
{
CONTROL_MODE_FLAG=0;
play(0,2,dis1);//显示按键模式
}
while(KEY_CONTROL_MODE==0);
delay(20);
while(KEY_CONTROL_MODE==0);
}
break;
}
/***************液晶背光控制**********/
if(aa%5==0)
{
if(BACK_LED_FLAG)
{
bb++;
if(bb==400){ BACK_LED_FLAG=0;bb=0;BACK_LED=0; }
}
if(IR_LED==0){ BACK_LED_FLAG=1;bb=0;BACK_LED=1; }
/*********************************/
}
}
}
void timer() interrupt 1 {
TH0=(65535-50000)/256;
TL0=(65535-50000)%256;
aa++;
if(aa==20)
{
aa=0;
lcd_pos(1,5);
lcd_wdat(30);
lcd_pos(2,4);
lcd_wdat(17);
lcd_pos(2,6);
lcd_wdat(16);
lcd_pos(3,5);
lcd_wdat(31);
}
}
文件二< init.h >
typedef unsigned char uchar;
typedef unsigned char uint; #define U16 unsigned int
unsigned char GS_H;//脉冲个数高8位
unsigned char GS_L;//脉冲个数低8位
sbit CE =P3^6;
sbit CSN =P1^6;
sbit SCK =P1^7; sbit MOSI =P3^4;
sbit MISO =P3^5;
sbit IRQ =P3^7;
sbit KEY_UP =P1^5;
sbit KEY_DOWN =P1^2;
sbit KEY_RIGHT =P1^4; sbit KEY_LEFT =P1^3;
sbit KEY_LED= P3^3;//小车灯光控制按键
uchar LED_FLAG=0;//小车灯光标志 0:关闭 1:开启
sbit BACK_LED=P2^2;//液晶背光控制位
sbit IR_LED=P2^3;
sbit KEY_CONTROL_MODE=P2^1;//模式控制按键 uchar CONTROL_MODE_FLAG=0; //控制模式标志 0:按键模式 1:重力模式 2:循迹模式
3:避障模式
sbit BUZZER=P2^0; //蜂鸣器控制位
uchar STATE=0; //小车状态
uchar keyflag=0;
uchar aa;
#define KEYDAT (P1 & 0xf0)
/**********************lcd相关定义**************/
#define WRITE_SECOND 0x80 #define WRITE_MINUTE 0x82 #define WRITE_HOUR 0x84 #define WRITE_WEEK 0x8A #define WRITE_YEAH 0x8C #define WRITE_MONTH 0x88 #define WRITE_DAY 0x86 #define READ_SECOND 0x81 #define READ_MINUTE 0x83 #define READ_HOUR 0x85 #define READ_WEEK 0x8B #define READ_YEAH 0x8D #define READ_MONTH 0x89 #define READ_DAY 0x87 #define WRITE_PROTECT 0x8E
#define delayNOP(); {_nop_();_nop_();_nop_();_nop_();};
bit lcd_busy();
uchar code table1[]={48,49,50,51,52,53,54,55,56,57};
uchar code dis1[] = {"按键模式"};
uchar code dis2[] = {"重力模式"};
uchar code dis3[] = {"循迹模式"};
uchar code dis4[] = {"避障模式"};
//位寻址寄存器定义
sbit ACC_7=ACC^7;
/*12864A-3 端口定义*/
#define LCD_data P0 //数据口
sbit LCD_RS = P2^4; //寄存器选择输入
sbit LCD_RW = P2^5; //液晶读/写控制
sbit LCD_EN = P2^6; //液晶使能控制
sbit LCD_PSB = P2^7; //串/并方式控制
//sbit LCD_RST = ; //液晶复位端口
/**********************/
/***********************/
uchar TxBuf2[5]={0,0,0,0,0};
uchar RxBuf[4];
#define TX_ADR_WIDTH 5 // 5 uints TX address width
#define RX_ADR_WIDTH 5 // 5 uints RX address width
#define TX_PLOAD_WIDTH 10 // 20 uints TX payload
#define RX_PLOAD_WIDTH 10 // 20 uints TX payload
uint const TX_ADDRESS[TX_ADR_WIDTH]= {0x34,0x43,0x10,0x10,0x01}; //本地地址 uint const RX_ADDRESS[RX_ADR_WIDTH]= {0x34,0x43,0x10,0x10,0x01}; //接收地址 #define READ_REG 0x00 // 读寄存器指令
#define WRITE_REG 0x20 // 写寄存器指令
#define RD_RX_PLOAD 0x61 // 读取接收数据指令
#define WR_TX_PLOAD 0xA0 // 写待发数据指令
#define FLUSH_TX 0xE1 // 冲洗发送 FIFO指令
#define FLUSH_RX 0xE2 // 冲洗接收 FIFO指令
#define REUSE_TX_PL 0xE3 // 定义重复装载数据指令
#define NOP 0xFF // 保留
#define CONFIG 0x00 // 配置收发状态,CRC校验模式以及收发状态响应方式 #define EN_AA 0x01 // 自动应答功能设置
#define EN_RXADDR 0x02 // 可用信道设置
#define SETUP_AW 0x03 // 收发地址宽度设置
#define SETUP_RETR 0x04 // 自动重发功能设置
#define RF_CH 0x05 // 工作频率设置
#define RF_SETUP 0x06 // 发射速率、功耗功能设置
#define STATUS 0x07 // 状态寄存器
#define OBSERVE_TX 0x08 // 发送监测功能
#define CD 0x09 // 地址检测 #define RX_ADDR_P0 0x0A // 频道0接收数据地址
#define RX_ADDR_P1 0x0B // 频道1接收数据地址
#define RX_ADDR_P2 0x0C // 频道2接收数据地址
#define RX_ADDR_P3 0x0D // 频道3接收数据地址
#define RX_ADDR_P4 0x0E // 频道4接收数据地址
#define RX_ADDR_P5 0x0F // 频道5接收数据地址
#define TX_ADDR 0x10 // 发送地址寄存器
#define RX_PW_P0 0x11 // 接收频道0接收数据长度
#define RX_PW_P1 0x12 // 接收频道0接收数据长度
#define RX_PW_P2 0x13 // 接收频道0接收数据长度
#define RX_PW_P3 0x14 // 接收频道0接收数据长度
#define RX_PW_P4 0x15 // 接收频道0接收数据长度
#define RX_PW_P5 0x16 // 接收频道0接收数据长度
#define FIFO_STATUS 0x17 // FIFO栈入栈出状态寄存器设置
void Delay(unsigned int s);
void inerDelay_us(unsigned char n); void init_NRF24L01(void);
uint SPI_RW(uint uchar);
uint SPI_RW_Reg(uchar reg, uchar value); uint SPI_Write_Buf(uchar reg, uchar *pBuf, uchar uchars);
void nRF24L01_TxPacket(unsigned char * tx_buf);
sbit SCL=P1^0; //IIC时钟引脚定义
sbit SDA=P1^1; //IIC数据引脚定义
//**************************************** // 定义MPU6050内部地址
//**************************************** #define SMPLRT_DIV 0x19 //陀螺仪采样率,典型值:0x07(125Hz) #define CONFIG_MPU 0x1A //低通滤波频率,典型值:0x06(5Hz) #define GYRO_CONFIG 0x1B //陀螺仪自检及测量范围,典型值:0x18(不自检,2000deg/s)
#define ACCEL_CONFIG 0x1C //加速计自检、测量范围及高通滤波频率,典型值:0x01(不自检,2G,5Hz)
#define ACCEL_XOUT_H 0x3B
#define ACCEL_XOUT_L 0x3C
#define ACCEL_YOUT_H 0x3D
#define ACCEL_YOUT_L 0x3E
#define ACCEL_ZOUT_H 0x3F
#define ACCEL_ZOUT_L 0x40
#define TEMP_OUT_H 0x41
#define TEMP_OUT_L 0x42
#define GYRO_XOUT_H 0x43
#define GYRO_XOUT_L 0x44
#define GYRO_YOUT_H 0x45
#define GYRO_YOUT_L 0x46
#define GYRO_ZOUT_H 0x47
#define GYRO_ZOUT_L 0x48
#define PWR_MGMT_1 0x6B //电源管理,典型值:0x00(正常启用) #define WHO_AM_I 0x75 //IIC地址寄存器(默认数值0x68,只读) #define SlaveAddress 0xD0 //IIC写入时的地址字节数据,+1为读取 //**************************************** //定义类型及变量
//**************************************** uchar dis[4]; //显示数字(-511至512)的字符数组 int dis_data; //变量
//int Temperature,Temp_h,Temp_l; //温度及高低位数据
//**************************************** //函数声明
//****************************************
//MPU6050操作函数
void InitMPU6050(); //初始化MPU6050
void Delay5us();
void I2C_Start();
void I2C_Stop();
void I2C_SendACK(bit ack);
bit I2C_RecvACK();
void I2C_SendByte(uchar dat); uchar I2C_RecvByte();
void I2C_ReadPage();
void I2C_WritePage();
void display_ACCEL_x();
void display_ACCEL_y();
void display_ACCEL_z();
uchar Single_ReadI2C(uchar REG_Address); //读取I2C数据 void Single_WriteI2C(uchar REG_Address,uchar REG_data); //向I2C写入数据
//************************************** //延时5微秒(STC90C52RC@12M)
//不同的工作环境,需要调整此函数
//当改用1T的MCU时,请调整此延时函数
//************************************** void Delay5us()
{
_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_(); }
void delay(uint z)
{
uint x,y;
for(x=110;x>0;x--)
for(y=z;y>0;y--);
}
uint bdata sta; //状态标志
sbit RX_DR =sta^6;
sbit TX_DS =sta^5;
sbit MAX_RT =sta^4;
void inerDelay_us(unsigned char n) {
for(;n>0;n--)
_nop_();
}
文件三< MPU6050.h >
//**************************************
//I2C起始信号
//**************************************
void I2C_Start()
{
SDA = 1; //拉高数据线
SCL = 1; //拉高时钟线
Delay5us(); //延时
SDA = 0; //产生下降沿
Delay5us(); //延时
SCL = 0; //拉低时钟线
}
//**************************************
//I2C停止信号
//**************************************
void I2C_Stop()
{
SDA = 0; //拉低数据线
SCL = 1; //拉高时钟线
Delay5us(); //延时
SDA = 1; //产生上升沿
Delay5us(); //延时
}
//**************************************
//I2C发送应答信号
//入口参数:ack (0:ACK 1:NAK) //**************************************
void I2C_SendACK(bit ack) {
SDA = ack; //写应答信号
SCL = 1; //拉高时钟线
Delay5us(); //延时
SCL = 0; //拉低时钟线
Delay5us(); //延时
}
//**************************************
//I2C接收应答信号
//**************************************
bit I2C_RecvACK()
{
SCL = 1; //拉高时钟线
Delay5us(); //延时
CY = SDA; //读应答信号
SCL = 0; //拉低时钟线
Delay5us(); //延时
return CY;
}
//**************************************
//向I2C总线发送一个字节数据
//**************************************
void I2C_SendByte(uchar dat) {
uchar i;
for (i=0; i<8; i++) //8位计数器
{
dat <<= 1; //移出数据的最高位
SDA = CY; //送数据口
SCL = 1; //拉高时钟线
Delay5us(); //延时
SCL = 0; //拉低时钟线
Delay5us(); //延时
}
I2C_RecvACK();
}
//**************************************
//从I2C总线接收一个字节数据
//************************************** uchar I2C_RecvByte()
{
uchar i;
uchar dat = 0;
SDA = 1; //使能内部上拉,准备读取数据,
for (i=0; i<8; i++) //8位计数器
{
dat <<= 1;
SCL = 1; //拉高时钟线
Delay5us(); //延时
dat |= SDA; //读数据
SCL = 0; //拉低时钟线
Delay5us(); //延时
}
return dat;
}
//************************************** //向I2C设备写入一个字节数据
//************************************** void Single_WriteI2C(uchar REG_Address,uchar REG_data)
{
I2C_Start(); //起始信号
I2C_SendByte(SlaveAddress); //发送设备地址+写信号
I2C_SendByte(REG_Address); //内部寄存器地址,
I2C_SendByte(REG_data); //内部寄存器数据,
I2C_Stop(); //发送停止信号
}
//************************************** //从I2C设备读取一个字节数据
//************************************** uchar Single_ReadI2C(uchar REG_Address) {
uchar REG_data;
I2C_Start(); //起始信号
I2C_SendByte(SlaveAddress); //发送设备地址+写信号
I2C_SendByte(REG_Address); //发送存储单元地址,从0开始
I2C_Start(); //起始信号
I2C_SendByte(SlaveAddress+1); //发送设备地址+读信号
REG_data=I2C_RecvByte(); //读出寄存器数据
I2C_SendACK(1); //接收应答信号
I2C_Stop(); //停止信号
return REG_data;
}
//**************************************
//初始化MPU6050
//**************************************
void InitMPU6050()
{
Single_WriteI2C(PWR_MGMT_1, 0x00); //解除休眠状态
Single_WriteI2C(SMPLRT_DIV, 0x07);
Single_WriteI2C(CONFIG, 0x06);
Single_WriteI2C(GYRO_CONFIG, 0x18);
Single_WriteI2C(ACCEL_CONFIG, 0x01);
}
//**************************************
//合成数据
//**************************************
int GetData(uchar REG_Address)
{
char H,L;
H=Single_ReadI2C(REG_Address);
L=Single_ReadI2C(REG_Address+1);
return (H<<8)+L; //合成数据
}
文件四< lcdinit.h >
/*******************************************************************/
/* */
/*写指令数据到LCD */
/*RS=L,RW=L,E=高脉冲,D0-D7=指令码。 */ /* */
/*******************************************************************/
void lcd_wcmd(uchar cmd)
{
while(lcd_busy());
LCD_RS = 0;LCD_RW = 0; LCD_EN = 0;
_nop_(); _nop_();
P0 = cmd;
delayNOP();LCD_EN = 1;
delayNOP();LCD_EN = 0;
}
/*******************************************************************/
/* */ /*写显示数据到LCD */ /*RS=H,RW=L,E=高脉冲,D0-D7=数据。 */ /* */ /*******************************************************************/ void lcd_wdat(uchar dat)
{
while(lcd_busy());
LCD_RS = 1;LCD_RW = 0;LCD_EN = 0;
P0 = dat;
delayNOP();LCD_EN = 1;
delayNOP();LCD_EN = 0;
}
/*********************************************************/ /* */ /* 设定显示位置 */
/* */ /*********************************************************/ void lcd_pos(uchar X,uchar Y)
{
uchar pos;
if (X==0) {X=0x80;}
else if (X==1) {X=0x90;}
else if (X==2) {X=0x88;}
else if (X==3) {X=0x98;}
pos = X+Y ;
lcd_wcmd(pos); //显示地址
}
/*******************************************************************/ /* */ /*检查LCD忙状态 */ /*lcd_busy为1时,忙,等待。lcd-busy为0时,闲,可写指令与数据。 */
/* */ /*******************************************************************/ bit lcd_busy()
{
bit result;
LCD_RS = 0; LCD_RW = 1;LCD_EN = 1;
delayNOP();
result = (bit)(P0&0x80);
LCD_EN = 0;
return(result);
}
/*******************************************************************/
/* */
/* LCD初始化设定 */ /* */
/*******************************************************************/
void lcdinit()
{
LCD_PSB = 1; //并口方式
lcd_wcmd(0x34); //扩充指令操作
delay(5); lcd_wcmd(0x30); //基本指令操作
delay(5); lcd_wcmd(0x0C); //显示开,关光标
delay(5); lcd_wcmd(0x01); //清除LCD的显示内容
delay(5);
}
void play(unsigned char X,unsigned char Y,char *DData) {
unsigned char length;
unsigned char i=0;
char *p;
p=DData;
length=strlen(p);
lcd_pos(X,Y);
for(i=0;i
void init_TX(void)
{
inerDelay_us(100);
CE=0; // chip enable
CSN=1; // Spi disable
SCK=0;
SPI_RW_Reg(WRITE_REG + CONFIG, 0x0e); // Set PWR_UP bit, enable CRC(2 bytes) &
SPI_RW_Reg(WRITE_REG + EN_AA, 0x01);
SPI_RW_Reg(WRITE_REG + EN_RXADDR, 0x01); // Enable Pipe0
SPI_RW_Reg(WRITE_REG + SETUP_AW, 0x02); // Setup address width=5 bytes
SPI_RW_Reg(WRITE_REG + SETUP_RETR, 0x1a); // 500us + 86us, 10 retrans...
SPI_RW_Reg(WRITE_REG + RF_CH, 0);
SPI_RW_Reg(WRITE_REG + RF_SETUP, 0x07); // TX_PWR:0dBm, Datarate:1Mbps,
SPI_RW_Reg(WRITE_REG + RX_PW_P0, RX_PLOAD_WIDTH);
SPI_Write_Buf(WRITE_REG + TX_ADDR, TX_ADDRESS, TX_ADR_WIDTH);
SPI_Write_Buf(WRITE_REG + RX_ADDR_P0, TX_ADDRESS, TX_ADR_WIDTH); //CE=1; //
}
uint SPI_RW(uint uchar)
{
uint bit_ctr;
for(bit_ctr=0;bit_ctr<8;bit_ctr++) // output 8-bit
{
MOSI = (uchar & 0x80); // output 'uchar', MSB to MOSI
uchar = (uchar << 1); // shift next bit into MSB..
SCK = 1; // Set SCK high..
uchar |= MISO; // capture current MISO bit
SCK = 0; // ..then set SCK low again
}
return(uchar); // return read uchar }
uint SPI_RW_Reg(uchar reg, uchar value)
{
uint status;
CSN = 0; // CSN low, init SPI transaction
status = SPI_RW(reg); // select register
SPI_RW(value); // ..and write value to it..
CSN = 1; // CSN high again
return(status); // return nRF24L01 status uchar }
uint SPI_Write_Buf(uchar reg, uchar *pBuf, uchar uchars)
{
uint status,uchar_ctr;
CSN = 0; //SPI使能
status = SPI_RW(reg);
for(uchar_ctr=0; uchar_ctr
本文档为【基于51的避障循迹重力感应遥控的智能小车设计(C语言)】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑,
图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。