本文基于小蜜蜂编程风格,并结合往期编程代码模板
由于在国赛中,如果同时考到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 ();
}
}