您现在的位置是:首页 >技术杂谈 >蓝桥杯单片机定时器不够用?PCA大力助你测距超声波!网站首页技术杂谈
蓝桥杯单片机定时器不够用?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的可以看我这篇文章:
武器打磨——定时器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;
}
实验结果串口截图:
最后不知我的文章是否有解决大家的问题,解决了请大方给个三连