本例中使用51单片机驱动两轮小车,配合超声波测距模块,实现运动中测距。超声波模块使用非常常见的那种收发一体的模块。
工作原理是:
1、给模块的触发引脚至少10微秒的搞电平信号。使模块通过超声波发射头发出8个40KHz的方波信号。
2、超声波接收头收到返回信号时,通过IO口输出一个高电平信号。这个高电平信号的持续时间,就是超声波发出后,遇到物体反射回来经过的时间,由公式
测试距离 = 高电平持续时间 * 声波速度 / 2
其中声波速度为340m/s。
本次实验采用数码管,以动态扫描的方式显示数据。电路如下:
为了实现动态测距,使用部分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的中断。前者用于测距,后者用于计时和动态扫描数码管显示。程序处理不是很细致,仅供初学者学习。