蓝桥杯第13届第二批结束了,很多人在考前没有准备超声波的部分,所以看起来总体比较难。
话不多说先看框图:
一共由五个部分组成,除去必考的按键、LED、数码管。考到的其实就是超声波和PCF8591。
按键部分的功能:
可能是因为考了超声波吧,所以这次题的按键部分考的就是简单的的独立按键,和第一批试题的小矩阵按键不同,可以说把总体的难度平衡了一下。
把 按键、数码管 这两个部分作为我们工程的最小框架:
看链接: 链接
S8作为界面的切换,S7作为设置参数的选择,S6是加上、下限的值,S5是减上、下限的值。
为了方便显示我们将上、下限的值扩大十倍,既从 5-50,每次加减 5.
在参数设置的说明里还有两个特殊的说明。
第一个特殊说明我们可以通过设置两个不同的参数,一个是在设置参数界面时被加减的参数,另一个是退出参数界面被赋值的参数。
第二个特殊的就是默认修改的参数是上线参数,上线参数相对应的是 set 的值。
因为需要显示3个界面,所以我们还需要添加两个界面的代码。
我们将后面两个界面先显示提示符,所以只需要修改dis[0]的内容。
连续按下按键按键8我们可以看到数码管界面的切换。
PCF8591部分:
我选择先做PCF8591,因为超声波需要涉及到DAC部分,所以先完成ADC和DAC。这部分需要用到IIC,将官方提供的IIC的代码添加到工程中。
为了不使ADC和DAC发生冲突,所以控制字直接写0x43.PCF8591解析:链接
这里RB2电压的adc的频率不宜太高,如果是放在主函数中连续的读取,会有以下几个弊端,第一影响按键的检测,第二影响dac的输出。所以在这里我推荐300ms读一次。或者是其他的时间,只要不太短就行。(换成一定频率读取后也可以放到主函数,也可以放中断)。
超声波部分:
这部分我是在比赛的前一天准备的。哈哈哈哈哈,怕什么来什么。还好是准备了。
位定义放到开头/
sbit TX=P1^0;
sbit RX=P1^1;
放到定时器初始化函数中///
CMOD = 0x08;
CCON = 0x00;
函数///
void Delay13us() //@12.000MHz
{
unsigned char i;
_nop_();
_nop_();
i = 39;
while (--i);
}
void send_wave()
{
unsigned char i=8;
do
{
TX=1;
Delay13us();
TX=0;
Delay13us();
}
while(i--);
}
void SonicDrive()
{
uint time;
send_wave();
CH = 0;
CL = 0;
CR = 1;
while((RX) && (CF==0));
CR= 0;
if(CF == 1)
{
CF = 0;
}
else
{
time = (CH * 256) + CL;
distance = (uint)((time * 0.017) / 12 );
}
}
LED闪烁:
LED的闪烁,其实具体的实现是靠闪烁的标志位的 0-1的变换 每100ms状态颠倒一次。
设计题:
main:
#include "stc15.h"
#include "iic.h"
#define FOSC 12000000L
#define uchar unsigned char
#define uint unsigned int
sbit TX=P1^0;
sbit RX=P1^1;
///
void Init();
void BTN();
void Timer();
void Delay100us();
void display_u();
void display_p();
void display_t();
void SonicDrive();
///
unsigned char Trg,Cont,flag,menu=1,set=1,add,sub;
void write_dac(uchar dat);
uchar code LedChar[]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90};
uint dac_v,distance;
uchar L1,L2,L3,L8;
bit L8_b;
uint volt_v;
uchar volt;
uchar dac_value,read_flag;
uchar set_max=45,set_min=5,set_max_1=45,set_min_1=5;
/
void main()
{
Init();
Timer();
while(1)
{
if(flag) //按键检测的标志10ms进一次
{
flag=0; //进入后清空
BTN(); //按键检测函数
if(Trg&0x08) // 判断是不是按键S8被按下
{
menu++; //按键S8被按下则进入下一个界面
if(menu==4) menu =1; //界面循环
}
else if(Trg&0x04) // 判断是不是按键S7被按下
{
set++;
if(set==3) set =1; //设置位循环
}
else if(Trg&0x02) // 判断是不是按键S5被按下
{
if(menu ==3) add++; //加的标志位,使用后会清空
}
else if(Trg&0x01) // 判断是不是按键S4被按下
{
if(menu ==3) sub++; //减的标志位,使用后会清空
}
}
//参数设置
if(menu ==3) //进入参数设置界面
{
if(set==1) //设置上限值
{
if(add) //执行加操作
{
add=0;
set_max_1=set_max_1+5;
}
if(sub) //执行减操作
{
sub=0;
set_max_1=set_max_1-5;
}
}
if(set==2)//设置下限值
{
if(add) //执行加操作
{
add=0;
set_min_1=set_min_1+5;
}
if(sub) //执行减操作
{
sub=0;
set_min_1 = set_min_1-5;
}
}
//循环 (5-50)
if(set_min_1<5) set_min_1=50;
if(set_max_1<5) set_max_1=50;
if(set_min_1>50) set_min_1=5;
if(set_max_1>50) set_max_1=5;
}
if(menu==1) //从设置界面出来后将设置的值赋给上下限,达到题目中的要求 ”退出设置界面生效“
{
set_max=set_max_1;
set_min=set_min_1;
}
adc dac
if(read_flag) //读取RB2电压值的标志位
{
read_flag =0; //清空标志位
dac_value = read_adc(0X43); //adc读取电压的同时,允许模拟输出
dac_v = dac_value*1.96; //电压转换,并扩大一百倍
}
/LED
if((set_min*10<dac_v) && (dac_v<set_max*10)) //因为ADC的值扩大了一百倍,而上下限在这设置的时候只扩大了10倍,所以这里还需要扩大十倍才能比较。
{
L8=1; //如果ADC的值在上下限之间则L8开始闪烁,同时是超声波开启连续测距的标志位
}
else //如果ADC的值在上下限之外则L8熄灭。
{
L8_b=0;
L8=0;
}
if(menu==1) //界面1 => L1 亮 L2、L3熄灭
{
L1=1;
L2=0;
L3=0;
}
else if(menu==2) //界面2 => L2 亮 L1、L3熄灭
{
L1=0;
L2=1;
L3=0;
}
else if(menu==3) //界面3 => L3 亮 L1、L2熄灭
{
L1=0;
L2=0;
L3=1;
}
if(L1&&L8_b)
{
P2=0x80;
P0=0X7E;
}
else if(L2&&L8_b)
{
P2=0x80;
P0=0X7d;
}
else if(L3&&L8_b)
{
P2=0x80;
P0=0X7b;
}
else if(L1)
{
P2=0x80;
P0=0XFE;
}
else if(L2)
{
P2=0x80;
P0=0XFD;
}
else if(L3)
{
P2=0x80;
P0=0XFb;
}
}
}
void Init()
{
P2=0X80;
P0=0XFF;
P2=0XA0;
P0=0X00;
}
void Timer()
{
AUXR |= 0x80;
TL0 = 0xCD;
TH0 = 0xD4;
EA = 1;
TR0 = 1;
ET0 = 1;
CMOD = 0x08; //超声波专用的定时器
CCON = 0x00;
}
void Timer0() interrupt 1
{
static uchar flag_cnt=0,dis_cnt=0,re_adc_cnt=0,L8_blink_cnt=0;
static uint re_dis_cnt=0;
flag_cnt++; //按键标志位的计数
if(flag_cnt==10) //周期10ms
{
flag_cnt=0;
flag=1;
}
dis_cnt++; //数码管显示的计数
if(dis_cnt==5) //周期5ms
{
dis_cnt=0;
if(menu==1) display_u();
else if(menu==2) display_p();
else if(menu==3) display_t();
}
re_adc_cnt++; //读取RB2电压的计数
if(re_adc_cnt==300); //周期300ms
{
re_adc_cnt=0;
read_flag =1;
}
if(L8==1) //L8点亮的标志位
{
L8_blink_cnt++;
if(L8_blink_cnt==100) //闪烁周期100ms
{
L8_blink_cnt=0;
L8_b=~L8_b;
}
}
if(L8==1) //超声波开启连续测距
{
re_dis_cnt++; //测距的计数
if(re_dis_cnt==300) //测距周期300ms
{
re_dis_cnt=0;
SonicDrive();
//distance=81;
if(distance>=20&&distance<=80) //对应函数
{
volt_v = 6.7*distance-34;
if(volt_v<=0) volt_v=0;
volt = (51* volt_v)/100;
write_dac(volt);
}
else if(distance<20)
{
write_dac(51);
}
else if(distance>80)
{
write_dac(255);
}
}
}
else if(L8 ==0) //如果不测距,则DAC输出0V
{
re_dis_cnt++;
if(re_dis_cnt>=300)
{
write_dac(10);
re_dis_cnt=0;
}
}
}
void Delay13us() //@12.000MHz
{
unsigned char i;
_nop_();
_nop_();
i = 39;
while (--i);
}
void send_wave()
{
unsigned char i=8;
do
{
TX=1;
Delay13us();
TX=0;
Delay13us();
}
while(i--);
}
void SonicDrive()
{
uint time;
send_wave();
CH = 0;
CL = 0;
CR = 1;
while((RX) && (CF==0));
CR= 0;
if(CF == 1)
{
CF = 0;
}
else
{
time = (CH * 256) + CL;
distance = (uint)((time * 0.017) / 12 );
}
}
void BTN()
{
unsigned char dat=P3^0xff;
Trg = dat & (dat^Cont);
Cont = dat;
}
void display_u()
{
uchar dis[8],i;
dis[0]=0Xc1;//0Xc1 0X88
dis[1]=0XFF ;
dis[2]=0XFF;
dis[3]=0XFF;
dis[4]=0XFF;
dis[5]=LedChar[dac_v/100%10]^0x80;
dis[6]=LedChar[dac_v/10%10] ;
dis[7]=LedChar[dac_v%10] ;
for(i=1;i<8;i++)
{
if(dis[i]==LedChar[0])
{
dis[i]=0xff;
}
else break;
}
for(i=0;i<8;i++){
P2&=0x1f;
P0=1<<i;
P2|=0xc0;
P2&=0x1f;
P0=dis[i];
P2|=0xe0;
Delay100us();
P0=0xff;
}
P2&=0x1f;
}
void display_p()
{
uchar dis[8],i;
dis[0]=0Xc3;
if(L8)
{
dis[1]=0xff;
dis[2]=0xff;
dis[3]=0xff;
dis[4]=0xff;
dis[5]=LedChar[distance/100%10];
dis[6]=LedChar[distance/10%10];
dis[7]=LedChar[distance%10];
}
else if(L8==0)
{
dis[1]=0xff;
dis[2]=0xff;
dis[3]=0xff;
dis[4]=0xff;
dis[5]=0X88;
dis[6]=0X88;
dis[7]=0X88;
}
for(i=5;i<8;i++)
{
if(dis[i]==LedChar[0])
{
dis[i]=0xff;
}
else break;
}
for(i=0;i<8;i++){
P2&=0x1f;
P0=1<<i;
P2|=0xc0;
P2&=0x1f;
P0=dis[i];
P2|=0xe0;
Delay100us();
P0=0xff;
}
P2&=0x1f;
}
void display_t()
{
uchar dis[8],i;
dis[0]=0X8c; //0X88
dis[1]=0XFF ;
dis[2]=0XFF;
dis[3]=LedChar[set_max_1/10%10]^0x80;
dis[4]=LedChar[set_max_1%10];
dis[5]=0XFF;
dis[6]=LedChar[set_min_1/10%10]^0x80;
dis[7]=LedChar[set_min_1%10] ;
for(i=1;i<8;i++)
{
if(dis[i]==LedChar[0])
{
dis[i]=0xff;
}
else break;
}
for(i=0;i<8;i++){
P2&=0x1f;
P0=1<<i;
P2|=0xc0;
P2&=0x1f;
P0=dis[i];
P2|=0xe0;
Delay100us();
P0=0xff;
}
P2&=0x1f;
}
void Delay100us() //@12.000MHz
{
unsigned char i, j;
i = 2;
j = 39;
do
{
while (--j);
} while (--i);
}
IIC.C
#include "iic.h"
#define DELAY_TIME 5
void IIC_Delay(unsigned char i)
{
do{_nop_();}
while(i--);
}
void IIC_Start(void)
{
SDA = 1;
SCL = 1;
IIC_Delay(DELAY_TIME);
SDA = 0;
IIC_Delay(DELAY_TIME);
SCL = 0;
}
void IIC_Stop(void)
{
SDA = 0;
SCL = 1;
IIC_Delay(DELAY_TIME);
SDA = 1;
IIC_Delay(DELAY_TIME);
}
void IIC_SendAck(bit ackbit)
{
SCL = 0;
SDA = ackbit;
IIC_Delay(DELAY_TIME);
SCL = 1;
IIC_Delay(DELAY_TIME);
SCL = 0;
SDA = 1;
IIC_Delay(DELAY_TIME);
}
bit IIC_WaitAck(void)
{
bit ackbit;
SCL = 1;
IIC_Delay(DELAY_TIME);
ackbit = SDA;
SCL = 0;
IIC_Delay(DELAY_TIME);
return ackbit;
}
void IIC_SendByte(unsigned char byt)
{
unsigned char i;
for(i=0; i<8; i++)
{
SCL = 0;
IIC_Delay(DELAY_TIME);
if(byt & 0x80) SDA = 1;
else SDA = 0;
IIC_Delay(DELAY_TIME);
SCL = 1;
byt <<= 1;
IIC_Delay(DELAY_TIME);
}
SCL = 0;
}
unsigned char IIC_RecByte(void)
{
unsigned char i, da;
for(i=0; i<8; i++)
{
SCL = 1;
IIC_Delay(DELAY_TIME);
da <<= 1;
if(SDA) da |= 1;
SCL = 0;
IIC_Delay(DELAY_TIME);
}
return da;
}
unsigned char read_adc(unsigned char dat)
{
unsigned char date;
IIC_Start();
IIC_SendByte(0x90);
IIC_WaitAck();
IIC_SendByte(dat);
IIC_WaitAck();
IIC_Start();
IIC_SendByte(0x91);
IIC_WaitAck();
date= IIC_RecByte();
IIC_Stop();
return date ;
}
void write_dac(unsigned char dat)
{
IIC_Start();
IIC_SendByte(0x90);
IIC_WaitAck();
IIC_SendByte(0x43);
IIC_WaitAck();
IIC_SendByte(dat);
IIC_WaitAck();
IIC_Stop();
}
IIC.H
#ifndef _IIC_H
#define _IIC_H
#include "stc15.h"
#include "intrins.h"
sbit SDA = P2^1;
sbit SCL = P2^0;
void IIC_Start(void);
void IIC_Stop(void);
bit IIC_WaitAck(void);
void IIC_SendAck(bit ackbit);
void IIC_SendByte(unsigned char byt);
unsigned char IIC_RecByte(void);
void write_dac(unsigned char dat);
unsigned char read_adc(unsigned char dat);
#endif
PS:省赛结束了,要开始备战国赛了,希望取得一个满意的成绩。hhhhhhh。