蓝桥杯单片机模块代码综合(考前快速复习)

今天做的是蓝桥杯所有模块的综合,只展示模块的核心代码,所以只能作为复习资料,而不是学习资料。这次给原理图全在官方给的资料里,考试记不起来可以在文件夹里找到!

(一)LED

  核心代码示例:

void lighten(u8 led)
{
  P0=~led;
  P2=P2&0X1F|0X8F;
  P2=P2&0X1F;
}

原理图: 

 回忆一下,译码器选择Y4使得其为低电平,J13中WR与地连接,所以或非门后Y4C为1,锁存器才能打开,P0的信息才能给右侧。二极管左侧为0才能亮,所以书写代码时我们常用~取反,使得P0取1为亮,取0为暗,符合人们的思维习惯。

(二)外中断

  核心代码示例: 

void open()
{
  EA=1;
  EX1=1;
  EX0=1;
  IT1=1;
  IT0=1;
}




void stop1() interrupt 0
{
  P0=~1;
  P2=P2&0X1F|0X80;
  P2=P2&0X1F;
}

void stop2() interrupt 2
{
  P0=~0;
  P2=P2&0X1F|0X80;
  P2=P2&0X1F;
}

原理图: 

 

打开中断→使用中断就可以,注意中断与中断,中断与主函数之间不能同时使用相同的引脚和期间,后果不可估计,如LED与数码管。

(三)定时器

  核心代码示例:

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

STC-ISP直接提供代码。中断号码在上一个模块的原理图中。

(四)数码管

  核心代码示例:

void translate(u8 org[],u8 tran[])
{
  u8 k,j,mid;
  for(j=0,k=0;j<8;j++,k++)
  {
	switch(org[k])
	{
      case '0': mid = 0xc0; break;
	  case '1': mid = 0xf9; break;  
	  case '2': mid = 0xa4; break;  
	  case '3': mid = 0xb0; break;  
   	  case '4': mid = 0x99; break;  
	  case '5': mid = 0x92; break;  
	  case '6': mid = 0x82; break;  
	  case '7': mid = 0xf8; break;  
	  case '8': mid = 0x80; break;  
	  case '9': mid = 0x90; break;
      case '-':	mid = 0xbf; break;		
  	  default: mid = 0xff;
	}
	if(org[k+1]=='.')
	{
	  mid=mid&0x7f;
  	  k++;
	}
	  tran[j]=mid;
  }
}

void display(u8 tran[],u8 wei)
{
  P0=0XFF;
  P2=P2&0X1F|0XE0; 
  P2&=0X1F;
  P0=1<<wei;
  P2=P2&0X1F|0XC0;
  P2=P2&0X1F;
  P0=tran[wei];
  P2=P2&0X1F|0XE0;
  P2=P2&0X1F;
}



void stop0() interrupt 1
{
  count++;
  if(count%1000==0)
  {
    i++;
	count=0;
  }
  display(tran,wei);
  if(++wei==8) wei=0;
}

 选择对应锁存器(一)就说过了吧,不重复了。我们输入的内容在org数组中,translate函数将其翻译为共阳数码管的对应内容,再通过display函数显示,定时器快速切换,使得肉眼认为其一直亮。注:如果位选段选部分互换,可能消影不理想。

(五)矩阵键盘

    核心代码展示:

u8 search()
{
  u16 key;
  u8 key_return;
  P44=0;P42=1;P35=1;P34=1;
  key=key|(P3&0X0F);
  P44=1;P42=0;P35=1;P34=1;
  key=(key<<4)|(P3&0X0F);
  P44=1;P42=1;P35=0;P34=1;
  key=(key<<4)|(P3&0X0F);
  P44=1;P42=1;P35=1;P34=0;
  key=(key<<4)|(P3&0X0F);
  switch(~key)
  {
    case 0x8000: key_return = 4; break; // S4  
	case 0x4000: key_return = 5; break; // S5  
	case 0x2000: key_return = 6; break; // S6  
	case 0x1000: key_return = 7; break; // S7  
	case 0x0800: key_return = 8; break; // S8  
	case 0x0400: key_return = 9; break; // S9  
	case 0x0200: key_return = 10; break; // S10  
	case 0x0100: key_return = 11; break; // S11  
	case 0x0080: key_return = 12; break; // S12  
	case 0x0040: key_return = 13; break; // S13  
	case 0x0020: key_return = 14; break; // S14  
	case 0x0010: key_return = 15; break; // S15  
	case 0x0008: key_return = 16; break; // S16  
	case 0x0004: key_return = 17; break; // S17  
	case 0x0002: key_return = 18; break; // S18  
	case 0x0001: key_return = 19; break; // S19  
	default: key_return = 0; 
  }
  return key_return;
}

void key_translate()
{
  new=search();
  if(new!=old&&new!=0)
  {
    if(new>=14)
	org[7]=new-14+'A';
	else org[7]=new-4+'0';
  }
  old=new;
}

 外中断时,我们的J5的23相连,此时是12。其确定按键的形式为P34,P35,P42,P44,分别单独置0,确定列。再通过P30,P31,P32,P33确定行,从而锁定按键。记住记录按件情况的key是十六位的。

  后一个函数是根据扫描的结果执行想要的结果,通过OLD,NEW参数让一次按下只识别一次,当然如需按下切换界面,松手消失之类,需要将功能写在  if(new!=old&&new!=0)外,这句话应该看不懂,除非你做过相应省赛。

(六)时钟芯片

  核心代码示例:

void read()
{
  u8 mid;
  mid=Read_Ds1302_Byte(0x85);
  time_now[0]=(mid>>4)*10+(mid&0x0f);
  mid=Read_Ds1302_Byte(0x83);
  time_now[1]=(mid>>4)*10+(mid&0x0f);
  mid=Read_Ds1302_Byte(0x81);
  time_now[2]=(mid>>4)*10+(mid&0x0f);
}

void stop0() interrupt 0
{
  u8 mid;
  Write_Ds1302_Byte(0x8e,0);
  mid=((time_set[0]/10)<<4)+time_set[0]%10;
  Write_Ds1302_Byte(0x84,mid);
  mid=((time_set[1]/10)<<4)+time_set[1]%10;
  Write_Ds1302_Byte(0x82,mid);
  mid=((time_set[2]/10)<<4)+time_set[2]%10;
  Write_Ds1302_Byte(0x80,mid);
  Write_Ds1302_Byte(0x8e,0x80);
}

最核心的图就是这个,底层代码解释就看我以前的文章吧。其存储方式是将十位和各位分开来,所以需要转化。读取和存储看最后一位(传输时时第一位,单线嘛) ,地址图最左侧两列。写时需要打开写保护,写完后记得关闭。

(七)温度模块

  核心代码示例:

void rd_temperature(unsigned char *zhen1,unsigned char *zhen2)
{
  init_ds18b20();
  Write_DS18B20(0xcc);
  Write_DS18B20(0x44);
  Delay_OneWire(10);
  init_ds18b20();
  Write_DS18B20(0xcc);
  Write_DS18B20(0xbe);
  *zhen1=Read_DS18B20();
  *zhen2=Read_DS18B20();
}

底层代码修改见前面的文章。

我们使用的只有0XCC,0XBE,0X44。初始化→跳过ROM→转化/读温度。

读温度遵循以下规则:

先读LS BYTE,后MS BYTE。主函数中         zhen1=&low;zhen2=&high;   rd_temperature(zhen1,zhen2);  temp=low+(high*256);,实际温度还需除以16,因为LS BYTE最低位为0.125。

(八)模数转换:

  核心代码示例:

u8 adc(u8 adress)
{
  u8 mid;
  IIC_Start();
  IIC_SendByte(0x90);
  IIC_WaitAck();
  IIC_SendByte(adress);
  IIC_WaitAck();
  IIC_Start();
  IIC_SendByte(0x91);
  IIC_WaitAck();
  mid=IIC_RecByte();
  IIC_SendAck(1);
  IIC_Stop();
  return mid;
}

初始化后第一步,本单片机中A2A1A0已经为0,写就第0位为0,读时1。

第二步一般是0X43或0X41,可以看之前文章理解。 

 本模块用IIC为底层,sendack函数1是代表接受结束,0代表还想继续接受。stop函数也不可以少。

(九)掉电存储数据

  核心代码展示:

u8 atr()
{
  u8 result;
  IIC_Start();
  IIC_SendByte(0xa0);
  IIC_WaitAck();
  IIC_SendByte(0);
  IIC_WaitAck();
  IIC_Start();
  IIC_SendByte(0xa1);
  IIC_WaitAck();
  result=IIC_RecByte();
  IIC_SendAck(1);
  IIC_Stop();
  return result;
}

 

 

 读和写在这里很清楚,按照顺序来就好,与上同,多个字节写法可以见我以前写的文章。

(10)串口

  核心代码展示:

void open()
{
  EA=1;
  ET0=1;
  ES=1;
}

void UartInit(void)		//9600bps@11.0592MHz
{
	SCON = 0x50;		//8位数据,可变波特率
	AUXR |= 0x40;		//定时器1时钟为Fosc,即1T
	AUXR &= 0xFE;		//串口1选择定时器1为波特率发生器
	TMOD &= 0x0F;		//设定定时器1为16位自动重装方式
	TL1 = 0xE0;		//设定定时初值
	TH1 = 0xFE;		//设定定时初值
	ET1 = 0;		//禁止定时器1中断
	TR1 = 1;		//启动定时器1
}

void out(u8 *a)
{
  while(*a!='\0')
  {
    SBUF=*a;
	while(TI==0);
	TI=0;
	a++;
  }
}

void chuankou() interrupt 4
{
   if(RI)
   {
     org[i++]=SBUF;
	 RI=0;
   }
   if(i==8) i=0;
}

 串口也是中断的一种,波特率的计算在STC-ISP中给出,从现在开始,为了避免BUG,各定时器不要重复出现。接收时RI会变成1,接受结束要清零。将要发出的信息写入SBUF,发送完成后TI变成1,发送结束要清零,用while清零。SBUF发送和接受在物理结构上独立,但使用时地址相同。

(11)超声波(前面文章没写,这里多啰嗦一点)

核心代码展示:

u16 flag;
float a;
sbit TX=P1^0;
sbit RX=P1^1;

void Timer1Init(void)		//12.5微秒@12.000MHz
{
	AUXR |= 0x40;		//定时器时钟1T模式
	TMOD &= 0x0F;		//设置定时器模式
	TL1 = 0x6A;		//设置定时初值
	TH1 = 0xFF;		//设置定时初值
	TF1 = 0;		//清除TF1标志
	TR1 = 0;		//定时器1关闭计时
}

void me_dis()
{
	u8 num=10;
	flag=0;  /*清空溢出标志*/
	TX=0;
	TL1=0X6A;
	TH1=0xFF;	
	TR1=1;    /*设置定时器初值,每次溢出均为12.5毫秒*/
	while(num--)
	{
		while(!TF1);/*等待溢出*/
		TX=TX^1;/*高低电平翻转*/
		TF1=0;/*消除溢出位*/
	}          /*发送五个周期的超声波*/
	TR1=0;    /*暂停计时*/
	TL1=0;
	TH1=0;    /*设置初值,方便计算的同时也将可测量距离达到最大*/
	TR1=1;    /*开始计时*/
	while(RX&&!TF1); /*未接受到信号时RX=1,未溢出时TF1=0,两者其一发生变化则跳出while*/
	TR1=0;    /*暂停计时*/
	if(TF1)   /*判断是否溢出*/
	{
		TF1=0;
		flag=1;/*溢出标志*/
	}
}

void main()
{
	close();
	Timer0Init();
	open();
	Timer1Init();
  while(1)
	{
		if(count>1000)
		{
			count=0;
			me_dis();
			a=((TH1*256+TL1)*0.017/12);/* 0.017= 10^-6*340/2*10^2,单位是厘米*/
			if(flag==0)
			sprintf(org,"   %02d.%03d",(u16)a,((a-(u16)a)*1000));/*在keil中无论如何都读取两个字节,而char为1字节,float为4字节,读取时可能出现错误,用上方的方式读取可以避免错误的发生*/
			else
			sprintf(org,"%04d%04d",9999,9999);/*超过测量范围显示99999999,但因int最大为65535,所以用两个9999显示*/
		}
		translate(org,tran);
	}
}

    首先我们需要满足超声波40kHZ的需求,并且他要求有50%的占空比。对应40kHZ的周期为1/400000,依据占空比为百分之五十,在一个周期之内有一半的时间发送的信号为高电平,有一半的时间发送的信号为低电平,经过单位的换算,我们知道对应的每一次高低电平的时间为12.5微秒,并且要求高低电平来回交换。要精确的达到12.5微秒,光靠延时函数是很难做到的,在单片机内我们可以用定时器来满足较为精准的时间需求。定时器的设置就不讲了,不懂得话看我之前的文章或者STC15用户手册。

    之后就是计算距离的长度,我们知道声音的速度大约为340m/s,假如我们要在数码管上显示的单位为厘米,那么本处换算后就是34000cm/s。距离与速度的关系是x=vt,下面计算物体与单片机的距离,唯一所缺的就是时间。依据上一段所说,在单片机内时间测量较为精准的就是定时器,所以本处我们也可以用定时器来测量声音传播的时间。

    如何计算时间呢?(在下面的程序中我们使用的是定时器1,本处我们就拿定时器1举例)我们可以观察TH1和TL1的变化,如果(TH1<<8)+TL1所构成的16位二进制的数字加1,代表时间经过了1/定时器频率(如果采用12T模式,还需要将定时器的频率除以12),之前提到定时器的读和写的地址是相同的,所以我们只需要将接受到信号时的(TH1<<8)+TL1减去发送信号时的(TH1<<8)+TL1,再将这个数据乘以每次加1的时间我们就可以得到公式中所需要的t。

    最后我们还要注意超声波的发送包括去和回两个过程,所以我们最后算出的距离是去和回的两次路径的总和,所以我们需要将最终的结果除以2。

    

(十二) NE555

核心代码展示:

  void timer_set()
{
	TL0=0XFf-15;
	TH0=0XFF;/*定时器0每接受到16次脉冲计数一次*/
	TL1=0xCD;
	TH1=0xD4;/*定时器1在16位自动重装载下计时1ms*/
	AUXR|=0x40;/*设置定时器1为1T模式*/
	TMOD|=0x04;/*设置定时器0为计数模式*/
	ET0=1;
	ET1=1;
	EA=1;/*打开中断*/
	TR1=1;
	TR0=1;/*两个计时器开始工作*/
}
之后适当修改主函数并设置两次中段的内容:
void main()
{
	close();
	timer_set();
  while(1)
	{
		sprintf(org,"%08d",(int)frequency*16);
		translate(org,tran);
	}
}

void time_0() interrupt 1/*没中断一次代表计数了16次*/
{
	number++;
}

void time_1() interrupt 3/*每1s输出一次实际频率的十六分之一*/
{
	count++;
		if(count==1000)
	{
		frequency=number;
		number=0;
		count=0;
	}
	display(tran,x);
	if(++x==8) x=0;
}

 

NE555是一个信号发生电路,有三个5kΩ的电阻分压,故称555定时器。NE555是一个纯硬件的设计,所以它没有可以编程的部分,唯独可以调节的是图中Rb3的阻值,一旦RB3的阻值确定,那么它的功能也就此确定。NE555没有可以编程的部分。

在我们使用的单面机上,P34与NET SIG相靠近,而定时器0的计数模式可以对P34外部的脉冲进行计数,所以我们将P34和NET SIG相联,用定时器0的计数模式,对NE555发出的信号进行计数,通过这种方式来计算它的频率。

 

好了,所有模块结束,祝比赛顺利!

 

物联沃分享整理
物联沃-IOTWORD物联网 » 蓝桥杯单片机模块代码综合(考前快速复习)

发表评论