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

参考

  • 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 MHzunsigned char i, j, k;_nop_();  // 空操作命令,确保编译器不会对后续的循环优化i = 4;j = 205;k = 187;// 外层循环的条件是 i != 0// 内层两层循环的条件分别是 j != 0 && k != 0do {do {while (--k);  // 循环耗时操作} while (--j);    // 嵌套循环耗时操作} while (--i);
}void main() {while(1) {P2 = 0xFE;     // 1111 1110Delay500ms();  // 单片机当中每次都是以 MHZ 速度运行,闪烁太快人眼看不出,因此要加延迟函数P2 = 0xFF;     // 1111 1111Delay500ms();}
}
  • 2-3 LED 流水灯(固定延时时间)
#include <REGX52.H>
#include <INTRINS.H>void Delay500ms() {  // @12.000 MHzunsigned 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 1110Delay500ms();P2 = 0xFD;  // 1111 1101Delay500ms();P2 = 0xFB;  // 1111 1011Delay500ms();P2 = 0xF7;  // 1111 0111Delay500ms();P2 = 0xEF;  // 1110 1111Delay500ms();P2 = 0xDF;  // 1101 1111Delay500ms();P2 = 0xBF;  // 1011 1111Delay500ms();P2 = 0x7F;  // 0111 1111Delay500ms();}
}
  • 2-4 LED 流水灯2(自定义延时时间)
#include <REGX52.H>void Delay1ms(unsigned int xms);  // @12.000MHzvoid main() {while (1) {P2 = 0xFE;  // 1111 1110Delay1ms(100);P2 = 0xFD;  // 1111 1101Delay1ms(100);P2 = 0xFB;  // 1111 1011Delay1ms(100);P2 = 0xF7;  // 1111 0111Delay1ms(100);P2 = 0xEF;  // 1110 1111Delay1ms(100);P2 = 0xDF;  // 1101 1111Delay1ms(100);P2 = 0xBF;  // 1011 1111Delay1ms(100);P2 = 0x7F;  // 0111 1111Delay1ms(100);}
}void Delay1ms(unsigned int xms)	{  // @12.000MHzunsigned 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 1111P2 = ~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;  // 全局变量定义默认为 0void 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 后变为 7LEDNum = 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 位置显示 3while (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) {       // 如果输入次数小于 4Password *= 10;  // 密码左移一位Password += KeyNum % 10;  // 获取一位密码Count++;    // 计次加一}LCD_ShowNum(2, 1, Password, 4);    // 更新显示}if (KeyNum == 11) {   // 如果 S11 按键按下,确认if (Password == 2345) {   // 如果密码等于正确密码LCD_ShowString(1, 14, "OK ");   // 显示 OKPassword = 0;    // 密码清零Count = 0;       // 计次清零LCD_ShowNum(2, 1, Password, 4);   // 更新显示} else {LCD_ShowString(1, 14, "ERR");  // 显示 ERRPassword = 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.mzph.cn/news/653331.shtml

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

相关文章

休息日的思考与额外题——链表

文章目录 前言链表知识点 一、 92. 反转链表 II二、21. 合并两个有序链表总结 前言 一个本硕双非的小菜鸡&#xff0c;备战24年秋招&#xff0c;计划二刷完卡子哥的刷题计划&#xff0c;加油&#xff01; 二刷决定精刷了&#xff0c;于是参加了卡子哥的刷题班&#xff0c;训练…

富文本编辑器CKEditor4简单使用-01

富文本编辑器CKEditor4简单使用-01 1. 快速体验入门1.1 通过从 CDN 加载 CKEditor 来快速体验1.2 从官方网站下载软件包1.2.1 官网下载1.2.2 解压、简单使用&#xff08;自带index页面示例&#xff09;1.2.3 将 CKEditor 4 添加到自己的页面1.2.3.1 目录结构1.2.3.2 效果1.2.3.…

TensorFlow2实战-系列教程6:迁移学习实战

&#x1f9e1;&#x1f49b;&#x1f49a;TensorFlow2实战-系列教程 总目录 有任何问题欢迎在下面留言 本篇文章的代码运行界面均在Jupyter Notebook中进行 本篇文章配套的代码资源已经上传 1、迁移学习 用已经训练好模型的权重参数当做自己任务的模型权重初始化一般全连接层需…

【机器学习】工程实践问题概述

机器学习实际应用时的工程问题与面临的挑战 一、实现细节问题 1.1 训练样本 训练样本与标注对各类机器学习算法和模型的精度影响 训练样本的选择对各类机器学习算法和模型的影响 训练样本的优化 如何进行数据增强&#xff1f; 如何进行数据清洗&#xff1f; 样本的标注对各类机…

数据结构(二)------单链表

制作不易&#xff0c;三连支持一下呗&#xff01;&#xff01;&#xff01; 文章目录 前言一.什么是链表二.链表的分类三.单链表的实现总结 前言 上一节&#xff0c;我们介绍了顺序表的实现与一些经典算法。 但是顺序表这个数据结构依然有不少缺陷&#xff1a; 1.顺序表指定…

导航页配置服务Dashy本地部署并实现公网远程访问

文章目录 简介1. 安装Dashy2. 安装cpolar3.配置公网访问地址4. 固定域名访问 简介 Dashy 是一个开源的自托管的导航页配置服务&#xff0c;具有易于使用的可视化编辑器、状态检查、小工具和主题等功能。你可以将自己常用的一些网站聚合起来放在一起&#xff0c;形成自己的导航…

基于springboot宠物领养系统

摘要 随着社会的不断发展和人们生活水平的提高&#xff0c;宠物在家庭中的地位逐渐上升&#xff0c;宠物领养成为一种流行的社会现象。为了更好地管理和促进宠物领养的过程&#xff0c;本文基于Spring Boot框架设计和实现了一套宠物领养系统。该系统以用户友好的界面为特点&…

时序分析中的去趋势化方法

时序分析中的去趋势化方法 时序分析是研究随时间变化的数据模式的一门学科。在时序数据中&#xff0c;趋势是一种随着时间推移而呈现的长期变化趋势&#xff0c;去趋势化是为了消除或减弱这种趋势&#xff0c;使数据更具平稳性。本文将简单介绍时序分析中常用的去趋势化方法&a…

跟着cherno手搓游戏引擎【13】着色器(shader)

创建着色器类&#xff1a; shader.h:初始化、绑定和解绑方法&#xff1a; #pragma once #include <string> namespace YOTO {class Shader {public:Shader(const std::string& vertexSrc, const std::string& fragmentSrc);~Shader();void Bind()const;void Un…

怎样自行搭建幻兽帕鲁游戏联机服务器?

幻兽帕鲁是一款深受玩家喜爱的多人在线游戏&#xff0c;为了获取更好的游戏体验&#xff0c;许多玩家希望能够自行搭建幻兽帕鲁游戏联机服务器&#xff0c;本文将指导大家如何自行搭建幻兽帕鲁游戏联机服务器。 自行搭建幻兽帕鲁游戏联机服务器&#xff0c;阿里云是一个不错的选…

结构体的增删查改

结构体&#xff0c;是为了解决生活中的一些不方便利用c语言自带数据类型来表示的问题。例如表示一个学生&#xff0c;那么学生这个个体假如用c语言自带数据类型怎么表示呢。可以使用名字&#xff0c;也就是字符数组&#xff1b;也可以使用学号&#xff0c;也就是int类型。但是这…

iOS 面试 Swift基础题

一、Swift 存储属性和计算属性比较&#xff1a; 存储型属性:用于存储一个常量或者变量 计算型属性: 计算性属性不直接存储值,而是用 get / set 来取值 和 赋值,可以操作其他属性的变化. 计算属性可以用于类、结构体和枚举&#xff0c;存储属性只能用于类和结构体。存储属性可…

检测头篇 | 原创自研 | YOLOv8 更换 SEResNeXtBottleneck 头 | 附详细结构图

左图:ResNet 的一个模块。右图:复杂度大致相同的 ResNeXt 模块,基数(cardinality)为32。图中的一层表示为(输入通道数,滤波器大小,输出通道数)。 1. 思路 ResNeXt是微软研究院在2017年发表的成果。它的设计灵感来自于经典的ResNet模型,但ResNeXt有个特别之处:它采用…

MySQL-窗口函数 简单易懂

窗口函数 考查知识点&#xff1a; • 如何用窗口函数解决排名问题、Top N问题、前百分之N问题、累计问题、每组内比较问题、连续问题。 什么是窗口函数 窗口函数也叫作OLAP&#xff08;Online Analytical Processing&#xff0c;联机分析处理&#xff09;函数&#xff0c;可…

Android 基础技术——列表卡顿问题如何分析解决

笔者希望做一个系列&#xff0c;整理 Android 基础技术&#xff0c;本章是关于列表卡顿问题如何分析解决 onBindViewHolder 优化 是否有耗时操作、重复创建对象、设置监听器、findViewByID、局部的动画对象等操作 是否存在内存泄漏 发生内存泄露&#xff0c;会导致一些不再使用…

游戏开发丨基于Tkinter的扫雷小游戏

文章目录 写在前面扫雷小游戏需求分析程序设计程序分析运行结果系列文章写在后面 写在前面 本期内容 基于tkinter的扫雷小游戏 所需环境 pythonpycharm或anaconda 下载地址 https://download.csdn.net/download/m0_68111267/88790713 扫雷小游戏 扫雷是一款广为人知的单…

RabbitMQ“延时队列“

1.RabbitMQ"延时队列" 延迟队列存储的对象是对应的延迟消息&#xff0c;所谓“延迟消息”是指当消息被发送以后&#xff0c;并不想让消费者立刻拿到消息&#xff0c;而是等待特定时间后&#xff0c;消费者才能拿到这个消息进行消费 注意RabbitMQ并没有延时队列慨念,…

OpenCV-29 自适应阈值二值化

一、引入 在前面的部分我们使用的是全局阈值&#xff0c;整幅图像采用同一个数作为阈值。当时这种方法并不适应于所有情况。尤其是当同一幅图像上的不同部分具有不同的亮度时。这种情况下我们需要采用自适应阈值。此时的阈值时根据图像上的每一个小区域计算与其对应的阈值。因此…

【幻兽帕鲁】开服务器,高性能高带宽(100mbps),免费!!!【学生党强推】

【幻兽帕鲁】开服务器&#xff0c;高性能高带宽&#xff08;100mbps&#xff09;&#xff0c;免费&#xff01;&#xff01;&#xff01;【学生党强推】 教程相关视频地址&#xff1a;https://www.bilibili.com/video/BV16e411Y7Fd/ 目前幻兽帕鲁开服务器有以下几套比较性价比的…

研发日记,Matlab/Simulink避坑指南(九)——可变数组应用Bug

文章目录 前言 背景介绍 问题描述 分析排查 解决方案 总结归纳 前言 见《研发日记&#xff0c;Matlab/Simulink避坑指南(四)——transpose()转置函数Bug》 见《研发日记&#xff0c;Matlab/Simulink避坑指南(五)——CAN解包 DLC Bug》 见《研发日记&#xff0c;Matlab/Si…