当前位置: 首页 > news >正文

04、数字电路与 C 语言基础

一、电平特性

        单片机是一种数字集成芯片,数字电路中只有两种电平:高电平和低电平。单片机 输出与输入为TTL电平,其中高电平为+5V,低电平为0V。计算机的串口为 RS232 电平且为负逻 辑电平,其中高电平为 -12V,低电平为 +12V。因此当计算机与单片机之间要通信时, 需要依靠电平转换芯片,比如 MAX232 电平转换芯片。

        常用的逻辑电平还有很多,比如TTL、CMOS、LVTTL、RS-232、RS-485等。 其中TTL和CMOS 的逻辑电平按典型电压可分为四类:5V系列(5VTTL和5VCMOS)、 3.3V 系列,2.5V系列和1.8V系列。

        5V TTL 和 5V CMMOS是通用的逻辑电平。3.3V及以下的逻辑电平被称为低电压逻辑电平,常用的为 LVTTL电平。低电压逻辑电平还有 2.5V 和 1.8V 两种。 输入/输出。 RS-232 和 RS-485 是串口的接口标准,RS-232是单端输入/输出。RS-485是差分输入/输出。

        TTL 电平信号用的最多,这是因为数据表示通常采用二进制,+5V等价于逻 辑1,0V等价于逻辑0,这被称为TTL(晶体管-晶体管逻辑电平)信号系统。

        CMOS 电平 VCC 可达12V,CMOS电路输出高电平约为 0.9VCC,而输出低电平 约为0.1VCC。CMOS 电路中不使用的输入端不能悬空,否则会造成逻辑混乱。另 外,CMOS集成电路电源电压可以在较大范围内变化,因而对电源的要求不像TTL 集成电路那样严格。

1.1、TTL 电路和 CMOS 电路的逻辑电平关系:

  1. VOH:逻辑电平 1 的输出电压;
  2. VOL:逻辑电平 0 的输出电压;
  3. VIH:逻辑电平1的输入电压;
  4. VIL:逻辑电平0的输入电压。

TTL 电平临界值:

  1. VOHmin = 2.4V,VOLmax = 0.4V。
  2. VIHmin = 2.0V,VILmax = 0.8V

COMS 电平临界值(假设电源电压为+5V):

  1. OHmin=4.99V,VOLmax=0.01V。
  2. VIHmin=3.5V,VILmax=1.5V。

        TTL 和 CMOS 的逻辑电平转换:CMOS电平能驱动 TTL 电平,但 TTL 电平不能驱动 CMOS电平,需加上拉电阻。

1.2、常用逻辑芯片的特点如下:

系列类型输入电平输出电平
74LS 系列TTLTTLTTL
74HC 系列CMOSCMOSCMOS
74HCTCMOSTTLCMOS
CD4000CMOSTTLCMOS

        单片机、DSP、FPGA之间引脚能否直接相连要参考以下方法进 行判断:一般来说,同电压的是可以相连的,不过最好还是要查看下芯片技术手 册上的VIL、VIH、VOL和VOH的值,看是否能够匹配。有些情况在一般应用中没 者不同批次的器件就不能运行。 有问题,但是参数上就是有点不够匹配,在某些情况下运行可能就不够稳定,或者不同批次的器件就不能运行。

二、二进制与十六进制

2.1、二进制

        数字电路中只有两种电平特性,即高电平和低电平,这也就决定了数字电路 中使用二进制。“ 逢二进一, 借一当二 ” 是二进制数的特点。

        举例:十进制数 1转换为二进制数是1B(这里 B表 示二进制数的后缀);十进制数 2转换为二进制数时,因为已经到 2,所以需要进 1,那么二进制数即为 10B;十进制数 5转换为二进制数,2为10B,那么 3 即为 10B+1B=11B,4 即为11B+1B=100B,5即为 100B+ 1B= 101B。依次类推,当十进制数为 254时,对应而 = 二进制数为 1111 1110B(中间空一格是为了方便阅读, 实际编写不要空格)。

        二进制数转换为十进制数时,从二进制数的最后一位往前看,每一位代表的数为2的n次幂,这里的 n表示从最后起的第几位二 进制数,n从 0算起,若对应该二进制数位上有 1,那么就有值,为 0则无值。 例如,再把二进制数1111 1110B反推回十进制数,计算过程如下:

0 * 2^{0} + 1* 2^{1} + 1 * 2^{2} + 1 * 2^{3} + 1 * 2^{4} + 1 * 2^{5} + 1 * 2^{6} + 1 * 2^{7} = 254
其中 2^{n} 称为“位权”,我们只需记住 0 ~ 15以内的二进制数就好,实际开发中常常会用到其它比较大的数,这时我们用Windows系统自带的计 算器,选择程序员模式,可以非常方便的进行二进制、八进制、十进制、十六进 制数之间的任意转换。一般在十六进制数的最后面加上后缀H,表示该数为十六进制数,如AH、DEH 等。这里的字母不区分大小写,在C语言编程时要写成“0xa,0xde”,在数的 最前面加上“0x”表示该数为十六进制数。

2.2、十六进制

        十六进制与二进制大同小异,不同之处就是十六进制是“逢十六进一,借一 当十六”。十进制的 0-15 表示成十六进制为 0~9、 A、B、C、D、E、F,即十进制的 10 对应十六进制的 A,11对应B,以此类推。

三、二进制的逻辑运算

3.1、与运算

        “与”运算是实现“必须都有,否则就没有”这种逻辑关系的一种运算。C 语言中运算符为“&”,其运算规则如下:0&0=0,0&1=0(1&0=0),1&1=1。其运 算符号如下图所示:

        C 语言中 “&&” 表示 “按位与”运算,意思是变量之间按二进制位数对应关系一一进行“与”运算。如(0101 0101)&&(1010 1010)=0000 0000,而上面 讲到的“&”运算只是对单一位进行运算。

3.2、或运算

        “或”运算是实现“只要其中之一有就有”这种逻辑关系的一种运算。C语 言中运算符为“|”,其运算规则如下:0|0=0,0|1=1(1|0=1),1|1=1。其运 算符号如下:

        C 语言中 “||” 表示“按位或”运算,意思是变量之间按二进制位数对应关系一一进行“或”运算。如(0101 0101)||(1010 1010)=1111 1111,而上面 讲到的“|”运算只是对单一位进行运算。

3.3、非运算

        “非”运算是实现“求反”这种逻辑关系的一种运算。C语言中运算符为“!”, 其运算规则如下:!0=1,!1=0。其运算符号如下图所示:

        C 语言中 “~” 表示“按位取反”运算。如~01010101=1010 1010,而上面讲 到的 “!” 运算只是对单一位进行运算。

3.4、同或运算

        “同或” 与 “异或”运算使用的较少,“同或”运算是实现“必须相同,否则就没有”这种逻 辑关系的一种运算,其逻辑运算符为 “⊙” 。其运算规则如下:

        0⊙0=1,0⊙1=0(1⊙0=0),1⊙1=1。在C语言中没有规定符号。其运算符号如下:

3.5、异或运算

        “异或”运算是实现“必须不同,否则就没有”这种逻辑关系的一种运算, 其逻辑运算符为“⊕”。其运算规则如下:0⊕0=0,0⊕1=1(1⊕0=1),1⊕1=0。 在C语言中有“按位异或”运算 “^”。其运算符号如下:

四、C51 基础知识

4.1、C 语言简介

        C语言是在70年代初问世的。一九七八年由美国电话电报公司(AT&T)贝尔 实验室正式发表了C语言。同时由B.W.Kernighan和D.M.Ritchit合著了著名的 “THE CPROGRAMMING LANGUAGE”一书。通常简称为《K&R》,也有人称之为《K&R》 标准。但是,在《K&R》中并没有定义一个完整的标准C语言,后来由美国国家标准协会(American National Standards Institute)在此基础上制定了一个 C 语言标准,于一九八三年发表。通常称之为 ANSI C。

        早期的C语言主要是用于UNIX系统。由于C语言的强大功能和各方面的优 点逐渐为人们认识,到了八十年代,C开始进入其它操作系统,并很快在各类大、 中、小和微型计算机上得到了广泛的使用,成为当代最优秀的程序设计语言之。

        C 语言作为一种非常方便的语言而得到广泛的支持,很多硬件开发都用C语 言编程,如各种单片机、DSP、ARM等。C语言程序本身不依赖于机器硬件系统, 基本上不做修改或仅做简单的修改就可以将程序从不同的系统移植过来直接使 用。C语言提供了很多数学函数并支持浮点运算,开发效率高,可极大的缩短开 比,有如下优点: 发周期,增加程序可读性和可维护性。单片机的C51编程与汇编 ASM-51编程相比,有如下优点:

  1. 对单片机的指令系统不要求有任何的了解,就可以用 C 语言直接编程操作单片机;
  2. 寄存器分配、不同存储器的寻址及数据类型等细节完全有编译器自动管理;
  3. 程序有规范的结构,可分成不同的函数,可使程序结构化;
  4. 库中包含许多标准子程序,具有较强的数据处理能力,使用方便;
  5. 具有方便的模块化编程技术,大大提供程序的可移植性。

4.2、C 数据类型

C 语言包含的数据类型如下图所示:

4.2.1、C51 基本数据类型

        当我们给单片机编程时,单片机也要运算,而在单片机的运算中, 这个“变量” 数据的大小是有限制的,我们不能随意给一个变量赋任意的值,因 为变量在单片机的内存中是要占据空间的,变量大小不同,所占据的空间就不同, 为了合理利用单片机内存空间,我们在编程时就要设定合适的数据类型,不同的 数据类型也就代表了十进制中不同的数据大小,所以我们在设定一个变量之前, 必须要给编译器声明这个变量的类型,以便让编译器提前从单片机内存中分配给 这个变量合适的空间。单片机的C语言中常用的基本数据类型如下:

        大家在C语言的书籍上还能看到有 short int,long int,signed  short  int 等数据类型,在单片机的 C 语言中我们默认的规则如下:short int即为 int, long int即为 long,前面若无 unsigned符号则一律认为是 signed型。

        在单片机中,所有的数据都是以二进制形式存储在存储器中的,既然是二进制,那么就只有两个数,0 和 1, 这两个数每一个所占的空间就是 1 位(b),位也是单片机存储器中最小的单位。 比位大的单位是字节(B),1个字节等于8位(即1B=8b或1Byte=8bit)。

        字符型占用存储空间最小,为 8 位,双精度浮点型最大, 为 64位。其中 float型和 double型是用来表示浮点数的,也就是带小数点的数, 如 12.345,0.213等。在这里要说明的是,在一般的系统中,float 型数据只能 提供7位有效数字,double 型数据能够提供15~16位有效数字,但是这个精度 还和编译器有关系,并不是所有的编译器都准守这条原则。当把一个 double 型 变量赋给 float 型变量时,系统会截取相应的有效位数,例如:

float a;
a = 123.1234567;

        由于 float 型变量只能接受7位有效数字,因此最后3位小数将会被四舍五 入截掉,即实际a的值将是123.1235。若将 a 改成double型变量,则能全部接收上述 10位数字并存储在变量 a中。

4.2.2、C51 扩充数据类型

        C51扩 充数据类型主要如下:

        SCON  是单片机的串行口控制寄存器,这个寄存器在单片机内存中的地址是 0X98。这样声明后,我们再以后要操作这个控制寄存器时,就可以直接对 SCON 进行操作,这时编译器也会明白,我们实际要操作的是单片机内部 0X98 地址处的这个寄存器,而 SCON仅仅是这个地址的一个代号或是名称而已,当然,我们 也可以定义成其他的名称

例如:sfr16 T2=0xCC;
声明一个16 位的特殊功能寄存器,它的起始地址为 0XCC。
例如:sbit TI = SCON^1;

        SCON 是一个 8 位寄存器,SCON^1 表示这个 8 位寄存器的次低位,最低位是 SCON^0;SCON^7 表示这个寄存器的最高位。该语句的功能就是将 SCON寄存器的 次低位声明为TI,以后若要对SCON寄存器的次低位操作,则可直接操作 TI。

        在C51语言程序中,有可能会出现在运算中数据类型不一致的情况。C51允 许任何标准数据类型的隐式转换,隐式转换的优先级顺序如下:

bit→char→int→long→float→signed→unsigned

        也就是说,当char型与int型进行运算时,先自动对char型扩展为int型, 然后与int型进行运算,运算结果为int型。C51除了支持隐式类型转换外,还 可以通过强制类型转换符“()”对数据类型进行人为的强制转换。

        C5l 编译器除了能支持以上这些基本数据类型之外,还能支持一些复杂的组合型数据类型,如数组类型、指针类型、结构类型、联合类型等这些复杂的数据 类型。对于初学者我们要求先掌握C语言基础知识,把基础的掌握了在学习复杂的。

4.3、运算

4.3.1、常量

        常量是指在程序执行过程中其值不能改变的量。在C51中支持整型常量、浮 点型常量、字符型常量和字符串型常量。

1)整型常量

        整型常量也就是整型常数,根据其值范围在计算机中分配不同的字节数来存放。在C51中它可以表示成以下几种形式:

  • 十进制整数。如234、-56、0 等;
  • 十六进制整数。以0x开头表示,如0x12表示十六进制数12H;
  • 长整数。
    在C51中当一个整数的值达到长整型的范围,则该数按长整型存放, 在存储器中占四个字节,另外,如一个整数后面加一个字母L,这个数在存储器 中也按长整型存放。如123L在存储器中占四个字节。

2)浮点型常量

        浮点型常量也就是实型常数。有十进制表示形式和指数表示形式。十进制表示形式又称定点表示形式,由数字和小数点组成。如 0.123、34.645等都是十进制数表示形式的浮点型常量。指数表示形式为: [] 数字 [.数字] e []数字 例如:123.456e-3、-3.123e2 等都是指数形式的浮点型常量。

3)字符型常量

        字符型常量是用单引号引起的字符,如‘a’、‘1’、‘F’ 等。可以是可 显示的 ASCII 字符,也可以是不可显示的控制字符。对不可显示的控制字符须在前面加上反斜杠 “\” 组成转义字符。利用它可以完成一些特殊功能和输出时的 格式控制。常用的转义字符如下表所示。

转义字符       含义 ASCII 码(十六进制数)
\o空字符(null)00H
\n换行符(LF)0AH
\r回车符(CR)0DH
\t水平制表符(HT))09H
\b退格符(BS)08H
\f换页符(FF)0CH
\'单引号27H
\"双引号22H
\\反斜杠5CH

4)字符串型常量

        字符串型常量由双引号 “” 括起的字符组成。如“D”、“1234”、“ABCD” 等。注意字符串常量与字符常量是不一样,一个字符常量在计算机内只用一个字 节存放,而一个字符串常量在内存中存放时不仅双引号内的字符一个占一个字 节,而且系统会自动的在后面加一个转义字符“\o”作为字符串结束符。因此不 要将字符常量和字符串常量混淆,如字符常量‘A’和字符串常量“A”是不一样 的。

4.3.2、变量

        变量是在程序运行过程中其值可以改变的量。一个变量由两部分组成:变量名和变量值。在C51中,变量在使用前必须对变量进行定义,指出变量的数据类型和存储模式。以便编译系统为它分配相应的存储单元。定义的格式如下:

[存储种类]    数据类型说明符    [存储器类型]    变量名1[=初值],变量名2[初值]…;
1)数据类型说明符

        在定义变量时,必须通过数据类型说明符指明变量的数据类型,指明变量在存储器中占用的字节数。可以是基本数据类型说明符,也可以是组合数据类型说明符,还可以是用typedef定义的类型别名。

        在C51中,为了增加程序的可读性,允许用户为系统固有的数据类型说明符 用typedef起别名,格式如下:

typedef    c51固有的数据类型说明符    别名;

        定义别名后,就可以用别名代替数据类型说明符对变量进行定义。别名可以 用大写,也可以用小写,为了区别一般用大写字母表示。

typedef unsigned int U16;
typedef unsigned char U8;
U8 a1 = 0x12;
U16 a2 = 0x1314;
2)变量名

        变量名是 C51 区分不同变量,为不同变量取的名称。在C51中规定变量名可以由字母、数字和下划线三种字符组成,且第一个位置必须为字母或下划线。变 量名有两种:普通变量名和指针变量名。它们的区别是指针变量名前面要带“*” 号。

3)存储种类(作用域)

        存储种类是指变量在程序执行过程中的作用范围。C51变量的存储种类有四种,分别是自动( auto )、外部( extern )、静态( static )和寄存器( register )。

  1. auto: 自动变量 默认数据域
    使用auto定义的变量称为自动变量,其作用范围在定义它的函数体或复合语句内部,当定义它的函数体或复合语句执行时,C51才为该变量分配内存空间, 结束时占用的内存空间释放。自动变量一般分配在内存的堆栈空间中。定义变量时,如果省略存储种类,则该变量默认为自动(auto)变量。
  2. extern:  外部变量
    使用extern定义的变量称为外部变量。在一个函数体内,要使用一个已在该函数体外或别的程序中定义过的外部变量时,该变量在该函数体内要用 extern说明。外部变量被定义后分配固定的内存空间,在程序整个执行时间内都有效,直到程序结束才释放。
  3. static: 静态变量
    使用 static 定义的变量称为静态变量。它又分为内部静态变量和外部静态变量。在函数体内部定义的静态变量为内部静态变量,它在对应的函数体内有效, 一直存在,但在函数体外不可见,这样不仅使变量在定义它的函数体外被保护, 还可以实现当离开函数时值不被改变外部静态变量上在函数外部定义的静态变 量。它在程序中一直存在,但在定义的范围之外是不可见的。如在多文件或多模块处理中,外部静态变量只在文件内部或模块内部有效。
  4. register: 寄存器变量
    使用 register 定义的变量称为寄存器变量。它定义的变量存放在CPU内部的寄存器中,处理速度快,但数目少。C51 编译器编译时能自动识别程序中使用频率最高的变量,并自动将其作为寄存器变量,用户可以无需专门声明。
4)存储类型

        存储器类型是用于指明变量所处的单片机的存储器区域情况。存储器类型与存储种类完全不同。C51编译器能识别的存储器类型有以下几种,见表所示。

存储器类型说明
data直接寻址的片内 RAM 低 128B,访问速度快
bdata片内 RAM 的可位寻址区(20H~2FH),允许字节和位混合访问
idata间接寻址访问的片内 RAM,允许访问全部片内 RAM
pdata用 Ri 间接访问的片外 RAM 的低 256B
xdata用 DPTR 间接访问的片外 RAM,允许访问全部 64k 片外 RAM
code程序存储器 ROM 64k 空间

        定义变量时也可以省 “存储器类型”,省时C51编译器将按编译模式默认存储器类型。在C51中,变量的默认存储器类型取决于编译器的存储模式设置,主要有以下3种模式:

  1. Small 模式:默认存储类型:  data  。
    特点:变量存储在片内RAM中,访问速度快,但片内RAM空间有限。适合对访问速度要求高且变量数量较少的程序。
  2. Compact 模式:默认存储类型:  pdata  。
    特点:变量存储在片外RAM的低256字节区域,通过寄存器R0或R1间接寻址。访问速度比片内RAM慢,但比Large模式快,适合中等规模的变量存储。
  3. Large模式:默认存储类型:  xdata  。
    特点:变量存储在片外RAM的 64KB 空间中,通过DPTR间接寻址。访问速度最慢,但存储空间最大,适合存储大量数据。
5)特殊功能寄存器变量

        51系列单片机片内有许多特殊功能寄存器,通过这些特殊功能寄存器可以控制 51系列单片机的定时器、计数器、串口、I/O及其它功能部件,每一个特殊功能寄存器在片内 RAM 中都对应于一个字节单元或两个字节单元

        在 C51中,允许用户对这些特殊功能寄存器进行访问,访问时须通过 sfr 或 sfr16 类型说明符进行定义,定义时须指明它们所对应的片内RAM单元的地址。格式如下:

sfr或sfr16    特殊功能寄存器名 = 地址;

        sfr 用于对 51 单片机中单字节的特殊功能寄存器进行定义,sfr16 用于对双 字节特殊功能寄存器进行定义。特殊功能寄存器名一般用大写字母表示。地址一 般用直接地址形式。

sfr PSW=0x80;
sfr SCON=0x98;
sfr TMOD=0x89;
sfr P1=0x90;
sfr16 DPTR=0x82;
sfr16 T1=0x8A;
6)位变量

        在C51中,允许用户通过位类型符定义位变量。位类型符有两个:bit 和 sbit。 可以定义两种位变量。

        bit 位类型符用于定义一般的可位处理位变量。它的格式如下:

bit    位变量名;

        在格式中可以加上各种修饰,但注意存储器类型只能是 bdata、data、idata。 只能是片内RAM 的可位寻址区,严格来说只能是 bdata。

        sbit 位类型符用于定义在可位寻址字节或特殊功能寄存器中的位,定义时须指明其位地址,可以是位直接地址,可以是可位寻址变量带位号,也可以是特殊功能寄存器名带位号。格式如下:

sbit 位变量名=位地址;

        如位地址为位直接地址,其取值范围为 0x00~0xff;如位地址是可位寻址变量带位号或特殊功能寄存器名带位号,则在它前面须对可位寻址变量或特殊功能寄存器进行定义。字节地址与位号之间、特殊功能寄存器与位号之间一般用 “^” 作间隔。如定义 51 单片机管脚:

sbit LED = P2^5;

        在C51中,为了用户操作方便,C51 编译器把 51单片机的常用的特殊功能寄存器和特殊位进行了定义,放在一个 “reg51.h” 或 “reg52.h” 的头文件中,当用户要使用时,只须要在使用之前用一条预处理命令“#include ” 或“#include”把这个头文件包含到程序最开始位置,然后就可使用 殊功能寄存器名和特殊位名称。

4.4、 C51 运算符

4.4.1、赋值运算符 “=”

        在C51中,它的功能是将一个数据的值赋给一个变量, 如 x = 1。利用赋值运算符将一个变量与一个表达式连接起来的式子称为赋值表 达式,在赋值表达式的后面加一个分号“;”就构成了赋值语句,一个赋值语句 的格式如下:

变量 = 表达式;

        执行时先计算出右边表达式的值,然后赋给左边的变量。例如:

x = 1 + 2;
x = y = 3;

        在C51中,允许在一个语句中同时给多个变量赋值,赋值顺序自右向左。

4.4.2、算术运算符

C51中支持的算术运算符有:

符号说明
+加或取正值运算符
-减或取负值运算符
*乘运算符
/除运算符
%取余运算符

        对于除运算,相除的两个数为浮点数则运算的结果也为浮点数,如相除的两个数为整数,则运算的结果也为整数,即为整除。如 25.0/20.0 结果为 1.25,而 25/20 结果为 1。

        对于取余运算,则要求参加运算的两个数必须为整数,运算结果为它们的余数。例如:x=5%3,结果 x 的值为 2。

4.4.3、关系运算符

C51 中有 6 种关系运算符:

符号说明
>大于
<小于
>=大于等于
<=小于等于
==等于
!=不等于

        关系运算用于比较两个数的大小,用关系运算符将两个表达式连接起来形成的式子称为关系表达式。关系表达式通常用来作为判别条件构造分支或循环程 序。关系表达式的一般形式如下:

表达式1    关系运算符    表达式2

        关系运算的结果为逻辑值,成立为真(1),不成立为假(0)。其结果可以作为一个逻辑量参与逻辑运算。例如:5>3,结果为真(1),而10==100,结果为假(0)。 注意:关系运算符等于 “==” 是由两个 “=” 组成。

4.4.4、逻辑运算符

C51有3种逻辑运算符:

符号说明
||逻辑或
&&逻辑与
!逻辑非

        关系运算符用于反映两个表达式之间的大小关系,逻辑运算符则用于求条件式的逻辑值,用逻辑运算符将关系表达式或逻辑值连接起来的式子就是逻辑表达式。

1)逻辑与

条件式1 && 条件式2

        当条件式 1 与条件式 2 都为真时结果为真(非0值),否则为假(0值)。

2)逻辑或

条件式1 || 条件式2

当条件式 1 与条件式 2 都为假时结果为假(0值),否则为真(非0值)。

3)逻辑非

!条件式

        当条件式原来为真(非0值),逻辑非后结果为假(0值)。当条件式原来 为假(0值),逻辑非后结果为真(非0值)。

4.4.5、位运算符

        C51语言能对运算对象按位进行操作,它与汇编语言使用一样方便。位运算是按位对变量进行运算,但并不改变参与运算的变量的值如果要求按位改变变量的值,则要利用相应的赋值运算C51 中位运算符只能对整数进行操作,不能对浮点数进行操作。C51中的位运算符有:

符号说明
&按位与
|按位或
^按位异或
~按位取反
<<左移
>>右移

例:设a=0x45=01010100B,b=0x3b=00111011B,则 a&b、a|b、a^b、~a、 a>2分别为多少?

a&b=00010000B=0x10
a|b=01111111B=0x7f
a^b=01101111B=0x6f
~a=10101011B=0xab
a<<2=01010000B=0x50
b>>2=00001110B=0x0e

4.4.6、复合赋值运算符

        C51 语言中支持在赋值运算符 “=” 的前面加上其它运算符,组成复合赋值运算符。下面是C51中支持的复合赋值运算符

符号说明符号说明
+=加法赋值-=减法赋值
*=乘法赋值/=除法赋值
%=取模赋值&=逻辑与赋值
|=逻辑或赋值^=逻辑异或赋值
~=逻辑非赋值>>=右移位赋值
<<=左移位赋值

复合赋值运算的一般格式如下:

变量    复合运算赋值符    表达式

        它的处理过程:先把变量与后面的表达式进行某种运算,然后将运算的结果赋给前面的变量。其实这是 C51 语言中简化程序的一种方法,大多数二目运算都可以用复合赋值运算符简化表示。例如:a+=6 相当于 a=a+6;a*=5 相当于 a=a*5; b&=0x55 相当于 b=b&0x55;x>>=2 相当于 x=x>>2。

4.4.7、逗号运算符

        在 C51 语言中,逗号 “,” 是一个特殊的运算符,可以用它将两个或两个以上的表达式连接起来,称为逗号表达式。逗号表达式的一般格式为:

表达式1,    表达式2,    ……,    表达式n

        程序执行时对逗号表达式的处理:按从左至右的顺序依次计算出各个表达式的值,而整个逗号表达式的值是最右边的表达式(表达式n)的值。例如:x=(a=3,6*3)结果x的值为18。

4.4.8、条件运算符

        条件运算符“?:”是 C51 语言中唯一的一个三目运算符,它要求有三个运算对象,用它可以将三个表达式连接在一起构成一个条件表达式。条件表达式的 一般格式为:

逻辑表达式?    表达式1:    表达式2

         其功能是先计算逻辑表达式的值,当逻辑表达式的值为真(非0值)时,将计算的表达式 1 的值作为整个条件表达式的值;当逻辑表达式的值为假(0值)时,将计算的表达式 2 的值作为整个条件表达式的值。例如:条件表达式 max=(a>b)?a:b的执行结果是将a和b中较大的数赋值给变量 max。

4.4.9、指针与地址运算符

        指针是 C51 语言中的一个十分重要的概念,在 C51 中的数据类型中专门有一 种指针类型。指针为变量的访问提供了另一种方式,变量的指针就是该变量的地址,还可以定义一个专门指向某个变量的地址的指针变量。 为了表示指针变量和它所指向的变量地址之间的关系,C51中提供了两个专 门的运算符:

符号说明
*指针运算符
&取地址运算符

        指针运算符 “*” 放在指针变量前面,通过它实现访问以指针变量的内容为地址所指向的存储单元。例如:指针变量 p 中的地址为 2000H,则 *p 所访问的是地址为2000H的存储单元,x=*p,实现把地址为 2000H 的存储单元的内容送给变量 x。

        取地址运算符 “&” 放在变量的前面,通过它取得变量的地址,变量的地址通常送给指针变量。例如:设变量 x 的内容为 12H,地址为2000H,则&x的值为 2000H,如有一指针变量 p,则通常用 p=&x,实现将 x 变量的地址送给指针变量 p,指针变量 p 指向变量x,以后可以通过 *p 访问变量 x。

4.5、C51 表达式及复合语句

4.5.1、表达式语句

在表达式的后边加一个分号“;”就构成了表达式语句,如:

a = ++b*9;
x = 8; y=7;
++k;

        可以一行放一个表达式形成表达式语句,也可以一行放多个表达式形成表达 式语句,这时每个表达式后面都必须带“;”号,另外,还可以仅由—个分号“;” 占一行形成一个表达式语句,这种语句称为空语句。

空语句在程序设计中通常用于两种情况:

1) 在程序中为有关语句提供标号,用以标记程序执行的位置。例如采用下面的语句可以构成一个循环。

repeat: 语句
...    功能
goto    repeat;

2) 在用 while 语句构成的循环语句后面加一个分号,形成一个不执行其它操作的空循环体。这种结构通常用于对某位进行判断,当不满足条件则等待,满足条件则执行。

4.5.2、复合语句

        复合语句是由若干条语句组合而成的一种语句,在C51中,用一个大括号 “{ }”将若干条语句括在一起就形成了一个复合语句,复合语句最后不需要以分号“;”结束,但它内部的各条语句仍需以分号“;”结束。复合语句的一般形式为:

{局部变量定义;语句1;语句2;
}

        复合语句在执行时,其中的各条单语句按顺序依次执行,整个复合语句在语法上等价于一条单语句,因此在 C51 中可以将复合语句视为一条单语句。通常复合语句出现在函数中,实际上,函数的执行部分(即函数体)就是一个复合语句; 复合语句中的单语句一般是可执行语句,此外还可以是变量的定义语句(说明变量的数据类型)。在复合语句内部语句所定义的变量,称为该复合语句中的局部变量,它仅在当前这个复合语句中有效。利用复合语句将多条单语句组合在—起, 以及在复合语句中进行局部变量定义是 C51 语言的一个重要特征。

4.6、C51 基本结构和相关语句        

4.6.1、C51 的基本结构

1)顺序结构

        顺序结构是最基本、最简单的结构,在这种结构中,程序由低地址到高地址依次执行,如图给出顺序结构流程图,程序先执行 A 操作,然后再执行 B 操作。

                                                    

2)选择结构

        选择结构可使程序根据不同的情况,选择执行不同的分支,在选择结构中,程序先都对一个条件进行判断。当条件成立,即条件语句为“真”时,执行一个分支,当条件不成立时,即条件语句为“假”时,执行另一个分支。如图:当条件 S 成立时,执行分支 A,当条件 P 不成立时,执行分支 B。

                          

        在 C51 中,实现选择结构的语句为 if/else,if/else if 语句。另外在 C51中还支持多分支结构,多分支结构既可以通过 if 和 else if 语句嵌套实现,可用 swith/case 语句实现。

3)循环结构

        在程序处理过程中,有时需要某一段程序重复执行多次,这时就需要循环结构来实现,循环结构就是能够使程序段重复执行的结构。循环结构又分为两种:当(while)型循环结构和直到(do...while)型循环结构。

①当型循环结构 while

        当型循环结构如图:当条件 P 成立(为“真”)时,重复执行语句 A,当条
件不成立(为“假”)时才停止重复,执行后面的程序。

                      

②直到型循环结构 do ... while

        直到型循环结构,先执行语句 A,再判断条件 P,当条件成立(为“真”)时,再重复执行语句 A,直到条件不成立(为“假”)时才停止重复,执行后面的程序。构成循环结构的语句主要有:while、do while、for、goto。

4.6.2 C51 的相关语句

1)if 语句

        if 语句是 C51 中的一个基本条件选择语句,它通常有三种格式:

①if (表达式) {语句;}

②if (表达式) {语句 1;} else {语句 2;}

③if (表达式 1) {语句 1;}

else if (表达式 2) (语句 2;)
else if (表达式 3) (语句 3;)
……
else if (表达式 n-1) (语句 n-1;)
else {语句 n}

例子:if 语句的用法

(1)if (x!=y) printf(“x=%d,y=%d\n”,x,y);
执行上面语句时,如果 x 不等于 y,则输出 x 的值和 y 的值。
(2)if (x>y) max=x;
else max=y;
执行上面语句时,如 x 大于 y 成立,则把 x 送给最大值变量 max,如 x 大于y 不成立,则把 y 送给最大值变量 max。使 max 变量得到 x、y 中的大数。
(3)if (score>=90) printf(“Your result is an A\n”);
else if (score>=80) printf(“Your result is an B\n”);
else if (score>=70) printf(“Your result is an C\n”);
else if (score>=60) printf(“Your result is an D\n”);
else printf(“Your result is an E\n”);
执行上面语句后,能够根据分数 score 分别打出 A、B、C、D、E 五个等级。

2)switch/case 语句

        if 语句通过嵌套可以实现多分支结构,但结构复杂。switch 是 C51 中提供的专门处理多分支结构的多分支选择语句。它的格式如下:

switch (表达式)
{
case 常量表达式 1:{语句 1;}break;
case 常量表达式 2:{语句 2;}break;
……
case 常量表达式 n:{语句 n;}break;
default:{语句 n+1;}
}

说明如下:
(1)switch 后面括号内的表达式,可以是整型或字符型表达式。
(2)当该表达式的值与某一 “case” 后面的常量表达式的值相等时,就执行该“case”后面的语句,然后遇到 break 语句退出 switch 语句。若表达式的值与所有 case 后的常量表达式的值都不相同,则执行 default 后面的语句,然后退出 switch 结构。
(3)每一个 case 常量表达式的值必须不同否则会出现自相矛盾的现象。
(4)case 语句和 default 语句的出现次序对执行过程没有影响。
(5)每个 case 语句后面可以有“break”,也可以没有。有 break 语句,执行到 break 则退出 switch 结构,若没有,则会顺次执行后面的语句,直到遇到 break 或结束。
(6)每一个 case 语句后面可以带一个语句,也可以带多个语句,还可以不带。语句可以用花括号括起,也可以不括。
(7)多个 case 可以共用一组执行语句。

例子:对学生成绩划分为 A~D,对应不同的百分制分数,要求根据不同的等级打印
出它的对应百分数。可以通过下面的 switch/case 语句实现。

switch(grade)
{case ‘A’;printf(”90~100\n”);break;case ‘B’;printf(”80~90\n”);break;case ‘C’;printf(”70~80\n”);break;case ‘D’;printf(”60~70\n”);break;case ‘E’;printf(”<60\n”);break;default;printf(”error”\n)
}
3)while 语句

while 语句在 C51 中用于实现当型循环结构,它的格式如下:

while(表达式){语句;
} /* 循环体*/

        while 语句后面的表达式是能否循环的条件,后面的语句是循环体。当表达式为非 0(真)时,就重复执行循环体内的语句;当表达式为 0(假),则中止while 循环,程序将执行循环结构之外的下一条语句。它的特点是:先判断条件,后执行循环体。在循环体中对条件进行改变,然后再判断条件,如条件成立,则再执行循环体,如条件不成立,则退出循环。如条件第一次就不成立,则循环体一次也不执行。

4)do ... while 语句

        do...while 语句在 C51 中用于实现直到型循环结构,它的格式如下:

do{语句;
}/* 循环体*/
while( 表达式);

它的特点是:先执行循环体中的语句,后判断表达式。如表达式成立(真),则再执行循环体,然后又判断,直到有表达式不成立(假)时,退出循环,执行do while 结构的下一条语句。do while 语句在执行时,循环体内的语句至少会被执行一次。

5)for 语句
for(表达式1;表达式2;表达式3){语句;
}/*循环体*/

for 语句后面带三个表达式,它的执行过程如下:

  1. 先求解表达式 1 的值;
  2. 求解表达式 2 的值,如表达式 2 的值为真,则执行循环休中的语句,
    然后执行下一步 3 的操作,如表达式 2 的值为假,则结束 for 循环,转到最
    后一步。
  3. 若表达式 2 的值为真,则执行完循环体中的语句后,求解表达式 3,然
    后转到第四步。
  4. 转到 2 继续执行
  5. 退出 for 循环,执行下面的一条语句。

        在 for 循环中,一般表达式 1 为初值表达式,用于给循环变量赋初值;表达式 2 为条件表达式,对循环变量进行判断;表达式 3 为循环变量更新表达式,用于对循环变量的值进行更新,使循环变量能不满足条件而退出循环。

6)循环嵌套

        在一个循环的循环体中允许又包含一个完整的循环结构,这种结构称为循环的嵌套。外面的循环称为外循环,里面的循环称为内循环,如果在内循环的循环体内又包含循环结构,就构成了多重循环。在 C51 中,允许三种循环结构相互嵌套

例:用嵌套结构构造一个延时程序

void delay(unsigned int x)
{
unsigned char j;
while(x--)
{for (j=0;j<125;j++);}

        用内循环构造一个基准的延时(几秒),调用时通过参数设置外循环的次数,这样就可以形成各种延时关系。

7)break 和 countinue 语句

        break 和 continue 语句通常用于循环结构中,用来跳出循环结构。但是二者又有所不同。

(1)break 语句

        用 break 语句可以跳出 switch 结构,使程序继续执行 switch结构后面的一个语句。使用 break 语句还可以从循环体中跳出循环,提前结束循环而接着执行循环结构下面的语句。它不能用在除了循环语句和 switch 语句之外的任何其它语句中。

例子:当计算到面积大于 100 时,由 break语句跳出循环

for (r=1;r<=10;r++)
{area=pi*r*r;if (area>100) break;printf(“%f\n”,area);
}

(2)countinue 语句

        continue 语句用在循环结构中,用于结束本次循环,跳过循环体中 continue下面尚未执行的语句,直接进行下一次是否执行循环的判定。

        continue 语句和 break 语句的区别在于:continue 语句只是结束本次循环而不是终止整个循环;break 语句则是结束循环,不再进行条件判断。

例子:输出 100~200 间不能被 3 整除的数。

for (i=100;i<=200;i++)
{if (i%3 == 0) continue;printf(“%d ”;i);
}

        在程序中,当 i 能被 3 整除时,执行 continue 语句,结束本次循环,跳过 printf 函数,只有能被 3 整除时才执行 printf 函数。

8)return 语句

        return 语句一般放在函数的最后位置,用于终止函数的执行,并控制程序返回调用该函数时所处的位置。返回时还可以通过 return 语句带回返回值。return语句格式有两种:

(1)return

(2)return (表达式);

        如果 return 语句后面带有表达式,则要计算表达式的值,并将表达式的值作为函数的返回值。若不带表达式,则函数返回时将返回一个不确定的值。通常我们用 return 语句把调用函数取得的值返回给主调用函数。

4.7、函数

4.7.1、函数的定义

函数定义的一般格式如下:

函数类型 函数名(形式参数表) [reentrant][interrupt m][using n] 形式参数说明
{局部变量定义函数体
}

前面部件称为函数的首部,后面称为函数的尾部。

  1. 函数类型:函数类型说明了函数返回值的类型。
  2. 函数名:函数名是用户为自定义函数取的名字以便调用函数时使用
  3. 形式参数表:形式参数表用于列录在主调函数与被调用函数之间进行数据传递的形式参
    数。
  4. reentrant 修饰符:这个修饰符用于把函数定义为可重入函数。所谓可重入函数就是允许被递归调用的函数。函数的递归调用是指当一个函数正被调用尚未返回时,又直接或间接调用函数本身。一般的函数不能做到这样,只有重入函数才允许递归调用。
  5. interrupt m 修饰符:interrupt m 是 C51 函数中非常重要的一个修饰符,这是因为中断函数必须通过它进行修饰。在 C51 程序设计中,当函数定义时用了 interrupt m 修饰符,系统编译时把对应函数转化为中断函数,自动加上程序头段和尾段,并按 51 系统中断的处理方式自动把它安排在程序存储器中的相应位置。

关于重入函数,注意以下几点:

(1)用 reentrant 修饰的重入函数被调用时,实参表内不允许使用 bit 类型的参数函数体内也不允许存在任何关于位变量的操作,更不能返回 bit 类型的值。
(2)编译时,系统为重入函数在内部或外部存储器中建立一个模拟堆栈区,称为重入栈。重入函数的局部变量及参数被放在重入栈中,使重入函数可以实现递归调用。
(3)在参数的传递上,实际参数可以传递给间接调用的重入函数。无重入属性的间接调用函数不能包含调用参数,但是可以使用定义的全局变量来进行参数传递。

关于 interrupt m 中,m 的取值为 0~31,对应的中断情况如下:

0——外部中断 0
1——定时/计数器 T0
2——外部中断 1
3——定时/计数器 T1
4——串行口中断
5——定时/计数器 T2

例子:定义一个返回两个整数的最大值的函数 max()。

int max(int x,int y)
{int z;z=x>y?x:y;return(z);
}

4.7.2、函数的调用与声明

1)函数的调用

函数调用的一般形式如下:

函数名(实参列表);

        对于有参数的函数调用,若实参列表包含多个实参,则各个实参之间用逗号隔开。
按照函数调用在主调函数中出现的位置,函数调用方式有以下三种:

  1. 函数语句。把被调用函数作为主调用函数的一个语句
  2. 函数表达式。函数被放在一个表达式中,以一个运算对象的方式出现。这时的被调用函数要求带有返回语句,以返回一个明确的数值参加表达式的运算。
  3. 函数参数。被调用函数作为另一个函数的参数。
2)自定义函数的声明

在 C51 中,函数原型一般形式如下:

[extern] 函数类型 函数名(形式参数表);

        函数的声明是把函数的名字、函数类型以及形参的类型、个数和顺序通知编译系统,以便调用函数时系统进行对照检查。函数的声明后面要加分号。
如果声明的函数在文件内部,则声明时不用 extern,如果声明的函数不在文件内部,而在另一个文件中,声明时须带 extern,指明使用的函数在另一个文件中。

4.8、C51 构造数据类型

4.8.1、数组

1)一维数组

        一维数组只有一个下标,定义的形式如下:

数据类型说明符 数组名[常量表达式][={初值,初值……}]

各部分说明如下:
(1)“数据类型说明符”说明了数组中各个元素存储的数据的类型。
(2)“数组名”是整个数组的标识符,它的取名方法与变量的取名方法相同。
(3)“常量表达式”,常量表达式要求取值要为整型常量,必须用方括号“[]”括起来。用于说明该数组的长度,即该数组元素的个数。
(4)“初值部分”用于给数组元素赋初值,这部分在数组定义时属于可选项。对数组元素赋值,可以在定义时赋值,也可以定义之后赋值。在定义时赋值,后面须带等号,初值须用花括号括起来,括号内的初值两两之间用逗号间隔,可以对数组的全部元素赋值,也可以只对部分元素赋值。初值为 0 的元素可以只用逗号占位而不写初值 0。

unsigned char x[5];
unsigned int y[3]={1,2,3};

        需要注意的是,C51 语言中数组的下标是从 0 开始的,因此上面第一句定义的 5 个元素分别是:x[0]、x[1]、x[2]、x[3]、x[4]。第二句定义的 3 个元素分别是:y[0]、y[1]、y[2]。赋值情况为:y[0]=1;y[1]=2;y[2]=3。
C51 规定在引用数组时,只能逐个引用数组中的各个元素,而不能一次引用整个数组。但如果是字符数组则可以一次引用整个数组。

2)字符数组

        用来存放字符数据的数组称为字符数组,它是 C 语言中常用的一种数组。字符数组中的每一个元素都用来存放一个字符,也可用字符数组来存放字符串。字符数组的定义下一般数组相同,只是在定义时把数据类型定义为 char 型。

        在 C51 语言中,字符数组用于存放一组字符或字符串,字符串以“\0”作为结束符,只存放一般字符的字符数组的赋值与使用和一般的数组完全相同。对于存放字符串的字符数组。既可以对字符数组的元素逐个进行访问,也可以对整个数组按字符串的方式进行处理。

http://www.dtcms.com/a/532989.html

相关文章:

  • 【代码】八数码难题模板 [宽搜]
  • 震惊!这家滑雪租赁服务商竟让千万用户疯狂抢订!
  • JSP技术入门指南十IDEA开发环境下MySQL数据可视化展示与前后端交互实战
  • 网站建设的思想静态html怎么部署到服务器
  • 餐饮网站建设的目的如何制作境外网站
  • [nanoGPT] configurator.py | exec() globals()
  • 【瑞芯微】【rk3128】【01.使用docker搭建rk3128 编译烧录环境】
  • 企业官网网站优化公司现在网站给源码
  • 知春路网站建设网站建设网络推广柯
  • 002-Spring AI Alibaba Prompt 功能完整案例
  • 网站栏目设计优化方案驾校网站源码下载
  • 大模型-模型压缩:量化、剪枝、蒸馏、二值化 (2)
  • 做文章网站汕尾建设网站首页
  • 数据结构<C++>——链表
  • 数据结构-并查集
  • 该如何建设和优化一个网站网页设计与制作工资多少
  • 建设自己的网站有什么wordpress radiate
  • Peppa Pig - Gardening
  • 蒙文门户网站建设淘宝seo搜索优化工具
  • html基本标签
  • 肇庆网站制作软件枫林seo
  • 网站地址申请极简网站模板
  • 《首屏加载优化手册:Vue3+Element Plus项目提速的技术细节》
  • typora1.9.5安装与激活
  • 自适应网站模板企业网站建设一般步骤
  • 我在高职教STM32(新07)——按键输入实验
  • Rust 与 WebAssembly:构建高效前端应用的全流程复盘
  • 网站百度搜索情况和反链接优化建议哪里有营销型网站最新报价
  • 设计模式-备忘录模式(Memento)
  • 河南建设厅特种工报考网站网站管理与建设总结