您现在的位置是:首页 >技术杂谈 >蓝桥杯单片机定时器不够用?PCA大力助你测距超声波!网站首页技术杂谈

蓝桥杯单片机定时器不够用?PCA大力助你测距超声波!

NULL指向我 2024-09-13 12:01:06
简介蓝桥杯单片机定时器不够用?PCA大力助你测距超声波!

在国赛的练习中遇到了定时器不够用的问题,也在网上有查阅到许多蓝桥杯单片机的用PCA定时器测距超声波的例子,但在移植实践运用了几个人的代码后总是各种各样的的问题不好用,因此深感有必要自己好好研究下,终于在一番摸爬中写出了用PCA计时的比较稳定准确的测距程序。

本文会简要讲解我的代码与配置步骤,最后会把整个能使用的测试代码与实验效果图全部贴上,复制编译了就能直接用!

本文研究实战问题不容易,希望看了得以解决问题的读友也大力三连支持一下!

发现BOSS问题——定时器不够用了!

在练习做第十三届国赛的时候遇到了一个比较大的问题:定时器不够用了!

第十三届国赛的题目乍一看用到的模块不是特别多,刚有几分侥幸,以为其十分简单~~

但仔细推敲发现:

NE555频率测量必定占用定时器0(因为SIGNEL要与定时器0引脚P3^4短接),

另外还需要俩个定时器分别进行PWM输出超声波测距

而我们蓝桥杯单片机开发板外部晶振定时器只有0、1、2这三个。

这样就貌似没有多余的外部定时器来给程序安排函数运行时序了!

问题通关思路——PCA定时器分担超声波测距-

但别忘了,外部晶振定时器确实只有三个了,但我们还有板载的CCP/PCA/PWM模块,其中的PCA (Programmable Counter Array可编程计数器阵列)终于有了它的用武之地了!

其实PCA的16位定时器模式与外部晶振定时器没有区别,中断优先级可能比较低,因此在使用的时候大部分操作习惯与步骤原理都是十分类似于 定时器0、1、2这类外部晶振定时器的。

因此我们可以根据定时器1 的 超声波测距程序对其进行改造,编程PCA的测距程序。

 想了解PCA的可以看我这篇文章:

蓝桥杯STC15F2K60S2单片机 CCP/PCA/PWM模块的学习与使用_NULL指向我的博客-CSDN博客

武器打磨——定时器1示例讲解超声波代码的编写原理:

由于我们文章主题是用PCA实现超声波测距,此处提到定时器1,是为了回归基础,

再之PCA定时器测距程序也是由定时器1测距程序修改而来的,因此有比对方面的需要,

但关于定时器1测距的代码,我就值贴出核心部分,就不贴出整个测试工程了(PCA会贴出整个测试工程!)

超声波的驱动我们一般分为下面俩个函数编写步骤:

1.40KHz启动信号函数

 在PCA定时器测距超声波时这个启动函数是不需要改动的

//超声波发送信号必要的10us延时
void Delay10us()
{
	unsigned char i;

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

void Send_Wave()//超声波 产生40KHx启动信号
{
	unsigned char i;
	for(i = 0; i < 10; i++)
	{
		TX = 1;
		Delay10us();	
		TX = 0;
		Delay10us();
	}
}

2.发送启动信号,开启定时器计时,计算距离函数

 这个距离计算函数的格式也是基本不需要改动的

u8 get_distance() //超声波收发计算距离函数
{
	u8 dis;
	Send_Wave();
	TR1=1;
	while(RX && !TF1);
	TR1=0;
	if(TF1){
		TF1=0;
		dis=255;
	}
	else
		dis = (TH1*256+TL1)*17/1000;
	
	TH1=0;
	TL1=0;
	return dis;
}	

武器升级——PCA定时器测距超声波:

 

在了解了定时器1的有关测距超声波的函数之后,

我们便可以在此基础上改写成PCA测距超声波了,具体步骤如下:

 1.定义返回变量

 2.清空PCA寄存器计数值

 3.初始化PCA控制寄存器/PCA定时器停止/清除CF/

 4.关闭总中断

 5.发送超声波驱动信号

 6.开启总中断

 7.PCA开始计数

 8.等待接收

 9.PCA停止计数

 10.计数器溢出处理


u8 get_distance()      //超声波收发计算距离函数
{
	u8 dis;               //定义返回变量
	CH=CL=0;				      //清空PCA寄存器计数值
	CCON=0;               //初始化PCA控制寄存器/PCA定时器停止/清除CF/
	
	EA=0;						      //关闭总中断
	Send_Wave();		      //发送超声波驱动信号
	EA=1;						      //开启总中断
	CR=1;						      //PCA开始计数
	while(RX && !CF);     //等待接收
	CR=0;									//PCA停止计数
	if(CF){CF=0;dis=255;} // 计数器溢出处理
	else dis = (CH*256+CL)*17/1000;               
	return dis;
}

通关秘籍——整体工程代码:

 全部复制编译通过下载即可:

/*  利用PCA计时驱动超声波测距
    定时器0用来450ms 驱动一次超声波
    用串口1发送手机的数据发送给上位机    
    根据定时器1测超声波改造的,比较准确好用,
    可能最终值需要 加上2~3cm 的误差*/ 

/*
	利用PCA计时驱动超声波测距
	定时器0用来450ms 驱动一次超声波
	用串口1发送手机的数据发送给上位机	
	根据定时器1测超声波改造的,比较准确好用,
	可能最终值需要 加上2~3cm 的误差
*/

#include "stc15f2k60s2.h"
#include "stdio.h"
#include "intrins.h"

typedef unsigned char u8;
typedef unsigned int u16; 
typedef int u32;

bit LCM_flag=0;
u16 LCM_cnt=0;

u32 distance=0;

//频率引脚
sbit MOTOR=P0^5;
//蜂鸣器继电器引脚:
sbit buzz=P0^6;
sbit RELAY=P0^4;
//超声波
sbit TX=P1^0;
sbit RX=P1^1;

void Timer0_Init(void);  //定时器0
void UartInit(void);		 //9600bps@11.0592MHz
void inint_port(u8 i);   //初始化74hc573芯片
u8 get_distance();       //超声波收发计算距离函数

void main()
{
	inint_port(5);buzz=0;  //关闭外设
	inint_port(5);RELAY=1;	
	inint_port(4);P0=0XFF;
	Timer0_Init();UartInit();
	
	while(1)
	{
	if(LCM_flag==1)
	{ LCM_flag=0;distance=get_distance();
		printf("distance=%d
",distance);}
	}
}

void inint_port(u8 i)//初始化74hc573芯片
{
	switch(i)
	{
		case 4:P2=(P2&0X1F)|0X80;break; //LED
		case 5:P2=(P2&0X1F)|0XA0;break; //BUZZ RELAY
		case 6:P2=(P2&0X1F)|0XC0;break; //数码管位选 
		case 7:P2=(P2&0X1F)|0XE0;break;	//数码管段选	
	}
}	

void Timer0_Init(void)//定时器0
{	
	AUXR &= 0x7F;TMOD &= 0xF0;
	TL0 = 0x18;TH0 = 0xFC;
	TF0 = 0;TR0 = 1;
	EA=1;ET0=1;	
}

void timer0_serv() interrupt 1
{
	if(++LCM_cnt==450)          //450ms一次超声波
	{LCM_cnt=0;LCM_flag=1;}
}

void UartInit(void)	 //9600bps@11.0592MHz
{
	SCON = 0x50;		   //8位数据,可变波特率
	AUXR |= 0x01;		   //串口1选择定时器2为波特率发生器
	AUXR |= 0x04;		   //定时器时钟1T模式
	T2L = 0xE0;		     //设置定时初始值
	T2H = 0xFE;		     //设置定时初始值
	AUXR |= 0x10;		   //定时器2开始计时
	REN=1;ES=1;EA=1;
}

//重定向printf();
void send_date(unsigned char date)  {SBUF = date; while(!TI); TI = 0;}
char putchar (char ch){send_date(ch);return ch;}

//串口中断服务函数
void Uart_1_serv() interrupt 4
{if(RI){RI=0;	}}

//超声波发送信号必要的10us延时
void Delay10us()
{
	unsigned char i;
	_nop_();_nop_();i = 27;
	while (--i);
}
void Send_Wave()//超声波 40KHx启动信号
{
	unsigned char i;
	for(i = 0; i < 10; i++)
	{
	 TX = 1;Delay10us();
	 TX = 0;Delay10us();
	}
}

u8 get_distance()      //超声波收发计算距离函数
{
	u8 dis;               //定义返回变量
	CH=CL=0;				      //清空PCA寄存器计数值
	CCON=0;               //初始化PCA控制寄存器/PCA定时器停止/清除CF/
	
	EA=0;						      //关闭总中断
	Send_Wave();		      //发送超声波驱动信号
	EA=1;						      //开启总中断
	CR=1;						      //PCA开始计数
	while(RX && !CF);     //等待接收
	CR=0;									//PCA停止计数
	if(CF){CF=0;dis=255;} // 计数器溢出处理
	else dis = (CH*256+CL)*17/1000;               
	return dis;
}

实验结果串口截图:

最后不知我的文章是否有解决大家的问题,解决了请大方给个三连

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。