蓝桥杯单片机设计与开发决赛-第十届

这篇博客介绍了一个基于STC15F2K60S2单片机的嵌入式系统设计,涵盖了IIC、1-Wire协议和超声波测距的应用。代码实现包括了定时器中断、串口通信和LCD显示功能,同时实现了温度读取、距离测量和参数设置。此外,系统还具备按键交互、AD转换控制以及串口数据发送等功能,适用于实时监测和控制应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这里的代码是主文件的代码,其他的头文件之前已经提供,下面的代码仅提供思路,不提供原理,如有错误请及时留言提醒。

#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中断中计时
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值