一、查看配置要求
首先根据基本要求,我们先将按键设置为矩阵按键模式,以及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();//其他显示函数
}
}
创作不易!
本人才疏学浅,以上内容如有误区欢迎大家评论区帮我指正!