这是一个也就是四天工作量搞出来的小玩意儿,还得感谢生产实习能给我们这次实现自己想法的机会,虽然实验条件简陋得离谱、资金也不敢投入太多,但是我们总算是克服了种种困难让它闪亮登场了,实现了既定的目标,享受了发现问题和解决问题的乐趣。顺利结题的主要原因是队友们都很给力啊,小帅帅,夏青、峰哥...咱们算是在奋战中给这个学期画上了圆满的句号。 目录 一、 前言----------------------------------------------------------- 二、 系统功能及器件选择-------------------------------------- 三、 图片资料----------------------------------------------------- 四、 I/O分配硬件及连接示意图------------------------------- 五、 相关模块的功能简介及其在系统中具体使用-------- 六、 功能实现示意图---------------------------------------------- 七、 调试中发现的一些问题及解决方法-------------------- 八、 讲座有感 九、 结束语
一、 前言 系统的灵感来源于我在网上看的一个叫“光影魔术手”的设计作品,它可以实现与手的同步前进和后退,看着相当炫,当时仔细想想其中的原理,觉得自己也可以设计作品实现同样的功能,恰好生产实习给了我这样一次机会。在想通原理之后,硬件的选用其实已经比较清晰了,控制核心采用单片机,传感器采用广泛用于避障和测距的超声波传感器,前进和后退用普通伺服电机和电机驱动模块实现。在网上选购单片机最小系统的时候,发现有一家商铺的最小系统上集成了红外模块,就想着不要浪费了,就萌生了做遥控车的想法,这样系统可以实现两个功能一个是类似于“光影魔术手”一样的物体追踪功能,一个是遥控运行功能。 组队之后,在与队员一起讨论,从原理上进行可行性分析,最后一致讨论通过,然后就分组从网上搜集相关的资料,最后确定并购买了相关的器件,我们从零开始熟悉开发环境、翻看器件的技术文档、搞懂模块原理及使用方法、模块的编程调试、系统功能编程调试、系统程序整合、装车、系统功能优化。这是一个发现问题和解决问题的过程,乐趣也正在于此,我们相信一切问题皆有解决的方法,我们队员四个克服种种困难最终实现了既定的系统的功能,为此次生产实习画上一个圆满的句号。 二、 系统功能及器件选择 1、系统功能简述 接通电源后,按下开机键,小车进入模式选择状态,按左三角键进入(超声波)物体追踪模式,小车可以与物体始终保持设定的距离,实现与手(或其它物体)同步运动,同时显示当下距离值。按右三角键进入红外遥控模式,可以遥控小车前进、后退、左转、右转、剎停。在两个模式下按关机键,可以终止当前模式,重新选择功能,终止当前模式后按关机键,彻底关机,必须按开机键系统才能重新工作。 2、器件选择 首先回答这个问题:为什么功能模块采用网购而不是自己焊接电路? 作品所用到的器件模块除了7805芯片稳压和一些基本的开关电路,主要的模块如单片机最小系统(含红外、数码管、LED电路)、超声波模块、电机驱动模块均是从网上购买,有人说买的就怎么怎么样,其实我觉得不然: ⑴因为我们现在还不是拥有设计电路能力的那个人,那如果是粘了别人设计好的电路图然后自己买元件焊接,那么硬件电路其实就是一个体力活。 ⑵硬件的焊接、调试其实也是一个比较有意思的过程,但是围着一个已经验证过功能电路团团转我觉得没有太大价值,而且本次设计时间只有一个周多些,而且实验条件太有限,没有固定的实验室,甚至连示波器、直流电源、万用表、焊枪、螺丝刀等基本的实验条件都不能保证。 ⑶我们要在短时间内实现更高的功能,就必须向硬件电路“借力”,把主要的经历放在软件调试上,硬件是“死”的,而可以把更多的想法诸如到软件中让单片机执行,以实现我们所要的功能,享受其中的乐趣。 ⑷从经济方面考虑,其实并没有想象中经济性那么差,量产的PCB电路有时是相当实惠的。如果单买单片机、面包板、LED、数码管、和一些诸如RS232 电平转换芯片、红外发射器、接收管等等。在按照电路图焊接的过程中相当费时费力费神,购置上述器件的花费也在30块钱以上,有些器件如电阻电容很便宜,不能单个购买,但是买多了又很浪费。而网购的最小系统板花费40元(虽然我们原来只想要最简单的有下载口的最小系统,发现了这个之后觉得划算就买了),上面已经将单片机(可插拔)、LED、数码管、红外模块集成,而且做好了USB下载口,并赠送了开发环境和相关例程、下载线。 电机驱动模块更给力。我们本打算用两片7960芯片自己焊接驱动模块,然后用2576给单片机和超声波模块做稳压电源,这样的花费是相当大的,一片7960就要15块钱,2576的价格也在5元左右,这样整个下来就得50块钱左右,我们在网上发现了这个用L298N制作的电机驱动模块,它不但可以同时驱动两个伺服电机,还拥有一个5v稳压输出,还有PWM和全整流两种工作模式,价格是35元,所以我们果断买下了。 超声波模块就没有疑问了,就算知道硬件电路图,买了器件其实在缺少相关设备的情况下调试起来也是非常困难的,我们经过货比三家,选了一个实用又比较实惠的模块。 这样硬件电路置备妥当了,最小系统板、超声波模块、电机驱动模块、7805稳压芯片、4节干电池盒、由于资金限制,我们向智能汽车实验室借用了一个废旧电池(7v左右,与干电池串联使用)、电子设计留下的废旧车底盘。 最小系统板可以兼容MCS-51系列单片机里的多种型号,我们没用自带的52RC而是采用了其中比较高端的STC12C5A60S2,(其实也就10块钱)它的指令执行周期更短,有AD、PCA模块(可以工作在PWM方式)、而且定时器、中断等与传统的8051基本兼容,非常容易掌握,也很适合小车的实时控制。 后来证明,这个借来的底盘很好用,本来我们都没打算在遥控的时候能实现转弯功能,因为用PWM控制的舵机太贵了,后来车上竟然发现了小的转向电机,我们眼前一亮,正好电机驱动可以驱动两路电机,就把ENB输入与5v短路,在用两个I/0直接控制这个简陋的“舵机”,阴差阳错的实现了遥控模式下的转弯功能。 但是由于电机驱动要求输入电压在7.2-12之间,我们的旧电池很明显力不从心,导致出现了很多意外问题,耽误了不少进度,后来用旧电池与干电池串联供电,单片机采用7805单独稳压供电,虽然问题有点缓解,还是显得不够给力,新电池又比较贵,我们及将就着用了,这也是系统设计的一大遗憾。 另一个遗憾就是没有实现物体的全方位追踪,只能实现前后方向的追踪,因为要实现转弯,超声波传感器就要求有多个,而且要求有能精确控制角度的PWM舵机,光这两样花费就在300以上,所以我们考虑了一下就放弃了。 三、 图片资料
整车图片
四、 I/O分配及硬件连接示意图 1、 I/O分配如下: IO | 功能 | 头文件定义 | IO | 功能 | 头文件定义 | P1^1 | 超声波发送使能端口 | TX | P0^0 | 电机正反转控制 | In1 | P1^2 | 超声波接收检测端口 | RX | P0^1 | 电机正反转控制 | In2 | P1^3 | PWM输出 | 无 | P0^2 | 转向控制 | In3 | P1^0 | 正转标志位 | zheng | P0^3 | 转向控制 | In4 | P1^7 | 反转标志位 | Fan | P1^4 | 剎停标志位 | Ting | P0 | 数码管数据端口 |
无 | P1^5 | 左右转向标志位、系统开关标志位 | 无 | P2^0-3 | 数码管驱动 | 无 | P1^6 | 同上 | 无 |
(注:最小系统板上P1口连接了LED)
2、系统硬件连接图如下:
五、 相关模块的功能简介及其在系统中的具体应用。 1、 PCA模块及其在系统中的应用 STC12C5A60S2内部集成了两路可编程计数器阵列(PCA),每个模块均有软件定时器、外部脉冲捕捉、高速脉冲输出和PWM模块,也就是说这款单片机有两路PWM输出,分别在P1.3和P1.4。(我们只用了一路,P1^3). PWM就是脉宽调制,就是可以通过设置参数,输出任意占空比的方波,这些高电平时间不一的方波经过电机驱动电路(双桥整流)可以输出大小的不同的电流,用于电机调速。 PCA模块的时钟源可以选择是来自于系统时钟的几分频还是来源于定时器,系统中采用了1/12的系统时钟源频率。因为PWM是8位的,所以PWM输出的波形频率是PCA时钟/256。 PWM的占空比调节是比较器的作用,当计数值CL小于设定值CCAPnL时(单片机的计数与定时器均是加计数)输出为低电平,当计数值超过时,输出高电平,所以我们可以通过设置CCAPnL来改变占空比。(因为每次溢出均需要重新赋值,所以同时给CCPAnH赋相同的值)。 与之相关的特殊功能寄存器寄存器有CCON、COMD、CCAPM0、CCAP0L、CCAP0H.。 系统中用pwminit();对该模块进行初始化,程序中改变占空比用pwm_set()给CCAP0H和CCAP0L赋不同值,用于驱动电机。
PWM模块的工作原理图如下。
2、 定时器模块及其在系统当中的应用 STC12C5A60S2拥有4个定时器模块,其中定时器0和1与传统的8051完全兼容(意思就是不光用法,连相关寄存器的地址也一样,这样开发环境中的52RC的头文件还是可以使用的),每个模块均可工作在定时器和计数器模式(通过P3^4,P3^5输入脉冲),定时器有四种工作方式:16位定时计数、13位定时计数、8位自动重装、两个8位计数。 与之相关的特殊功能寄存器有:控制寄存器TCON(其中比较重要的是TF和TR分别是中断标志和计数开关,很奇怪的外部中断的设置IE IT也在该寄存器中)。工作模式设置寄存器TMOD(可以设置定时器还是计数器,工作于哪个方式?)TL,TH中装着当前计数值,非常重要。AUXR可以设置定时器方式下计数频率是等于系统时钟还是等于1/12系统时钟。 系统中用到了定时器0和1,定时器0用于红外模块,通过外部中断测定信号周期(TL0/TH0)以确定其携带的信息是0还是1。定时器1用于超声波模块,测定从发出超声波到超声波返回之间经过的时间,乘以声速除以2来得到距离值。
3、 中断模块及其在系统中的应用 中断是CPU处理外界紧急事件的机制,STC12C5A60S2拥有10个中断源,它们有规定好的优先级,有中断嵌套机制,可以对中断进行允许和屏蔽。中断采用两级控制模式,有总开关EA和寄存器中IE单独的开关(默认是0,即禁止)。10个中断中常用的是:定时器0、1;两个外部中断(P3^2/P3^3)、AD中断、串口中断,他们都有规定好的中断向量地址,使用那个中断就将总开关打开,在IE中将专用开关打开,对应中断号interrupt x编写中断服务子程序(不用声明)。 系统中红外模块用到了外部中断INT0(下降沿触发),其原理是红外发射器将按键码按0和1调制成不同周期长度的红外波形,红外接收头将这些波形接收后调制成不同周期长度的方波,每个每个下降沿都将触发外部中断,中断服务子程序中取一个计数值以确定其周期长度,进而知道它是0是1。
4、 IO模块及其在系统中的应用。 STC12C5A60S2比传统的8051增添了P4和P5两个端口,系统中均未对IO口进行初始化,即工作在默认的双向模式。2v以上为高电平,0.8v一下为低电平,每个口的驱动能力都在20mA以上,但要求总体不超过120mA.因为P1口接了LED所以被当作标志位用的较多。P0口是数码管的数据端口,P20-3是数码管数据端口,详细内容参见上文中的I/O分配图。(数码管的使用是先用P0口发送8位译码数据,然后用P2口发送4位控制信号,点亮四个数码管中的一个,LED与P1口直接连接,低电平的时候点亮LED,而且具有自保持功能)。
5、 超声波模块、电机驱动模块、红外模块的使用 超声波模块有TX,RX,VCC,两个GND共计5个端口,使用5V电压供电,系统中使用最小系统板上的5v电压输出。工作时:当TX端口收到一个持续时间在10us以上的脉冲,超声波模块将发射一组特定超声波,并RX置1超声波遇到障碍物反射回来,当接收装置收到反射回来的信号,将RX置0。系统设计中我们将TX与RX分别与I0口连接,用I0口发送10us以上脉冲,同时将定时器打开,并开始检测RX,当RX为0时停止计数,取出计数值并计算距离。 电机驱动模块输入为7.2-12v电压,同时IN1和IN2可以控制out1和Out2所接电机的正反转,IN3和IN4可以控制out3和ou4所接电机的正反转。10为正转,01为反转,00停止,11剎停。其中ena和enb可以控制输出的电流大小,若用短路帽与+5v短接,则是全整流,若与PWM线连接则使用占空比整流。系统中,IN1和IN2控制电机,并用PWM(P1^3)控制电流以调速。IN3和IN4控制转向电机,使用ENB和5v短接。 红外发射器将按键码按0和1调制成不同周期长度的红外波形,红外接收头将这些波形接收后调制成不同周期长度的方波,每个每个下降沿都将触发外部中断,中断服务子程序中取一个计数值以确定其周期长度,进而知道它是0是1。发送的信息中包括前导码、识别码、用户码、用户反码(用于校验),其中信息接收完毕且正确后(IROK=1),32位信息均存储在IM数组中,我们需要的码值在IM【2】中,取出来判断要进行的操作即可。其中我们用到的键码有:
按键 | 功能 | 键码 | 按键 | 功能 | 键码 | 开机 | 系统开 | 0x09 | 右箭头 | 右转 | 0x1e | 声音开关 | 系统关 | 0x03 | Ok键 | 舵机摆正 | 0x05 | 上箭头 | 前进 | 0x0e | 左三角 | 红外模式 | 0x0c | 下箭头 | 后退 | 0x1a | 右三角 | 物体追踪 | 0x02 | 左箭头 | 左转 | 0x0a | Pause键 | 剎停 | 0x54
| 六、 系统功能实现示意图
七、 调试中发现的一些问题及解决的方法。
首先在调试的过程当中在一些小错误中学到了不少零碎的知识,如:复用引脚第二功能优先、LED自保持、IO口不如不初始化直接工作在双向模式、在各个模块连接的时候一定要共地,在开发环境下中断服务子程序的写法、单片机头文件的写法等。一些小问题的解决比如头文件缺少、复杂,PWM赋值函数适应PID,超声波由于IO端口问题问题不能数码管显示距离(P2口换到P1口)、舵机在超声波模式剧烈震动(在每次判断误差时让IN3/IN4归零)、在PID运算中加入微分之后,震荡更加厉害(数组求差值求反了),当然有时也犯诸如线忘了插、键值写错的很低级的错误,但是经过一步步排查总能找出来,遇到比较大的问题有: 1、 单片机在工作的过程当中总是死机?必须重启才能工作,而且死机愈加频繁… 在超声波模块调试过程中我们发现了这个问题,我们首先从程序中查找错误,用了之前已经调试通过的简单程序,工作一段时间后还是会死机,我们把注意力转移到硬件上,在反复排查后,我们把问题锁定在了单片机电压上,经过测试,我们发现电机工作一段时间后,由电机驱动输送给单片机的电压降到了3.5V以下,单片机复位,停止工作。我们的解决方法是用了三节干电池串联主电池。问题暂时得到解决。当我们加上红外遥控,两个电机一起工作的时候,同样的问题再次出现,我们决定串联四节干电池,并且给单片机用7805稳压(直接接电池电压),问题得到基本解决。因为7805要求电压在5v以上,所以在系统长时间工作的情况下单片机还是会复位。 2、 系统不能进行功能切换,而且在一个模式下工作极不稳定? 在开始进行两个模块的程序整合的时候,总是不能相互切换,而且在红外模式下工作很不稳定,按关机键一次就可以彻底关机,经过排查,我们发现红外取值有效的标志位IROK=1之后,判断并执行完一步之后,没有清零,导致程序中一直读取IM【2】中的数据(乱码),而且关机键之后在IROK=1的情况下,数组中的0x03可以直接让系统停止工作。在每次键值判断之后都给IROK清零之后,问题得到了圆满的解决。 八、 讲座有感 第一个讲座东方电子的刘总给我们介绍了物联网的应用和他的广阔前景,在IBM大力倡导建设“智慧的地球”的前提下,中国的物联网事业也快速起步,山东也规划了物联网发展的计划,但是我国目前主要的芯片还是不能自己生产,主要是做后期设计和系统集成,讲座上刘总主要介绍了RFID电子标签的应用,这也是东方电子的强项。RFID电子标签在仓储管理、物流追踪、零售业、交通管理、机场应用、智能图书馆、医院婴儿防盗、身份证、防伪、动物识别、敌我识别方面有着广泛的应用,通过东方电子在烟台港等几个地方的实际项目,我们深切的体会到物联网已经走到了我们生活中的各个角落。最后刘总定义了什么是物联网:人与人,物与物、物与人之间,永远在相互感知,信息实时互动,分享随时发生,它就是信息化的深度应用。 第二个讲座数学院的吴老师,给我的第一印象就是他相当的诚恳,特别是最后一个同学问问题,他不了解的内容就一句都不多说,话语间透着谦逊和语重心长。他所讲解的主要内容是控制科学领域的大家和他们的成长历程、所获的成就,这对于我们中立志在自动化领域做出杰出贡献,要成为大家的同学具有很好的指导意义,同时也很励志。维纳、钱学森、陈翰馥、郭雷、彭实戈、朱位秋等大家的成长历程告诉我们:控制科学是横断学科,不光要精通控制理论,而且要博学,特别是数学好有很大的优势,郭老师说的好,计算机往上走,跟数学是一家子。我们还晓得了,中国科学院院士要比中国工程院的院士值钱的多,真正搞工程厉害的不在中科院自动化研究所,在系统所,要成为IEEEE FELLOW很不容易。最后吴老师给我们讲了一下他的成长历程,我觉得很励志,三次高考失败、读专科、回本村教书、七年后考取研究生、博士,看起来比实际年龄要大很多很多。最后,一句很实在的话:人无远虑,必有近忧。很受用。 - //******************************系统功能说明************************************************
-
- /*接通电源后,按下开机键,单片机进入模式选择状态,按左三角键进入(超声波)物体追踪模式,小车可以与
- 物体始终保持设定的距离,按右三角键进入红外遥控模式,可以遥控小车前进、后退、左转、右转、剎停
- 在两个模式下按关机键,可以终止当前模式,重新选择功能,此时按关机键,直接关机,必须按开机键才能重新选择*/
-
- //1111**************************IO分配详细说明**********************************************
- //系统IO分配
- //p1_1、p1-2,超声波模块发送和接受信号线tx rx
- //p1_3,pwm输出线
- //p1_0、p1_7,电机正反转的标志位zheng fan
- //p0数码管数据口
- //p2_0/1/2/3数码管驱动
- //p0_0、p0_1电机驱动正反转控制
- //p0_1、p0_2转向电机(舵机)正反转控制位
- //p1_4剎停标志位
- //p1_5、p1_6左右方向标志位,红外功能开关标志位
- //2222****************************头文件区***************************************************
- #include <reg52.h> //系统头文件
- #include<AT89X51.H> //IO口、定时器、中断、串口等相关特殊寄存器头文件
- #include<IO.H> //自定义功能IO头文件
- #include <pwm.h> //自编stc12csa60s的pwm模块头文件
-
- //AT89X51.H中除了IO口 和中断的定义,其余的都与reg52.h的定义重合。
-
- //3333****************************公共变量区**************************************************
- //下面是关于红外遥控模块的公共变量区
- unsigned char f; //找到起始码开始取值的标志
- #define Imax 14000 //此处为晶振为11.0592时的取值,
- #define Imin 8000 // 用来识别起始码
- #define Inum1 1450 //周期取值范围,代表0 和1
- #define Inum2 700
- #define Inum3 3000 //不同的周期所对应的计数值,代表不同的信息
- unsigned char Im[4]={0x00,0x00,0x00,0x00}; //起始码、用户码、校验码的存储数组
- unsigned long m,Tc; //位计数、信号周期计数值
- unsigned char IrOK; //读码有效标志位
- //下面是关于超声波模块的公共数据
- #define SETVALUE 15 //设定值为15厘米
- #define P 15
- #define I 0.5
- #define D 10
- unsigned char code table[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90}; //数码管的0-9译码值
- long S=0; //获取距离值(当前值)
- int OFFSET=0; //偏差
- int pwm_kong=0;//PID运算后的占空比控制因子0-255
- int SUM_OFFSET=0; //上一次的偏差
- int err[10];
- int I_OFFSET=0;
- int qian_sum=0;
- int hou_sum=0;
- int D_OFFSET=0;
- unsigned int time=0; //从发送到接受所经历的计数值
- unsigned int ge=0; //距离值的个位(厘米计)一下以此类推
- unsigned int shi=0;
- unsigned int bai=0;
- unsigned int qian=0;
- int flag_chao=0;
- int flag_hong=0;//进入两个模块的标志位
- int flag=0; //红外模式下定义的系统总的开关
- //4444*****************************函数声明区(中断服务子程序不参与声明(外部1))**************************************************
- //下面是超声波物体追踪模块的相关函数声明
- void display(); //数码管显示函数
- void delayus(unsigned int us); //微秒级延时函数
- void delay(unsigned char i); //延时函数
- void StartModule(void) ; //通过p1-1发送一个10us以上的脉冲
- void Count() ;//将计数值转化成距离值
- void pwminit() ; //PWM模块初始化函数
- void PWM0_set (unsigned int a); //占空比调节函数
- void chaoshengbo();//物体追踪模式下的控制函数
-
- //下面是红外遥控模块的相关函数声明
-
- //void delay(unsigned i); //延时函数 (公用)
- //void pwminit(); //PWM模块初始化函数 (公用)
- void hongwai(); //红外遥控模式的控制函数
-
- //5555*******************************主函数区****************************************************
-
- main()
- {
- //两个模块均要用到定时器(超声波用来计算距离值,红外用来根据信号周期确定信息类型),
- //为了防止干扰,用两个定时器模块,前1后0,都用到了PWM模块
- //且要红外控制 以决定进入物体追踪还是红外遥控,所以,需要公共定义的变量如下:
- //***********************两个模块公共需要
- // pwminit(); //PWM初始化 75%占空比
- TMOD=0x11;
- TH0=0;TL0=0; //定时器0和1为方式1,16位计数 ,计数值清零
- TR0=1; //红外模块需要马上开启定时器工作
- TH1=0;TL1=0;//定时器1计数值清零等待超声波模块使用
- //*************************需要从红外控制进入两个模块,定义如下:
-
- EA=1; //开启所有中断
- IT1=1; //外部中断为下降沿触发
- EX1=1; //开启外部中断 红外控制依赖于外部中断
- m=0;
- f=0; //红外控制标志位清零
- in1=0;
- in2=0; //初始化使电机停转,防止开机开车
- in3=0;
- in4=0; //防止开机乱摆
- //修改
- flag_chao=0;
- flag_hong=0;
-
- //********************************主干程序如下:
-
- while(1)
- {
- if(IrOK==1) //红外取值有效,开始判断
- {
- if(Im[2]==0x09) //开机,p1_5、6全亮
- {p1_5=0;
- p1_6=0;
- flag=1;
-
- //修改
- IrOK=0; }
-
- if(Im[2]==0x03 ) //关机,p1_5、6全灭
- //两级退出模式,在模块运行中按关机键可进入主函数循环重新选择模块
- //在主函数循环中按关机键,直接退出系统,必须重新开机,才能操作。
- {p1_5=1;
- p1_6=1;
- flag=0;
- in1=0;
- in2=0;
- in3=0;
- in4=0;
- //修改
- IrOK=0; }
-
- if(Im[2]==0x0c && flag==1) //左三角号键,代表进入超声波物体追踪模块
- {flag_chao=1;flag_hong=0;
- //修改
- IrOK=0; }
- if(Im[2]==0x02 && flag==1) //右三角号键,代表进入红外遥控模块
- {flag_chao=0;flag_hong=1;
- //修改
- IrOK=0; }
- }
- //模块选择;
- if(flag_chao==1 && flag_hong==0)
- {chaoshengbo();
- //修改
- p1_5=1;fan=1; }
-
- if(flag_chao==0 && flag_hong==1)
- {hongwai();
- //修改
- p1_5=1;fan=1; }
- }
-
-
- }
-
- //6666*******************************所调用的功能函数区*********************************************
-
- //下面是关于超声波物体追踪模块的功能函数区
- void delay(unsigned char i)
- {
- unsigned char j,k;
- for(j=i;j>0;j--)
- for(k=125;k>0;k--);
- }
- //**************************************************************************************************
-
- void display() //通过p0口发送数据并且通过p2的0-3发送是使能信号
- { P0=table[qian];
- P2_0 = 0;
- delay(2);
- P2_0 = 1;
-
- P0=table[bai];
- P2_1 = 0;
- delay(5);
- P2_1 = 1;
-
- P0=table[shi];
- P2_2 = 0;
- delay(5);
- P2_2 = 1;
-
- P0=table[ge];
- P2_3 = 0;
- delay(5);
- P2_3= 1;
- }
- //*********************************************************************************************
- void delayus(unsigned int us)
- {
- unsigned char j;
- for(;us;us--)
- {
-
- j=10;
- while(--j);
- }
- }
- void Count(void)
- {
- time=TH1*256+TL1; //获取计数值
- TH1=0;
- TL1=0; //计数值清零
- S=time*1.87/100; //通过计数值和系统频率、声速来计算距离
- ge=S%1000%100%10; //依次获取距离值的各个位
- shi=S%1000%100/10;
- bai=S%1000/100;
- qian=S/1000;
- display();
-
- }
- //************************************************************************************
-
- void StartModule() //通过p1-1发送一个10us以上的脉冲
- {
- TX=1;
- delayus(10);
-
- TX=0;
- }
- //**************************************************************************************
- void pwminit() //PWM模块初始化函数
- {
- CCON=0;//关闭PCA计数器,清除模块0、1的中断标志位
- CL=0;
- CH=0;//计数值清零
- //CMOD=0x02;//PCA模块输入时钟为系统时钟的1/2,通过此寄存器改变方波频率
- CMOD=0; //12分之1的系统频率
- //CCAP0H=CCAP0L=0x80; //占空比为50%,256/2
- // CCAP0H=CCAP0L=0xC0;//占空比25%
- CCAP0H=CCAP0L=0x3f; //初始占空比0,输出0
- //CCAP0H=CCAP0L=0x00; //初始占空比100%,输出1
- CCAPM0=0x42;//允许9位比较器功能,PCA模块0为8位无中断PWM方式 在p1.3输出
- CR=1;//开启PCA模块计数器。
- // CCON=0x40;
- }
- //****************************************************************************//
- void PWM0_set (unsigned int a) //占空比调节函数
- {
- a=255-a; //这样的写法是为了适应PID算法
- CCAP0L= a; //设置值直接写入CCAP0L
- CCAP0H= a; //设置值直接写入CCAP0H
- }
- //*************************************************************************************
- void chaoshengbo()
- {
- int i;
- in1=0;
- in2=0; //误差数组计数变量
- in3=0;
- in4=0;
- p1_5=0;fan=1;
- pwminit();
- while(1)
- {
- if(IrOK==1) //判断当前有无退出模式的信号
- {
- if(Im[2]==0x03 ){
- flag_chao=0;
- flag_hong=0;
- //修改
- IrOK=0;
- in1=0;
- in2=0;
- return;}//若有关机键按下则退出
- //修改
- IrOK=0; }
-
- StartModule(); //发送一个10us的发送使能信号
- while(!RX); //当(p1-2)RX为零时等待
- TR1=1; //开启定时器1计数
- while(RX); //当RX为1计数并等待
- TR1=0; //关闭定时器1计数
- Count(); //计算并发送到数码管
-
- //*******************************************一下开始PID运算
-
- OFFSET=S-SETVALUE; //获取偏差
- if(OFFSET>0) //偏差大于零,大于设定值,电机正转 前进
- {
- in1=1;
- in2=0;
- in3=0;
- in4=0;//防止转向电机抖动
- zheng=0;
- fan=1;//电机驱动正转模式,正转LED亮 p1-0
- ting=1;
- for(i=0;i<9;i++)
- err[i+1]=err[i];
- err[0]=OFFSET; //误差数组更新
- for(i=0;i<=9;i++)
- SUM_OFFSET+=err[i]; //误差数组求和
- for(i=0;i<=4;i++)
- qian_sum+=err[i];
- for(i=5;i<=9;i++)
- hou_sum+=err[i]; //微分数组求和
- D_OFFSET=qian_sum-hou_sum;
- I_OFFSET=I*SUM_OFFSET; //求积分量
- if(I_OFFSET>255) I_OFFSET=255;
- pwm_kong=P*OFFSET+I_OFFSET+D*D_OFFSET;//PID运算
- if(pwm_kong>255)pwm_kong=255;//PWM为8位,总的计数值为0-255
- if(pwm_kong<0)pwm_kong=0;//PWM为8位,总的计数值为0-255
- PWM0_set(pwm_kong);
- }
- else if(OFFSET<0)//偏差小于零,小于设定值 电机反转 后退
- {
- OFFSET=-OFFSET;
-
- in1=0;
- in2=1;
- in3=0;
- in4=0;//防止抖动
- zheng=1;
- fan=0;//电机驱动反转模式,反转LED亮 p1-7
- ting=1;
- for(i=0;i<9;i++)
- err[i+1]=err[i];
- err[0]=OFFSET; //误差数组更新
-
- for(i=0;i<=9;i++)
- SUM_OFFSET+=err[i]; //误差数组求和
-
- for(i=0;i<=4;i++)
- qian_sum+=err[i];
- for(i=5;i<=9;i++)
- hou_sum+=err[i]; //微分数组求和
- D_OFFSET=qian_sum-hou_sum;
-
-
- I_OFFSET=I*SUM_OFFSET; //求积分量
- if(I_OFFSET>255) I_OFFSET=255;
-
- pwm_kong=P*OFFSET+I_OFFSET+D*D_OFFSET;//PID运算
- if(pwm_kong>255)pwm_kong=255;//PWM为8位,总的计数值为0-255
- PWM0_set(pwm_kong);
-
-
- }
- else if(OFFSET==0)
- {
- zheng=1;
- fan=1;
- ting=0;
- in1=1;
- in2=1;//当没有偏差时剎停
- in3=0;
- in4=0;//防止抖动
-
- }
-
- }
-
- }
-
- //***************************************************************************************
- //下面是关于红外遥控模块的功能函数集合
- void intersvr1(void) interrupt 2 using 1 //外部中断解码服务子程序
- {
- Tc=TH0*256+TL0; //提取中断时间间隔时长,以确定信息类型
- TH0=0;
- TL0=0; //定时中断重新置零
- if((Tc>Imin)&&(Tc<Imax))
- {
- m=0;
- f=1;
- return;
- } //找到启始码
- if(f==1)
- { //周期长度在这个范围内代表1
- if(Tc>Inum1&&Tc<Inum3)
- {
- Im[m/8]=Im[m/8]>>1|0x80; m++;
- }
- if(Tc>Inum2&&Tc<Inum1) //周期在此范围内代表0
- {
- Im[m/8]=Im[m/8]>>1; m++; //取码
- }
- if(m==32) //取够4个八位二进制数
- {
- m=0;
- f=0;
- if(Im[2]==~Im[3]) //进行校验
- {
- IrOK=1; //读码有效
- }
- else IrOK=0; //取码无效
- }
-
- }
- }
- //***********************************************************************************
- void hongwai() //红外遥控模式的控制函数
- {
- //修改
- in1=0;
- in2=0; //误差数组计数变量
- in3=0;
- in4=0;
- p1_5=1;fan=0;
- pwminit();
- while(1)
- {
- if(IrOK==1)
- {
-
- if(Im[2]==0x0a && flag==1) //左,亮灭
- { p1_5=0;
- p1_6=1;
- in3=1;
- in4=0;//电机正转,车向左
- //修改
- IrOK=0;
- }
- if(Im[2]==0x1e && flag==1) //右,灭亮
- { p1_5=1;
- p1_6=0;
- in3=0;
- in4=1;//电机反转,车向右
- //修改
- IrOK=0;
- }
- if(Im[2]==0x05 && flag==1) //ok键摆正
-
- {
- in3=0;
- in4=0;
- //修改
- IrOK=0;
- }
-
- if(Im[2]==0x0e && flag==1) //向前跑
- {
- zheng=0;
- fan=1;
- in1=1;
- in2=0;
- //修改
- IrOK=0;
- }
-
- if(Im[2]==0x1a&& flag==1) //向后跑
- {
- fan=0;
- zheng=1;
- in1=0;
- in2=1;
- //修改
- IrOK=0;
- }
-
- if(Im[2]==0x54 && flag==1 ) //剎停
- {p1_5=1;
- p1_6=1;
- in1=0;
- in2=0;
- in3=0;
- in4=0;
- //修改
- IrOK=0; }
- if(Im[2]==0x03 && flag==1) //退出红外模式,进入主函数循环
- {
- flag_chao=0;
- flag_hong=0;
- //修改
- IrOK=0;
- in1=0;
- in2=0;
- in3=0;
- in4=0;
- return;}
- //修改
- IrOK=0;
- }
- }
- }
复制代码
|