大家好,今天给大家分享第十二届蓝桥杯单片机省赛的程序题;我们来先看题目:

题目的第一页内容只有一些注意事项,在考试时注意一下就行,这一页我们可以知道这道题所用到的模块有DS18B20、DA输出、按键输入、LED指示灯,那么我们就先把官方给的DS18B20和DA输出模块的程序给他补充完整。
这个是I2C.C的参考代码:
#include <reg52.h>
#include "intrins.h"
#include "iic.h"
#define somenop {_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();}
#define SlaveAddrW 0xA0
#define SlaveAddrR 0xA1
//总线引脚定义
sbit SDA = P2^1; /* 数据线 */
sbit SCL = P2^0; /* 时钟线 */
void DA_write(uchar dat)
{
IIC_Start();
IIC_SendByte(0x90);
IIC_WaitAck();
IIC_SendByte(0x40);
IIC_WaitAck();
IIC_SendByte(dat);
IIC_WaitAck();
IIC_Stop();
}
//总线启动条件
void IIC_Start(void)
{
SDA = 1;
SCL = 1;
somenop;
SDA = 0;
somenop;
SCL = 0;
}
//总线停止条件
void IIC_Stop(void)
{
SDA = 0;
SCL = 1;
somenop;
SDA = 1;
}
//等待应答
bit IIC_WaitAck(void)
{
SDA = 1;
somenop;
SCL = 1;
somenop;
if(SDA)
{
SCL = 0;
IIC_Stop();
return 0;
}
else
{
SCL = 0;
return 1;
}
}
//通过I2C总线发送数据
void IIC_SendByte(unsigned char byt)
{
unsigned char i;
for(i=0;i<8;i++)
{
if(byt&0x80)
{
SDA = 1;
}
else
{
SDA = 0;
}
somenop;
SCL = 1;
byt <<= 1;
somenop;
SCL = 0;
}
}
//从I2C总线上接收数据
unsigned char IIC_RecByte(void)
{
unsigned char da;
unsigned char i;
for(i=0;i<8;i++)
{
SCL = 1;
somenop;
da <<= 1;
if(SDA)
da |= 0x01;
SCL = 0;
somenop;
}
return da;
}I2C.H的参考代码:
#ifndef _IIC_H #define _IIC_H #define uchar unsigned char #define uint unsigned int //函数声明 void IIC_Start(void); void IIC_Stop(void); void IIC_SendByte(unsigned char byt); bit IIC_WaitAck(void); unsigned char IIC_RecByte(void); void DA_write(uchar dat); #endif
这个是温度传感器的参考代码:
#include "onewire.h"
/****************************单总线延时函数****************************/
void Delay_OneWire(unsigned int t)
{
unsigned char aa;
while(t--)
{
for(aa=0;aa<10;aa++);
}
}
/****************************DS18B20芯片初始化****************************/
bit Init_DS18B20(void)
{
bit initflag = 0;
DQ = 1;
Delay_OneWire(12);
DQ = 0;
Delay_OneWire(80);
DQ = 1;
Delay_OneWire(10);
initflag = DQ;
Delay_OneWire(5);
return initflag;
}
/****************************通过单总线向DS18B20写一个字节****************************/
void Write_DS18B20(unsigned char dat)
{
unsigned char i;
for(i=0;i<8;i++)
{
DQ = 0;
DQ = dat&0x01;
Delay_OneWire(5);
DQ = 1;
dat >>= 1;
}
Delay_OneWire(5);
}
/****************************从DS18B20读取一个字节****************************/
unsigned char Read_DS18B20(void)
{
unsigned char i;
unsigned char dat;
for(i=0;i<8;i++)
{
DQ = 0;
dat >>= 1;
DQ = 1;
if(DQ)
{
dat |= 0x80;
}
Delay_OneWire(5);
}
return dat;
}
/****************************温度读取处理函数****************************/
unsigned char Tempget()
{
unsigned int low,high,temp;
Init_DS18B20();
Write_DS18B20(0xcc);
Write_DS18B20(0x44);
Delay_OneWire(20);
Init_DS18B20();
Write_DS18B20(0xcc);
Write_DS18B20(0xbe);
low=Read_DS18B20();
high=Read_DS18B20();
temp=high<<8;
temp=temp | low;
return temp; //精确到了小数点后一位
}温度传感器代码的头文件:
#ifndef _ONEWIRE_H #define _ONEWIRE_H #include "reg52.h" #define OW_SKIP_ROM 0xcc #define DS18B20_CONVERT 0x44 #define DS18B20_READ 0xbe //IC引脚定义 sbit DQ = P1^4; //函数声明 void Delay_OneWire(unsigned int t); void Write_DS18B20(unsigned char dat); bit Init_DS18B20(void); unsigned char Read_DS18B20(void); unsigned char Tempget(); #endif
写完底层驱动后,我们新建一个工程,把控制LED和蜂鸣器继电器、数码管显示的通道选择函数写出来,数码管段选数组、矩阵键盘的位定义、延迟函数等准备工作做好:
#include "reg52.h"
#include "iic.h"
#include "onewire.h"
typedef unsigned int u16;
typedef unsigned char u8;
sfr P4 = 0XC0; //REG52的库内没有定义P4端口,所以我们需要自己定义上
sbit C1 = P4^2;
sbit C2 = P4^4;
sbit R1 = P3^2;
sbit R2 = P3^3;
sbit L1 = P0^0; //LED的位定义
sbit L2 = P0^1;
sbit L3 = P0^2;
sbit L4 = P0^3;
u8 code smgduan[10]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90};//共阳极数码管段选
u8 code smgduan_dp[10]={0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10};//共阳极数码管段选带小数点
/****************延迟函数,单位为1ms*********************/
void delayms(u16 i)
{
u16 aa;
while(i--)
{
for(aa=0;aa<845;aa++);
}
}
/****************通道选择函数*********************/
void InitHC138(u8 add)
{
switch(add)
{
case(4):
P2=(P2&0X1F)|0X80;break; //LED、
case(5):
P2=(P2&0X1F)|0Xa0;break; //蜂鸣器继电器、
case(6):
P2=(P2&0X1F)|0Xc0;break; //数码管段选
case(7):
P2=(P2&0X1F)|0Xe0;break; //数码管位选
}
}
/***************LED、蜂鸣器继电器初始化函数*********************/
void InitHC138(u8 add)
void InitHC573()
{
InitHC138(4);
P0=0XFF;
InitHC138(5);
P0=0X00;
}
/***************数码管选择函数*********************/
void DigDisplay(u8 add,u8 dat)
{
InitHC138(6);
P0=0X01<<add;
InitHC138(7);
P0=dat;
}
/***************消隐函数*********************/
void DisplayOFF()
{
InitHC138(7);
P0=0XFF;
}把这些准备工作做好后,我们就可以开始审题了:

4.3要求我们显示三个页面,温度显示界面、参数设置界面,DAC输出界面,那么我们就先写三个显示函数,把他们分别封装起来,方便后面调用,首先,我们需要设置几个全局变量:
u16 temp_dat; //温度获取
u8 temp_setup = 25; //温度参数
u16 output_v; //输出电压
/**************环境温度显示函数*************************/
void DisplayTempDat()
{
DigDisplay(0,0xc6);
delayms(1);
DigDisplay(4,smgduan[temp_dat*10/1000]);
delayms(1);
DigDisplay(5,smgduan_dp[temp_dat*10/100%10]);
delayms(1);
DigDisplay(6,smgduan[temp_dat*10/10%10]);
delayms(1);
DigDisplay(7,smgduan[temp_dat*10%10]);
delayms(1);
DisplayOFF();
}
/**************温度参数显示函数*************************/
void DisplayTempSetup()
{
DigDisplay(0,0X8C);
delayms(1);
DigDisplay(6,smgduan[temp_setup/10]);
delayms(1);
DigDisplay(7,smgduan[temp_setup%10]);
delayms(1);
DisplayOFF();
}
/**************电压输出显示函数*************************/
void DisplayDAC()
{
DigDisplay(0,0x88);
delayms(1);
DigDisplay(5,smgduan_dp[output_v/100]);
delayms(1);
DigDisplay(6,smgduan[output_v/10%10]);
delayms(1);
DigDisplay(7,smgduan[output_v%10]);
delayms(1);
DisplayOFF();
}写好之后,我们就来看按键功能的要求:


S4是控制三个界面来回切换,为了不影响数码管显示和数据采集,我们只设置一个变量,这个变量只有三个值,0,1,2,每当S4按下,这个变量依次累加,大于2之后又会变回0,之后再写一个函数根据变量的值进行显示;S5是控制模式切换,我们可以设置一个标志变量,每当S5按下,这个标志变量就取反;S8、S9是温度参数加减按键,只在参数设置界面有效,所以S8、S9按键扫描的时候还需要判断S4的变量的条件,“设定的温度参数在退出参数设置界面时生效”,这句话的意思就是,只有在进入DAC输出界面时生效,所以S5在扫面的时候也需要额外判断S4的变量条件:
bit MODE = 1; //标志变量
u8 dis_add;//界面切换变量
/**************按键扫描处理函数*************************/
void KeyScan()
{
R1 = 0;
R2 = 1;C1 = 1;C2 = 1;
if((C2 == 0)&&(dis_add == 2)) //S5
{
delayms(10);
if(C2 == 0)
{
MODE = ~MODE;
while(!C2);
}
}
else if((C1 == 0) && (dis_add == 1)) //S9
{
delayms(10);
if((C1 == 0) && (dis_add == 1))
{
temp_setup++;
while(!C1);
}
}
R2 = 0;
R1 = 1;C1 = 1;C2 = 1;
if(C2 == 0) //S4
{
delayms(10);
if(C2 == 0)
{
dis_add++;
while(!C2);
}
}
else if((C1 == 0) && (dis_add == 1)) //S8
{
delayms(10);
if((C1 == 0) && (dis_add == 1))
{
temp_setup--;
while(!C1);
}
}
}
/**************界面显示函数*************************/
void DisplayDat()
{
switch(dis_add)
{
case(0):DisplayTempDat();break;
case(1):DisplayTempSetup();break;
case(2):DisplayDAC();break;
}
}按键说明要求中还涉及到DAC的输出逻辑,模式1下判断环境温度和温度参数的大小并输出0V或5V,而且这个输出只能在DAC输出界面时才输出,在模式2下当达到40℃时输出4V,小于20℃时输出1V,在20℃-40℃时输出电压与温度呈线性关系,我们可以得到这个方程的斜率:0.15,由于我们的温度获取函数得到的温度需要除以10才是我们的实际温度,所以需要对得到的环境温度进行处理,每变化1℃,DAC变化0.15V,且输出电压在1V和4V之间:
/**************DAC处理函数*************************/
void DACModePro()
{
if((MODE==0)&&(dis_add == 2))
{
if(temp_dat<(temp_setup*10))
{
DA_write(0);
output_v=0;
}
else
{
DA_write(255);
output_v=500;
}
}
if(MODE==1)
{
if(temp_dat<=200)
{
DA_write(51);
output_v=100;
}
else if(temp_dat>=400)
{
DA_write(204);
output_v=400;
}
else if(temp_dat>200&&temp_dat<400)
{
int temp1_dat;
temp1_dat=temp_dat/10;
DA_write((1+(0.15*(temp_dat-20)))*51);
output_v=100*(1+(0.15*(temp1_dat-20)));
}
}
}写到这里,我们还要写一个函数对之前设置的那些参数变量进行范围约束:
/*************数据处理函数*************************/
void DatPro()
{
if(temp_setup > 50)
{
temp_setup = 1;
}
if(temp_setup < 1)
{
temp_setup = 50;
}
if(dis_add>2)
{
dis_add = 0;
}
}接下来就是LED模块要求了:

本次的LED模块设置比较简单,根据前面设置的标志位以及变量进行判断就好:
/**************LED处理函数*************************/
void LedPro()
{
InitHC138(4);
P0 = 0XFF;
if(MODE == 1) L1 = 0;else L1 = 1;
if(dis_add == 0) L2 = 0;else L2 = 1;
if(dis_add == 1) L3 = 0;else L3 = 1;
if(dis_add == 2) L4 = 0;else L4 = 1;
}初始状态我们在设置全局变量的时候进行设置就好,最后,我们需要在主函数里进行调用就可以,需要说明的是,我们可以把DAC输出和温度获取函数放在定时器里获取,可以设置每隔一秒获取一次,不需要让数据获取的那么频繁:
/**************定时器初始化函数*************************/
void TimeInit()
{
TMOD = 0X01;
TL0 = 0x18; //设置定时初值1MS
TH0 = 0xFC;
TR0 = 1;
ET0 = 1;
EA = 1;
}
/**************主函数*************************/
void main()
{
InitHC573();
TimeInit();
while(1)
{
KeyScan();
DisplayDat();
DatPro();
LedPro();
}
}
/**************定时器处理*************************/
void timer0() interrupt 1
{
u16 count1;
TL0 = 0x18; //设置定时初值1MS
TH0 = 0xFC;
count1++;
if(count1 == 1000)
{
temp_dat = Tempget();
DACModePro();
count1 = 0;
}
}好了,第十二届蓝桥杯单片机程序题的分享就到这里,感谢大家o(* ̄▽ ̄*)ブ。
我要赚赏金
