1、#*PIC 单片机 C 语言入门1.2.1 认识 PIC 单片机 C 语言用 C 语言来开发单片机系统软件最大的好处是编写代码效率高。软件调试直观、维护升级方便、代码的重复利用率高、便于跨平台的代码移植等等,因此 C 语编程在单片机系统设计中已得到越来越广泛的应用。但在单片机上用 C 语言写程序和在 PC 机上写程序绝对不能简单等同。珊在的 PC 机资源十分丰富,运算能力强大,因此,程序员在写 PC 机的应用程序时,几乎不用关心编译后的可执行代码在运行过程中需要占用多少系统资源,也基本不用担心运行效率有多高。写单片机的 C 语言程序最关键的一点是单片机内的资源非常有限,控制的实时性要求又很高,
2、因此,如果没有对单片机体系结构和硬件资源作详尽的了解,以笔者的愚见认为是无法写出高质量实用性很高的 C 语程序。本书围绕 PIC 中档系统单机来展开讨论的,Microchip 公司自己没有针对 PIC 中档系列单片机的 C 语言编译器,但很多专业的第三方公司有众多支持 PIC 单片机的 C 语言编译器提供,常见的有 Hitech、CCS、IAR、Bytecraft 等公司。其中笔者最常用的是 Hitech 公司的 PICC 编译器,它稳定可靠,编译生成的代码效率高,在用 PIC 单片机进行系统设计和开发的工程师群体中得到广泛认可。其正式完全版软件需要购置,但在其网站上有限时的试用版供用户评估。
3、另外,Hitech 公司针对广大 PIC 的业余爱好者和初学者,还提供了完全免费的学习版 PICC-Lite 编译器套件。它的使用方式与完全版相同,只是支持的 PIC 单片机型号限制在 PIC16F84、PIC16F877 和 PIC16F628 等几款。这几款 Flash 型的单片机因其所具备的丰富的片上资源而最适用于单片机学习入门,因此,笔者建议感兴趣的读者可以从 PICC-Lite 入手掌握 PIC 单片机的 C 语言编程。我们一起来认识训练项目 2跑马灯的仿真与演示的程序 LEDS.C。1 #include“pic.h“2 int a; /定义整型变量 a3 void main( )4
4、 TRISB=0X00; /定义端口 B 为输出模式5 while(1) /while 循环语句6 PORTB=0XFE; /1111 1110 7 for(a=1000;a0;a-); /for 循环语句8 PORTB=0XFD; /1111 11019 for(a=1000;a0;a-); /for 循环语句10 PORTB=0XFB; /1111 101111 for(a=1000;a0;a-); /for 循环语句12 PORTB=0XF7; /1111 011113 for(a=1000;a0;a-); /for 循环语句14 PORTB=0XEF; /1110 111115 for
5、(a=1000;a0;a-); /for 循环语句16 PORTB=0XDF; /1101 111117 for(a=1000;a0;a-); /for 循环语句18 PORTB=0XBF; /1011 111119 for(a=1000;a0;a-); /for 循环语句20 PORTB=0X7F; /0111 111121 for(a=1000;a0;a-); /for 循环语句22 #*23 说明:第 1 行:#include“pic.h“是文件包含语句,表示把语句中指定文件的全部内容复制到此处,与当前的源程序文件链接成一个源文件。该语句中指定的文件 pic.h 是 Hitech-PIC
6、C 编译器提供的头文件,保存在“C:HT-PICinclude”路径下,该文件包含了对 PIC 单片机特殊功能寄存器 SFR 和位名称的定义。在编写每一个程序时,首先编写的一条语句就是#include“pic.h“。第 2 行:int a。是定义一个整型变量 a,具体什么是整型变量?有何功能?请看数据与数据类型号中的介绍。第 3 行:void main( )。定义主函数 void main( )。其是 C 语言程序中必不可少的主函数,也是程序开始执行的第一个函数。函数一般分成两个部分,一个部分为函数名:main,另一个部分为函数体:从第 4 行的到 23 行的 为止。第 4 行:TRISB=0
7、X00。是给单片机的 B 端口方向寄存器赋值,使 B 端口数据方向为输出方向,具体 TRISB 寄存器功能请查看【知识链接 3】PIC 单片机 I/O 接口及其应用。第 5 行:while(1)。是 while( ) 循环语句。第 6 行:PORTB=0XFE。是给单片机的 B 端口寄存器赋值,使 B 端口寄存器为 0XFE,具体PORTB 寄存器功能请查看【知识链接 3】PIC 单片机 I/O 接口及其应用。第 7 行:for(a=1000;a0;a-)。是 for 循环语句。第 821 行都与第 6、7 行相类似,在此就不再重复,int 、while、for 等 C 语言知识接下将一一介绍
8、。1.2.2 数据与数据类型数据是计算机的对象,任何程序设计都要进行数据处理。具有一定格式的数字或数值称为数据,数据的不同格式称为数据类型。在 C 语言中,数据类型可分为:基本数据类型、构造数据类型、指针类型、空类型四大类,如图 1-2-1 所示。图 1-2-1 C 语言的数据类型在进行 PIC 单片机程序设计时,支持的数据类型与编译器相关。Hitech-PICC 编译器所支持的数据类型如表 1-2-1 所示,其遵循 Little-endian 标准,多字节变量的低字节放在存储空间的低地址,高字节放在高地址。数据类型单精度型(float)双精度型(double)整型(int)实型(浮点型)字符
9、型(char)基本类型构造类型枚举类型号(enum)数组类型结构体类型(struct)共用体类型(union)指针类型空类型(void)#*表 1-2-1 Hitech-PICC 编译器所支持的数据类型序号 数据类型 名称 长度(位数) 值 域1 位类型 bit 1B 0 或 1 2 有符号字符型 char 8B -128 +1273 无符号字符型 unsigned char 8B 0 2554 有符号整型 short 16B -32768 +327675 无符号整型 unsigned short 16B 0 655356 有符号整型 int 16B -32768 +327677 无符号整型
10、unsigned int 16B 0 655358 有符号长整型 long 32B -2 147 483 648 +2 147 483 6479 无符号长整型 unsigned long 32B 0 4294 967 29510 浮点型 float 24B -2 147 483 648 +2 147 483 64711 双精度浮点型 double 24B 或 32B-8 388 608 +8 388 607 或-2 147 483 648 +2 147 483 647注:Hitech-PICC 编译器缺省认定 double 型变量为 24 位长,但可以改变编译选项改成 32 位长1.2.3 常
11、量与变量单片机程序中处理的数据有常量和变量两种形式,二者的区别在于:常量的值在程序执行期间是不能发生变化的,而变量的值在程序执行期间可以发生变化。1常量在程序运行的过程中,其值不能改变的量称为常量。常量的数据类型有整型、浮点型、字符型和字符串型。(1)整型常量可以表示为十进制数、十六进制数或八进制数等,例如:十进制数 12、-60 等;十六进制数以 0x 开关,如 0x13、0xAB 等;八进制数以字母 o 开关,如 o14、o17 等。若要表示长整型,就在数字后面加字母 L,如 104L、o34L、0Xf340L 等。(2)浮点型常量可以分为十进制表示形式和指数表示形式两种,如0.888、3
12、345.345、123e3、-2.4e-2 等。(3)字符型常量是用单引号括起来的单一字符,如a 、 9等。(4)字符串型常量是用双引号括起来的一串字符,如“test” 、 “ok”等。字符串是由多个字符连接起来组成的。2变量在程序运行中,其值可以改变的量称为变量。一个变量主要由两部分构成;一个是变量名,一个是变量值。每个变量都有一个变量名,在内存中占据一定的存储单元(地址) ,并在该内存单元中存放该变量的值。下例为对符号常量和变量进行说明:1 #define CONST 602 void main( )3 int variable, result;4 variable = 20;5 resu
13、lt = variable * CONST;6 第 1 行:#define CONST 60。这一行定义了一个符号常量 CONST,其值为 60。这样在后面的#*程序中,凡是出现 CONST 的地方,都代表常量 60。第 2 行:variable 和 result 就是变量。它们的数据类型为整型(int) 。注意:符号常量与变量的区别在于,符号变量的值在作用域(本例中为主函数)中,不能改变,也不能用等号赋值,习惯上,总将符号常量名用大写,变量名用小写,以示区别。(1)变量的定义变量必须先定义后使用,用标识符作为变量名,并指出所用的数据类型和存储模式,这样编译系统才能为变量分配相应的存储空间。变
14、量的定义格式如下:存储种类 数据类型 存储器类型 变量名表;其中,数据类型和变量名表是必须的,存储种类和存储类型是可选项。存储种类有四种:auto(自动变量) 、extern(外部变量) 、static(静态变量)和 register(寄存器变量) 。默认类型为 auto(自动变量) 。例如:int a ; /*定义 a 为整型变量*/int m,n; /*定义 m 和 n 为整型变量 */float x,y,z; /*定义 x,y,z 单精度实型变量*/char ch; /*定义 ch 为字符变量*/long int t; /*定义 t 为长整型变量*/static int r; /*定义
15、r 为静态的整型变量*/进行变量定义时,应注意以下几点: 允许在一个数据类型标识符后,说明多外相同类型的变量,各变量名之间用逗号隔开; 数据类型标识符与变量名之间至少用一个空格隔开; 最后一个变量名后必须以分号“;”结尾; 变量说明必须放在变量使用之前,一般放在函数体的开头部分; 在同一个程序中变量不允许重复定义。例如:int x,y,z;int a,b,x; /*变量 x 被重得定义*/(2)变量的初始化在定义变量的同时可以给变量赋初值,称为变量的初始化。变量初始化的一般格式为:数据类型标识符 变量名 1=常量 1,变量名 2=常量 2,变量名 n=常量 n;例如:int m=3,n=5;
16、/*定义 m 和 n 为整型变量,同时 m,n 分别赋初值 3,5*/float x=0,y=0,z=0; /*定义 x,y,z 为单精度实型变量,同时 x,y,z 都赋初值为 0*/char ch=a; /*定义 ch 为字符型变量,同时赋初值字符a*/long int a=1000,b; /*定义 a,b 为长整型变量,同时 a 赋初值 1000*/(3)整型变量整型变量的基本类型符为 int,可以根据数值的范围将整型变量定义为基本整型变量、短整型变量或长整型变量。 基本整型变量用 int 表示; 短整型变量用 short int 表示(或用 short)表示; 长整型变量用 long i
17、nt 表示(或用 long)表示。#*在计算机(或单片机)中数据是以二进制形式表示的,基本整型变量占用内存 2 个字节,即16 位。16 位中最左边的一位表示符号,该位 0,表示正数;该位 1,表示负数。一个基本整型变量的值范围为-215(215-1) ,即-32 76832 767。实际应用时注意整型变量的值不能超出范围,否则容易出错,为了充分利用变量表示数据的范围,可以将变量定义为“无符号”类型。归纳起来,可以使用以下 6 种整型变量:带符号基本整型变量 signed int无符号基本整型变量 unsigned int带符号短整型变量 signed short int无符号短整型变量 un
18、signed short int带符号长整型变量 signed long int无符号长整型变量 unsigned long int如果在定义整型变量时不指定 unsigned,则隐含为有符号(signed) 。假设定义两个变量 a 和 b 如下:int a;usigned int b;则变量 a 的取范围为了-32 76832 767。而变量 b 的取值范围为 065 535。图 1-2-2(a)表示有符号的基本整型变量 a 的最大值(32 767) ,图 1-2-2(b)表示无符号的基本整型变量 b 的最大值(65 535) 。0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
19、(a)有符号整型变量1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1(b)无符号整型变量图 1-2-2 变量 a 和 b 的最大值(4)实型变量实型数据与整型数据在内定中的存储方式不同,实型数据是按照指数形式存放的,系统把一个实数分成小数和指数两个部分分别进行存放。指数部分采用规范化的指数形式,例如:-21.34638在内存中的存放形式如图 1-2-3 所示。实型号变量分为单精度( float) 、双精度(double)和长双精度(long double) ,但 Hitech-PICC 编译器只支持单精度(float)和双精度(double)两种,具体规则如表 1-2-1 所示。
20、- .2134638 2图 1-2-3 实数在内存中的存放形式(5)字符型变量字符型变量用来存放字符常量,注意只能存放一个字符。例如:=32 767符号位表示数据的最高位=65 535符号 小数部分 指数- .2134638 2#*char c1,c2,c3,c4,c5; /定义 5 个字符变量c1=a; /正确c2=”a”; /不正确c3=abc; /不正确c4=107; /正确c5=6; /正确将一个字符常量放到一个字符变量中,实际上并不是把该字符本身放到内存单元中去,而是将该字符的相应的 ASCII 代码放到存储单元中。例如:char c1,c2; /定义 2 个字符变量c1=a;c2=
21、b; /给字符变量 c1 赋值为 a、c2 赋值为b字符a的 ASCII 代码为十进制数 97,字符b的 ASCII 代码为十进制数 98,在内存中变量c1、c2 的实际上是以二进形式存放的。(6)位变量bit 型位变量只能是全局的或静态的。PICC 将把定位在同一 bank 内的 8 个位变量合并成 1 个字节存放于一个固定地址。因此,所有针对位变量的操作将直接使用 PIC 单片机的位操作汇编指令高效实现。基于此,位变量不能是局部自动型变量,也无法将其组合成复合型号高级变量。1.2.5 运算符和表达式数据处理是程序的核心部分。在数据处理中,各种运算又是最主要的部分。C 语言不仅数据类型丰富,
22、而且运算符也十分丰富,几乎所有的操作都可以用运算符来处理。由运算符加适当的运算对象(常量、变量、函数等)可构成表达式,而表达式是 C 语言的重要要素之一,因此掌握好运算符的使用对编写程序十分重要。对于每一个运算符,要注意从两个方面去把握:运算符的优先级和运算符的结合性。运算符的优先级指多个运算符用在同一个表达式中时先进行什么运算,后进行什么运算;而运算的结合性是指运算符所需要的数据是从左边开始取还是从右边开始取,因而有所谓“左结合性”和“右结合性”之说。C 语言提供了丰富的运算符,它们能构成多种表达式,处理不同的问题,从而使 C 语言的运算功能十分强大。另外 C 语言的运算符可以分为 12 类
23、,如表 1-2-3 所示。表 1-2-3 C 语言的运算符运算符名 运算符算术运算符 + - * / % + -关系运算符 = j=+i; /j=101,i=101j=i+; /j=101,i=102j=-i; /j=101,i=101j=i-; /j=101,i=100编程时常将“+” 、 “-”这两个运算符用于循环语句中,使循环变量自动 1;也常用于指针变量,使指针自动加 1 指向下一个地址。3赋值运算符与赋值表达式赋值运算符“=”的作用变是给变量赋值,如“x=10;” 。用赋值运算符将一个变量与一个表达式连接起来 的式子称为赋值表达式,在表达式后面加“;”便构成了赋值语句。赋值勤语句的格
24、式如下。#*变量 = 表达式;例如:k=0xff; /将十六进制数 FFH 赋予变量 kb=c=33; /将 33 同时赋予变量 b 和 cd=e; /将变量 e 的值赋予变量 df=a+b; /将表达式 a+b 的值赋予变量 f由此可见,赋值表达式的功能是计算表达式的值再赋予左边的变量。赋值运算符具有右结合性,因此有下面的语句:a=b=c=5;可以理解为:a=(b=(c=5);按照 C 语言的规定上,任何表达式在其末尾加上分号就构成语句。因此“x=8; ”和“a=b=c=5;”都是赋值语句。如果赋值运算符两过的数据类型号不相同,系统将自动进行类型转换,即把赋值右边的类型换成左边的类型。具体规
25、定如下: 实型赋给整型,舍去小数部分。 整型赋给实型,数值不变,但将以浮点开式存放,即增加小数部分(小数部分的值为 0) 。 字符型赋给整型,由于字符型为 1 字节,而整型 2 字节,故将字符 ASCII 码值放到整型量的低 8 位中,高 8 位为 0。 整型赋给字符型,只把低 8 位赋给字符量。图 1-2-4 表示了各数据类型自动转换的规则。图 1-2-4 各种数据类型的高低顺序在 C 语言程序设计中,经常使用复合赋值运算符对变量进行赋值。复合赋值运算符就是在赋值符“= ”之前加上其他运算符,表 1-2-4 中优先级 14 就是复合赋值运算符。构成复合赋值表达式的一般形式为:变量 双目运算符
26、 = 表达式;它等效于:变量 = 变量 运算符 表达式;例如:a+=5; /相当于 a=a+5;x*=y+7; /相当于 x=x*(y+7);r%=p; /相当于 r=r%p;在程序中使用复合赋值运算符,可以简化程序,有利于编译处理,提高编译效率并产生质量较高的目标代码。4关系运算符与赋值表达式在前面介绍过的分支选择程序结构中,经常需要比较两个变量的大小关系,以决定程序下一步的操作。比较两个数据量的运算符称为关系运算符。C 语言提供了 6 种关系运算符: 大于运算符: ;低int unsigned long doubleunsigned longshor、char float高#* 大于等于运
27、算符: =; 小于运算符: 、=的优先级相同,=和!= 优先级相同;前者优先级高于后者。例如:“a=bc;”应理解为 “a=(bc);”。关系运算符优先级低于算术运算符,高于赋值运算符。例如:“a+bc+d;”应理解为 “(a+b)(c+d);”。关系表达式是用关系运算符连接的两个表达式。它绵一般形式为:表达式 关系运算符 表达式关系表达式的值只有 0 和 1 两种,即逻辑的“真”与“假” 。当指定的条件满足时,结果为1,不满足时结果为 0。例如表达式“50;”的值为“真” ,即结果为 1;而表达式“(a=3)(b=5);”由于 35 不成立,故其值为“假” ,即结果为 0。a+bc /若 a
28、=1,b=2,c=3,则表达式的值为 0(假)a3/2 /若 x=2,则表达式的值为 1(真)c=5 /若 c=1,则表达式的值为 0(假)5关系运算符与赋值表达式C 语言中提供了三种逻辑运算符,一般形式有以下三种。 逻辑与运算符: &; 逻辑或运算符: |; 逻辑非运算符: !;逻辑表达式的一般形式有以下三种。逻辑与:条件式 1&条件式 2逻辑或:条件式 1|条件式 2逻辑非:!条件式“&”和“|” 是双目运算符,要求有两个运算对象,结合方向是从左到右。 “!”是单目运算符,只要求一个运算对象,结合方向是从右至左。(1)逻辑与:a&b,当且仅当两个运算量的值都为“真”时,运算结果为“真” ,否则为“假” 。(2)逻辑或:a|b,当且仅当两个运算量的值都为 “假”时,运算结果为“假” ,否则为“真” 。(3)逻辑非:!a,当运算量的值为“真”时,运算结果为 “假” ;当运算量的值为“假”时,运算结果为“真” 。表 1-2-6 给出了执行逻辑运算的结果条件 1 条件 2 逻辑运算