蓝桥杯第15届单片机满分

1. 为什么会在第 5 位显示出 8?

复制代码
freq_jiao=freq+seg_jiao;//频率数据的最终结果
if(freq_jiao<0)
{
    wrong=1;//频率界面数码管显示LL,表示此状态错误
}
else wrong=0;

而在 serviceT1 的中断里,每 1000ms 更新一次 freq

当测试系统改变输入频率(比如变为 100)或你通过按键改变校准值(比如变为 -300)时,这两个动作之间存在时间差。

但是,最重要的一个逻辑漏洞出现在这里: 当 freq_jiao < 0 时,你的确设置了 wrong = 1,并给 seg_buf[3]~seg_buf[5] 赋了值 10但是,注意看 wrong == 0 的分支(正常显示分支):

测试平台的提示:"(同时,之前频率值不为LL)"。也就是说,它之前显示了一个正常的频率,比如 freq_jiao = 800。 此时 seg_buf[5] = 800 % 1000 / 100 = 8

然后你把校准值调成了 -300freq_jiao 变成了负数,wrong = 1。你设置了 seg_buf[5] = 10

为什么还会显示 8 呢? 因为你在 seg_proc() 中的这个逻辑:

复制代码
void seg_proc()
{
	if(seg_slow_down<80)return;  // <--- 注意这里!!!
	seg_slow_down=0;
    // ...

你的数码管数据更新是每 80ms 更新一次 。 但是在你的中断扫描函数 serviceT1 中,数码管是 1ms 扫一位的:

复制代码
	if(++seg_pos==8)seg_pos=0;
	seg_disp(seg_pos,seg_buf[seg_pos],seg_point[seg_pos]);

如果测试系统对你的程序进行了非常严格的时序捕捉 ,它在你在 80ms 的间隔内改变了按键或者频率,导致状态被置为错误(wrong=1),但是由于这 80ms 的延时,seg_buf 还没有来得及把 seg_buf[5]8 刷新成 10(熄灭)。

为了彻底解决"残影"或者未完全覆盖的问题,我建议你修改 case 0 的写法。直接在进入 case 0 时,无条件先清空所有显示位,然后再根据状态进行覆盖。

请将你的 seg_proc 函数的 case 0 修改为:

复制代码
		case 0://频率界面
			// 先把所有位置为熄灭,确保不会有残影
			seg_buf[0]=12;
			seg_buf[1]=10;
			seg_buf[2]=10;
			seg_buf[3]=10;
			seg_buf[4]=10;
			seg_buf[5]=10;
			seg_buf[6]=10;
			seg_buf[7]=10;
			
			if(wrong==0)
			{
				seg_buf[3]=(freq_jiao>=10000)?freq_jiao/10000:10;
				seg_buf[4]=(freq_jiao>=1000)?(freq_jiao%10000)/1000:10;
				seg_buf[5]=(freq_jiao>=100)?(freq_jiao%1000)/100:10;
				seg_buf[6]=(freq_jiao>=10)?(freq_jiao%100)/10:10;
				seg_buf[7]=(freq_jiao>=0)?(freq_jiao%10)/1:10;
			}
			else
			{
				// 错误状态显示 LL
				seg_buf[6]=16;
				seg_buf[7]=16;
			}
		break;

main.c

复制代码
#include <STC15F2K60S2.H>
#include <seg.h>
#include <intrins.h>
#include <ds1302.h>
#include <key.h>
#include <pcf8591.h>
#include <led.h>
#include <init.h>
unsigned int seg_slow_down;
unsigned char segmode;
idata unsigned char seg_buf[8]={10,10,10,10,10,10,10,10};
unsigned char seg_pos;
idata unsigned char seg_point[8]={0,0,0,0,0,0,0,0};
idata unsigned char ucled[8]={0,0,0,0,0,0,0,0};
unsigned char ad_slow_down;

idata unsigned char time[7]={0,0,0};//秒分时
idata unsigned char time_max[7]={0,0,0};
unsigned int t1000ms;
unsigned char t200ms;
bit liang;
idata long int freq;
idata long int freq_jiao;
idata long int freq_max;

bit s1;
bit s2;
unsigned char key_slow_down;
unsigned char key_val,key_down,key_old;
unsigned int seg_pp=2000;
unsigned int sta_pp=2000;

int seg_jiao;
idata bit wrong;
float v;
unsigned char v_out;






void serviceT1() interrupt 3//1ms进来一次
{
	seg_slow_down++;
	key_slow_down++;
	ad_slow_down++;
	if(++seg_pos==8)seg_pos=0;
	seg_disp(seg_pos,seg_buf[seg_pos],seg_point[seg_pos]);
	Led_Disp(ucled);
	
	if(++t1000ms==1000)//1s
	{
		t1000ms=0;
		
		TR0 = 0;//计数器T0停止计数
		freq=(TH0<<8)|TL0;
		TH0=0X00;
		TL0=0X00;
		TR0=1;
	}
	if(++t200ms==200)
	{
		t200ms=0;
		liang^=1;
	}



}
void working_proc()
{
	if(ad_slow_down<70) return ;
	ad_slow_down=0;
	
		ucled[0]=(segmode==0)?liang:0;
		ucled[1]=(freq_jiao>sta_pp)?liang:0;
		
		
	
	//DAC输出功能
		if(freq_jiao<0)//校准后为负
		{
			v=0;
			ucled[1]=1;
		}//DAC输出与测量频率关系
		else if(freq_jiao>=0&&freq_jiao<=500)
		{
			v=1.0;
		}
		else if(freq_jiao>=sta_pp)
		{
			v=5.0;
		}
		else
		{
			v=1.0+(4.0/(sta_pp-500))*(freq_jiao-500);
		}
	v_out=(unsigned char )(v*51.0);
	DA_zhuanhuan(v_out);
}
void seg_proc()
{
	int x;
	if(seg_slow_down<80)return;
	seg_slow_down=0;
	
	read_rtc(time);
	freq_jiao=freq+seg_jiao;//频率数据的最终结果
	if(freq_jiao<0)
	{
		wrong=1;//频率界面数码管显示LL,表示此状态错误
	}
	else wrong=0;
	
	if(freq_jiao>freq_max)
	{
		freq_max=freq_jiao;
		time_max[0]=time[0];
		time_max[1]=time[1];
		time_max[2]=time[2];
	}
	switch(segmode)
	{
		case 0://频率界面
		seg_buf[0]=12;
		seg_buf[1]=10;
		seg_buf[2]=10;
		seg_buf[3]=10;
		seg_buf[4]=10;
		seg_buf[5]=10;
		seg_buf[6]=10;
		seg_buf[7]=10;
		if(wrong==0)//没有错误
		{
			seg_buf[3]=(freq_jiao>=10000)?freq_jiao/10000:10;
			seg_buf[4]=(freq_jiao>=1000)?freq_jiao%10000/1000:10;
			seg_buf[5]=(freq_jiao>=100)?freq_jiao%1000/100:10;
			seg_buf[6]=(freq_jiao>=10)?freq_jiao%100/10:10;
			seg_buf[7]=(freq_jiao>=0)?freq_jiao%10/1:10;
		}
		else//存在错误
		{
			seg_buf[3]=10;
			seg_buf[4]=10;
			seg_buf[5]=10;
			seg_buf[6]=16;
			seg_buf[7]=16;
		}
		
			
		break;
		case 1://参数界面
			if(s1==0)//超限参数
			{
				seg_buf[0]=13;
				seg_buf[1]=1;
				seg_buf[2]=10;
				seg_buf[3]=10;
				
				seg_buf[4]=seg_pp/1000;
				seg_buf[5]=0;
				seg_buf[6]=0;
				seg_buf[7]=0;
			}
			else
			{
						seg_buf[0]=13;
						seg_buf[1]=2;
						seg_buf[2]=10;
						seg_buf[3]=10;
						if(seg_jiao==0)
						{
						
									seg_buf[4]=10;
									seg_buf[5]=10;
									seg_buf[6]=10;
									seg_buf[7]=0;
						}
						else if(seg_jiao>0)
						{
							
									seg_buf[4]=10;
									seg_buf[5]=seg_jiao/100;
									seg_buf[6]=0;
									seg_buf[7]=0;
						
						}
						else
						{
										x=-1*seg_jiao;
										seg_buf[4]=11;
										seg_buf[5]=x/100;
										seg_buf[6]=0;
										seg_buf[7]=0;
						}
						
			
			
			
			}
		
			
		break;
		case 2://时间界面
					seg_buf[0]=time[2]/16;
					seg_buf[1]=time[2]%16;
					seg_buf[2]=11;
					seg_buf[3]=time[1]/16;
					
					seg_buf[4]=time[1]%16;
					seg_buf[5]=11;
					seg_buf[6]=time[0]/16;
					seg_buf[7]=time[0]%16;
			
		break;
		case 3://回显界面
		seg_buf[0]=14;
		if(s2==0)//频率回显界面
		{
					seg_buf[1]=12;
					seg_buf[2]=10;
					seg_buf[3]=(freq_max>=10000)?freq_max/10000:10;
					
					seg_buf[4]=(freq_max>=1000)?freq_max%10000/1000:10;
					seg_buf[5]=(freq_max>=100)?freq_max%1000/100:10;
					seg_buf[6]=(freq_max>=10)?freq_max%100/10:10;
					seg_buf[7]=(freq_max>=0)?freq_max%10/1:10;
		}
		else//时间回显界面
		{
						seg_buf[1]=15;
						seg_buf[2]=time_max[2]/16;
						seg_buf[3]=time_max[2]%16;
						seg_buf[4]=time_max[1]/16;
						seg_buf[5]=time_max[1]%16;
						seg_buf[6]=time_max[0]/16;
						seg_buf[7]=time_max[0]%16;
		
		
		
		}
		
			
		break;
		
	
	
	
	
	}

}
void key_proc()
{
	if(key_slow_down<10)return ;
	key_slow_down=0;
	
	key_val = key_read();
	key_down = key_val & (key_old ^ key_val);
	key_old = key_val;
	switch(key_down)
	{
		case 4:
			if(++segmode==4)segmode=0;
			if(segmode==1)//进入参数界面
			{
				seg_pp=sta_pp;
//				seg_jiao=sta_jiao;
				s1=0;//超限参数界面
			}
			if(segmode==2)//进入时间界面
			{
				sta_pp=seg_pp;
//				sta_jiao=seg_jiao;
			}
			if(segmode==3)//进入回显界面
			{
				s2=0;//频率回显界面
			}
			
			
		break;
		case 5:
			if(segmode==1)
			s1^=1;
			if(segmode==3)
			s2^=1;
			
		break;
		case 8:
			if(segmode==1)
			{
					if(s1==0)//超限参数界面
					{
						seg_pp+=1000;
						if(seg_pp>9000)seg_pp=9000;
					
					
					
					}
					else//校准值参数界面
					{
						seg_jiao+=100;
						if(seg_jiao>900)seg_jiao=900;
					
					
					
					}
			
			
			}
			
		break;
		case 9:
			if(segmode==1)
			{
				
					if(s1==0)//超限参数界面
					{
						seg_pp-=1000;
						if(seg_pp<1000)seg_pp=1000;
					
					
					
					}
					else//校准值参数界面
					{
						seg_jiao-=100;
						if(seg_jiao<-900)seg_jiao=-900;
					
					
					
					}
			
			
			
			}
			
		break;
	
	
	
	
	
	}
	



}

void main()
{
	Timer1_Init();
	Timer0_Init();
	sys_init();
	set_rtc(time);
	while(1)
	{
		seg_proc();
		working_proc();
		key_proc();
	
	
	
	}


}

seg.c

复制代码
#include <seg.h>
unsigned char code seg_wei[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
unsigned char code seg_duan[]={0xC0,0xF9,0xA4,0xB0,0x99,
                          0x92,0x82,0xF8,0x80,0x90,
                          0xff,0xbf,0x8e,0x8c,0x89,0x88,0xc7};

void seg_disp(unsigned char wei,duan,point)
{
	P0=0XFF;
	P2=P2&0X1F|0XE0;
	P2&=0X1F;
	
	P0= seg_wei[wei];
	P2=P2&0X1F|0Xc0;
	P2&=0X1F;
	
	P0=seg_duan[duan];
	if(point==1)P0&=0x7f;
	P2=P2&0X1F|0XE0;
	P2&=0X1F;
}

ds1302.c

复制代码
#include <ds1302.h>
#include <intrins.h>

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

unsigned char code write_rtc_addr[7]={0x80,0x82,0x84,0x86,0x88,0x8a,0x8c};
unsigned char code read_rtc_addr[7]={0x81,0x83,0x85,0x87,0x89,0x8b,0x8d};

							

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

void set_rtc(unsigned char *time)
{
	unsigned char i;
	EA=0;
	Write_Ds1302_Byte(0x8e,0x00);
	
	for(i=0;i<7;i++)
	{
		Write_Ds1302_Byte(write_rtc_addr[i],time[i]);
	}
	
	Write_Ds1302_Byte(0x8e,0x80);
	EA=1;
}

void read_rtc(unsigned char *time)
{
	unsigned char i;
	EA=0;
	for(i=0;i<7;i++)
	{
		time[i]=Read_Ds1302_Byte (read_rtc_addr[i]);
	}
	EA=1;
}

key.c

复制代码
#include <key.h>

unsigned char key_read(void)
{
	unsigned char temp=0;
	AUXR &= ~0x10;
	P44=0;P42=1;P35=1;
	//P34=1;
	if(P30==0)temp=7;
	if(P31==0)temp=6;
	if(P32==0)temp=5;
	if(P33==0)temp=4;
	
	
	P44=1;P42=0;P35=1;
	//P34=1;
	if(P30==0)temp=11;
	if(P31==0)temp=10;
	if(P32==0)temp=9;
	if(P33==0)temp=8;
	
	
	
	P44=1;P42=1;P35=0;
	//P34=1;
	if(P30==0)temp=15;
	if(P31==0)temp=14;
	if(P32==0)temp=13;
	if(P33==0)temp=12;
	
	AUXR|=0X10;
	return temp;
}

pcf8591.c

复制代码
#include <pcf8591.h>
#include <intrins.h>


sbit sda=P2^1;
sbit scl=P2^0;

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

void DA_zhuanhuan(unsigned char dat)
{
	EA=0;
	
	I2CStart();
	I2CSendByte(0x90);
	I2CWaitAck();
	
	I2CSendByte(0x41);
	I2CWaitAck();
	
	I2CSendByte(dat);
	I2CWaitAck();
	I2CStop();
	
	EA=1;
}

//unsigned char AD_zhuanhuan(unsigned char addr)
//{
//	unsigned char temp;
//	
//	I2CStart();
//	I2CSendByte(0x90);
//	I2CWaitAck();
//	I2CSendByte(addr);
//	I2CWaitAck();
//	
//	
//	I2CStart();
//	I2CSendByte(0x91);
//	I2CWaitAck();
//	
//	temp=I2CReceiveByte();
//	I2CSendAck(1);
//	I2CStop();
//	return temp;
//	
//	
//	
//	
//	




//}

led.c

复制代码
#include <led.h>
idata  unsigned char temp_1 = 0x00;
 idata  unsigned char temp_old_1 = 0xff;

void Led_Disp(unsigned char *ucLed)
{
   unsigned char temp;
   temp_1=0x00;
   temp_1 = (ucLed[0] << 0) | (ucLed[1] << 1) | (ucLed[2] << 2) | (ucLed[3] << 3) |
         (ucLed[4] << 4) | (ucLed[5] << 5) | (ucLed[6] << 6) | (ucLed[7] << 7);
  if (temp_1 != temp_old_1)
  {
    P0 = ~temp_1;
    
    // 操作P2锁存器
    temp = P2 & 0x1f;    // 保留P2的低5位
    temp = temp | 0x80;  // 与0x80进行或操作
    P2 = temp;           // 写入P2
    temp = P2 & 0x1f;    // 保留P2的低5位
    P2 = temp;           // 写入P2,关闭锁存器
    
    temp_old_1 = temp_1;
  }
}

init.c

复制代码
#include <init.h>
void Timer1_Init(void)		//1毫秒@12.000MHz
{
	AUXR &= 0xBF;			//定时器时钟12T模式
	TMOD &= 0x0F;			//设置定时器模式
	TL1 = 0x18;				//设置定时初始值
	TH1 = 0xFC;				//设置定时初始值
	TF1 = 0;				//清除TF1标志
	TR1 = 1;				//定时器1开始计时
	ET1=1;
	EA=1;
}
void Timer0_Init(void)		//1毫秒@12.000MHz
{
	AUXR &= 0x7F;			//定时器时钟12T模式
	TMOD &= 0xF0;			//设置定时器模式
	TMOD |=0X05;
	TL0 = 0X00;				//设置定时初始值
	TH0 = 0x00;				//设置定时初始值
	TF0 = 0;				//清除TF0标志
	TR0 = 1;				//定时器0开始计时
}

void sys_init()
{
 P0=0x00;//蜂鸣器
 P2=P2&0x1f|0xa0;
 P2&=0x1f;
	
	P0=0xff;//led
	P2=P2&0x1f|0x80;
	P2&=0x1f;
}
相关推荐
June bug2 小时前
全链路测试
功能测试·面试·职场和发展
倦王2 小时前
力扣日刷47
算法·leetcode·职场和发展
The_era_achievs_hero3 小时前
产品360度展示(蓝桥杯)
蓝桥杯
不做无法实现的梦~3 小时前
STM32解析PPM协议
stm32·单片机·嵌入式硬件
abant24 小时前
leetcode 239 单调队列 需要一些记忆
算法·leetcode·职场和发展
czhaii4 小时前
基于Arm Cortex-M7内核GD32H7
单片机·嵌入式硬件
番茄灭世神4 小时前
MCU开发常见软件BUG总结(持续更新)
c语言·stm32·单片机·嵌入式·gd32
EVERSPIN5 小时前
SQPI PSRAM为单片机提供RAM扩展方案
单片机·嵌入式硬件·psram·sqpi psram
进击的小头5 小时前
第6篇:嵌入式芯片算力核心来源:多级流水线架构与指令并行机制详解
单片机·嵌入式硬件·架构