网上的蓝桥杯教程(模块)中,超声波模块大多发送八个周期的40khz方波. 然而在实际操作中,会显得非常不稳定.
在一次ai debug会话中,ai给出了以下发送超声波代码:
1 2 3 4 5 6 7
| void Transmit() { EA=0; TX=1; Delay12us(); Delay12us(); TX=0; }
|
这段脉冲的周期远低于八个周期,但出乎意料的,这段代码运行的很稳定.
于是,为了求证,我在4T平台找到蓝桥杯官方给出的超声波代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| unsigned char Dist_Meas(void) { unsigned char ucNum = 10;
TX = 0; CL = 0xf4; CH = 0xff; CR = 1; while (ucNum--) { while (!CF); TX ^= 1; CL = 0xf4; CH = 0xff; CF = 0; } CR = 0;
CL = 0; CH = 0; CR = 1; while(RX && !CF); CR = 0; return ((CH<<8)+CL)*0.017; }
|
它一共翻转了ucNum次电平,翻转了10次,也就是5个周期的40khz方波.
以上似乎已经说明:8个周期的方波对于蓝桥杯的超声波模块来说过于多了,以至于可能出现RX 的放大/比较电路在发射期间被串扰饱和的情况。导致等脉冲发完,换能器还在机械余震,RX 继续振荡,造成干扰.
8周期的 8051 超声波例程最早来自某个开发板,那个板的接收电路增益低,需要 8 个脉冲才能让回波高于比较器阈值. 蓝桥杯竞赛板的模拟前端比那些开发板好,1个脉冲就够了.
验证
考虑从稳定性和准确性两个方面。
测量方法
拿一本书,使得其距离超声波传感器上沿距离为15cm、35cm、55cm,在不同的距离下,改变发射周期 (1≤i≤11) 并记录数码管显示数据.
公式部分
在每次测量中,间隔500ms更新一次数码管显示距离,一共产生五次数据,计算平均值和标准差 (N=5)
平均值计算公式为:
μ=N1i=1∑Nxi
标准差计算公式为:
σ=N1i=1∑N(xi−μ)2
其中,标准差用于衡量稳定性,平均值作如下运算用于衡量准确性
d误差=∣μ−d实际距离∣
代码部分
通过改变按键增减waveNum的值来改变发射次数.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
| #include <REGX52.H> #include <INTRINS.H>
sfr AUXR = 0x8E; sfr CL = 0xE9; sfr CCAP0L = 0xEA; sfr CCON = 0xD8; sbit CF = CCON^7; sbit CR = CCON^6; sbit CCF0 = CCON^0; sfr CH = 0xF9; sfr CCAP0H = 0xFA; sfr CMOD = 0xD9; sfr CCAPM0 = 0xDA; sfr T2H = 0xD6; sfr T2L = 0xD7; sbit TX = P1^0; sbit RX = P1^1; unsigned char segData[8] = {10, 10, 10, 10, 10, 10, 10, 10}; unsigned value=1000; unsigned char code segTable[18] = {0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90, 0xFF, 0xBF, 0xC7, 0x89, 0x8E, 0x8C, 0x86, 0x88, }; void PCA_Init(void) { CCON = 0; CL = 0; CH = 0; CMOD = 0x00; CCAP0L = value; CCAP0H = value>>8; CCAPM0 = 0x49; value += 1000; CR = 1; EA = 1; } void Init() { P2 = (P2&0x1F)|0xA0; P0_4=0; P0_6=0; P2 = (P2&0x1F)|0x80; P0=0xFF; P2 = (P2&0x1F); }
bit runFlag=0; unsigned char waveNum=9; void Delay12us(void) { unsigned char data i;
_nop_(); _nop_(); i = 33; while (--i); }
void Transmit() { unsigned char i; EA=0; for(i = 0; i < waveNum; i++) { TX = 1;Delay12us();TX = 0;Delay12us(); } EA=1; } unsigned GetDistance() { unsigned t=0;
TMOD &= 0x0f; TL1 = 0x00; TH1 = 0x00; TF1 = 0; TR1 = 0;
Transmit(); TR1 = 1; while(RX==0 && TF1==0); while(RX==1 && TF1==0); TR1 = 0;
if(TF1) { TF1=0; return 0; } t = (TH1<<8) | TL1; return t*0.017; } void main() { Init(); PCA_Init(); while(1) { static unsigned char cnt=0; unsigned char i; unsigned temp; if(runFlag) { cnt++; runFlag=0; segData[0]=waveNum/10; segData[1]=waveNum%10; if(cnt>=10) { cnt=0; temp = GetDistance(); segData[3]=segData[4]=segData[5]=segData[6]=segData[7]=10; i=7; segData[i]=0; while(temp) { segData[i--]=temp%10; temp /= 10; } } } } } sfr P4 = 0xC0; sbit COL1 = P4^4;sbit COL2 = P4^2;sbit COL3 = P3^5; sbit ROW3 = P3^2;sbit ROW4 = P3^3; unsigned char ScanKeyboard(){ unsigned char key=0; COL1=0; COL2=1; COL3=1; if(!ROW3) key=5; if(!ROW4) key=4;
COL1=1; COL2=0; COL3=1; if(!ROW3) key=9; if(!ROW4) key=8;
COL1=1; COL2=1; COL3=0; if(!ROW3) key=13; if(!ROW4) key=12; return key; }
void ProKey() { static unsigned char preKey=0; unsigned char key=ScanKeyboard(); if(key==0 && preKey!=0) { if(preKey==4) { if(waveNum-1>=0) waveNum--; } if(preKey==5) { if(waveNum+1<=11) waveNum++; } } preKey=key; } void DisplaySingleDigit(unsigned char addr) { P2 = (P2 & 0x1F) | 0xE0; P0 = 0xFF; P2 = (P2 & 0x1F) | 0xC0; P0 = (1<<addr); P2 = (P2 & 0x1F) | 0xE0; P0 = segTable[segData[addr]]; P2 = (P2 & 0x1F); } void PCA_Routine(void) interrupt 7 { static unsigned char clkCnt=0, segCnt=0; CCF0 = 0; CCAP0L = value; CCAP0H = value>>8; if(clkCnt>=100) {clkCnt=0;runFlag=1;ProKey();} if(segCnt>=8) segCnt=0; DisplaySingleDigit(segCnt); clkCnt++; if(clkCnt&0x01)segCnt++; value += 1000; }
|
数据
测量得以下数据(主包手比较抖,距离偏差为±1cm):
| 目标距离 |
发射周期数 |
1 |
2 |
3 |
4 |
5 |
| 15cm |
1 |
15 |
15 |
15 |
15 |
15 |
|
3 |
14 |
14 |
14 |
14 |
14 |
|
5 |
14 |
13 |
13 |
14 |
13 |
|
7 |
5 |
6 |
7 |
13 |
4 |
|
9 |
6 |
5 |
5 |
5 |
5 |
| 35cm |
1 |
36 |
36 |
36 |
36 |
36 |
|
3 |
34 |
34 |
34 |
34 |
34 |
|
5 |
34 |
34 |
34 |
34 |
34 |
|
7 |
6 |
5 |
5 |
5 |
5 |
|
9 |
4 |
7 |
4 |
5 |
4 |
| 55cm |
1 |
56 |
56 |
56 |
56 |
56 |
|
3 |
55 |
54 |
54 |
55 |
55 |
|
5 |
54 |
55 |
7 |
55 |
55 |
|
7 |
6 |
5 |
6 |
5 |
4 |
|
9 |
4 |
4 |
4 |
4 |
4 |
计算得:
| 目标距离 (cm) |
发射周期数 |
均值 |
标准差 |
误差 |
| 15 |
1 |
15.0 |
0.00 |
0.0 |
|
3 |
14.0 |
0.00 |
1.0 |
|
5 |
13.4 |
0.49 |
1.6 |
|
7 |
7.0 |
3.16 |
8.0 |
|
9 |
5.2 |
0.40 |
9.8 |
| 35 |
1 |
36.0 |
0.00 |
1.0 |
|
3 |
34.0 |
0.00 |
1.0 |
|
5 |
34.0 |
0.00 |
1.0 |
|
7 |
5.2 |
0.40 |
29.8 |
|
9 |
4.8 |
1.17 |
30.2 |
| 55 |
1 |
56.0 |
0.00 |
1.0 |
|
3 |
54.6 |
0.49 |
0.4 |
|
5 |
45.2 |
19.10 |
9.8 |
|
7 |
5.2 |
0.75 |
49.8 |
|
9 |
4.0 |
0.00 |
51.0 |
可视化
结论
总的来说,短距离时,waveNum在[1,5]这个区间内,超声波测量值基本准确. 这个检验还是有不严谨的地方,测量次数太少且未去除极端数据(去除极端数据的话,waveNum为5也可用).
补充
此外,长距离时,则需要更长的发射周期. 但是受串扰影响,代码需要修改:在Transmit()后硬编码几个Delay12us()或添加while(RX==1&&TF1==0);while(RX==0&&TF1==0);while(RX==1&&TF1==0);.
若有错误,欢迎指出.