目录
蓝桥杯大赛历届真题
一、第九届比赛题
二、代码实现
main.c
iic.c
iic.h
前言
蓝桥杯的真题可以再官网上查到,链接放下边了,点击即可跳转到官网:
蓝桥杯大赛历届真题
突然发现官网上的题也不全,而且还有一部分是模拟题,不过也没关系,回头可以再去其他地方找找真题,分享给大家。
蓝桥杯比赛的题型目前是:客观题: 15%基于硬件平台的程序设计与调试: 85%。客观题是数电模电+单片机基础知识的选择题,这里包括往后开元代码时,就不再带着大家看了,大家可以去上边发的那个链接里找。
根据经验,省赛程序设计是大头,如果你程序设计写的很好,但是客观题写的一塌糊涂,省一也是绝对没问题的,当然参加国赛想拿好名次的话,客观题就也得重视起来了。
一、第九届比赛题
但看这个模拟题的话还算挺正规的,已经接近现在的题目要求了,不过难度的话,估计还赶不上今年省赛题目的难度。
二、代码实现
相信随着不断地训练,很快,大家也能得心应手地应对每一道赛题
main.c
#include <stc15.h>
#include <intrins.h>
#include "iic.h"
code unsigned char Seg_Table[] =
{
0xc0, //0
0xf9, //1
0xa4, //2
0xb0, //3
0x99, //4
0x92, //5
0x82, //6
0xf8, //7
0x80, //8
0x90, //9
0xFF,//熄灭 10
0xc6, //C 11
0x8e //F 12
};
unsigned char Led_Num=0xFF;
#define LED_ON(x) Led_Num&=~(0x01<<x);P0=Led_Num;P2|=0x80;P2&=0x9F;P2&=0x1F;
#define LED_OFF(x) Led_Num|=0x01<<x; P0=Led_Num;P2|=0x80;P2&=0x9F;P2&=0x1F;
#define LED_OFF_ALL() Led_Num=0xFF; P0=0xFF;P2|=0x80;P2&=0x9F;P2&=0x1F;
#define NIXIE_CHECK() P2|=0xC0;P2&=0xDF;P2&=0x1F;
#define NIXIE_ON() P2|=0xE0;P2&=0xFF;P2&=0x1F;
void Timer0_Init(void); //1毫秒@11.0592MHz
void Delay100ms(void); //@11.0592MHz
void get_key(void);//按键读取与处理函数
void show_menu(void);//显示菜单函数
void Timer1_Init(void); //1毫秒@11.0592MHz
void read_ult(void);//超声波测距,测量的信息直接存到remote内
void loaddata_at(void);//从eeprom获取存储的数据
void updata_at(void);//将数据存储到eeprom内
unsigned char Nixie_num[]={10,10,10,10,10,10,10,10};//数码管待显示的数据
unsigned char location=0;//中间变量
unsigned char key_value=0;//读取到的按键键值,重甲变量
unsigned char mod=0;//菜单模式,0:测距,1:回显,2:参数设置
unsigned int ult_t=0;//超声波来回的时间,中间变量
unsigned int remote=0; //超声波读取到的距离值
unsigned char remote_count=0;//用于记录last_remote数字的位置,取值0到3
unsigned int last_remote[4]={0,0,0,0};//记录四次超声波读取到的数据
sbit TX=P1^0;//定义超声波的引脚
sbit RX=P1^1;
unsigned char show_four_times=0;//在mod1中,显示四个数据而不影响remote_count的中间变量
unsigned char mang_qu=0;//测量盲区
bit is_led_flash=0;//led闪烁的时间标志位,为0时,且启动了led灯闪烁,则1s后被置为1
bit is_run_flash=0;//是否启动LED闪烁,0:不启动闪烁,1:启动闪烁
unsigned char run3_times=0;//记录LED灯灭两三次后停止
bit L1_is_on=0;//led闪烁的状态标志位
void main()
{
loaddata_at();//上电先从eeprom读取一次记录的数据
LED_OFF_ALL();//熄灭led灯
Timer0_Init();
Timer1_Init();
EA=1;
Delay100ms();
while(1)
{
get_key();
/*DA输出,由于输出到逻辑比较简单,这里直接写main里了*/
if(remote<=mang_qu)//距离小于盲区,输出0
write_pcf(0);
else if(remote>mang_qu&&(remote-mang_qu)<50)//距离大于盲区,输出(距离-盲区)*0.2v
write_pcf((unsigned char)((remote-mang_qu)*0.02/5*255));
else if(remote>mang_qu&&(remote-mang_qu)>50)//待输出电压高于5v,就输出5v
write_pcf(255);
/*LED等闪烁,关于led灯的,除了几个切换模式要开关灯之外,只有一个需要闪烁的地方。逻辑也比较简单,闪烁代码写在main里
开关灯的代码写在get_key()里了,因为模式都是在get_key()内切换的*/
if(is_run_flash==1&&is_led_flash==1)//is_run_flash==1表示开启led灯闪烁,闪烁三次会被置为0 停止闪烁
{
is_led_flash=0;
if(L1_is_on==0)
{
LED_ON(0);
L1_is_on=1;
}
else if(L1_is_on==1)
{
LED_OFF(0);
L1_is_on=0;
}
if(++run3_times==6)
{
run3_times=0;
LED_OFF(0);
is_run_flash=0;
}
}
show_menu();
//Delay100ms();
}
}
unsigned int count_1000=0;
void Timer0_Isr(void) interrupt 1
{
P0=0x01<<location;NIXIE_CHECK();
P0=Seg_Table[Nixie_num[location]];NIXIE_ON();
if(++location==8)
location=0;
if(is_led_flash==0)//当is_led_flash=0时,1000ms之后被置为1
{
if(++count_1000>1000)
{
is_led_flash=1;
count_1000=0;
}
}
}
void Timer0_Init(void) //1毫秒@11.0592MHz
{
AUXR |= 0x80; //定时器时钟1T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0xCD; //设置定时初始值
TH0 = 0xD4; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 = 1; //使能定时器0中断
}
void Timer1_Init(void) //1毫秒@11.0592MHz
{
AUXR |= 0x40; //定时器时钟1T模式
TMOD &= 0x0F; //设置定时器模式
TL1 = 0x00; //设置定时初始值
TH1 = 0x00; //设置定时初始值
TF1 = 0; //清除TF1标志
//TR1 = 1; //定时器1开始计时
}
void Delay100ms(void) //@11.0592MHz
{
unsigned char data i, j, k;
_nop_();
_nop_();
i = 5;
j = 52;
k = 195;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void Delay5ms(void) //@11.0592MHz
{
unsigned char data i, j;
i = 54;
j = 199;
do
{
while (--j);
} while (--i);
}
void get_key(void)
{
unsigned char key_P3;
P3=0xFF;
if(P30==0){Delay5ms();while(P30==0);Delay5ms();key_value=7;}
else if(P31==0){Delay5ms();while(P31==0);Delay5ms();key_value=6;}
else if(P32==0){Delay5ms();while(P32==0);Delay5ms();key_value=5;}
else if(P33==0){Delay5ms();while(P33==0);Delay5ms();key_value=4;}
//S4
if(key_value==4&&mod==0)//在菜单0,按下s4之后读取一次超声波并记录
{
read_ult();//读取超声波
last_remote[remote_count]=remote;//并记录
if(++remote_count==4)//更新remote_count信息
remote_count=0;
is_run_flash=1;//开启led灯闪烁
updata_at();//更新一次eeprom的数据
}
//S5
else if(key_value==5)
{
if(mod==0)//在模式0按下s5,进入模式1数据回显
{
mod=1;
LED_ON(7);
}
else if(mod==1)//在模式1按下s5,再切换回距离测量也就是模式0
{
mod=0;
LED_OFF(7);
}
}
//S7
else if(key_value==7)
{
if(mod==1)
{
if(++show_four_times==4)//记录四次之后,在循坏过来从0开始
show_four_times=0;
}
else if(mod==2)//在模式2下
{
mang_qu+=10;//按下S7,盲区加10
if(mang_qu>30)//最高不超过30
mang_qu=0;
}
}
//S6
else if(key_value==6)//按下s6
{
if(mod==0|mod==1)//如果不在模式2,则进入模式2,并开启L7
{
mod=2;
LED_ON(6);
LED_OFF(7);//如果是从模式1跳转到模式2的,也就是退出了模式1,熄灭L7
}
else if(mod==2)//如果在模式2,则进入模式0,并熄灭L7,并保存数据
{
mod=0;//进入模式1
LED_OFF(6);//熄灭L7
wirite_at(104,mang_qu);//保存数据
}
}
key_value=0;
P3=key_P3;
P4=key_P4;
}
void Delay14us(void) //@11.0592MHz
{
unsigned char data i;
_nop_();
i = 45;
while (--i);
}
void send_wave(void)
{
unsigned char i=0;
for(i=0;i<8;i++)
{
TX=1;Delay14us();
TX=0;Delay14us();
}
}
void show_menu(void)
{
unsigned char remote_count_s=0;//中间变量,用于显示上一次距离信息
/*下边的逻辑是为了显示上一次记录的距离信息。
第一次读取距离后,读取的距离存储在0位置,此时remote_count=1,上一次数据应该在3位置(也就是差了两个1)
也不知道逻辑对不对,看不懂题目的意思
*/
if(remote_count>1)
remote_count_s=remote_count-1-1;
else if(remote_count==1)
remote_count_s=3;
else if(remote_count==0)
remote_count_s=2;
if(mod==0)//模式0,显示距离
{
Nixie_num[0]=11;
Nixie_num[1]=10;
Nixie_num[2]=remote/100%10;
Nixie_num[3]=remote/10%10;
Nixie_num[4]=remote/1%10;
Nixie_num[5]=last_remote[remote_count_s]/100%10;
Nixie_num[6]=last_remote[remote_count_s]/10%10;
Nixie_num[7]=last_remote[remote_count_s]/1%10;
}
else if(mod==1)//mod=1,回显数据
{
Nixie_num[0]=show_four_times+1;
Nixie_num[1]=10;
Nixie_num[2]=10;
Nixie_num[3]=10;
Nixie_num[4]=10;
Nixie_num[5]=last_remote[show_four_times]/100%10;
Nixie_num[6]=last_remote[show_four_times]/10%10;
Nixie_num[7]=last_remote[show_four_times]/1%10;
}
else if(mod==2)//mod=2显示盲区
{
Nixie_num[0]=12;
Nixie_num[1]=10;
Nixie_num[2]=10;
Nixie_num[3]=10;
Nixie_num[4]=10;
Nixie_num[5]=10;
Nixie_num[6]=mang_qu/10%10;
Nixie_num[7]=mang_qu/1%10;
}
}
void read_ult(void)//读取超声波函数
{
send_wave();//发送8个40KHz的超声波
TR1=1;//开始计时
while((RX==1)&&(TF1==0));
TR1=0;//停止计时
if(TF1==1)//如果是定时器溢出
{
ult_t=0;//则记为0(没取到信息)
TF1=0;
}
else//如果接收到数据了
{
//就获取定时器计数
ult_t=TH1;
ult_t<<=8;
ult_t|=TL1;
}
remote=(unsigned int)(ult_t*0.001537);//从而计算出距离信息
ult_t=0;
TH1=0;
TL1=0;
TF1=0;
}
void loaddata_at(void)//存储数据
{
//关于盲区数据的存储放在“退出模式2”的按键功能内了,因为题目要求退出模式2时储存数据
last_remote[0]=read_at(100);
Delay100ms();//注意不能连续读写eeprom,会读取或者写入失败
last_remote[1]=read_at(101);
Delay100ms();
last_remote[2]=read_at(102);
Delay100ms();
last_remote[3]=read_at(103);
Delay100ms();
mang_qu=read_at(104);
}
void updata_at(void)
{
wirite_at(100,last_remote[0]);
Delay100ms();
wirite_at(101,last_remote[1]);
Delay100ms();
wirite_at(102,last_remote[2]);
Delay100ms();
wirite_at(103,last_remote[3]);
}
iic.c
/* # I2C代码片段说明
1. 本文件夹中提供的驱动代码供参赛选手完成程序设计参考。
2. 参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题
中对单片机时钟频率的要求,进行代码调试和修改。
*/
#define DELAY_TIME 5
#include <stc15.h>
#include <intrins.h>
#include "iic.h"
sbit sda=P2^1;
sbit scl=P2^0;
//
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);
}
void write_pcf(unsigned char dat)
{
I2CStart();
I2CSendByte(0x90);
I2CWaitAck();
I2CSendByte(0x40);
I2CWaitAck();
I2CSendByte(dat);
I2CWaitAck();
I2CStop();
}
/*本次题目没有用到AD转化,只有DA输出,所以把AD转化的代码注释掉了(注意iic.h内也注释掉了)*/
//unsigned char read_pcf(unsigned char add)
//{
// unsigned char ad=0;
// I2CStart();
// I2CSendByte(0x90);
// I2CWaitAck();
// I2CSendByte(add);
// I2CWaitAck();
// I2CStop();
//
// I2CStart();
// I2CSendByte(0x91);
// I2CWaitAck();
// ad=I2CReceiveByte();
// I2CSendAck(1);
// I2CStop();
//
// return ad;
//}
void wirite_at(unsigned char add,dat)
{
I2CStart();
I2CSendByte(0xA0);
I2CWaitAck();
I2CSendByte(add);
I2CWaitAck();
I2CSendByte(dat);
I2CWaitAck();
I2CStop();
}
unsigned char read_at(unsigned char add)
{
unsigned char at=0;
I2CStart();
I2CSendByte(0xA0);
I2CWaitAck();
I2CSendByte(add);
I2CWaitAck();
I2CStop();
I2CStart();
I2CSendByte(0xA1);
I2CWaitAck();
at=I2CReceiveByte();
I2CSendAck(1);
I2CStop();
return at;
}
iic.h
#ifndef _IIC_H_
#define _IIC_H_
void write_pcf(unsigned char dat);
//unsigned char read_pcf(unsigned char add);
void wirite_at(unsigned char add,dat);
unsigned char read_at(unsigned char add);
#endif