第十五届蓝桥杯,第三次模拟赛,手把手保姆级教学,底部附答案!

一、查看配置要求

首先根据基本要求,我们先将按键设置为矩阵按键模式,以及IO模式

二、查看硬件框图设计外设

根据所给硬件框图我们可知使用到了,超声波、PCF8951等外设

三、编写底层代码

首先我们编写,按键输入、数码管、LED底层代码

根据查看原理图可知,操作按键列的针脚口分别为P44、P42、P35、P34,操作按键行的针脚口为P33、P32、P31、P30

g

1、按键检测代码如下:

.c文件代码

/*头文件*/
#include <key.h>

/**
*@函数名称 按键检测函数
*@函数功能 按键处于矩阵按键模式时,按键的按下检测
*@入口参数 无
*@返回值   对应按键键值
*/
unsigned char key_read()
{
	unsigned char temp = 0;
	P44 = 0; P42 = 1; P35 = 1; P34 = 1; 
	if(P33==0) temp = 4;
	if(P32==0) temp = 5;
	if(P31==0) temp = 6;
	if(P30==0) temp = 7;
	
	P44 = 1; P42 = 0; P35 = 1; P34 = 1; 
	if(P33==0) temp = 8;
	if(P32==0) temp = 9;
	if(P31==0) temp = 10;
	if(P30==0) temp = 11;
	
	P44 = 1; P42 = 1; P35 = 0; P34 = 1; 
	if(P33==0) temp = 12;
	if(P32==0) temp = 13;
	if(P31==0) temp = 14;
	if(P30==0) temp = 15;
	
	P44 = 1; P42 = 1; P35 = 1; P34 = 0; 
	if(P33==0) temp = 16;
	if(P32==0) temp = 17;
	if(P31==0) temp = 18;
	if(P30==0) temp = 19;
	
	return temp;
}

.h文件代码

/*芯片头文件*/
#include <STC15F2K60S2.H>

/*按键检测函数*/
unsigned char key_read();

 2、数码管显示函数如下

.c文件代码

/*头文件*/
#include <seg.h>

unsigned char seg_wela[] = {0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};//数码管位选择
                             //0   //1  //2  //3  //4  //5 //6  //7  //8 //9 //熄灭 //A                              														
unsigned char seg_dula[] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xff,0x88,
//b   //C  //d  //E //F  //P   //-
0x83,0xc6,0xa1,0x86,0x8e,0x8c,0xbf};//数码管段选择
/**
*@函数名称 数码管显示函数
*@函数功能 让指定数码管显示指定内容
*@入口参数 位选、段选择、小数点选择
*@返回值   无
*/
void seg_disp(unsigned char wela,dula,point)
{
	//数码管消影
	P0 = 0xff;
	//打开段选择写入数据后关闭
	P2 = P2 & 0x1f | 0xe0;
	P2 &= 0x1f;
	
	//数码管位选择
	P0 = seg_wela[wela];
	//打开位选择写入数据后关闭
	P2 = P2 & 0x1f | 0xc0;
	P2 &= 0x1f;
	
	//数码管段选择
	P0 = seg_dula[dula];
	//判断是否需要点亮小数点
	if(point)
		P0 &= 0x7f;
	//打开段选择写入数据后关闭
	P2 = P2 & 0x1f | 0xe0;
	P2 &= 0x1f;
	
}

.h文件代码

/*芯片头文件*/
#include <STC15F2K60S2.H>
/*数码管显示函数*/
void seg_disp(unsigned char wela,dula,point);

3、LED显示函数如下

/*头文件*/
#include <led.h>

/**
*@函数名称 LED函数
*@函数功能 点亮指定LED
*@入口参数 需要操作的LED,点亮或者关闭
*@返回值   无
*/
void led_disp(unsigned char addr,enable)
{
	static unsigned char temp = 0x00;
	static unsigned char temp_old = 0xff;
	
	if(enable)
		temp |= 0x01 << addr;
	else
		temp &= ~(0x01 << addr);
	
	if(temp != temp_old)
	{
		P0 = ~temp;
		P2 = P2 & 0x1f | 0x80;
		P2 &= 0x1f;
		temp_old = temp;
	}
}

.h文件

/*芯片头文件*/
#include <STC15F2K60S2.H>
/*LED函数*/
void led_disp(unsigned char addr,enable);

为了防止上电后LED、蜂鸣器、继电器等异常,我们还需要编写一段上电初始化函数

4、上电初始化函数

/*头文件*/
#include <init.h>
/*上电初始化函数*/
void syinit()
{
	//关闭所有LED
	P0 = 0xff;
	P2 = P2 & 0x1f | 0x80;
	P2 &= 0x1f;
	
	//关闭LED、继电器
	P0 = 0x00;
	P2 = P2 & 0x1f | 0xa0;
	P2 &= 0x1f;
}

.h文件

/*芯片头文件*/
#include <STC15F2K60S2.H>
/*初始化函数*/
void syinit();

接下来我们还需要把超声波、PCF8951等外设底层编写

因为官方没有提供超声波底层文件,我们自己编写底层.c

5、超声波距离读取函数

/*头文件声明区*/
#include <wave.h>
#include <intrins.h>

//阵脚定义
sbit Tx = P1^0;
sbit Rx = P1^1;


void Delay12us()		//@12.000MHz
{
	unsigned char i;

	_nop_();
	_nop_();
	i = 38;
	while (--i);
}

/*超声波发射生成函数*/
void wave_init()
{
	unsigned char i;
	
	for(i=0;i<8;i++)
	{
		Tx = 1;
		Delay12us();
		
		Tx = 0;
		Delay12us();
	}
	
}

/*超声波距离读取函数*/
unsigned char wave_read()
{
	unsigned int time;
	CMOD = 0x00;
	CH = CL =0;
	wave_init();
	
	CR = 1;
	while(Rx == 1 && CF == 0);
	CR = 0;
	
	if(CF==0)
	{
		time = CH << 8 | CL;
		return (time * 0.017);
	}
	else
	{
		CF = 0;
		return 0;
	}
}

.h文件

/*芯片头文件*/
#include <STC15F2K60S2.H>
/*超声波距离读取函数*/
unsigned char wave_read();

6、PCF8951 AD,DA转换底层

我们在官方提供的.c文件之上编写AD,DA转换函数

/*	#   I2C代码片段说明
	1. 	本文件夹中提供的驱动代码供参赛选手完成程序设计参考。
	2. 	参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题
		中对单片机时钟频率的要求,进行代码调试和修改。
*/

#include <iic.h>
#include <reg52.h>
#include <intrins.h>

sbit scl = P2^0;
sbit sda = P2^1;

#define DELAY_TIME	5

//
static void I2C_Delay(unsigned char n)
{
    do
    {
        _nop_();_nop_();_nop_();_nop_();_nop_();
        _nop_();_nop_();_nop_();_nop_();_nop_();
        _nop_();_nop_();_nop_();_nop_();_nop_();		
    }
    while(n--);      	
}

//
void I2CStart(void)
{
    sda = 1;
    scl = 1;
	I2C_Delay(DELAY_TIME);
    sda = 0;
	I2C_Delay(DELAY_TIME);
    scl = 0;    
}

//
void I2CStop(void)
{
    sda = 0;
    scl = 1;
	I2C_Delay(DELAY_TIME);
    sda = 1;
	I2C_Delay(DELAY_TIME);
}

//
void I2CSendByte(unsigned char byt)
{
    unsigned char i;
	
    for(i=0; i<8; i++){
        scl = 0;
		I2C_Delay(DELAY_TIME);
        if(byt & 0x80){
            sda = 1;
        }
        else{
            sda = 0;
        }
		I2C_Delay(DELAY_TIME);
        scl = 1;
        byt <<= 1;
		I2C_Delay(DELAY_TIME);
    }
	
    scl = 0;  
}

//
unsigned char I2CReceiveByte(void)
{
	unsigned char da;
	unsigned char i;
	for(i=0;i<8;i++){   
		scl = 1;
		I2C_Delay(DELAY_TIME);
		da <<= 1;
		if(sda) 
			da |= 0x01;
		scl = 0;
		I2C_Delay(DELAY_TIME);
	}
	return da;    
}

//
unsigned char I2CWaitAck(void)
{
	unsigned char ackbit;
	
    scl = 1;
	I2C_Delay(DELAY_TIME);
    ackbit = sda; 
    scl = 0;
	I2C_Delay(DELAY_TIME);
	
	return ackbit;
}

//
void I2CSendAck(unsigned char ackbit)
{
    scl = 0;
    sda = ackbit; 
	I2C_Delay(DELAY_TIME);
    scl = 1;
	I2C_Delay(DELAY_TIME);
    scl = 0; 
	sda = 1;
	I2C_Delay(DELAY_TIME);
}

//AD转换

unsigned char AD_read(unsigned char addr)
{
	unsigned char temp;
	
	I2CStart();
	I2CSendByte(0x90);
	I2CWaitAck();
	
	I2CSendByte(addr);
	I2CWaitAck();
	
	I2CStart();
	I2CSendByte(0x91);
	I2CWaitAck();
	
	temp = I2CReceiveByte();
	I2CSendAck(1);
	I2CStop();
	
	return temp;
}

//DA转换

void DA_write(unsigned char addr)
{
	I2CStart();
	I2CSendByte(0x90);
	I2CWaitAck();
	
	I2CSendByte(0x41);
	I2CWaitAck();
	
	I2CSendByte(addr);
	I2CWaitAck();
	I2CStop();
}

.h文件

#ifndef __IIC_H
#define	__IIC_H
//AD
unsigned char AD_read(unsigned char addr);
//DA
void DA_write(unsigned char addr);
#endif

四、功能的编写

/*头文件声明区*/
#include <STC15F2K60S2.H>
#include <key.h>//按键底层
#include <seg.h>//数码管底层
#include <led.h>//LED底层
#include <init.h>//初始化底层
#include <iic.h>//PCF8951底层
#include <wave.h>//超声波底层
/*变量声明区*/
unsigned char key_slow_down;//按键10ms减速变量
unsigned int  seg_slow_down;//数码管500ms减速变量
unsigned char key_val,key_ldp,key_ldi,key_old;//按键按下、松开检测变量
unsigned char seg_buf[8] = {10,10,10,10,10,10,10,10};//数码管显示数据存放数组
unsigned char seg_point[8] = {0,0,0,0,0,0,0,0};//数码管小数点显示数据存放数组
unsigned char seg_pos;//数码管,LED扫码专用变量
unsigned char ucled[8] = {0,0,0,0,0,0,0,0};//LED显示专用数组变量

unsigned char wave_r;//超声波读取储存变量 
unsigned char wave_r_old;//超声波数据变化对比值
unsigned char seg_mode;//界面切换专用变量 0-测距界面,1-参数界面,2-记录界面
unsigned char seg_nm[2] = {10,60};//下限,上限储存数组 
unsigned char seg_bj;//记录界面报警次数记录变量
unsigned char uc_100ms;//0.1s


float				  ad_r;//电压读取存储变量
unsigned int  seg_200ms;//0.2秒

bit						seg_flag;//0-按键模式,1-旋钮模式
bit 					wave_flag;//超声波测试数据稳定标志位
bit 					wave_flag1;//防止报警次数重复标志位
bit						ad_flag;//s9旋钮模式
bit						ad_flag1;//s8旋钮模式
bit 					uc_flag;//L8以0.1s闪烁标志位
/*按键处理函数*/
void key_proc()
{
	unsigned char i;
	
	//按键减速10ms
	if(key_slow_down) return;
	key_slow_down = 0;
	
	key_val = key_read();//按键值获取
	key_ldp = key_val & (key_val^key_old);//按下
	key_ldi = ~key_val & (key_val^key_old);//松开
	key_old = key_val;//对比值
	
	//按键按下检测
	switch(key_ldp)
	{
		//S4界面切换
		case 4:
			for(i=0;i<8;i++)
			{
				seg_buf[i] = 10;
			}
			if(++seg_mode == 3)
				seg_mode = 0;
		break;
		
		//按键\旋钮模式切换
		case 5:
			if(seg_mode == 1)
			{
				seg_flag ^= 1;
				ad_flag = 0;
				ad_flag1 = 0;
			}
			
			if(seg_mode == 2)
				seg_bj = 0;
		break;
			
		//下限值调整
		case 8:
			//按键模式
			ad_flag = 0;
			if(seg_mode == 1 && seg_flag == 0)
			{
				seg_nm[0] += 10;
				//判断是否超过调整范围
				if(seg_nm[0] >= 50)
					seg_nm[0] = 0;
			}
			
			//RB2旋钮模式
			if(seg_mode == 1 && seg_flag)
			{
				ad_flag1 = 1;
			}
		break;
			
		//上限值调整
		case 9:
			//按键模式
			ad_flag1 = 0;
			if(seg_mode == 1 && seg_flag == 0)
			{
				seg_nm[1] += 10;
				//判断是否超过调整范围
				if(seg_nm[1] >= 100)
					seg_nm[1] = 50;
			}
			
			//RB2旋钮模式
			if(seg_mode == 1 && seg_flag)
			{
				ad_flag = 1;
			}
		break;
			

	}
}
/*信息显示函数*/
void seg_proc()
{
	unsigned char i = 5;
	
	//数码管500ms减速
	if(seg_slow_down) return;
	seg_slow_down = 0;
	
	//超声波读取
	wave_r = wave_read();

	//RB2电压读取
	ad_r = AD_read(0x43) / 51.0;
	
	//s9旋钮模式调节上限值
	if(ad_flag)
	{
		if(ad_r >= 0 && ad_r <1.0)
		seg_nm[1] = 0;
		
		if(ad_r >= 1.0 && ad_r <2.0)
			seg_nm[1] = 10;
		
		if(ad_r >= 2.0 && ad_r <3.0)
			seg_nm[1] = 20;
		
		if(ad_r >= 3.0 && ad_r <4.0)
			seg_nm[1] = 30;
		
		if(ad_r >= 4.0 && ad_r <=5.0)
			seg_nm[1] = 40;
	}

	//s8旋钮模式调节下限值
	if(ad_flag1)
	{
		if(ad_r >= 0 && ad_r <1.0)
		seg_nm[0] = 50;
		
		if(ad_r >= 1.0 && ad_r <2.0)
			seg_nm[0] = 60;
		
		if(ad_r >= 2.0 && ad_r <3.0)
			seg_nm[0] = 70;
		
		if(ad_r >= 3.0 && ad_r <4.0)
			seg_nm[0] = 80;
		
		if(ad_r >= 4.0 && ad_r <=5.0)
			seg_nm[0] = 90;
	}
	
	//报警次数 数据保持1.5秒并且防止重复标志位为0  
	if(wave_flag && wave_flag1 == 0)
	{
		if(wave_r < seg_nm[0] || wave_r > seg_nm[1])
		{
			seg_bj++;
			wave_flag1 = 1;
		}
	}

		                               
	
	//测距界面显示
	if(seg_mode == 0)
	{
		seg_buf[0] = 11; //A
		seg_buf[5] = wave_r / 100;//百
		seg_buf[6] = wave_r / 10 % 10;//十
		seg_buf[7] = wave_r % 10;//个
		
		//数据不足0位熄灭
		while(seg_buf[i] == 0)
		{
			seg_buf[i] = 10;
			if(++i == 7) break;
		}
	}
	
	//参数界面显示
	if(seg_mode == 1)
	{
		seg_buf[0] = 17;//P
		seg_buf[1] = seg_flag == 0? 1 : 2;
		seg_buf[5] = 18;//-
		for(i=0;i<2;i++)
		{
			seg_buf[3+i*3] = seg_nm[i] / 10;
			seg_buf[4+i*3] = seg_nm[i] % 10;
		}
	} 
	
	//记录界面
	if(seg_mode == 2)
	{
		seg_buf[0] = 15;//E
		if(seg_bj <= 9)
			seg_buf[7] = seg_bj;
		else
			seg_buf[7] = 18;//-
	}
}
/*其他显示函数*/
void led_proc()
{
	unsigned char i;
	
	//L1,2,3互斥点亮
	for(i=0;i<3;i++)
	{
		ucled[i] = (i == seg_mode);
	}
	
	//L8
	if(seg_nm[0] <= wave_r && wave_r <= seg_nm[1])
		ucled[7] = 1;
	else
		ucled[7] = uc_flag ? 0 : 1;
}
/*定时器0中断初始化函数*/
void Timer0Init(void)		//1毫秒@12.000MHz
{
	AUXR &= 0x7F;		//定时器时钟12T模式
	TMOD &= 0xF0;		//设置定时器模式
	TL0 = 0x18;		//设置定时初值
	TH0 = 0xFC;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
	ET0 = 1;
	EA = 1;
}

/*定时器0中断服务函数*/
void Timer0server() interrupt 1
{
	if(++key_slow_down == 10 ) key_slow_down = 0;//按键减速程序
	if(++seg_slow_down == 500) seg_slow_down  = 0;//数码管减速程序
	if(++seg_pos == 8) seg_pos = 0;//数码管,LED扫码
	
	seg_disp(seg_pos,seg_buf[seg_pos],seg_point[seg_pos]);//数码管动态扫描
	led_disp(seg_pos,ucled[seg_pos]);//LED显示
	
	//超声波数据保持0.2秒
	if(wave_r == wave_r_old)
	{
		if(++seg_200ms == 1500)
		{
			wave_flag = 1;
			seg_200ms = 1500;
		}
	}
	else
	{
		wave_r_old = wave_r;
		seg_200ms = 0;
		wave_flag = 0;
		wave_flag1 = 0;
	}
		
	if(++uc_100ms == 100)
	{
		uc_100ms = 0;
		uc_flag ^= 1;
	}
}
/*Main*/
void main()
{
	syinit();//初始化函数
	Timer0Init();//定时器0中断初始化函数
	while(1)
	{
		key_proc();//按键处理函数
		seg_proc();//信息显示函数
		led_proc();//其他显示函数
	}
}

 创作不易!

本人才疏学浅,以上内容如有误区欢迎大家评论区帮我指正!

  • 26
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值