第十一章PIC单片机的C语言编程 11PIC单片机C语言编程简介 用C语言来开发单片机系统软件最大的好处是编写代码效率高、软件调试直观、维护 升级方便、代码的重复利用率高、便于跨平台的代码移植等等,因此C语言编程在单片机 系统设计中已得到越来越广泛的运用。针对PC单片机的软件开发,同样可以用C语言实 但在单片机上用C语言写程序和在PC机上写程序绝对不能简单等同。现在的PC机资 源十分丰富,运算能力强大,因此程序员在写PC机的应用程序时几乎不用关心编译后的可 执行代码在运行过程中需要占用多少系统资源,也基本不用担心运行效率有多高。写单片机 的C程序最关键的一点是单片机内的资源非常有限,控制的实时性要求又很高,因此,如 果没有对单片机体系结构和硬件资源作详尽的了解,以笔者的愚见认为是无法写出高质量实 用的C语言程序。这就是为什么前面所有章节中的的示范代码全部用基础的汇编指令实现 的原因,希望籍此能使读者对PC单片机的指令体系和硬件资源有深入了解,在这基础之 上再来讨论C语言编程,就有水到渠成的感觉。 本书围绕中档系列PC单片机来展开讨论, Microchip公司自己没有针对中低档系列PIC 单片机的C语言编译器,但很多专业的第三方公司有众多支持PC单片机的C语言编译器 提供,常见的有 Hitech、CCS、IAR、 Bytecraft等公司。其中笔者最常用的是 Hitech公司的 PICC编译器,它稳定可靠,编译生成的代码效率高,在用PC单片机进行系统设计和开发 的工程师群体中得到广泛认可。其正式完全版软件需要购置,但在其网站上有限时的试用版 供用户评估。另外, Hitech公司针对广大PC的业余爱好者和初学者还提供了完全免费的学 习版PICC-Lite编译器套件,它的使用方式和完全版相同,只是支持的PIC单片机型号限制 在PC16F84、PICl6F877和PICl6F628等几款。这几款 Flash型的单片机因其所具备的丰富 的片上资源而最适用于单片机学习入门,因此笔者建议感兴趣的读者可从 PICC-Lite入手掌 握PC单片机的C语言编程。 在此列出几个主要的针对PC单片机的C编译器相关连接网址,供读者参考 Hitech-PICC: www.htsoft.com LAR: wwwlar.com www.ccsinfo.com/picc.shtml Byte Craft: www.bytecraft.com/mpccaps.html 本章将介绍 Hitech-PlCC编译器的一些基本概念,由于篇幅所限将不涉及C语言的标准 语法和基础知识介绍,因为在这些方面都有大量的书籍可以参考。重点突出针对PIC单片 机的特点而所需要特别注意的地方 112 Hitech-PICC编译器 PICC基本上符合ANSI标准,除了一点:它不支持函数的递归调用。其主要原因是因 为PC单片机特殊的堆栈结构。在前面介绍PIC单片机架构时已经详细说明了PIC单片机 张明峰2004-4-7于上海1of26
张明峰 2004-4-7 于上海 1 of 26 第十一章 PIC 单片机的 C 语言编程 11.1 PIC 单片机 C 语言编程简介 用 C 语言来开发单片机系统软件最大的好处是编写代码效率高、软件调试直观、维护 升级方便、代码的重复利用率高、便于跨平台的代码移植等等,因此 C 语言编程在单片机 系统设计中已得到越来越广泛的运用。针对 PIC 单片机的软件开发,同样可以用 C 语言实 现。 但在单片机上用 C 语言写程序和在 PC 机上写程序绝对不能简单等同。现在的 PC 机资 源十分丰富,运算能力强大,因此程序员在写 PC 机的应用程序时几乎不用关心编译后的可 执行代码在运行过程中需要占用多少系统资源,也基本不用担心运行效率有多高。写单片机 的 C 程序最关键的一点是单片机内的资源非常有限,控制的实时性要求又很高,因此,如 果没有对单片机体系结构和硬件资源作详尽的了解,以笔者的愚见认为是无法写出高质量实 用的 C 语言程序。这就是为什么前面所有章节中的的示范代码全部用基础的汇编指令实现 的原因,希望籍此能使读者对 PIC 单片机的指令体系和硬件资源有深入了解,在这基础之 上再来讨论 C 语言编程,就有水到渠成的感觉。 本书围绕中档系列 PIC 单片机来展开讨论,Microchip 公司自己没有针对中低档系列 PIC 单片机的 C 语言编译器,但很多专业的第三方公司有众多支持 PIC 单片机的 C 语言编译器 提供,常见的有 Hitech、CCS、IAR、Bytecraft 等公司。其中笔者最常用的是 Hitech 公司的 PICC 编译器,它稳定可靠,编译生成的代码效率高,在用 PIC 单片机进行系统设计和开发 的工程师群体中得到广泛认可。其正式完全版软件需要购置,但在其网站上有限时的试用版 供用户评估。另外,Hitech 公司针对广大 PIC 的业余爱好者和初学者还提供了完全免费的学 习版 PICC-Lite 编译器套件,它的使用方式和完全版相同,只是支持的 PIC 单片机型号限制 在 PIC16F84、PIC16F877 和 PIC16F628 等几款。这几款 Flash 型的单片机因其所具备的丰富 的片上资源而最适用于单片机学习入门,因此笔者建议感兴趣的读者可从 PICC-Lite 入手掌 握 PIC 单片机的 C 语言编程。 在此列出几个主要的针对 PIC 单片机的 C 编译器相关连接网址,供读者参考: Hitech-PICC: www.htsoft.com IAR: www.iar.com CCS: www.ccsinfo.com/picc.shtml ByteCraft: www.bytecraft.com/mpccaps.html 本章将介绍 Hitech-PICC 编译器的一些基本概念,由于篇幅所限将不涉及 C 语言的标准 语法和基础知识介绍,因为在这些方面都有大量的书籍可以参考。重点突出针对 PIC 单片 机的特点而所需要特别注意的地方。 11.2 Hitech-PICC 编译器 PICC 基本上符合 ANSI 标准,除了一点:它不支持函数的递归调用。其主要原因是因 为 PIC 单片机特殊的堆栈结构。在前面介绍 PIC 单片机架构时已经详细说明了 PIC 单片机
中的堆栈是硬件实现的,其深度已随芯片而固定,无法实现需要大量堆栈操作的递归算法; 另外在PlC单片机中实现软件堆栈的效率也不是很高,为此,PICC编译器采用一种叫做“静 态覆盖”的技术以实现对C语言函数中的局部变量分配固定的地址空间。经这样处理后产 生出的机器代码效率很高,按笔者实际使用的体会,当代码量超过4K字后,C语言编译出 的代码长度和全部用汇编代码实现时的差别已经不是很大(<10%),当然前提是在整个C 代码编写过程中须时时处处注意所编写语句的效率,而如果没有对PIC单片机的内核结构 各功能模块及其汇编指令深入了解,要做到这点是很难的。 113 MPLAB-IDE内挂接PCC PICC编译器可以直接挂接在 MPLAB-IDE集成开发平台下,实现一体化的编译连接和 原代码调试。使用 MPLAB-IDE内的调试工具ICE2000、ICD2和软件模拟器都可以实现原 代码级的程序调试,非常方便 首先必须在你的计算机中安装PICC编译器,无论是完全版还是学习版都可以和 MPLAB-IDE挂接。安装成功后可以进入IDE,选择菜单项 Project> Set language tool Locations.,打开语言工具挂接设置对话框,如图11-1所示: Reseed Tools CLsC Ccee to P02/14/16/18 H-TEO PCC Todman FC Lrde ltee eel Deai Seych Fys 6 DecT HLTEO ACc10 Tonio VEVTPEB NAECE Caree 图11-1 MPLAB-IDE语言工具设置对话框 在对话框中选择“H- TECH PICC Toolsuite”栏,展开可执行文件组“ Executable”后, 列出了将被 MPLAB-IDE后台调用的编译器所用到的所有可执行文件,其中有汇编编译器 “ PICC ASSembler”、C原程序编译器“ PICC Compiler”和连接定位程序“ PICC LInker”。同 时在此列表中还显示了对应的可执行程序名,请注意在这里都是“ PICC. EXE”。用鼠标分别 点击选中这三项可执行文件,观察对话框下面“ Location”一栏中显示的文件路径,用 Browse.”按纽,从计算机中已经安装的PICC编译器文件夹中选择 PICCEXE文件。实 际上 PICCEXE只是一个调度管理程序,它会按照所输入的文件扩展名自动调用对应的编译 器和连接器,用户要注意的是C语言原程序扩展名用“c”,汇编原程序用“as”即可。 工具挂接完成后,在建立项目时可以选择语言工具为“H- TECH PICC”,具体步骤可以 参阅第三章3.1.3节,此处不再重复。项目建立完成后可以加入C或汇编原程序,也可以加 入已有的库文件或已经编译的目标文件。最常见的是只加入C原程序。用C语言编程的好 张明峰2004-4-7于上海2of26
张明峰 2004-4-7 于上海 2 of 26 中的堆栈是硬件实现的,其深度已随芯片而固定,无法实现需要大量堆栈操作的递归算法; 另外在 PIC 单片机中实现软件堆栈的效率也不是很高,为此,PICC 编译器采用一种叫做“静 态覆盖”的技术以实现对 C 语言函数中的局部变量分配固定的地址空间。经这样处理后产 生出的机器代码效率很高,按笔者实际使用的体会,当代码量超过 4K 字后,C 语言编译出 的代码长度和全部用汇编代码实现时的差别已经不是很大(<10%),当然前提是在整个 C 代码编写过程中须时时处处注意所编写语句的效率,而如果没有对 PIC 单片机的内核结构、 各功能模块及其汇编指令深入了解,要做到这点是很难的。 11.3 MPLAB-IDE 内挂接 PICC PICC 编译器可以直接挂接在 MPLAB-IDE 集成开发平台下,实现一体化的编译连接和 原代码调试。使用 MPLAB-IDE 内的调试工具 ICE2000、ICD2 和软件模拟器都可以实现原 代码级的程序调试,非常方便。 首先必须在你的计算机中安装 PICC 编译器,无论是完全版还是学习版都可以和 MPLAB-IDE 挂接。安装成功后可以进入 IDE,选择菜单项 Project Æ Set Language Tool Locations…,打开语言工具挂接设置对话框,如图 11-1 所示: 在对话框中选择“HI-TECH PICC Toolsuite”栏,展开可执行文件组“Executable”后, 列出了将被 MPLAB-IDE 后台调用的编译器所用到的所有可执行文件,其中有汇编编译器 “PICC Assembler”、C 原程序编译器“PICC Compiler”和连接定位程序“PICC Linker”。同 时在此列表中还显示了对应的可执行程序名,请注意在这里都是“PICC.EXE”。用鼠标分别 点击选中这三项可执行文件,观察对话框下面“Location”一栏中显示的文件路径,用 “Browse…”按纽,从计算机中已经安装的 PICC 编译器文件夹中选择 PICC.EXE 文件。实 际上 PICC.EXE 只是一个调度管理程序,它会按照所输入的文件扩展名自动调用对应的编译 器和连接器,用户要注意的是 C 语言原程序扩展名用“.c”,汇编原程序用“.as”即可。 工具挂接完成后,在建立项目时可以选择语言工具为“HI-TECH PICC”,具体步骤可以 参阅第三章 3.1.3 节,此处不再重复。项目建立完成后可以加入 C 或汇编原程序,也可以加 入已有的库文件或已经编译的目标文件。最常见的是只加入 C 原程序。用 C 语言编程的好 图 11-1 MPLAB-IDE 语言工具设置对话框
处是可以实现模块化编程。程序编写者应尽量把相互 PC68 agu 独立的控制任务用多个独立的C原程序文件实现,如 果程序量较大,一般不要把所有的代码写在一个文件 Seres Files 内。图112列出的是笔者建立的一个项目中所有C原 68. 程序模块,其中主控、数值计算、IC总线操作、命令 IC59 EEL C 按键处理和液晶显示驱动等不同的功能分别在不同的 petaled e Header Filas 独立的原程序模块中实现。 Librry Files 图11-2C语言多模块编程 114PIC单片机的C语言原程序基本框架 基于PICC编译环境编写PC单片机程序的基本方式和标准C程序类似,程序一般由以 下几个主要部分组成 ●在程序的最前面用# nclude预处理指令引用包含头文件,其中必须包含一个编译器 提供的“pich”文件,实现单片机内特殊寄存器和其它特殊符号的声明 用“ CONFIG”预处理指令定义芯片的配置位 声明本模块内被调用的所有函数的类型,PICC将对所调用的函数进行严格的类型 匹配检查 定义全局变量或符号替换; 实现函数(子程序),特别注意main函数必须是一个没有返回的死循环。 下面的例11-1为一个C原程序的范例,供大家参考。 #include <pic.h> //包含单片机内部资源预定义 # include“pc68.h //包含自定义头文件 //定义芯片工作时的配置位 CONFIG (HS PRotEcT PWRTEN BOREN WDTDIS) //声明本模块中所调用的函数类型 void SetsFR(void) void Clock(void) void keyscan (void) oid Measure ( void) d LCD_Test (void d LCD_Disp(unsigned char); unsigned char second, minute, hour bit flag, flac //函数和子程序 张明峰2004-4-7于上海3of26
张明峰 2004-4-7 于上海 3 of 26 处是可以实现模块化编程。程序编写者应尽量把相互 独立的控制任务用多个独立的 C 原程序文件实现,如 果程序量较大,一般不要把所有的代码写在一个文件 内。图 11-2 列出的是笔者建立的一个项目中所有 C 原 程序模块,其中主控、数值计算、I 2 C 总线操作、命令 按键处理和液晶显示驱动等不同的功能分别在不同的 独立的原程序模块中实现。 11.4 PIC 单片机的 C 语言原程序基本框架 基于 PICC 编译环境编写 PIC 单片机程序的基本方式和标准 C 程序类似,程序一般由以 下几个主要部分组成: z 在程序的最前面用#include 预处理指令引用包含头文件,其中必须包含一个编译器 提供的“pic.h”文件,实现单片机内特殊寄存器和其它特殊符号的声明; z 用“__CONFIG”预处理指令定义芯片的配置位; z 声明本模块内被调用的所有函数的类型,PICC 将对所调用的函数进行严格的类型 匹配检查; z 定义全局变量或符号替换; z 实现函数(子程序),特别注意 main 函数必须是一个没有返回的死循环。 下面的例 11-1 为一个 C 原程序的范例,供大家参考。 #include <pic.h> //包含单片机内部资源预定义 #include “pc68.h” //包含自定义头文件 //定义芯片工作时的配置位 __CONFIG (HS & PROTECT & PWRTEN & BOREN & WDTDIS); //声明本模块中所调用的函数类型 void SetSFR(void); void Clock(void); void KeyScan(void); void Measure(void); void LCD_Test(void); void LCD_Disp(unsigned char); //定义变量 unsigned char second, minute, hour; bit flag1,flag2; //函数和子程序 图 11-2 C 语言多模块编程
void main(void) TMRIH + TMR1H-_CONST; LEDI LED OFF LCD_Testo //程序工作主循环 asm( clrwdt //清看门狗 Measure: //数据测量 例11-1C语言原程序框架举例 5PICC中的变量定义 1151PCC中的基本变量类型 PICC支持的基本变量类型见表11-1: 类型 数学表达 (位数) bit 布尔型位变量 量,0或1两种取值 cha 有符号或无符号字符变量,PICC缺省认定char型变量为无符 8 号数,但可以通过编译选项改为有符号字节变量 unsigned cha 无符号字符变量 short 16 有符号整型数 unsigned short 16 无符号整型数 unsigned int 无符号整型数 32 有符号长整型数 unsigned long 32 无符号长整型数 double 24或32浮点数,Prcc缺省认定doub1型变量为24位长,但可以改 变編译选项改成32位 表11-1PIcc的基本变量类型 PCC遵循 Little- endian标准,多字节变量的低字节放在存储空间的低地址,高字节放 在高地址。 张明峰2004-4-7于上海4of26
张明峰 2004-4-7 于上海 4 of 26 void main(void) { SetSFR(); PORTC = 0x00; TMR1H += TMR1H_CONST; LED1 = LED_OFF; LCD_Test(); //程序工作主循环 while(1) { asm(“clrwdt”); //清看门狗 Clock(); //更新时钟 KeyScan(); //扫描键盘 Measure(); //数据测量 SetSFR(); //刷新特殊功能寄存器 } } 例 11-1 C 语言原程序框架举例 11.5 PICC 中的变量定义 11.5.1 PICC 中的基本变量类型 PICC 支持的基本变量类型见表 11-1: 类型 长度 (位数) 数学表达 bit 1 布尔型位变量,0 或 1 两种取值 char 8 有符号或无符号字符变量,PICC 缺省认定 char 型变量为无符 号数,但可以通过编译选项改为有符号字节变量 unsigned char 8 无符号字符变量 short 16 有符号整型数 unsigned short 16 无符号整型数 int 16 有符号整型数 unsigned int 16 无符号整型数 long 32 有符号长整型数 unsigned long 32 无符号长整型数 float 24 浮点数 double 24 或 32 浮点数,PICC 缺省认定 double 型变量为 24 位长,但可以改 变编译选项改成 32 位 表 11-1 PICC 的基本变量类型 PICC 遵循 Little-endian 标准,多字节变量的低字节放在存储空间的低地址,高字节放 在高地址
1152PICC中的高级变量 基于表11-1的基本变量,除了bit型位变量外,PlCC完全支持数组、结构和联合等复 合型高级变量,这和标准的C语言所支持的高级变量类型没有什么区别。例如 数组: unsigned int data[10] unsigned char inBuff[8] unsigned char getptr, putPtr 联合: unsigned char c[2]: 例11-2C语言高级变量举例 11.53PICC对数据寄存器bank的管理 为了使编译器产生最高效的机器码,PICC把单片机中数据寄存器的bank问题交由编程 员自己管理,因此在定义用户变量时你必须自己决定这些变量具体放在哪一个bank中。如 果没有特别指明,所定义的变量将被定位在bank0,例如下面所定义的这些变量: unsigned char buffer [32] float val[8] 除了bank0内的变量声明时不需特殊处理外,定义在其它bank内的变量前面必须加上 相应的bank序号,例如 bankl unsigned char buffer [32] //变量定位在bank1中 bank2 bit flag, flag2 //变量定位在bank2中 bank3 float val[8] //变量定位在bank3中 中档系列PC单片机数据寄存器的一个bank大小为128字节,刨去前面若干字节的特 殊功能寄存器区域,在C语言中某一bank内定义的变量字节总数不能超过可用RAM字节 数。如果超过bank容量,在最后连接时会报错,大致信息如下 Error[000] Cant find 0x12C words for psect rbss_l in segment BANKl 连接器告诉你总共有0x12C(300)个字节准备放到bank1中但 bankI容量不够。显然,只 有把一部分原本定位在 bankI中的变量改放到其它bank中才能解决此问题。 虽然变量所在的bank定位必须由编程员自己决定,但在编写原程序时进行变量存取操 作前无需再特意编写设定bank的指令。C编译器会根据所操作的对象自动生成对应bank设 定的汇编指令。为避免频繁的bank切换以提高代码效率,尽量把实现同一任务的变量定位 在同一个bank内:对不同bank内的变量进行读写操作时也尽量把位于相同bank内的变量 归并在一起进行连续操作。 张明峰2004-4-7于上海5of26
张明峰 2004-4-7 于上海 5 of 26 11.5.2 PICC 中的高级变量 基于表 11-1 的基本变量,除了 bit 型位变量外,PICC 完全支持数组、结构和联合等复 合型高级变量,这和标准的 C 语言所支持的高级变量类型没有什么区别。例如: 数组:unsigned int data[10]; 结构:struct commInData { unsigned char inBuff[8]; unsigned char getPtr, putPtr; }; 联合:union int_Byte { unsigned char c[2]; unsigned int i; }; 例 11-2 C 语言高级变量举例 11.5.3 PICC 对数据寄存器 bank 的管理 为了使编译器产生最高效的机器码,PICC 把单片机中数据寄存器的 bank 问题交由编程 员自己管理,因此在定义用户变量时你必须自己决定这些变量具体放在哪一个 bank 中。如 果没有特别指明,所定义的变量将被定位在 bank0,例如下面所定义的这些变量: unsigned char buffer[32]; bit flag1,flag2; float val[8]; 除了 bank0 内的变量声明时不需特殊处理外,定义在其它 bank 内的变量前面必须加上 相应的 bank 序号,例如: bank1 unsigned char buffer[32]; //变量定位在 bank1 中 bank2 bit flag1,flag2; //变量定位在 bank2 中 bank3 float val[8]; //变量定位在 bank3 中 中档系列 PIC 单片机数据寄存器的一个 bank 大小为 128 字节,刨去前面若干字节的特 殊功能寄存器区域,在 C 语言中某一 bank 内定义的变量字节总数不能超过可用 RAM 字节 数。如果超过 bank 容量,在最后连接时会报错,大致信息如下: Error[000] : Can't find 0x12C words for psect rbss_1 in segment BANK1 连接器告诉你总共有 0x12C(300)个字节准备放到 bank1 中但 bank1 容量不够。显然,只 有把一部分原本定位在 bank1 中的变量改放到其它 bank 中才能解决此问题。 虽然变量所在的 bank 定位必须由编程员自己决定,但在编写原程序时进行变量存取操 作前无需再特意编写设定 bank 的指令。C 编译器会根据所操作的对象自动生成对应 bank 设 定的汇编指令。为避免频繁的 bank 切换以提高代码效率,尽量把实现同一任务的变量定位 在同一个 bank 内;对不同 bank 内的变量进行读写操作时也尽量把位于相同 bank 内的变量 归并在一起进行连续操作