2.2 相关C51语言知识 2.2.1 数据类型概述
数据以二进制形式存储在微控制器内。 C51的数据类型如下: 1.基本类型
C51的基本数据类型包括字符型、整型、实数型等,见表2-1。
表2-1 C51的数据类型
注:1.3.4e-38 是 3.4 乘以 10 的负 38 次方,3.4e38 是 3.4 乘以 10 的 38 次方。
2. 位类型是C51语言特有的类型。
2.其他数据类型
除了基本类型之外,还有构造类型(包括数组、结构体和联合体)、指针类型、void类型等。这里只需要了解一点,根据需要详细、深入地研究后续章节。
2.2.2 常量和变量
1.常数
在程序运行期间其值不能改变的量称为常量。
(1) 整数常量。 整数常量通常可以用以下形式表示:
十进制整数:如1234、-5678、0等
十六进制整数:以0x开头的数字是十六进制数。 例如,程序中出现的0x123代表十六进制数123H,相当于十进制数291。x可以是小写,也可以是大写。
(2) 字符常数。 详细信息请参见第 5 章知识链接。
(3) 实常数。 如110.38、3.14e-3等。由于实型数据占用大量存储空间,所以一般最好避免使用实型数据。 本书不提供介绍。
注:符号常量
编程时,经常使用符号来代替常量的值。 这样的符号称为“符号常数”。 符号常量可以使用英文全称或缩写,也可以使用汉语拼音,但不能使用系统关键字。 尽量做到“见名知意”,这样有利于阅读。 符号常量必须用关键字“const”修饰。 定义格式示例如下:
常量=3.14.5926;
编译时,符号量被视为常量,不分配内存空间。 程序执行过程中,遇到该符号常量时,会被替换为定义时的初始值。 因此,声明符号常量时必须赋初值。
符号常量也可以使用宏定义来定义,如下:
#LEDON 0xfe
上述语句是宏定义语句。 编译时,LEDON将被0xfe替换。 这样做的好处是:看名字就知道意思了(比如我们用LEDON表示LED亮了,名字的意思就很明确了。注:本例中0xfe是具体的点亮LED的数据(硬件连接不同数据也不同)。
2.变量
在程序执行期间,变量将占用微控制器存储器(RAM)中的空间。
(1) 变量是在程序执行过程中其值可以改变的量。 C语言程序中的每个变量都必须有一个标识符作为其变量名。 在使用变量之前,必须先定义该变量并指明其数据类型和存储方式,以便编译系统为其分配相应的存储单元。
(2) C51中定义变量的格式如下:
[存储类型] 数据类型 [内存类型] 变量名表;
解释如下:
1)“存储类型”和“存储类型”是可选的。 变量存储有四种类型:自动变量(auto)、全局变量、静态变量()、寄存器()。 详细信息请参见表2-2。
2)变量的数据类型有位(bit)、有符号字符(char)、无符号字符(un-)、有符号整型(int)、无符号整型(int)和浮点型等。
表2-2 4种变量存储类型
(继续)
3)内存类型。 C51 编译器还允许指定变量的内存类型。 该编译器完全支持8051系列单片机的硬件结构,可以访问其硬件系统的所有部分。 每个变量都可以准确地分配其内存类型,从而可以在微控制器系统中准确定位。 如果省略内存类型,则默认将内存类型定义为“日期”类型。 该类型下可直接访问单片机内部数据存储器,访问速度最快。 通常,变量被定义为“日期”类型。
例如:intx,y; 该语句定义了无符号整型变量x和y,省略了存储类型和内存类型。
定义变量的注意事项:
定义变量时,只要取值范围(数值范围)足够,就应该尽量定义和使用较小的数据类型,如char类型、bit类型,因为较小的数据类型占用的内存单元较小。 例如,如果x的值为1,那么当x被定义为类型时,它将占用两个字节的存储空间。 如果定义为type,则只占用1B的存储空间。 如果定义为bit类型,则只占用1bit。 贮存。
51系列单片机是8位机,8位数据操作比16位及更多数据操作要快很多,所以尽量使用char类型。
如果满足需要,尽量使用(即无符号)数据类型,因为单片机处理有符号数据时,需要对符号进行判断和处理,运算速度会变慢。 由于单片机的速度没有PC快,而且单片机是实时工作的,所以任何能够提高效率的措施都必须认真对待。
4) 任何合法的标识符都可以用作变量名。
2.2.3 标识符和关键字
1. 标识符
编程时,标识符用于表示自定义对象名称,其中自定义对象可以是常量、变量、数组、函数、语句标签等。
标识符必须以字母或下划线开头,后面可以是字母、下划线或数字的组合,但长度一般不超过32个字符。 不能使用系统关键字。 例如area、PI、s123、P101p都是合法的标识符。 字符、456P、code-y、a&b 都是非法的。 标识符区分大小写,例如A1和a1代表两个不同的标识符。
为了可读性,标识符应保持简单并且其含义清晰可见。 一般可以使用英文单词缩写、汉语拼音或汉语拼音缩写。
2.关键词
关键字是C51编译器保留的一些特殊标识符,有特定的定义和用法。
单片机C51编程语言继承了ANSIC标准定义的32个关键字,同时结合自身特点扩展了C51中的一些关键字。 详细内容请参见附录。
2.2.4 C51函数介绍[1]
1. 函数的基本类型
它是一段具有特定功能的代码。 功能分类详见表2-3。
表2-3 功能分类
①定义可以理解为具体内容的书写。
2. 功能特点
C51单片机语言支持库函数和自定义函数,这是C51功能强大的直接体现。 库函数添加到头文件中。 开始编程时,包含头文件后,就可以使用头文件中的库函数,这样可以简化代码设计,减少工作量。 使用自定义函数可以使代码结构化和模块化。
在C51语言中,函数的数量没有限制。 但是,这么多的函数,应该从哪个函数执行呢? 在C51语言中,提供了一个特殊的函数,即main函数,也称为主函数。 主函数中还可以调用其他函数(注:为了区分,将主函数之外的其他函数称为子函数)。 其他函数(子函数)可以互相调用,但不能调用主函数。 程序首先从函数的第一条语句开始执行,然后逐条执行。 如果执行过程中遇到调用子函数的语句,就会转到对应的子函数去一一执行子函数内部的语句。 子函数执行完毕后,会返回到原来的调用位置,继续向下执行。
注意:
① 在一个函数体内,不能定义另一个函数,即不能嵌套。
②函数可以调用自身,称为递归调用。
③ 函数之间允许嵌套调用。
④同一个函数可以被一个或多个函数调用任意多次。
2.2.5 单片机C语言程序基本结构
单片机的C语言程序结构清晰、组织清晰。 一般包括六部分,具体见表2-4。
表2-4 单片机C语言基本结构
2.2.6 再讨论局部变量和全局变量
1. 局部变量
函数内定义的变量称为局部变量,仅在该函数的作用域内有效。 仅在调用函数时才为变量分配内存单元,调用完成后回收内存单元。
注意:
① 主函数中定义的变量仅在主函数中有效,在主函数调用的子函数中无效。
② 不同子函数中可以使用同名变量,但代表不同对象,互不干扰。
③函数的形参也是局部变量,只能在函数内部使用。
④{}内的复合语句中可以定义变量,但这些变量只能在该复合语句中使用。
2.全局变量
一个源文件可以包含一个或多个函数。 在函数外部定义的变量称为全局变量,全局变量可供源文件中的所有函数使用。
注意:
①函数既可以使用函数内定义的局部变量,也可以使用函数外定义的全局变量。
②如果不是绝对必要,尽量少用全局变量。 原因是: a) 全局变量在整个程序执行过程中占用存储单元,而不是像局部变量那样只在需要时占用存储单元。 b) 全局变量会降低函数的通用性。 我们在编写函数的时候,都希望函数具有良好的可移植性,以便其他程序能够方便地使用。 c) 使用过多的全局变量会降低整个程序的清晰度。 因为在调试程序时,如果某个全局变量的值与你预期的不一样,你无法快速判断是哪个函数出现了问题。
③在同一个源文件中,如果全局变量和局部变量同名,则全局变量会被阻塞在局部变量的作用域内。
2.2.7 C语言中的算术运算符和算术表达式
C语言中运算符的范围非常广泛。 除了控制语句和输入输出语句之外,大多数基本操作都是由运算符处理的。 运算符有很多,其中算术运算符和算术表达式的知识详见表2-5。
表2-5 C语言算术运算符和算术表达式
2.2.8 关系运算符和关系表达式
1.关系运算符
C语言总共提供了6个关系运算符,具体见表2-6。
2. 关系表达式
用关系运算符连接两个操作数形成的公式称为关系表达式,如a+b>b+c、a==b
注:如果关系表达式为真,则表达式的值为 1;否则,表达式的值为 1。 如果不成立,则表达式的值为 0。
例如,表达式“a=c>b”的理解为:当c的值大于b的值时,关系表达式“c>b”的值为1,将该值赋给a,所以a的值为1,否则,如果c的值小于b,则“c
表2-6 C语言关系运算符
2.2.9 自增和自减运算符
自增和自减运算符的作用是将变量的值增加或减少1。详细信息请参见表2-7。
表 2-7 自增和自减运算符
2.2.10 单片机的几个周期介绍
学习单片机需要掌握时钟周期、机器周期和指令周期三个概念。 详细信息请参见表2-8。
表2-8 单片机的时钟周期、机器周期和指令周期
2.2.11 while循环语句和for循环语句
1.while语句是循环语句
while 语句的形式为:
while(条件表达式)
{几个程序语句;}
while语句的执行过程是:判断()中的条件表达式是否为真。 如果不为true(即表达式的值为0),则不会执行{}内的语句,直接跳转到{}后面的语句。 如果表达式为 true(即表达式的值为 1),则执行 {} 内的每个程序语句。 执行完成后,返回判断条件表达式是否为真。 如果仍然为 true,则执行 {} 内的语句。 如果不成立,则执行{}后面的语句,如图2-3所示。
图2-3 while循环语句执行流程图
【范例】使用while循环语句编写延时函数,如下:
执行过程为:首先判断i>0是否成立。 只要为真,就执行i=i-1; 直到i减小到0,如果i>0不成立,则跳出循环。 如果不成立,则执行以下语句。 这就开始了延迟效果来了。
注意:赋给变量的值必须在变量类型的取值范围内,否则会出错。 例如第02行,如果写i=70000,就会出错,因为i是int类型变量,取值范围是0~65535。 给它赋值70000,超出了取值范围。
while语句()中的条件表达式可以是常量(如1),也可以是运算,也可以是有返回值的函数。
2.for循环语句
for循环语句的一般结构为:
for(为循环变量分配初始值;条件表达式;增加或减少循环变量)
{几个程序语句;}
执行过程为:
第一步:给循环数量赋一个初始值。
步骤2:判断条件表达式是否为真。 如果不为true,则不执行{}内的语句,直接跳出for循环,执行{}之后的语句。 如果条件表达式为真,则执行{}内的程序语句。 执行完成后,返回for后面的(),进行循环变量的递增或递减,然后判断条件表达式是否为真。 如果没有,就跳出去。 for循环语句执行{}后面的语句。 如果为 true,则执行 {} 内的语句。 这个循环一直持续到循环爆发为止,如图2-4所示。
图2-4 for循环语句执行流程图
注意:for循环语句的{}内的语句可以为空。 这种情况下不需要写{},即for循环语句可以写为:for(给循环变量赋初值;条件表达式;增加或减少循环变量); 注意:分号不能少。
例如,可以使用for循环语句编写延迟函数,如下所示:
执行过程为:首先给i赋一个初始值,然后判断是否i>0为真。 如果不成立,则跳出for循环。 如果为true,由于后面没有{}内容,所以省略了执行{}内语句的过程,然后再次执行i--,然后判断i>0是否成立...直到i= 0(i需要执行3000次才能递减i),i>0不会成立,会跳出for循环,起到延时作用。 。
2.2.12 如何编写和调用无参数和带参数函数
1.如何编写和调用无参函数
如果程序中多次使用某些语句,并且语句的内容完全相同,则可以将这些语句写成不带参数的子函数。 当主函数中需要这些语句时,直接调用该子函数即可。 就是这样。 例如:1s的延时子函数如下:
执行过程:首先执行第03行,从x=1000开始,x>0为真,所以执行第04行(即y从110逐渐减1,直到减到0,大约需要1ms)。 执行完第04行后,再次执行x--,x的值变为999,然后判断x>0是否成立。 结果为真,于是再次执行第04行(耗时约1ms),然后再次执行x--,继续循环。 x每减1,y就会从110逐渐减1,直到减到0。X总共减1000次,所以大约需要1秒。
注意:子函数可以写在主函数之前或之后,但不能写在主函数内部。 如果写在main函数之后,需要在main函数之前声明。
声明的方法为:返回值属性函数名()。
如果函数没有参数,则 () 为空。 如果函数有参数,则参数的类型必须按顺序写在()中。
【应用举例】 使用调用延时子函数的方法编写程序,使图2-1中的发光二极管(LED)D1每600ms闪烁一次。
2. 如何编写带参数的函数
如果程序中需要不同的延迟时间,就需要编写多个不同的延迟函数。 上述子函数不带参数使用起来很不方便。 在这种情况下,最好使用带参数的子函数。 经典流程如下:
【应用示例】 详情请参见2.3节。