找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 9093|回复: 18
收起左侧

基于51单片机的超声波测距设计(带温度补偿)

  [复制链接]
ID:189697 发表于 2017-7-6 09:41 | 显示全部楼层 |阅读模式
利用STC89C52单片机、超声波模块(HC-RS04)、DS18B20数字温度传感器和LCD1602液晶显示模块制作一个超声波测距仪,液晶屏第一行显示温度和超声波速度“T:30°C  V:349m/s”,第二行显示测量距离“S=X.XXXm”。
所设计的测距仪测量单位为米,精确到小数点后面3位(毫米),测量范围:0.05m~5m。
(一)基本部分
1、液晶显示功能
(1)开机时,液晶屏第一行显示温度和超声波速度,例如“T:30°C  V:349m/s”,第二行显示测量距离“S=0.000m”
(2)操作对应的功能按键时,液晶屏第一行显示温度和超声波速度,例如“T:30°C  V:349m/s”,第二行显示测量距离“S=X.XXXm”。
(3)本超声波测距仪使用4个独立按键。第1个按键为复位清零功能;第2个按键为单次测量功能;第3个按键为瞬时连续测量功能;第4个按键为平均连续测量功能。
(4)第1个按键为复位清零功能。无论什么时候按下该键,液晶屏第二行均显示测量距离“S=0.000m”
(5)第2个按键为单次测量功能。每按一次该键,就发射一次超声波,液晶屏第二行对应显示一次测量距离“S=X.XXXm”
(6)第3个按键为瞬时连续测量功能。按下该键,连续发射超声波,液晶屏第二行对应显示瞬时测量距离“S=X.XXXm”,再按一次该键,数字保持,以便读数
(7)第4个按键为平均连续测量功能。按下该键,连续发射超声波,液晶屏第二行对应显示平均测量距离“S=X.XXXm”,再按一次该键,数字保持,以便读数

(8)第5个按键为自动/手动温度设置按键,转换到手动设置温度值,这时可以利用加键和减键使温度在0度至50度范围内调节,再按一次该键,重新回到自动温控状态,如此循环。
(9)第6个按键为温度加按键,当处于手动温度控制状态时,按下本按键可以加温度,最高可以加到50摄氏度。
(10)第7个按键为温度减按键,当处于手动温度控制状态时,按下本按键可以减温度,最低可以减0摄氏度。

程序代码如下:
  1. /**************************************/
  2. /*   超声波测距(自动/手动温度校正)  */
  3. /*   (超声波模块+18B20+1602液晶)    */
  4. /**************************************/
  5. #include <reg52.h>
  6. #include <intrins.h>
  7. #define uchar unsigned char
  8. #define uint unsigned int
  9. #define LcdData P0                   //1602数据端口

  10. sbit LCD_RS=P2^6;      //1602 RS端口
  11. sbit LCD_RW=P2^5;      //1602 RW端口
  12. sbit LCD_EN=P2^7;      //1602 EN端口
  13. sbit Echo=P2^4;        //HC-SR04 接收端口
  14. sbit Trig=P2^3;        //HC-SR04 发射端口
  15. sbit Resets_Key=P1^0;  //复位清零按键
  16. sbit Single_Key=P1^1;  //单次测量按键    误差较大
  17. sbit Contin_Key=P1^2;  //连续测量按键    误差小
  18. sbit Averag_Key=P1^3;  //连续(平均测量)按键   误差较小
  19. sbit Setting_Key=P1^4; //温度修正按键
  20. sbit Add_Key=P1^5;     //温度加1按键
  21. sbit Sub_Key=P1^6;     //温度减1按键
  22. sbit DQ=P3^7;          //DS18B20单总线接口

  23. bit Temp_Flag;         //正负温度标志:温度为正Temp_Flag=0,否则为1
  24. uint temp=25;          //温度值
  25. bit flag_flow=0,flag_one=0,flag_clear=0,flag_con1=0,flag_con2=0,flag_temp=0;
  26. uchar i=0,m,j,k;
  27. uint time=0,S=0,S1=0,totle=0;
  28. float V=346.0;
  29. uint Sav[11];                                                          //连续测量时10次平均值数组
  30. uchar Line1[16]={"T:   C  V:346m/s"};          //1602第一行初始字符显示数组
  31. uchar Line2[16]={"S=     m        "};          //1602第二行初始字符显示数组

  32. void Delayms(uchar xms);                        //延时xms函数
  33. void WriteLcd(uchar Dat,bit x);        //1602写函数
  34. void InitLcd(void);                                //1602初始化函数
  35. void DisplayLcd();                                //1602显示函数
  36. void init();                                        //初始化函数
  37. void keyscan();                                        //键扫描函数
  38. void StartModule();                            //启动模块函数
  39. void Conut(void);                                 //测量计算函数
  40. void Delayus(uchar xus);        //us级延时函数
  41. bit Init_DS18B20(void);         //初始化DS18B20函数
  42. uchar Read_DS18B20(void);       //读DS18B20函数
  43. void Write_DS18B20(uchar Dat);  //写DS18B20函数
  44. void GetTemp();                 //取温度函数
  45. void CalcTestTemp();            //温度处理函数

  46. void main(void)    //主函数
  47. {
  48.         init();
  49.         InitLcd();
  50.         while(1)
  51.         {
  52.                 keyscan();                         //键扫描函数
  53.             DisplayLcd();
  54.             if(flag_temp==0)
  55.             {
  56.                     GetTemp();
  57.                     CalcTestTemp();
  58.             }
  59.             if(flag_one==1||flag_con1==1||flag_con2==1)
  60.             {
  61.                     StartModule();         //开始发射超声波
  62.                 while(!Echo);                 //当RX为零时等待
  63.             TR0=1;                         //开启计数
  64.             while(Echo);             //当RX为1计数并等待
  65.                 TR0=0;                         //关闭计数
  66.             Conut();                 //计算
  67.             Delayus(200);
  68.                 flag_one=0;
  69.             }
  70.         }
  71. }

  72. void Delayms(uchar xms)   //延时ms函数
  73. {
  74.     uchar i,j;
  75.     for(i=xms;i>0;i--)
  76.             for(j=110;j>0;j--);
  77. }

  78. void WriteLcd(uchar Dat,bit x)  //1602写函数(写指令时x=0,写数据时x=1)
  79. {
  80.         LCD_EN=0;
  81.         LcdData=Dat;
  82.         LCD_RS=x;
  83.         LCD_RW=0;
  84.         LCD_EN=1;
  85.         Delayms(1);
  86.         LCD_EN=0;
  87. }

  88. void InitLcd(void)           //1602初始化函数
  89. {
  90.         WriteLcd(0x38,0);  //功能设定(38H),8位数据,2行显示,5*7点阵
  91.         WriteLcd(0x0C,0);  //显示开、关设定(0CH),开显示,不显示光标,光标不闪烁
  92.         WriteLcd(0x06,0);  //输入模式设定(06H),读写一个字符后,地址指针加1,且光标加1
  93.         WriteLcd(0x01,0);  //清除显示(01H),清除数据RAM中的数据
  94. }

  95. void DisplayLcd()                                  //液晶屏显示函数
  96. {
  97.     uchar y;
  98.     V=(331.4+temp*0.607);
  99.     Line1[2]=temp/10+0x30;
  100.     Line1[3]=temp%10+0x30;
  101.     Line1[4]=0xDF;                  //显示℃中C前面的小圆
  102.     Line1[10]=(uint)V/100+0x30;
  103.     Line1[11]=(uint)V%100/10+0x30;
  104.     Line1[12]=(uint)V%10+0x30;
  105.     if(flag_clear==1)
  106.     S1=0;
  107.     if((S1>=7000)||flag_flow==1)    //超出测量范围显示“-”
  108.         {         
  109.             flag_flow=0;
  110.             Line2[2]='-';
  111.             Line2[3]='.';
  112.             Line2[4]='-';
  113.             Line2[5]='-';
  114.             Line2[6]='-';
  115.         }
  116.     else
  117.         {
  118.             Line2[2]=S1/1000+0x30;
  119.             Line2[3]='.';
  120.             Line2[4]=S1%1000/100+0x30;
  121.             Line2[5]=S1%100/10+0x30;
  122.             Line2[6]=S1%10+0x30;
  123.         }
  124.     WriteLcd(0x80,0);               //设定第一行地址
  125.     for(y=0; y<16; y++)
  126.         WriteLcd(Line1[y],1);
  127.     WriteLcd(0xc0,0);               //设定第二行地址
  128.     for(y=0; y<16; y++)
  129.         WriteLcd(Line2[y],1);
  130. }

  131. void init()        //初始化函数
  132. {
  133.     TMOD=0x01;        //设T0为方式1,GATE=1;
  134.     TH0=0;
  135.     TL0=0;         
  136.     ET0=1; //允许T0中断
  137.     EA=1;  //开启总中断
  138. }

  139. void keyscan()                         //键扫描函数
  140. {
  141.     if(Resets_Key==0)                         //复位清零键
  142.     {
  143.         Delayms(5);
  144.         if(Resets_Key==0)
  145.         {
  146.                 while(Resets_Key==0);
  147.             flag_clear=1;
  148.             }
  149.     }
  150.     if(Single_Key==0)                         //单次测量键
  151.     {
  152.         Delayms(5);
  153.         if(Single_Key==0)
  154.         {
  155.                 while(Single_Key==0);
  156.                 flag_one=1;
  157.             flag_con1=0;
  158.                 flag_con2=0;
  159.                 flag_clear=0;
  160.             }
  161.     }
  162.     if(Contin_Key==0)                         //连续瞬时测量键
  163.     {
  164.         Delayms(5);
  165.         if(Contin_Key==0)
  166.         {
  167.                 while(Contin_Key==0);
  168.                 flag_con1=~flag_con1;
  169.                 if(flag_con1==1)
  170.                 {
  171.                     flag_one=0;
  172.                     flag_con2=0;
  173.                     flag_clear=0;
  174.                 }
  175.             }
  176.     }
  177.     if(Averag_Key==0)                        //连续平均测量键
  178.     {
  179.         Delayms(5);
  180.         if(Averag_Key==0)
  181.         {
  182.                 while(Averag_Key==0);
  183.                 flag_con2=~flag_con2;
  184.                 if(flag_con2==1)
  185.                 {
  186.                     flag_one=0;
  187.                     flag_con1=0;
  188.                     flag_clear=0;
  189.                 }
  190.             }
  191.     }
  192.     if(Setting_Key==0)                   //自动和手动转换键
  193.     {
  194.         Delayms(5);
  195.         if(Setting_Key==0)
  196.         {
  197.                  while(Setting_Key==0);
  198.                  flag_temp=~flag_temp;
  199.             }
  200.     }
  201.     if(Add_Key==0)                   //温度减键
  202.     {
  203.         Delayms(5);
  204.         if(Add_Key==0)
  205.         {
  206.                 while(Add_Key==0);
  207.                 if(flag_temp==1)
  208.                 {
  209.                 temp++;
  210.                     if(temp>=50)
  211.                     temp=50;
  212.                 }
  213.             }
  214.     }
  215.     if(Sub_Key==0)                   //温度加键
  216.     {
  217.         Delayms(5);
  218.         if(Sub_Key==0)
  219.         {
  220.                 while(Sub_Key==0);
  221.                 if(flag_temp==1)
  222.                 {
  223.                     if(temp>0)
  224.                 temp--;
  225.                     if(temp<=0)
  226.                     temp=0;
  227.                 }
  228.             }
  229.     }
  230. }

  231. void StartModule()                        //启动模块函数(触发一次,提供大于10us的高电平)
  232. {
  233.     Trig=1;                                       //启动一次模块
  234.     Delayus(15);                                    //延时2i+5=35us
  235.     Trig=0;
  236. }

  237. void Conut(void)                           //测量计算函数
  238. {
  239.     time=TH0*256+TL0;
  240.     TH0=0;
  241.     TL0=0;
  242.     V=(331.4+temp*0.607);        //由温度计算速度值,单位是m/s
  243.     S=(uint)(time*V/2000);       //算出来是mm
  244.     if(flag_con2==1)                           //如果是连续测量,则取10次的平均值
  245.     {
  246.         if(k<=10)
  247.             k++;
  248.             if(k>=11)
  249.         k=1;
  250.             Sav[k]=S;
  251.             j++;
  252.             if(j<10)
  253.             S1=S;
  254.             if(j>=10)
  255.             {
  256.                 j=10;
  257.                 for(m=1;m<=10;m++)
  258.             totle=totle+Sav[m];
  259.             S1=(uint)(totle/10);              //取10次的平均值
  260.                 totle=0;
  261.         }
  262.     }
  263.     if(flag_one==1||flag_con1==1)          //如果是单次测量或连续瞬时测量,则取1次的值
  264.     {
  265.         S1=S;
  266.     }
  267. }

  268. void Delayus(uchar xus) //晶振为12MHz,延时时间为2i+5 us
  269. {
  270.     while(--xus);
  271. }

  272. bit Init_DS18B20(void)
  273. {
  274.         bit x;
  275.         DQ=1;
  276.         DQ=0;
  277.         Delayus(250);
  278.         DQ=1;
  279.         Delayus(20);
  280.         if(!DQ) x=0;
  281.         else x=1;
  282.         Delayus(250);
  283.         DQ=1;
  284.         return x;
  285. }

  286. //读DS18B20函数
  287. uchar Read_DS18B20(void)
  288. {
  289.         uchar i=0,Dat=0;
  290.         for(i=0;i<8;i++)
  291.         {
  292.                 DQ=1;
  293.                 DQ=0;
  294.                 Dat>>=1;
  295.                 DQ=1;
  296.                 if(DQ) Dat |= 0x80;
  297.                 DQ=1;
  298.                 Delayus(30);
  299.         }
  300.         return Dat;
  301. }


  302. //写DS18B20函数
  303. void Write_DS18B20(uchar Dat)
  304. {
  305.         uchar i=0;
  306.         for(i=0;i<8;i++)//循环8次,写入一个字节
  307.         {
  308.                 DQ=1;//未发送前的状态
  309.                 Dat >>= 1;//将要传送的最低位放入CY
  310.                 DQ=0;//将总线拉低,产生写时序
  311.                 DQ=CY;//将要传送的位状态送到总线上
  312.                 Delayus(30);//延时50us,即保持总线状态,待DS18B20采样
  313.                 DQ=1;//恢复期,总线置1
  314.         }
  315. }

  316. void GetTemp(void)       //获取温度函数
  317. {
  318.         uchar a=0,b=0;
  319.         Init_DS18B20();
  320.         Write_DS18B20(0xcc); //跳过ROM
  321.         Write_DS18B20(0x44); //开启温度转换
  322.         Init_DS18B20();
  323.         Write_DS18B20(0xcc); //跳过ROM
  324.         Write_DS18B20(0xbe); //读暂存器
  325.         a=Read_DS18B20();    //读取高速暂存字节0,温度低8位
  326.         b=Read_DS18B20();    //读取高速暂存字节1,温度高8位
  327.         temp=b;
  328.         temp<<=8;
  329.         temp=temp|a;         //将高、低位温度编码合在一起
  330.         if(b>=8)             //判断温度值是否为负,如果温度高字节大于等于8说明温度值为负
  331.         {
  332.             temp=~temp+1;      //将补码转换成原码
  333.             Temp_Flag=1;       //温度标志为1,表示温度为负
  334.         }
  335.         else
  336.         {
  337.             Temp_Flag=0;       //温度标志为0,表示温度为正
  338.         }
  339.         temp=temp*0.0625+0.5;//将温度编码转换成温度值,加0.5是为了四舍五入
  340. }

  341. void CalcTestTemp()                              //温度处理函数
  342. {
  343.         if(temp<100) Line1[2]=' ';        //如果温度值小于10,十位显示空白(不显示0)
  344.         else Line1[2]=temp%100/10+0x30;   //取温度十位并转换成ASCII码
  345.         Line1[3]=temp%10+0x30;            //取温度个位并转换成ASCII码       
  346. }
复制代码



Ultrasonic ranging.zip

41.3 KB, 下载次数: 147, 下载积分: 黑币 -5

源代码

评分

参与人数 1黑币 +100 收起 理由
admin + 100 共享资料的黑币奖励!

查看全部评分

回复

使用道具 举报

ID:89286 发表于 2017-7-7 05:17 | 显示全部楼层
thanks for sharing
回复

使用道具 举报

ID:82765 发表于 2017-7-7 12:56 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
回复

使用道具 举报

ID:152003 发表于 2017-7-7 21:50 | 显示全部楼层
好资料 不错呢
回复

使用道具 举报

ID:1 发表于 2017-7-31 04:01 | 显示全部楼层
好资料,51黑有你更精彩!!!
回复

使用道具 举报

ID:223525 发表于 2017-8-1 16:23 | 显示全部楼层
不错不错
回复

使用道具 举报

ID:236761 发表于 2017-12-23 13:47 | 显示全部楼层

好资料,51黑有你更精彩!!!
回复

使用道具 举报

ID:88279 发表于 2018-3-3 22:10 来自手机 | 显示全部楼层
厉害。感谢分享!!!
回复

使用道具 举报

ID:300843 发表于 2018-4-5 23:31 | 显示全部楼层
感谢分享
回复

使用道具 举报

ID:493169 发表于 2019-3-20 11:54 | 显示全部楼层
感谢分享
回复

使用道具 举报

ID:156662 发表于 2019-3-20 13:38 | 显示全部楼层
感谢楼主无私奉献
回复

使用道具 举报

ID:530187 发表于 2019-5-9 17:10 来自手机 | 显示全部楼层
程序好像不完全,flag_flow在哪里变化哒?
回复

使用道具 举报

ID:506687 发表于 2019-5-18 17:38 | 显示全部楼层
请问有原理图吗?
回复

使用道具 举报

ID:559822 发表于 2019-6-11 12:23 | 显示全部楼层
感谢分享 得好好琢磨琢磨
回复

使用道具 举报

ID:645965 发表于 2019-11-20 18:27 | 显示全部楼层
有实物连线图吗?
回复

使用道具 举报

ID:645965 发表于 2019-11-20 21:35 | 显示全部楼层
请问下那个lcd1602怎么连线,有点看不懂?
回复

使用道具 举报

ID:645965 发表于 2019-11-20 21:59 | 显示全部楼层
为什么1602的其余引脚没有定义?
回复

使用道具 举报

ID:484092 发表于 2020-6-18 21:31 | 显示全部楼层
谢谢分享
。。。。。。。。。
回复

使用道具 举报

ID:484092 发表于 2020-6-18 21:31 | 显示全部楼层
邵晓杰 发表于 2019-11-20 21:59
为什么1602的其余引脚没有定义?

定义了  p0口
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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