蓝桥杯单片机 超声波模块和PCA模块

本文介绍了蓝桥杯单片机比赛中超声波模块和PCA模块的工作原理。超声波测量利用超声波的反射计算距离,PCA模块则通过硬件计时和电平捕获实现精确计时。文中提供了PCA定时器的配置方法以及超声波测量的参考代码。

原理

超声波测量的原理

超声波由一个超声波模块向前方发出,并在空气中传播,在遇到障碍物时发生反射,再被另一个超声波模块接收。单片机用定时器测量超声波从发出到接收的时长,再根据声速,即可算出超声波模块到障碍物之间的距离。公式:距离=(声速 × Δ时间)÷2 。若距离单位为厘米,时间单位为微秒,距离公式为距离=Δ时间 x 0.017

另外,对距离进行微分等其他数学运算,还可以得到速度等其他测量值。测速公式:速度= (本次测量距离 - 上次测量距离 )÷两次测量时间间隔

CT107D竞赛板为例,控制单片机在P10输出一段38~41kHz的方波,八个周期,发射模块就会产生一段超声波。当接收模块接收到超声波,则会在P11产生一个下降沿。需测量超声波发出后,到下降沿产生之间的时间长度。下图为示波器测量的P10 P11的波形图。
在这里插入图片描述

PCA模块的原理

目前蓝桥杯单片机MCU型号为IAP15F2K61S2,它有一个PCA模块可以用来实现定时、测量脉宽、PWM等功能。在使用PCA模块时,注意加载STC15F2K60S2.H的头文件来定义特殊功能寄存器。

使用PCA模块驱动超声波的优点就在于,它由硬件控制计时,并可以自动捕获电平的变化,装载计数的值同时产生中断。CPU可以在控制发射超声波后,空闲出来进行数码管显示等其他任务。
在这里插入图片描述
关于PCA定时器的计数频率,可以选择SYSclk/12,这样时间单位才为微秒,因此CMOD寄存器应该设置为0x01(不要以图示给出的顺序配置CPSn位,请参考数据手册关于CMOD的说明,来配置寄存器)。
PCA1
对于超声波模块对应的P11引脚,要使PCA工作在下降沿捕获模式,须将CAPN0置位,将CAPP0清零。发送超声波后,先将阵列寄存器(CL CH)和中断标志(CCF0 CF)清零,再开启PCA模块计时及其中断。当P11产生下降沿时,阵列寄存器(CLCH)的值装载到模块的捕获寄存器(CCAP0LCCAP0H)中,并产生中断。

中断处理时要判断是下降沿捕获(CCF0==1)还是定时器溢出(CF==1),注意清零中断标志、关闭PCA定时器及其中断标志。

代码

PCA定时器的范例程序

STC-ISP上可以直接获取PCA定时器的范例程序,在“范例程序 > STC15Fxx/STC15Lxx/STC15Wxx Series > PCA的16位捕获测量脉宽 > C”。或从STC15系列单片机用户手册,第11章11.9节获得范例程序。

选手可在比赛中参考该代码,编写超声波驱动。范例程序如下。

/*---------------------------------------------------------------------*/
/* --- STC MCU Limited ------------------------------------------------*/
/* --- STC15F4K60S4 系列 PCA实现16位捕获举例---------------------------*/
/* --- Mobile: (86)13922805190 ----------------------------------------*/
/* --- Fax: 86-0513-55012956,55012947,55012969 ------------------------*/
/* --- Tel: 86-0513-55012928,55012929,55012966-------------------------*/
/* --- Web: www.STCMCU.com --------------------------------------------*/
/* --- Web: www.GXWMCU.com --------------------------------------------*/
/* 如果要在程序中使用此代码,请在程序中注明使用了STC的资料及程序        */
/* 如果要在文章中应用此代码,请在文章中注明使用了STC的资料及程序        */
/*---------------------------------------------------------------------*/

//本示例在Keil开发环境下请选择Intel的8058芯片型号进行编译
//若无特别说明,工作频率一般为11.0592MHz


#include "reg51.h"
#include "intrins.h"

#define FOSC    11059200L

typedef unsigned char BYTE;
typedef unsigned int WORD;
typedef unsigned long DWORD;

sfr P0M1 = 0x93;
sfr P0M0 = 0x94;
sfr P1M1 = 0x91;
sfr P1M0 = 0x92;
sfr P2M1 = 0x95;
sfr P2M0 = 0x96;
sfr P3M1 = 0xb1;
sfr P3M0 = 0xb2;
sfr P4M1 = 0xb3;
sfr P4M0 = 0xb4;
sfr P5M1 = 0xC9;
sfr P5M0 = 0xCA;
sfr P6M1 = 0xCB;
sfr P6M0 = 0xCC;
sfr P7M1 = 0xE1;
sfr P7M0 = 0xE2;

sfr P_SW1       = 0xA2;             //外设功能切换寄存器1

#define CCP_S0 0x10                 //P_SW1.4
#define CCP_S1 0x20                 //P_SW1.5

sfr CCON        =   0xD8;           //PCA控制寄存器
sbit CCF0       =   CCON^0;         //PCA模块0中断标志
sbit CCF1       =   CCON^1;         //PCA模块1中断标志
sbit CR         =   CCON^6;         //PCA定时器运行控制位
sbit CF         =   CCON^7;         //PCA定时器溢出标志
sfr CMOD        =   0xD9;           //PCA模式寄存器
sfr CL          =   0xE9;           //PCA定时器低字节
sfr CH          =   0xF9;           //PCA定时器高字节
sfr CCAPM0      =   0xDA;           //PCA模块0模式寄存器
sfr CCAP0L      =   0xEA;           //PCA模块0捕获寄存器 LOW
sfr CCAP0H      =   0xFA;           //PCA模块0捕获寄存器 HIGH
sfr CCAPM1      =   0xDB;           //PCA模块1模式寄存器
sfr CCAP1L      =   0xEB;           //PCA模块1捕获寄存器 LOW
sfr CCAP1H      =   0xFB;           //PCA模块1捕获寄存器 HIGH
sfr CCAPM2      =   0xDC;           //PCA模块2模式寄存器
sfr CCAP2L      =   0xEC;           //PCA模块2捕获寄存器 LOW
sfr CCAP2H      =   0xFC;           //PCA模块2捕获寄存器 HIGH
sfr PCA_PWM0    =   0xf2;           //PCA模块0的PWM寄存器
sfr PCA_PWM1    =   0xf3;           //PCA模块1的PWM寄存器
sfr PCA_PWM2    =   0xf4;           //PCA模块2的PWM寄存器

BYTE cnt;                           //存储PCA计时溢出次数
DWORD count0;                       //记录上一次的捕获值
DWORD count1;                       //记录本次的捕获值
DWORD length;                       //存储信号的时间长度(count1 - count0)

void main()
{
    P0M0 = 0x00;
    P0M1 = 0x00;
    P1M0 = 0x00;
    P1M1 = 0x00;
    P2M0 = 0x00;
    P2M1 = 0x00;
    P3M0 = 0x00;
    P3M1 = 0x00;
    P4M0 = 0x00;
    P4M1 = 0x00;
    P5M0 = 0x00;
    P5M1 = 0x00;
    P6M0 = 0x00;
    P6M1 = 0x00;
    P7M0 = 0x00;
    P7M1 = 0x00;

    ACC = P_SW1;
    ACC &= ~(CCP_S0 | CCP_S1);      //CCP_S0=0 CCP_S1=0
    P_SW1 = ACC;                    //(P1.2/ECI, P1.1/CCP0, P1.0/CCP1, P3.7/CCP2)
    
//  ACC = P_SW1;
//  ACC &= ~(CCP_S0 | CCP_S1);      //CCP_S0=1 CCP_S1=0
//  ACC |= CCP_S0;                  //(P3.4/ECI_2, P3.5/CCP0_2, P3.6/CCP1_2, P3.7/CCP2_2)
//  P_SW1 = ACC;  
//  
//  ACC = P_SW1;
//  ACC &= ~(CCP_S0 | CCP_S1);      //CCP_S0=0 CCP_S1=1
//  ACC |= CCP_S1;                  //(P2.4/ECI_3, P2.5/CCP0_3, P2.6/CCP1_3, P2.7/CCP2_3)
//  P_SW1 = ACC;  

    CCON = 0;                       //初始化PCA控制寄存器
                                    //PCA定时器停止
                                    //清除CF标志
                                    //清除模块中断标志
    CL = 0;                         //复位PCA寄存器
    CH = 0;
    CCAP0L = 0;
    CCAP0H = 0;
    CMOD = 0x09;                    //设置PCA时钟源为系统时钟,且使能PCA计时溢出中断
    CCAPM0 = 0x21;                  //PCA模块0为16位捕获模式(上升沿捕获,可测从高电平开始的整个周期),且产生捕获中断
//  CCAPM0 = 0x11;                  //PCA模块0为16位捕获模式(下降沿捕获,可测从低电平开始的整个周期),且产生捕获中断
//  CCAPM0 = 0x31;                  //PCA模块0为16位捕获模式(上升沿/下降沿捕获,可测高电平或者低电平宽度),且产生捕获中断

    CR = 1;                         //PCA定时器开始工作
    EA = 1;

    cnt = 0;
    count0 = 0;
    count1 = 0;

    while (1);
}

void PCA_isr() interrupt 7
{
    if (CCF0)
    {
        CCF0 = 0;
        if (CF && ((CCAP0H & 0x80) == 0))
        {
            CF = 0;
            cnt++;
        }
        count0 = count1;            //备份上一次的捕获值
        ((BYTE *)&count1)[3] = CCAP0L;  //保存本次的捕获值
        ((BYTE *)&count1)[2] = CCAP0H;
        ((BYTE *)&count1)[1] = cnt;
        ((BYTE *)&count1)[0] = 0;
        length = count1 - count0;   //计算两次捕获的差值,即得到时间长度
        ((BYTE *)&length)[0] = 0;
    }
    if (CF)
    {
        CF = 0;
        cnt++;                      //PCA计时溢出次数+1
    }
}

超声波测量的参考代码

对上述范例程序进行改写,同时还须参照数据手册配置寄存器。
实际测试达到了270cm,与传感器的精度有关。

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

#ifndef u8
#define u8 unsigned char
#endif

#ifndef u16
#define u16 unsigned int
#endif

#ifndef u32
#define u32 unsigned long
#endif

//定义超声波模块引脚
sbit Trig = P1^0;	//发送端
sbit Echo = P1^1;	//接收端

u8 code font[10]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};
u8 code y4=0x80,y5=0xa0,y6=0xc0,y7=0xe0;
u8 dis[8]={0xc7,0xff,0xff,0xff,0xff,0xff,0xff,0xff};

bit trig_sign=1,echo_sign=0,time_out_sign=0;
u16 trig_cnt=1000;
u16 len,len_t;

void PCA_init();
void trig_len();
void echo_len();

void dis_smg();
/********************延时函数********************/
void delay100us()		//@12.000MHz
{
	unsigned char i, j;

	i = 2;
	j = 39;
	do
	{
		while (--j);
	} while (--i);
}
void delay12us()		//@12.000MHz
{
	unsigned char i;

	_nop_();
	_nop_();
	i = 33;
	while (--i);
}
/********************初始化函数******************/
void PCA_init(){
	P_SW1 &= 0xcf;	//(P1.2/ECI, P1.1/CCP0, P1.0/CCP1, P3.7/CCP2)
	CCON = 0;       //初始化PCA控制寄存器
					//PCA定时器停止
					//清除溢出中断标志
					//清除捕获中断标志
	CL = 0;         //清零阵列寄存器
	CH = 0;
	CMOD = 0x01;    //设置PCA时钟源为SYSclk/12,允许溢出中断
	CCAPM0 = 0x10;  //PCA模块0为下降沿触发,关闭中断。
}

void Timer0Init(void)		//1毫秒@12.000MHz
{
	AUXR &= 0x7F;			//定时器时钟12T模式
	TMOD &= 0xF0;			//设置定时器模式
	TL0 = 0x18;				//设置定时初值
	TH0 = 0xFC;				//设置定时初值
	TF0 = 0;				//清除中断标志
	TR0 = 1;				//定时器0开始计时
	
	ET0 = 1;				//使能定时器0中断
}
/****************中断处理函数********************/
void PCA_isr() interrupt 7		//PCA中断处理函数
{	
	//捕获成功
	if (CCF0){
		len_t = (CCAP0H<<8)|CCAP0L;		//保存本次的捕获值
		echo_sign = 1;
		CR = 0;							//PCA定时器停止工作
		CCAPM0 &= 0xfe;					//关闭中断
	}
	//超时
	else if (CF){
		time_out_sign = 1;
		CR = 0;							//PCA定时器停止工作
		CCAPM0 &= 0xfe;					//关闭中断
	}
	CCF0 = 0;							//清理中断标志
	CF = 0;
}

void T0_isr() interrupt 1		//T0中断处理函数,每1000ms发射一次超声波
{
	if(--trig_cnt == 0) {
		trig_cnt = 1000;
		trig_sign = 1;
	}
}
/*********************主函数*********************/
void main() {
	Trig = 0;
	Timer0Init();
	PCA_init();	 
	EA = 1;
	while(1) {
		dis_smg();
		if(trig_sign) trig_len();
		if(echo_sign | time_out_sign) echo_len();
	}
}

/*************************************************
*函数:trig_len()
*功能:发射超声波,开启PCA计时及中断
*************************************************/
void trig_len() {
	u8 i=8;
	
	//产生八个周期7kHz方波信号
	while(i--){
		Trig = 1;
		delay12us();
		Trig = 0;
		delay12us();
	}
	CL = 0;							//计时器清零
	CH = 0;
	CCF0 = 0;						//清标志(开启前必须清标志)
	CF = 0;
	CCAPM0 |= 0x01;					//开启中断
	CR = 1;							//PCA定时器开始工作
	
	trig_sign = 0;
}

/*************************************************
*函数:echo_len()
*功能:计算距离,产生数码管字模
*************************************************/
void echo_len() {
	u8 i;
	
	if(echo_sign){
		//计算距离
		len=len_t *0.017 *10;	//保留1位小数
		//生成字模
		dis[3]=font[len/10000];
		dis[4]=font[len/1000%10];
		dis[5]=font[len/100%10];
		dis[6]=font[len/10%10]&0x7f;
		dis[7]=font[len%10];
		//消零
		for(i=3;dis[i]==font[0];i++) dis[i]=0xff;
	}else if(time_out_sign){
		//超时设置
		len=9999;
		dis[3]=font[9];
		dis[4]=font[9];
		dis[5]=font[9];
		dis[6]=font[9]&0x7f;
		dis[7]=font[9];
	}
	time_out_sign = 0;
	echo_sign = 0;
}

/*************************************************
*函数:dis_smg()
*功能:数码管显示函数
*************************************************/
void dis_smg() {
	u8 i;

	for(i=0;i<8;i++){
		P2&=0x1f;
		P0=1<<i;
		P2|=y6;
		P2&=0x1f;
		P0=dis[i];
		P2|=y7;
		delay100us();
		P0=0xff;
	}
}
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值