赞
踩
这篇文章主要给AutoLeaders控制组成员制定学习方法与提供一些小技巧,在你们学到每一个阶段时都可以先看一下我的笔记哦。当然啦,其他小伙伴也可以根据这篇文章去备战蓝桥杯哦。
首先是在2022年9月到10月,用了一个月的时间学会c语言,学完了指针与字符串。这里我补充一点,对于51单片机来说,指针和后面的结构体是基本不会用到的,但是51只是一个入门级别的开发板,所以如果你们也想冲蓝桥杯拿好奖的话指针可以简单看看,结构体可以先不看。但是如果你们不只是想打这个比赛的话,那就把结构体也学会,这个对于之后的开发,例如stm32,亦或者跳算法之后学c++与Python都是需要懂的。蓝桥杯只是个用来练手的比赛,而不是终点哦。
学习连接:翁恺C语言
在单片机赛道中是完全不需要用到结构体的,所以我们也并没有很深入的进行学习,但可以大概了解。前面的内容则需要完全掌握。
学完c语言之后,我从2022年10月到12月学习单片机,单片机先从stc89c520开始入门,把最主要的几个模块:定时器、LED、数码管、温度传感器ds18b20、时钟芯片ds1302、串口、独立键盘与矩阵键盘学完就可以进入下一个阶段了。
学习连接:江科大的51单片机入门
在视频中还教我们使用LCD1602,、点阵屏等,这两个东西在蓝桥杯单片机中不会考到,所以你们可以根据自己的喜好学习。LCD1602是个很好的调试设备,但我认为对于初学者来说学会串口都能够代替它了,所以没必要花时间去学习。(但我是学了的,因为我学的那时候不知道会不会用到)
你们需要知道蓝桥杯比赛时用的板子是固定的,并且虽然都是51单片机,但是也会因为板子型号不一样,芯片型号不一样,所以我们需要提早使用这块板子,提早学会它。所以我从1月份开始就学习蓝桥杯的单片机了,但是因为有过51的基础,所以学习起来会比较快,大概一个月左右就学完了,所以如果你们觉得这样的速度太快的话完全可以放宽松一些,因为蓝桥杯省赛在4月份,时间还是比较充裕的。
如果是AutoLeaders的成员,可以联系学长来实验室借用这个单片机进行学习,数量有限,先到先得。对于其他小伙伴来说,可以直接去淘宝搜索蓝桥杯单片机进行购买,需要看清楚的是,这两年的板子是绿色的,别买错买成其他颜色的了。
学习链接:小蜜蜂蓝桥杯单片机
在这个视频中,虽然有一些模块与前一个视频51单片机如中的类似,但每一个模块都有不一样的地方,所以都是需要认真看完的。
如果只是看完这个视频的话,有很多模块会考到的但是小蜜蜂老师并没有讲到,有部分的模块在小蜜蜂老师的官网上也能找得到对应的代码,但是比赛时候基本是手搓代码的,如果直接看他给的代码很大概率记不下来,所以需要找视频看去理解这些代码是如何写出来的。除去这个视频之外我们还需要学习的模块有:at24c02、超声波、pcf8591。
学习完这个之后那就需要刷几套题了,需要做到一个怎样的地步呢?就是你能除去赛点给出的代码之外还能够完全手搓出来3套省赛题目。那就已经基本稳省一直接进国赛了。
赛前其实不需要多少时间复习的,只需要复习每一个模块怎么写,然后看着原理图上的引脚去写就可以了。至于其他的主要还需要积累与逻辑的临场发挥。这个时间点的话如果你时间够多可以写一道国赛题,注意一定是国赛题,就算是打省赛你要是能在省赛前把国赛题做出来那已经说明省一基本稳定拿下的了。下面我会详细介绍一些方法与技巧。
可能有人会问为什么我没有给出客观题该如何复习。我只能说,性价比太低了,吃力不讨好,客观题考得非常的杂,涉及到的方面主要包含:单片机原理、数字电路、模拟电路三大模块。并且有一些是没有办法通过学习学到的,需要从用户手册中找的,这个也是看自己的造化了。最重要的是客观题只占15分,都是不定项选择题,稍微蒙对一两道也差不多了,只要后面的程序题写出来了基本上挺稳的。所以客观题没有任何必要
我认为模块化编程非常的重要,在江科大的单片机课程中有教到,用模块化来写代码会让代码非常整洁,自己也容易看,检查起来也比较容易。然后会让main函数里面的代码变得非常的简洁,我分享给你们我写的一个第十届国赛真题的代码,主函数里面就只有十几行代码。(可能程序有一点小bug但是应该不影响整体功能实现)
链接:第十届国赛
提取码:u9lx
不要用reg52了,不要用reg52了,不要用reg52了。重要的事情说三遍,reg52是好,可以适配所有的51单片机,但是也因为他太普通了,有一些特殊的io口都需要自己去定义,非常的麻烦,但是头文件也不是你想写就能写的。还需要先配置。
首先,先打开stc-isp,然后找到keil仿真设置
在这里可以选择单片机型号,蓝桥杯单片机的型号是IAP15F2K61S2,如果你需要配置其他的话那就选择其他的型号即可。
下一步就是需要找到你电脑中keil的文件路径,该路径下要包含C51、UV4两个文件夹
然后再点击keil仿真设置中的“添加型号的头文件到keil中”,找到上面说的keil文件路径再点击确定即可。
最后重启keil,就可以在选择芯片的时候选择STC15F2K61S2。
可能有人会问为什么前面是IAP15F2K61S2而选择芯片的时候是STC15F2K61S2,这两个有什么不同呢?其实我也不知道,貌似都是同一个东西,照着用就可以了。
选择好芯片之后就可以直接右键包含头文件了,这个头文件就是当前芯片的,不需要再额外定义引脚,非常的方便,而且在使用stc-isp中的定时器计算器之类的东西时候会出现的一个辅助寄存器AUXR也是有定义的可以直接使用。
我了解到目前有些同学在学习51单片机时比较少用stc-isp这软件去生成代码,又或者忘记一些模块如何写的时候不知道改怎么办。其实这些在stc-isp上都有的,很多代码可以直接生成不需要自己打,也有很多的代码示例可以参考。
如果是需要用到Delay延时并想要通过输入变量来控制延时时间的话,那就先生成一个1ms的延时
先在右上方找到“软件延时计算器”,然后在这里有三个项需要填,一个是系统频率,在stc89c52中是有一个固定的晶振震动频率的,这个就需要看你自己的板子来选择了;而如果是蓝桥杯的单片机板子上是通过在左边选择烧录进去的频率进行决定的,一般而言蓝桥杯比赛中会要求使用12MHz频率,那就选择这个。第二个就不用多说了,自己选择即可。第三个是指令集,就是适用于哪块板子的延时函数,所以你需要看好自己的板子型号,然后看右边适用于哪个系列的。自己选择即可。
选择相应配置后得到一个1ms的延时函数
void Delay1ms() //@12.000MHz
{
unsigned char i, j;
i = 12;
j = 169;
do
{
while (--j);
} while (--i);
}
然后只需要改一点东西就能够变成可以自己输入变量xms的函数
void Delay(unsigned int xms) //@12.000MHz
{
while(xms--)
{
unsigned char i, j;
i = 12;
j = 169;
do
{
while (--j);
} while (--i);
}
}
定时器的代码生成比延时函数稍微复杂一点,并且可能需要先了解定时器之后才好理解代码,首先还是先在右上角选择定时器计算器
这里的系统频率与延时函数中的同理,定时长度也不说了,比较好理解。然后就是选择定时器了,一般而言最常用的是定时器0和定时器1,其他的我没有用过。然后定时器模式的话就得先学过定时器是什么才能理解了。
但是要知道的是stc89c52中定时器是没有16位自动重载模式的,所以用这款芯片时需要注意,而蓝桥杯的板子中是有16位自动重载模式的;并且stc89c52中也没有AUXR这个寄存器,所以生成之后需要手动把这个寄存器删掉(不会影响正常使用),而蓝桥杯单片机有这个寄存器,如果你发现编译不成功原因是AUXR未定义的话那就看我文章前面如何添加对应芯片的头文件,如果这个芯片有这个寄存器是会有定义的。
定时器时钟有两个选项,一个是1T,一个是12T,区别在于12T能够定时的最大时间比1T长12倍,但是定时的误差也会更大。(但一般来说不会超过1%就可以把误差视为0)一般来说在51单片机中这个误差无关紧要。
波特率计算器与上面定时器计算器配置比较相似,需要注意的是在51单片机中波特率一般配置为9600或者4800,这两个比较常用,如果在蓝桥杯比赛中要用到串口的话那就得多留意题目,题目会告诉你用多少波特率的,按照题目说的配置就可以了,至于波特率倍速,应该是用来降低误差的,如果发现误差太大那就使用波特率倍速降低误差。如果误差还是太大的话建议更改波特率。
当你们玩一段时间单片机后应该就会发现,想要按按键之后还能显示数码管会很难做吧,然后网上找一些教你们如何按下按键还能够不影响其他程序的运行。我找过的都是使用定时器扫描按键,个人觉得不容易理解且如果定时器被其他程序占用时可能会出现一些抽象的bug,可移植性比较差。然后我就自己琢磨出一个简单,容易理解,并且代码可移植性强的方法:
unsigned char key() { unsigned char keynum=0,a=0; if(P30==0){keynum=1;} if(P31==0){keynum=2;} if(P32==0){keynum=3;} if(P33==0){keynum=4;} if(P30==0 && P31==0){a=1;} return keynum; } void keyrun() { unsigned char keynum; keynum=key();//这里的keynum和上面函数的keynum不是同一个哦 if(keynum!=keytemp)//当前状态和前状态不相同时进行按键消抖操作 { Delay(10);//这是一个延时函数延时10ms,用来做按键消抖 } if(keynum==0 && keytemp==1)//当前状态为0,即按键没按下;前状态为1,即按键1按下——上升沿触发按键 { //按键1松开做的操作 } if(keynum==0 && keytemp==2) { //按键2松开做的操作 } if(keynum==0 && keytemp==3) { //按键3松开做的操作 } if(keynum==0 && keytemp==4) { //按键4松开做的操作 } keytemp=keynum;//获取前一次状态 }
这里的a是用来做两个按键按下需要实现的逻辑的,如果题目没有涉及这个就不需要。就例如23年国赛题目中就需要实现两个按键按下2s后就实现一个初始化的操作。那我们就可以用这个a做判断,当a=1时在定时器中写入一个变量加一,直到判断a=1超过2秒后再做一个判断a变成0之后就做初始化操作。这个应该不难,你们可以先去做一次23年的国赛题,如果不会就私信问我哦。
P30、P31、P32、P33是四个独立按键的IO口,一般来说,我比较喜欢把key()放在另一个.c文件里面。然后包含头文件key.h。这个就需要用到模块化了
在我上面给出的超声波的学习链接中用的是基本定时器1或2,这样会导致一个弊端,如果题目中又考了ne555,又考了超声波的话会导致定时器不够用,那怎么办呢?只需要让超声波用另一个定时器就可以了。
下面是正常情况下超声波的代码,用到的是定时器1,然后我们用另一个定时器PCA定时器,这个和其他定时器有什么区别不需要特别了解,只需要把代码中的T换成C就是PCA的寄存器了,并且除了CMOD其他的作用都是相同的,可以直接跟定时器一样使用。
sbit TX=P1^0; sbit RX=P1^1; void send_wave() { unsigned char i; for(i=0;i<8;i++) { TX=1; Delay13us(); TX=0; Delay13us(); } } int receive_wave() { unsigned int dis,time; TMOD &= 0x0F; //设置定时器模式 TMOD |= 0x10; //设置定时器模式 TL1 = 0x00; //设置定时初始值 TH1 = 0x00; //设置定时初始值 TF1 = 0; //清除TF1标志 TR1 = 0; //定时器1不开始计时 send_wave(); TR1 = 1; //定时器1开始计时 while(RX == 1 && TF1 == 0); //等待超声波信号返回或者等到测量超出范围 返回RX = 0 TR1 = 0; if(TF1 == 1) //溢出了还没有返回信号 { TF1 = 0; dis = 999; } else { time=(TH1<<8)|TL1; dis=time*0.0172; } return dis; }
改好寄存器后所有都赋值成0即可
sbit TX=P1^0; sbit RX=P1^1; void send_wave() { unsigned char i; for(i=0;i<8;i++) { TX=1; Delay13us(); TX=0; Delay13us(); } } unsigned char receive_wave() { unsigned int time,dis; CMOD = 0x00; //设置定时器模式 CL = 0x00; //设置定时初始值 CH = 0x00; //设置定时重载值 CF = 0; //清除CF标志 CR = 0; //定时器0不开始计时 send_wave(); CR=1; while(RX==1 && CH<0x17); CR=0; if(RX==0) { RX=1; time=CH*0x100+CL; dis=time*0.017; } else { dis=99; } return dis; }
在这里我还使用了一个小技巧
CR=1;
while(RX==1 && CH<0x17);
CR=0;
这段代码中,如果是按照视频中的判断RX等于0或者CF标志位变1时才跳出循环的话那会导致while卡住的时间非常的长,然后就会导致数码管闪得很明显,这种情况再蓝桥杯中是会被扣大分的。那需要怎么做呢?先看题目要求,一般来说是只需要检测距离一米以内,就是以厘米为单位取两位数的范围,那就只需要知道这个时间大于99cm所用的时间就直接跳出循环就可以了,这个怎么算也是一个非常简单的数学知识,我就不多赘述了,你们也可以直接用我算出来的这个值CH<0x17,其实算出来之后是0x16左右但为了避免误差就把这个值提升了0x01,这样就可以减少非常多while卡主的时间,完美解决数码管闪烁的问题。
可能有的人会说用定时器扫描数码管也可以避免这个问题,我个人曾经尝试过这种写法但是这样会导致定时器的误差变得很大,因为跑数码管的代码挺复杂的,51单片机算力不够,所以这个代码运行时间比较长,会提升定时器的误差,所以我个人不建议使用这个方法,直接扔在while(1)里面跑就可以了。
串口省赛没有考过,所以如果是备战省赛的时候其实可以不需要太深入的学习,只需要能够做到发一个字节,收一个字节就差不多了,这个应该还是比较简单的。如果你们冲进国赛了那就再来看这里下面的内容。
串口考得难的话可能会需要你能够收发字符串并且进行操作,这个会比较难,并且按照传统的串口收发来说只能一个字节一个字节的发送。那这需要怎么做呢?
//首先就是需要包含这个在c语言中非常常用的头文件 #include <stdio.h> unsigned char uart_dat[8]; //这里其实就是很普通的单片机发一个字节数据的函数 void send_uart(unsigned char dat) { SBUF=dat; while(TI==0); TI=0; } //这个就是发字符串的函数,但是有弊端,无法发送变量的值,所以下面会介绍使用printf来做 void send_string(unsigned char *dat) { while(*dat!='\0') send_uart(*dat++); } //这里就是单片机收数据的函数了,与传统的收数据不同,这个函数可以接收多个字节,也就是接收字符串 unsigned int cnt,Rdat; void uart_interrupt() interrupt 4 { if(RI==1) { RI=0; Rdat=SBUF; //每一次SBUF都只会存储一个字节的数据,所以存上一个数据之后就读取出来给到uart_dat[]。uart_dat[]的大小看题目要求自行定义即可 if(Rdat!='\n') { //只要Rdat还有值那就继续读取 uart_dat[cnt]=Rdat; cnt++; } else { //Rdat没有值时就让最后一位赋值一个'\n'并且让uart_num=1 uart_dat[cnt]='\n'; cnt=0; uart_num=1; } } } //这个函数需要放在while(1)里面一直跑 void uartrun() { //这里判断uart_num是否是1,如果是那就证明接收完了字符串,就做下列操作 if(uart_num==1) { //进入函数后uart_num清零 uart_num=0; //这里写入接收完字符串后需要做的操作 } } //这个函数是对putchar的重定义,printf这个函数内部是调用putchar函数来输出字符串的 //我们重定义之后就可以使用printf来直接单片机发送字符串了,这样就可以打印变量出来 char putchar(char ch) { //重定义非常简单,就是调用发送字节的那个字符串 send_uart(ch); return ch; } //例如: void main() { //打开串口之后复位就能打印出东西了,与c语言中printf的用法相同 printf("$%d,%d.0%d\r\n",(int)distence,T_high,T_low); while(1) { } }
当你们都按照我上面说的全部内容做完之后,那就相当于算是已经学会了所有模块了,那就可以不需要再学了,直接等到赛前一周(我是觉得三个晚上足够的,如果你们比较慌的话那就提前一个星期)开始复习所有模块,除去赛点提供的代码外所有的代码自己手搓出来,不要复制粘贴,可以先用一个晚上把所有的模块都看一遍先,然后第二天到第三天就把所有的模块都整合到一个工程里面跑,尽量做到只需要按按键就可以把所有的模块都能实现。给你们看一下我国赛前3天晚上做的一个整合。
链接:所有整合
提取码:7a7z
里面包含了几乎所有的模块(除了矩阵按键和独立按键无法共存外)。我是觉得如果你们能够像我一样所有模块都写出来的话那国二国三应该不成问题。
主要还是需要你们自己能够把视频看完,视频看完是最重要的,特别是后面pcf8591、at24c02那几个视频,是教你如何看用户手册来写代码的(这些东西在比赛时候会有给的),我觉得那个视频教的非常好,只可惜不知道为什么没有什么流量。
下面是比赛是能给你的数据包
链接:2023单片机赛点数据包
提取码:z0ot
未完待续
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。