蓝桥杯单片机第九届省赛-彩虹控制灯

        第九届省赛总体难度不大,和第八届有相似的地方(比如数码管选中闪烁——其实只要使用定时器就可以解决),疑难点主要是LED灯亮的四个等级,其实也是可以通过PWM波来调制的。但我试了一下感觉效果一般,感觉是我还不太会用定时器(新手),就直接采用延时的方法进行这一步。话不多说,接下来将呈现最简洁,通俗易懂的代码。

        首先看题目,这届赛题也是只使用了IIC通信这一个底层程序,其中包括Rb2电位器的AD转换和EEPROM的存储和读取。可以先添加底层程序(由官网给出)。

iic.c

#include <stc15f2k60s2.h>
#include <iic.h>
#include <intrins.h>
#define DELAY_TIME	5
sbit sda=P2^1;
sbit scl=P2^0;
//
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;
}


uchar IIC_Read(uchar add)
{
	uchar temp;
	I2CStart();
	I2CSendByte(0x90);
	I2CWaitAck();
	I2CSendByte(add);
	I2CWaitAck();
	I2CStop();
	
	I2CStart();
	I2CSendByte(0x91);
	I2CWaitAck();
	temp=I2CReceiveByte();
	I2CWaitAck();
	I2CStop();
	return temp;
}

void EEPROM_Write(uchar add,uchar dat)
{
	I2CStart();
	I2CSendByte(0xa0);
	I2CWaitAck();
	I2CSendByte(add);
	I2CWaitAck();
	I2CSendByte(dat);
	I2CWaitAck();
	I2CStop();
}

uchar EEPROM_Read(uchar add)
{
	uchar temp;
	I2CStart();
	I2CSendByte(0xa0);
	I2CWaitAck();
	I2CSendByte(add);
	I2CWaitAck();
	I2CStop();
	
	I2CStart();
	I2CSendByte(0xa1);
	I2CWaitAck();
	temp=I2CReceiveByte();
	I2CWaitAck();
	I2CStop();
	return temp;
}

 iic.h

#ifndef _IIC_H
#define _IIC_H

#define uchar unsigned char
#define uint unsigned int

static void I2C_Delay(unsigned char n);
void I2CStart(void);
void I2CStop(void);
void I2CSendByte(unsigned char byt);
unsigned char I2CReceiveByte(void);
unsigned char I2CWaitAck(void);
uchar IIC_Read(uchar add);
void EEPROM_Write(uchar add,uchar dat);
uchar EEPROM_Read(uchar add);

#endif

底层程序代码写完之后,接下来可以写基本运行函数,比如数码管显示,外设初始化,延时函数等

数码管动态显示

void display(void)//动态显示
{
	uchar i;
	for(i=0;i<8;i++)
	{
		P0=0x00;
		P2=(P2&0x1f)|0xc0;
		P0=dis_bit[i];
		P2=0;//防止外设冲突
		
		P0=0xff;
		P2=(P2&0x1f)|0xf0;
		P0=tab[dis_buf[i]];//根据dis_buf[]数组修改值,在tab[]数组中进行段选
		P2=0;
		
		Delayms(1);
		
		P0=0xff;//消除鬼影
		P2=(P2&0x1f)|0xf0;
		P2=0;
	}
}

外设初始化与延时函数

void Allinit(void)
{
	P2=(P2&0x1f)|0xa0;
	P0=0x00;
	P2=0;
	P2=(P2&0x1f)|0x80;
	P0=0xff;
	P2=0;
	P2=(P2&0x1f)|0xc0;
	P0=0x00;
	P2=0;
	P2=(P2&0x1f)|0xf0;
	P0=0xff;
	P2=0;
}
void Delayms(uint ms)
{
	uint i,j;
	for(i=ms;i>0;i--)
	for(j=845;j>0;j--);
}

 接下来可以按照自己编程的习惯,写独立按键(别忘记修改跳线帽)或者数码显示和Led灯,个人喜欢调用函数的方式,使代码更简洁,主函数如下(有注释讲解):

main.c

#include <stc15f2k60s2.h>
#include <iic.h>
#include <intrins.h>

	
uchar tab[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xbf,0xff};
uchar dis_bit[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};//位选
uchar dis_buf[]={11,11,11,11,11,11,11,11};//段选
uchar mode1[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};//四种工作模式
uchar mode2[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};
uchar mode3[]={0x7e,0xbd,0xdb,0xe7};
uchar mode4[]={0x7e,0xbd,0xdb,0xe7};
void display(void);//显示函数
void Delayms(uint ms);	//延时函数
void Allinit(void);//外设初始化函数
void keyscan(void);//矩阵键盘扫描
void shuma(void);//数码管显示
void Timer0Init(void);//定时器初始化
void Led_Level_Judge(void);//判断亮度等级
void Led_work(void);//Led工作模式
void delay(uchar t);//空延时函数
uchar AD;//Rb2的输出值
bit qidong=0;//启动1/停止0
uchar mode=0;//LED四种工作模式
uchar shezhi=0;//设置界面 0熄灭 1运行 2间隔
bit yxflag=0;//运行闪烁标志位
bit spanflag=0;//流转间隔闪烁标志位
uint i;//0.8s闪烁
uint tt;//判断是否达到流转时间
uchar index;//模式下的索引
uchar Copyspan;//由于存储只能是0-256,再建一个变量缩小十倍span
uchar finish;//设置完成
uchar t;//改变亮度变化的快慢,持续的时间
uchar pwm;//延迟的占空比
uchar noshezhi=0;//不在设置状态时按下s4
uchar level=0;//亮度等级显示
uint span=400;//流转间隔
void main(void)
{
	Allinit();
	Timer0Init();
	span=EEPROM_Read(0x10)*10;//地址从0x00-0xff都可以进行存储,存进去是/10,读出时要*10
	Delayms(10);//读和写EEPROM都应延迟5ms以上,防止太快读写错误
	while(1)
	{
		AD=IIC_Read(0x03);//0x03为rb2电位器的地址
		keyscan();
		Led_Level_Judge();//亮度等级判断函数
		shuma();//数码如何显示函数
		display();
	}
}

void Led_work(void)//led四种工作模式
{
	if(mode==1)
	{
		P2=(P2&0x1f)|0x80;P0=mode1[index];P2=0;
	}
	else if(mode==2)
	{
		P2=(P2&0x1f)|0x80;P0=mode2[7-index];P2=0;
	}
	else if(mode==3)
	{
		P2=(P2&0x1f)|0x80;P0=mode3[index];P2=0;
	}
	else if(mode==4)
	{
		P2=(P2&0x1f)|0x80;P0=mode4[3-index];P2=0;
	}
}

void keyscan(void)//独立按键程序
{
	if(P30==0)//s7
	{
		Delayms(10);
		if(P30==0)
		{
			if(finish==1)
			{
				qidong=~qidong;	//取反
			}	
		}
		while(P30==0);//等待按键弹起
	}
	if(P31==0)//s6
	{
		Delayms(10);
		if(P31==0)
		{
			shezhi++;finish=0;if(mode==0)mode=1;//防止led还未配置完就亮
			if(shezhi==3){shezhi=0;finish=1;index=0;Copyspan=span/10;EEPROM_Write(0x10,Copyspan);Delayms(10);}
		}
		while(P31==0);
	}
	if(P32==0)//s5
	{
		Delayms(10);
		if(P32==0)
		{
			if(shezhi==1)
			{
				mode++;
				if(mode>4)mode=1;
			}
			else if(shezhi==2)
			{
				span+=100;
				if(span>1200)span=400;
			}
		}
		while(P32==0);
	}
//以下是长按操作,有很多写法,当然通常是设置标志位,当然也可以在等待按键弹起里面写
	if(P33==0&&(shezhi==1||shezhi==2))//设置模式下的s4
	{
		Delayms(10);
		if(P33==0)
		{
			if(shezhi==1)
			{
				mode--;
				if(mode<1)mode=4;
			}
			else if(shezhi==2)
			{
				span-=100;
				if(span<400)span=1200;
			}
		}
		while(P33==0);
	}
	if(P33==0&&(shezhi!=1||shezhi!=2))//非设置模式下的s4
	{
		Delayms(10);
		if(P33==0)
		{
			noshezhi=1;
		}
	}
	if(P33==1)
	{
		noshezhi=0;
	}
}


void shuma(void)
{
	if(shezhi==0)
	{
		dis_buf[0]=11;dis_buf[1]=11;dis_buf[2]=11;dis_buf[3]=11;
		dis_buf[4]=11;dis_buf[5]=11;dis_buf[6]=11;dis_buf[7]=11;
	}
	if(shezhi==1&&yxflag==0)//0.8s为间隔闪烁
	{
		dis_buf[0]=10;dis_buf[1]=mode;dis_buf[2]=10;dis_buf[3]=11;
		dis_buf[4]=span/1000;dis_buf[5]=span%1000/100;dis_buf[6]=span%100/10;dis_buf[7]=span%10;
	}
	else if(shezhi==1&&yxflag==1)
	{
		dis_buf[0]=11;dis_buf[1]=11;dis_buf[2]=11;dis_buf[3]=11;
		dis_buf[4]=span/1000;dis_buf[5]=span%1000/100;dis_buf[6]=span%100/10;dis_buf[7]=span%10;
	}
	if(shezhi==2&&spanflag==0)
	{
		dis_buf[0]=10;dis_buf[1]=mode;dis_buf[2]=10;dis_buf[3]=11;
		dis_buf[4]=span/1000;dis_buf[5]=span%1000/100;dis_buf[6]=span%100/10;dis_buf[7]=span%10;
	}
	else if(shezhi==2&&spanflag==1)
	{
		dis_buf[0]=10;dis_buf[1]=mode;dis_buf[2]=10;dis_buf[3]=11;
		dis_buf[4]=11;dis_buf[5]=11;dis_buf[6]=11;dis_buf[7]=11;
	}
	if(noshezhi==1)//长按显示
	{
		dis_buf[0]=11;dis_buf[1]=11;dis_buf[2]=11;dis_buf[3]=11;
		dis_buf[4]=11;dis_buf[5]=11;dis_buf[6]=10;dis_buf[7]=level;
	}
	else if(shezhi==0&&noshezhi==0)
	{
		dis_buf[0]=11;dis_buf[1]=11;dis_buf[2]=11;dis_buf[3]=11;
		dis_buf[4]=11;dis_buf[5]=11;dis_buf[6]=11;dis_buf[7]=11;
	}
}


void Led_Level_Judge(void)//亮度等级判断函数
{
	if(AD>=0&&AD<64){level=1;pwm=25;}
	else if(AD>=64&&AD<128){level=2;pwm=50;}
	else if(AD>=128&&AD<196){level=3;pwm=75;}
	else if(AD>=196&&AD<256){level=4;pwm=100;}
}
void Timer0Init(void)		//5毫秒@11.0592MHz  //可直接去stc-isp里复制
{
	AUXR |= 0x80;		//定时器时钟1T模式
	TMOD &= 0xF0;		//设置定时器模式
	TL0 = 0x00;		//设置定时初值
	TH0 = 0x28;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
	EA=1;
	ET0=1;
}
void Tim0(void) interrupt 1
{
	if(shezhi==1||shezhi==2)//在设置模式下才开始闪烁
	{
		i++;
		if(i==160)
		{
			i=0;
			if(shezhi==1)yxflag=~yxflag;
			else if(shezhi==2)spanflag=~spanflag;
		}
	}
	
	if(qidong==1&&finish==1)
	{
		tt++;
		if(tt==span/5)
		{
			for(t=0;t<200;t++)
			{
				Led_work();
				delay(pwm);//通过亮的时间从而来控制亮度
				P2=(P2&0x1f)|0x80;P0=0xff;P2=0;
				delay(100-pwm);
			}
			tt=0;index++;//时间到移位
			if(mode==1||mode==2)
			{
				if(index==8)index=0;
			}
			else if(mode==3||mode==4)
			{
				if(index==4)index=0;
			}
		}
	}
	else if(qidong==0)
	{
		tt=0;
	}
}
void delay(uchar t)//一定要是空循环,不然用软件生成的一闪一闪很明显
{
	while(t--);
}
void Allinit(void)
{
	P2=(P2&0x1f)|0xa0;
	P0=0x00;
	P2=0;
	P2=(P2&0x1f)|0x80;
	P0=0xff;
	P2=0;
	P2=(P2&0x1f)|0xc0;
	P0=0x00;
	P2=0;
	P2=(P2&0x1f)|0xf0;
	P0=0xff;
	P2=0;
}
void display(void)
{
	uchar i;
	for(i=0;i<8;i++)
	{
		P0=0x00;
		P2=(P2&0x1f)|0xc0;
		P0=dis_bit[i];
		P2=0;
		
		P0=0xff;
		P2=(P2&0x1f)|0xf0;
		P0=tab[dis_buf[i]];
		P2=0;
		
		Delayms(1);
		
		P0=0xff;//消除鬼影
		P2=(P2&0x1f)|0xf0;
		P2=0;
	}
}
void Delayms(uint ms)
{
	uint i,j;
	for(i=ms;i>0;i--)
	for(j=845;j>0;j--);
}

以上就是蓝桥杯单片机第九届省赛的内容,欢迎大佬们的指点。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值