【蓝桥杯单片机】学长经验总结

        本文所述来自方天宇学长的线上蓝桥杯经验分享会整理。

目录

0 关于bilibili网课推荐

1 STC头文件的添加(STC15F2K60S2.H)

2 书写习惯

2.1 变量定义 

2.2 38译码器相关函数封装 

2.2 LED和数码管

2.3 独立按键

2.4 矩阵按键

2.5 NE555频率测量

2.6 PWM脉冲调制 

2.7 超声波测距 

 3 学长的知识点总结


0 关于bilibili网课推荐

        基础部分跟小蜜蜂老师足够,进阶外设部分强推up主机械狗

        主要还是形成自己的代码风格,或者说形成自己的代码模板。

1 STC头文件的添加(STC15F2K60S2.H)

        打开STC-ISP 

 

        打开keil,新建新工程,选择如下:

         #include<STC15F2K60S2.H>即可

2 书写习惯

2.1 变量定义 

typedef unsigned char uchar;
typedef unsigned int uint;

2.2 38译码器相关函数封装 

#define selec(y,x)	P0=x;P2|=y;P2&=0x1f;//此宏定义很关键,可以节省大量代码

         P0 = _ _ _ _   _ _ _ _ 

        P2 |= y:

        P2 = _ _ _ _   _ _ _ _   =>   P2 = x x x _   _ _ _ _

        (Y4: P2 |= 0x80;  Y5: P2 |= 0xa0;  Y6: P2 |= 0xc0;  Y7:P2 |= 0xe0)

        P2 &= 0x1f(前序数据已锁存,此时清零无影响):

        P2 = x x x _   _ _ _ _   =>   P2 = 0 0 0 _   _ _ _ _

2.2 LED和数码管

        定义与声明

#define selec(y,x)	P0=x;P2|=y;P2&=0x1f;//此宏定义很关键,可以节省大量代码

uchar code t_display[]={                       //????
//   0    1    2    3    4    5    6    7    8    9    A    B    C    D    E    F
    0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71,
//black  -     H    J    K    L    N    o   P    U     t    G    Q    r   M    y
    0x00,0x40,0x76,0x1E,0x70,0x38,0x37,0x5C,0x73,0x3E,0x78,0x3d,0x67,0x50,0x37,0x6e,
    0xBF,0x86,0xDB,0xCF,0xE6,0xED,0xFD,0x87,0xFF,0xEF,0x46};    //0. 1. 2. 3. 4. 5. 6. 7. 8. 9. -1

uchar code T_COM[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80}; //可从烧录软件中复制

uchar smg[8]={16,16,16,16,16,16,16,16};//数码管相关数组
uchar L[9]={0,0,0,0,0,0,0,0,0};//LED相关数组

uchar countkey;//按键扫描计时

uint	counttemp;//温度读取计时

uint	temp;

         其中,code t_display[]和code T_COM[]为STC-ISP中自带:

        处理(整体放于中断之中,与主体独立开)

void Timer0Init(void)		//1??@12.000MHz
{
	AUXR |= 0x80;		//?????1T??
	TMOD &= 0xF0;		//???????
	TL0 = 0x20;		//???????
	TH0 = 0xD1;		//???????
	TF0 = 0;		//??TF0??
	TR0 = 1;		//???0????
	EA=1;
	ET0=1;
}

void servicet0()  interrupt 1
{
	static uchar dspcom=0;

//led处理代码	
	selec(0x80,~(L[8]*128+L[7]*64+L[6]*32+L[5]*16+L[4]*8+L[3]*4+L[2]*2+L[1]))

//由此开始的后续五行代码是数码管处理代码
	selec(0xc0,0x00);//先关闭所有数码管,消除虚影
	selec(0xe0,~t_display[smg[dspcom]]);//段选
	selec(0xc0,T_COM[dspcom]);//位选
	if(++dspcom==8)//扫描
		dspcom=0;
	
	++countkey;//计时变量  加一次就是1ms
	++counttemp;
	
}

        其中,void Timer0Init(void)为STC-ISP中自带(EA=1;ET0=1;自己加):

        想要 L3亮,L2、L1灭:

L[3]=1;    L[2]=0;    L[1]=0;
smg[0]=12;    //从左往右第0位显示字母C
smg[5]=temp/100;    //第5位显示百位(实际表示十位)
smg[6]=temp/10%10+32;    //第6位显示十位(实际表示个位)    +32:数字加上小数点的显示方式
smg[7]=temp%10;    //第7位显示个位(实际表示第一位小数)
for(i=1;i<5;i++)    //第1~4位熄灭
smg[i]=16;    //16对应black,即直接熄灭

2.3 独立按键

        这里的按键的前三行代码有异常精妙之处,详见:最精辟和实用的按键处理程序 - 51单片机 

#define	set    1

uchar trg,cnt;//独立按键相关变量
uchar smgstat;
uchar bitset;
uchar tset1,tset2;
uchar tmax,tmin;

void keyscan()//按键扫描函数
{
	uchar readdat=P3^0xff,i=0;
	trg=readdat&(readdat^cnt);
	cnt=readdat;    //这三行代码很关键,注意理解
	
	
	if(trg&0x01)	//s7按下
	{
		if(smgstat==set)
			if(bitset==1)
			{
				if(tmax>0)
					tmax--;
			}
			else if(bitset==2)
			{

				if(tmin>0)
					tmin--;
			}
	}
	else if(trg&0x02) //s6按下
	{
		if(smgstat==set)
			if(bitset==1)
			{

				if(tmax<99)
					tmax++;
			}
			else if(bitset==2)
			{

				if(tmin<99)
					tmin++;
			}
	}
	else if(trg&0x04) //s5按下
	{
		if(smgstat==set)
			if(bitset==1)
			{
				bitset=2;
			}
			else if(bitset==2)
			{
				bitset=1;
			}
	}
	else if(trg&0x08) //s4按下
	{
		if(smgstat==wendu)
		{
			smgstat=set;
			tset1=tmax;
			tset2=tmin;
			bitset=1;
		}
		else if(smgstat==set)
		{
			if(tmin>tmax)
			{
				tmax=tset1;
				tmin=tset2;
				L[4]=1;
				
			}
			else 
			{
				L[4]=0;
			}
			smgstat=wendu;
			
		}
	}
}

        主函数while(1)中放一个扫描函数,10ms扫一次

if(countkey>9)//按键扫描10ms
		{
			countkey=0;
			keyscan();
		}

2.4 矩阵按键

void keyscan()
{
	uchar i;
	static uchar keystat=0; //必须是静态变量
	uchar keyx=0,keyy=0;
	P3=0x0f;P4=0x00;
	if(!P30)			keyx=3;
	else if(!P31)		keyx=2;
	else if(!P32)		keyx=1;
	else if(!P33)		keyx=0;
	P3=0xf0;P4=0xff;
	if(!P34)			keyy=4;
	else if(!P35)		keyy=3;
	else if(!P42)		keyy=2;
	else if(!P44)		keyy=1;
	
	keyval=keyx+keyy*4;
/*
y x 0    1    2    3
1   4    5    6    7
2   8    9    10   11    
3   12   13   14   15
4   16   17   18   19
*/
		
	switch(keystat)
	{
		case 0:
			if(keyval!=0) keystat=1;
			else{ for(i=0;i<9;i++) L[i]=0;}
			break;
		case 1:	
			if(keyval==0)
				keystat=0;  //这句不能少
			else 
			{
				keystat=2;	
					value = keyval;				//这句不能少
				switch(keyval)
				{
					case 4: 
						L[1]=L[2]=1;
					break;
					case 8: 
						L[3]=L[4]=1;;
					break;
					case 12:
						L[5]=L[6]=1;
					break;
					case 16: 
						L[7]=L[8]=1;
					break;				
					default: ;break;
				}				
			}
			break;
		case 2:
			if(keyval==0)	keystat=0;	//这两句都不能少
			break;
					
	}
}
void main()
{
	initsys();

	while(1)
	{
	if(keyval>9)
	{
		smg[6]=keyval/10;    //十位
		smg[7]=keyval%10;    //个位
	}
	else if(keyval<10&&keyval>0)
	{	
		smg[6]=16;    //灭
		smg[7]=keyval%10;    //个位
	}
	else if(keyval==0)
	{
		smg[6]=16;    //灭
		smg[7]=16;    //灭
	}
		
        if(countkey==10)
		{
			countkey=0;
			keyscan();
		}
		
	}

另一种情况(第十届国赛超声波dac): 

static uchar keystat=0;
	uchar keyx=0,keyy=0;
	
	P32=P33=1;P34=P35=0;

	if(!P32)		keyx=1;  //这里很关键,防止串口通信被破坏。
	else if(!P33)		keyx=0;
	P32=P33=0;P34=P35=1;
	if(!P34)				keyy=4;
	else if(!P35)		keyy=3;

2.5 NE555频率测量

         注意:板子上P34需和SIGNAL通过短路帽相短接。

#include "stc15f2k60s2.h"


typedef unsigned char uchar;
typedef unsigned int uint;

#define selec573(y,x) P0=x;P2=y;P2=0;
uchar code t_display[]={                       //????
//   0    1    2    3    4    5    6    7    8    9    A    B    C    D    E    F
    0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71,
//black  -     H    J    K    L    N    o   P    U     t    G    Q    r   M    y
    0x00,0x40,0x76,0x1E,0x70,0x38,0x37,0x5C,0x73,0x3E,0x78,0x3d,0x67,0x50,0x37,0x6e,
    0xBF,0x86,0xDB,0xCF,0xE6,0xED,0xFD,0x87,0xFF,0xEF,0x46};    //0. 1. 2. 3. 4. 5. 6. 7. 8. 9. -1

uchar code T_COM[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};      //??
uchar smg[8]={16,16,16,16,16,16,16,16};

uint fre=0,fre2;
uint count;


void T0init()//定时器0工作在计数
{
	AUXR |=0x80;
	TMOD |=0x04;
	TH0=0xff;
	TL0=0xff;
	TR0=1;
	EA=1;
	ET0=1;
}
//上面这个没得复制

void serviceT0() interrupt 1
{
	fre++;
}

void T1init()//定时器1工作在定时
{
	AUXR |= 0x40;		//?????1T??
	TMOD &= 0x0F;		//???????
	TL1 = 0x20;		//???????
	TH1 = 0xD1;		//???????
	TF1 = 0;		//??TF1??
	TR1 = 1;		//???1????
	ET1=1;
}
void serviceT1() interrupt 3
{
	
	static uchar dspcom=0;
	
	selec573(0xc0,0x00);
	selec573(0xe0,~t_display[smg[dspcom]]);
	selec573(0xc0,T_COM[dspcom]);
	
	if(++dspcom==8)
		dspcom=0;
	if(++count>499)    //500ms
		{
			count=0;
			
			ET0=0;//停止计数
			fre2= 2*fre;
			fre=0;
			ET0=1;//开始计数
		} 
		
}

void initsys()
{
	selec573(0x80,0xff);
	selec573(0xa0,0x00);
	
	T0init();
	T1init();
}
void main()
{
	initsys();
	while(1)
	{

			smg[3]=fre2/10000;//数码管显示示例
			smg[4]=fre2/1000%10;
			smg[5]=fre2/100%10;
			smg[6]=fre2/10%10;
			smg[7]=fre2%10;
		
	}
	
}

2.6 PWM脉冲调制 

#include <STC15F2K60S2.H>

typedef unsigned char uchar;
typedef unsigned int 	uint;

#define selec(y,x)  P0=x;P2=y;P2=0;

uchar code t_display[]={                       //????
//   0    1    2    3    4    5    6    7    8    9    A    B    C    D    E    F
    0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71,
//black  -     H    J    K    L    N    o   P    U     t    G    Q    r   M    y
    0x00,0x40,0x76,0x1E,0x70,0x38,0x37,0x5C,0x73,0x3E,0x78,0x3d,0x67,0x50,0x37,0x6e,
    0xBF,0x86,0xDB,0xCF,0xE6,0xED,0xFD,0x87,0xFF,0xEF,0x46};    //0. 1. 2. 3. 4. 5. 6. 7. 8. 9. -1

uchar code T_COM[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};      //??

uchar smg[8]={16,16,16,16,16,16,16,16};
uchar L[9]={0,0,0,0,0,0,0,0,0};

uchar countkey;
uchar keyval;
uchar pwmval;
uchar countpwm;
uchar buzz,relay;

void Timer0Init(void)		//100us@12.000MHz   定时器中断周期时间为100微秒也就是
{
	AUXR |= 0x80;		//?????1T??
	TMOD &= 0xF0;		//???????
	TL0 = 0x50;		//???????
	TH0 = 0xFB;		//???????
	TF0 = 0;		//??TF0??
	TR0 = 1;		//???0????
	ET0=1;
	EA=1;
}

void servicet0() interrupt 1
{
	static uchar dspcom=0;
	uchar temp;
	
	if(++temp<pwmval)// pwmval就是用于控制占空比的变量
	{
		L[1]=L[2]=1;//控制该led灯的亮度
		buzz=1;//蜂鸣器
	}
	else
	{
		L[1]=L[2]=0;
		buzz=0;
	}
	
	selec(0x80,~(L[8]*128+L[7]*64+L[6]*32+L[5]*16+L[4]*8+L[3]*4+L[2]*2+L[1]*1))
	selec(0xA0,buzz*64+relay*16)
//蜂鸣器与继电器处理函数(十三届国赛还用到了电机引脚,是同理)
	if(temp>9)
	{
		selec(0xc0,0x00);
		selec(0xe0,~t_display[smg[dspcom]]);
		selec(0xc0,0x01<<dspcom);
	
		if(++dspcom==8)
			dspcom=0;
		temp=0;
	}
	
	++countkey;

}

void keyscan()
{
	static uchar keystat=0;
	uchar keyx=0,keyy=0,i;
	
	P3=0x0f;P4=0x00;
	if(!P30)				keyx=3;
	else if(!P31)		keyx=2;
	else if(!P32)		keyx=1;
	else if(!P33)		keyx=0;
	P3=0xf0;P4=0xff;
	if(!P34)				keyy=4;
	else if(!P35)		keyy=3;
	else if(!P42)		keyy=2;
	else if(!P44)		keyy=1;
	
	keyval= keyx+keyy*4;
	
	switch(keystat)
	{
		case 0:if(keyval!=0)keystat=1;break;
		case 1:
			if(keyval==0)
				keystat=0;
			else 
			{
				keystat=2;
				switch(keyval)
				{
					case 4: pwmval+=1;break;//按键控制pwm波占空比
					case 5: pwmval=2;break;
					case 6: pwmval=9;break;
					
				}
			}
			break;
		case 2:
			if(keyval==0) 
			{
				keystat=0;	
				for(i=0;i<9;i++)
					L[i]=0;
			}
				
	
	}
}

void main()
{
	selec(0xa0,0x00);
	Timer0Init();
	while(1)
	{
//		if(countpwm<pwmval)
//		{
//			L[1]=L[2]=1;
//		}
//		else if(countpwm==10)
//		{
//			L[1]=L[2]=0;
//			countpwm=0;
//		}
//		
		smg[6]=pwmval;
		smg[7]=0;
		
		if(countkey>9)
		{
			countkey=0;
			keyscan();
		}
	}
}

2.7 超声波测距 

         注意:软件延时的_nop_()需要#include "intrins.h"这个头文件

#include <STC15F2K60S2.H>
#include "intrins.h"//主要是软件延时的_nop_()需要此头文件
#define	selec(y,x)	P0=x;P2|=y;P2&=0x1f;

typedef unsigned char uchar;
typedef unsigned int	uint;

sbit	TX=P1^0;//这里很关键,要预定义。
sbit	RX=P1^1;


uchar code t_display[]={                       //????
//   0    1    2    3    4    5    6    7    8    9    A    B    C    D    E    F
    0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71,
//black  -     H    J    K    L    N    o   P    U     t    G    Q    r   M    y
    0x00,0x40,0x76,0x1E,0x70,0x38,0x37,0x5C,0x73,0x3E,0x78,0x3d,0x67,0x50,0x37,0x6e,
    0xBF,0x86,0xDB,0xCF,0xE6,0xED,0xFD,0x87,0xFF,0xEF,0x46};    //0. 1. 2. 3. 4. 5. 6. 7. 8. 9. -1

uchar code T_COM[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};      //??

uchar smg[8]={16,16,16,16,16,16,16,16};
uint distance;//注意变量类型
uchar countdis;
bit L1;

//======以下三个软件延时根据硬件误差情况选一个能用的,都不能用再试试14us,10us。==============
//======比赛直接用12us,都可以测距成功=============
//超声波测距需要占用一个定时器
void Delay12us()		//@12.000MHz
{
	unsigned char i;

	_nop_();
	_nop_();
	i = 33;
	while (--i);
}
void Delay13us()		//@12.000MHz
{
	unsigned char i;

	_nop_();
	_nop_();
	i = 36;
	while (--i);
}

void Delay11us()		//@12.000MHz
{
	unsigned char i;

	_nop_();
	_nop_();
	i = 30;
	while (--i);
}


void Timer0Init(void)		//1??@12.000MHz
{
	AUXR |= 0x80;		//?????1T??
	TMOD &= 0xF0;		//???????
	TL0 = 0x20;		//???????
	TH0 = 0xD1;		//???????
	TF0 = 0;		//??TF0??
	TR0 = 1;		//???0????
	EA=1;
	ET0=1;
}

void servicet0() interrupt 1   //一套常规操作
{
	static uchar dspcom=0;
	//selec(0x80,~L1);
	selec(0xc0,0x00);
	selec(0xe0,~t_display[smg[dspcom]]);
	selec(0xc0,T_COM[dspcom]);
	
	if(++dspcom==8)
		dspcom=0;
	
	++countdis;
}


void sendwave()//发送超声波===========
{
	uchar i;
	for(i=0;i<16;i++)//每隔12us取一次反,发送八个周期的波形
	{
		TX=~TX;
		Delay12us();
	}
}

uint getdis()//接收波并计算距离的函数
{
	uint	distan=0;    //不能用uchar
	
	TH1=TL1=0;//定时器1清零
	sendwave();//发送超声波
	TR1=1;//开始计数
	while((RX==1)&&(TF1==0));//计数溢出,或收到脉冲
	TR1=0; //停止计数
	
	if(TF1==0)//如果没有溢出
	{
		distan=(TH1<<8|TL1)*0.017+0.5;//0.017是根据音速等计算出来的参数。加0.5是为了补偿误差(其他地方同理)
	}
	else 
	{
		TF1=0;
		distan=999;
	}
	
	return distan;//注意返回值
}




void main()
{
	selec(0x80,0xff);
	selec(0xa0,0x00);
	
	
	Timer0Init();
	while(1)
	{
		
		if(countdis>199)//199ms采集一次
		{
			countdis=0;
			distance=getdis();
			if(distance<10)
				L1=1;
			else
				L1=0;
			
			smg[5]=distance/100;
			smg[6]=distance/10%10;
			smg[7]=distance%10;
			
		}
		
	}
}
	

        其中,void Delay12us()等延时函数为STC-ISP中自带:       

  

 3 学长的知识点总结

知识点总结
①LED:闪烁,状态表征等。
②数码管、蜂鸣器和继电器。
③按键:独立按键和矩阵按键的短按、长按、松开触发、双击。其中矩阵按键避免与其他功能引脚的冲突(如第十届国赛题)。
④NE555频率测量。
⑤IIC:数模转换(PCF8591)、EEPROM(AT24c02)。
⑥DS18B20
⑦DS1302
⑧超声波测距
⑨PWM脉宽调制
⑩串口通信(多字节和单字节收发识别)——可靠后学习,省赛一般不考,但是无法保证不会像十三届第二次省赛一样,考以前没在省赛考过的超声波测距。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
学长的紫根,指的是学长的经验和智慧,是他在学术和人生方面积累的宝贵财富。坐在学长的紫根上写作po意味着在他的引导下进行写作创作,借助他的经验和指导,拓展自己的写作能力和视野。 学长的紫根是学术传承的重要一环,他们通过自己的学习和研究,积攒了许多知识和见解。坐在学长的紫根上进行写作po,我们可以学习他们的研究方法和思维方式,对于我们的创作会产生很大的启发。学长的指导可以让我们少走弯路,更加有针对性地进行写作,避免重复造轮子。 坐在学长的紫根上写作po还可以促进交流与合作。学长可以分享他们的想法和经验,指导我们如何提升写作技巧和思考深度。通过和学长的交流,我们可以得到更多的反馈和建议,提升自己的写作水平。同时,与学长合作也有助于培养团队合作精神和学术交流的能力。 此外,坐在学长的紫根上写作po也是一种学习谦虚和尊重的体现。我们借助学长的经验和指导,展现了对他们的尊重和认可。同时,学长们也会感到荣幸,因为他们的经验和知识能够为后辈们所借鉴和继承。 总之,坐在学长的紫根上写作po是一种聪明的选择。通过借助学长的经验和智慧,我们能够提升写作能力和创作水平,促进学术传承和交流合作。这是一种互惠互利的过程,值得我们每个人珍惜和努力去做好。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值