找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 4101|回复: 5
收起左侧

超声波测距仿真(温度补偿)按键代指超声波测距

[复制链接]
ID:345978 发表于 2018-6-6 10:59 | 显示全部楼层 |阅读模式
仿真原理图如下(proteus仿真工程文件可到本帖附件中下载)
0.png 0.png 0.png

单片机源程序如下:
  1. #include <reg52.h>     //包括一个52标准内核的头文件
  2. #include<intrins.h>  //包含_nop_()函数定义的头文件
  3. #define uchar unsigned char //定义一下方便使用
  4. #define uint  unsigned int
  5. #define ulong unsigned long

  6. #define RdCommand 0x01 //定义ISP的操作命令
  7. #define PrgCommand 0x02
  8. #define EraseCommand 0x03
  9. #define Error 1
  10. #define Ok 0
  11. #define WaitTime 0x01 //定义CPU的等待时间

  12. sfr ISP_DATA=0xe2;  //寄存器申明
  13. sfr ISP_ADDRH=0xe3;
  14. sfr ISP_ADDRL=0xe4;
  15. sfr ISP_CMD=0xe5;
  16. sfr ISP_TRIG=0xe6;
  17. sfr ISP_CONTR=0xe7;
  18. char a_a;

  19. sbit key1=P1^1;//参数加
  20. sbit key3=P1^2;//参数减
  21. sbit key2=P1^0;//参数设置按键
  22. sbit DQ=P3^3;
  23. sbit LED=P2^7;
  24. sbit alarm=P3^4;//蜂鸣器管脚定义

  25. sbit TX=P3^5; //超声波模块管脚定义(发射脉冲引脚定义)
  26. sbit RX=P3^6; //超声波模块管脚定义 (回波引脚定义)

  27. sbit RS=P2^0;    //液晶寄存器选择位,将RS位定义为P2.0引脚
  28. sbit RW=P2^1;    //液晶读写选择位,将RW位定义为P2.1引脚
  29. sbit E=P2^2;     //液晶使能信号位,将E位定义为P2.2引脚
  30. sbit BF=P0^7;    //液晶忙碌标志位,,将BF位定义为P0.7引脚

  31. unsigned char code digit[ ]={"0123456789"}; //定义字符数组显示数字
  32. uint time=0,timer=0,S=0;
  33. uchar menu;//参数调整液晶显示参数
  34. uchar ge,shi,bai,qian,flag;  //距离数据转换
  35. uchar num1,num2;//上下限参数定义
  36. uchar H_bai,H_shi,H_ge;//上限参数值分解显示定义
  37. uchar L_bai,L_shi,L_ge;//下限参数值分解显示定义
  38. uint temp1,temp2;//上下限参数转换,用于报警值比较判断
  39. uchar time0;   //设置全局变量,专门用于严格延时
  40. uchar TL;     //储存暂存器的温度低位
  41. uchar TH;    //储存暂存器的温度高位
  42. uchar TN;      //储存温度的整数部分
  43. uchar TD;       //储存温度的小数部分
  44. uchar WD_temp;

  45. /* ================ 打开 ISP,IAP 功能 ================= */
  46. void ISP_IAP_enable(void)
  47. {
  48. EA = 0;       /* 关中断   */
  49. ISP_CONTR = ISP_CONTR & 0x18;       /* 0001,1000 */
  50. ISP_CONTR = ISP_CONTR | WaitTime; /* 写入硬件延时 */
  51. ISP_CONTR = ISP_CONTR | 0x80;       /* ISPEN=1  */
  52. }
  53. /* =============== 关闭 ISP,IAP 功能 ================== */
  54. void ISP_IAP_disable(void)
  55. {
  56. ISP_CONTR = ISP_CONTR & 0x7f; /* ISPEN = 0 */
  57. ISP_TRIG = 0x00;
  58. EA   =   1;   /* 开中断 */
  59. }
  60. /* ================ 公用的触发代码 ==================== */
  61. void ISPgoon(void)
  62. {
  63. ISP_IAP_enable();   /* 打开 ISP,IAP 功能 */
  64. ISP_TRIG = 0x46;  /* 触发ISP_IAP命令字节1 */
  65. ISP_TRIG = 0xb9;  /* 触发ISP_IAP命令字节2 */
  66. _nop_();
  67. }
  68. /* ==================== 字节读 ======================== */
  69. unsigned char byte_read(unsigned int byte_addr)
  70. {
  71. EA = 0;
  72. ISP_ADDRH = (unsigned char)(byte_addr >> 8);/* 地址赋值 */
  73. ISP_ADDRL = (unsigned char)(byte_addr & 0x00ff);
  74. ISP_CMD   = ISP_CMD & 0xf8;   /* 清除低3位  */
  75. ISP_CMD   = ISP_CMD | RdCommand; /* 写入读命令 */
  76. ISPgoon();       /* 触发执行  */
  77. ISP_IAP_disable();    /* 关闭ISP,IAP功能 */
  78. EA  = 1;
  79. return (ISP_DATA);    /* 返回读到的数据 */
  80. }
  81. /* ================== 扇区擦除 ======================== */
  82. void SectorErase(unsigned int sector_addr)
  83. {
  84. unsigned int iSectorAddr;
  85. iSectorAddr = (sector_addr & 0xfe00); /* 取扇区地址 */
  86. ISP_ADDRH = (unsigned char)(iSectorAddr >> 8);
  87. ISP_ADDRL = 0x00;
  88. ISP_CMD = ISP_CMD & 0xf8;   /* 清空低3位  */
  89. ISP_CMD = ISP_CMD | EraseCommand; /* 擦除命令3  */
  90. ISPgoon();       /* 触发执行  */
  91. ISP_IAP_disable();    /* 关闭ISP,IAP功能 */
  92. }
  93. /* ==================== 字节写 ======================== */
  94. void byte_write(unsigned int byte_addr, unsigned char original_data)
  95. {
  96. EA  = 0;
  97. ISP_ADDRH = (unsigned char)(byte_addr >> 8);  /* 取地址  */
  98. ISP_ADDRL = (unsigned char)(byte_addr & 0x00ff);
  99. ISP_CMD  = ISP_CMD & 0xf8;    /* 清低3位 */
  100. ISP_CMD  = ISP_CMD | PrgCommand;  /* 写命令2 */
  101. ISP_DATA = original_data;   /* 写入数据准备 */
  102. ISPgoon();       /* 触发执行  */
  103. ISP_IAP_disable();     /* 关闭IAP功能 */
  104. EA =1;
  105. }
  106. void write_eeprom()                           //内部eeprom写函数
  107. {
  108. SectorErase(0x2000);                   //清空
  109. SectorErase(0x2c00);
  110. SectorErase(0x2e00);
  111. byte_write(0x2c00,num1);       
  112. byte_write(0x2c01,num2);                          
  113. byte_write(0x2060,0x01);
  114. }
  115. /******************把数据从单片机内部eeprom中读出来*****************/
  116. void read_eeprom()                                                                                                                                  
  117. {
  118. num1=byte_read(0x2c00);
  119. num2=byte_read(0x2c01);
  120. a_a=byte_read(0x2060);         
  121. }
  122. /**************开机自检eeprom初始化*****************/
  123. void init_eeprom()
  124. {
  125. a_a=byte_read(0x2060);
  126. if(a_a!=1)                //新的单片机初始单片机内问eeprom
  127. {
  128.   a_a=1;
  129.   write_eeprom();           //保存数据
  130. }       
  131. }

  132. //*****************I2C函数设置**********************//
  133. /*****************************************************
  134. 函数功能:将DS18B20传感器初始化,读取应答信号
  135. 出口参数:flag
  136. ***************************************************/
  137. bit Init_DS18B20(void)       
  138. {
  139. bit flag;         //储存DS18B20是否存在的标志,flag=0,表示存在;flag=1,表示不存在
  140. DQ = 1;           //先将数据线拉高
  141. for(time0=0;time0<2;time0++);//略微延时约6微秒
  142. DQ = 0;           //再将数据线从高拉低,要求保持480~960us
  143. for(time0=0;time0<200;time0++)  //略微延时约600微秒
  144.      ;         //以向DS18B20发出一持续480~960us的低电平复位脉冲
  145. DQ = 1;           //释放数据线(将数据线拉高)
  146.   for(time0=0;time0<10;time0++)
  147.      ;  //延时约30us(释放总线后需等待15~60us让DS18B20输出存在脉冲)
  148. flag=DQ;          //让单片机检测是否输出了存在脉冲(DQ=0表示存在)      
  149. for(time0=0;time0<200;time0++)  //延时足够长时间,等待存在脉冲输出完毕
  150.       ;
  151. return (flag);    //返回检测成功标志
  152. }
  153. /*****************************************************
  154. 函数功能:从DS18B20读取一个字节数据
  155. 出口参数:dat
  156. ***************************************************/
  157. unsigned char ReadOneChar(void)
  158. {
  159. unsigned char i=0;       
  160. unsigned char dat;  //储存读出的一个字节数据
  161. for (i=0;i<8;i++)
  162. {                  
  163.   DQ =1;       // 先将数据线拉高
  164.   _nop_();            //等待一个机器周期         
  165.   DQ = 0;      //单片机从DS18B20读书据时,将数据线从高拉低即启动读时序
  166.   dat>>=1;
  167.   _nop_();     //等待一个机器周期                  
  168.   DQ = 1;     //将数据线"人为"拉高,为单片机检测DS18B20的输出电平作准备
  169.   for(time0=0;time0<2;time0++);      //延时约6us,使主机在15us内采样
  170.   if(DQ==1)
  171.   dat|=0x80;  //如果读到的数据是1,则将1存入dat
  172.   else
  173.   dat|=0x00;//如果读到的数据是0,则将0存入dat
  174.   //将单片机检测到的电平信号DQ存入r[i]       
  175.   for(time0=0;time0<8;time0++);              //延时3us,两个读时序之间必须有大于1us的恢复期       
  176. }                            
  177. return(dat);    //返回读出的十进制数据
  178. }
  179. /*****************************************************
  180. 函数功能:向DS18B20写入一个字节数据
  181. 入口参数:dat
  182. ***************************************************/  
  183. WriteOneChar(unsigned char dat)
  184. {
  185. unsigned char i=0;
  186. for (i=0; i<8; i++)
  187. {
  188.   DQ =1;         // 先将数据线拉高
  189.   _nop_();             //等待一个机器周期         
  190.   DQ=0;          //将数据线从高拉低时即启动写时序      
  191.   DQ=dat&0x01;   //利用与运算取出要写的某位二进制数据,
  192.                        //并将其送到数据线上等待DS18B20采样       
  193.   for(time0=0;time0<10;time0++);//延时约30us,DS18B20在拉低后的约15~60us期间从数据线上采样
  194.   DQ=1;          //释放数据线                    
  195.   for(time0=0;time0<1;time0++);//延时3us,两个写时序间至少需要1us的恢复期
  196.   dat>>=1;       //将dat中的各二进制位数据右移1位
  197. }
  198. for(time0=0;time0<4;time0++); //稍作延时,给硬件一点反应时间
  199. }


  200. //液晶函数设置************************************/
  201. /*****************************************************
  202. 函数功能:延时1ms
  203. (3j+2)*i=(3×33+2)×10=1010(微秒),可以认为是1毫秒
  204. ***************************************************/
  205. void delay1ms()
  206. {
  207.    unsigned char i,j;       
  208.          for(i=0;i<10;i++)
  209.           for(j=0;j<33;j++)
  210.            ;                 
  211. }
  212. /*****************************************************
  213. 函数功能:延时若干毫秒
  214. 入口参数:n
  215. ***************************************************/
  216. void delay(unsigned char n)
  217. {
  218.    unsigned char i;
  219.         for(i=0;i<n;i++)
  220.            delay1ms();
  221. }
  222. /*****************************************************
  223. 函数功能:判断液晶模块的忙碌状态
  224. 返回值:result。result=1,忙碌;result=0,不忙
  225. ***************************************************/
  226. unsigned char BusyTest(void)
  227.   {
  228.     bit result;
  229.         RS=0;       //根据规定,RS为低电平,RW为高电平时,可以读状态
  230.     RW=1;
  231.     E=1;        //E=1,才允许读写
  232.     _nop_();   //空操作
  233.     _nop_();
  234.     _nop_();
  235.     _nop_();   //空操作四个机器周期,给硬件反应时间       
  236.     result=BF;  //将忙碌标志电平赋给result
  237.    E=0;         //将E恢复低电平
  238.     return result;
  239.   }
  240. /*****************************************************
  241. 函数功能:将模式设置指令或显示地址写入液晶模块
  242. 入口参数:dictate
  243. ***************************************************/
  244. void WriteInstruction (unsigned char dictate)
  245. {   
  246.     while(BusyTest()==1); //如果忙就等待
  247.          RS=0;                  //根据规定,RS和R/W同时为低电平时,可以写入指令
  248.          RW=0;   
  249.          E=0;                   //E置低电平(根据表8-6,写指令时,E为高脉冲,
  250.                              // 就是让E从0到1发生正跳变,所以应先置"0"
  251.          _nop_();
  252.          _nop_();             //空操作两个机器周期,给硬件反应时间
  253.          P0=dictate;            //将数据送入P0口,即写入指令或地址
  254.          _nop_();
  255.          _nop_();
  256.          _nop_();
  257.          _nop_();               //空操作四个机器周期,给硬件反应时间
  258.          E=1;                   //E置高电平
  259.          _nop_();
  260.          _nop_();
  261.          _nop_();
  262.          _nop_();               //空操作四个机器周期,给硬件反应时间
  263.           E=0;                  //当E由高电平跳变成低电平时,液晶模块开始执行命令
  264. }
  265. /*****************************************************
  266. 函数功能:指定字符显示的实际地址
  267. 入口参数:x
  268. ***************************************************/
  269. void WriteAddress(unsigned char x)
  270. {
  271.      WriteInstruction(x|0x80); //显示位置的确定方法规定为"80H+地址码x"
  272. }
  273. /*****************************************************
  274. 函数功能:将数据(字符的标准ASCII码)写入液晶模块
  275. 入口参数:y(为字符常量)
  276. ***************************************************/
  277. void WriteData(unsigned char y)
  278. {
  279.     while(BusyTest()==1);  
  280.           RS=1;           //RS为高电平,RW为低电平时,可以写入数据
  281.           RW=0;
  282.           E=0;            //E置低电平(根据表8-6,写指令时,E为高脉冲,
  283.                        // 就是让E从0到1发生正跳变,所以应先置"0"
  284.           P0=y;           //将数据送入P0口,即将数据写入液晶模块
  285.           _nop_();
  286.           _nop_();
  287.           _nop_();
  288.      _nop_();       //空操作四个机器周期,给硬件反应时间
  289.           E=1;          //E置高电平
  290.           _nop_();
  291.           _nop_();
  292.           _nop_();
  293.          _nop_();        //空操作四个机器周期,给硬件反应时间
  294.          E=0;            //当E由高电平跳变成低电平时,液晶模块开始执行命令
  295. }
  296. /*****************************************************
  297. 函数功能:对LCD的显示模式进行初始化设置
  298. ***************************************************/
  299. void LcdInitiate(void)
  300. {
  301. delay(15);             //延时15ms,首次写指令时应给LCD一段较长的反应时间
  302. WriteInstruction(0x38);  //显示模式设置:16×2显示,5×7点阵,8位数据接口
  303. delay(5);               //延时5ms ,给硬件一点反应时间
  304. WriteInstruction(0x38);
  305. delay(5);
  306. WriteInstruction(0x38); //连续三次,确保初始化成功
  307. delay(5);
  308. WriteInstruction(0x0c);  //显示模式设置:显示开,无光标,光标不闪烁
  309. delay(5);
  310. WriteInstruction(0x06);  //显示模式设置:光标右移,字符不移
  311. delay(5);
  312. WriteInstruction(0x01);  //清屏幕指令,将以前的显示内容清除
  313. delay(5);
  314. WriteAddress(0);//从第一行开始显示
  315. WriteData('J');  //将万位数字的字符常量写入LCD
  316. WriteData('L');  //将万位数字的字符常量写入LCD
  317. WriteData(':');  //将万位数字的字符常量写入LCD

  318. WriteAddress(7);//从第一行开始显示
  319. WriteData('C');  //将万位数字的字符常量写入LCD
  320. WriteData('M');  //将万位数字的字符常量写入LCD

  321. WriteAddress(0x40);
  322. WriteData('H');
  323. WriteData(':');
  324. WriteAddress(0x40+5);
  325. WriteData('C');
  326. WriteData('M');

  327. WriteAddress(0x40+9);
  328. WriteData('L');
  329. WriteData(':');
  330. WriteAddress(0x40+14);
  331. WriteData('C');
  332. WriteData('M');

  333. WriteAddress(15);
  334. WriteData(0xdf);
  335. }
  336. //假如温度25摄氏度时,声速位346m/s
  337. //时钟周期为T0=1/11.0592M,则机器周期为1/11.0592*12us=1.08us,定时器计时为time0(定时的数量就是时钟周期数)
  338. //实际时间t=time0*1.08
  339. //距离S=vt/2=v*time0*1.08/2/1000000=算出来的值是米
  340. void Conut(void)
  341. {
  342. float vv;
  343. time=TH0*256+TL0;                //TH0左移8位后与TL0相加
  344. TH0=0;
  345. TL0=0;
  346. vv=(331.5+0.607*TN);//声速VV和声速TN的关系
  347. S=((time*vv*1/200)/10); //注意除以10相当于算出来的值单位毫米,*1.08是因为晶振11.0592,如果是12MHZ就是*1       
  348. /*time=TH0*256+TL0;                //TH0左移8位后与TL0相加
  349. TH0=0;
  350. TL0=0;
  351. S=((time*1.87)/10); //注意除以10相当于算出来的值单位毫米 */
  352. if((S>=4000)||(flag==1)) //超出测量范围(4米)
  353. {         
  354.    flag=0;
  355.    qian=0;
  356.    bai=0;
  357.    shi=0;
  358.    ge=0;
  359.    delay(10);
  360. }
  361. else
  362. {
  363.   qian=S/1000;
  364.   bai=S%1000/100;
  365.   shi=S%1000%100/10;
  366.   ge=S%1000%10 %10;
  367. }
  368. }

  369. /*****************************************************
  370. 函数功能:显示温度的整数部分
  371. 入口参数:x
  372. ***************************************************/
  373. void display_temp1(unsigned char x)
  374. {
  375. unsigned char j,k,l;     //j,k,l分别储存温度的百位、十位和个位
  376. j=x/100;              //取百位
  377. k=(x%100)/10;    //取十位
  378. l=x%10;             //取个位  
  379. WriteAddress(11);    //写显示地址,将在第2行第7列开始显示
  380. //WriteData(digit[j]);    //将百位数字的字符常量写入LCD
  381. WriteData(digit[k]);    //将十位数字的字符常量写入LCD
  382. WriteData(digit[l]);    //将个位数字的字符常量写入LCD
  383. delay(50);         //延时1ms给硬件一点反应时间     
  384. }
  385. /*****************************************************
  386. 函数功能:显示温度的小数数部分
  387. 入口参数:x
  388. ***************************************************/
  389. void display_temp2(unsigned char x)
  390. {
  391. WriteAddress(14);      //写显示地址,将在第2行第11列开始显示
  392. WriteData(digit[x]);     //将小数部分的第一位数字字符常量写入LCD
  393. delay(50);          //延时1ms给硬件一点反应时间
  394. }
  395. /*****************************************************
  396. 函数功能:做好读温度的准备
  397. ***************************************************/
  398. void ReadyReadTemp(void)
  399. {
  400. Init_DS18B20();     //将DS18B20初始化
  401. WriteOneChar(0xCC); // 跳过读序号列号的操作
  402. WriteOneChar(0x44); // 启动温度转换          
  403. for(time=0;time<150;time++)
  404. ;         //温度转换需要一点时间
  405. Init_DS18B20();     //将DS18B20初始化
  406. WriteOneChar(0xCC); //跳过读序号列号的操作
  407. WriteOneChar(0xBE); //读取温度寄存器,前两个分别是温度的低位和高位       
  408. }

  409. void alarming()//蜂鸣器报警子函数
  410. {
  411. alarm=0;
  412. LED=0;
  413. delay(50);
  414. alarm=1;
  415. LED=1;
  416. delay(100);
  417. }
  418. /***************按键设置****************************/
  419. void key_shezhi()
  420. {
  421. if(key2==0)//如果上下限参数调整按键按下
  422. {
  423.    delay(30);
  424.    if(key2==0)
  425.    {
  426.     menu++;//设置页面调整
  427.     if(menu>2)//如果设置完下限值后,液晶回归正常显示
  428.     {
  429.      menu=0;
  430.      LcdInitiate();//更新初始化显示
  431.          delay(20);//适当延时
  432.     }
  433.    }
  434.    while(!key2);
  435. }
  436. if(menu==0)
  437. {
  438.   ET0=1;  //打开定时中断
  439.   ET1=1;
  440.   WriteAddress(3);
  441.   WriteData(digit[qian]);//更新显示实时测距的距离  
  442.   delay(20);
  443.   WriteData(digit[bai]);//更新显示实时测距的距离  
  444.   delay(20);
  445.   WriteData(digit[shi]);//更新显示实时测距的距离  
  446.   delay(20);
  447.   //WriteData('.');//增加一个小数点,用于毫米和厘米的切换指示  
  448.   //delay(20);
  449.   //WriteData(digit[ge]); //更新显示实时测距的距离
  450.   //delay(20);
  451.   ET0=0;  //关闭定时中断,执行温度读取
  452.   ET1=0;
  453.   ReadyReadTemp();     //读温度准备
  454.   TL=ReadOneChar();    //先读的是温度值低位
  455.   TH=ReadOneChar();    //接着读的是温度值高位
  456.   TN=TH*16+TL/16;      //实际温度值=(TH*256+TL)/16,即:TH*16+TL/16
  457.                                           //这样得出的是温度的整数部分,小数部分被丢弃了
  458.   TD=(TL%16)*10/16;    //计算温度的小数部分,将余数乘以10再除以16取整,
  459.                                           //这样得到的是温度小数部分的第一位数字(保留1位小数)

  460.   if((TN==85)&&(WD_temp==0))//18B20芯片开电首次读取的温度是85,本程序避开85的问题
  461.   {
  462.     TN=0;
  463.   }
  464.   else if(TN!=85)
  465.   {
  466.    WD_temp=1;//开电首次85避开
  467.   }
  468.   ET0=1;  //温度处理完后打开定时中断
  469.   ET1=1;
  470.   display_temp1(TN);    //显示温度的整数部分
  471.   display_temp2(TD);    //显示温度的小数部分
  472.   WriteAddress(13);
  473.   WriteData('.');

  474.   H_bai=num1/100;//更新显示调整后的上限值
  475.   H_shi=num1%100/10;//更新显示调整后的上限值
  476.   H_ge=num1%100%10;        //更新显示调整后的上限值
  477.   WriteAddress(0x40+2);//更新显示调整后的上限值
  478.   WriteData(digit[H_bai]);//更新显示调整后的上限值
  479.   WriteData(digit[H_shi]);//更新显示调整后的上限值
  480.   WriteData(digit[H_ge]);//更新显示调整后的上限值

  481.   L_bai=num2/100; //更新显示调整后的下限值
  482.   L_shi=num2%100/10;//更新显示调整后的下限值
  483.   L_ge=num2%100%10;//更新显示调整后的下限值
  484.   WriteAddress(0x40+11);//更新显示调整后的下限值
  485.   WriteData(digit[L_bai]); //更新显示调整后的下限值
  486.   WriteData(digit[L_shi]);//更新显示调整后的下限值
  487.   WriteData(digit[L_ge]); //更新显示调整后的下限值

  488.   if(S>=temp1)//报警设置
  489.   {
  490.    alarming();//如果距离大于上限值报警
  491.   }
  492.   else if(S<temp2)
  493.   {
  494.    alarming();//如果距离小于下限值报警
  495.   }
  496.   else
  497.   {
  498.    alarm=1;//关闭报警
  499.   }
  500. }
  501. if(menu==1)//切换液晶上限设置
  502. {
  503.   ET0=0;//把定时中断暂时关闭
  504.   ET1=0;
  505.   if(key1==0)//加按键
  506.   {
  507.     delay(30);
  508.         if(key1==0)
  509.         {
  510.      num1++;
  511.      if(num1==400)//上限4米
  512.      {
  513.           num1=0;
  514.      }
  515.      if(num1<num2)//调整上限值得时候,上限值不能小于下限值
  516.      {
  517.       num1=num2;//否则等于下限值
  518.      }
  519.         }
  520.         while(!key1);//等待按键释放
  521.   }
  522.   if(key3==0)//减按键
  523.   {
  524.     delay(30);
  525.         if(key3==0)
  526.         {
  527.      if((num1>0)&&(num1>num2))//如果同时满足num1大于零和上限值大于下限值,可以减减
  528.      {
  529.       num1--;
  530.      }         
  531.         }
  532.         while(!key3);//等待按键释放
  533.   }
  534.   write_eeprom();
  535.   delay(2);
  536.   delay(2);
  537.   H_bai=num1/100;//上限值转换用于液晶显示
  538.   H_shi=num1%100/10;//上限值转换用于液晶显示
  539.   H_ge=num1%100%10;//上限值转换用于液晶显示
  540.   
  541.   delay(20);//延时消除光标重影
  542.   WriteAddress(0x40+2);//定位位置用于显示更新上限值
  543.   WriteData(digit[H_bai]);//上限值更新显示
  544.   WriteData(digit[H_shi]);//上限值更新显示
  545.   WriteData(digit[H_ge]);//上限值更新显示

  546.   WriteAddress(0x40+4);//移到上限值得个位数,用于光标显示
  547.   WriteInstruction(0x0e);//光标显示,不闪烁
  548. }
  549. if(menu==2)//下限值调整设置
  550. {
  551.   ET0=0;
  552.   ET1=0;
  553.   if(key1==0)
  554.   {       
  555.     delay(30);
  556.         if(key1==0)
  557.         {
  558.      if(num2<num1)
  559.      {
  560.           num2++;
  561.      }
  562.         }
  563.         while(!key1);
  564.   }
  565.   if(key3==0)
  566.   {       
  567.     delay(30);
  568.         if(key3==0)
  569.         {
  570.      if((num1>=num2)&&(num2>0))
  571.      {
  572.           num2--;
  573.      }
  574.         }
  575.         while(!key3);
  576.        
  577.   }
  578.   write_eeprom();
  579.   delay(2);
  580.   L_bai=num2/100;
  581.   L_shi=num2%100/10;
  582.   L_ge=num2%100%10;
  583.   delay(20);//延时消除光标重影
  584.   WriteAddress(0x40+11);
  585.   WriteData(digit[L_bai]);
  586.   WriteData(digit[L_shi]);
  587.   WriteData(digit[L_ge]);

  588.   WriteAddress(0x40+13);//移到个位,光标闪烁
  589.   WriteInstruction(0x0e);
  590. }  
  591. }
  592. void main(void)   // 主程序
  593. {
  594. LED=1;
  595. key1=1;//按键初始化
  596. key2=1;//按键初始化
  597. key3=1;//按键初始化
  598. TMOD=0x11;                                                //设置定时器0和1工作方式为1(0001 0001)
  599. TH0=0;                                                     //装初值
  600. TL0=0;                                                        //装初值
  601. TH1=(65536-50000)/256;
  602. TL1=(65536-50000)%256;
  603. EA=1;                                                        //开总中断
  604. ET0=1;                                                          //开定时器0中断
  605. ET1=1;                                                          //开定时器1中断
  606. TR1=1;                                                         //启动定时器1
  607. TX=0;
  608. RX=0;
  609. LcdInitiate();         //调用LCD初始化函数  
  610. delay(10);             //延时10ms,给硬件一点反应时间
  611. init_eeprom();                                  //初始化eeprom
  612. delay(10);
  613. read_eeprom();                                  //读取eeprom数据
  614. delay(10);
  615. while(1)         //程序循环
  616. {
  617.   key_shezhi();        //循环调用按键设置函数
  618.   temp1=num1*10;//由于num1单位是厘米,S单位是毫米,要*10转换成统一单位进行比较,用于报警设置
  619.   temp2=num2*10;
  620. }
  621. }
  622. void zd0() interrupt 1                         //T0中断用来计数器溢出,超过测距范围
  623. {
  624. flag=1;                                                //中断溢出标志
  625. }
  626. void zd3() interrupt 3                                          //T1中断
  627. {
  628. TH1=(65536-50000)/256;        //重新装初值高8位  约0.05s
  629. TL1=(65536-50000)%256;//重新装初值低8位
  630. timer++;//
  631. if(timer>=5)//中断5次约0.25s
  632. {
  633.   TR1=0;                         //关闭定时器1
  634.   TH1=(65536-50000)/256;
  635.   TL1=(65536-50000)%256;
  636.   timer=0;
  637.   TX=1;//先吧TX拉高//给个高位脉冲
  638.   _nop_();//延时
  639.   _nop_();//延时
  640.   _nop_();//延时
  641.   _nop_();//延时
  642.   _nop_();//延时
  643.   _nop_();//延时
  644.   _nop_();//延时
  645.   _nop_();//延时
  646.   _nop_();//延时
  647.   _nop_();//延时
  648.   TX=0;        //拉低
  649.   while(!RX);                 //当RX为零时等待
  650.   TR0=1;                          //启动定时器0,开启计数
  651.   while(RX);                         //当RX为1计数并等待
  652.   TR0=0;                         //关闭定时器0  关闭计数//计时完毕
  653.   Conut();                        //调用距离计算函数,计算距离
  654.   TR1=1;                           //启动定时器1
  655. }
  656. }


复制代码

所有资料51hei提供下载:
211 超声波测距仿真(温度补偿).zip (420.11 KB, 下载次数: 74)
回复

使用道具 举报

ID:364667 发表于 2018-7-18 18:23 | 显示全部楼层
大佬你的Proteus版本多少 我下了你的仿真文件结果没有Proteus打开这一方式 我的是8.6版本的
回复

使用道具 举报

ID:89286 发表于 2018-7-25 07:49 | 显示全部楼层
thanks for sharing
回复

使用道具 举报

ID:545457 发表于 2020-5-25 16:03 | 显示全部楼层
dragonraja 发表于 2018-7-18 18:23
大佬你的Proteus版本多少 我下了你的仿真文件结果没有Proteus打开这一方式 我的是8.6版本的

楼主分享真心不错,但同样问题打不开Proteus文件
回复

使用道具 举报

ID:545457 发表于 2020-5-25 16:23 | 显示全部楼层
楼主资源非常好用,Proteus  7.8版本可以打开  ,现象明显
回复

使用道具 举报

ID:545457 发表于 2020-5-25 16:25 | 显示全部楼层
补充:楼主资料包里还有文档资料,可供文档等参考
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表