|
我的超声波测距仪有个设置报警距离功能,当测得的距离小于设定时蜂鸣器报警。问题是更改报警距离后,再开机,报警距离就又是初始化的值,不是更改后的值,不知道哪里出了问题。
以下是源程序:
#include <reg52.h> //调用单片机头文件
#define uchar unsigned char //无符号字符型 宏定义 变量范围0~255
#define uint unsigned int //无符号整型 宏定义 变量范围0~65535
#include <intrins.h>
sbit c_send = P1^5; //超声波发射
sbit c_recive = P3^2; //超声波接收
//这三个引脚参考资料
sbit rs=P1^0; //1602数据/命令选择引脚 H:数据 L:命令
sbit rw=P1^1; //1602读写引脚 H:数据寄存器 L:指令寄存器
sbit e =P2^5; //1602使能引脚 下降沿触发
uchar code table_num[]="0123456789abcdefg";
/***********************语音模块控制IO口的定义************************/
sbit VBUSY= P2^0;
sbit VSDA = P2^1;
sbit VCS = P2^2;
sbit VSCL = P2^6;
sbit VRST = P2^4;
uchar yujing[3];
sbit beep = P2^3; //蜂鸣器IO口定义
bit flag_300ms ;
long distance; //距离
uint set_d; //距离
bit flag_csb_juli; //超声波超出量程
uint flag_time0; //用来保存定时器0的时候的
// 按键的IO变量的定义
uchar key_can; //按键值的变量
uchar menu_1; //菜单设计的变量
uchar a_a;
#define RdCommand 0x01 //定义ISP的操作命令
#define PrgCommand 0x02
#define EraseCommand 0x03
#define Error 1
#define Ok 0
#define WaitTime 0x07 //定义CPU的等待时间
sfr ISP_DATA=0xe6; //寄存器申明
sfr ISP_ADDRH=0xe5;
sfr ISP_ADDRL=0xe4;
sfr ISP_CMD=0xe3;
sfr ISP_TRIG=0xe2;
sfr ISP_CONTR=0xe1;
/* ================ 打开 ISP,IAP 功能 ================= */
void ISP_IAP_enable(void)
{
EA = 0; /* 关中断 */
ISP_CONTR = ISP_CONTR & 0x18; /* 0001,1000 */
ISP_CONTR = ISP_CONTR | WaitTime; /* 写入硬件延时 */
ISP_CONTR = ISP_CONTR | 0x80; /* ISPEN=1 */
}
/* =============== 关闭 ISP,IAP 功能 ================== */
void ISP_IAP_disable(void)
{
ISP_CONTR = ISP_CONTR & 0x7f; /* ISPEN = 0 */
ISP_TRIG = 0x00;
EA = 1; /* 开中断 */
}
/* ================ 公用的触发代码 ==================== */
void ISPgoon(void)
{
ISP_IAP_enable(); /* 打开 ISP,IAP 功能 */
ISP_TRIG = 0x46; /* 触发ISP_IAP命令字节1 */
ISP_TRIG = 0xb9; /* 触发ISP_IAP命令字节2 */
_nop_();
}
/* ==================== 字节读 ======================== */
unsigned char byte_read(unsigned int byte_addr)
{
EA = 0;
ISP_ADDRH = (unsigned char)(byte_addr >> 8);/* 地址赋值 */
ISP_ADDRL = (unsigned char)(byte_addr & 0x00ff);
ISP_CMD = ISP_CMD & 0xf8; /* 清除低3位 */
ISP_CMD = ISP_CMD | RdCommand; /* 写入读命令 */
ISPgoon(); /* 触发执行 */
ISP_IAP_disable(); /* 关闭ISP,IAP功能 */
EA = 1;
return (ISP_DATA); /* 返回读到的数据 */
}
/* ================== 扇区擦除 ======================== */
void SectorErase(unsigned int sector_addr)
{
unsigned int iSectorAddr;
iSectorAddr = (sector_addr & 0xfe00); /* 取扇区地址 */
ISP_ADDRH = (unsigned char)(iSectorAddr >> 8);
ISP_ADDRL = 0x00;
ISP_CMD = ISP_CMD & 0xf8; /* 清空低3位 */
ISPgoon(); /* 触发执行 */
ISP_IAP_disable(); /* 关闭ISP,IAP功能 */
}
/* ==================== 字节写 ======================== */
void byte_write(unsigned int byte_addr, unsigned char original_data)
{
EA = 0;
SectorErase(byte_addr);
ISP_ADDRH = (unsigned char)(byte_addr >> 8); /* 取地址 */
ISP_ADDRL = (unsigned char)(byte_addr & 0x00ff);
ISP_CMD = ISP_CMD & 0xf8; /* 清低3位 */
ISP_DATA = original_data; /* 写入数据准备 */
ISPgoon(); /* 触发执行 */
ISP_IAP_disable(); /* 关闭IAP功能 */
EA =1;
}
/***********************1ms延时函数*****************************/
void delay_1ms(uint q)
{
uint i,j;
for(i=0;i<q;i++)
for(j=0;j<120;j++);
}
void delay_us (unsigned int us)
{
while(us--)
{
_nop_();
}
}
/***********************三线发码子程序************************/
void Send_threelines(unsigned char addr)
{
unsigned char i;
VRST=0;
delay_1ms(5);
VRST=1;
delay_1ms(20); /* 复位拉高20ms*/
VCS=0;
delay_1ms(5); /* 片选拉低5ms */
for(i=0;i<8;i++)
{
VSCL=0;
if(addr&0x01)
{
VSDA=1;
}
else
VSDA=0;
addr<<=1;
delay_us(150); /* 150us */
VSCL=1;
delay_us(150); /* 150us */
}
VCS=1;
}
/********************************************************************
* 名称 : delay_uint()
* 功能 : 小延时。
* 输入 : 无
* 输出 : 无
***********************************************************************/
void delay_uint(uint q)
{
while(q--);
}
/********************************************************************
* 名称 : write_com(uchar com)
* 功能 : 1602命令函数
* 输入 : 输入的命令值
* 输出 : 无
***********************************************************************/
void write_com(uchar com)
{
e=0;
rs=0;
rw=0;
P0=com;
delay_uint(25);
e=1;
delay_uint(100);
e=0;
}
/********************************************************************
* 名称 : write_data(uchar dat)
* 功能 : 1602写数据函数
* 输入 : 需要写入1602的数据
* 输出 : 无
***********************************************************************/
void write_data(uchar dat)
{
e=0;
rs=1;
rw=0;
P0=dat;
delay_uint(25);
e=1;
delay_uint(100);
e=0;
}
/********************************************************************
* 名称 : write_string(uchar hang,uchar add,uchar *p)
* 功能 : 改变液晶中某位的值,如果要让第一行,第五个字符开始显示"ab cd ef" ,调用该函数如下
write_string(1,5,"ab cd ef;")
* 输入 : 行,列,需要输入1602的数据
* 输出 : 无
***********************************************************************/
void write_string(uchar hang,uchar add,uchar *p)
{
if(hang==1)
write_com(0x80+add);
else
write_com(0x80+0x40+add);
while(1)
{
if(*p == '\0') break;
write_data(*p);
p++;
}
}
/***********************lcd1602上显示两位十进制数************************/
void write_sfm3(uchar hang,uchar add,uint date)
{
if(hang==1)
write_com(0x80+add);
else
write_com(0x80+0x40+add);
write_data(0x30+date/100%10);
write_data('.');
write_data(0x30+date/10%10);
write_data(0x30+date%10);
}
/********************************************************************
* 名称 : init_1602()
* 功能 : 初始化1602液晶
* 输入 : 无
* 输出 : 无
***********************************************************************/
void init_1602() //1602初始化
{
write_com(0x38);
write_com(0x0c);
write_com(0x06);
delay_uint(1000);
write_string(1,0," juli:0.00m ");
write_string(2,0," set:0.00m ");
}
/******************把数据保存到单片机内部eepom中******************/
void write_eeprom() //保存数据
{
SectorErase(0x2000);
byte_write(0x2000, set_d % 256);
byte_write(0x2001, set_d / 256);
byte_write(0x2058, a_a);
}
/******************把数据从单片机内部eepom中读出来*****************/
void read_eeprom() //读出保存数据
{
set_d = byte_read(0x2001);
set_d <<= 8;
set_d |= byte_read(0x2000);
a_a = byte_read(0x2058);
}
///**************开机自检eeprom初始化*****************/
void init_eeprom() ////开始初始化保存的数据
{
read_eeprom(); //读出保存数据
if(a_a != 15) //新的单片机初始单片机内问EEPOM
{
set_d = 50;
a_a = 15;
write_eeprom(); //保存数据
}
}
/********************独立按键程序*****************/
uchar key_can; //按键值
void key() //独立按键程序
{
static uchar key_new;
key_can = 20; //按键值还原
P3 |= 0xf0;
if((P3 & 0xf0) != 0xf0) //按键按下
{
delay_1ms(1); //按键消抖动
if(((P3 & 0xf0) != 0xf0) && (key_new == 1))
{ //确认是按键按下
key_new = 0;
switch(P3 & 0xf0)
{
//case 0xb0: key_can = 3; break; //得到k3键值
case 0xb0: key_can = 3; break;
//case 0x70: key_can = 2; break; //得到k2键值
case 0xd0: key_can = 2; break;
//case 0x30: key_can = 1; break; //得到k4键值
case 0x70: key_can = 1; break;
}
}
}
else
key_new = 1;
}
/****************按键处理显示函数***************/
void key_with()
{
if(key_can == 1) //设置键
{
menu_1 ++;
if(menu_1 >= 2)
{
menu_1 = 0;
init_1602() ; //1602初始化
write_sfm3(2,7,set_d); //显示设置的距离
}
}
if(menu_1 == 1) //设置距离报警
{
if(key_can == 2)
{
set_d ++ ; //加1
if(set_d > 500)
set_d = 500;
}
if(key_can == 3)
{
set_d -- ; //减1
if(set_d <= 1)
set_d = 1;
}
write_sfm3(2,7,set_d); //显示设置的距离
write_com(0x80+0x40+6); //将光标显示地址
write_com(0x0f); //显示光标并且闪烁
write_eeprom(); //保存数据
}
}
/**************10us延时函数***************/
void delay()
{
_nop_(); //执行一条_nop_()指令就是1us
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
}
/*********************超声波测距程序*****************************/
void send_wave()
{
c_send = 1; //10us的高电平触发
delay();
c_send = 0;
TH0 = 0; //给定时器0清零
TL0 = 0;
TR0 = 0; //关定时器0定时
while(!c_recive); //当c_recive为零时等待
TR0=1;
while(c_recive) //当c_recive为1计数并等待
{
flag_time0 = TH0 * 256 + TL0;
if((flag_time0 > 40000)) //当超声波超过测量范围时,显示3个888
{
TR0 = 0;
distance = 888;
break ;
}
else
{
flag_csb_juli = 1;
}
}
if(flag_csb_juli == 1)
{
TR0=0; //关定时器0定时
distance =flag_time0; //读出定时器0的时间
distance *= 0.017; // 0.017 = 340M / 2 = 170M = 0.017M 算出来是米
if((distance > 600)) //距离 = 速度 * 时间
{
distance = 888; //如果大于3.8m就超出超声波的量程
}
}
}
/*********************定时器0、定时器1初始化******************/
void time_init()
{
EA = 1; //开总中断
TMOD = 0X11; //定时器0、定时器1工作方式1
ET0 = 0; //关定时器0中断
TR0 = 1; //允许定时器0定时
ET1 = 1; //开定时器1中断
TR1 = 1; //允许定时器1定时
}
/****************报警函数***************/
void clock_h_l()
{
static uchar value;
if(distance <= set_d)
{
value ++; //消除实际距离在设定距离左右变化时的干扰
if(value >= 2)
{
beep = ~beep; //蜂鸣器报警
}
}
else
{
value = 0;
beep = 1; //取消报警
}
}
/***************主函数*****************/
void main()
{
static uchar value = 5;
beep = 0; //开机蜂鸣器响一下
delay_1ms(200);
P0 = P1 = P2 = P3 = 0xff; //初始化单片机IO口为高电平
init_eeprom(); //开机自检eeprom初始化
init_1602(); //1602初始化
time_init();
send_wave(); //测距离函数
write_sfm3(2,7,set_d); //显示设置的距离
while(1)
{
if(flag_300ms == 1)
{
flag_300ms = 0;
if((beep == 1))
send_wave(); //测距离函数
if(menu_1 == 0)
write_sfm3(1,7,distance); //显示距离
yujing[0] = distance % 10;
yujing[1] = distance / 10 % 10;
yujing[2] = distance / 100 % 10;
value ++;
if(value > 10) //3秒钟自动播放一次
{
value = 0;
if(distance == 888)
{
Send_threelines(0x0d); //语音播放已超出量程
}
else
{
Send_threelines(yujing[2]); //语音播放
Send_threelines(0x0b);
Send_threelines(yujing[1]);
Send_threelines(yujing[0]);
Send_threelines(0x0c);
}
}
}
key(); //按键函数
if(key_can < 10)
{
key_with(); //按键处理函数
}
}
}
/*********************定时器1中断服务程序************************/
void time1_int() interrupt 3
{
static uchar value; //定时10ms中断一次
TH1 = 0xf8;
TL1 = 0x30; //2ms
value++;
if(value >= 150)
{
value = 0;
flag_300ms = 1;
clock_h_l(); //报警函数
}
}
|
|