ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

蓝桥杯单片机第九届初赛主观题-----彩灯控制系统

2021-03-17 21:57:49  阅读:263  来源: 互联网

标签:主观题 LED SHOW uint8 初赛 蓝桥 0xff IIC SMG


蓝桥杯单片机第九届初赛主观题-----彩灯控制系统

前言

俺又来了,这次连续好几天都没写博客,真不是我偷懒,这次是真的写了好久!各种bug可给我整惨了,让后最近害仔细看了看大佬写的,于是就把格式给改的略微整齐了一点。。。
废话不多说,我们开始


赛题分析

1、硬件框图分析

仍然是赛题分析先开始,先看基本硬件框图
在这里插入图片描述
根据这张图总结一下大概需要的器件以及所需要的特殊功能寄存器

输入功能
1、模拟输入,IIC通讯下的PCF8591进行AD转换
2、按键,定时器0进行间隔1ms的定时器中断进行按键扫描消抖

存储功能
3、E2PROM,IIC通讯下的24C02存储器的数据读写,存储

输出功能
4、LED指示灯,138译码器和573锁存器选择LED输出
5、数码管显示,138译码器和573锁存器选择数码管,并运用定时器0进行间隔1ms的定时器中断输出显示内容

2、功能分析

在这里插入图片描述
根据基本功能描述,我们可以知道,我们还需要用到PWM输出、PCF8591的AIN3通道。
在这里插入图片描述
在这里插入图片描述

1)关闭蜂鸣器这个很简单,直接跳过。

2)上电后关闭LED,数码管,我们可以设置状态位,当状态位改变时再启动LED或者数码管

3)设置一个无符号整型变量用来存储流转间隔

在这里插入图片描述

4)同样可以设置状态数负责LED的流转,时间可在定时器中断中计算。
在这里插入图片描述
这里可以根据PCF8591读取到的数值来改变PWM的数值
在这里插入图片描述
大的来了 ,按键功能一般是赛题中的核心,需要特别重视!

1)S7改变LED的状态数来启动LED
2)我们设置S6按键的状态数,通过S6改变状态数来改变数码管的显示,并且可以通过状态数选择按键S5与S4的功能
3)S5在对应状态数下,改变之前创建的存储流转间隔数据或者运行模式的变量与状态数。
4)S4在对应状态数下,改变之前创建的存储流转间隔数据或者运行模式的变量与状态数。设置温度显示状态数,在另一对应状态下,按下松开改变温度显示状态数。

说了这么多废话,其实总结一下就是,给我设置状态数啊kora!!!

代码讲解

主函数页面代码

#ifndef _SYS_H
#define _SYS_H

typedef  unsigned char uint8;
typedef  unsigned int  uint16;
typedef  unsigned long uint32;

#include "reg52.h"
#include "intrins.h"
#include "iic.h"
#include "Keyboard.h"
#include "LED.h"
#include "SMG.h"
#include "Init.h"

extern uint8  TurnMod;  //运行模式状态数
extern bit LED_ON;		//LED开关状态数
extern uint8 Con_Mod;	//模式控制状态数
extern uint16 TurnHZ;	//流转间隔时间存储变量(单位毫秒)
extern uint8 PWM_NUM;	//PWM值存储变量
extern bit Tmp_Show;	//亮度显示状态数
extern uint8 TurnHZ_L;	//流转间隔时间存储变量缩小100倍版,方便eeprom存储
#endif
#include "Sys.h"
uint16 TurnHZ = 400;	//流转间隔时间存储变量(单位毫秒)
uint8 TurnHZ_L;			//流转间隔时间存储变量缩小100倍版,方便eeprom存储
uint8  TurnMod = 0x10;	//运行模式状态数
bit Fms = 1;			//运行模式时间抵达状态数
uint8 PWM_NUM = 255;	//PWM存储变量
bit F200ms = 1;			//200ms时间间隔状态数
bit LED_ON = 0;			//LED开关状态数
uint8 Con_Mod = 0x00;	//模式控制状态数
bit Tmp_Show = 0;		//亮度显示状态数
bit FADC = 0;			//PCF8591读取数据状态数
/*
iic读取数据时需要关闭中断,因此应设置读取间隔。
若不设置读取间隔,让iic连续读取,则会造成led闪烁
若读取时不关闭中断,则会造成读取数据不稳定
*/
void main()
{
	Close_BUZZ();		//关闭蜂鸣器与继电器
	Init_T0();			//初始化T0定时器
	Init_PWM();			//初始化T1定时器(PWM)
	TurnHZ_L = EEPROM_read(0x12);//读取EEPROM的数值,存入八位数据中
	TurnHZ = TurnHZ_L*100;//数据增大100倍,使流转时间单位变为1ms
	if(TurnHZ < 400 || TurnHZ % 100 != 0 || TurnHZ>1200)
		TurnHZ = 400;
/*
此处if的作用是防止初次上电EEPROM中无存储数据
*/
	while(1)			//进入主循环
	{
		if(LED_ON)		//LED状态数为开启时
		{
			if(Fms)     //到达流转时间间隔
			{
				Fms = 0;//清零状态数
				LED_Cal();//计算LED状态
			}
			LED_Con(TurnMod);//根据状态选择点亮的LED
	  }
		if(F200ms)		//达到200ms间隔
		{
			SMG_Cal();	//计算一次数码管对应的数值
			F200ms = 0; //置零状态数
		}
		Key_main();		//扫描按键
		if(FADC)		//到达读取PCF8691数据的时间间隔
		{
			PWM_NUM = PCF8591NUM();//PWM数值读取
			FADC = 0;	//置零状态数
		}
	}
}

/*定时器0中断主要负责
数码管的刷新,按键的扫描,
以及各种时间状态数的计算*/
void it0() interrupt 1	
{
	static uint16 flag_TurnHZ =0;//流转间隔时间计数
	static uint8 flag_200ms = 0; //200ms间隔时间计数
	static uint16 pdata flag_SMGOFF = 0;//数码管闪烁时间计数
	static uint8 flag_ADC = 0;	 //PCF8591时间计数
	TH0 = (65536 - 1000)/256;	 //1ms时间间隔
	TL0 = (65536 - 1000)%256;
	
	if(LED_ON)				//当LED为开启状态
	{
		flag_TurnHZ ++;		//流转时间间隔计数加1ms
		if(flag_TurnHZ == TurnHZ)//到达流转间隔时间
		{
			Fms = 1;		//运行模式时间抵达状态数置一
			flag_TurnHZ = 0;//归零
			TurnMod++;		//流转模式状态数加一
			//这里流转模式状态数运用了自定义规则存储数据,到LED页细说
		}
	}
	flag_SMGOFF++;			//数码管闪烁时间计数
	flag_200ms++;			//200ms间隔时间计数	
	flag_ADC++;				//PCF8591读取间隔时间计数
	
	if(flag_ADC >= 100)		//PCF8591读取间隔到100ms
	{
		flag_ADC = 0;
		FADC = 1;
	}
	if(flag_200ms >= 200)	//200ms间隔时间
	{
		flag_200ms = 0;
		F200ms = 1;
	}
	if(flag_SMGOFF >= 800)	//数码管闪烁时间到800ms
	{
		flag_SMGOFF = 0;
		SMG_OFF = ~SMG_OFF;
	}
	
	SMG_Scan();			//数码管扫描
	Key_Scan();			//按键扫描
}

/*定时器1中断负责PWM输出*/
void PWM_it1() interrupt 3
{
	static uint8 HZZ = 0;
	TH1 = (65536 - 100)/256; //100hz每秒
	TL1 = (65536 - 100)%256;
	ET0 = 0;
	P0 = 0xff;
	if(HZZ<PWM_NUM && LED_ON)//进入次数小于PWM设定值且led为打开状态时
	{
		Sel_HC138(4);
		P0 = LED_Sta;	//打开LED_Cal()计算的对应LED灯
		Sel_HC138(0);
	}
	else
	{
		Sel_HC138(4);
		P0 = 0xff;		//关闭LED灯,进行PWM输出
		Sel_HC138(0);
	}
	ET0 = 1;
	HZZ++;
	if(HZZ>=100)		//第100次进入时归零
		HZZ = 0;
}

关于定时优先级问题,数码管扫描的优先级应大与PWM输出,防止数码管扫描过程种数据被干扰。

不过还好,默认定时器0中断大与定时器1中断。

让后是LED页
首先先看一下我们自定义的LED转变模式的状态功能
在这里插入图片描述
根据此表格编写

#ifndef _LED_H
#define _LED_H
extern uint8 LED_Sta;

void LED_Con(uint8 T_Mod);
void LED_Cal();

#endif
#include "Sys.h"

uint8 LED_Sta = 0xff;


void MOD3(uint8 n)	//模式3
{
	switch(n)
	{
		case 0x01:
			LED_Sta = ~0x81;
		break;
		case 0x02:
			LED_Sta = ~0x42;
		break;
		case 0x03:
			LED_Sta = ~0x24;
		break;
		case 0x04:
			LED_Sta = ~0x18;
		break;
		default:break;
	}
}
void MOD4(uint8 n)	//模式4
{
	switch(n)
	{
		case 0x04:
			LED_Sta = ~0x81;
		break;
		case 0x03:
			LED_Sta = ~0x42;
		break;
		case 0x02:
			LED_Sta = ~0x24;
		break;
		case 0x01:
			LED_Sta = ~0x18;
		break;
		default:break;
	}
}

void LED_Cal() //LED状态数值计算
{
		if((TurnMod>>4)<=0x02)
		{
			if((TurnMod&0x0f) >= 0x09)
			{
				TurnMod = ((TurnMod&0xf0)|0x01)+16;
			}
		}
		else if((TurnMod>>4)>0x02)
		{
			if((TurnMod&0x0f) >= 0x05)
			{
				TurnMod = ((TurnMod&0xf0)|0x01)+16;
				if((TurnMod>>4)>=0x05)
					TurnMod = 0x11;
			}
		}
}

void LED_Con(uint8 T_Mod)
{
	uint8 n;
	n = (T_Mod&0x0f);
	switch(T_Mod>>4)
	{
		case 0x01:LED_Sta = ~(0x80>>n);break;
		case 0x02:LED_Sta = ~(0x01<<n);break;
		case 0x03:MOD3(n);break;
		case 0x04:MOD4(n);break;
		default:break;
	}
}

iic是官方给的页面,但是具体部件的读取写入还得看自己,所以这里就只放具体部件的读取与写入
iic的读取与写入对时许要求严格,此期间必须关闭中断!!!

uint8 PCF8591NUM()
{
	uint8 Val;
	EA = 0;
	IIC_Start();				//IIC启动
	IIC_SendByte(0x90); 		//IIC选择地址,并发送写指令
	IIC_WaitAck();				//等待IIC应答
	IIC_SendByte(0x03);			//选择模拟量输入通道
	IIC_WaitAck();
	IIC_Stop();    				//IIC停止,接下来开始读取数据
	
	IIC_Start();				//IIC启动
	IIC_SendByte(0x91); 		//IIC选择地址,并发送读指令
	IIC_WaitAck();				//IIC等待应答
	Val = IIC_RecByte();		//接收PCF8591数据
	IIC_SendAck(0);				//发送应答位
	IIC_Stop();
	EA = 1;
	if(Val>=0&&Val<50)
		Val = 10;
	else if(Val>=50&&Val<100)
		Val = 40;
	else if(Val>=100&&Val<140)
		Val = 70;
	else if(Val>=140&&Val<256)
		Val = 100;
	/*根据所得ad值返回四种不同的数据给PWM*/
	return Val;
}

/*addr指eeprom的存储地址,Val指要输入的数据*/
void EEPROM_write(uint8 addr,uint8 Val)//EEPROM写入
{
	EA = 0;
	IIC_Start();
	IIC_SendByte(0xa0);	
	IIC_WaitAck();
	IIC_SendByte(addr);
	IIC_WaitAck();
	IIC_SendByte(Val);
	IIC_WaitAck();
	IIC_Stop();
	
	EA = 1;
}
/*addr指eeprom的存储地址*/
uint8 EEPROM_read(uint8 addr)//EEPROM读取
{
	uint8 Val;
	EA = 0;
	IIC_Start();				//IIC启动
	IIC_SendByte(0xa0); 		//IIC选择地址,并发送写指令
	IIC_WaitAck();				//等待IIC应答
	IIC_SendByte(addr);			//选择地址
	IIC_WaitAck();
	IIC_Stop();    				//IIC停止,接下来开始读取数据
	
	IIC_Start();				//IIC启动
	IIC_SendByte(0xa1); 		//IIC选择地址,并发送读指令
	IIC_WaitAck();				//IIC等待应答
	Val = IIC_RecByte();		//接收EEPROM数据
	IIC_SendAck(0);				//发送应答位
	IIC_Stop();
	EA = 1;
	return Val;
}
#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); 
uint8 PCF8591NUM();
void EEPROM_write(uint8 addr,uint8 Val);
uint8 EEPROM_read(uint8 addr);

#endif

让后是按键页,基本方法基于金沙滩按键教学

#ifndef _KEYBOARD_H
#define _KEYBOARD_H

sbit KEY4 = P3^3;
sbit KEY5 = P3^2;
sbit KEY6 = P3^1;
sbit KEY7 = P3^0;

void Key_Action(uint8 KeyCode);
void Key_main();
void Key_Scan();

extern uint8 code Key_Num[4];
extern uint8 Key_Sta[4];


#endif
#include "Sys.h"

uint8 code Key_Num[4] = {
	4,5,6,7
};

uint8 Key_Sta[4] = {
	1,1,1,1
};

//改变各种状态数与存储数据实现功能
void Key_Action(uint8 KeyCode)
{
	switch(KeyCode)
	{
		case 4:
			
		switch(Con_Mod>>4)
			{
			case 0x00:
				Tmp_Show = 1;
			break;
			//查看温度
				case 0x0f:
					TurnMod = (TurnMod-16)&0xf0;
					if(TurnMod < 0x10)
						TurnMod = 0x40;
				break;
				//运行模式+1
				case 0x0e:
					TurnHZ -= 100;
				if(TurnHZ < 400)
					TurnHZ = 1200;
				break;
				//流转间隔模式加100ms
				default:break;
			}
		
		break;
		
		case 5:
			switch(Con_Mod>>4)
			{
				case 0x0f:
					TurnMod = (TurnMod+16)&0xf0;
					if(TurnMod > 0x40)
						TurnMod = 0x10;
				break;
				//运行模式+1
				case 0x0e:
					TurnHZ += 100;
				if(TurnHZ > 1200)
					TurnHZ = 400;
				TurnHZ_L = TurnHZ/100;
				EEPROM_write(0x12,TurnHZ_L);
				break;
				//流转间隔模式加100ms
				default:break;
			}
		break;
		
		case 6:
			switch(Con_Mod>>4)
			{
				case 0x00:
					Con_Mod = 0xf0;
				break;
				//转入模式编号模式
				case 0x0f:
					Con_Mod = ((Con_Mod&0x00)|0xe0)|(TurnHZ/100);
				break;
				//转入流转间隔模式
				case 0x0e:
					Con_Mod = 0x00;
				break;
				//转入数码管熄灭
				default:break;
			}
		break;
		
		case 7:LED_ON = ~LED_ON;break;//流转模式开关
		default:break;
	}
}

void Key_main()		//按键识别
{
	static uint8 backup[4] = {
		1,1,1,1
	};
	uint8 i;
	for(i=0;i<4;i++)
	{
		if(backup[i] != Key_Sta[i])
		{
			if(backup[i] == 1)//按键按下状态
			{
				Key_Action(Key_Num[i]);
			}
			if(backup[i] == 0)//按键松开状态
			{
				if(i == 0)
					Tmp_Show = 0;
			}
			backup[i] = Key_Sta[i];
		}
	}
}

void Key_Scan()		//中断种的按键扫描消抖
{
	static uint8 Key_Buf[4] = {
		0xff,0xff,0xff,0xff
	};
	uint8 i;
	
	Key_Buf[0] = (Key_Buf[0]<<1)|KEY4;
	Key_Buf[1] = (Key_Buf[1]<<1)|KEY5;
	Key_Buf[2] = (Key_Buf[2]<<1)|KEY6;
	Key_Buf[3] = (Key_Buf[3]<<1)|KEY7;
	
	for(i=0;i<4;i++)
	{
		if(Key_Buf[i] == 0x00)
			Key_Sta[i] = 0;
		else if(Key_Buf[i] == 0xff)
			Key_Sta[i] = 1;
	}
}

最后就是数码管页,这里需要根据按键改变的状态数改变显示的数据
如表
在这里插入图片描述

#ifndef _KEYBOARD_H
#define _KEYBOARD_H

sbit KEY4 = P3^3;
sbit KEY5 = P3^2;
sbit KEY6 = P3^1;
sbit KEY7 = P3^0;

void Key_Action(uint8 KeyCode);
void Key_main();
void Key_Scan();

extern uint8 code Key_Num[4];
extern uint8 Key_Sta[4];


#endif
#include "Sys.h"

uint8 SMG_NUM[10] = {
	0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90
};
uint8 SMG_SHOW[8] = {
	0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
};

bit SMG_OFF = 0;

//该函数结合上图表看!!!
void SMG_Cal()
{
	SMG_SHOW[4] = 0xff;
	if(Con_Mod != 0 && Tmp_Show != 1)
	{
			switch(Con_Mod>>4)
			{
				case 0x0f:
					if(SMG_OFF)
					{
						SMG_SHOW[7] = 0xff;
						SMG_SHOW[6] = 0xff;
						SMG_SHOW[5] = 0xff;
					}
					else
					{
						SMG_SHOW[7] = 0xbf;
						SMG_SHOW[6] = SMG_NUM[TurnMod>>4];
						SMG_SHOW[5] = 0xbf;
					}
					if((TurnHZ/1000)%10==0)
							SMG_SHOW[3] = 0xff;C
						else
							SMG_SHOW[3] = SMG_NUM[(TurnHZ/1000)%10];
					SMG_SHOW[2] = SMG_NUM[(TurnHZ/100)%10];
					SMG_SHOW[1] = SMG_NUM[(TurnHZ/10)%10];
					SMG_SHOW[0] = SMG_NUM[TurnHZ%10];
				break;
				case 0x0e:
					SMG_SHOW[7] = 0xbf;
					SMG_SHOW[6] = SMG_NUM[TurnMod>>4];
					SMG_SHOW[5] = 0xbf;
					if(SMG_OFF)
					{
						SMG_SHOW[2] = 0xff;
						SMG_SHOW[1] = 0xff;
						SMG_SHOW[0] = 0xff;
						SMG_SHOW[3] = 0xff;
					}
					else
					{
						if((TurnHZ/1000)%10==0)
							SMG_SHOW[3] = 0xff;
						else
							SMG_SHOW[3] = SMG_NUM[(TurnHZ/1000)%10];
						SMG_SHOW[2] = SMG_NUM[(TurnHZ/100)%10];
						SMG_SHOW[1] = SMG_NUM[(TurnHZ/10)%10];
						SMG_SHOW[0] = SMG_NUM[TurnHZ%10];
					}
				break;
				default:break;
			}
	}
	else if(Con_Mod==0 && Tmp_Show!=1)
	{
		SMG_SHOW[7] = 0xff;
		SMG_SHOW[6] = 0xff;
		SMG_SHOW[5] = 0xff;
		SMG_SHOW[3] = 0xff;
		SMG_SHOW[2] = 0xff;
		SMG_SHOW[1] = 0xff;
		SMG_SHOW[0] = 0xff;
	}
	else if(Tmp_Show == 1)
	{
		SMG_SHOW[7] = 0xff;
		SMG_SHOW[6] = 0xff;
		SMG_SHOW[5] = 0xff;
		SMG_SHOW[3] = 0xff;
		SMG_SHOW[2] = 0xff;
		SMG_SHOW[1] = 0xbf;
		switch(PWM_NUM)
		{
			case  10:SMG_SHOW[0] = SMG_NUM[1];break;
			case  40:SMG_SHOW[0] = SMG_NUM[2];break;
			case  70:SMG_SHOW[0] = SMG_NUM[3];break;
			case 100:SMG_SHOW[0] = SMG_NUM[4];break;
			default:break;
		}
	}
}
//数码管动态刷新————基于金沙滩数码管教学
void SMG_Scan()
{
	static uint8 index = 0;
	Sel_HC138(7);
	P0 = 0xff;
	Sel_HC138(0);//数码管消隐

	Sel_HC138(6);
	P0 = (0x80>>index);
	Sel_HC138(0);
	
	Sel_HC138(7);
	P0 = SMG_SHOW[index];
	Sel_HC138(0);
	
	index++;
	index &= 0x07;
}

总结

这次写代码明显不够细心,好几次忘记把状态位置零,并且iic读取数据是关闭中断也忘记,导致开始LED闪烁没办法,这次算是长个教训。。。

源码度娘

链接:https://pan.baidu.com/s/10vvrz0IukBPUC5V2ysLUjQ
提取码:xly2

标签:主观题,LED,SHOW,uint8,初赛,蓝桥,0xff,IIC,SMG
来源: https://blog.csdn.net/QWQ_DIODA/article/details/114945980

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有