这里的代码是主文件的代码,其他的头文件之前已经提供,下面的代码仅提供思路,不提供原理,如有错误请及时留言提醒。
#include <stc15f2k60s2.h>
#include <intrins.h>
#include "iic.h"
#include "onewire.h"
#include <stdio.h>
typedef unsigned int uint;
code uchar TAB[] = {0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90, 0xff, 0xc6, 0xc7, 0xc8, 0x8c};//显示1-9 灭 C L N P
uchar tab1, tab2, tab3, tab4, tab5, tab6, tab7, tab8;
uchar key, dat;
uint temp;
#define somenop() {_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();}//超声波模块延时,注意使用define后面不用加分号
uint sonic_time;
uchar distance;// distance为超声波测距距离, 用uchar定义也是可以的,定时器的局限性它最多只能测100cm左右
sbit TX = P1^0;//超声波模块发送 1为关闭 0为发送
sbit RX = P1^1;//超声波模块接收 1为关闭 0为接受
uint dat_t;
bit temp_state, sonic_state;
uchar function;//显示功能变量
bit set;//数据参数界面标志位
uint times = 2;//参数变动次数
uchar temp_set = 30, distance_set = 35;//初始 温度参数为30 距离参数为35
uchar temp_tem, distance_tem;//临时温度存放,临时距离存放
uint long_t;//长按计时
bit long_key, long_state;//长按计时完成标志位,长按开始标志位
bit output = 1;//AD输出状态
uchar res_times, res_t;//串口结束所用的中间变量
bit res_state, send_state1, send_state2, send_state3;//开始计时读取时间标志,串口发送数据指令标志位,串口发送参数参数指令标志位,错误指令标志位
bit send_state;//定时串口发送标志位
uchar PCdat[] = {"$20,24.32\r\n"};//定义发送数据的数组时,最好预先写入你所需发送的数据,可防止未定义数组位数超出
uchar RecByte[8];//清除临时接受指令数组
uchar led = 0xff;//存放led的状态
bit output_state;//AD定时判断标志位,,在定时器0中断中,定时置位输出
void delay(uint z)//延时函数
{
uint x, y;
for(x=z; x>0; x--)
for(y=845; y>0; y--);
}
void timer_int()//定时器初始,一般是手写放在这里面
{
EA = 1;
TR0 = 1;//定时器0,16位,定时1ms,定时中断
ET0 = 1;
TMOD |= 0X01;
TH0 = (65536-1000)/256;
TL0 = (65535-1000)%256;
TMOD |= 0x10;// 定时器1,16位,用来定时超声波的往返时间
TH1 = 0;
TL1 = 0;
}
void UartInit(void) //4800bps@12.000MHz
{
SCON = 0x50;
AUXR |= 0x01; //定时器2串口通讯,16位自动重载,可以在烧录程序中直接输入参数复制
AUXR |= 0x04;
T2L = 0x8F;
T2H = 0xFD;
AUXR |= 0x10;
ES = 1;// 使用串口通讯,一定要打开串口中断
}
void display()//显示函数,配合定时器0中断使用,达到循环扫描
{
static uchar i;//定义静态变量i
switch(i)
{
case 0: P2=0XC0; P0=0X01; P2=0XE0; P0=TAB[tab1]; i=1; break;
case 1: P2=0XC0; P0=0X02; P2=0XE0; P0=TAB[tab2]; i=2; break;
case 2: P2=0XC0; P0=0X04; P2=0XE0; P0=TAB[tab3]; i=3; break;
case 3: P2=0XC0; P0=0X08; P2=0XE0; P0=TAB[tab4]; i=4; break;
case 4: P2=0XC0; P0=0X10; P2=0XE0; P0=TAB[tab5]; i=5; break;
case 5: P2=0XC0; P0=0X20; P2=0XE0;
if(function==0) P0=TAB[tab6]&0x7f; else P0=TAB[tab6]; //判断是否处于温度显示界面,在数码管上增加小数点
i=6; break;
case 6: P2=0XC0; P0=0X40; P2=0XE0; P0=TAB[tab7]; i=7; break;
case 7: P2=0XC0; P0=0X80; P2=0XE0; P0=TAB[tab8]; i=0; break;
}
}
void key_scan()//四分之一 矩阵键盘扫描,常用需要理解记住
{
key = 0;//按键标志清零,每循环一次清除一次,在判断按键里面就不用再清零
long_t = 0; long_state = 0; long_key = 0;//对长按设置的中间变量复位,清零
P35 = 1; P34 = 1; P33 = 0; P32 = 0;
if((P35==0)|(P34==0))
{
P35 = 1; P34 = 1; P33 = 0; P32 = 0;//当控制的IO口不多时,可以直接位操作
delay(2);//消抖
if((P35==0)|(P34==0)) //判断纵列按键
{
if(P35==0) key = 12;
if(P34==0) key = 16;
}
P35 = 0; P34 = 0; P33 = 1; P32 = 1;
if((P33==0)|(P32==0))
{
P35 = 0; P34 = 0; P33 = 1; P32 = 1;
delay(2);//消抖
if((P33==0)|(P32==0))//判断行列按键
{
if(P33==0) key += 0;
if(P32==0) key += 1;
}
}
while((P33==0)|(P32==0))//判断抬手
{
if((key==12)|(key==13))//判断是否是S12或S23按下
{
long_state = 1; //开始长按计时,在定时器0中断中开始计时
if(long_key==1) //是否计时完成
{
if(key==12) times = 0;//S12长按 重置参数变动次数
else if(key==13) //S13长按 切换DAC输出功能
{
if(output==0) { output = 1; P2 = 0X80; led &= 0xfb; P0 = led; } //启动状态下,点亮L3
else if(output==1) { output = 0; P2 = 0X80; led |= 0x04; P0 = led; } //停止状态下,熄灭L3
}
key = 0;//使用完长按功能后,要清零,防止短按再执行一次
}
}
}
}
}
void sonic_read()//超声波测距读取
{
uchar i;
TF1 = 0; TH1 = 0; TL1 = 0;//清空定时器计数和标志位
for(i=0; i<8; i++)
{
TX = 0;//发送超声波
somenop();somenop();somenop();somenop();somenop();somenop();somenop();somenop();somenop();somenop();//小延迟
TX = 1;//关闭超声波
somenop();somenop();somenop();somenop();somenop();somenop();somenop();somenop();somenop();somenop();
}
TR1 = 1;//打开定时器,记录超声波往返时间
while((TF1==0)&(RX==1));//等待接受,或定时器溢出
TR1 = 0;//关闭定时器
if(TF1==1)//定时器溢出,没有在测量范围内
{
distance = 99;
}
else
{
sonic_time = TH1<<8;
sonic_time |= TL1;//计算传播时间,记得得定义16位int
distance = (uchar)(sonic_time*0.017); //计算往返时间
if(distance>=99) distance = 99;//设置上限,因为是两位数码管显示
}
}
void PCdat_send(uchar *p)//串口发送数据函数
{
uchar i = 0;//设置发送变量i=0
while(p[i]!=0) //判断是否结束
{
SBUF = p[i++];
while(TI==0);//等待发送
TI = 0;
}
}
void initialize()//初始化函数
{
P2=0XA0; P0=0X00; P2=0X80; P0=0XFF;//初始化,熄灭灯,关闭蜂鸣器和继电器
P2 = 0X80; led &= 0xfb; P0 = led;//初始化,应题目要求,DAC启动状态下,点亮L3
if(EEPROM_read(0x66)!=12)//初始化参数,第一遍烧录程序只执行一次,比较好的模板,可记住
{
delay(3);
EEPROM_write(0x66,12);
delay(3);
EEPROM_write(0x11,2);
}
delay(2);
times = EEPROM_read(0x11);//读取EEPROM中的 参数变动次数
delay(2);
timer_int();//定时器0,1定义
UartInit(); //定时器2定义
}
void read_dat()//定时数据读取函数
{
if(temp_state==1) { temp_state = 0; temp = temp_read(); }//定时读取温度
else if(sonic_state==1) { sonic_state = 0; sonic_read(); }//定时读取距离
else if(output_state==1)//定时判断AD输出
{
output_state = 0;
if(output==1)//判断AD输出是否启动
{
if(distance<=distance_set) AD_write(2*51);//判断实际距离是否小于等于距离参数 输出2V
else AD_write(4*51);//判断实际距离是否大于距离参数 输出2V
}
else AD_write(0.4*51); //判断是否在停止状态下,输出0.4V
}
else if(send_state==1) //定时判断是否需要串口输出
{
send_state = 0;
if(send_state1==1)
{
send_state1 = 0;//清零,只发送一次
sprintf((uchar*)PCdat,"$%0.2bd,%0.2bd.%0.2bd\r\n",distance,(uchar)(temp/100),(uchar)(temp%100));//打印输出的数据,需要调用<stdio.h>头文件
PCdat_send(PCdat);//串口输出数据
}
else if(send_state2==1)
{
send_state2 = 0;
sprintf((uchar*)PCdat,"#%0.2bd,%0.2bd\r\n",distance_set,temp_set);//打印输出的数据
PCdat_send(PCdat);//串口输出参数
}
else if(send_state3==1)
{
send_state3 = 0;
sprintf((uchar*)PCdat,"ERROR\r\n");//打印输出的数据
PCdat_send(PCdat);
}
}
}
void key_function()//按键功能函数
{
if(key==12)//短按切换按键,长按的功能在按键扫描函数里面
{
if(set==0)//判断是否处于数据界面
{
if(function==0) function = 1;//界面切换
else if(function==1) function = 2;
else if(function==2) function = 0;
}
else//判断是否处于参数界面
{
if(function==3) function = 4;//界面切换
else if(function==4) function = 3;
}
}
else if(key==13)//短按界面按键,长按的功能在按键扫描函数里面
{
if(set==0)//判断是否处于数据界面
{
set = 1; function = 3;//转到默认温度参数界面
temp_tem = temp_set; distance_tem = distance_set;//保存临时温度参数和临时距离参数,以便后面判断是否参数有改变
}
else if(set==1) //判断是否处于参数界面
{
set = 0; function = 0; //转到默认温度数据界面
if((temp_tem!=temp_set)|(distance_tem!=distance_set)) //判断参数是否发生变化
{
times++;//参数次数变动
EEPROM_write(0x11,times);//写入EEPROM
}
}
}
else if(key==16)//减按键
{
if(function==3) { if(temp_set==0) temp_set = 2; temp_set -= 2; }//减少2摄氏度,注意设置下限
else if(function==4) { if(distance_set==0) distance_set = 5; distance_set -= 5; } //减少5cm,注意设置下限
}
else if(key==17)//加按键
{
if(function==3) { temp_set += 2; if(temp_set==100) temp_set = 98; }//增加2摄氏度,注意设置上限
else if(function==4) { distance_set += 5; if(temp_set==100) temp_set = 95;}//减少5cm,注意设置上限
}
}
void dat_display()//数据显示函数
{
if(function==0)//温度显示界面
{
tab1=11; tab2=10; tab3=10; tab4=10; tab5=temp/1000; tab6=temp%1000/100; tab7=temp%100/10; tab8=temp%10;
}
else if(function==1)//距离显示界面
{
tab1=12; tab2=10; tab3=10; tab4=10; tab5=10; tab6=10; tab7=distance/10; tab8=distance%10;
}
else if(function==2)//参数变更次数显示界面
{
tab1=13; tab2=10; tab3=10;
if((times/10000==0)&(times%10000/1000==0)&(times%1000/100==0)&(times%100/10==0))//判断是否高位为零,不用的高位数码管熄灭
{ tab4=10; tab5=10; tab6=10; tab7=10; tab8=times%10; }
else if((times/10000==0)&(times%10000/1000==0)&(times%1000/100==0))
{ tab4=10; tab5=10; tab6=10; tab7=times%100/10; tab8=times%10; }
else if((times/10000==0)&(times%10000/1000==0))
{ tab4=10; tab5=10; tab6=times%1000/100; tab7=times%100/10; tab8=times%10; }
else if(times/10000==0)
{ tab4=10; tab5=times%10000/1000; tab6=times%1000/100; tab7=times%100/10; tab8=times%10; }
else
{ tab4=times/10000; tab5=times%10000/1000; tab6=times%1000/100; tab7=times%100/10; tab8=times%10; }
}
else if(function==3)//温度参数显示界面
{
tab1=14; tab2=10; tab3=10; tab4=1; tab5=10; tab6=10; tab7=temp_set/10; tab8=temp_set%10;
}
else if(function==4)//距离参数显示界面
{
tab1=14; tab2=10; tab3=10; tab4=2; tab5=10; tab6=10; tab7=distance_set/10; tab8=distance_set%10;
}
}
void main()
{
initialize();//初始化函数
while(1)
{
key_scan();//按键扫描
read_dat();
key_function();
dat_display();
}
}
void timer0_display()interrupt 1
{
TH0 = (65536-1000)/256;//定时器0装载
TL0 = (65535-1000)%256;
display(); //数码管扫描显示
dat_t++;
if(dat_t==50) temp_state = 1;
else if(dat_t==150) sonic_state = 1;
else if(dat_t==250) send_state = 1;
else if(dat_t==350)
{
dat_t = 0;
if(temp>(temp_set*100)) { P2 = 0X80; led &= 0xfe; P0 = led; }//判断温度数据是否超过温度参数,这些led状态的写入,也可以写在 定时读取函数里面 ,写在这里是方便看
else { P2 = 0X80; led |= 0x01; P0 = led; }
if(distance<distance_set) { P2 = 0X80; led &= 0xfd; P0 = led; }//判断距离数据是否小于距离参数
else { P2 = 0X80; led |= 0x02; P0 = led; }
output_state = 1;//定时AD输出标志位
}
if(long_state==1)//开始长按计时
{
if(++long_t==1000)//计时100ms = 1s
{
long_t = 0;
long_key = 1;//长按计时完成
}
}
if(res_state==1)//开始计时读取时间
{
res_t++;//计时
if(res_t==30)//计时时间超过30ms没接受到数据时,标志接受完成,开始判断指令是否正确
{
res_t = 0; //对串口接收的中间变量进行清零
res_state = 0;
res_times = 0;
if((RecByte[0]=='S')&(RecByte[1]=='T')&(RecByte[2]=='\r')&(RecByte[3]=='\n'))//判断是否 符合查询数据指令
{
send_state1 = 1;
RecByte[0] = 0; RecByte[1] = 0; RecByte[2] = 0; RecByte[3] = 0;//清除临时接受指令数组中的数据
}
else if((RecByte[0]=='P')&(RecByte[1]=='A')&(RecByte[2]=='R')&(RecByte[3]=='A')&(RecByte[4]=='\r')&(RecByte[5]=='\n'))//判断是否符合 查询参数指令
{
send_state2 = 1;
RecByte[0] = 0; RecByte[1] = 0; RecByte[2] = 0; RecByte[3] = 0; RecByte[4] = 0; RecByte[5] = 0;//清除临时接受指令数组中的数据
}
else { send_state1 = 0; send_state2 = 0; send_state3 = 1;}//判断是否接受到错误指令,标志错误指令,并清零其他的指令
}
}
}
void timer2_port()interrupt 4 // 串口接收中断函数
{
if(RI==1)
{
RI = 0; //接受标志位必须清理
RecByte[res_times++] = SBUF;//读取输入的数据
res_state = 1;//开始计时读取时间标志
res_t = 1;//计时读取数据的时间,在定时器0中断中计时
}
}