蓝桥杯学习笔记:利用PCA模块实现超声波测距(单片机CT107D)
一.前言
最近在琢磨开发板上的超声波模块,一般的方法是发出超声波后开启一个计时器,等待接收端口(P1^1)变为低电平或者触发计时器溢出中断后计算数据计算或是其他操作,但这么做有个问题,就是在等待接收端口回应的时候用的是while循环,这一段时间内无法进行其他操作,所以在距离较长的时候会出现一些问题(如数码管亮度减少),同时为了数据的准确,不能再while中加入数码管扫描的操作
附:之前使用的代码
void Measure_Distance()
{
unsigned int time;
TMOD&=0xf0;
TH0=0;
TL0=0;
SendWave();
TR0=1;
while(RX==1&&TF0==0);//问题就出现这里
TR0=0;
if(TF0==0)
{
time=TH0<<8|TL0;
distance=time*0.017;
}
if(TF0==1)
{
TF0=0;
distance=255;
}
}
前几天查阅资料发现,15系列单片机有一个叫PCA的东西,其中一个模块正好再P1^1端口处,所以可以考虑利用PCA中断实现超声波测距
二.PCA介绍
手册上是这么介绍的
没错,这个PCA是可以用作定时器的,所以再CT107D上我们就有了四个定时器(定时器0,1,2,PCA),但在这里我们用他的外部脉冲捕捉(即捕获模式)那手册上说明可以用于PWM输出,那么是否可以在比赛使用呢,我觉得是可以,但由于端口是固定的,局限性很大(见下图)
1.PCA寄存器介绍(部分)
CMOD寄存器
一般设为0x01(12分频,开启计数溢出中断)
CCON寄存器:主要用于中断的设置,以及计时器的启动
初始化时全部置0即可
CCAPMx寄存器(由于CCAPM0连接了P1^1端口,所以以CCAPM0举例)
由于接收到信号时RX位下降沿,所以CAPN0设为1,同时需要开启捕获标志CCF0
2.捕获模式介绍
总结一下流程:在配置好寄存器后,打开计数标志位,在PCA捕获到信号后,计数器中CH,CL中的值会给到CCAPnH和CCAPnL中 ,通过读取这两个寄存器的值便可得到时间.
附:在PCA中,计时器溢出标志位和PCA模块标志位都会触发中断,在中断函数中注意判断是谁触发的中断并执行相应的操作.
三.示例代码
由于距离计算需要一点时间,所以我在定时器1中设置每隔大概125ms执行一次数据计算
#include <STC15F2K60S2.H>
#include "intrins.h"
sbit TX=P1^0;
sbit RX=P1^1;
code unsigned char SMG_Nodot[] =
{
0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e};
bit PCA_Flag;
unsigned int PCA_times;//时间读取
unsigned char Distance;//距离
unsigned int count=0;//计数器计数
void Measure_Distance();
void Delay12us() //@12.000MHz用于发送40khz的脉冲
{
unsigned char i;
_nop_();
_nop_();
i = 33;
while (--i);
}
void Delay_SMG(unsigned int t)
{
while(t--);
}
void SelectHC573(unsigned char channel,unsigned char dat)
{
P0=dat;
switch(channel)
{
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;
}
P2=(P2&0x1f)|0x00;
}
void Display_SMG(unsigned char pos,unsigned char dat)
{
SelectHC573(6,0x01<<(pos-1));
SelectHC573(7,dat);
Delay_SMG(500);
SelectHC573(6,0x01<<(pos-1));
SelectHC573(7,0xff);
}
void Display_Dynamic()
{
Display_SMG(6,SMG_Nodot[Distance/100]);
Display_SMG(7,SMG_Nodot[Distance%100/10]);
Display_SMG(8,SMG_Nodot[Distance%10]);
}
void Timer1_Init()
{
TH1=(65535-1000)/256;
TL1=(65535-1000)%256;
TMOD=0x16;
TR1=1;
PT1=1;
ET1=1;
EA=1;
}
void Timer1_Service() interrupt 3//每隔一定时间触发一次中断
{
TH1=(65535-1000)/256;
TL1=(65535-1000)%256;
count++;
if(count==125)
{
Measure_Distance();
count=0;
}
}
void PCA_Init()//PCA初始化
{
CMOD=0x01;
CCAPM0=0x11;
CCON=0x00;
}
void PCA_Service() interrupt 7
{
CR=0;
if(CCF0==1)//由模块标志位触发的中断,说明数据有效
{
CCF0=0;
PCA_times=(CCAP0H<<8)|CCAP0L;//读取时间
PCA_Flag=1;
}
if(CF==1)//计时器溢出中断,说明数据无效
{
CF=0;
PCA_Flag=0;
}
}
void Send_Wave()//发送40khz脉冲
{
unsigned char i;
for(i=0;i<8;i++)
{
TX=1;
Delay12us();
TX=0;
Delay12us();
}
}
void Measure_Distance()//距离计算
{
Send_Wave();
CH=0;
CL=0;
CCF0=0;
CF=0;
CR=1;
if(PCA_Flag==1)
{
Distance=PCA_times*0.017;
}
if(PCA_Flag==0)
{
Distance=255;
}
}
void Sys_Init()
{
SelectHC573(4,0xff);
SelectHC573(5,0x00);
PCA_Init();
Timer0_Init();
}
void main()
{
Sys_Init();
while(1)
{
Display_Dynamic();
}
}
作者:Hans_Rudle