蓝桥杯

一.LED以及锁存器的跟随模式,io模式vcc或非门,keil新建项目文件以及第一个程序

capture_20240228192033725

led有八个灯珠,右边vcc为正极供电,因此,只需要控制左边负极为低电平,即可控制led。

但是在led左边有一个M74hc573锁存器。

简单来说,就是由输入引脚D1-D8,来控制输出引脚Q1-Q8,OE和LE的取值会影响输入数据控制输出数据。

当OE为L(低电平0v)时,LE为高电平H,为跟随模式,即输出数据=输入数据。

OE为L时,LE为低电平L时,为锁存模式,即输出数据=上一个时刻的输入数据。

我们这个锁存器的OE已经接了地,也就是说已经接了低电平,那我们只需要控制LE为高电平即可。

但是,这里LE接了一个”Y4C”,我们使用全局搜素,在原理图下面找到了另一个Y4C.capture_20240228192602249

这是一个或非门,蓝桥杯比赛中,WR和GND是接在一起的,也就是说,WR固定为0,如果Y4(12)为0,Y4C则输出高电平,反之则低电平。capture_20240228194518765

74HC02的Y4口又和U24接在一起,这个东西叫做74HC138译码器,

左边从上到下,如果为000,换为二进制则Y0口输出为低电平,Y1-Y7为高电平。

001,Y1口0其余1。

010,Y2口0其余1

011,Y3口0其余1….

100,Y4口0其余1

新建项目文件::

代码开始:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <reg52.h>
sbit LSA =P2^5;
sbit LSB =P2^6;
sbit LSC=P2^7;
sbit L1 = P0^0;
sbit L2 = P0^1;
sbit L3 = P0^2;
sbit L4 = P0^3;
sbit L5 = P0^4;
sbit L6 = P0^5;
sbit L7 = P0^6;
sbit L8 = P0^7;
void init()//不过,我们的蜂鸣器会乱响,这里我们加入一点东西(我看不懂思密达,反正就是加了)
{
LSC=1;LSB =0;LSA=1;
P0=0x00;
}
void main()
{ init()//这一行也是加的
​ LSC=LSA=LSB;
​ L1=0;
​ L2=L3=L4=L5=L6=L7=L8=1;
}


LSAbc为锁存器部分,P0为LED部分,另外一定要在程序里加上while,确保单片机的程序可以重复运行。

延时函数:

在isp烧录软件里找到延时函数,选择12mhz,定时长度为1s

capture_20240228201640366

先把整个复制下来,

1
2
3
4
5
6
7
8
9
10
 void Delay_ms()		//@12MHz
{
unsigned char i, j;
i = 12;
j = 169;
do
{
while (--j);
} while (--i);
}

在此之前,为了方便起见,宏定义两个快捷操作

1
2
3
 #define uint unsigned int

#define uchar unsignded char

把延时函数变量改为

1
void Delay_ms(uint xms)

我们希望它延时多少毫秒,就把它循环几次

1
for (  ;  xms > 0; xms--)

于是,延时函数就变成了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void Delay_ms(uint xms)		//@12MHz
{
for ( ; xms > 0; xms--)
​ {
unsigned char i, j;
​ i = 12;
​ j = 169;
do
​ {
while (--j);
​ } while (--i);

​ }

}

这里的部分我们放到所以sbit下面,作为自定义函数。

如果在程序内需要引用,这样使用:

1
delay_ms(500);//将延时函数循环执行500次,即延时500ms

于是,我们的主函数就变成了

1
2
3
4
5
6
7
8
9
void main()
{ init()
LSC=LSA=LSB;
L1= L2=L3=L4=L5=L6=L7=L8=1;//熄灭
delay_ms(500)
L1= L2=L3=L4=L5=L6=L7=L8=0;//点亮
delay_ms(500)

}

每次都要把L1到L8都写一遍。是不是这样写太麻烦了?还有一个更简单的写法。

把L1到L8对应的数字改写成16进制,

主函数部分更改为

1
2
3
4
5
6
7
while(1)
{
P0=0x00;
delay_ms(500);
P0=0xff;
delay_ms(500);
}

另外需要注意的一点是,每次LED的初始状态需要为熄灭状态

所以我们这样写:

1
2
3
4
5
6
7
void init()//init函数为初始化LED
{
LSC=1;LSB =0;LSA=1;
P0=0x00;//防止蜂鸣器和继电器乱响
LSC=1;LSB=0;LSB=0;
P0=0xff;//LED初始化为熄灭状态
}

注:我后续写程序的思路是模块化编程,每个功能尽量使用一个函数来实现。

例如:刚刚写的LED程序,改成如下:

1
2
3
4
5
6
7
void led()
{
P0=0x00;
delay_ms(500);
P0=0xff;
delay_ms(500);
}

那我们的主函数就变为了:

1
2
3
4
5
6
7
8
void main()
{
init();//初始化
while(1)
{
led();//调用led函数
}
}

逻辑运算符

通过取反操作符,我们还能简化led的程序

1
2
3
4
5
void led()
{
P0 =~P0;
delay_ms(500);
}

可以这么做的原因的我们在init()中已经把P0全部熄灭了,在led的程序中我们使用取反操作符就刚好能把0x00全部取反为0xff,也就是把熄灭变为点亮。

流水灯

讲完上面最麻烦的方法,逐一定义每个灯可以实现,不过,我们有运算符,那么,我们流水灯的代码也就不复杂了。

1
2
3
4
5
6
7
8
9
   void main()
{
init();//初始化
while(1)
{
P0 = P0 << 1;//这里也可以更改为我们定义好的led函数,写法是一样的
delay_ms(500)
}
}

这样的话,程序的循环体每次执行一次,我们的P0的值就会从11111变为01111,00111,00011…..从而达到流水灯的效果。

PS:从左到右

十分简洁。