本文基于小蜜蜂编程风格,并结合往期编程代码模板


        由于在国赛中,如果同时考到LM555与超声波,会产生定时计数器不够用现象,因此我们可以将采用PCA方式来实现超声波的计数功能。

1. PCA简介

PCA方式全称为:Programmable Counter Array。我们不需要搞懂其内部的原理,只需要掌握其具备的计数功能,从而便于我们使用。

PCA计数的过程,与定时计数器相似,只不过定时计数器可以在中断服务函数中响应计数溢出,PCA没有中断服务函数,而直接进行计数值的读取。

在配置时,我们将PCA与定时计数器的相关寄存器和位,类比来看,便可快速掌握:

定时计数器 PCA计数器 作用
TMOD CMOD 定义计数模式
TCON CCON 控制计数器
THx,TLx CH,CL 计数值寄存器
TFx CF 溢出标志位
TRx CR 启动或暂停标志

以上便是配置PCA计数器会用到的所有标志,我们要做的就是记下来,知道是什么东西。

2. 定义特殊寄存器和特殊位变量

        如果头文件是stc15f2k60s2.h , 则不需要此步骤,因此寄存器在这个头文件中都有了

        如果头文件是reg52.h , reg51.h , REGX52.H :则需要添加特殊寄存器地址和位:

不用背!!看这里:

        这些东西在stc-isp里面都可以查到,此外AUXR,P4等也是可以查出来的

3. 实现关键步骤

        初始化:

        读取过程(使用0x08):(可以看出读取过程与平时使用定时计数器完全一样,只是位变量名不同)

          读取过程(使用0x01):(需要配置,设置计数值达到某一定量时,退出循环,避免程序卡死)

4. 参考代码

#include <REGX52.H>
#include <intrins.h>

sfr AUXR = 0x8e;
sfr CMOD = 0xd9;
sfr CCON = 0xd8;
sfr CL = 0xE9;   //0000,0000 PCA计数器低字节
sfr CH = 0xF9;   //0000,0000 PCA计数器高字节

sbit CF = CCON^7;
sbit CR = CCON^6;



sbit TX = P1^0;
sbit RX = P1^1;

void flash_SMG ();

unsigned int distance = 0;
unsigned char sonic_flag = 0; //500ms刷新一次超声波数据标志位
unsigned char code duanma[20] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,
                                 0x88,0x83,0xc6,0xc0,0x86,0x8e,0xbf,0xc7,0x89,0x8c};

//锁存器通道选择函数
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;
		case 0:P2 = ( P2 & 0x1f ) | 0x00;break;
	}
}

//单位数码管显示函数
void state_SMG ( unsigned char pos_SMG , unsigned char value_SMG )
{
	select_HC573 ( 0 );
	P0 = 0x01 << pos_SMG;select_HC573( 6 );
	select_HC573 ( 0 );
	P0 = value_SMG;select_HC573( 7 );
	select_HC573 ( 0 );
}

//全位数码管静态显示
void state_SMG_all ( unsigned char value_SMG_all )
{
	select_HC573 ( 0 );
	P0 = 0xff;select_HC573( 6 );
	select_HC573 ( 0 );
	P0 = value_SMG_all;select_HC573( 7 );
	select_HC573 ( 0 );
}	

//初始化系统,关闭继电器和蜂鸣器
void init_sys ()
{
	select_HC573 ( 0 );
	P0 = 0xff;select_HC573 ( 4 );
	select_HC573 ( 0 );
	P0 = 0x00;select_HC573 ( 5 );
	select_HC573 ( 0 );
}

//延时函数生成13us,然后把变量值改成38效果最佳
void Delay13us()		//@12.000MHz
{
	unsigned char i;

	_nop_();
	_nop_();
	i = 38;
	while (--i);
}

void init_pca ()
{

	CMOD=0x01;//设置定时器时钟
	//0x00可以正常使用,但是感觉发出去的波形不是垂直的,带有一定的弧角,最大测量132cm
	//0x01效果更佳,最大实测到了305cm,数据比较稳定,但题目复杂时,while循环等待太久单片机会死机,因此需要根据CH的值推出循环
	//0x08效果积极稳定,实测最大可以到93cm,且最终距离数据需要除以12后使用。数据稳定度最佳。
	//综合比较之后,在国赛中,如果测距小于90则用0x08,大于90用0x01,并使用我调试过的超声波程序。
		
    CCON=0x00;//将溢出位cf(溢出标志位)置零cr(启停位)置零停止计数	
}

void send_sonic ()
{
	unsigned char i;
	for ( i=0 ; i<8 ; i++ )
	{
		TX = 1;
		Delay13us();
		TX = 0;
		Delay13us();
	}
}


void recive_sonic ()
{
	if ( sonic_flag == 1 ) //刷新标志位
	{
		unsigned int time = 0;
		CF = 0;		
		CL = 0x00;
		CH = 0x00;	
		
		EA = 0;
		send_sonic ();
		EA = 1;
		CR = 1;

		while ( (CH<0xb0) && (RX == 1) );  //CH的数据需要根据比赛时调整,我测试时感觉没有定值。14届国赛中我用的0x40,可以测量到265cm,但这里0x40不行,看来是和程序的量有关系的,毕竟单片机i性能有限
		CR = 0;
		if ( CH>=0xb0 )
		{
			distance = 999;
			CF = 0;
		}
		else 
		{
			time = CH;
			time = (time<<8)|CL;
			distance = (unsigned int)(time*0.0172);
		}
		
		sonic_flag = 0;
	}
}

void init_timer0(void)		//5微秒@12.000MHz
{
	AUXR &= 0x7F;		//定时器时钟12T模式
	TMOD &= 0xF0;		//设置定时器模式
	TMOD |= 0x02;		//设置定时器模式
	TL0 = 0xFB;		//设置定时初始值
	TH0 = 0xFB;		//设置定时重载值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
	
	ET0 = 1;
	EA = 1;
}

unsigned char count_50us = 0;
unsigned char count_10ms = 0;
unsigned char flash_count = 0;
void timer0_service () interrupt 1
{
	if ( ++count_50us == 200 )
	{
		count_50us = 0;
		if ( ++count_10ms == 50 )
		{
			count_10ms = 0;
			sonic_flag = 1;
		}	
	}
	
	if ( count_50us % 100 == 0 )
	{
		if ( ++flash_count == 6 )
		{
			flash_count = 0;
		}
		flash_SMG ();
	}
}

void flash_SMG ()
{
	state_SMG_all ( 0xff );
	switch ( flash_count )
	{
		case 0:
			state_SMG ( 0 , duanma[12] );break;
		case 1:
			state_SMG ( 1 , 0xc7 );break;
		case 2:
			if ( distance > 999 )
			{
				state_SMG ( 4 , duanma[distance/1000%10] );
			}
			else 
			{
				state_SMG ( 4 , 0xff );
			}
			break;
		case 3:
			if ( distance > 99 )
			{
				state_SMG ( 5 , duanma[distance/100%10] );
			}
			else 
			{
				state_SMG ( 5 , 0xff );
			}
			break;
		case 4:
			if ( distance > 9 )
			{
				state_SMG ( 6 , duanma[distance/10%10] );
			}
			else 
			{
				state_SMG ( 6 , 0xff );
			}
			break;		
		case 5:
				state_SMG ( 7 , duanma[distance%10] );break;		
	}
}	

void main ()
{
	init_pca ();
	init_timer0 ();
	init_sys ();
	while ( 1 )
	{
		recive_sonic ();
	}
}

Logo

技术共进,成长同行——讯飞AI开发者社区

更多推荐