AutoLeaders控制组——51单片机的学习笔记(1)

51单片机学习笔记(1)

1. 环境配置

要使用单片机,需要下载下面两个软件:

Keil5用来编写程序,然后生成单片机可以识别的二进制文件,其后缀为.hex;

STC-ISP则是用来下载程序:打开生成的程序文件,然后通过数据线将程序下载到单片机上。

2.单片机

1.单片机介绍

 

ROM:只读存储器(Read-Only Memory),相当于电脑的硬盘,存储的数据也不会丢失。

RAM:随机存取存储器(Random Access Memory),相当于电脑的内存条,与CPU直接交换数据,断电后数据就会消失。

2.单片机管脚

 

3. LED

LED是发光二极管。LED省电而且亮度高,应用很广泛。

LED原理图:

1. 点亮LED

要点亮LED,就需要给P2寄存器特定数字,但C语言程序不能直接识别二进制,所以需要写成十六进制(前面要加上0x)。

#include <at89c51RC2.h>
​
void main()
{
    P2=0xFE;//1111 1110
    while (1)//如果不加上while循环,会导致不断地跑上面那行代码
    {
        
    }
}

2. LED闪烁

要让LED闪烁,需要让LED不断重复亮灭的操作。但是如果亮了之后立马熄灭会因为视觉暂留导致看起来是一直亮。所以需要在每次操作执行完后加入一个延时操作。

#include <at89c51RC2.h>
#include <INTRINS.H>    //包含_nop_()函数的头文件
​
void Delay500ms()       //@11.0592MHz
{
    unsigned char i, j, k;
​
    _nop_();
    i = 4;
    j = 129;
    k = 119;
    do
    {
        do
        {
            while (--k);
        } while (--j);
    } while (--i);
}
​
​
void main()
{
    while (1) {
        P2=0xFE;    //亮灯
        Delay500ms();
        P2=0xFF;    //全灭
        Delay500ms();
    }
}

3. LED流水灯

流水灯的操作通过让LED逐个亮起,同时每次操作后进行延时来实现。

为了能方便控制流水灯变化的速度,可以改变一毫秒延时函数:

void Delay1ms(unsigned int xms)     //定义参数为我们需要的时间xms
{
    unsigned char i, j;
    while(xms--){       //将一毫秒延时函数套入循环,循环次数为xms的大小
        _nop_();    //这样就实现了xms的延时
    i = 2;
    j = 199;
    do
    {
        while (--j);
    } while (--i);
    }
}

流水灯的具体代码如下:

#include <at89c51RC2.h>
#include <intrins.h>
​
void Delay1ms(unsigned int xms)     //@11.0592MHz
{
    unsigned char i, j;
    while(xms--){
        _nop_();
    i = 2;
    j = 199;
    do
    {
        while (--j);
    } while (--i);
    }
}
​
​
void main()
{
    while(1) {
        P2=0xFE;//1111 1110
        Delay1ms(1000);
        P2=0xFD;//1111 1101
        Delay1ms(1000);
        P2=0xFB;//1111 1011
        Delay1ms(100);
        P2=0xF7;//1111 0111
        Delay1ms(100);
        P2=0xEF;//1110 1111
        Delay1ms(100);
        P2=0xDF;//1101 1111
        Delay1ms(100);
        P2=0xBF;//1011 1111
        Delay1ms(100);
        P2=0x7F;//0111 1111
        Delay1ms(100);
    }
}

4. 独立按键控制

 

按下按键时,io口的线相当于接地,所以可以通过寄存器对应的高低电平判断按键是否按下。

四个按键分别对应P3_1;P3_0;P3_2;P3_3。等于1表示按键未按下,等于0表示按键按下。

1. 位运算

 

位运算是对位进行操作:

按位左移/右移:0011 1100 <<x (x表示移动的位数)。左移时高位移出去,低位会补上0;右移时低位移出去,高位补上0

按位与/或:0001 1000 &/| 0010 1010 每一位一一对应进行&或|的操作(0为假,1为真)。

按位异或:0001 1000 ^ 0010 1010 ->0010 0010 一个一个位比较,相同为0,不同为1。

按位取反:~0001 1000 ->1110 0111 0变成1,1变成0。

2. 独立按键控制LED亮灭

P2=0xFE是对8位的寄存器进行操作。

如果要单独对一个LED进行操作,需要写成P2_x=0或1 。(x为0到7,分别对应8个LED)

通过判断按键对应寄存器的电平,判断按键是否被按下,再执行按下后的操作。

#include <at89c51RC2.h>
​
void main()
{
    while(1) {
        if(P3_1==0) {
            P2_0=0;
        } else {
            P2_0=1;
        }
    }
}

该代码为按键1按下后灯亮,一松开灯就会熄灭。

3. 独立按键控制LED状态

1.消抖

 

由于按键的抖动,当我们按下一次的时候,可能单片机会判断成多次按下,导致产生与预期效果不符的结果。

所以,我们需要在按下和松开按键之后加入一定时间的延时,保证开关为电平稳定后再进行操作;

为了知道什么时候松手,需要用一个循环,让按键按下后一直处于循环中,松手后才执行后面的代码:

    if(P3_1==0) {       
        Delay(20);
        while(P3_1==0);//开关一直按下,松手后离开循环
        Delay(20);
    }

2. 控制LED状态

LED的初始状态均为1,也就是高电平,处于熄灭状态。

当我们对LED取反,1变为0,再取反0就变回1,从而实现LED状态的改变:

#include <at89c51RC2.h>
#include <intrins.h>
​
void Delay(unsigned int xms)        //@11.0592MHz
{
    unsigned char i, j;
    while (xms) {
        _nop_();
        i = 2;
        j = 199;
        do
        {
            while (--j);
        } while (--i);
        xms--;
    }
}
​
​
void main()
{
    while(1) {
        if(P3_1==0) {
            Delay(20);
            while(P3_1==0);
            Delay(20);
            
            P2_0=~P2_0;
        }
    }
}

4. 独立按键控制LED显示二进制

要实现二进制的显示,可以通过0000 0000每一次循环都加1,然后取反。

但是P2一开始为1111 1111,并且P2直接控制了灯的亮灭,如果取反,加1,取反,也不能出现预期效果。

所以需要引入一个初始化为0的变量,执行完加1,取反后再将值赋给P2。

#include <at89c51RC2.h>
#include <intrins.h>
​
void Delay(unsigned int xms)        //@11.0592MHz
{
    unsigned char i, j;
    while(xms--) {  
        _nop_();
        i = 2;
        j = 199;
        do
        {
            while (--j);
        } while (--i);
    }
}
​
​
void main()
{
    unsigned char LEDNum=0;
    while(1) {
        if(P3_1==0) {
            Delay(20);
            while(P3_1==0);
            Delay(20);
            
//      P2--;
            LEDNum++;
            P2=~LEDNum;
        }
    }
}

再尝试过之后发现,其实直接写个P2--也可以实现一样的效果。

5.独立按键控制LED移位

控制LED移位可以用0000 0001再通过<<运算符移位,用一个初始化为0的变量a表示移位的位数,

按一次按键移位的位数就加1,再将取反后的值赋给P2。当这个变量a等于8时,将其重新变为0。

如果要实现通过其他按键执行相反方向的移位,需要按下该按键后让a减1,左移位数减少,表现为右移的效果。

要注意如果a=0,要让a变为7来实现右移。

#include <at89c51RC2.h>
#include <intrins.h>
​
void Delay(unsigned int xms);
​
unsigned char LEDNum;//È«¾Ö±äÁ¿²»¸ø³õʼֵĬÈÏÊÇ0
void main()
{
    P2=~0x01;
    while(1) {
            if(P3_1==0){
                Delay(20);
                while(P3_1==0);
                Delay(20);
            
        
                
                LEDNum++;
                if(LEDNum>=8) {
                    LEDNum=0;
                }
                P2=~(0x01<<LEDNum);
            }
            if(P3_0==0){
                Delay(20);
                while(P3_0==0);
                Delay(20);
            
                
                if(LEDNum==0) {
                    LEDNum=7;
                } else {
                    LEDNum--;
                }
                P2=~(0x01<<LEDNum);
            }
        }
    }
​
​
void Delay(unsigned int xms)        //@11.0592MHz
{
    unsigned char i, j;
    while(xms--) {  
        _nop_();
        i = 2;
        j = 199;
        do
        {
            while (--j);
        } while (--i);
    }
}

5. 数码管

数码管由8个LED组成,7个构成数字或者其他的表示,1个用来表示小数点。

 

数码管接法:

 

3,8端为位选端,其他端为段选端(选中特定段的LED),此为共阴极接法,当位选为低电平,段选为高电平时,LED点亮。

四位一体数码管:

 

通过位选选中要显示的数码管位置,再通过段选选出想点亮的LED。所以每次点亮只能选中多个数码管中的一个,显示一个数字。

1. 静态数码管

1. 138译码器

138译码器控制位选:通过P2_4,P2_3,P2_2分别赋0或1,三个二进制数字转换为十进制,指定Y0到Y7中的一个为0。

使得对应数码管被选中。注意要从高位排到低位(P24,P23,P22)来写。

2. 74HC245

 

之前LED的点亮是给寄存器赋0点亮,是低电平点亮,低电平驱动能力较强,电流大,灯比较亮

但是这里数码管的点亮是高电平点亮,高电平驱动能力较弱,电流小。

所以用74HC245芯片作为缓冲器来提高驱动能力。

(将寄存器的信号作为控制信号,从自己接的电源上汲取能量,从而放大电流。)

3.数码管显示

通过P2的三个接口选中显示的数码管位置,再通过给P0段码的数据,点亮特定LED。

注意从高位排到低位(P0_7~P0_0)来写。

为了使主函数里的代码更简洁,我们可以通过函数和数组来实现模块化,具体代码如下:

unsigned char NixieTable[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
//上方数组对应数码管显示0到9
void Nixie(unsigned char Location,unsigned char Number) 
{
    switch(Location) {
        case 1:P2_4=1;P2_3=1;P2_2=1;break;
        case 2:P2_4=1;P2_3=1;P2_2=0;break;
        case 3:P2_4=1;P2_3=0;P2_2=1;break;
        case 4:P2_4=1;P2_3=0;P2_2=0;break;
        case 5:P2_4=0;P2_3=1;P2_2=1;break;
        case 6:P2_4=0;P2_3=1;P2_2=0;break;
        case 7:P2_4=0;P2_3=0;P2_2=1;break;
        case 8:P2_4=0;P2_3=0;P2_2=0;break;
    }
    P0=NixieTable[Number];
}
//引用函数时写成Nixie(a,b) a为数码管位置,b为显示的数字。

然后在主函数里引用函数,输入对应的参数,(引用函数时写成Nixie(a,b) a为数码管位置,b为显示的数字),即可实现想要的结果:

#include <at89c51RC2.h>
​
unsigned char NixieTable[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
​
void Nixie(unsigned char Location,unsigned char Number) 
{
    switch(Location) {
        case 1:P2_4=1;P2_3=1;P2_2=1;break;
        case 2:P2_4=1;P2_3=1;P2_2=0;break;
        case 3:P2_4=1;P2_3=0;P2_2=1;break;
        case 4:P2_4=1;P2_3=0;P2_2=0;break;
        case 5:P2_4=0;P2_3=1;P2_2=1;break;
        case 6:P2_4=0;P2_3=1;P2_2=0;break;
        case 7:P2_4=0;P2_3=0;P2_2=1;break;
        case 8:P2_4=0;P2_3=0;P2_2=0;break;
    }
    P0=NixieTable[Number];
}
​
void main()
{
    Nixie(5,7);
    while(1) {
        
    };
}

2 动态数码管

前面有说到数码管只能一次在一个位显示一个数字。要实现多位多个数字的显示,需要用到视觉残留。

当显示完一个后立马显示下一个,此时人眼就会看见多个数字。

但是如果直接把几行显示的代码紧密地接在一起,显示会错乱:动态数码管的显示是位选与段选的重复,如果接得太紧密,第一个数的段选会窜到下一个数的位选里,在那个位里面显示。

消影操作(在函数中加入代码):在每个数字显示之后Delay1毫秒,然后让数码管清零(P0=0x00)。从而消除显示错乱的现象。(在函数中加入代码)

#include <at89c51RC2.h>
#include <intrins.h>
​
unsigned char NixieTable[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
​
void Delay(unsigned int xms)        //@11.0592MHz
{
    unsigned char i, j;
    while(xms--) {
        _nop_();
    i = 2;
    j = 199;
    do
    {
        while (--j);
    } while (--i);
    }
}
​
​
​
void Nixie(unsigned char Location,unsigned char Number) 
{
    switch(Location) {
        case 1:P2_4=1;P2_3=1;P2_2=1;break;
        case 2:P2_4=1;P2_3=1;P2_2=0;break;
        case 3:P2_4=1;P2_3=0;P2_2=1;break;
        case 4:P2_4=1;P2_3=0;P2_2=0;break;
        case 5:P2_4=0;P2_3=1;P2_2=1;break;
        case 6:P2_4=0;P2_3=1;P2_2=0;break;
        case 7:P2_4=0;P2_3=0;P2_2=1;break;
        case 8:P2_4=0;P2_3=0;P2_2=0;break;
    }
    P0=NixieTable[Number];
    Delay(1);
    P0=0x00;
    
}
​
void main()
{
    
    while(1) {
        Nixie(1,1);
//      Delay(20);
        Nixie(2,2);
//      Delay(20);
        Nixie(3,3);
//      Delay(20);
    };
}

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值