万事开头难、要勇敢迈出第一步通过一个学期的学习单片机的学习已经进入尾声。开始的时候,不要老是给自己找借口,说KEIL不会建项目啦、没有实验板啦之类的。遇到困难要一件件攻克,不会建项目,就先学它,这方面网上教程很多,随便找找看一下,做几次就懂了。然后可以参考别的人程序,抄过来也无所谓,写一个最简单的,让它运行起来,先培养一下自己的感觉,知道写程序是怎么一回事,无论写大程序还是小程序,要做的工序不会差多少,总得建个项目,再配置一下项目,然后建个程序,加入项目中,再写代码、编译、生成HEX,刷进单片机中、运行。必须熟悉这一套工序。个人认为,一块学习板还是必要的,写好程序在上面运行一下看结果,学习效果会好很多,仿真器就看个人需要了。单片机是注重理论和实践的,光看书不动手,是学不会的。知识点用到才学,不用的暂时丢一边。厚厚的一本书,看着人头都晕了,学了后面的,前面的估计也快忘光了,所以,最好结合实际程序,用到的时候才去看,不必说非要把书从第一页看起,看完它才来写程序。比如你写流水灯,完全就没必要看中断的知识,专心把流水灯学好就是了,这是把整本书化整为零,一小点一小点的啃。程序不要光看不写,一定要自己写一次。最开始的时候,啥都不懂,可以抄人家的程序过来,看看每一句是干什么用的,达到什么目的,运行后有什么后果,看明白了之后,就要自己写一次,你会发现,原来看明白别人的程序很容易,但到自己写的时候却一句也写不出来,这就是差距……当你自己能写出来的时候,说明你就真的懂了。必须学会掌握调试程序的方法。这是一种很不好的行为,应该自己学会发现问题和学会如何解决问题。这就需要学习调试程序的方法,比如KEIL里,可以下断点啦,查看寄存器内容等等,这些都是调试程序的手段,当你发现你写的程序运行结果和你想象中不一样的时候,你可以单步,也可以下断点,然后跟踪,查看各相关寄存器内容,看看程序运行过中是不是有什么偏差,找出影响结果的地方,改正过来。这一个过程非常重要,通过程序的排错,你可以学到的知识是书上得不到的。找到解决问题思路比找到代码更重要。我们用单片机来控制周边器件,达到我们想到的目的,这是一个题目,而如何写出一个程序,来控制器件按你想要的结果去运作,这个就是解题的思路。要写程序,就得先找到解决问题的思路,你学会找出这个解题思路,比你找到代码更为重要。不少人很喜欢找人家的代码,有的人甚至有了代码就直接复制到自己的程序中,可以说,这不是一种学习的态度,无助于你编程水平的提高。我几乎不怎么看人家的代码,多数时候是看别人的思路,有方框图最好,没有的话文字说明也可以,要从代码中看出别人处理问题的思路,是相当困难的,特别是大型的程序,看起来是非常的累人,所以现在我也明白了,以前读书时说的程序流程图很重要,现在算是知道了。当你知道一个问题怎么去解决了,那么剩下的只是你安排代码去完成,这就已经不是什么问题了。举个例子:数码管动态扫描,没写过的初学者可能搞不清是怎么回事,其实,就是分时让每一时间段时只控制一只数码管显示数字,几只数码管轮流显示,由于速度很快,人眼的看起来是全部数码管都亮的。明白是这么一回事,事情就好办了,剩下的事情,无非是你安排让一只只数码管轮流显示出相应的数值。显示数字,然后延时一下,再下一只显示数字,延时,知道是这样,我们实际程序上只要做到这样就可以:往段口送段码,然后打开位选显示一只,延时一下,再关闭位选,再送出段码,再打开另一只位选仅此而已。有了解决问题的思路,我们就能问题拆分开来,然后逐一的解决,如果动态扫描的原理都没懂,不知道如何做,那么这个程序是怎么也写不出来的。开动脑筋,运用多种方法,不断优化自己的程序。想想用各种不同方法来实现同一功能。这是一个练习和提高的过程,一个问题,你解决了,那么你再想想,能不能换种写法,也可以实现同一功能,或者说,你写出来的代码,能不能再精简一点,让程序执行效率更高,这个过程,就是一个进步的过程。很多知识和经验的获得,并不是直接写在书让你看就可以得到的,需要自己去实践,开动脑筋,经验才能得到积累,编程水平才能有所提高。看别人的代码,学习人家的思路。这个在学习初期是很有用,通过看别人的代码,特别是有多年编程经验的人写出的具有一定水平的代码,可以使自己编程水平得到迅速的提高,同时,也可以结合别人的编程手法,与自己的想法融合在一起,写出更高水平的代码,从中得到进步。但要注意,切忌将学习变成抄袭,更不是抄袭完了就认为自己学会了,这样做只会使你退步。尝试编写一下综合应用的程序。从流水灯学起,到动态扫描,再到中断,那么,你可以试试写一下时钟这种综合性应用的程序,不要小看时钟,要写好它不是一件容易的事情,它包括了单片机大部分的知识,比如有按键(IO读取)、动态扫描(IO输出)、中断等,如何协调好各功能模块正常工作,才是编程者需要学习的地方,当你单独写一个功能的时候,比如按键读取,你可能感觉很容易,因为你的程序啥也不做,只是读按键。但把它和其它功能混合在一起,如何在整个程序运行中使每一部分都正常工作,这就不是写一个按键读取这么容易的事情,功能模块之间有可能会互相影响,比如你需要让数码管既能显示,又要去处理按键读取,怎么使这两部分都正常工作,这就是一个协调过程。当你有了这个处理协调能力,你就算是入门了。着重于培养解决问题的能力,而不是具体看自己编写了多少代码或者做过什么。“学单片机重点在于学习解决问题的思路,而不是局限于具体的芯片类型和语言”这一直是我的座右铭,是我学单片机多年来感悟出来的。经常看到有人说“你会驱动XX芯片,真牛啊”“你搞过XX项目,真厉害”之类的话,其实这是非常片面的,搞过XX芯片,搞过XX项目,只能说明你做过这一项目,它只是你的业绩,并不是代表能力就一定高。真正的能力应该是:“遇到没有解决过的问题或器件,能利用自己已学的知识,迅速找到解决问题的方法。”这个才是能力。写程序的过程就是一个创造的过程,几乎没有完全一样的项目,每次你遇上的几乎都不相同,所以你拥有的必须是你面对新项目时的创造能力,而不是标榜着你以往做过多少项目。当然,业绩也能从另一侧面反映你的经验和水平。如果有可能,多学习计算机专业的知识,比如数据结构等。这些是你解决问题的基础知识,你把这些知识应用得越好,就会发现越容易找到解决问题的方法,这就是为什么一个学计算机专业的人编的程序和一个非计算机专业的人编的程序有差异的原因。也是一个菜鸟进军到高手所要配备的知识。如果我们把编程分为宏观编程和微观编程,那么微观编程就是写具体的代码,比如控制某某器件的语句;而宏观编程就是如何对整个程序进行布局、安排,使功能模块以你想要的方式去运行,得出你想要的结果。如前所说“会控制XX器件”这些只能算是微观编程,能做到这一步还只能算是菜鸟级别,如果面对一个新的器件,你心里没底,没把握去写这个控制程序,那说明你还是一个初级的菜鸟。当你有了一定的编程经验,控制过相当数量的器件之后,你就会发现,控制器件这些工作都是相似的、重复的工作,体现不出编程的水平,最多也是写得好与不好的差别,只能算是一些小技巧的应用。而对整个程序进行布局、安排这些才是最头痛的事情,能达到宏观编程和微观编程都做好才是真正的高手。对于规模越大的程序,越能体现出这一点。面对一个新项目时,多自己开动脑筋,不要急于找别人的程序。有不少人面对一个新项目时,第一步想到的就是网上找别人写过的代码,然后抄一段,自己再写几句,凑在一起就完成任务,这虽然可能是省时间,但绝对不利你的学习。当你接到一个新项目时,应该先自己构思一下整个程序的'架构,想想如何来完成,有可能的话,画一个流程图,简单的可以画在脑子里,对程序中用到的数据、变量有一个初步的安排,然后自己动手去写,遇到实在没办法解决的地方,再去请教别人,或看别人是怎么处理的,这样首先起码你自己动过脑想过,自己有自己的思路,如果你一开始就看别人的程序,你的思维就会受限在别人的思维里,自己想再创新就更难了,这样你自己永远也没办法提高,因为你是走在别人的影子里。多利用网络的搜索,学会提问题。一般来说,学习过程中,你遇上的问题,前人们多数也有遇上的,所以如果有什么不懂,在自己解决不了的时候,最好先到网上搜索一下,看能不能找到答案,找不到再到论坛里发问,发问也要有目的性,尽量简单明了的描述问题,让帮助你的人可以用最少的时间就看懂你说什么,毕竟人家帮助你是免费的,时间也是有限的。
左右跑马灯
#include <reg52.h>
char LED;
void delay(unsigned int i)
{
unsigned char j;
for(i; i > 0; i--)
for(j = 255; j > 0; j--);
}
main()
{
unsigned char i;
while (1)
{ LED = 0xfe;
for (i = 0 ;i < 8 ; i++)
{
P1 = LED;
delay(500);
LED = LED << 1;
LED = LED | 0x01;
if (LED == 0x7f) break;
}
for (i = 0 ;i < 8 ; i++)
{
P1 = LED;
delay(500);
LED = LED >> 1;
LED = LED | 0x80;
}
}
}
再后来在学校学习做项目 与同学一起完成单片机小车的项目
温度显示实验
#include <at89x51.h>
sbit DQ = P2 ^ 6; //¶¨Òå¶Ë¿ÚDQ
sbit led1 =P2^0;
sbit led2 =P2^1;
sbit led3 =P2^2;
sbit led4 =P2^3;
unsigned char tx[10]={0,0,0x2E,0,0,0,0,0xDF,0x43,0x0A};
unsigned code table[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,
0xf8,0x80,0x90};
void Delay(int num)
{
while(num--) ;
}
void Delaynms(unsigned int di)
{
unsigned int da,db;
for(da=0;da<di;da++)
for(db=0;db<100;db++);
}
void Init_DS18B20(void)//³õʼ»¯ds1820
{
unsigned char x=0;
DQ = 1;
Delay(8);
DQ = 0;
Delay(80);
DQ = 1;
Delay(14);
x=DQ;
Delay(20);
}
unsigned char ReadOneChar(void)
{
unsigned char i=0;
unsigned char dat = 0;
for (i=8;i>0;i--)
{
DQ = 0;
dat>>=1;
DQ = 1;
if(DQ)
dat|=0x80;
Delay(4);
}
return(dat);
}
void WriteOneChar(unsigned char dat)
{
unsigned char i=0;
for (i=8; i>0; i--)
{
DQ = 0;
DQ = dat&0x01;
Delay(2);
DQ = 1;
dat>>=1;
}
}
void ReadTemperature(void)
{
unsigned char a=0;
unsigned char b=0;
unsigned char Data_L=0;
unsigned char num=0;
Init_DS18B20();
WriteOneChar(0xCC);
WriteOneChar(0x44);
Init_DS18B20();
WriteOneChar(0xCC);
WriteOneChar(0xBE);
a=ReadOneChar();
b=ReadOneChar();
tx[0] = (a/16+b*16)/10;
tx[1] = (a/16+b*16)%10;
Data_L=a&0X0F;
for(num=3;num<7;num++)
{
Data_L=Data_L*10;
tx[num]=Data_L/16;
Data_L=Data_L%16;
}
}
void Display_SMG(void)
{
unsigned char a;
for(a=0;a<=50;a++)
{
P0=table[tx[0]];
P2_0 = 0;
Delaynms(5);
P2_0 = 1;
P0=(table[tx[1]])&0x7f;
P2_1 = 0;
Delaynms(5);
P2_1 = 1;
P0=table[tx[3]];
P2_2 = 0;
Delaynms(5);
P2_2 = 1;
P0=table[tx[4]];
P2_3 = 0;
Delaynms(5);
P2_3 = 1;
}
}
void main(void)
{
Init_DS18B20();
while(1)
{
ReadTemperature();
Display_SMG();
}
}
我们制作的单片机小车基于51芯片的蓝牙红外小车,因为是新手也没有人教,网上的资料也不够齐全(很多都是跳过新手那一步,比如说接线),做的过程中遇到很多问题,做的比较艰难,因而写一篇记录整个过程的文章,希望能够给新手提供一些帮助同时也做一个总结。
材料可以去淘宝和拼多多购买 拼多多比较便宜
1.4wd小车一台,包含马达,两个底盘,和螺丝等(要自己组装,)
2. L298N电机驱动模块两个,用来控制马达的转速,方向,停止与否。
3. 51单片机最小系统板一个和STC89C52RC芯片一个
4.HC-05蓝牙模块一个(蓝牙遥控)和usb转TTL转接模块一个(用于设置和调试蓝牙模块)
5. 4路红外循迹模块(黑白循迹)
6. 18650电池四个和18650电池盒和杜邦线若干。
组装:
第二个难点就是组装,难在如何接线。
1.先把四个马达和车轮装在一起,组装成一个小车框架。
2.L298N电机的接线
一个L298N有四个输出口(out1,out2,out3,out4)接马达,每两个输出口控制一个马达,因此可以控制两个马达,12v接口接电源正极,5v接口是给最小系统板供电,所以5v接系统板5v口。四个IO输入口(in1,in2,in3,in4)通过芯片控制输出口。两个使能端口(A,B),只有高电平时电机才有有效,低电平时电机不转因为有两个电机只有一个供电源所以两个电机要并联连接在一起。下面是电机的控制表,当使能端位低电平(0)时电机处于停止状态,当输出口为(00或11)时处于制动状态,阻止电机转动。
L298N无效,无法驱动马达的原因(1.电压不够,起码要7v以上。2.使能端没有处于高电平状态 。3.L298N或马达坏了).
3.蓝牙的接线
蓝牙接在最小系统板,蓝牙的TXD(发送口)口接系统板的RXD(接收口)口,RXD口接TXD口也就是交叉连接,发送口对接收口。
在接在系统板之前,要对蓝牙进行一些设置(名称,密码,波特率),这时就要用到usb转TTL模块。
代码:
#include<reg52.h>
#include<intrins.h>
sbit LEA=P1^0;//左边轮的使能端A
sbit LEB=P1^1;//左边轮的使能端B
//左前轮
sbit LIN1=P1^2;//
sbit LIN2=P1^3;//
//左后轮
sbit LIN3=P1^4;
sbit LIN4=P1^5;
sbit REA=P3^2;//右边轮使能端A
sbit REB=P3^3;//右边轮使能端B
//右前轮
sbit RIN1=P3^4;
sbit RIN2=P3^5;
//右后轮
sbit RIN3=P3^6;
sbit RIN4=P3^7;
//方向控制
void delay(int time);//延时函数
void forward();//前进
void back();//后退
void stop();//停止
void start();//启动
void turnLeft();//左转
void turnRight();//右转
//蓝牙模块
//定时器1用作串口发生器,用于与蓝牙通信
void time1Init();//定时器1的初始化
void processMsg();//处理蓝牙接收到的信息
//红外模块
//从右到左
//in1是最右边的红外灯
sbit in1=P2^0;
sbit in2=P2^1;
sbit in3=P2^2;
sbit in4=P2^3;
int isTrace=0;//是否开启循迹功能
void trace();//红外循迹
//PWM调速
//通过控制使能端高低电平输出的时间从而控制速度
int count=0; //用于计数
int pwm=20; //表示启动的时间,20表示启动20ms
void time0Init();//定时器0的初始化.
//用于控制高低电平的时间
//定时器比较准确
void go();//启动 使能端为高电平 1
void pause();//停止 使能端为低电平 0
void time0Fun();//定时器0的中断函数
int main()
{
EA=1;//打开总中断
time1Init();//定时器1初始化
time0Init();//定时器0初始化
start();//使车的方向一开始向前
while(1)
{
if(isTrace)
trace();
if(count>=100) //100ms一个周期
count=0;
if(count<pwm) //pwm表示启动的时间
//pwm越大 速度越快
go();
else if(count>=pwm)
pause();
}
return 0;
}
void delay(int time)//延时函数
{
int i;
for(i=0;i<time;i++)
{
_nop_();//在晶振12M下
//一个nop函数延时1us
}
}
void forward()//前进
{
//前进,所有轮正转
LIN1=0;
LIN2=1;
LIN3=0;
LIN4=1;
RIN1=0;
RIN2=1;
RIN3=0;
RIN4=1;
}
void back()//后退
{
//前进,所有轮反转
LIN1=1;
LIN2=0;
LIN3=1;
LIN4=0;
RIN1=1;
RIN2=0;
RIN3=1;
RIN4=0;
}
void turnLeft()//左转
{
//左转 左边的轮正转,右边轮反转
LIN1=0;
LIN2=1;
LIN3=0;
LIN4=1;
RIN1=1;
RIN2=0;
RIN3=1;
RIN4=0;
delay(5000);//延时5ms 控制转弯的角度
}
void turnRight()//右转
{
//右转,左边轮反转,右边轮正转
LIN1=1;
LIN2=0;
LIN3=1;
LIN4=0;
RIN1=1;
RIN2=0;
RIN3=1;
RIN4=0;
delay(5000);//延时5ms 控制转弯的角度
}
void stop()//停止
{
//停止,全部高电平或低电平
LIN1=0;
LIN2=0;
LIN3=0;
LIN4=0;
RIN1=0;
RIN2=0;
RIN3=0;
RIN4=0;
}
void start()
{
forward();//一开始,前进方向
}
//定时器1用作串口发生器,用于与蓝牙通信
void time1Init()//定时器1的初始化
{
TMOD=0x20;//定时器1,工作模式2,8位自动重装模式
TH1=0xfd;//9600波特率,要与蓝牙的一致
TL1=0xfd;
//串口通信方式1
SM0=0;
SM1=1;
PCON=0x00;//波特率不加倍
SCON=0x50;//工作方式一
REN=1;//允许接收
ES=1;//开启串口中断
TR1=1;//启动定时1
}
void processMsg() interrupt 4 //中断4
//处理蓝牙接收到的信息
{
char ch;
//等待接收完毕
while(!RI)//当RI为0时没有接收完毕
; //为1时接收完毕
//信息存存在sbuf中
ch=SBUF;//获取接收的信息
switch(ch)
{
case 'f': //前进
forward();
break;
case 'b': //后退
back();
break;
case 'l': //左转
turnLeft();
break;
case 'r': //右转
turnRight();
break;
case 's': //停止
stop();
break;
case 'o':
start();
break;//启动
case 't': //开启循迹功能
isTrace=1;
break;
case '1': //一档
pwm=20;
break;
case '2': //二档
pwm=60;
break;
case '3': //三档
pwm=100;
break;
default:
break;
}
}
void trace()//红外循迹
{
//当红外遇到黑线时吸收红外光,输出高电平
//遇到白线时反射红外,输出低电平
int dir=0;//0表示直走
//1 代表左转
//2 代表右转
//0 0 0 0
if(in1==0&&in2==0&&in3==0&&in4==0)
dir=0;
//1 0 0 0
else if (in1==1&&in2==0&&in3==0&&in4==0)
dir=2;
//1 1 0 0
else if (in1==1&&in2==1&&in3==0&&in4==0)
dir=2;
//1 1 1 0
else if (in1==1&&in2==1&&in3==1&&in4==0)
dir=0;
//1 1 1 1
else if (in1==1&&in2==1&&in3==1&&in4==1)
dir=0;
//0 1 0 0
else if (in1==0&&in2==1&&in3==0&&in4==0)
dir=2;
//0 1 1 0
else if (in1==0&&in2==1&&in3==1&&in4==0)
dir=0;
//0 1 1 1
else if (in1==0&&in2==1&&in3==1&&in4==1)
dir=1;
//0 0 1 0
else if (in1==0&&in2==0&&in3==1&&in4==0)
dir=1;
//0 0 1 1
else if (in1==0&&in2==0&&in3==1&&in4==1)
dir=1;
//1 0 1 0
else if (in1==1&&in2==0&&in3==1&&in4==0)
dir=1;
//1 1 1 0
else if (in1==1&&in2==1&&in3==1&&in4==0)
dir=2;
//1 0 1 1
else if (in1==1&&in2==0&&in3==1&&in4==1)
dir=0;
//1 0 0 1
else if (in1==1&&in2==0&&in3==0&&in4==1)
dir=0;
//0 0 0 1
else if (in1==0&&in2==0&&in3==0&&in4==1)
dir=1;
//1 1 0 1
else if (in1==1&&in2==1&&in3==0&&in4==1)
dir=0;
switch(dir)
{
case 0:
forward();
break;
case 1:
turnLeft();
break;
case 2:
turnRight();
break;
}
}
void time0Init()//定时器0的初始化.
//用于控制高低电平的时间
//定时器比较准确
{
TMOD|=0x01;//工作方式一
//用或运算是为了不改变
//定时器1对应位的值。
TH0=0xfc;
TL0=0x66; //定时1ms
ET0=1;//开启定时器0的中断
TR0=1;//启动定时器0
}
void go()//启动 使能端为高电平 1
{
LEA=1;
LEB=1;
REA=1;
REB=1;
}
void pause()//停止 使能端为低电平 0
{
LEA=0;
LEB=0;
REA=0;
REB=0;
}
void time0Fun() interrupt 1
{
TH0=0xfc;
TL0=0x66;
//1ms 中断一次,count加一
count++;
}
以上就是学习单片机的成功 及项目制作