蓝桥杯单片机组备赛指南请查看 :本专栏第1篇文章
本文章针对蓝桥杯-单片机组比赛开发板所写,代码可直接在比赛开发板上使用。
型号:国信天长4T开发板(绿板),芯片:IAP15F2K61S2
(使用国信天长蓝板也可以完美兼容,与绿板几乎无差别)
1. 代码目的
分别采用软件延时与中断两种方式
实现正确设置超声波,并将障碍物距离超声波模块的距离折算成cm显示在数码管上。
数码管显示格式如下:
S | 1 | 2 | |||||
标识 | 无用位熄灭 | 数据位 |
表示距离为12cm,其中开发板数码管为共阳极连接,符号S的采用数字5表示
2. 命题现状
官方开发板使用的超声波传感器为:CX20106A。该芯片与常见的HC-SR04不同。
20年以前的资料,大家都会说该模块省赛不考,但却是国赛的重点
21年省赛成功考了,因此大家需要在省赛掌握该模块
目前市面上的蓝桥杯超声波代码,基本都是基于小蜜蜂老师的程序,采用软件延时的方式近似产生周期为40Khz的方波,本文将提供采用定时计数器方式的代码。
3. 超声波测距原理
超声波的原理很好理解,通过TX引脚连续发送8个40Khz(周期25us)的方波信号,当信号碰到障碍物便会反射回来被接收,模块自动将RX引脚置为低电平。我们利用定时计数器获取发出信号与接收信号的时间差,乘以声速并除以2便可得到距离。
CX20106A芯片要求发送信号为38KHZ~41KHZ,大部分教程建议取40Khz。
其计算公式如下:
其中:
表示距离,
表示发出信号与接收到信号的时间差,
表示声波的传播速度,一般取340M/S。
同时我们的比赛开发板采用11.0592Mhz,经过12分频后,定时计数器加1为1.085us.
带入计算,并且将单位折算为cm,我们可以得到超声波最终的计算表达式为:
Distance = 0.0184 * 计数值 (cm)
8个40Khz的波形实现:
方法:
比较常见的方法是,利用for循环写8次循环,在一次循环中将TX引脚置为高电平,软件延时13~14us,再将TX引脚置为低电平,软件延时13~14us。从而在一次for循环后,发出了一个周期为26~28us的矩形波。循环8次即发送了8个矩形波信号。
实际测试中,14us的软件延时最完美
出现的问题(可以不看):
因为方法1会造成8*26us=2.08ms的软件延时,对于数码管或其他外设而言可能会耽误工作时序。因此我们可以采用定时计数器去产生标准12.5us的中断,进行方波信号的发送。当定时值达到12.5us的整数倍时,中断产生,即取反一次TX引脚。共定时12.5*8*2=200us时进行定时,信号发送完成。
但其实2.08ms的软件延时,基本不对数码管造成显著影响。且iic等底层通信有自己的时序,很少产生负面影响。为了节省单片机定时计数器个数,因此忽略该影响
关于定时计数器:
理论上,假设我们的超声波发出去,不论距离多远只要碰到障碍物都会反射回来。那我们可以测量的范围只由定时计数器的工作方式决定
当工作在方式0,13位计数器,最大计数值为8192,则最远可以8192*0.0184=150cm
当工作在方式1,16为计数器,最大计数值65536,则最远可以1205cm
当工作在方式2,8位计数器,最大计数值256,则最远可以4cm
经过实际测试,只有方式0是比较适合我们实际使用的,因此都采用方式0
问题(数码管闪烁原因):如果超声波正常反射并被接收,则等待接收数据的过程较短,不影响数码管的动态显示。但是当没有接收到反射的超声波时,程序等待时长会达到8192*1.085=8.888ms,这个时间会让数码管产生明显的闪烁。因此,我们需要研究如何使用中断方式。但是在蓝桥杯比赛中,开发板上并未将中断的引脚进行连接,因此未接收到信号时的闪烁问题,可以忽略。本文将在末尾提出闪烁的解决方案,供大家一起学习
4. 函数实现参考
软件延时发送超声波:
轮询方式判断是否有超声波信号返回:
5. 开发板原理图介绍(可以不看)
N_B1为超声波模块CX20106A发射端接口,通过跳线帽与TX(P11)引脚相连:
N_A1为超声波模块CX20106A接收端接口,通过跳线帽与RX(P10)引脚相连:
6. 全代码参考
6.1 传统软件延时方法
采用传统软件延时方案的超声波数据显示:
#include <reg52.h>
#include <intrins.h>
sbit AUXR = 0x8e;
sbit TX = P1^0;
sbit RX = P1^1;
void SMGrunning ();
unsigned char code duanma[18]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,
0x88,0x80,0xc6,0xc0,0x86,0x8e,0xbf,0x7f};
void select_HC573 ( unsigned char channal )
{
switch ( channal )
{
case 4:
P2 = ( P2 & 0x1f ) | 0x80;
break;
case 5:
P2 = ( P2 & 0x1f ) | 0xa0;
break;
case 6:
P2 = ( P2 & 0x1f ) | 0xc0;
break;
case 7:
P2 = ( P2 & 0x1f ) | 0xe0;
break;
}
}
void state_SMG ( unsigned char pos_SMG , unsigned char value_SMG )
{
select_HC573 ( 7 );
P0 = 0xff;
select_HC573 ( 6 );
P0 = 0x01 << pos_SMG;
select_HC573 ( 7 );
P0 = value_SMG;
}
void state_SMG_all ( unsigned char value_SMG_all )
{
select_HC573 ( 6 );
P0 = 0xff;
select_HC573 ( 7 );
P0 = value_SMG_all;
}
void init_sys ( )
{
select_HC573 ( 4 );
P0 = 0xff;
select_HC573 ( 5 );
P0 = 0x00;
}
void Delay1ms() //@11.0592MHz
{
unsigned char i, j;
_nop_();
_nop_();
_nop_();
i = 11;
j = 190;
do
{
while (--j);
} while (--i);
}
void Delay14us() //@11.0592MHz
{
unsigned char i;
_nop_();
i = 36;
while (--i);
}
void init_timer0 ()
{
AUXR &= 0x7F; //定时器时钟12T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0x00; //设置定时初始值
TH0 = 0x00; //设置定时初始值
TF0 = 0; //清除TF0标志
}
void send_sonic ()
{
unsigned char i;
for ( i=0 ; i<8 ; i++ )
{
TX = 1;
Delay14us();
TX = 0;
Delay14us();
}
}
unsigned int value_distance = 0;
void read_sonicrunning ()
{
TL0 = 0x00;
TH0 = 0x00;
send_sonic ();
TR0 = 1;
//当超声波未接受到返回信号,且计时器没有溢出
while ( (RX ==1) && (TF0 == 0) );
TR0 = 0;
if ( TF0 == 0 ) //如果接收到了返回信号,且定时器未溢出
{
value_distance = TH0;
value_distance = (value_distance<<8) | TL0;
value_distance = value_distance * 0.0184;//加7是误差补偿,室温26℃
}
else //如果定时器溢出,却还没返回信号
{
TF0 = 0;
value_distance = 999;
}
}
void SMGrunning ()
{
state_SMG ( 0 , duanma[5] );
Delay1ms();
if ( value_distance>99 )
{
state_SMG ( 5 , duanma[value_distance/100%10] );
Delay1ms();
}
if ( value_distance>9 )
{
state_SMG ( 6 , duanma[value_distance/10%10] );
Delay1ms();
}
state_SMG ( 7 , duanma[value_distance%10] );
Delay1ms();
state_SMG_all ( 0xff );
Delay1ms();
}
void main ()
{
init_sys ();
init_timer0 ();
while ( 1 )
{
read_sonicrunning ();
SMGrunning ();
}
}
6.2 采用中断的方法
采用中断,核心目的在于要消除当传感器未接受到返回信号时,等待的8192*1.085=8.888ms时间内,数码管刷新一次。从而避免闪烁。
但是一次数码管刷新函数涉及8ms以上的时间,又会对超声波造成严重的影响。
但是我们可以找规律,当传感器未接受到返回信号,数码管会显示的内容只有一位或者三四位。并不需要全部刷新一遍。因此我们可以定义一个间隔时间为2ms的中断,一次只刷新一位数码管