第1章 基础知识概述

1.1 单片机概述

1.1.1 基本概念

现代微型计算机技术有两大分支

  • 通用微型计算机系统(Universal microcomputer System,UCS),也成为个人计算机(PC)
    • 主要技术目标:追求高速计算和海量存储能力
    • 发展方向:CPU速度不断提升和存储容量不断扩大
  • 嵌入式计算机系统(Embedded Computer System,ECS)
    • 主要技术目标:满足被控对象体系的物理、电气和环境及产品成本等要求
    • 发展方向:与对象系统密切相关的嵌入性能、控制能力和控制可靠性

通过对PC进行电气加固、机械加固,并配置各种接口办卡,可以让其成为能嵌入大型被控对象系统中的工控机(Industrial Personal Computer,IPC),进而实现工业过程自动化和智能化。

单片微型计算机(Single Chip Microcomputer,SCM),简称单片机,是一类特殊的嵌入式计算机系统,它能嵌入小型被控对象(家用电器、医疗仪器、汽车电器等)之中,成为智能化的核心。

单片机是一种集成电路芯片,采用超大规模集成电路技术将中央处理器(CPU)、程序存储器(ROM)、数据存储器(RAM)、并行输入输出(I/O)口、串行口、中断系统、定时/计数器(T/C)、总线控制器、片内时钟等电路,集成到一块硅片上构成一个微型计算机系统

1.1.2 应用领域

单片机的特点:体积小、可靠性高、性价比高

  • 工业自动化控制系统

    • 数据采集系统、通信系统、信号检测系统、无限感知系统、测控系统、智能化管理系统、电梯智能化控制系统、各种报警系统、与计算机联网构成的控制系统、机电一体化控制系统等等
  • 智能仪器仪表

    • 电压、功率、频率、温湿度、流量、速度、厚度、角度、长度、硬度、压力等物理量
  • 通信设备

    • 多机网络中各种计算机之间的通信、计算机与外围设备(键盘、打印机等)之间的协作
  • 汽车电子与航空航天电子系统

    • 发动机电子控制器、灯光控制系统、ABS防抱死系统、电子制动系统、胎压检测系统等。
  • 家用电器

微控制技术:以软件取代硬件并能提高系统性能的控制技术

1.1.3 主流单片机及其特点

按照不同分类方法,单片机具有不同产品类型

  • 字长:4位、8位、16位、32位等
  • 指令:精简指令集、复杂指令集
  • 内核:51系列、PIC系列、AVR系列、ARM系列等
  1. 51系列

    Intel公司早年推出的MCS-51单片机包括众多型号,后来通过专利转让技术或技术交换将51内核技术授予了Atmel、Philips、Cygnal、Adi、Winbond等公司,它们连同MCS-51单片机一起被成为51系列单片机,简称51单片机。

    • Atmel公司的AT89C51、AT89C52、AT89C2051等
    • Winbond公司的78C52、77E58等
    • 宏晶公司的STC15W201S、STC15W401AS等
    • Cygnal公司的C8051F020、C8051F340等
  2. PIC系列

    PIC单片机是美国Microchip(微芯)公司的产品,最大特点是重视产品的性能与性价比

  3. AVR系列

    AVR单片机是97年Atmel公司,在公司的Flash新技术基础上,研发的RISC(精简指令集)型高性能单片机

  4. ARM系列

    ARM(Acorn RISC Machine)单片机是英国Acorn公司设计的低功耗、低成本RISC型微处理器。具有三大特点:耗电少且功能强、具有16位/32位双指令集、拥有众多合作伙伴

1.1.4 发展趋势

  • 单片机的发展趋势
  1. CMOS化

    CMOS(Complementary Metal-Oxide-Semiconductor,互补金属-氧化物-半导体)是制造大规模集成电路芯片采用的一种工艺。优点是:动态功耗低、工作压力范围宽、抗干扰能力强、温度稳定性能好等

  2. 低功耗化

    单片机的功耗电流已降到毫安(mA)甚至微安(μA)级,供电电压在3~6V之间。低功耗化能给产品带来高可靠性、高抗干扰能力及产品的便携性

  3. 低电压化

  4. 高性能化

    采用RISC解构和流水线技术,目前指令速度已超过100MIPS(Million Instruction Per Seconds,百万条指令每秒),比普通单片机高出10倍以上;加强了位处理能力、中断和定时控制功能,实时响应能力大幅提高

  5. 大容量化

    目前,单片机片内ROM已达64KB,片内RAM也达2KB

  • 单片机的发展阶段
  1. SCM(Single Chip Microcomputer,单片微型计算机)阶段

    主要技术发展方向:寻求最佳单片形态的嵌入式系统体系结构

  2. MCU(Micro Controller Unit,微控制器)阶段

    主要技术发展方向:在SCM的基础上不仅提升了速度、功耗等基础性能,而且集成了许多外围电路,外围电路内装化,大幅提高了嵌入式系统的可靠性

  3. SoC(System on Chip,片上芯片)阶段

    将单片微控制器延伸到单片应用系统:在单片机内核基础上集成了嵌入式系统所需的主要功能模块

1.1.5 安装Proteus

8.13版本:单片机仿真软件Proteus8.13安装破解汉化教程_哔哩哔哩_bilibili

8.16版本:【配音+字幕】Proteus8.16安装破解汉化教程 详细靠谱(附下载链接)_哔哩哔哩_bilibili

1.2 基本知识

1.2.1 有符号数

0代表符号“+”,1代表符号“-”;有符号数具有原码、反码和补码3中表示法

  1. 原码

    最高位为符号位,其余位为数值部分。8位二进制原码表示范围为1111 1111B0111 1111B(-127+127)。

    其中,0000 0000B与1000 0000B的数值部分相同但符号相反,分别表+0和-0

  2. 反码

    正数的反码与原码相同;负数的反码为:符号位不变,原码数值部分按位取反

  3. 补码

    正数的补码与原码相同:负数的补码为:符号位不变,原码数值部分按位取反,末尾加1

1.2.2 位、字节和字

  1. 位(bit)

    计算机内部数据存储的最小单位

  2. 字节(Byte)

    计算机数据处理的基本单位

  3. 字(Word)

    计算机一次存取、加工和传送的数据长度。MCS-51单片机的字由单字节组成,字长为8

第2章 MCS-51的结构组成

2.1 单片机的基本结构

2.1.1 MCS-51的内部结构

MCS-51单片机内部结构
  • 1个8位中央处理器(CPU)
  • 1个片内振荡器和时钟电路
  • 4KB片内程序存储器(ROM)
  • 256字节的片内数据存储器(RAM)
  • 2个16位定时/计数器(T/C)
  • 可寻址64KB程序存储空间和64KB数据存储中间的总线控制器
  • 4个8位双向并行I/O口
  • 1个全双工串行口
  • 5个中断源

​ CPU的主要功能是产生各种控制器信号,控制存储器、I/O口的数据传送,进行数据运算、逻辑运算等。从功能上可分为控制器运算器两部分

1. 控制器

​ 控制器的作用是对取自程序存储器(ROM)中的指令进行译码,在规定的时刻发出各种操作所需的控制信号,完成执行所规定的功能。其由程序计数器(PC)指令寄存器(IR)指令译码器(ID)数据指针(DPTR)及定时控制与条件转移逻辑电路等组成

  1. 程序计数器(Program Counter,PC)

    PC是个16位的专用寄存器,存放着下一条要执行指令的首地址,即PC的内容决定着程序的运行轨迹。当CPU取出指令后,PC的内容可以自动加1。系统复位后,PC的内容自动赋为0000H。

  2. 指令寄存器(Instruction Register,IR)

    IR是个8位寄存器,用于暂存待执行的指令,等待译码。

  3. 指令译码器(Instruction Decoder,ID)

    对IR中的指令进行译码,将指令转变成执行此指令所需的电信号

  4. 数据指针(Data Pointer,DPTR)

    DPTR是个16位的专用地址指令寄存器,由两个8位寄存器DPH(高8位)和DPL(低8位)拼装而成。既可以作为一个16位存储器,也可以作为两个独立的8位存储器使用。

2. 运算器

​ 运算器由算术逻辑部件(ALU)累加器(ACC)程序状态字寄存器(PSW)及运算调整电路等组成。为了提高数据处理速度,片内还增加了一个通用寄存器B和一些专用寄存器与位处理逻辑电路。

  1. 累加器(Accumulator,ACC)

    ACC是个8位寄存器,简称为A,通过暂存器与ALU相连,用来存放一个操作数或中间结果。

  2. 通用寄存器B(General Purpose Register)

    通用寄存器B是为了配合ACC进行乘法和除法运算而设置的,也是一个8位寄存器。

  3. 算术逻辑部件(Arithmetic Logic Unit,ALU)

    ALU由加法器和其他逻辑电路组成,用于对数据进行四则运算和逻辑运算等。ALU的两个操作数,一个由暂存器2输入,一个由暂存器1输入,运算结果的状态传送给PSW。

  4. 程序状态字寄存器(Program State Word,PSW)

    PSW是个8位专用寄存器,用于存放程序运行过程中的各种状态信息。

2.1.2 MCS-51外部引脚及其功能

采用HMOS制造工艺的51单片机一般采用40个引脚的双列直插封装(DIP)方式

双列直插

采用CHMOS制造工艺的MCS-51单片机采用DIP封装方式外,还采用44个引脚的方形封装方式,其4个引脚是无用的。

方形封装

80C51采用40个引脚的DIP方式,引脚分布如下所示

80C51引脚图

80C51的40个引脚按功能划分,可分为以下3类

  • 电源及晶振引脚(4个)——VCC、VPP、XTAL1、XTAL2
  • 控制引脚(4个)——PSEN、ALE/PROGEA/VPP、RST/VPD
  • 并行I/O口引脚(32个)——P0.0-P0.7、P1.0-P1.7、P2.0-P2.7、P3.0-P3.7
  1. 电源及晶振引脚

    • VCC(40脚):+5V电源引脚
    • VPP(20脚):接地引脚
    • XTAL1、XTAL2(19和18脚):外接晶振
  2. 控制引脚

    • RST/VPD(9脚),复位/备用电源引脚

      • RST:单片机上电后,其内部各寄存器处于随机状态,若在该引脚上输入满足时间要求的高电平,将使单片机复位
      • VPD:在主电源掉电期间,可利用该引脚外接+5V备用电源为单片机片内RAM供电,保证片内RAM信息不丢失
    • ALE/PROG(30脚),地址锁存使能输出/编程脉冲输入引脚

      • ALE:当单片机访问外部存储器时,外部存储器的16位地址信号由P0口输出低8位,P2口输出高8位,ALE可以作低8位地址锁存控制信号。当不用作外部存储器地址地址锁存信号时,该引脚仍以时钟脉冲频率的1/6固定输出正脉冲
      • PROG:对含有EPROM的单片机,在进行片内EPROM编程时,需要由此输入编程脉冲
    • PSEN(29脚),输出访问片外程序存储器读选通信号引脚

      CPU在片外ROM取指令期间,该引脚将在每个机器周期内产生两次负跳变脉冲,用作片外ROM芯片的使能信号

    • EA/VPP(31脚),外部ROM允许访问/编程电源输入引脚

      • EA:当EA=1或悬空时,CPU从片内ROM开始读取指令。当程序计数器PC的值超过4KB地址范围时,将自动转向片外ROM的指令。当EA=0或接地时,CPU仅访问片外ROM
      • VPP:在对含有EPROM的单片机进行编程时,此引脚应接+12V编程电压
  3. 并行I/O口引脚

    P0~P3口都可以作为通用输入/输出(I/O)口的使用。P0和P2还具有其他单片机地址/数据总线的作用,P3口还具有第二功能的作用

2.2 单片机的存储器结构

2.2.1 存储器划分方法

计算机存储器地址空间有两种结构形式:普林斯顿结构和哈佛结构

计算机存储器地址的两种结构形式
  • 普林斯顿结构:也称冯·诺依曼结构,将ROM和RAM统一编址。X86、奔腾、ARM7等微处理器采用这种结构
  • 哈佛结构:ROM和RAM单独编址,ROM和RAM位于不同的存储空间。哈佛结构有利于缓解程序运行时的访问瓶颈问题。51系列、AVR系列、Z8系列等微处理器采用此结构
MCS-51存储器空间结构

​ 从物理地址上看,MCS-51单片机有4个存储空间,片内ROM、片外ROM、片内RAM、片外RAM。由于片内ROM和片外ROM是统一编址的,从逻辑地址上看,只有3个空间。

​ C51语言访问片内RAM、片外RAM和ROM分别对应dataxdatacode 。片内ROM地址空间为0000H-0FFFH(共4KB),片外ROM地址空间为0000H-FFFFH(共64KB);片内RAM地址空间为00H-FFH(共256B),片外RAM地址空间为0000H-FFFFH(共64KB)。

2.2.2 程序存储器

​ 程序存储器(ROM)主要用于存放程序代码程序中用到的常数。程序调试成功后,编程器将程序代码写入到ROM中。由于其只读性,程序或数据不会掉电丢失(掉电保护)

​ 根据EA引脚的不同电位,可对ROM的低4KB地址进行选择

使用两种ROM时的地址分配

​ 当EA引脚接高电平时(A端),4KB以内的地址在片内ROM中,大于4KB的地址在片外ROM中;当EA引脚接低电平时(B端),片内ROM被禁用,全部64KB地址都在片外ROM中。

​ 在80C51的ROM中,有6个特殊地址单元是专为复位和中断功能设计的

  • 0000H:程序首地址,单片机复位后从此单元开始运行程序。一般在该单元存放一条跳转指令跳转到用户设计的主程序
  • 0003H:外部中断0入口地址
  • 000BH:定时/计数器0溢出中断入口地址
  • 0013H:外部中断1入口地址
  • 001BH:定时/计数器1溢出中断入口地址
  • 0023H:串行口中断入口地址

2.2.3 数据存储器

​ 数据存储器(RAM)存放运行中间结果标志位待调试的程序等。数据掉电丢失

​ RAM在物理上和逻辑上都占有两个地址空间:一个是片内256B的RAM,另一个是片外最大可扩充64KB的RAM。80C51片内RAM配置如图

80C51片内RAM配置

​ 片内RAM可分为低128B(左图)、高128B两部分,其中,低128B为普通RAM,地址为00H-7FH;高128B为特殊功能寄存器,地址为80H-FFH,其中仅21字节是有定义的。

  • 低128B RAM区

    1. 00H~1FH

      32个数据存储单元可作为工作寄存器使用,分为4组,每组8个单元,按序命名为工作寄存器R0~R7。在任意时刻CPU只能选用一组工作寄存器作为当前工作寄存器组,未选中的3组可作为一般数据存储器。当前工作寄存器组通过PSW中的RS1和RS0标志位进行设置,CPU复位后默认使用第0组

      工作寄存器的地址分配表
    2. 20H~2FH

      16个字节单元,既可以像普通RAM单元一样按字节地址进行存取,又可以按位进行存取

      字节寻址与位寻址
    3. 30H~7FH

      80个字节单元为用户RAM区,只能按字节存取。用户可以设置堆栈区存储中间数据

  • 高128B RAM区

    80H~FFH的区域中,离散地分布有21个特殊功能寄存器(特殊功能寄存器区)。空闲单元为后来功能增加预留空间。字节地址能被8整除的特殊功能寄存器还有位地址

SFR的名称、符号和地址分布

2.3 单片机的复位与时序

2.3.1 单片机的复位

单片机在开机时需要复位,以便使CPU及其他功能部件处于一个确定的初始状态。

在单片机工作过程中,如果出现死机现象,也应当使其复位,摆脱死机状态。

复位时片内各寄存器的初始值

单片机复位的产生条件是:在RST引脚出现满足复位时间要求的高电平状态,该时间等于系统时钟振荡周期建立时间再加上2个机器周期时间(一般不小于10ms)

单片机复位的产生方式有两种:上电复位方式、按键复位方式

  1. 上电复位:利用阻容充电电路实现,在单片机上电的瞬间,RST引脚的电位与VCC相同。随着充电电流减小,RST引脚的电位将逐渐下降。选择合适的电容C1和电阻R1,使其RC时间常数大于复位时间即可保证上电复位发生。

    RC时间常数是描述电阻-电容(RC)电路充放电过程的一个重要参数。它反映了电容电压变化到稳态值的63.2%所需的时间。在单片机的复位电路中,RC时间常数决定了复位持续的时间。

    RC时间常数的定义为:

    1
    τ = R × C

    其中,τ是时间常数,单位为秒(s);R是电阻值,单位为欧姆(Ω);C是电容值,单位为法拉(F)。

    在RC电路的充电过程中:

    • 当时间t=τ时,电容上的电压Vc达到稳态值的63.2%;
    • 当时间t=2τ时,电容上的电压Vc达到稳态值的86.5%;
    • 当时间t=3τ时,电容上的电压Vc达到稳态值的95.0%;
    • 当时间t=5τ时,电容上的电压Vc达到稳态值的99.3%,可以认为已经充满。

    在单片机复位电路中,我们通常希望复位信号在上电后能够持续一段时间,以确保单片机内部电路完全稳定。这个持续时间就是由RC时间常数决定的。

    例如,如果复位电路中R=8.2kΩ,C=10µF,则时间常数τ=8.2kΩ×10µF=82ms。这意味着:

    • 上电82ms后,RST引脚电压达到VCC的63.2%;
    • 上电164ms后,RST引脚电压达到VCC的86.5%;
    • 上电246ms后,RST引脚电压达到VCC的95.0%;
    • 上电410ms后,RST引脚电压达到VCC的99.3%,复位结束。

    在实际设计中,我们通常取5τ作为复位持续的时间,以确保复位的可靠性。同时,还需要考虑电阻和电容的精度、温度特性等因素,以保证复位电路在各种工作条件下都能正常工作。

  2. 按键复位:利用电阻分压电路实现,当按键压下时,串联电阻R2上的分压可以使RST引脚产生高电平,抬起时产生低电平。只要按键动作产生的复位脉冲宽度大于复位时间,即可保证复位的发生。

实际应用中,常采用上述两种方式整合在一起的复合复位做法

2.3.2 单片机的时序

  1. 时钟方式

    单片机执行指令的过程可分为取指令分析指令执行指令3个步骤,每个步骤又由许多微操作组成,这些微操作必须在一个统一的时钟控制下才能按照正确的顺序执行。

    单片机的时钟信号可以由两种方式产生:内部时钟方式、外部时钟方式

    • 内部时钟方式(a):利用单片机芯片内部的振荡电路实现的,通过XTAL1和XTAL2引脚外接一个用晶体振荡器和电容组成的并联谐振回路。C1和C2一般取30pF左右,主要作用是帮助振荡器起振,晶体的振荡频率范围位1.2-13MHz。晶体振荡频率越高,系统的时钟频率越高,单片机运行速度也就越快

    • 外部时钟方式(b):在多片单片机组成的系统中,采用公用外部时钟信号使各个单片机之间的时钟信号严格同步。外部脉冲信号经XTAL2引脚注入。

    时钟引脚的接线方式
  2. 时序的概念

    时序是指按照时间顺序显示的对象(或引脚、事件、信息)序列关系,最常用的表示方式为时序图。

    时序图也成为波形图或序列图,有两个坐标轴:横轴表示时间,纵轴表示不同对象的电平。

    典型操作时序图

    从图中可以看出:

    • 最左边是引脚标识,该图反应了RS、R/W、E、D0-D7引脚间的时序关系
    • 交叉线部分表示电平的变化,高电平和低电平
    • D0-D7的封闭菱形部分表示数据的有效范围(Valid Data)
    • 双箭头线段表示两个状态间的持续时间,用字母t加下标字符以示区别

    时序使用定时单位来描述的,MCS-51单片机的时序单位有4种:时钟周期、状态周期、机器周期和指令周期。

    MCS-51各个周期之间的关系
    • 时钟周期:晶振或外加振荡源的振荡周期称为时钟周期,用P表示。时钟周期是单片机中最小的时序单位1个时钟周期等于产生该时钟的晶振频率的倒数
    • 状态周期:1个状态周期等于2个时钟周期,既由P1和P2组成,用S表示
    • 机器周期:1个机器周期等于6个状态周期(12个时钟周期)
    • 指令周期:执行一条执行所需要的时间称为指令周期。1个指令周期由1-4个机器周期组成,指令周期是单片机中最大的时序单位
  3. 时序逻辑电路

    单片机时序就是CPU在执行指令时所需的控制信号的时间顺序,CPU实质上是一个复杂的同步时序电路,在时钟信号的推动下工作。

    从用途看,CPU发出的时序信号可分为两类:

    • 片内各功能部件的控制(不必了解)
    • 片外存储器或I/O口的控制:需要通过单片机的控制引脚送到片外单元,本节仅介绍D触发器。

    D触发器又称边沿D触发器(或维持-阻塞边沿D触发器),可分为正边沿和负边沿D触发器两类。

    • 正边沿D触发器

      原理图如下所示,(a)为正边沿D触发器的电路符号,包括输入端D、时钟端CLK、输出端Q和输出端Q。(b)为时序波形图

      正边沿D触发器

      正边沿D触发器只在时钟脉冲CLK正边沿到来的时刻,才采样D端的输入信号,并据此立即改变Q和Q端的输出状态。而在其他时刻,D与Q是信号隔离的。

    • 负边沿D触发器

      原理和正边沿D触发器类似,只不过在时钟脉冲CLK负边沿到来时刻,才采样D端的输入信号

      负边沿D触发器

2.4 单片机并行I/O口

2.4.1 P1口

P1口由P1.0~P1.7共8个接口电路组成,下图是其中一位P1.n的结构原理图

P1.n的结构原理图
  1. P1口中的8个D触发器构成了可存储8位二进制码的P1口锁存器(特殊功能寄存器),字节地址为90H
  2. 场效应管V与上拉电阻R组成输出驱动器,增大P1口带负载能力
  3. 三态门1和2在输入和输出时作为缓冲器作用

P1口作为通用I/O口使用,具有输出、读引脚、读锁存器3种工作方式。

  • 输出方式

    单片机执行向P1口写数据指令时,如P1=0x2b。根据D触发器原理,由内部总线送到锁存器D端的数据,在“写锁存器”的除法信号(CLK)控制下传送给Q端,Q=D。触发脉冲结束后,Q端不再跟随D端变化但仍保持先前数据不变(锁存功能)。此后会有两种结果:

    • Q=1→Q=0→场效应管V因栅极的0电平而截止→漏极电平=VCC→引脚P1.n=1
    • Q=0→Q=1→场效应管V因栅极的1电平而导通→漏极相当于对地短路→引脚P1.n=0

    内部总线电平可以由P1口输出,也可以锁存在P1口中。

  • 读引脚方式

    单片机执行从P1口读数据并存到变量val指令时,如val=P1。此时引脚P1.n电平在“读引脚”控制下经过三态门1后到达内部总线(val中),此时会有两种结果:

    • P1.n的值为0→val=0(与场效应管V无关)
    • P1.n的值为1且场效应管V处于导通状态(如曾执行过写0指令)→val=0(出现错误)

    上述第二种结果发生是因为导通的V会使P1.n的电平钳制为0。为避免这一情况,读引脚前需执行一条写1指令强迫V截止。P1口作为输出口是无条件的,但作为输入口是有条件的,所以P1被称为准双向口

  • 读锁存器方式

    单片机执行“读—改—写”类的指令时,如P1++。在”读锁存器“控制下,Q端电平经过三态门2读入内部总线→在运算器中进行+1运算→结果重新写到Q端(同时也输出到P1.n引脚)

读锁存器和读引脚的效果是不同的,前者是为了获得前次的锁存值,后者则是为了获得引脚上(外部数据)上的当前值。

P1口能驱动4个LS TTL负载。通常将100μA的电流定义为一个LS TTL负载的电流。P1口内已有内部上大电阻,无需外接。

在单片机的背景下,LS TTL负载具有不同的含义。

LS是指低功耗斯密特触发器(Low-power Schmmitt Trigger)的缩写。TTL则是指晶体管-晶体管逻辑(Transistor-Transistor Logic)电路的一种常见逻辑家族。

因此,在单片机中LS TTL负载指的是:

  1. LS - 低功耗斯密特触发器,是一种具有较宽噪声裕量、低功耗特性的数字逻辑电路。

  2. TTL - 指传统的晶体管-晶体管逻辑电路家族,工作电压通常为5V。

  3. 负载 - 指单片机的I/O口外连接的电路负载,如LED灯、继电器等。

当单片机需要驱动一些较大的电流负载时,像LED等,由于单片机I/O口驱动能力有限,通常需要外加放大电路。这时可以使用LS TTL器件作为放大驱动电路,它能够将单片机I/O口的低电流信号放大为能够驱动较大负载的电流。

总之,在单片机领域,LS TTL负载是指使用低功耗斯密特触发器TTL逻辑电路,作为外部负载的驱动放大器,以驱动一些较大的电流负载。这是由于单片机本身I/O口驱动能力有限的原因。

2.4.2 P3口

下图为P3口其中一位P3.n的结构原理图

P3.n的结构原理图

8个D触发器构成了P3口锁存器(特殊功能寄存器),字节地址为B0H。与P1口相比,P3口结构中多了与非门B缓冲器T。P3口除具有通过I/O口功能外,还具有第二功能。

  • ”第二输出功能“端保持“1”状态时,与非门B对锁存器Q端是畅通的,此时P3口工作在通用I/O口方式,即P3.n具有与P1.n相同的3个基本功能,仍为准双向口。

    Q=1→B输出0→V截止→P3.n=1

    Q=0→B输出1→V导通→P3.n=0

  • 锁存器Q端保持“1”状态时,与非门B对“第二输出功能”端保持一致,即“第二输出功能“端分别为1和0时,P3.n可分别输出1和0。

    ”第二输入功能“端的输入值与经由缓冲器T的P3.n引脚电平保持一致,即P3.n分别为1和0时,”第二输入功能“端也分别为1和0

P3口的第二功能定义如下表

P3口第二功能定义

2.4.3 P0口

下图为P0口其中一位P0.n的结构原理图

P0.n结构原理图

8个D触发器构成了P0口锁存器(特殊功能寄存器),字节地址为80H。P0口的输出驱动电路上拉场效应管V2驱动场效应管V1组成;控制电路包括1个与门A、1个非门X和一个多路开关MUX,其余组成与P1口相同。

P0口既可以作为通用的I/O口,也可以作为单片机系统的地址/数据线使用。在CPU的控制信号作用下,多路开关MUX可以分别接通锁存器输出地址/数据输出

  • P0口作为通用I/O口使用时

    ”控制“端保持”0“电平→封锁与门A(恒定输出0)→上拉场效应管V2截止→V1漏极开路(等效电路如下图);

    ”控制“端为0→多路开关MUX连接Q端。此时P0口与P1口同样有3中工作方式,准双向通用I/O口,但由于V1漏极开路,要使D端正常输出”1“,必须外接上拉电阻(100Ω~10kΩ)

    外接上拉电阻后的等效电路图
  • P0口连接外部存储器时

    ”控制“端保持”1“电平→打开与门A(控制权交给”地址/数据“端)→V2导通

    ”控制“端为1→多路开关MUX连接非门X。此时P0口工作在地址/数据分时复用方式

    • CPU需要输出地址和数据信息时:”地址/数据“端电平会与引脚P0.n电平保持一致,实现了地址或数据输出到P0.n功能。

      “地址/数据”为1→与门A输出”1“→V2导通→P0.n=1

      “地址/数据”为0→与门A输出“0”→V2封闭,同时非门X输出1→V1导通→P0.n=0

    • CPU需要输入P0.n的数据时:CPU会自动使“地址/数据”端切换为“1”电平,以使V1截止,确保P0.n的数据不会被误读。因而地址/数据分时复用方式时的P0口是真正的双向口。

P0口能以吸收电流的方式驱动8个LS TTL负载,即灌电流不大于800μA。

2.4.4 P2口

下图为P2口其中一位P2.n的结构原理图

P2.n结构原理图

8个D触发器构成了P2口锁存器(特殊功能寄存器),字节地址为A0H。与P1口相比,P2口中多了一个多路开关MUX,实现通用I/O口地址输出两种功能。

  • P2口用作通用I/O口时,在“控制”端作用下,MUX转向锁存器Q端(向下),构成一个准双向口,具备输出、读引脚和读锁存器功能

  • 单片机访问片外RAM或片外ROM指令时,PC(程序计数器)或DPTR(数据指针)的高8位地址需由P2.n引脚输出。此时,MUX在CPU的控制下转向”地址“端(向上),”地址“端信号通过P2.n输出。

    ”地址“端为1→非门X输出”0“→V封闭→P2.n=1

    ”地址“端为0→非门X输出”1“→V导通→P2.n=0

P2口的负载能力和P1口相同,驱动4个LS TTL负载。

P0~P3口都可以作为准双向通用I/O口提供给用户,其中P1~P3无需外接上拉电阻;在需要扩展片外存储器时,P2口可作为其地址线接口,P0口可作为其地址/数据线复用接口,此时P0口是真正的双向口

2.5 绘制Proteus原理图

第3章 C51编程语言基础

51单片机有两种常用的编程语言:A51汇编语言C51语言

  • A51汇编语言

    是一种面向机器的编程语言,能直接操作51单片机的硬件资源,优点是指令效率高、执行速度快。但A51汇编语言属于低级编程语言,程序可读性差,编程工作量大。

  • C51语言

    51单片机设计的一种高级编程语言,属于标准C语言的一个子集,优点是可读性强、易于调试维护、编程工作量小。

3.1 编译工具

3.1.1 源程序开发过程

C51采用的是编译型执行方式(源程序全部转换为目标代码后再执行)的源程序开发,过程如下图所示。

C51源程序的开发过程

图中,方框表示开发环节,圆框表示参与开发过程的程序文件。

3.1.2 Source Code工作界面

编程界面

3.1.3 Source Code基本用法

3.2 基本执行语句

3.2.1 表达式语句

3.2.2 选择语句

3.2.3 循环语句

3.2.4 注释语句

3.3 变量

3.3.1 变量概述

3.3.2 变量名

3.3.3 数据类型

  1. 标准C语言传承的数据类型

  2. C51特有的数据类型

    • bit类型

      51单片机中由许多按位(bit)进行读/写操作的存储单元,如片内低128B RAM中字节地址为20H~2FH的区间,可存放0x00~0x7f共128个位存储单元。每个位存储单元的值只能是0或1。

    • sfr类型

      80C51的高128B RAM中有21个特殊功能寄存器(SFR),除DPTR为16位寄存器外,其余都是8位寄存器,每个SFR都有特定的字节地址,部分SFR中还有独立的位地址(详见2.2.3节),C51采用两种专属的变量类型说明符,sfr(sfr16)和sbit

      1
      2
      3
      sfr P1 = 0x90;		// 指定变量P1为sfr类型,对应地址为0x90
      sfr PSW = 0xd0;
      sfr16 DPTR = 0x82;
    • sbit类型

      sbit用于定义SFR中具有位地址变量的类型说明符,有以下3种不同用法

      1
      2
      3
      1. sbit 位变量名=位地址
      2. sbit 位变量名=可位寻址的SFR字节地址^相对位置
      3. sbit 位变量名=可位寻址变量^相对位置

      以下以定义变量CY(PSW中的进位标志位)说明3中用法

      1
      2
      3
      sbit CY = 0xd7;		// 已有明确的位地址
      sbit CY = 0xd0^7; // PSW的字节地址为0xd0
      sbit CY = PSW^7; // 如果变量PSW已用sfr PSW = 0xd0;定义过

sfr和sbit都必须定义为全局变量,即必须在所有C51函数之前进行定义。

C51常用数据类型一览表

bitunsigned char这两种数据类型都可以直接支持单片机机器指令,因此代码的执行效率最高。

3.3.4 存储类型

80C51具有3个逻辑存储空间:片内低128B RAM、片外64KB RAM和片内外统一编址的64KB ROM,对于80C52还具有片内高128B RAM空间。

为了合理使用51单片机的存储空间,需要进一步细化存储区域的组成,C51将3个逻辑存储空间细分成6个存储类型区,如下图。

存储空间与存储类型的关系示意图

片内低128B RAM空间被分为databdata两个存储区,80C52单片机专有的高128B RAM被称为idata区域,片内外统一ROM空间被称为code存储区,片外RAM空间被分为xdatapdata两个存储区。C51的存储类型与存储空间对应关系见下表。

变量在定义时,只有将其数据类型和存储类型的信息都站现在变量定义式中,才能保证编译器顺利工作。

1
2
unsigned char data cc = 0x15;	// 指定变量cc为无符号字符型变量,存储单元位于片内低128B RAM中
int xdata xy; // 指定变量xy为有符号整型变量,存储单元位于片外64KB RAM中

实际应用中,小型系统只需使用片内RAM即可,大型系统则需要扩展片外RAM。为此,C51编译器设立了3中编译模式:SMALL(小型)、COMPACT(紧凑)、LARGE(大型)。如下图所示,在SMALL编译模式下,如果变量定义语句种省略了存储类型参数,则系统默认采用data存储类型,COMPACT和LARGE同理。

3种编译模式的相互关系

3.3.5 存储种类

  1. 自动型(auto)

    作用域是在定义该变量的函数体或语句组内。当函数调用结束或语句组执行完毕,自动型变量所占用的存储空间就被释放。默认选项

  2. 静态型(static)

    作用域是定义它的函数体、程序文件或语句组内。静态型变量具有变量的隐藏性存储持久性和默认0储值3个特点。

  3. 外部型(extern)

    如果变量的定义与使用不在同一作用域内,则用extern声明后就能将原作用域扩展到声明所在的位置,从而将变量值带到新的作用域内。

  4. 寄存器型(register)

    如果变量在使用种需要频繁地与内存进行数据交换,可以通过register定义将变量的存储单元指定为寄存器。

3.4 指针

3.5 数组

  1. 指针法引用数据元素

    若将一个变量用来存放一个数组的的起始地址,则这个变量就是指向数组的指针变量。

    1
    2
    int a[10];			// 定义a为包含10个整型元素的数组
    int *app = &a[0]; // 定义app为指向整型数组a的指针变量

    数组名就是为数组分配的连续存储单元的首地址,因而上述第二条语句等价于

    1
    int *app = a;

    实例:利用指针法计算10~19的平均值

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    int i, a[10]={10,11,12,13,14,15,16,17,18,19};
    int *app;

    void main(void)
    {
    float result = 0;
    for(app=a;app<(a+10);app++)
    result+=*app;
    result/=10;
    while (1);
    }

3.6 函数

C51函数定义语句称为函数头或函数首部,定义函数的语法格式如下

1
(返回值类型) 函数名((形式参数)) (编译模式) (reentrant) (interrupt x) (using y)
各项含义一览表

第4章 通用I/O口方式应用

4.1 独立按键识别

开始时LED均为熄灭状态,随后根据按键动作点亮对应LED(在按键释放后能继续保持亮灯状态,直到新的按键压下为止)

4.1电路图
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <reg51.h>

#define uchar unsigned char
#define uint unsigned int

void main() {
uchar key = 0;
while(1) {
key = P0 & 0x0f;
if (key != 0x0f)
P2 = key;
}
}

4.2 键控流水灯

在4.1电路图基础上,当K1按下时,流水灯从下往上;K2按下停止,且全部灯灭;K3按下使灯从下往上;K4按下使灯从上往下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <reg51.h>

#define uchar unsigned char
#define uint unsigned int

uchar code led[] = {0xf7, 0xfb, 0xfd, 0xfe};
void delay(uchar time) {
uint j = 15000;
for(; time>0; time--)
for(; j>0; j--);
}

void main() {
char i; // 如果 i 定义为无符号类型,则下面for循环中 i-- 会使 i=255,造成数组越界
bit dir=0, run=0;
while(1) {
switch(P0 & 0x0f) {
case 0x0e: run=1,dir=1;break;
case 0x0d: run=0;break;
case 0x0b: dir=1;break;
case 0x07: dir=0;break;
}
if(run) {
if(dir) {
for(i=0; i<=3; i++) {
P2=led[i];
delay(200);
}
}else {
for(i=3; i>=0; i--) {
P2=led[i];
delay(200);
}
}
}else {
P2=0xff;
}
}
}

4.3 流水灯控制

P2口连接8个低电平驱动LED,采用循环移位法,从D8到D1依次点亮后熄灭

函 数 名 函 数 声 明 功 能
_crol_ uchar _crol_(uchar,uchar); 将字符型数据按照二进制循环左移n位
_irol_ uint _irol_(uint,uchar); 将整型数据按照二进制循环左移n位
_lrol_ ulong _lrol_(ulong ,uchar); 将长整型数据按照二进制循环左移n位
_cror_ uchar _cror_(uchar,uchar); 将字符型整数按照二进制循环右移n位
_iror_ uint _iror_(uchar,uchar); 将整型数据按照二进制循环右移n位
_lror_ ulong _lror_(ulong ,uchar); 将长整型数据按照二进制循环右移n位
_nop_ void _nop_(uchar _sfr); 使单片机程序产生延时
_testbit_ bit _testbit_ (bit); 对字节中的一位进行测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <reg51.h>

#define uchar unsigned char
#define uint unsigned int

void delay() {
uchar i, j;
for(i=0; i<=100; i++)
for(j=0; j<=200; j++);
}

void main() {
P2 = 0x7f;
delay();
while(1) {
P2=_cror_(P2,1); // 将字符型数据按照二进制循环向右移n位
delay();
}
}

4.4 LED数码管显示

8位数码管,其中P0口与共阴极数码管的段码引脚相连,实现循环显示0~9字符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <reg51.h>

#define uchar unsigned char
#define uint unsigned int

char led_mod[]={0xbf,0x86,0xdb,0xcf,0xe6,0xed,0xfd,0x87,0xff,0xef};

void delay(uint time)
{
uint j;
for(;time>0;time--)
for(j=0;j<125;j++);
}

void main()
{
char i = 0;
while(1)
{
for(i=0;i<=9;i++)
{
P0=led_mod[i];
delay(500);
}
}
}

4.5 计数显示器

数码管显示初值为0,单击按键后,按增量1进行累加,累加值实时显示在数码管上。累加到99时,清空

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <reg51.h>

#define uchar unsigned char
#define uint unsigned int

sbit P3_7=P3^7;

uchar code table[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
uchar count;

void delay(uint time)
{
uint j;
for(;time>0;time--)
for(j=0;j<125;j++);
}

void main()
{
count=0;
P0=table[count/10]; // 十位
P2=table[count%10]; // 个位
while(1)
{
if(P3_7==0) // 检测按键是否被按下
{
delay(10); // 消抖延时
if(P3_7==0) // 确认按键被按下
{
count++;
if(count==100) count=0;
P0=table[count/10]; // 十位
P2=table[count%10]; // 个位
while(P3_7==0); // 等待按键松开,防止连续计数
}
}
}
}

4.6 数码管动态显示

采用动态显示原理显示字符“L2”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <reg51.h>

#define uchar unsigned char
#define uint unsigned int

uchar led_mode[] = {0x38, 0x5b};

void delay(uint time);

void main()
{
char led_point=0;
while(1)
{
P3=2-led_point; // 输出LED位码
P2=led_mode[led_point];
led_point=1-led_point; // 刷新LED位码
delay(30);
}
}

void delay(uint time)
{
uchar j;
for(;time>0;time--)
for(j=0;j<125;j++);
}

4.7 矩阵键盘

开机后数码管暂为黑屏状态,按下任意键后数码管立即显示该键对应的字符(0~F)。若没有新键按下,维持前次结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <reg51.h>

#define uchar unsigned char
#define uint unsigned int

char led_mod[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x58,0x5e,0x79,0x71};
char key_buf[]={0xee,0xde,0xbe,0x7e,
0xed,0xdd,0xbd,0x7d,
0xeb,0xdb,0xbb,0x7b,
0xe7,0xd7,0xb7,0x77};

char getKey()
{
char key_scan[]={0xef,0xdf,0xbf,0x7f}; // 键扫描码
char i=0,j=0;
for(i=0;i<4;i++)
{
P2=key_scan[i]; // P2送出键扫描码
if((P2&0x0f)!=0x0f) // 如果有按键被按下
{
for(j=0;j<16;j++) // 找出按下按键的号码
{
if(key_buf[j]==P2) return j;
}
}
}
return -1; // 没有按下按键返回 -1
}

void main()
{
char key=0;
P0=0x00;
while(1)
{
key = getKey();
if(key!=-1)
P0=led_mod[key];
}
}

4.8 一位密码锁

在4.7原理图上,系统上电后LED灭,数码管显示闪烁“8”,约1s后改为“-”(待机状态);单击按键表示输入一位密码,若密码正确,显示“P”,LED灯亮,随后进入待机状态;否则显示”E”,LED灯灭,随后进入待机状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <reg51.h>

#define uchar unsigned char
#define uint unsigned int

sbit lock=P3^0;

char key_buf[]={0xee,0xde,0xbe,0x7e,
0xed,0xdd,0xbd,0x7d,
0xeb,0xdb,0xbb,0x7b,
0xe7,0xd7,0xb7,0x77};

uchar init=0x7f,on=0x73,off=0x79,lock_on=0,lock_off=1;

char getKey()
{
// 同4.7getKey()方法
}
void delay(uint time)
{
uchar j;
for(;time>0;time--) for(j=0;j<125;j++);
}
void action(char stat, char num)
{
uchar i;
lock=num;
for(i=1;i<=2;i++)
{
P0=stat;
delay(500);
P0=0x0;
delay(500);
}
P0=0x40;
lock=1;
}

void main()
{
char key=0;
action(init,lock_off);
while(1)
{
key=getKey();
if(key!=-1)
{
if(key!=7) action(off,lock_off);
else action(on,lock_on);
}
}
}

第5章 中断系统

5.1 中断的概念

计算机在运行当前程序的过程中,若遇紧急或突发事件,可以暂停当前程序的运行,转向处理该突发事件,处理完成后再从当前程序的间断处接着运行。

中断管理系统处理突发事件的过程,成为CPU的中断响应过程

中断管理系统处理的突发事件称为中断源,中断源向CPU提出的处理请求称为中断请求,针对中断源和中断请求提供的服务函数称为中断函数。调用中断函数的过程是系统根据工作环境随机决定

中断能实现的功能:

  • 分时操作:CPU可以与多个外设同时工作
  • 实时处理:CPU可以立即响应并加以处理请求
  • 故障处理:当出现故障时,CPU可及时转去执行故障处理程序,自行处理而不会死机。