当前位置: 首页 > news >正文

第八届蓝桥杯单片机省赛

什么?你把最近几届省赛真题做完已经无题可做了,那不妨来看看老古董第八届省赛的题目吧!

附件:第八届蓝桥杯单片机省赛
在这里插入图片描述

一、数码管

1.页面流转

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
以上的页面流转功能可以用下图总结:

长按S4
松开S4
按下S7
按下S6
时间显示页面
温度显示页面
时钟设置
闹钟设置

这边给出一种实现方法:

  • 定义unsigned char型变量SetMode来控制三个主要页面(SetMode=0:时间显示页面、SetMode=1:时钟设置页面、SetMode=2:闹钟设置页面)
  • 然后定义bit 型变量SegMode在时间显示页面(SetMode=0)单独控制温度显示页面(SegMode=0:时间显示页面、SegMode=1:温度显示页面)

2.变量初定义

关于时钟设置和闹钟设置是在设置期间数据就生效还是退出到时间显示页面才生效这个问题,题目并没有明确指出,本篇文章默认时钟设置和闹钟设置在退出到时间显示页面才生效。

typedef unsigned char u8;
typedef unsigned int u16;
/* 页面参数 */	
idata u8 SetMode; //0-时间页面 1-时间设置 2-闹钟设置
idata bit SegMode;//0-时间显示 1-温度显示
/* 时钟、闹钟参数 */
idata u8 Index;//0-无 1-时 2-分 3-秒
pdata u8 Rtc[3] = {0x23,0x59,0x50};//时钟显示数据
pdata u8 RtcSet[3];//时钟设置时修改的数据
pdata u8 Alarm[3] = {0x00,0x00,0x00};//闹钟存放时间
pdata u8 AlarmSet[3];//闹钟设置时修改的数据
/* 温度 */
idata u8 tem;	
/* 老朋友  */
idata u8 SegPos;
pdata u8 SegBuf[8] = {10,10,10,10,10,10,10,10};
pdata u8 SegPoint[8] = {0,0,0,0,0,0,0,0};
pdata u8 ucLed[8] = {0,0,0,0,0,0,0,0};

3.时间显示页面

在这里插入图片描述

  • ds1302.h
#ifndef __ds1302_H__
#define __ds1302_H__

void SetRtc(unsigned char *Rtc);
void GetRtc(unsigned char *Rtc);

#endif
  • ds1302.c
#include <STC15F2K60S2.H>
#include <intrins.h>

sbit SCK = P1^7;
sbit SDA = P2^3;
sbit RST = P1^3;

void Write_Ds1302(unsigned  char temp) 
{
	unsigned char i;
	for (i=0;i<8;i++)     	
	{ 
		SCK = 0;
		SDA = temp&0x01;
		temp>>=1; 
		SCK=1;
	}
}   

//
void Write_Ds1302_Byte( unsigned char address,unsigned char dat )     
{
 	RST=0;	_nop_();
 	SCK=0;	_nop_();
 	RST=1; 	_nop_();  
 	Write_Ds1302(address);	
 	Write_Ds1302(dat);		
 	RST=0; 
}

//
unsigned char Read_Ds1302_Byte ( unsigned char address )
{
 	unsigned char i,temp=0x00;
 	RST=0;	_nop_();
 	SCK=0;	_nop_();
 	RST=1;	_nop_();
 	Write_Ds1302(address);
 	for (i=0;i<8;i++) 	
 	{		
		SCK=0;
		temp>>=1;	
 		if(SDA)
 		temp|=0x80;	
 		SCK=1;
	} 
 	RST=0;	_nop_();
 	SCK=0;	_nop_();
	SCK=1;	_nop_();
	SDA=0;	_nop_();
	SDA=1;	_nop_();
	return (temp);			
}

code unsigned char DS1302[4] = {0x84,0x82,0x80,0x8E};

void SetRtc(unsigned char *Rtc)
{
	unsigned char i;
	Write_Ds1302_Byte(DS1302[3],0x00);
	for(i = 0; i < 3; i++)
		Write_Ds1302_Byte(DS1302[i],Rtc[i]);
	Write_Ds1302_Byte(DS1302[3],0x80);
}
void GetRtc(unsigned char *Rtc)
{
	unsigned char i;
	
	for(i = 0; i < 3; i++)
		Rtc[i] = Read_Ds1302_Byte(DS1302[i]+1);
}
  • main.c
void SegProc()
{
	unsigned char i;
	GetRtc(Rtc);//读取当前时间
	switch(SetMode)
	{
		case 0:
			if(!SegMode)
			{
				SegBuf[2] = SegBuf[5] = 11;
				for(i = 0; i < 3; i++)
				{				
					SegBuf[3*i] = Rtc[i] / 16;
					SegBuf[3*i+1] = Rtc[i] % 16;
				}
			}
		break;
	}
}

void main()
{
	SystemInit();
	Timer0_Init();
	SetRtc(Rtc);//上电时设置时间
	while(1)
	{
		//...
	}
}

4.温度显示页面

在这里插入图片描述

  • ds18b20.h
#ifndef __ds18b20_H__
#define __ds18b20_H__

float TemRead();
	
#endif
  • ds18b20.c
#include <STC15F2K60S2.H>

sbit DQ = P1^4;

void Delay_OneWire(unsigned int t)  
{
	unsigned char i;
	while(t--){
		for(i=0;i<12;i++);
	}
}

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

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

//
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 TemRead()
{
	unsigned char tem_low, tem_hig;
	init_ds18b20();
	Write_DS18B20(0xcc);
	Write_DS18B20(0x44);
	
	init_ds18b20();
	Write_DS18B20(0xcc);
	Write_DS18B20(0xbe);
	tem_low = Read_DS18B20();
	tem_hig = Read_DS18B20();
	
	return ((tem_hig << 8) | tem_low) / 16.0;
}
  • main.c
void SegProc()
{
	tem = TemRead();
	switch(SetMode)
	{
		case 0:
			if(!SegMode)
			{
				//...
			}
			else//温度显示页面
			{
				SegBuf[0] = 10;
				SegBuf[1] = 10;
				SegBuf[2] = 10;
				SegBuf[3] = 10;
				SegBuf[4] = 10;
				SegBuf[5] = tem / 10;
				SegBuf[6] = tem % 10;
				SegBuf[7] = 12;//C
			}
		break;
	}
}

5.时钟设置页面

在这里插入图片描述
只显示时钟设置的数据:

for(i = 0; i < 3; i++)
{				
	SegBuf[3*i] = RtcSet[i] / 16;
	SegBuf[3*i+1] = RtcSet[i] % 16;
}

根据按键S7去选择待调整的时、分、秒,当前选择的显示单元以 1 秒为间隔亮灭。
可以通过Index来选择时、分、秒,选中后,定义计时变量idata u16 Time_1s;来计时1s让idata bit SegFlash;取反达到数码管闪烁的效果(和指示灯闪烁一样)。

idata u16 Time_1s;
idata bit SegFlash;

void SegProc()
{
	//...
	switch(SetMode)
	{
		case 0:
			//...
		break;
		
		case 1://时钟设置页面
			SegBuf[2] = SegBuf[5] = 11;
			for(i = 0; i < 3; i++)
			{				
				SegBuf[3*i] = (Index == i+1 && SegFlash) ? 10 : RtcSet[i] / 16;
				SegBuf[3*i+1] = (Index == i+1 && SegFlash) ? 10 : RtcSet[i] % 16;
			}
		break;
	}
}

void Timer0_Isr(void) interrupt 1
{
	if(++SegPos == 8) SegPos = 0;
	SegDisp(SegPos,SegBuf[SegPos],SegPoint[SegPos]);
	if(++Time_1s == 1000)
	{
		Time_1s = 0;
		SegFlash = !SegFlash;
	}
}

6.闹钟设置页面

在这里插入图片描述
闹钟设置页面不用考虑数码管闪烁

void SegProc()
{
	u8 i;
	switch(SetMode)
	{
		case 2:
			SegBuf[2] = SegBuf[5] = 11;
			for(i = 0; i < 3; i++)
			{				
				SegBuf[3*i] = AlarmSet[i] / 16;
				SegBuf[3*i+1] = AlarmSet[i] % 16;
			}
		break;
	}
}

7.数码管完整代码

typedef unsigned char u8;
typedef unsigned int u16;
/* 页面参数 */	
idata u8 SetMode; //0-时间页面 1-时间设置 2-闹钟设置
idata bit SegMode;//0-时间显示 1-温度显示
/* 时钟、闹钟参数 */
idata u8 Index;//0-无 1-时 2-分 3-秒
pdata u8 Rtc[3] = {0x23,0x59,0x50};//时钟显示数据
pdata u8 RtcSet[3];//时钟设置时修改的数据
pdata u8 Alarm[3] = {0x00,0x00,0x00};//闹钟存放时间
pdata u8 AlarmSet[3];//闹钟设置时修改的数据
/* 温度 */
idata u8 tem;	

void SegProc()
{
	unsigned char i;
	tem = TemRead();
	GetRtc(Rtc);
	switch(SetMode)
	{
		case 0:
			if(!SegMode)
			{
				SegBuf[2] = SegBuf[5] = 11;
				for(i = 0; i < 3; i++)
				{				
					SegBuf[3*i] = Rtc[i] / 16;
					SegBuf[3*i+1] = Rtc[i] % 16;
				}
			}
			else
			{
				SegBuf[0] = 10;
				SegBuf[1] = 10;
				SegBuf[2] = 10;
				SegBuf[3] = 10;
				SegBuf[4] = 10;
				SegBuf[5] = tem / 10;
				SegBuf[6] = tem % 10;
				SegBuf[7] = 12;
			}
		break;
		
		case 1:
			SegBuf[2] = SegBuf[5] = 11;
			for(i = 0; i < 3; i++)
			{				
				SegBuf[3*i] = (Index == i+1 && SegFlash) ? 10 : RtcSet[i] / 16;
				SegBuf[3*i+1] = (Index == i+1 && SegFlash) ? 10 : RtcSet[i] % 16;
			}
		break;
			
		case 2:
			SegBuf[2] 	= SegBuf[5] = 11;
			for(i = 0; i < 3; i++)
			{				
				SegBuf[3*i] = AlarmSet[i] / 16;
				SegBuf[3*i+1] = AlarmSet[i] % 16;
			}
		break;
	}
}

void Timer0_Init(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 Timer0_Isr(void) interrupt 1
{
	if(++SegPos == 8) SegPos = 0;
	SegDisp(SegPos,SegBuf[SegPos],SegPoint[SegPos]);
	if(++Time_1s == 1000)
	{
		Time_1s = 0;
		SegFlash = !SegFlash;
	}
}

二、按键

1.S4、S5

在这里插入图片描述
在设置时钟和闹钟的时候,由于要考虑参数的边界性,所以在加和减参数的时候要考虑最大值和最小值,防止数据越界,小时的范围是00 ~ 23,分钟的范围是00 ~ 59,秒钟的范围是00 ~ 59,这边需要注意的是,unsigned char的数据范围是0~255,假设参数减到0,再按下S4后,下一次的值是255。

void KeyProc()
{
	KeyVal = KeyDisp();
	KeyDown = KeyVal & ~KeyOld;
	KeyUp = ~KeyVal & KeyOld;
	KeyOld = KeyVal;
	switch(KeyDown)
	{
		case 5:
			if(SetMode == 1)//时间设置
			{
				RtcSet[Index-1]++;
				if(RtcSet[0] == 24)
					RtcSet[0] = 23;
				if(RtcSet[1] == 60)
					RtcSet[1] = 59;
				if(RtcSet[2] == 60)
					RtcSet[2] = 59;
				
			}
			else if(SetMode == 2)//闹钟设置
			{
				AlarmSet[Index-1]++;
				if(AlarmSet[0] == 24)
					AlarmSet[0] = 23;
				if(AlarmSet[1] == 60)
					AlarmSet[1] = 59;
				if(AlarmSet[2] == 60)
					AlarmSet[2] = 59;
			}
		break;
			
		case 4:
			if(SetMode == 1)
			{
				RtcSet[Index-1]--;
				if(RtcSet[Index-1] == 255)
					RtcSet[Index-1] = 0;
			}
			else if(SetMode == 2)
			{
				AlarmSet[Index-1]--;
				if(AlarmSet[Index-1] == 255)
					AlarmSet[Index-1] = 0;
			}
		break;
	}
}

1.S6、S7

对于S6、S7。只需要在进入小时的设置时将原先的时钟/闹钟数据传给AlarmSet/AlarmSet,完成设置返回时间显示页面时再将AlarmSet/AlarmSet的值传回时钟/闹钟。

switch(KeyDown)
	{
		case 6:
			Index++;
			if(Index == 1)
			{
				SetMode = 2;
				memcpy(AlarmSet,Alarm,3);
			}
			if(Index == 4)
			{
				SetMode = 0;
				memcpy(Alarm,AlarmSet,3);
				Index = 0;
			}
		break;
		
		case 7:
			Index++;
			if(Index == 1)
			{
				SetMode = 1;
				memcpy(RtcSet,Rtc,3);
			}
			if(Index == 4)
			{
				SetMode = 0;
				memcpy(Rtc,RtcSet,3);
				Index = 0;
				SetRtc(Rtc);
			}
		break;

3.完整代码

void KeyProc()
{
	KeyVal = KeyDisp();
	KeyDown = KeyVal & ~KeyOld;
	KeyUp = ~KeyVal & KeyOld;
	KeyOld = KeyVal;
	
	//长按S4进入温度显示页面,松手返回时间显示页面
	if(!SetMode)
	{
		if(KeyOld == 4)
			SegMode = 1;
		if(KeyUp == 4)
			SegMode = 0;
	}
	
	switch(KeyDown)
	{
		case 6:
			Index++;
			if(Index == 1)
			{
				SetMode = 2;
				memcpy(AlarmSet,Alarm,3);
			}
			if(Index == 4)
			{
				SetMode = 0;
				memcpy(Alarm,AlarmSet,3);
				Index = 0;
			}
		break;
		
		case 7:
			Index++;
			if(Index == 1)
			{
				SetMode = 1;
				memcpy(RtcSet,Rtc,3);
			}
			if(Index == 4)
			{
				SetMode = 0;
				memcpy(Rtc,RtcSet,3);
				Index = 0;
				SetRtc(Rtc);
			}
		break;
			
		case 5:
			if(SetMode == 1)
			{
				RtcSet[Index-1]++;
				if(RtcSet[0] == 24)
					RtcSet[0] = 23;
				if(RtcSet[1] == 60)
					RtcSet[1] = 59;
				if(RtcSet[2] == 60)
					RtcSet[2] = 59;
				
			}
			else if(SetMode == 2)
			{
				AlarmSet[Index-1]++;
				if(AlarmSet[0] == 24)
					AlarmSet[0] = 23;
				if(AlarmSet[1] == 60)
					AlarmSet[1] = 59;
				if(AlarmSet[2] == 60)
					AlarmSet[2] = 59;
			}
		break;
			
		case 4:
			if(SetMode == 1)
			{
				RtcSet[Index-1]--;
				if(RtcSet[Index-1] == 255)
					RtcSet[Index-1] = 0;
			}
			else if(SetMode == 2)
			{
				AlarmSet[Index-1]--;
				if(AlarmSet[Index-1] == 255)
					AlarmSet[Index-1] = 0;
			}
		break;
	}
}

三、指示灯

在这里插入图片描述
先实现闹钟响的时候L1以200ms为间隔闪烁,持续5s。

idata u8 Time_200ms;//闪烁间隔
idata u16 Time_5s;//定时5s
idata bit AlarmFlag;//闹钟响
idata bit LedFlash;//指示灯闪烁

void LedProc()
{
	//闹钟响了
	if(Alarm[0] == Rtc[0] && Alarm[1] == Rtc[1] && Alarm[2] == Rtc[2])
		AlarmFlag = 1;
	ucLed[0] = LedFlash;
	LedDisp(ucLed);
}

void Timer0_Isr(void) interrupt 1
{
	systick++;
	if(++SegPos == 8) SegPos = 0;
	SegDisp(SegPos,SegBuf[SegPos],SegPoint[SegPos]);
	if(AlarmFlag)//闹钟响
	{
		Time_5s++;//开始计时5s
		if(Time_5s == 5000)//计时到5s关闭闹钟
		{
			Time_5s = 0;
			AlarmFlag = 0;//关闭闹钟
		}
		else//还未计时5秒,L1闪烁
		{
			if(++Time_200ms == 200)
			{
				Time_200ms = 0;
				LedFlash = !LedFlash;
			}
		}
	}
	else
		LedFlash = 0;
}

接下来写在任意按键按下关闭闹钟

void KeyProc()
{
	//...
	if(AlarmFlag)
	{
		if(KeyDown)//按键值不为0
			AlarmFlag = 0;//关闭闹钟
		//防止这次的按键干扰到其他功能按键,关闭闹钟直接退出不执行下面语句
		return;
	}
}

相关文章:

  • 【08】单片机变量命名规范指南
  • AI编程工具-(七)
  • 【鸿蒙开发】入门篇:node与express
  • 优化 NFS 挂载参数以提升可靠性与容错性
  • Spring Boot 日志
  • LabVIEW基于双通道FFT共轭相乘的噪声抑制
  • 关于统计建模大赛的选题
  • 【鸿蒙开发】Hi3861学习笔记- 软件定时器示例
  • 【Linux】进程间通信
  • 批量创建BOM的RFC接口
  • 常见的设计模式和应用场景(一)
  • 文本转语音-音画适时推送rtsp并播放
  • 静态路由实验
  • Spring Boot/Spring Cloud 整合 ELK(Elasticsearch、Logstash、Kibana)详细避坑指南
  • 【CSS3】元婴篇
  • [数据结构]并查集
  • 【 <一> 炼丹初探:JavaWeb 的起源与基础】之 JavaWeb 项目的部署:从开发环境到生产环境
  • 智能焊机监测系统:打造工业安全的数字化盾牌
  • Git合并工具在开发中的使用指南
  • 常用中文开源embedding模型应用
  • 超导电路新设计有望提升量子处理器速度
  • 强沙尘暴压城近万名游客被困,敦煌如何用3小时跑赢12级狂风?
  • 胡塞武装称以色列所有机场均为其打击目标
  • 蓝佛安主持东盟与中日韩财长和央行行长系列会议并举行多场双边会见
  • 中国海警局回应日本民用飞机侵闯我钓鱼岛领空:依法警告驱离
  • 下达专项资金、党政主官田间调研……全国多地力保夏粮稳收