蓝桥笔记(一)
关于单片机
蓝桥杯大赛指定单片机模块,型号为IAP15F2K61S2,这个型号的单片机也属于51单片机的一种,其外设也比较丰富,并且结构比上一款51单片机要复杂一些,比如说它有了锁存器的应用。当然,原理其实与其他的51单片机相似。注意在使用STC-ISP烧录软件进行烧录的时候需要选择单片机型号为IAP15F2K61S2,并在烧录之前安装相关USB的驱动,否则是无法烧录的。检查一下单片机型号和驱动,准备好了我们就开始吧。
单片机外设
IAP15F2K61S2也是51单片机的一种,但与之前的51单片机不同的是,一些外设并没有办法被直接控制,而是需要借助138译码器 和573锁存器才能对外设进行控制。
1.关于138译码器和573锁存器
138译码器在之前的51单片机的学习笔记中是有介绍的,大致原理图如下:
简单来讲就是 138译码器 可以通过三路输入来控制八路的输出,当然是互斥低有效输出的,即同时只可以输出一路信号。573锁存器 能对信号进行锁存,并可以持续输出某一信号。在单片机中,我们需要控制138译码器 的输出,再对573锁存器 输入一定信号来实现外设的控制。
可以这么理解:138译码器 是用来选择外设的,而573锁存器 是用来操作外设的。
理解了这一点,我们就可以轻松地操作外设了。
三路输入对应的输出关系如下:
P27 | P26 | P25 | 对应输出 |
---|---|---|---|
0 | 0 | 0 | Y0 |
0 | 0 | 1 | Y1 |
0 | 1 | 0 | Y2 |
0 | 1 | 1 | Y3 |
1 | 0 | 0 | Y4 |
1 | 0 | 1 | Y5 |
1 | 1 | 0 | Y6 |
1 | 1 | 1 | Y7 |
其实这个输入输出对应关系很简单,把P27、P26、P25看作是二进制的三位数,其中P27是最高位,对应的输出就是其二进制所表示的数,这样就不需要死记硬背了。
我们在写代码之前需要把J13的2、3短接,就是将WR接地,当我们利用138译码器 选中一路进行低电平输出的时候(拿Y4做例子),Y4输出低电平,与同是低电平的WR经过逻辑门后给Y4C输出高电平。
当Y4C输出高电平给LE时,就会让锁存器允许信号输入,这时我们才可以通过P0寄存器来控制外设。
2.独立LED
独立LED是单片机的一个很基础的模块,有八盏灯,在平常测试时可以利用LED来反映数据变化,可以进行一定幅度的debug,是非常实用的一个模块。
通过这两个图我们就可以发现,只要控制138译码器 选择Y4进行输出,再控制P0寄存器对573锁存器 进行输入就可以控制LED的点亮了。当然,看图就可以知道,如果想要点亮L1,需要让对应的P00口置0。由于锁存器有锁存信号的作用,不需要时时刻刻都向P0寄存器输入信号也能维持LED的点亮。如果要熄灭LED,需要给对应的P口置1。这样其实就很方便,再做其他操作的时候就不需要操心LED是不是还亮着。
让我们来实际操作一下,做一个LED频闪:
#include"reg52.h"
sbit SA = P2^5;
sbit SB = P2^6;
sbit SC = P2^7;
void delay(unsigned int i)
{
while(i--);
}
void main()
{
SC=1;SB=0;SA=0;
while(1)
{
P0=0X00;
delay(10000);
delay(10000);
delay(10000);
P0=0Xff;
delay(10000);
delay(10000);
delay(10000);
}
}
这里我引入的头文件是”reg52.h“
,我们也可以引入头文件<regx52.h>
,这都没什么关系。关于延时,我们还没有介绍过延时,所以用一个最基础最原始的方法,就是让单片机做一些没有意义的运算,用来消耗时间,达到延时的目的。
3.蜂鸣器和继电器
继电器和蜂鸣器也是需要利用到138译码器 和573锁存器才能控制的外设,其原理图如下:
其中对应蜂鸣器的是BUZZ,向左对应的是P06,对应继电器的是RELAY,向右对应的是P04(很容易看错对应关系)。想要控制蜂鸣器或者是继电器,首先要控制138译码器 选择Y5,再对对应的P06或者P04置1。
下面让我们来实际操作一下:
void delay(unsigned int i)
{
while(i--);
}
void main()
{
SA=1;
SB=0;
SC=1;
while(1)
{
P0=0X40;
delay(10000);
P0=0X00;
delay(10000);
P0=0X10;
delay(10000);
P0=0X00;
delay(10000);
}
}
在这里我们让蜂鸣器和继电器交替工作,场面还算”壮观“,感兴趣的小伙伴可以自己试验一下,能不能通过这两个外设来一场“音乐表演”。
4.数码管
单片机上面有着两组数码管,每组可以显示4位数字,两组加起来一共可以显示8位数字。
以上是数码管显示的原理图,显示原理其实也很好理解:一共有,8个数码管,每个数码管有八块显示的灯,我们需要通过138译码器 来选中Y6来选择亮起哪个数码管(即com1~com8),再在138译码器 选中Y7来决定具体亮起数码管上的哪个部分。
在Y7的状态下可供选中亮起的八个数码管灯如下:
八个灯对应字母:a、b、c、d、e、f、dp,其中dp对应着小数点,他们与P0寄存器的对应关系是:
P0^0 | P0^1 | P0^2 | **** | P0^6 | P0^7 |
---|---|---|---|---|---|
a | b | C | **** | f | dp |
P0寄存器中某一位的值为0的时候,其对应的灯会亮起来。
具体的操作如下:
void nixie(unsigned char loc,num)
{
unsigned char numb[10]={0xc0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90};
SC=1; SB=1; SA=0;
P0=00000001 << (loc-1);
SC=1; SB=1; SA=1;
P0=numb[num];
delay(1000);
}
这里的数组nub是显示0~9的数字具体的参数。这里除了num参数,还有一个参数loc,即亮起的位置,nixie(1,1);
就是在第一个位置显示数字“1”。
这里在数码管显示的时候没有提到“扫描”了,这是由于我们可以在138译码器 选中Y6的时候同时选中多盏灯,不需要一盏一盏地扫描来达到“同时亮起”的效果。而且有了锁存器帮忙,我们不需要每时每刻都要执行灯亮起的代码,一旦亮起,如果你不让它熄灭,它就会一直亮着。
如果要动态地显示数字,只需要直接改变显示内容就可以了:
void main
{
unsigned char i1=0;
nixie(2,2);
while(1)
{
nixie(1,i1);
if(i1<9)
{
i1++;
}
else
{
i1=0;
}
}
}
这里我们每一次都改变i1
的值,他就会动态显示(当然由于变化太快,我们完全可以再设置一个delay函数让这个现象更加明显些)。
并且由于锁存器会一直输出信号,如果要改变显示内容,只需要单独修改我们需要改变显示的内容,而输出不变的地方无需做任何修改。所以上面的代码中,虽然我只是显示了一次nixie(2,2);
但由于锁存器一直输出信号,我们数码管的第二位会一直显示数字“2”。
5.独立按键
这块单片机上面没有单独的独立按键,独立按键是混合在了矩阵按键中的,我们要想区分独立按键和矩阵按键,需要手动操作,更改J5跳线帽的位置:
如果J5将2、3短接,就是接地,就是独立按键,此时S4、S5、S6、S7,就是独立按键。观察上图可以发现,S4、S5、S6、S7的一端与J5连接,当2、3短接时,就相当于四个按钮一端接地。如果当按钮被按下,那么另外一段就能读出低电平,这就是独立按键的检测原理。
四个按键另一端对应的是P30、P31、P32、P33,据此我们可以写一些关于独立按键的代码:
void main()
{
while(1)
{
if(P30==0)
{
nixie(1,1);
}
}
}
这里对P30进行检测,如果按键S7被按下,P30将会读出0,那么第一个数码管就会显示“1”。
6.矩阵按键
当我们将J5跳线帽将1、2短接,就相当于将按钮的另外一端与P44相连,这样就组成了一个矩阵网络:
在这个矩阵网络中,每一个按键对应着两个位,像坐标一样。要想确定某个按键是否被按下,需要用到扫描的概念。扫描可以按行扫描,也可以按列扫描。举个例子:当我按行扫描的时候,需要首先从第一行开始,把P30置1,再检测S7是否被按下,再检测S11是否被按下,再检测S15是否被按下,S19是否被按下,第一行结束,再对第二行开始检测,把P31置1,再检测·····
说人话就是先把P30、P31、P32、P33全部清零
先P30给1,分别检测P34,P35,P42,P44是不是1(第一行),再把P30、P31、P32、P33全部清零。
先P31给1,分别检测P34,P35,P42,P44是不是1(第二行),再把P30、P31、P32、P33全部清零。
先P32给1,分别检测P34,P35,P42,P44是不是1(第三行),再把P30、P31、P32、P33全部清零。
先P33给1,分别检测P34,P35,P42,P44是不是1(第四行),再把P30、P31、P32、P33全部清零。
这样才算把按键扫描完了一遍。我们可以把这一段单独拿出来做成函数,并且做一个返回键值,当需要检测按键按下情况的时候就调用这个函数来返回键值。
接下来让我们来实际操作一下:
sbit H30=P3^0;
sbit H31=P3^1;
sbit H32=P3^2;
sbit H33=P3^3;
sbit L30=P4^4;
sbit L31=P4^2;
sbit L32=P3^5;
sbit L33=P3^4;
unsigned char keyee()
{
unsigned char key=0;
P3=f;
H30=l;
if(L30==l){delay(10000);while(L30==l);delay (10000);key=1;}
if(L31==l){delay(10000);while(L31==l);delay (10000);key=2;}
if(L32==l){delay(10000);while(L32==l);delay (10000);key=3;}
if(L33==l){delay(10000);while(L33==l);delay (10000);key=4;}
P3=f;
H31=l;
if(L30==l){delay(10000);while(L30==l);delay (10000);key=5;}
if(L31==l){delay(10000);while(L31==l);delay (10000);key=6;}
if(L32==l){delay(10000);while(L32==l);delay (10000);key=7;}
if(L33==l){delay(10000);while(L33==l);delay (10000);key=8;}
P3=f;
H32=l;
if(L30==l){delay(10000);while(L30==l);delay (10000);key=9;}
if(L31==l){delay(10000);while(L31==l);delay (10000);key=10;}
if(L32==l){delay(10000);while(L32==l);delay (10000);key=11;}
if(L33==l){delay(10000);while(L33==l);delay (10000);key=12;}
P3=f;
H33=l;
if(L30==l){delay(10000);while(L30==l);delay (10000);key=13;}
if(L31==l){delay(10000);while(L31==l);delay (10000);key=14;}
if(L32==l){delay(10000);while(L32==l;)delay (10000);key=15;}
if(L33==l){delay(10000);while(L33==l);delay (10000);key=16;}
return key;
}
做好了这个扫描系统,我们就可以放心在主函数里面写出键值所对应应该做的事情。