这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 活动中心 » 板卡试用 » 【换取手持数字示波器】+51单片机测试超声波测距模块

共3条 1/1 1 跳转至

【换取手持数字示波器】+51单片机测试超声波测距模块

专家
2024-05-28 14:37:13   被打赏 22 分(兑奖)     打赏

本例中使用51单片机驱动两轮小车,配合超声波测距模块,实现运动中测距。超声波模块使用非常常见的那种收发一体的模块。

工作原理是:

1、给模块的触发引脚至少10微秒的搞电平信号。使模块通过超声波发射头发出8个40KHz的方波信号。

2、超声波接收头收到返回信号时,通过IO口输出一个高电平信号。这个高电平信号的持续时间,就是超声波发出后,遇到物体反射回来经过的时间,由公式

测试距离 = 高电平持续时间 * 声波速度 / 2

其中声波速度为340m/s。

本次实验采用数码管,以动态扫描的方式显示数据。电路如下:

图片3.png为了实现动态测距,使用部分IO口用来驱动小车的两个电机,控制小车向前移动,并在移动过程中测试前方物体距离传感器的距离。

程序如下:

/***********************************************************************************************************/
//hc-sr04 超声波测距模块
//晶振:11。0592
//接线:模块TRIG接 P2.7(P1.0显示)  ECH0(P1.1显示) 接P2.6
// T1:2ms单位发生中断,累计到400(800ms)时使超声波模块开始工作,发10微秒高电平给Trig端
// T0:1微妙单位计数,最大65535。用于超声波模块回传测距数值时统计时间长度。
// 距离换算(未考虑温度补偿):声速340m/s->0.340mm/微秒,距离=时间累计*0.34/2
//数码管:共阳数码管P1接数据口,P2.0 P2.1 P2.2 P2.3接选通数码管
/***********************************************************************************************************/    
#include <reg51.h>//器件配置文件
#include <intrins.h>
#define uchar unsigned char
unsigned int  time=0;
unsigned int  timer=0;
unsigned char posit=0;
unsigned long s=0;
unsigned long old_s=0;
bit      flag =0;
bit      test_flag =0;
unsigned char const table[] ={ 0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xBF,0xff/*-*/};
unsigned char disbuff[4]   ={ 0,0,0,0,};
sbit LED1 = P2^0; // 数码管1的电源
sbit LED2 = P2^1; // 数码管2的电源
sbit LED3 = P2^2; // 数码管3的电源
sbit LED4 = P2^3; // 数码管4的电源
sbit Trip  = P2^7 ;  // 输出端,超声波模块触发端
sbit SData  = P2^6 ;  // 输入端,数据回传
sbit motor1_a = P1^0 ;  // 驱动轮马达-1-a
sbit motor1_b = P1^1 ;  // 驱动轮马达-1-b
sbit motor2_a = P1^2 ;  // 驱动轮马达-1-a
sbit motor2_b = P1^3 ;  // 驱动轮马达-1-b
bit motor1_a_bak = 0 ;  // 驱动轮马达-1-a
bit motor1_b_bak = 0;  // 驱动轮马达-1-b
bit motor2_a_bak = 0;  // 驱动轮马达-1-a
bit motor2_b_bak = 0;  // 驱动轮马达-1-b
sbit test_led = P1^4 ;
/********************************************************/
/** 延时 */
void delay1(uchar i) {
  uchar j,k; 
  for(j=i;j>0;j--)
    for(k=125;k>0;k--);
}
/** 显示数据 */
void Display(void){
   P0=table[disbuff[0]];
   LED1 = 0;
   _nop_();
   _nop_();
   LED1 = 1;
   P0=table[disbuff[1]];
   LED2 = 0;
   _nop_();
   _nop_();
   LED2 = 1;
   P0=table[disbuff[2]];
   LED3 = 0;
   _nop_();
   _nop_();
   LED3 = 1;
   P0=table[disbuff[3]];
   LED4 = 0;
   _nop_();
   _nop_();
   LED4 = 1;
}
/** 使用12M的晶振,中断周期为1微秒。1微秒的计数 */
void Conut(void) {
unsigned int  tmp1=0;
unsigned int  tmp2=0;
s=TH0*256+TL0;          // 取得计数值(单位:1微秒)
TH0=0;
TL0=0;
s=s*0.17;                            // 算出来是mm
disbuff[0]=(s - s%1000)/1000;         // 米
disbuff[1]=(s%1000 - s%100)/100;      // 分米
disbuff[2]=(s%100 - s%10)/10;  // 厘米
disbuff[3]=s%10;                  // 毫米
                    
if (flag==1) {
        disbuff[0] = 10;
disbuff[1] = 10;
disbuff[2] = 10;
disbuff[3] = 10;
flag=0;
}
}
/** T0中断用来计数器溢出,超过测距范围 */
void zd0() interrupt 1 {
    flag=1; //中断溢出标志
  }
/** T1中断用来扫描数码管和计800MS启动模块 */
void  zd3()  interrupt 3 {
EA=0;   //关闭总中断
TH1=0xf8;
TL1=0x30;
Display();         // 显示测试结果
if (timer < 400) {
   timer++;       // 启动测试的周期(单位:2毫秒)
} else {   // 超过800毫秒了,准备测距
    timer = 0;
    if (test_flag == 0) {
   test_flag = 1;
test_led = 0;
Trip=1;   // 800MS  启动一次模块,利用NOP操作完成10微秒的启动脉冲
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
Trip=0;
}
}
EA=1;//开启总中断
}
/**
 *  后退
 */
void back(void) {
   motor1_a = 1;
   motor2_a = 1;
   motor1_b = 0;
   motor2_b = 0;
//   P1 = P1 & 0xF0;
//   P1 = P1 | 0xF5;
   motor1_a_bak = motor1_a;
   motor1_b_bak = motor1_b;
   motor2_a_bak = motor2_a;
   motor2_b_bak = motor2_b;
}
/**
 *  前进
 */
void go(void) {
motor1_b = 1;
motor2_b = 1;
   motor1_a = 0;
   motor2_a = 0;
//   P1 = P1 & 0xF0;
//   P1 = P1 | 0xFA;
   motor1_a_bak = motor1_a;
   motor1_b_bak = motor1_b;
   motor2_a_bak = motor2_a;
   motor2_b_bak = motor2_b;
}
/**
 *  停车
 */
void stop(void) {
   motor1_a = 0;
   motor1_b = 0;
   motor2_a = 0;
   motor2_b = 0;
}
void clear(void) {
   motor1_a_bak = 0;
   motor1_b_bak = 0;
   motor2_a_bak = 0;
   motor2_b_bak = 0;
}
/**
 *  右轮不动,向后转
 */
void left_back(void) {
   motor1_a = 1;
   motor1_b = 0;
   motor2_a = 0;
   motor2_b = 0;
}
/**
 *  左轮不动,向后转
 */
void right_back() {
   motor1_a = 0;
   motor1_b = 0;
   motor2_a = 1;
   motor2_b = 0;
}
/**
 *  右轮不动,向前转
 */
void left_go() {
   motor1_a = 0;
   motor1_b = 1;
   motor2_a = 0;
   motor2_b = 0;
}
void ret_status(void) {
   motor1_a = motor1_a_bak;
   motor1_b = motor1_b_bak;
   motor2_a = motor2_a_bak;
   motor2_b = motor2_b_bak;
}
/**
 *  左轮不动,向前转
 */
void right_go() {
   motor1_a = 0;
   motor1_b = 0;
   motor2_a = 0;
   motor2_b = 1;
}
   
/*************主程序**************/
void main(void) {
    char tmp = 0;
    EA=0;
    stop();  
      TMOD=0x11;   //设T0为方式1,GATE=0;
      TH0=0;
      TL0=0;          
      TH1=0xf8;   //2MS定时
      TL1=0x30;
      ET0=1;             //允许T0中断
      ET1=1;   //允许T1中断
      TR1=1;   //开启定时器
      EA=1;   //开启总中断
      while(1) {
          if (test_flag==1) {
                stop();
                  while(!SData) ;//当接收为零时等待,直到收到测试完成后的发送开始信号
                  TR0=1;    //开启T0计数(计数单位为微秒)
                  while(SData);//等待测距数据发完
                  TR0=0;//关闭测距计数
                  test_led = 1;
                  Conut();//计算
              // 取得距离前方障碍物的距离(单位:mm)
                  if (s < 50 ) {
                      // 前方障碍小于15mm时,停止前进
                        //back();
                        stop();
                        clear();
                  } else {
                      // 继续前进
                        go();
                  }
                  test_flag=0;
            }
            tmp = timer % 12;
            if ((tmp % 4) == 0) {
               ret_status();
            } else {
               stop();
            }
      }
}

程序中使用了计数器T0和T1的中断。前者用于测距,后者用于计时和动态扫描数码管显示。程序处理不是很细致,仅供初学者学习。



院士
2024-06-04 16:47:08     打赏
2楼

版主,这些精确时间控制是不是要放在中断函数里面来执行啊?


高工
2024-06-18 06:27:36     打赏
3楼

感谢分享


共3条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]