第十一届蓝桥杯单片机省赛(第一套)——基于西风模版

一、题目

二、难点剖析

1.计数值触发条件

思路分析:由图可知计数值加1的条件是:Vain3在下降沿时等于参数值Vp,所以在程序中定义了一个Vain3_old去记录上一个Vain3的值,通过判断Vain3_old>Vp 并且 Vain3 <=Vp实现计数值的加1了。代码实现如下:

	//计数功能
	if((vain3 <= temp_v[0]*10) && (vain3_old>temp_v[0]*10))
		count++;
	vain3_old = vain3 ;

2.无效按键状态的判断

        通过 switch 语句对按键值 Key_down 进行判断,当按键为无效按键时,没有与之匹配的case语句,所以则用default语句去处理无效按键,值得注意的是:当吗没有按键按下时,Key_down=0,所以得将这种情况排除掉,只记录当Key_down 不为0的状态,无效按键的计数值才加1,且只要有其他按键按下,无效按键的计数值就置0。

switch(Key_down)
	{
		case 12:
			if(++Smg_mode == 3) Smg_mode = 0;
			if(Smg_mode == 2) 
				{ 
					temp_v[0]  = temp_v1;
					EEPROM_write(temp_v,0,1);
				}
			no_key = 0;
	  break;
		
		case 13:
			if(Smg_mode == 2){
				count = 0;
			}
			no_key = 0;
	  break;
		
		case 16:	//参数电压加0.5,扩大100倍,便于处理
			if(Smg_mode == 1){
				if(temp_v1 == 50)	temp_v1 = 0;
				else
					temp_v1 = temp_v1 + 5;
			}
			no_key = 0;
	  break;
		
		case 17:	//参数电压减0.5
			if(Smg_mode == 1){
				if(temp_v1 == 0)	temp_v1 = 50;
				else
					temp_v1 = temp_v1 - 5;
			}
			no_key = 0;
	  break;
		
		default:if(Key_down != 0) no_key++;	//当没有按键按下时,Key_down为0,所以排除0值
	
	}

三.参考程序

1.main.c

/*头文件声明区*/
#include <STC15F2K60S2.H>
#include <key.H>
#include <seg.H>
#include <iic.H>


/*变量声明区*/
unsigned char Smg_slow_down,Key_slow_down;//数码管、扫描按键减速变量
unsigned char Key_up,Key_down,Key_old,Key_val;
unsigned char Smg_pos,Smg_mode = 0;	//数码管扫描变量,数码管模式变量

unsigned char Smg_buff[8] = {10,10,10,10,10,10,10,10};
unsigned char Smg_point_buff[8] = {0,0,0,0,0,0,0,0};

unsigned char Led_buff[8] = {0,0,0,0,0,0,0,0};
unsigned int vain3 = 0;	//读取滑动变阻器的值,为实际值的100倍
unsigned int vain3_old = 0;	

unsigned char temp_v1 = 10;//电压参数
unsigned int temp_vdsip = 100;//数码管显示电压参数
unsigned int count = 0;	//计数值变量
unsigned char temp_v[] = {10};//电压参数

unsigned int Led_5s = 0;//VAIN3<参数5s时,L1亮
bit enable_5s = 0;//使能5s计数器
unsigned char no_key = 0;//无效按键计数参数

/*按键处理函数*/
void Key_proc()
{
	if(Key_slow_down)	return;
	Key_slow_down = 1;
	
	Key_val = Key_read();
	Key_down = Key_val & (Key_old ^Key_val);
	Key_up = ~Key_val & (Key_old ^Key_val);
	Key_old = Key_val;
	
	switch(Key_down)
	{
		case 12:
			if(++Smg_mode == 3) Smg_mode = 0;
			if(Smg_mode == 2) 
				{ 
					temp_v[0]  = temp_v1;
					EEPROM_write(temp_v,0,1);
				}
			no_key = 0;
	  break;
		
		case 13:
			if(Smg_mode == 2){
				count = 0;
			}
			no_key = 0;
	  break;
		
		case 16:	//参数电压加0.5,扩大100倍,便于处理
			if(Smg_mode == 1){
				if(temp_v1 == 50)	temp_v1 = 0;
				else
					temp_v1 = temp_v1 + 5;
			}
			no_key = 0;
	  break;
		
		case 17:	//参数电压减0.5
			if(Smg_mode == 1){
				if(temp_v1 == 0)	temp_v1 = 50;
				else
					temp_v1 = temp_v1 - 5;
			}
			no_key = 0;
	  break;
		
		default:if(Key_down != 0) no_key++;	//当没有按键按下时,Key_down为0,所以排除0值
	
	}
}

/*信息处理函数*/
void Smg_proc()
{
	if(Smg_slow_down)	return;
	Smg_slow_down = 1;
	
	//信息读取区域
	vain3 = PCF8591_ADC();
	
	/*数据显示区域*/
	switch(Smg_mode){
	
		case 0:
			Smg_buff[0] = 18;
		
			Smg_buff[5] = vain3/100;
			Smg_buff[6] = (vain3/10)%10;
			Smg_buff[7] = vain3%10;
			Smg_point_buff[5] = 1;
		break;
		
		case 1:
			
			Smg_buff[0] = 17;
			temp_vdsip = temp_v1*10;
			Smg_buff[5] = temp_vdsip/100;
			Smg_buff[6] = (temp_vdsip/10)%10;
			Smg_buff[7] = temp_vdsip%10;
			
			Smg_point_buff[5] = 1;
		
		break;
		
		case 2:
			
			Smg_buff[0] = 19;
		
			Smg_buff[5] = 10;
			
		//当计数值高位为0时,熄灭
			if(count/10 == 0)	Smg_buff[6] = 10;
			else Smg_buff[6] = count/10;
			Smg_buff[7] = count%10;
			
			Smg_point_buff[5] =0;
		
		break;
		
	}
	

}

/*其他处理函数*/
void Led_proc()
{
//	EEPROM_write(temp_v,0,1);

	//计数功能
	if((vain3 <= temp_v[0]*10) && (vain3_old>temp_v[0]*10))
		count++;
	vain3_old = vain3 ;
	
	//L1:实时电压小于参数超过5s,L1点亮
	if(vain3<temp_v[0]*10)
		enable_5s = 1;
	else{
		enable_5s = 0;
		Led_buff[0] = 0;
	}
	
	//大于的原因:在非定时器中断的程序中不一定能执行到刚好等于4999的状态,定时中断1ms执行一次
	if(Led_5s > 5000-1)
		Led_buff[0] = 1;
	
	
	//L2:计数值为奇数时,点亮
	if(count%2 == 0)
		Led_buff[1] = 0;
	else
		Led_buff[1] = 1;
	
	//L3:无效按键的次数大于等于3次时,L3亮
		if(no_key >2 )
			Led_buff[2] = 1;
		else
			Led_buff[2] = 0;
}


void Timer0Init(void)		//1毫秒@12.000MHz
{
	AUXR &= 0x7F;		//定时器时钟12T模式
	TMOD &= 0xF0;		//设置定时器模式
	TL0 = 0x18;		//设置定时初值
	TH0 = 0xFC;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
	
	
	ET0 = 1;
	EA = 1;
}

void Timer0_service()  interrupt 1
{
	//Smg_slow_down,Key_slow_down
	if(++Smg_slow_down == 200) Smg_slow_down = 0;
	if(++Key_slow_down == 10) Key_slow_down = 0;
	
	if(++Smg_pos == 8) Smg_pos = 0;
	
	Led_disp(Smg_pos,Led_buff[Smg_pos]);
	Smg_disp(Smg_pos,Smg_buff[Smg_pos],Smg_point_buff[Smg_pos]);
	
	if(enable_5s)	Led_5s++;
	else Led_5s = 0;
		

}


void main()
{
	System_init();
	Timer0Init();
	EEPROM_write(temp_v,0,1);
	PCF8591_ADC();	//先读一次数据丢掉,避免初始显示不正确
	
	while(1)
	{
		Key_proc();
	  Smg_proc();
		Led_proc();
	}
}

2.key.c

#include <key.H>

unsigned char Key_read()
{
	unsigned char temp = 0;
	
	//独立按键
//	if(P33 == 0)	temp = 4;
//	if(P32 == 0)	temp = 5;
//	if(P31 == 0)	temp = 6;
//	if(P30 == 0)	temp = 7;
	
	//矩阵按键
	P44 = 0;P42 =1 ;P35 = 1;P34 = 1;
	if(P33 == 0)	temp = 4;
	if(P32 == 0)	temp = 5;
	if(P31 == 0)	temp = 6;
	if(P30 == 0)	temp = 7;
	
	P44 = 1;P42 =0 ;P35 = 1;P34 = 1;
	if(P33 == 0)	temp = 8;
	if(P32 == 0)	temp = 9;
	if(P31 == 0)	temp = 10;
	if(P30 == 0)	temp = 11;
	
	P44 = 1;P42 =1 ;P35 = 0;P34 = 1;
	if(P33 == 0)	temp = 12;
	if(P32 == 0)	temp = 13;
	if(P31 == 0)	temp = 14;
	if(P30 == 0)	temp = 15;
	
	P44 = 1;P42 =1 ;P35 = 1;P34 = 0;
	if(P33 == 0)	temp = 16;
	if(P32 == 0)	temp = 17;
	if(P31 == 0)	temp = 18;
	if(P30 == 0)	temp = 19;
	
	return temp;
}

3.seg.c

#include <seg.H>

code unsigned char Seg_Table[]={                       //标准字库
//   0    1    2    3    4    5    6    7    8    9    全灭  B    C    D    E    F
    0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x00,0x7C,0x39,0x5E,0x79,0x71,
//black - P    U    N    K    L    N    o   P    U     t    G    Q    r   M    y
    0x00,0x73,0x3E,0x37,0x70,0x38,0x37,0x5C,0x73,0x3E,0x78,0x3d,0x67,0x50,0x37,0x6e,
    0xBF,0x86,0xDB,0xCF,0xE6,0xED,0xFD,0x87,0xFF,0xEF,0x46};  
static unsigned char temp = 0x00;
static unsigned char temp_old = 0x00;

void Select_138(unsigned char channel)
{
	switch(channel)
	{
		case 4:	//led
				P2 = P2 & 0X1F	| 0X80;
		break;
		case 5:	//蜂鸣器、继电器
				P2 = P2 & 0X1F	| 0Xa0;
		break;
		case 6:	//位选
				P2 = P2 & 0X1F	| 0Xc0;
		break;
		case 7:	//段选
				P2 = P2 & 0X1F	| 0Xe0;
		break;
	}
	P2 &= 0X1F; //关闭锁存器

}

void System_init()
{
	//关闭LED、蜂鸣器、继电器
	P0 = 0Xff;
	Select_138(4);
	
	P0 = 0X00;
	Select_138(5);

}


void Led_disp(unsigned char pos,enable)
{
	static unsigned char temp_led = 0x00;
	static unsigned char temp_led_old = 0x00;
	
	if(enable)
		temp_led = temp_led | (0x01<<pos);
	else
		temp_led = temp_led & ~(0x01<<pos);
	
	if(temp_led != temp_led_old )
	{
		P0 = ~temp_led;
		Select_138(4);
		temp_led_old  = temp_led;
	}

}

void Buzz(unsigned char enable)
{
	if(enable)
		temp |= 0x40;
 	else
	  temp &= ~0x40;
	
	if(temp != temp_old )
	{
		P0 = temp;
		Select_138(5);
		temp_old  = temp;
	}

}

void Ready(unsigned char enable)
{
	if(enable)
		temp |=0x10;
	else
	  temp &= ~0x10;
	
	if(temp != temp_old )
	{
		P0 = temp;
		Select_138(5);
		temp_old  = temp;
	}
}


void Smg_disp(unsigned char wela,dula,point)
{
	P0 = 0XFF;//消隐
	Select_138(7);
	
	P0 = 0X01<<wela;
	Select_138(6);
	
	P0 = ~Seg_Table[dula];//消隐
	Select_138(7);
	
	if(point)
	{
		P0 &=0X7F;
		Select_138(7);
	}
}



4.iic.c

/*	#   I2C代码片段说明
	1. 	本文件夹中提供的驱动代码供参赛选手完成程序设计参考。
	2. 	参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题
		中对单片机时钟频率的要求,进行代码调试和修改。
*/
#include <iic.H>
#include <intrins.H>

#define DELAY_TIME	10
sbit scl = P2^0;
sbit sda = P2^1 ;

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

unsigned int PCF8591_ADC()
{
	unsigned char dat;
	I2CStart();
	I2CSendByte(0X90);
	I2CWaitAck();
	I2CSendByte(0X03);
	I2CWaitAck();
	
	I2CStart();
	I2CSendByte(0X91);
	I2CWaitAck();
	dat = I2CReceiveByte();
	I2CSendAck(1);
	I2CStop();
	return dat*(5.0/255)*100;
	
}


//函数名:写EEPROM函数
//入口函数:需要写的字符串,写入的地址(务必为8的整数),写入数量
//返回值:无
//函数功能:向EEPROM的某个地址写入字符串中特定数量的字符
void EEPROM_write(unsigned char *EEPROM_string,unsigned char addr,unsigned num)
{
	I2CStart();					//发送开始信号
	I2CSendByte(0xa0);	//选择EEPROM芯片,确定写模式
	I2CWaitAck();				//等待EEPROM反馈
	
	I2CSendByte(addr);	//选择EEPROM芯片,确定写模式
	I2CWaitAck();				//等待EEPROM反馈
	
	while(num--)
	{
		I2CSendByte(*EEPROM_string++);		//写入信息
		I2CWaitAck();					//等待EEPROM反馈
		I2C_Delay(200);
	}
	I2CStop();						//停止发送
}

//函数名:读EEPROM函数
//入口函数:读取数据需要存储的字符串,读取的地址(务必为8的整数),读取数量
//返回值:无
//函数功能:读取EEPROM的某个地址的数据,并存放在字符串数组中
void EEPROM_read(unsigned char *EEPROM_string,unsigned char addr,unsigned num)
{
	I2CStart();
	I2CSendByte(0xa0);	//选择EEPROM芯片,确定写模式
	I2CWaitAck();				//等待EEPROM反馈
	
	I2CSendByte(addr);	//写入读取的数据地址
	I2CWaitAck();				//等待EEPROM反馈
	
	I2CStart();					//发送开启信号
	I2CSendByte(0xa1);	//选择EEPROM芯片,确定读模式
	I2CWaitAck();				//等待EEPROM反馈
	
	while(num--)
	{
		*EEPROM_string++ = I2CReceiveByte();
		if(num)	I2CSendAck(0);	//发送应答
		else I2CSendAck(1);			//不应答
	}
	I2CStop();			//停止发送
}

四、现存问题

        烧录程序后在板子上看到的所有功能均实现了,但是在四梯科技上测评时还有两个问题,如下:

       程序部分在网站上的得分为67.2/70,这2.8分是真没发现问题在哪儿,要是比赛时能写到这个程度也不错了,不去纠结这点儿分了。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值