1.优化分析
昨天我在看原理图的发现超声波模块的反馈引脚P11刚好可以使用PCA模块0的捕获功能,我就想着把PCA功能留给超声波,然后测频功能还是改成定时器0来完成,然后前后台功能改成定时器1。
至于我为什么要这么改呢,看一下我原来封装的超声波代码就知道了,下图高亮部分,如果一直没有接收到反馈信号,程序就会一直等待65ms,严重吃MCU资源,而且会导致数码管闪动。如果换成PCA模块捕获功能来做的话,我们只需初始化一下,然后在每个下降沿捕获中断里面获取距离就行,这大大提高了代码效率!
2.代码示例
STC15使用CCP功能驱动超声波模块
#include "wave.h"
#define Wave_TX P10 //超声波发送引脚
#define Wave_RX P11 //超声波信号接收反馈引脚
float Distance=0; //单位:cm
void Send_Vave() //发出超声波脉冲
{
u8 i=8;
while(i--)
{
Wave_TX=1;
_nop_();_nop_();_nop_();
Wave_TX=0;
_nop_();_nop_();_nop_();
}
}
void Wave_Init()
{
P_SW1 &=0XCF; //清除CCP_S1,CCP_S0位
P_SW1 |=0X00; //CCP在P11/CCP0
CCON = 0; //清除CF标志 PCA定时器停止 清除模块0/1/2中断标志
CL = 0;CH = 0; //复位PCA计数值
CCAP0L = 0;CCAP0H = 0; //清除捕获值
CMOD = 0x01; //设置PCA时钟源:系统时钟/12,允许PCA溢出中断
CCAPM0 = 0x11; //PCA模块0允许下降沿捕获,开捕获中断
CR = 1; //启动PCA计数器阵列计数
EA = 1; //开总中断
}
void PCA_isr() interrupt 7
{
static u8 count=0;
if (CF){ //每65.536ms发生溢出中断
CF = 0;
CR=0;
CL = 0;CH = 0; //复位PCA计数值
CCAP0L = 0;CCAP0H = 0; //清除捕获值
if(++count==8) count=0;
Send_Vave();
CR=1; //启动PCA计数阵列
}
if (CCF0) //发生下降沿捕获中断
{
CCF0 = 0;
if(count==4) //65*8ms采样一次,防止数值频繁变动
Distance = (CCAP0H<<8 | CCAP0L)*0.017; //返回距离
}
}
定时器0外部脉冲输入测频
#include "capture.h"
u8 Overflow=0;
//定时器0对P34输入脉冲计数
void Capture_Init()
{
TMOD |= 0x04; //定时器0外部脉冲计数,16自动重装载
TL0 = 0x00; //设置自动重装载值
TH0 = 0x00;
TF0 = 0; //清除TF0标志
ET0 = 1;
EA = 1;
TR0 = 1; //定时器0开始计时
}
void Timer0_Isr(void) interrupt 1
{
Overflow++;
}
//每1S获取一次计数值,即频率
u32 Get_Frequency()
{
u32 count = (Overflow<<16) | (TH0<<8) |TL0;
TR0=0; //先关定时器再清零
TH0 = 0;
TL0 = 0;
Overflow=0;
TR0=1;
return count; //返回计数值(频率),单位HZ
}
定时器1前后台
#include "main.h"
bit KeyScan_Flag=0;
u32 frequency=0;
extern float Distance; //单位:cm
void System_Init(void);
void Timer1_Init(void);
void main()
{
System_Init();
Timer1_Init();
Capture_Init();
Wave_Init();
while(1)
{
if(KeyScan_Flag){ //50HZ
Keys_Scan();
KeyScan_Flag=0;
}
//前四位显示超声波获取距离
Nixie_Display(1,((u8)Distance%100)/10);
Nixie_Display(2,(u8)Distance%10);
Nixie_Display(2,16); //小数点
Nixie_Display(3,(u8)(Distance*10)%10);
Nixie_Display(4,(u8)(Distance*100)%10);
//后四位显示频率
Nixie_Display(5,(frequency%10000)/1000);
Nixie_Display(6,(frequency%1000)/100);
Nixie_Display(7,(frequency%100)/10);
Nixie_Display(8,frequency%10);
}
}
void Timer1_Isr(void) interrupt 3 //1ms中断一次
{
static u8 count1=0,count2=0;
static u16 counter3=0;
if(++count1==20){ //20ms扫描一次按键
KeyScan_Flag=1;
count1=0;
}
if(++count2==250){ //LED8一秒闪四次
P12=!P12;
Set_Leds(8,P12);
count2=0;
}
if(++counter3==1000)
{
frequency = Get_Frequency();
counter3=0;
}
}
void Timer1_Init(void) //1毫秒@12.000MHz
{
AUXR &= 0xBF;
TMOD &= 0x0F;
TL1 = 0x18;
TH1 = 0xFC;
TF1 = 0;
ET1 = 1;
EA = 1;
TR1 = 1;
}
void System_Init()//系统上电初始化
{
//先锁存蜂鸣器,继电器所在573输出低电平,防止上电乱叫
P25=1;P26=0;P27=1; //74HC138-->Y5=0,else=1-->Y5C=1,else=0
P04=0;P06=0; //ULN2003输入经过非门送入达林顿管,低电平有效
P25=0;P26=0;P27=0;//锁存数据
//关闭所有LED灯
P25=0;P26=0;P27=1; //74HC138-->Y4=0,else=1-->Y4C=1,else=0
P0=0XFF;
P25=0;P26=0;P27=0;//锁存数据
}