2023-05-12

51单片机简单学习教程(适合新手朋友)

学习51单片机之前你一定要具备的基础知识。

1、C语言基础。

2、数字电路基础。

3、模拟电路基础。

如果你已经具备这些知识那么我们就可以来学习单片机。

学习单片准备工作:1、51单片机开发板一张。

2、电脑一台。

学习的重点:

1、51单片机的最小系统。

51单片机最小系统.jpg

所谓的最小系统其实就是能够保证单片机正常运行的最小电路,它分为一下三个部分。

①电源系统。

5V电源.jpg

电源是单片机的能量来源,没有它单片机什么也不能干,我们单片机通常使用的有两种电压一种是5V另外一种是3.3V,现在学的是传统单片机(AT89S52,STC89C52),所以电压就5V,如果以后用增强型单片机一定要去读规格书看看单片机的电压。

②复位系统。

51单片机复位电路.jpg

如果单片机死机,或者想让单片机恢复到初始状态,就需要复位电路进行复位,复位电路有两种,一种是上电复位,也就是说当你给单片机通电的时候单片机就会自动进入初始状态,另外一种就是按键复位,你可以在任何时候让单片你恢复到初始状态,只需按下该复位按钮。

③时钟系统。

51单片机时钟电路.jpg

通常我们所说的单片机时钟就是我们平时所说的电脑的主频,电脑的主频越高,电脑的性能就越好,运行的速度就越快,那么我们单片机也是一样的时钟越高,单片机的运行速度就越快,但是51单片机的时钟也是有限制的不能高于33MHZ,(STC公司的单片机时钟频率可以高达40MHZ,但他是1T单片机,所谓的1T就是单片机的 机械周期 =1/f ,就是接下来要讲的内容)。

51单片机的时钟、状态、机器周期和指令周期

①时钟周期

时钟周期有称为振荡周期是51单片机的最小时间单位,在一个时钟周期内,CPU仅完成一个最基本的动作。

T=1/F F为晶体振荡器的频率,如果晶体振荡器的频率为12MHZ那么T = 0x0000000833333S

②状态周期

在51单片机中把1个时钟周期定义为一个节拍(用P表示),2个节拍定义为一个状态周期(用S表示)。

③机械周期

一条指令的执行过程可以分为若干个阶段,如取指令、读存储器、写存储器等。完成某一个操作的时间称为一个机器周期。通常情况下,一个机器周期由 12个时钟周期组成。

④指令周期

执行一条指令所需要的时间称为指令周期,一般由若干个机器周期组成。指令不同,所需的机器周期数也不同,一般由1~4个机器周期组成。

如:MOV A, Rn (数据传送指令) 就只需要一个机械周期,如果晶体振荡器为12MHZ那么执行该指令就只需要1us的时间, DIV AB (除法指令)就需要四个机械周期,执行该指令就需要4us的时间。

注意:上面讲的51单片机的汇编指令,后面我们写的程序是用C语言,但是C语言最终是会编译成汇编指令,在keil上的debug中可以看得到,后面讲的内容中延时程序就是通过以上的指令周期来进行计算的。

在上面讲过STC的增强型单片机是1T的单片机,它是机械周期=时钟周期,也就是说执行MOV A,Rn 这条指令只需要0.08us左右,比如STC8H8K64U,STC32(32位51单片机)。

2、点亮一只发光二极管。

①、学习单片机的I/O口操作(I/O 是input/output的简写,为输入输出接口)。

51单片机管脚图.jpg

51单片机一共有4个并口,分别是P1口,P2口,P3口,P4口,每一个并口有8位,每位引脚可以输入输出1位数据(0/1)。如P1口当中的8位分别表示为(按照从高位到低位排列):P1.7,P1.6,P1.5,P1.4,P1.3,P1.2,P1.1,P1.0。

那我们怎么操作这些I/O口呢 ?就拿P1来举例。

如: P1 = 0X01;那么P1口输出的数据如图:

51单片机P1口输出的每一位数据.jpg

P1.7 输出0,P1.6 输出0,P1.5输出0,P1.4 输出0,P1.3 输出0,P1.2输出0,P1.1输出0,P1.0 输出1,刚好就对应了十六进制0x01的二进制数据00000001B。51单片机I/O口操作是不是就这么简单。

②发光二极管的接法(共阴极还是共阳极)如图所示:

LED共阳极.jpg

如共阳接法,那么单片机的I/O口就要输出低电平(也就是逻辑0)该LED就能点亮,如共阴接法,那么单片机I/O口就要输出高电平(也就是逻辑1)该LED就能点亮。

③代码

如图所示LED的阴极接在单片机的P0.0,阳极通过一个限流电阻和一个电流表接在电源的正极,现在需要通过程序点亮这只LED。

单片机接一支LED(闪烁).gif
#include<reg52.h>//包含一个单片机标准的头文件(里面包含了单片机各个寄存器的定义)
void   main()//主函数 一个工程里面有且只能有一个主函数。名字必须为main
{
     while(1)//while大循环,只要while(1)小括号里面的内容不为0,那么单片机就会一直循环运行while大括号里面的内容
    {
        //发光二极管阳极接的是电源的正极,阴极接的是单片机的P0.0。
        P0=0xfe;//点亮一只发光二极管  P1  (P1.7  P1.6  P1.5  P1.4  P1.3  P1.2  P1.1  P1.0)  十六进制//        1    1    1    1    1    1    1    0        0xfe
    }
}

如果能理解上面的这个点灯程序,那么你已经踏入单片机的大门了,接下来就来强化这个点灯程序程序来让LED以500ms的速度闪烁起来,单片机的晶体振荡器位12MHZ。

分析:从题目可知让LED以500ms的速度闪烁起来,其实就是让LED亮500ms然后在熄灭500ms,然后在亮500ms,然后在熄灭500ms。那么这里的难点就在这500ms,我们要在程序中写出一个延时函数,这个函数的运行时间位500ms,当函数运行完后就改变LED的亮灭状态。

在学习单片机的时钟系统的时候,学习了指令周期,指令周期其实就是运行一条指令的时间,那么我们就可以通过运行一些指令来达到延时的目的。例如下面这一串汇编指令,单片机运行下面这一串汇编指令时间大概就是500ms,其实延时就是CPU执行一串没有意义的代码,唯一的作用就是延时。

这串汇编代码,只需要了解,如果想知道里面指令的作用,可以结合着51单片机的汇编指令来看。

DELAY500MS:         
    PUSH    30H   
    PUSH    31H
    PUSH    32H
    MOV 30H,#4
    MOV 31H,#205
    MOV 32H,#180
NEXT:
    DJNZ    32H,NEXT
    DJNZ    31H,NEXT
    DJNZ    30H,NEXT
    POP 32H
    POP 31H
    POP 30H
    RET

接下来就通过C语言来写延时函数:

/*
*函数名:延时函数
*描  述:当z = 1时,该演示函数大概延时1MS
*注  意:该延时函数为阻塞函数,当运行该函数时CPU只能运行它,其他什么事都干不了,只有CPU把它运行完后才能执行其他代码。
*/
void delay(int z)
{
    int x,y;
    for(x=z;x>0;x--)
    {
        for(y=110;y>0;y--)
    }
}
   delay(int z),括号里面的z,就是延时多少MS的参数入口,当z=1时,该延时函数就位1ms,如果要延时500ms,那么只需让 z = 500,就好了。

接下来就来编写LED闪烁代码:

#include<reg52.h>//包含一个单片机标准的头文件(里面包含了单片机各个寄存器的定义)
/*
*函数名:延时函数
*描  述:当z = 1时,该演示函数大概延时1MS
*注  意:该延时函数为阻塞函数,当运行该函数时CPU只能运行它,其他什么事都干不了,只有CPU把它运行完后才能执行其他代码。
*/
void delay(int z)
{
    int x,y;
    for(x=z;x>0;x--)
    {
        for(y=110;y>0;y--)
    }
}

void main()//主函数 一个工程里面有且只能有一个主函数。名字必须为main
{
    while(1)//while大循环,只要while(1)小括号里面的内容不为0,那么单片机就会一直循环运行while大括号里面的内容
    {
        
        P0 = 0xfe;//点亮LED
        delay(500);//点亮500ms
        P0 = 0xff;//熄灭LED
        delay(500);//熄灭500ms
        
    }
}   

能看明白以上例子后,我们就可以来做一个流水灯,8只LED分别点亮,间隔时间大概一秒钟,8只LED 接在单片机的P1口,假如该单片机的晶振频率为12MHZ,如图所示。

流水灯.gif

从该电路可知,8只LED为共阳极连接,所以点亮LED的数据就应该如下:

8只LED流水显示数据.jpg
#include<reg52.h>

/*定义一个数组,用来存放LED的点亮数据*/
char led[] = {0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};
/*
*函数名:延时函数
*描  述:当z = 1时,该演示函数大概延时1MS
*注  意:该延时函数为阻塞函数,当运行该函数时CPU只能运行它,其他什么事都干不了,只有CPU把它运行完后才能执行其他代码。
*/
void delay(int z)
{
    int x,y;
    for(x=z;x>0;x--)
    {
        for(y=110;y>0;y--)
    }
}

void main()
{
    char i;
    while(1)
    {
       for(i=0;i<8;i++)//一共有8只LED,所以循环8次,分别取出了led[]数组中的数据,送个P1口。
       {
            P1 = led[i];//分别把LED的点亮数据送入单片机的P1口
            delay(1000);//延时1S
       }
    }
}

3、51单片机的中断系统
在这里一定要理解中断的概念,实际上中断很简单,就是你现在正在做事情,然后你的领导教你帮他做事情,因为这是你领导你不能拒绝所以你只能去帮领导干事情,等你把领导的事情干完了以后,再回来接着你刚刚被领导打断的那里继续做你的事情,这就是中断。中断是单片机当中的一个重要内容,中断具有响应速度快的特点。51单片机(普通)一共有5个中断源,分别是外部中断(INT0)、定时器中断(T0)、外部中断(INT1)、定时器中断(T1)、串口中断(RX TX)。

51单片机中断系统.jpg

各中断源优先级的排列和中断序号

中断优先级.png

中断系统中一共有三个寄存器,分别是TCON,IE,IP实际上我们可以把这几个寄存器理解为设置开关(这些寄存器没有我们想象中那么复杂,它就是一个开关。为0就断开,为1就闭合),大家从上面的图都可以看出你要使用哪一个中断,你就要把相应的的开关打开。

例如我要使用外部中断(INT0)那么我首先要先择它的触发方式IT0(当IT0=0为电平触发,IT0 = 1为下降沿触发)在这里围殴选择下降沿触发,所以IT0 = 1;接下来就是EX = 1;EA是属于总中断开关所以EA = 1,接下来就是PX0,PX0为优先级选择,为1选择高优先级,为0选择低优先级,在这里我们就选则高优先级,所以PX0 = 1,这样设置先来外部中断(INT0)就可以使用了。

代码:

例子:

如图所示,在单片机P1口上接了4只发光二极管LED,都在单片机P3口的P3.2/INT0和P3.3/INT1上分别接了一个按钮开关(按钮就是你按下去就打开,松手就断开,游戏机手柄上面的按钮就是这样)按钮的另外一端接的是地,当按钮按下后单片机P3.2引脚电平就被拉低,单片机就能检测出引脚的电平变化,从而做出相应的相应。

注意:这里只用外部中断0(INT0),外部中1不用,会操作INT0那么INT1也就会操作了。 要求:当按钮P1按下,全部点亮LED,当按键松开LED做流水显示。

51单片机外部中断实验.jpg

51单片机中断实验

#include<reg52.h>

/*定义一个数组,用来存放LED的点亮数据,这里只有4个LED所以数组中只存放了4个LED的点亮数据。*/
char led[] = {0xfe,0xfd,0xfb,0xf7};

/*
*函数名:延时函数
*描  述:当z = 1时,该演示函数大概延时1MS
*注  意:该延时函数为阻塞函数,当运行该函数时CPU只能运行它,其他什么事都干不了,只有CPU把它运行完后才能执行其他代码。
*/
void delay(int z)
{
    int x,y;
    for(x=z;x>0;x--)
    {
        for(y=110;y>0;y--)
    }
}
/*
*函数名:外部中断INT0初始化函数
*描  述:通过设置上述寄存器,使得外部中断INT0能够正常工作。
*/
void int0_init()
{
    IT0 = 0;//关闭IT0,INT0为电平触发方式,
    EX0 = 1;//打开EX0,INT0响应中断请求
    EA  = 1;//打开EA,打开总中断
    PX0 = 1;//打开PX0,把INT0设置为高优先级,这里只有一个中断,其实可以不设置优先级。
}
/*主函数*/
void main()
{
    char i;
    int0_init();//初始化函数只运行一次
    while(1)
    {
       for(i=0;i<4;i++)//一共有4只LED,所以循环4次,分别取出了led[]数组中的数据,送个P1口。
       {
            P1 = led[i];
            delay(1000);//延时1S
       }
    }
}
/*外部中断INT0的中断服务函数*/
void int0()interrupt 0//这里的0表示为中断序号。
{
    //4只LED全部点亮
    P1 = 0x00;
    delay(5000);//点亮大概5S
}

当没有按下按钮,单片机会一直运行while循环里面的内容,当按钮按下后,单片机会立马执行int0()这个函数里面的内容,该函数里面的内容执行完毕后,有会回到主函数中while循环继续执行。

知识点: 在单片相应中断之前,单片机会自动保存当前while循环里面的运行状态(如:现在是点亮的第4只LED,那么单片机就会保存下来),在运行完中断代码后回来就会接着上次被打断的地方继续执行。

4、定时器/计数器

顾名思义就是定时,计数用的,51单片机(普通)一共有两个定时器(定时器T0和定时器T1)

我们要使用51单片机的定时器/计数器,我们就要对定时器/计数器进行设置,设置定时器/计数器一共有两个寄存器,一个是TMOD(模式控制寄存器),一个是TCON控制寄存器。

TMOD.png

TMOD的高四位用于控制定时器T1,低四位用于控制定时器T0。在我们后面的举例中我们都是用定时器T0。

GATE:若GATE=0,只需要把TR1/TR0设置位1就可以启动相应的定时器,若GATE = 1,则还需要将外部中断(INT0/INT1)引脚输入为高电平,才可以启动相应的定时器。

C/T:选择定时器还是计数器,C/T = 0定时模式,C/T = 1计数模式。

M1,M0:为定时器/计数器工作方式选择。

定时计数器工作方式.png

在后面的举例中都用定时器T0并且都工作在方式1(16位定时/计数器)所以M0 = 1,M1 = 0。由于工作在定时器所以C/T = 0,GATE = 0。

那么在设置TMOD的时候就可以直接设置成:TMOD = 0x01。如下图所示

TMOD设置.png
TCON.png

我们平时用得最多的是定时器/计数器中断,所以中断标志我们现在可以不管他,TCON里面位是可以进行位操作。 在后面的举例当中我们都是用定时器T0。所以要启动定时器T0,就可以直接操作TR0, TR0 = 1;就表示打开定时器T0,这是定时器T0就开始正常计数,当达到一定的计数值是就会产生中断。

要想运用定时器,那么还要了解定时器里面的一个最重要的两个寄存器TL0和TH0(计数寄存器)TL0为低8位,TH0为高8位。你可以把计数寄存器理解位一个水缸,当水缸里面的水装满了过后就会溢出,这时候你听到水声,就会马上跑过去关闭水龙头,这个就是定时器中断。当计数寄存器装满后就会发出中断请求,然后CPU就会相应该中断。

计数寄存器装的最大值位0-65535,当它装满65535就会发出中断请求,如:单片机的晶体振荡器位12MHZ,一个机械周期就为1us,那么计数寄存器就会1us计数一次,以此类推当计数寄存器计数到65535是,就相当于65535us,也就是65535us定时器就会发出一次中断请求。但是在我们做的项目中不需要这么长的时间响应中断,比如50000us相应一次中断,那我们怎么办呢?我们可以装入初值,65535-50000 = 15535,其中50000就为我们需要定时的时间,15535就为我为我们提前转入计数寄存器中的值,就我们有一个18升的水缸,我需要装6升水这个水缸就满了,所以我们提前就要往水缸里装如12升水。上面王计数寄存器装入初值也就是这个意思。这里我们要把15535十进制数转换成十六进制数,其中把高8位装入TH0中,把低8位装入TL0中。15535转换成十六进制为0x3caf,TH0 = 0x3c TL0 = 0xaf。

例子:用定时器中断操作流水灯,间隔时间为1S。

流水灯.gif
#include<reg52.h>

/*定义一个数组,用来存放LED的点亮数据,这里只有4个LED所以数组中只存放了4个LED的点亮数据。*/
char led[] = {0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};
char num;//定义一个全局计数变量
char i;//哪一只LED点亮
/*
*函数名:定时器T0初始化函数
*描  述:通过设置上述寄存器,使得定时器T0能够正常工作。
*/
void timer_init()
{
   TMOD = 0X01;//设置定时/计数器工作在定时器方式一
   TH0 = 0X3C;//装入初值,定时器每过50000us进入中断
   TL0 = 0XAF;
   ET0 = 1;//打开定时器T0中断
   EA  = 1;//打开总中断
   TR0 = 1;//启动定时器
}
/*主函数*/
void main()
{
    
    timer_init();//初始化函数只运行一次
    while(1)
    {
       //主函数里面什么也不做
    }
}
/*函数名:定时器T0中断服务函数
* 描  述:每过50000us运行一次
*/
void timer0()interrupt 1//这里的1表示为中断序号。
{
       TH0 = 0X3C;//每次运行中断服务函数都要为定时器从新装入初值
       TL0 = 0XAF;
       num++;//没中断一次num就自加一次,加一次为50000us(50ms),当num=20时就相当于1S.
       if(num==20)
       {
           num=0;//把num清零,让它从新计数
          P1 = led[i];//点亮LED
          i++;
          if(i==8)
          {
             i = 0;
          }
       }
}

学完以上内容,你的单片你就基本入门,接下来你就要对上面内容进行强化练习,然后在学习进阶内容,比如独立按键操作,矩阵键盘操作,液晶显示操作,电机调速(PWM),UART串口通信,IIC总线,SPI总线等等。

写得不是很好,文章中如有错误,还请大家指出,谢谢大家。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容