1. 1. 感悟
  2. 2. 新手上路
  3. 3. 学习思路
  4. 4. 一些需要注意的点!!
    1. 4.1. LED
    2. 4.2. 延迟delay
    3. 4.3. 按键消抖
    4. 4.4. 矩阵按键
    5. 4.5. DS13B20
    6. 4.6. IIC
    7. 4.7. PWM
    8. 4.8. IO和MM模式
  5. 5. 结尾
  6. 6. 最后,青山不改,碧水长流

感悟

今年的蓝桥杯单片机难吗,不是很难,但注重细心。一分付出,一分收获。今年我有幸参加了第十二届的比赛,但因为自己操作不当,优化代码的过程中删了定时器,没在主函数删了调用函数,导致已经完成的大厦轰然倒塌。自己也很难受,但没办法,希望看到文章的你们不要犯这种低级问题。
Emmmm…,不过后来拿了省一,挺意外的。

新手上路

基础部分的内容,推荐大家去看小蜜蜂老师的视频,讲的很好,网址: https://www.xmf393.com/2019/06/11/2019061103.

学习思路

以下是我的学习思路
首先:先学会LED、蜂鸣器、继电器、按键、数码管、数码管位选、串口等基本操作。
其次:ds1302,ds18b20,eeprom(24C02),PCF8591等。
然后:PWM呼吸灯,DA(因为我刚开始一直没找到)、超声波。
最关键的是一定要做省赛题!实操很重要!

赛题链接:https://pan.baidu.com/s/1ULZzd9nSA8l21VQQiDYvuQ .
提取码:1234

一些需要注意的点!!

LED

首先要说的是LED灯,比如第10届省赛题目中,如果单独点亮LED,会出现原先没有定义的灯跟着一起亮,我的解决方法如下

1
2
3
4
5
6
7
8
9
10
11
12
13
void led()
{
unsigned char key1,key2,key3,key4;
if(s4_stat == 0) key1 = 0x01;
else if(s4_stat == 1) key1 = 0x02;
if((dat_sz>=0&&dat_sz<150)||(dat_sz>=250&&dat_sz<350)) key2 = 0x04;
else key2 = 0x00;
if(dat_f>=0&&dat_f<1000||dat_f>=5000&&dat_f<10000) key3 = 0x08;
else key3 = 0x00;
if(DA_dy == 103) key4 = 0x10;
else key4 = 0x00;
P2 = (P2 & 0x1f) | 0x80;P0 = ~(key1|key2|key3|key4);P2 = 0x00;
}

延迟delay

本来不想单独拿出来讲的,实在是初学者这里出问题的太多了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//这个t是0-255
void delay(unsigned char t)
{
while(t--);
}

//这个t是0-65536
void delay(unsigned int t)
{
while(t--);
}

//这个不影响数码管显示
void delay(unsigned char t)
{
while(t--)
{数码管显示}
}

按键消抖

有时候题目会让你只加1,但你按了按键会出现加的好多,这里推荐一种解决方法

1
2
3
4
5
6
7
8
9
if(key == 0)
{
delay(100);
if(key == 0)
{
相关操作;
while(key == 0){数码管操作;}
}
}

矩阵按键

我参考的B站一位UP主,写的很好,这里用的是reg52

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
26
27
28
29
30
31
32
33
34
35
sbit P42 = P4^2;
sbit P44 = P4^4;
unsigned char key_value;
void scan_key()
{
P3 = 0x0f;P42 = 0;P44 = 0;
if(P3 != 0x0f)
{
delay(100);
if(P3 != 0x0f)
{
switch(P3)
{
case 0x0e:key_value = 0;break;
case 0x0d:key_value = 4;break;
case 0x0b:key_value = 8;break;
case 0x07:key_value = 12;break;
}
P3 = 0xf0;P42 = 1;P44 = 1;
switch(P3)
{
case 0xe0:key_value += 3;break;
case 0xd0:key_value += 2;break;
}
if(P42 == 0)
{
key_value += 1;
}
// !!这里可以根据key_value的值做其他的按键操作!!
while(P3 != 0xf0);
while(P42 != 1);
while(P44 != 1);
}
}
}

DS13B20

关于ds13b20读取温度,onewire里面的延迟都要增大10-12倍。特别注意的就是,蜜蜂老师那的temp是16位的,LSB、MSB是8位的,还有就是老师那的delay(700)是unsigned int的而不是unsigned char,要特别注意一下。两位小数的读法我建议大家去找找相关内容看看,以免考到。

IIC

IIC这一块除了DA输出,其他没啥可说的,DA输出参考代码如下(只需要这样然后主函数调用就行,接着你去拿万用表测J3的D/A与GND两个位置就可以得到差不多的数值):

1
2
3
4
5
6
7
8
9
10
11
void write_DA()
{
IIC_Start();
IIC_SendByte(0x90);
IIC_WaitAck();
IIC_SendByte(0x40); //0100 0000 这里的第二位置为1为DA,0为AD
IIC_WaitAck();
IIC_SendByte(DA_dy1); //DA_dy1的值是0-255,换算5V电压
IIC_WaitAck(); //你只需要乘以500/255.0f = 1.96可到就行
IIC_Stop();
}

还有一个点就是应答和非应答。

1
IIC_SendAck(x);	//0的话值0-128,1的话值是0-255.

PWM

有些小伙伴可能学了很久都没搞懂PWM是怎么搞的,我举个例子。

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
26
27
28
29
30
31
32
33
34
35
unsigned char count,time,pwm_duty,led_light;
void Init() //定时器
{
TMOD = 0x01;
TH0 = (65536 - 100) / 256;
TL0 = (65536 - 100) % 256;
TR0 = 1;
ET0 = 1;
EA = 1;
}

void 函数名() interrupt 1
{
TH0 = (65536 - 100) / 256;
TL0 = (65536 - 100) % 256;
count++;
if(count <= pwm_duty)
{P2 = (P2 & 0x1f) | 0x80;P0 = 0xfe;P2 = 0x00;}
else if(count < 10)
{P2 = (P2 & 0x1f) | 0x80;P0 = 0xff;P2 = 0x00;}
else
{count = 0;time++;P2 = (P2 & 0x1f) | 0x80;P0 = 0xfe;P2 = 0x00;}
}
void pwm()
{
if(time == 5)
{
time = 0;
if(led_light == 0) //0是亮、1是暗
if(pwm_duty == 10) pwm_duty = 10,led_light = 1; else pwm_duty++;
else if(led_light == 1)
if(pwm_duty == 0) pwm_duty = 0,led_light = 0; else pwm_duty--;
}
}

首先我们要的是小灯缓慢亮0.5s,缓慢灭0.5s,先定义一个100us的定时器,先经过count走了10次后,time++了,五次后也就是count加了50次(换算也就是50ms),然后led灯会变一次pwm_duty,也就是小灯的亮度,总共要变10次,也就是0.5s,达到效果。

IO和MM模式

省赛我还没看到要求用MM模式的,大多数都是要求IO模式,最好都学一下,MM模式比IO模式多用一个absacc.h的头文件。

结尾

希望看到这篇文章的你,不会和我刚考完一样,感觉自己的付出和回报不成正比,自己的心态很受打击。当我知道没删那个的时候,我当时人直接懵了,手机也摔地上碎屏了,自己付出了很多,和老师与陪自己一起走过来经常交流的赛友们学到了很多,但最后因为一个很小的问题导致这样,自己真的完全接受不了。不过人生总有得有失吧,唉,大家国赛加油!
最后记得根据题目意思交压缩包,而不是只交了一份hex文件!!!

最后,青山不改,碧水长流