《蓝桥杯真题》:2021单片机省赛第一场(第十二届第一场)


真题内容取自: FILWY_M

设计任务及要求

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

实现代码

注意:①注意修改驱动代码时要留意一下onewire.h中单总线延时函数,是STC89C52RC,还是15系列的,15系列的单片机速度比51快8~12倍,需要修改对应的延时函数。②onewire.h中需要我们自己编写读取温度函数,有两种实现对温度保留两位小数的操作,一种在rd_temperature_f实现对温度的放大100倍,还有一种是使用浮点数,在函数得到温度的两个小数的保留。如下,看个人使用习惯采用

函数内保留两位小数

void rd_temperature_f()
{
	unsigned char low, high;
	init_ds18b20(); // 初始化
	Write_DS18B20(0xCC); // 跳过 ROM
	Write_DS18B20(0x44); // 转换温度
	
	init_ds18b20();
	Write_DS18B20(0xCC);
	Write_DS18B20(0xBE); // 读暂存器
	low = Read_DS18B20(); // 低字节
	high = Read_DS18B20(); // 高字节

	current_temperature = high;
	current_temperature <<= 8;
	current_temperature |= low;
	
	if (current_temperature & 0xf800 == 0x0000)//温度为正
	{
		current_temperature >>= 4;//拿到温度的整数部分
		current_temperature *= 100;//温度保留
		current_temperature = current_temperature + (low & 0x0f) * 6.25;
	}
}

函数外保留两位小数

//注意这边是浮点型float!!!
//主函数中使用参数uint curr_temp = rd_temperature_f()拿到两位小数
float rd_temperature_f(void)
{
    unsigned int temp;
	  float temperature;
    unsigned char low,high;
  
  	init_ds18b20();
  	Write_DS18B20(0xCC); // 仅有一个DS18B20,故跳过 ROM
  	Write_DS18B20(0x44); //启动温度转换
	//while (!DQ); //等待转换完成

  	init_ds18b20();
  	Write_DS18B20(0xCC);
  	Write_DS18B20(0xBE); //读取寄存器

  	low = Read_DS18B20(); //低字节
  	high = Read_DS18B20(); //高字节
  /** 精度为0.0625摄氏度 */  
	temp = (high&0x0f);
	temp <<= 8;
	temp |= low;
	temperature = temp*0.0625;
  	return temperature;
}

复制实验代码顺序

  1. 新建文件夹中建立三个.c文件,两个.h文件分别将下面文件复制到keil中去
  2. 注意选择生成.hex文件

实验代码

  • main.c
#include "STC15F2K60S2.h"
#include "onewire.h"  //单总线函数库
#include "iic.h"
#define uchar unsigned char
#define uint unsigned int

unsigned char dspcom = 0;//位选线
unsigned char dspbuf[8] = {10,10,10,10,10,10,10,10};//显示缓冲区
code unsigned char tab[] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xff,0xc6,0x88,0x8c};
//tab[11] = 'C',tab[12] = 'A', tab[13] = 'P';

unsigned int  curr_temp, temp_para=25, v_val;
unsigned char key_value=0xff,interface,mod = 1,ad_val;
bit flag_temp = 0;//温度读取标志
bit flag_key = 0;//按键读取标志
float temperature;
void display(void);		//数码管显示
void key_scanf(void);	//按键扫描
void key_proc(void);	//按键处理
void close_peripheral();	//关外设
void Timer0Init(void);		//1毫秒@12.000MHz
void dac_pcf8591(unsigned char dat);	//数字信号转化为模拟的电压信号

//主函数
void main(void)
{ 
    close_peripheral();//关外设
	Timer0Init();//定时器0初始化
    while(1)
    {
		if(flag_key)//20ms扫描键盘
	   {
			flag_key = 0;
			key_scanf();
		}
		key_proc();
				 
		if(interface==0)
		{
			//当前处于温度显示界面,指示灯L2点亮,否则熄灭
			P2 = (P2 & 0x1f) | 0x80;
			P0 = 0xfd;
			P2 &= 0x1f;
		 
			 if(flag_temp)
				{
					flag_temp = 0;
					temperature = rd_temperature_f();    
					curr_temp=temperature*100;
					dspbuf[0] = 11;//字符'C'
					dspbuf[4] = curr_temp/1000;
					dspbuf[5] = curr_temp%1000/100;       
					dspbuf[6] = curr_temp%1000%100/10; 
					dspbuf[7] = curr_temp%10;					
				}
		}

		if(interface==1)
		{
			当前处于参数设置界面,指示灯L3点亮,否则熄灭。
				P2 = (P2 & 0x1f) | 0x80;
				P0 = 0xfb;
				P2 &= 0x1f;

				dspbuf[0] = 13;//字符'P'
				dspbuf[1] = 10;
				dspbuf[2] = 10; 
				dspbuf[3] = 10;
				dspbuf[4] = 10;
				dspbuf[5] = 10; 
				dspbuf[6] = temp_para/10; 
				dspbuf[7] = temp_para%10;	
		}

		if(interface==2)
		{
			//DAC界面,L4点亮
			P2 = (P2 & 0x1f) | 0x80;
			P0 = ~(0x08);
			P2 &= 0x1f;
			if (mod == 1)//模式1
			{
				//且处于模式1,L1同时亮
				P2 = (P2 & 0x1f) | 0x80;
				P0 = ~(0x09);
				P2 &= 0x1f;
				temperature = rd_temperature_f();    
				curr_temp=temperature*100;
				
				if(curr_temp<temp_para*100)
				{
				   dac_pcf8591(0x00);
					 v_val=0;
				}
				
				if(curr_temp>=temp_para*100)
				{
				   dac_pcf8591(0xff);
					 v_val=500;
				}
			
			}
			else if (mod == 2)//模式2
			{
				temperature = rd_temperature_f();    
				curr_temp=temperature*100;
			
				if(curr_temp<2000)
				{
					 dac_pcf8591(51);
					 v_val=100;
				}
				if((curr_temp>=2000)&&(curr_temp<4000))
				{
				   v_val= (3*curr_temp/20)-200;
					 ad_val = v_val*51/100;
					 dac_pcf8591(ad_val);
				}
				if(curr_temp>=4000)
				{
				   dac_pcf8591(204);
					 v_val=400;
				}
			
			}
			dspbuf[0] = 12;//字符'A'
			dspbuf[1] = 10;
			dspbuf[2] = 10; 
			dspbuf[3] = 10;
			dspbuf[4] = 10;
			dspbuf[5] = v_val/100;       
			dspbuf[6] = v_val%100/10; 
			dspbuf[7] = v_val%10;			
		}									 
    }
}

//定时器0中断服务函数
void isr_timer_0(void)  interrupt 1  
{
	static int intr1, intr2;
 	display();
	if(++intr1== 20) 
	{
        intr1 = 0;
		flag_key = 1;         //20ms按键扫描标志位置1
   }
	if(++intr2== 500)  
	{
        intr2 = 0;
		    flag_temp = 1;  //500ms温度读取标志位置1
   }
}

void key_proc(void)
{
	switch(key_value)
	{
		case 12:
				interface++;
				interface=interface%3;
				key_value=0xff;
				break;
		case 9: 
				if (interface == 1)
			    	temp_para++;
		        key_value=0xff;
			    break;
			
		case 13:
				if (interface == 1)
			    	temp_para--;
		        key_value=0xff;
		        break;
			
		case 8: 
			    mod++;
		        if (mod == 3)
					mod = 1;
		        key_value=0xff;
		        break;
	}
}

//按键识别--行列扫描法(先行再列)
void key_scanf(void)
{
   	unsigned char row;
	static unsigned char key_state=0;	
	switch(key_state)
	{
			case 0:
			{
				P32 = 1, P33 = 1, P42 = 0; P44 = 0;
				if((P32 != 1) || (P33 != 1)) //有按键按下
				key_state=1;	
			}break;
			case 1:
			{
				P32 = 1, P33 = 1, P42 = 0; P44 = 0;

				if((P32 != 1) || (P33 != 1)) //有按键按下
				{
					if(P32 == 0)row = 3;
					if(P33 == 0)row = 4;//确定行	    
					switch(row)
					{
						case 3:{P32 = 0, P33 = 0, P42 = 1; P44 = 1;
										if(P44 == 0) {key_value=8;key_state=2;}
										else if(P42 == 0) {key_value=9;key_state=2;}				
						}break;
						case 4:{P32 = 0, P33 = 0, P42 = 1; P44 = 1;
										if(P44 == 0) {key_value=12;key_state=2;}
										else if(P42 == 0) {key_value=13;key_state=2;}
						}break;
					}	
				}
				else
				{
					key_state=0;	
				}  	   
			}break;
			case 2:     
			{
				P32 = 1, P33 = 1, P42 = 0; P44 = 0;
				if((P32 == 1) && (P33 == 1)) //按键放开
					key_state=0;	
			}break;					
  } 
}


//显示函数
void display(void)
{   
	//消隐
	P2 = (P2&0x1f) | 0xe0; 
	P0 = 0xff;
	P2 &= 0x1f;

	//位选
	P2 = (P2&0x1f) | 0xc0; 
	P0 = 1<<dspcom;
	P2 &= 0x1f;

	//段选
  	P2 = ((P2&0x1f)|0xE0); 
	P0 = tab[dspbuf[dspcom]];
	P2 &= 0x1f;
	
  if(((interface==0)||(interface==2)) && (dspcom == 5))
	{
		P2 = ((P2&0x1f)|0xE0); 
		P0 = (tab[dspbuf[dspcom]])&0x7f;//添加小数点
		P2 &= 0x1f;
	}
	 
  if(++dspcom == 8)
	{
		dspcom = 0;
	} 		
}




void close_peripheral()
{
	//初始化led为全灭
	P2 = (P2 & 0x1f) | 0x80;
	P0 = 0xff;
	
	//初始化继电器,蜂鸣器不吸附,不发声
	P2 = (P2 & 0x1f) | 0xa0;
	P0 = 0xaf;	
	P2 &= 0x1f;
}




//软件中的定时器模式16位自动重载
void Timer0Init(void)		//1毫秒@12.000MHz
{
	AUXR &= 0x7F;		//定时器时钟12T模式
	TMOD &= 0xF0;		//设置定时器模式
	TL0 = 0x18;		//设置定时初值
	TH0 = 0xFC;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
	ET0 = 1;		//打开定时器0中断允许
	EA = 1;		//打开总中断
}



void dac_pcf8591(unsigned char dat)//数字信号转化为模拟的电压信号
{
	IIC_Start();
	IIC_SendByte(0x90);
	IIC_WaitAck();

	IIC_SendByte(0x40);  //DAC输出模式,允许 DAC, ADC 通道 0
	IIC_WaitAck();

	IIC_SendByte(dat);
	IIC_WaitAck();
	IIC_Stop();
}
  • iic.c
/*
  程序说明: IIC总线驱动程序
  软件环境: Keil uVision 4.10 
  硬件环境: CT107单片机综合实训平台 8051,12MHz
  日    期: 2011-8-9
*/

#include "reg52.h"
#include "intrins.h"

#define DELAY_TIME 5

#define SlaveAddrW 0xA0
#define SlaveAddrR 0xA1

//总线引脚定义
sbit SDA = P2^1;  /* 数据线 */
sbit SCL = P2^0;  /* 时钟线 */

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;  					// 0:应答,1:非应答
    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;
}

//通过I2C总线发送数据
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;  
}

//从I2C总线上接收数据
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;    
}


  • IIC.H
#ifndef _IIC_H
#define _IIC_H

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); 

#endif
  • onewire.c
/*
  程序说明: 单总线驱动程序
  软件环境: Keil uVision 4.10 
  硬件环境: CT107单片机综合实训平台(外部晶振12MHz) STC89C52RC单片机
  日    期: 2011-8-9
*/
#include "reg52.h"

sbit DQ = P1^4;  //单总线接口

//单总线延时函数
void Delay_OneWire(unsigned int t)  //STC89C52RC
{
	unsigned char i;
	while(t--)
	{
		for(i=0;i<12;i++);
	}
}

//通过单总线向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;
}

//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;
}

float rd_temperature_f(void)
{
    unsigned int temp;
	  float temperature;
    unsigned char low,high;
  
  	init_ds18b20();
  	Write_DS18B20(0xCC); // 仅有一个DS18B20,故跳过 ROM
  	Write_DS18B20(0x44); //启动温度转换
	//while (!DQ); //等待转换完成

  	init_ds18b20();
  	Write_DS18B20(0xCC);
  	Write_DS18B20(0xBE); //读取寄存器

  	low = Read_DS18B20(); //低字节
  	high = Read_DS18B20(); //高字节
  /** 精度为0.0625摄氏度 */  
	temp = (high&0x0f);
	temp <<= 8;
	temp |= low;
	temperature = temp*0.0625;
  	return temperature;
}
  • ONEWIRE.H
#ifndef __ONEWIRE_H
#define __ONEWIRE_H

void Write_DS18B20(unsigned char dat);
unsigned char Read_DS18B20(void);
float rd_temperature_f(void);
bit init_ds18b20();
#endif

  • 3
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值