C51 单片机学习(一):基础外设

news2024/11/19 23:36:34

参考

  • 51单片机入门教程

1. 单片机简介

1.1 定义

  • 单片机(Micro Controller Unit,简称 MCU)
    • 内部集成了 CPU、RAM、ROM、定时器、中断系统、通讯接口等一系列电脑的常用硬件功能
    • 单片机的任务是信息采集(依靠传感器)、处理(依靠CPU)和硬件设备(例如电机,LED 等)的控制
    • 单片机跟计算机相比,单片机算是一个袖珍版计算机,一个芯片就能构成完整的计算机系统。但在性能上,与计算机相差甚远,但单片机成本低、体积小、结构简单,在生活和工业控制领域大有所用
    • 应用领域:智能仪表、实时工控、通讯设备、导航系统、家用电器等

1.2 STC89C52 单片机

  • STC 公司 51 单片机系列,8 位,RAM(512 字节),ROM(8K,Flash),工作频率 12MHz

    • STC89C52RC 的晶振集成在芯片内部,工作频率为 11.0592MHz
    • 单片机晶振的作用:利用一种能将电能和机械能相互转换的晶体,在单片机中提供一个精确的时钟信号,以便单片机能够正确执行其功能

    51 单片机为什么叫 51?

    • 是因为这种单片机最初采用的是英特尔公司的 8051 指令系统。随着时间的推移,有多家公司生产了兼容 8051 指令系统的单片机,它们虽然功能各异,但是核心架构相同,因此都被统称为 51 单片机
    • “51” 在这里代表了 8051 架构,也就是 Intel 公司发明的一种早期的单片控制器架构
  • 命名规则
    在这里插入图片描述

在这里插入图片描述

1.3 单片机内部结构图

  • 静态随机存取存储器(Static Random-Access Memory,SRAM)是随机存取存储器的一种,所谓的 “静态”,是指这种存储器只要保持通电,里面储存的数据就可以恒常保持
  • 只读存储器(Read-Only Memory,ROM)以非破坏性读出方式工作,只能读出无法写入信息,信息一旦写入后就固定下来,即使切断电源,信息也不会丢失,所以又称为固定存储器
  • 闪存是一种非易失性(Non-Volatile)内存,在没有电流供应的条件下也能够长久地保持数据
  • 单片机通过配置寄存器来控制内部线路的连接,通过内部不同线路的连接来实现不同电路以完成不同功能
    在这里插入图片描述

在这里插入图片描述

1.4 单片机管脚图

在这里插入图片描述

1.5 单片机最小系统

在这里插入图片描述

1.6 单片机核心原理图

在这里插入图片描述

2. LED

2.1 简介

  • 发光二极管(Light Emitting Diode,LED),用于照明、广告灯、指引灯、屏幕,是一种冷光源,因此比较环保且响应速度快,左图:左边是正极、右边是负极
    在这里插入图片描述

2.2 LED 原理图

在这里插入图片描述

  • 单片机上 RP9 上的数字 102:前面两位数是一个有效数字 10,第三位数字就是倍率 00,102 = 10*10^2 = 1KΩ(电阻或电容的通用读数方式),此处的电阻也称为限流电阻
  • 单片机可以通过控制 IO 口的输出模式和电平状态来实现对 IO 口输出高低电平的控制
    • 1、设置 IO 口为输出模式:在单片机的相应寄存器中设置 IO 口对应的引脚为输出模式
    • 2、控制 IO 口输出高低电平:将 IO 口对应的引脚设置为期望输出的电平,通常使用高电平表示逻辑 “1”,低电平表示逻辑 “0”

    单片机 TTL 电平:高电平 5V,低电平 0V,LED 具有单向导电性,当 LED 的正端接了高电位,负端连接了低电位,且正负端电位差超过 1.8V 以上时,LED 就会亮起来

2.3 进制转换

在这里插入图片描述

2.4 C51 数据类型

在这里插入图片描述

2.5 示例代码

  • 2-1 点亮一个 LED
#include <REGX52.H>  // 定义寄存器和端口(识别 P2 口)

void main() {
    P2 = 0xFE;  // 1111 1110,0x 前缀表示 十六进制,引脚配置为 “低电平有效”
    // P2 = 0x55;  // 0101 0101,8 个 LED 灯间隔点亮
    while (1) {
        
    }
}
  • 2-2 LED 闪烁
#include <REGX52.H>
#include <INTRINS.H>  // _nop_(); 需要的头文件

void Delay500ms() {  // @12.000 MHz
    unsigned char i, j, k;
    
    _nop_();  // 空操作命令,确保编译器不会对后续的循环优化
    i = 4;
    j = 205;
    k = 187;
    // 外层循环的条件是 i != 0
    // 内层两层循环的条件分别是 j != 0 && k != 0
    do {
        do {
            while (--k);  // 循环耗时操作
        } while (--j);    // 嵌套循环耗时操作
    } while (--i);
}

void main() {
    while(1) {
        P2 = 0xFE;     // 1111 1110
        Delay500ms();  // 单片机当中每次都是以 MHZ 速度运行,闪烁太快人眼看不出,因此要加延迟函数
        P2 = 0xFF;     // 1111 1111
        Delay500ms();
    }
}
  • 2-3 LED 流水灯(固定延时时间)
#include <REGX52.H>
#include <INTRINS.H>

void Delay500ms() {  // @12.000 MHz
    unsigned char i, j, k;
    
    _nop_();
    i = 4;
    j = 205;
    k = 187; 
    do {
        do {
            while (--k);
        } while (--j);
    } while (--i);
}

void main() {
    while (1) {
        P2 = 0xFE;  // 1111 1110
        Delay500ms();
        P2 = 0xFD;  // 1111 1101
        Delay500ms();
        P2 = 0xFB;  // 1111 1011
        Delay500ms();
        P2 = 0xF7;  // 1111 0111
        Delay500ms();
        P2 = 0xEF;  // 1110 1111
        Delay500ms();
        P2 = 0xDF;  // 1101 1111
        Delay500ms();
        P2 = 0xBF;  // 1011 1111
        Delay500ms();
        P2 = 0x7F;  // 0111 1111
        Delay500ms();
    }
}
  • 2-4 LED 流水灯2(自定义延时时间)
#include <REGX52.H>

void Delay1ms(unsigned int xms);  // @12.000MHz

void main() {
    while (1) {
        P2 = 0xFE;  // 1111 1110
        Delay1ms(100);
        P2 = 0xFD;  // 1111 1101
        Delay1ms(100);
        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);
    }
}

void Delay1ms(unsigned int xms)	{  // @12.000MHz
    unsigned char i, j;
    while (xms) {
        i = 2;
        j = 239;
        do {
            while (--j);
        } while (--i);
        xms--;
    }
}

3. 独立按键

3.1 按键介绍

  • 轻触按键:相当于是一种电子开关,按下时开关接通,松开时开关断开,实现原理是通过轻触按键内部的金属弹片受力弹动来实现接通和断开
    在这里插入图片描述

3.2 独立按键原理图

  • 单片机上电时所有 IO 口默认都是高电平,那么按键没有按下时这个 IO 口就是高电平,按下后这个 IO 口就变成低电平,寄存器会检测 IO 口的电平,然后再读回这个寄存器中
    在这里插入图片描述

3.3 C51 数据运算

在这里插入图片描述

3.4 C51 基本语句

在这里插入图片描述

3.5 按键的抖动

  • 对于机械开关,当机械触点断开、闭合时,由于机械触点的弹性作用,一个开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开,所以在开关闭合及断开的瞬间会伴随一连串的抖动
    在这里插入图片描述

3.6 示例代码

  • 3-1 独立按键控制 LED 亮灭
#include <REGX52.H>

void main() {
    while (1) {
        if (P3_1 == 0 || P3_0 == 0) {  // 如果 K1 按键(RXD,P3_1)或 K2 按键(TXD,P3_0)按下
            P2_0 = 0;  // LED1 输出 0,点亮
        } else {
            P2_0 = 1;  // LED1 输出 1,熄灭
        }
    }
}
  • 3-2 独立按键控制 LED 状态
#include <REGX52.H>

void Delay(unsigned int xms) {
    unsigned char i, j;
    while (xms) {
        i = 2;
        j = 239;
        do {
            while (--j);
        } while (--i);
        xms--;
    }
}

void main() {
    while (1) {
        if (P3_1 == 0) {        // 如果 K1 按键按下
            Delay(20);          // 延时,以消除按键抖动带来的影响
            while (P3_1 == 0);  // 判断 K1 按键是否仍处于按下状态,松手检测
            Delay(20);          // 延时,以消除按键抖动带来的影响
            P2_0 = ~P2_0;       // LED1 取反
        }
    }
}
  • 3-3 独立按键控制 LED 显示二进制
#include <REGX52.H>

void Delay(unsigned int xms) {
    unsigned char i, j;
    while (xms--) {
        i = 2;
        j = 239;
        do {
            while (--j);
        } while (--i);
    }
}

void main() {
    unsigned char LEDNum = 0;   // 无符号字符型(所占1字节 = 8bit位)刚好对应着 8 位二进制的数据
    while (1) {
        if (P3_1 == 0) {        // 如果 K1 按键按下
            Delay(20);          // 延时消抖
            while (P3_1 == 0);  // 判断 K1 按键是否仍处于按下状态,松手检测
            Delay(20);          // 延时消抖
            
            LEDNum++;           // 变量自增,用于切换 LED 灯的状态
            // P2 口上电之后和单片机的 IO 上电一样都是默认的是高电平:1111 1111
            P2 = ~LEDNum;       // 变量取反输出给 LED,控制 LED 灯的亮灭
        }
    }
}
  • 3-4 独立按键控制 LED 移位
// K1 = P3_1;K2 = P3_0;K3 = P3_2;K4 = P3_3
#include <REGX52.H>

void Delay(unsigned int xms);
unsigned char LEDNum;  // 全局变量定义默认为 0

void main() {
    P2 = ~0x01;     // 上电默认 LED1 点亮
    while (1) {
        if (P3_1 == 0) {       // 如果 K1 按键按下
            Delay(20);
            while (P3_1 == 0);
            Delay(20);
            
            LEDNum++;            // LEDNum 自增
            if (LEDNum >= 8)	 // 限制 LEDNum 自增范围
                LEDNum = 0;
            P2 = ~(0x01 << LEDNum);	 // LED 的第 LEDNum 位点亮
        }
        if (P3_0 == 0) {     // 如果 K2 按键按下
            Delay(20);
            while (P3_0 == 0);
            Delay(20);
            
            if (LEDNum == 0)    // LEDNum 减到 0 后变为 7
                LEDNum = 7;
            else                // LEDNum 未减到 0,自减
                LEDNum--;
            P2 = ~(0x01 << LEDNum);  // LED 的第 LEDNum 位点亮
        }
    }
}

void Delay(unsigned int xms) {
    unsigned char i, j;
    while (xms--) {
        i = 2;
        j = 239;
        do {
            while (--j);
        } while (--i);
    }
}

4. 数码管

4.1 简介

  • LED 数码管:一种简单、廉价的显示器,是由多个发光二极管封装在一起组成 “8” 字型的器件
    • 数码管分共阳数码管和共阴数码管
      • 共阳数码管:把 8 段 LED 的正极并在一起作为公共端连接在 5V 上(共阳极),然后 8 个 LED 通过单片机的 8 个 IO 端口输出高低电平使其决定点亮哪几个段
    • 数码管其实就是 8 个段的发光二极管,只点亮其中的几个段即可显示出数字或字母用来表达信息
      在这里插入图片描述

4.2 数码管的引脚定义

  • 以共阴极为例(下图右上),若要显示数字 6

    • 1、把共阴极的公共端(位选端)接地/负极,即给这个数据 “0” 或是低电平
    • 2、把段码 A、C、D、E、F、G 接正极,即给这个数据 “1” 或是高电平
      在这里插入图片描述
  • 以共阴极为例(下图右上),若要在第三位数码管显示数字 1

    • 1、把共阴极的公共端(位选端)当中的第三位数码管接地/负极,即给这个数据 “0” 或是低电平
    • 2、再给 1、2、4 上的位选给 “1” 或是高电平
      在这里插入图片描述

4.3 数码管原理图

  • LED1~LED8 都是接到 138 译码器上的输出端,138 译码器原理如下

    • 把 P22、P23、P24 三个端口变成 8 个端口(LED1~LED8)来控制
    • 左边的 A、B、C 是输入端(正极),右边 Y0~Y7 是输出端(负极)
      • C 是高位、B 在中间、A 是低位
      • C B A 按高、低位排序后,再将二进制转换为十进制数,对应着输出端 Y0~Y7,例如 C B A:0 0 0 = Y0,C B A:0 0 1 = Y1(对应 LED2),C B A:1 0 1 = Y5,C B A:1 1 0 = Y6
    • 右下角三个引脚称为使能端(相当于一种开关,如果使能电平有效,它就可以工作)
  • 74HC245 芯片作用:也称双向数据缓冲器,用来提高芯片驱动能力

  • 电容 CC2 作用:起到电源滤波作用,使得芯片的供电更加稳定

  • RP4 电阻:限流作用,100R 单位为 Ω
    在这里插入图片描述

  • 位选

    • 如 C B A = 0 1 1 = Y3 = LED4,LED4 就是有效的/允许显示数码管的,那么其它的数码管是不能被允许显示的/不是有效的
  • 段选

    • 选中之后,就是给 P0 口段码的数据:假设给上数据,经过缓冲送到公共端的段码端。那么,送到段码端就会显示数码管相对应的数字,P0 口给上数据是从高位到低位给上段码端的

4.3 C51 数组 & 子函数

  • 数组:把相同类型的一系列数据统一编制到某一个组别中,可以通过数组名 + 索引号简单快捷的操作大量数据

    int x[3];         // 定义一组变量(3个)
    int x[]={1,2,3};  // 定义一组变量并初始化
    
    x[0];             //引用数组的第0个变量
    x[1];             //引用数组的第1个变量
    x[2];             //引用数组的第2个变量
    // 引用 x[3] 时,数组越界,读出的数值不确定,应避免这种操作
    
  • 子函数:将完成某一种功能的程序代码单独抽取出来形成一个模块,在其它函数中可随时调用此模块,以达到代码的复用和优化程序结构的目的

    void Function(unsigned char x, y) {
    	
    }
    
    返回值 函数名(形参){
    	函数体
    }
    

4.4 数码管段码表

在这里插入图片描述

4.5 数码管驱动方式

  • 单片机直接扫描:硬件设备简单,但会耗费大量的单片机 CPU 时间
  • 专用驱动芯片:内部自带显存、扫描电路,单片机只需告诉它显示什么即可(如下述 TM1640
    在这里插入图片描述

4.6 示例代码

  • 4-1 静态数码管显示
#include <REGX52.H>

// 数码管段码表
unsigned char NixieTable[] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F};

// Location:数码管的位置,Number:显示数码管的数字
void Nixie(unsigned char Location, 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 (2, 3);    // 在数码管的第 2 位置显示 3
    while (1) {}
}
  • 4-2 动态数码管显示
#include <REGX52.H>

// 数码管段码表
unsigned char NixieTable[] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F};

//延时子函数
void Delay(unsigned int xms) {
    unsigned char i, j;
    while (xms--) {
        i = 2;
        j = 239;
        do {
            while (--j);
        } while (--i);
    }
}

// Location:数码管的位置,Number:显示数码管的数字
void Nixie(unsigned char Location, 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;                // 段码清 0,消影
}

void main() {
	while (1) {
		Nixie(1, 1);    // 在数码管的第 1 位置显示 1
//		Delay(20);
		Nixie(2, 2);    // 在数码管的第 2 位置显示 2
//		Delay(20);
		Nixie(3, 3);    // 在数码管的第 3 位置显示 3
//		Delay(20);
	}
}

5. 模块化编程和 LCD 调试工具

5.1 模块化编程

  • 传统方式编程
    • 所有的函数均放在 main.c 里,若使用的模块比较多,则一个文件内会有很多的代码,不利于代码的组织和管理,而且很影响编程者的思路
  • 模块化编程
    • 把各个模块代码放在不同 .c 文件里,在 .h 文件里提供外部可调用函数的声明,其它 .c 文件想使用其中的代码时,只需 #include “XXX.h” 文件即可,模块化编程可极大的提高代码可读性、可维护性、可移植性等
      • .c 文件:函数、变量的定义
      • .h 文件:可被外部调用的函数、变量的声明
  • 注意事项
    • 任何自定义的变量、函数在调用前必须有定义或声明(同一个.c)
    • 使用到的自定义函数的 .c 文件必须添加到工程参与编译
    • 使用到的 .h 文件必须要放在编译器可寻找到的地方(工程文件夹根目录、安装目录、自定义)
      在这里插入图片描述

5.2 C 预编译

  • C 语言的预编译以 # 开头,作用是在真正的编译开始之前,对代码做一些处理(预编译)
    在这里插入图片描述

5.3 LCD1602 调试工具

  • 使用 LCD1602 液晶屏作为调试窗口,提供类似 printf 函数的功能,可实时观察单片机内部数据的变换情况,便于调试和演示
    • LCD1602 连接的口是 P0 口 还占用三个 P2 口,所以使用 LCD1602 液晶屏后,三个 LED 就不能进行使用,数码管也不能使用

在这里插入图片描述

  • LCD1602 原理图
    在这里插入图片描述

5.4 使用示例

#include <REGX52.H>
#include <LCD1602.H>

int main(void) {
    unsigned int Number = 51;
    signed int negative = -1;
    LCD_Init();

    while (1) {
        LCD_ShowChar(1, 1, 'W');
        LCD_ShowString(1, 2, "XH");
        LCD_ShowNum(1, 4, Number,2);
        LCD_ShowSignedNum(1, 7, negative, 1);
        LCD_ShowHexNum(2, 1, 0xFF, 2);
        LCD_ShowBinNum(2, 4, 0x00, 8);
    }
}

6. 矩阵键盘

6.1 简介

  • 在键盘中按键数量较多时,为了减少 I/O 口的占用,通常将按键排列成矩阵形式,采用逐行或逐列的 “扫描”,就可以读出任何位置按键的状态
    在这里插入图片描述

  • 扫描

    • 数码管扫描(输出扫描)
      • 原理:显示第 1 位 → 显示第 2 位 → 显示第 3 位→……,然后快速循环这个过程,最终实现所有数码管同时显示的效果
    • 矩阵键盘扫描(输入扫描)
      • 原理:读取第 1 行(列) → 读取第 2 行(列) → 读取第 3 行(列) → ……,然后快速循环这个过程,最终实现所有按键同时检测的效果

    以上两种扫描方式的共性:节省 I/O 口

  • 单片机 IO 口模式

    • 单片机的 IO 口是一种弱上拉的模式,又被称作是准双向口(input,output 既可以输入又可以输出)
  • 为什么单片机它的 IO 口是默认为高电平呢?

    • 是因为有一个上拉电阻把低电平变成高电平了,所以才导致单片机是高电平
    • 还有一个是当口线输出为 1 的时候驱动能力很弱,允许外部装置将其拉低
    • 当引脚的输出为低电平的时候,它的驱动能力很强,可以吸收相当大的电流
    • 单片机中 P1、P2、P3 都是一种弱上拉的一种模式

6.2 矩阵键盘原理图

在这里插入图片描述

6.3 使用示例

  • MatrixKey.h
#ifndef __MATRIXKEY_H__
#define __MATRIXKEY_H__

unsigned char MatrixKey();

#endif
  • MatrixKey.c
#include <REGX52.H>
#include "Delay.h"

/**
  * @brief  矩阵键盘读取按键键码
  * @param  无
  * @retval KeyNumber 按下按键的键码值
            如果按键按下不放,程序会停留在此函数,松手的一瞬间,返回按键键码,没有按键按下时,返回 0
*/
unsigned char MatrixKey() {
    unsigned char KeyNumber = 0;
    
    P1 = 0xFF;
    P1_3 = 0;
    if (P1_7 == 0) {Delay(20); while(P1_7 == 0); Delay(20); KeyNumber = 1;}
    if (P1_6 == 0) {Delay(20); while(P1_6 == 0); Delay(20); KeyNumber = 5;}
    if (P1_5 == 0) {Delay(20); while(P1_5 == 0); Delay(20); KeyNumber = 9;}
    if (P1_4 == 0) {Delay(20); while(P1_4 == 0); Delay(20); KeyNumber = 13;}
    
    P1 = 0xFF;
    P1_2 = 0;
    if (P1_7 == 0) {Delay(20); while(P1_7 == 0); Delay(20); KeyNumber = 2;}
    if (P1_6 == 0) {Delay(20); while(P1_6 == 0); Delay(20); KeyNumber = 6;}
    if (P1_5 == 0) {Delay(20); while(P1_5 == 0); Delay(20); KeyNumber = 10;}
    if (P1_4 == 0) {Delay(20); while(P1_4 == 0); Delay(20); KeyNumber = 14;}
    
    P1 = 0xFF;
    P1_1 = 0;
    if (P1_7 == 0) {Delay(20); while(P1_7 == 0); Delay(20); KeyNumber = 3;}
    if (P1_6 == 0) {Delay(20); while(P1_6 == 0); Delay(20); KeyNumber = 7;}
    if (P1_5 == 0) {Delay(20); while(P1_5 == 0); Delay(20); KeyNumber = 11;}
    if (P1_4 == 0) {Delay(20); while(P1_4 == 0); Delay(20); KeyNumber = 15;}
    
    P1 = 0xFF;
    P1_0 = 0;
    if (P1_7 == 0) {Delay(20); while(P1_7 == 0); Delay(20); KeyNumber = 4;}
    if (P1_6 == 0) {Delay(20); while(P1_6 == 0); Delay(20); KeyNumber = 8;}
    if (P1_5 == 0) {Delay(20); while(P1_5 == 0); Delay(20); KeyNumber = 12;}
    if (P1_4 == 0) {Delay(20); while(P1_4 == 0); Delay(20); KeyNumber = 16;}
    
    return KeyNumber;
}
  • main.c
#include <REGX52.H>
#include "Delay.h"      // 包含 Delay 头文件
#include "LCD1602.h"    // 包含 LCD1602 头文件
#include "MatrixKey.h"  // 包含矩阵键盘头文件

unsigned char KeyNum;

void main() {
    LCD_Init();	  // LCD 初始化
    LCD_ShowString(1, 1, "MatrixKey:");  // LCD 显示字符串
    while (1) {
        KeyNum = MatrixKey();  // 获取矩阵键盘键码
        if (KeyNum) {  // 如果有按键按下
            LCD_ShowNum(2, 1, KeyNum, 2);  // LCD  显示键码
        }
    }
}

6.4 矩阵键盘密码锁

#include <REGX52.H>
#include "Delay.h"
#include "LCD1602.h"
#include "MatrixKey.h"

unsigned char KeyNum;
unsigned int Password, Count;

void main() {
    LCD_Init();
    LCD_ShowString(1, 1, "Password:");
    while (1) {
        KeyNum = MatrixKey();
        if (KeyNum) {
            if (KeyNum <= 10)	{    // 如果 S1~S10 按键按下,输入密码
                if (Count<4) {       // 如果输入次数小于 4
                    Password *= 10;  // 密码左移一位
                    Password += KeyNum % 10;  // 获取一位密码
                    Count++;    // 计次加一
                }
                LCD_ShowNum(2, 1, Password, 4);    // 更新显示
            }
            if (KeyNum == 11) {   // 如果 S11 按键按下,确认
                if (Password == 2345) {   // 如果密码等于正确密码
                    LCD_ShowString(1, 14, "OK ");   // 显示 OK
                    Password = 0;    // 密码清零
                    Count = 0;       // 计次清零
                    LCD_ShowNum(2, 1, Password, 4);   // 更新显示
                } else {
                    LCD_ShowString(1, 14, "ERR");  // 显示 ERR
                    Password = 0;    // 密码清零
                    Count = 0;		 // 计次清零
                    LCD_ShowNum(2, 1, Password, 4);  // 更新显示
                }
            }
            if (KeyNum == 12) {   // 如果 S12 按键按下,取消
                Password = 0;     // 密码清零
                Count = 0;        // 计次清零
                LCD_ShowNum(2, 1, Password, 4);  // 更新显示
            }
        }
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1417187.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Spark系列2】Spark编程模型RDD

RDD概述 RDD最初的概述来源于一片论文-伯克利实验室的Resilient Distributed Datasets&#xff1a;A Fault-Tolerant Abstraction for In-Memory Cluster Computing。这篇论文奠定了RDD基本功能的思想 RDD实际为Resilient Distribution Datasets的简称&#xff0c;意为弹性分…

Linux提权:Docker组挂载 Rsync未授权 Sudo-CVE Polkit-CVE

目录 Rsync未授权访问 docker组挂载 Sudo-CVE漏洞 Polkit-CVE漏洞 这里的提权手法是需要有一个普通用户的权限&#xff0c;一般情况下取得的webshell权限可能不够 Rsync未授权访问 Rsync是linux下一款数据备份工具&#xff0c;默认开启873端口 https://vulhub.org/#/envir…

Linux:共享内存

文章目录 System V共享内存的原理管理共享内存shmgetshmatshmdtshmctl 共享内存和管道实现进程间同步通信 前面介绍完了匿名管道和命名管道&#xff0c;那么本篇要引入的主题是共享内存 System V 作为进程通信部分的内容&#xff0c;共享内存必然有其存在的意义和价值&#x…

RabbitMQ快速实战

目录 什么是消息队列&#xff1f; 消息队列的优势 应用解耦 异步提速 削峰填谷 总结 主流MQ产品特点比较 Rabbitmq快速上手 创建用户admin Exchange和Queue Connection和Channel RabbitMQ中的核心概念总结 什么是消息队列&#xff1f; MQ全称Message Queue&#xf…

从零学习Linux操作系统 第二十二部分 企业域名解析服务的部署及安全优化

# 一、dns的主要信息 关于dns的名词解释&#xff1a;dns: domain name service(域名解析服务) 关于客户端: /etc/resolv.conf dns指向文件 A记录 ##ip地址叫做域名的Address 记录 SOA ##授权起始主机 关于服务端 bind安装包named服务名称/etc/named.conf主配置文件/var/na…

【深度学习:多关节嵌入模型】 Meta 解释的 ImageBind 多关节嵌入模型

【深度学习&#xff1a;多关节嵌入模型】 Meta 解释的 ImageBind 多关节嵌入模型 Meta 发布开源人工智能工具的历史分段任何模型DINOv2 什么是多模态学习&#xff1f;什么是嵌入&#xff1f;什么是 ImageBind&#xff1f;集成在 ImageBind 中的模式图像绑定架构特定模式编码器跨…

window下如何安装ffmpeg(跨平台多媒体处理工具)

ffmpeg是什么? FFmpeg是一个开源的跨平台多媒体处理工具&#xff0c;可以用于录制、转换和流媒体处理音视频。它包含了几个核心库和工具&#xff0c;可以在命令行下执行各种音视频处理操作&#xff0c;如剪辑、分割、合并、媒体格式转换、编解码、流媒体传输等。FFmpeg支持多…

java设计模式:工厂模式

1&#xff1a;在平常的开发工作中&#xff0c;我们可能会用到不同的设计模式&#xff0c;合理的使用设计模式&#xff0c;可以提高开发效率&#xff0c;提高代码质量&#xff0c;提高系统的可拓展性&#xff0c;今天来简单聊聊工厂模式。 2&#xff1a;工厂模式是一种创建对象的…

Java TemporalAdjusters 时间调节器

提供了非常多处理日期相关的函数&#xff1a; 使用示例&#xff1a; /*** JCccc* param args*/public static void main(String[] args) {DateTimeFormatter pattern DateTimeFormatter.ofPattern("yyyy-MM-dd");LocalDateTime now LocalDateTime.now();//获取当月…

备战蓝桥杯---二分(入门)

话不多说&#xff0c;先来个模板题来回顾一下上次讲的&#xff1a; 下面是AC代码&#xff1a; 下面进入正题&#xff1a; 本题对1&#xff0c;2行与3&#xff0c;4行组合&#xff0c;再用二分查找即可实现n^2logn的复杂度。 下面是AC代码&#xff1a; 接题&#xff1a; 让我们…

基于springboot校园交友网站源码和论文

随着信息技术和网络技术的飞速发展&#xff0c;人类已进入全新信息化时代&#xff0c;传统管理技术已无法高效&#xff0c;便捷地管理信息。为了迎合时代需求&#xff0c;优化管理效率&#xff0c;各种各样的管理系统应运而生&#xff0c;各行各业相继进入信息管理时代&#xf…

QT学习日记 | QT的环境搭建

目录 前言 一、QT概述 二、QT的环境搭建 1、QT SDK安装 2、环境变量的配置 前言 本系列为小编新开的一个系列&#xff0c;主要记录小编学习QT的过程&#xff0c;作为笔记仅供各位参考&#xff1b; 一、QT概述 Qt是一个跨平台C图形应用界面框架&#xff1b;简单来说&#x…

Android 13.0 SystemUI下拉状态栏定制二 锁屏页面横竖屏时钟都居中功能实现二

1.前言 在13.0的系统rom定制化开发中,在关于systemui的锁屏页面功能定制中,由于在平板横屏锁屏功能中,时钟显示的很大,并且是在左旁边居中显示的, 由于需要和竖屏显示一样,所以就需要用到小时钟显示,然后同样需要居中,所以就来分析下相关的源码,来实现具体的功能 如图…

C++:异常体系

异常体系 异常1.C语言传统的处理错误的方式2.C异常概念3.异常的使用3.1异常的抛出和捕获3.2 异常的重新抛出3.3异常安全3.4 异常规范 4.C标准库的异常体系5.异常的优缺点 异常 1.C语言传统的处理错误的方式 终止程序&#xff0c;如assert&#xff0c;缺陷&#xff1a;用户难以…

鸿蒙 ArkTs初识

前提&#xff1a;基于官网3.1/4.0文档。参考官网文档 基于Android开发体系来进行比较和思考。&#xff08;或有偏颇&#xff0c;自行斟酌&#xff09; 吐槽&#xff1a;官网上的案例只有代码和文档解释&#xff0c;没有可以直接运行查看效果的模拟器&#xff0c;这一点上&#…

Qt|大小端数据转换

后面打算写Qt关于网络编程的博客&#xff0c;网络编程就绕不开字节流数据传输&#xff0c;字节流数据的传输一般是根据协议来定义对应的报文该如何组包&#xff0c;那这就必然牵扯到了大端字节序和小端字节序的问题了。不清楚的大小端的可以看一下相关资料&#xff1a;大小端模…

【RTP】webrtc 学习3: webrtc对h264的rtp解包

rtp_rtcp\source\video_rtp_depacketizer_h264.cc【RTP】webrtc 学习2: webrtc对h264的rtp打包 中分析了打包过程的代码,这样再来看解析过程的源码就容易多了:本代码主要基于m79,m98类似。这里注明了jitterbuffer 会再次 做 解析stap-a 变为NAL units解析ParseFuaNalu 第一…

Jmeter 分布式测试

Jmeter单机进行压测&#xff0c;受到单台机器的性能影响&#xff0c;Jmeter支持分布式测试&#xff0c;用一个控制节点去控制多个工作节点去模拟更多的用户。 版本信息 内容版本号JDK1.8Jmeter5.6.2 分布式测试原理 jmeter 官网对分布式测试有说明&#xff0c;jmeter分布式…

RabbitMQ 死信交换机的详述➕应用

&#x1f973;&#x1f973;Welcome 的Huihuis Code World ! !&#x1f973;&#x1f973; 接下来看看由辉辉所写的关于RabbitMQ的相关操作吧 目录 &#x1f973;&#x1f973;Welcome 的Huihuis Code World ! !&#x1f973;&#x1f973; 一.什么是死信交换机 二. 死信队列…

说说你对vue的mixin的理解,有什么应用场景

mixin是什么 Vue中的mixin 局部混入全局混入注意事项: 使用场景源码分析Vue 的几种类型的合并策略 替换型合并型队列性叠加型小结 此文章&#xff0c;来源于印客学院的资料&#xff0c;这里只是分享&#xff0c;便于查漏补缺。 mixin是什么 Mixin 是 面向对象程序设计语言中…