PIC单片机C语言编程教程_第1页
PIC单片机C语言编程教程_第2页
PIC单片机C语言编程教程_第3页
PIC单片机C语言编程教程_第4页
PIC单片机C语言编程教程_第5页
已阅读5页,还剩61页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、PIC单片机c语言编程简介用C语言来开发迪11系统软件最大的好处是编'弓代码效率高、软件调试直观、维护升级方便、代码的乘复利用率商、便于跨平台的代码移植等等,因此C语言编程在单片机系统设计中已得到越来越广泛的运用。针对PIC单片机的软件开发,同样可以用C语言实现。但在|1&机上用C讲言写程序和在PC机上写程序绝对不能简单等同。现在的PC机资源卜分丰富,运算能力强大,因此程序员在写PC机的应用程序时儿乎不用关心编译后的可执行代讶在运行过程中需姜占用多少系统资源,也基本不用担心运行效率有多商。写匕 的c程序最关键的一点足单左机内的资源非常冇限,控制的实时性要求乂很高,因此,如 果没

2、冇时里jfL体系结构和硬件资源作详尽的了解,以笔者的愚见认为足无法写I碍质杲实 用的c语盍程序。这就足为什么询而所有策节中的的示范代码全部用皐础的汇编指令实现 的原因,希望籍此能使读者对pic 血辿的指令体系和硬件资源有深入解,在这荃础之 匕再来讨论c语言编程,就冇水到渠成的感觉。本书围境中档系列PIC单片机来展开讨论,Microchip公司自己没冇针对中低档系列PIC单片机的C语言编译器,但很多匕业的第三方公司有众多支持PIC单片机的C语言编译器 提供,常见的有Hitech、CCS、IAR, Bytecraft等公用。其中笔者最常用的是Hitech公用的 PICC编译器,它稳定可筋,编译生成

3、的代码效率拓,在用PIC巴辿进行系统设计和开发的程师群体中得到广泛认可。其正式完全版软件需要购置,但在其网站上冇限时的试用版供用户评佔。另外,Hitech公司针对广大PIC的业余爱好者和初学者还提供了完全免费的学习版PICC-Ute编译器套件,它的使用方式和完全版相同,只足支持的PIC巧机型号限制在PIC16F84. PIC16F877和PIC16F628等儿款。这儿款Flash型的单片机因其所具备的半富 的片上资源而最适用于血辿学习入门,因此笔者建议感兴趣的读者可从PICC-Ute入于学 握PIC屮片机的C语言编程。在此列叽儿个主要的针对PIC 训机的C编译器相关连接网址,洪读者參考:Hit

4、ech-PICC:1, htsoft .comIAR: CCS: ByteCraft: 本章将介绍Hitech-PICC编译器的一些基本概念,山于篇幅所限将不涉及C语言的标准 语法和爆础知识介绍,因为在这些方而都冇大最的书籍可以参考。敢点突出针对PIC单片 机的特点而所需要特别注意的地方。11.2Hitech-PICC 编译器PICC用本上符合ANSI标陽 除了一点:它不支持函数的递归训用。其丄要原因是因 为PIC屮出卫特殊的堆栈结构。在询面介绍PIC单片机架构时己经详细说明了 PIC单片机中的堆栈是硬件实现的,其深度已随芯片而固定,无法实现需鉴大帚堆栈操作的递归算法; 另外在PIC单i M中

5、实现软件堆栈的效率也不足很岛,为此,PICC编译器采用一种叫做"静 态覆盖"的技术以实现对 C语言函数中的局部变眾分配固定的地址空间。经这样处理后产 生出的机器代码效率很岛,按笔者实际使用的体会,、气代码虽趙过4K ?后,C语言编译出 的代码氏度和全部用汇编代码实现时的差别己经不是很大(10%) ,、“|然询提是在整个C 代码编写过程中须时时处处注意所编写语句的效率,而如果没冇对PIC1的内孩结构、各功能模块及其汇编指令深入了解,要做到这点足很难的。11.3MPLAB-IDE 内挂接 PICCPICC编译器可以亩接挂接在MPLAB-IDE集成开发平台下,实现一体化的编译连接

6、和 原代码调试。使用MPLAB-IDE内的调试I具ICE2000. ICD2和软件模拟器都可以实现原 代码级的程调试,非常方便。由先必须在你的il算机中安装PICC编译器,无论是完全版还足学习版都可以和MPLABIDE挂接。安装成功后可以进入IDE,选择菜单项Project Set Language ToolLocations.,打开语言匸具挂接设置对话框,如图111所示:图11-1 MPUkB-IDE语言L具设置对话框在对话框中选择"HI-TECH PICC Toolsuite"栏.展开可执行文件'Executables, 列出了将被MPLAB-IDE后台调用的编

7、译器所用到的所有可执行文件,其中有汇编编译器''PICC Assembler". C 原程序编译器''PICC Compiler"和连接定位程序"PICC Linker"。同时在此列表中还显示了对应的可执行程序名,请注意在这里都足"PICC.EXE"。用鼠标分别点市选中这三项可执行文件,观察对话框下iTLocation"-栏中显茹的文件路径,用''Browse."按纽,从计算机中已经安装的PICC编译器文件夹中选择PICC.EXE文件。实际上PICC.EXE只足一个调

8、度管理程序,它会按照所输入的文件扩展名自动调用对应的编译器和连接器,用户要注意的足C语言原程序扩展名用”.c",汇编原程序用、'.as"即可。匸具挂接完成后,在建立项目时可以选择诳言1-具为、'HITECH PICC",具体步骤可以参阅第三章3.1.3节,此处不再谎复。项U建立完成后可以加入C或汇编原程序,也可以加 入己有的库文件或已经编译的口标文件。最泊见的是只加入C脈程序。用C涪言编程的好 处是可以实现模块化编程。程序编写者应尽疑把相互独立的控制任务用多个独立的C原程序文件实 现,如果程序战较大,一般不要把所有的代码写在-个文件内。图112列出

9、的足笔者建立的个项LI中所有C原程用模块,真中主控、数值计算、I2C总线操 作、命令按键处理和液晶显示驱动等不同的功能分别在不同的独立的原程序模块中实现。图112 C语言多模块编程11.4 PIC屮片札的C语言原程序某本框架圧于PICC编译环境编写PIC门;匚程序的卑本方式和标准C程序类似,程序i般由以F儿个主要部分纽成:&O1540;在程序的最前面H#include预处理指令引用包含头文杵,其中必须包含一个编译器提供的pch"文件,实现 训 机内特殊奇存器和其它特殊符号的声外&O1540;用"_CONFIG阪处理捋令定义芯片的配置位:&O1540;

10、声明本模块内被调用的所有函数的类犁,PICC将对所调用的函数进行严格的类熨&O1540;定义全局变最或符号替换;&O1540;实现函数(了程序),特别注意main函数必须是-个没有返回的死循环。下面的列11-1为 个C原程序的范例,供大家参考。# include包含单片机内部资源预定义include npc68.h"包含自定义头文件定义芯片匸作时的配遥位_CONFIG (HS & PROTECT & PWRTEN & BOREN & WDTDIS);声明本模块中所调用的函数类里void SetSFR(void);void Clock(v

11、oid);void KeyScan(void);void Measure(void);void LCD_Test(void);void LCD_Disp(unsigned char);定义变昴unsigned char second, minute, hour;bit flagl,flag2;函数和了程序void main(void)SetSFR();PORTC = 0x00;TMR1H += TMR1H_CONST;LED1 = LED.OFF;LCD_Test();程序匸作上循环whileasmf'clrwdn;Clozk();KeyScan();Measure();SetSFR(

12、);淸看门狗更新时钟扫描键盘数据测量刷新特殊功能寄存器11.5PICC中的变杲定义例lli C语言原程序框架举例11.5.1 PICC中的嚴本变気类型PICC遵循Uttle-endian标准,多7节变世的低7节放在存储空间的低地址,高7节放在高地址。11.5.2 PICC中的筒级变量堪于表11-1的荃本变眾,除r bit熨位变杲外,PICC完全支持数组、结构和联合等复合型拓级变世,这和标准的c说言所支持的高级变暈类型没有什么区别。例如:数组:unsigned int data10;结构:struct commlnData unsigned char inBuff8;unsigned char

13、getPtr, putPtr;联合:union int_Byte unsigned char c2;unsigned int i;例112 C语言高级变武举例11.5.3 PICC对数据寄存器bank的管理 为了使編译器产生最高效的机器码,PICC把屮吕血中数据寄存器的bank问题交由编程 员自己管理.因此在定义用户变虽时你必须自己决定这些变駅具体放在哪一个bank中。如 果没冇待别指明,所定义的变疑将彼定位在bankO.例如下面所定义的这些变杲: unsigned char buffer32;bit flagl,flag2;float val8;除了 bankO内的变眾声明时不需特殊处理外,

14、定义在其它bank内的变杲前而必须加上 相应的bank序号,例如:bankl unsigned char buffer32;变量定位在 bankl 中bank2 bit flagl,flag2;bank3 float val8;变世定位在bank2中变虽定位在bank3中 中档系列PIC单片机数拡寄存益的-个bank大小为128字节,刨去询而若T字节的特 殊功能寄存器区域,任C语言中某- bank内定义的变疑字节总数不能趙过可用RAM字节 数。如果超过bank容疑,在最后连接时会报错,大致信息如下:ErrorODO : Can't find 0xl2C words for psect

15、rbss_l in segment BANK1连接器告诉你总共有0xl2C (300)个字节准备放到bankl中但bankl容厳不够。显然,只 有把一部分原本定位在bankl中的变慑改放到其它bank中才能解决此问题。虽然变吊所在的bank定位必须由编程员自己决定,但在编写原程序时进行变杲存取操作前无需再特意:编写设定bank的指令。C编译器会根据所操作的对象自动生成对应bank设 定的汇编指令。为避免频繁的bank切换以提高代码效率,尽虽把实现同-任务的变疑定位 在同一个bank内:对不同bank内的变最进行读写操作时也尽吊把位于相同bank内的变昴 归并在-起进行连续操作。11.5.4 P

16、ICC中的局部变駅PICC把所有函数内部定义的auto型局部变吊放在bankOo为节约宝贵的存储空间,它采用了一种被叫做''静态覆盖”的技术来实现局部变最的地址分配。其大致的原理足在编译 器编译駅代码时扫描整个程序中函数调用的嵌套关系和层次,算出每个函数中的崗部变帚7 节数,憑后为每个局部变疑分配 个固定的地址,且按调用嵌套的层次关系各变杲的地址可 以相互重况。利用这一技术后所有的动态局部变斌都可以按己知的閤定地址地进行苴接寻 址,用PIC汇编指令实现的效率最拓,但这时不能出现函数递片谨用。PICC在编译时会严 格检査递归调用的问题并认为这足一个严重错误而立即终止编译过程。既然

17、所有的局部变虽将占用bankO的存储空间,因此用户自己定位在bankO内的变虽字 节数将受到定的限制,在实际使用时需注意。11.5.5 PICC中的位变疑bit熨位变杲只能是全局的或静态的。PICC将把定位在同 bank内的8个位变駅合并 成一个7节存放于一个固定地址。因此所有针对位变虽的操作将直接使用pic生迪的位 操作汇编指令奇效实现。軀于此,位变疑不能足局部自动型变最,也无法将其组合成复合型PICC对较个数据存储空间实行位编址,0x000单元的第0位是位地址0x0000,以此后 推,每个7节有8个位地址。编制位地址的意义纯粹是为了编译器最后产生汇编级位操作指 令而用.对编程人员来说里本可

18、以不管但若能r解位变最的位地址编址方式就可以在最后 程序调试时方便地含找自己所定义的位变最,如果一个位变昴flagl彼编址为0x123,那么 实际的存储空间位于:/ 节地址=0x123/8 = 0x24位偏移 =0xl23%8 = 3即flagl位变宦位于地址为0x24 7节的第3位。在程序调试时如果要观察flagl的变化,必 须观察地址为0x24的字节而不足0x123.PIC的位操作指令是非常高效的.因此,PICC在编译原代码时只要有可能,对普通变最的操作也将以最简单的位操作指令来实现。假设一个7节变駅tmp最后被定位在地址0x20,那么tmp |= 0x80tmp &二 0xf7=

19、> bsf=> bcf0x20,70x20,3if (tmp&Oxfe)=> btfsc 0x20,0即所冇只对变疑中某一位操作的c ifi句代码将被肖接编译成汇编的位操作指令。虽然编程 时可以不用太关心,但如果能r解编译器足如何匸作的,那将有助于引导我们写出筒效简介 的c语言原程序。在冇些应用中需要将一组位变量放在同一个7节中以便需要时一次性地进行读写,这一功能可以通过定义一个位域结构和一个7节变駅的联合来实现,例如:union struct unsigned bO: 1;unsigned bl: 1;unsigned b2: 1;unsigned b3: 1;un

20、signed b4: 1;unsigned b5: 1;unsigned : 2; 最高两位保留 oneBit;unsigned char allBits; myFlag;例113定义位变駅于同字节需要存取其中某位时可以myFlag.oneBit.b3=l; /b3 位置 1一次性将全部位淸零时可以myFlag.allBits=O;全部位变戢淸0'“I程序中把非位变暈进行强制类型转换成位变屋时,要注意编译器只对普通变議的最低位做判别:如果显低位是0,则转换成位变疑0;如果最低位足I.则转换成位变駅1。而标 准的ANSI-C做法足判整个变駅伯足否为0。另外,函数可以返回一个位变最,实际上

21、此返 回的位变杲将存放于辿也的进位位中带出返回。11.5.6 PICC中的浮点数PICC中描述浮点数足以IEEE-754标准格式实现的。此标准下定义的浮点数为32位长,在 生札中要用4个字节存储.为了节约单片机的数据空间和程庁空间,PICC $门提供了种反度为24位的截短型浮点数,它损失了浮点数的-点梢度,但浮点运算的效率得以提尚。在程序中定义的float型标准浮点数的氏度固定为24位,双将度double型浮点数一般 也足24位K,但可以在程序编译选项中选择double型浮点数为32位,以提高计算的耕度。-般控创系统中关心的是的运行效率,因此在耕度能够满足的前提下尽屋选幷24位的浮点数运算.1

22、1.5.7 PICC中变帯的绝对定位忤先必须强调,在用C语言写程序时变最般山编译器和连接器最后定位,在写程序 之时无需知道所定义的变僦貝体被放在哪个地址(除了 bank必须声明)。真正需要绝对定位的只足心丿:肌中的那些特殊功能寄存器,而这些寄存器的地址定位在PICC编译环境所提供的头文件中已经实现,无需用户操心。编程员所要了斛的也就足PICC 足如何定义这些特殊功能寄存器和其中的相关控制位的名称。好在PICC的定义标准荃本上 按照芯片的数据乎册中的名称描述进行,这样就秉承了变慣命名的一贯性。个变疑绝对定 位的例子如下:unsigned char tmpData 0x20; /tmpData 定

23、位在地址 0x20干万注惫,PICC对绝对定位的变眾不保留地址空间.换句话说,上面变晁tmpData的 地址是0x20,但最后0x20处完全有可能乂被分配给了其它变帰使用,这样就发生了地址冲 突。因比针对变尿的绝对定位要特别小心.从笔者的应用经验看,在一般的程序设计中用户 自定义的变杲实在足没冇绝对定位的必要。如果需耍,位变世也可以绝对定位。但必须遵循上面介绍的位变僦编址的方式。如果-个普通变虽己经被绝对定位,那么此变呆中的每个数据位就可以用下而的计算方式实现位变 駅指派:unsigned char tmpData 0x20; /tmpData 定位在地址 0x20bit tmpBitO tm

24、pData*8+0; /tmpBitO 对应于 tmpData 第 0 位bit tmpBitl tmpData*8+l; /tmpBitO 对应于 tmpData 第 1 位 extern bankl unsigned char varl, var2; 声明位于 bankl 的外部变駅如果tmpData爭先没有被绝对定位,那就不能用上面的位变眾定位方式。11.5.8 PICC的英它变最修饰关键诃&O1540; extern 外部变杲声明如果在一个C程序文件中要使用-些变罐但梵原熨定义写在另外的文件中,那么在本文件中必须将这些变昂声明成"extern"外部类熨。例如

25、程序文件codel.c中冇如下定义:bankl unsigned char varl, var2;定义了 bankl中的两个变慑在另外一个程序文件code2.c中要对上而定义的变杲进行操作,则必须在程序的开头定义:&O1540; volatile -易变型变眾声明PICC中还有一个变最修饰诃在普通的C语言介绍中-般足看不到的,这就足关键诃 "volatile"。顾名思义,它说明了一个变駅的伯足会随机变化的,即使程序没冇刻意对它进 行任何赋值操作。在也中,作为输入的10端口其内容将足随意变化的;在中断内被修 改的变憬相对主程序流程来讲也是随意变化的:很多特殊功能寄存器

26、的值也将随着指令的运 行而动态改变。所有这种类型的变量必须将它们明确定义成"volatile"类犁,例如:volatile unsigned char STATUS 0x03;volatile bit commFlag;"volatile"类型定义在匕E也的C涪言编程中是如此的垂要,是因为它可以告诉編译器的优化处理器这些变供足实实在在存在的,在优化过程中不能无故消除。假定你的程序定 义了-个变眾并对其作了一-次赋值,但随后就再也没冇对兀进行任何读写操作,如果足非 volatile堆变量,优化后的结果是这个变眾将有可能被彻底删除以廿约存储空间。刃外-种悄形

27、足在使用某-个变就进行连续的运算操作时,这个变量的值将在第-次操作时被复制到中间临时变虽中,如果它足非volatile樂变疑,则紧接其后的其它操作将有可能但接从临时变最中収数以提高运行效率,显然这样做后对于那些随机变化的参数就会HI问题。只要将H 定义成volatile类型后,编译后的代码就可以保证每次操作时戌接从变最地址处収数。&O1540; const 一常数熨变疑声明如果变駅定义丽就以”8nst'类型修饰,那么所冇这些变疑就成为常数,程序运行过程中不能对其修改。除f位变量,其它所有珏本类型的变杲或岛级组合变駅都将被存放在程 序空间(ROM区)以节约数据存储空间。显然,被定

28、义在ROM区的变杲足不能再在程序 中对其进行赋值修改的,这也足"const'的本来意义。实际上这些数列最终都将以''retlw" 的抬令形式存放在程序空间,但PICC会自动编译生成相关的附加弋码从程序空间读取这些 常数,编程员无需太多操心。例如:const unsigned char name='This is a demo" 定义个常気7符串如果定义了 "consT类塑的位变世,那么这些位变議还绘被放置在RAM中,但程序不能对其赋伯修改.本来,不能修改的位变貳没冇什么太多的实际恵义,相倍大家在实际编 程时不会大最用到。&

29、amp;O1540; persistent 苗初始化变母声明 按照标准C语言的做法,程序在开始运行両百先要把所有定义的但没有预置初值的变 吊全部清零。PICC会在最后生成的机器码中加入一小段初始化代码来实现这一变讯淸冬操 作,且这一操作将在main函数被调用之前执行。问题足作为一个门;机的控制系统冇很多 变眾足不允许在程序复位后被清零的。为了达到这一U的,PICC提供了''persistent7'修饰 诃以声明此类变世无需在复位时自动淸零,編程员应该F1己决定程序中的那些变疑是必须声 明成"persisten啖里,而且须自己判断什么时候需要对其进行初始化赋值。

30、例如: persistent unsigned char hour,minute,second; 定义时分秒变帚经常用到的是如果程序经上电复位后开始运行,那么需要将persistent和的变杲初始化, 如果是氏它形式的复位,例如看门狗引发的复位,则无需对persistent型变戢作任何修改。 PIC勺£也内提供了各种复位的判别标志,用户程序可依貝体设计灵活处理不同的复位悄 形。11.5.9 PICC中的指针PICC中指针的卑本概念和标准C语法没有太多的差别。但足在PIC E片机这-特定的 架构h.指针的定义方式还足冇儿点需要持别注意.&O1540; 指向RAM的指针 如果是汇

31、编语言编程,实现指针寻址的方法疔定就是用FSR寄存龄,PICC也不例外.为了生戎商效的代码,PICC在编译C原程序时将指向RAM的指计操作最终用FSR来实现 间接寻址。这样就势必产生一个问题:FSR能够戌接连续寻址的范刃足256 7节(bankO/1 或bank2/3),要覆盖最大512字节的内部数拯存储空间,乂该如何让定义指针? PICC还足 将这-诃题留给编程员自己解决:在定义指针时必须明确指定该指针所适用的寻址区域,例 如:unsigned char *ptrO; 定义覆盖 bankO/1 的指针bank2 unsigned char *ptrl; 定义覆盖 bank2/3 的指针ban

32、k3 unsigned char *ptr2; 定义覆盖 bank2/3 的指针上面定义了三个指针变就,其中指针没有任何bank限定,缺省就是指向bankO和bankl: 和一个指明了 bank2,另一个指明了 bank3,但实际上两者足一样的,因为一个指针可以同时谡譴两个bank的存储区域。另外,上而三个抬针变昂自身都存放在bankO中。我们 将在稍后介绍如何住英它bank中存放指针变尿。既然定义的指针有明确的bank适用区域,在对指针变最赋值时就必须实现类型匹配,下而的指针赋伯将产生一个致命错误:unsigned char *ptrO;bank2 unsigned char buff8;程

33、序语旬:定义指向bankO/1的指针定义bank2中的-个缓冲区ptrO = buff; 错误!试图将bank2内的变最地址赋给指向bankO/1的指针若出现比类错误的指针操作,PICC在最后连接时会告知类似于下而的Fixup overflow in expression ()同样的道理,若函数调用时用了指针作为传递参数,也必须注惹bank作用域的匹配,而这点往往容易被忽视。假定冇下面的函数实现发送一个7符串的功能:void SendMessage(unsigned char *);那么被发送的字符串必须位于bankO或bankl中。如果你还要发送位于bank2或bank3内的7符必须再另外单

34、独写一个函数: void SendMessage_2(bank2 unsigned char *); 这两个因数从内部代码的实现来看可以模一样,但传递的参数类型不同。按笔者的应用经验体会,如果你看到厂Fixup overflow"的错误指示,儿乎可以肯定 足指针类熨不匹配的賦值所至。请旅点检査程序中有关描针的操作。&O1540;指向ROM常数的指针如果组变眾足已经被定义在ROM区的常数,那么指向它的指针可以这样定义:const unsigned char company二Mcrochip"const unsigned char *romPtr;程序中可以对上面的指针

35、变赧賦值和实现取数操作:romPtr = company; 指针賦初伯data = *romPtr+; 取指针指向的-个数,然后指针加1定义ROM中的常数定义指向ROM的指针 反过来,下面的操作将是i个错误,因为该指针指向的足當数舉变燧,不能赋值。*romPtr = data; 往指针指向的地址写一个数&O1540; 指向函数的指针卩虫札編程时函数指针的应用相对较少,但作为标准C语法的-部分,PICC同样支持 函数指计调用。如果你对编译原理有一定的了解,就应该明口在PIC即5这一特定的架 构上实现函数指针调用的效率足不高的:PICC将在RAM中建立一个调用返回表,真正的 调用和返回过程

36、足靠戌接修改PC指针来实现的。因此,除非特殊瘁法的需要,建议大家尽 杲不要使用函数指针。&O1540; 指针的类型修饰前而介绍的指针定义都是最堪本的形式。和普通变僦样,指针定义也可以在前而加上 特殊类型的修饰关键诃,例如"persistent"、''volatile"等。考虑指针本身还要限定其作用域, 因此PICC中的指针定义初看起来显得冇点复杂,但只要了解各部分的貝体含义,理解一个指针的实际用图就变得很苴接.()bank修饰诃的位置含义前而介绍的一些指针冇的作Hl于bankO/1,冇的作用于bank2/3.但它们本身的存放位置 全部在ba

37、nkOo显然,在一个程序设计中指针变駅将冇可陡彼定位在任何可用的地址空间, 这时,bank修饰词出现的位置就足个关键,看下而的例了:定义指向bankO/1的指针,指针变虽为于bankO中unsigned char *ptrO;定义指向bank2/3的指针,指针变耳为于bankO中bank2 unsigned char *ptrO;定义指向bank2/3的抬针,指针变昴:为于bankl中bank2 unsigned char bankl ptrO;从中可以看Hl规律:前面的bank修饰诃指明了此指针的作用域:后面的bank修饰诚定义f 此指针变疑自身的存放位置。只要学握了这一法则,你就可以定义任

38、何作用域的描针且可以 将指针变昴:放于任何bank中。volatilepersistent和const修饰诃的位置含义如果能理解上而介绍的bank修饰詞的位置含义,实际上volatile, persistent和const这些关键词出现在询后不同位置上的含义规律足和bank 词相-致的。例如:定义指向bankO/1易变型7符变录的指针,指针变杲位于bankO中且自身为非易变住volatile unsigned char *ptrO;定义指向bank2/3非易变塑?符变吊的指针.指针变最位于bankl中且自身为易变期bank2 unsigned char * volatile bankl ptr

39、O;定义指向ROM区的指针,指针变昴本身也足存放于ROM区的常数const unsigned char * const ptrO;亦即出现在询面的修饰诃英作用对彖足指针所指处的变眾:山现在点面的修饰诃X作用对彖 就是指计变疑白己。11.6PICC中的程序和函数中档系列的PIC也程序空间冇分页的概念,但用C讲吉编程时垄本不用太多关心代码的分页问题。因为所有函数或了程序调用时的页而设定(如果代码超过一个页而)都由 编谦器自动生成的指令实现。11.6.1函数的代码反度限制PICC决定了 c瓯程序中的一个函数经编译后生成的机器码一定会放在同-个程序贝面内。中档系列的PIC丿;门其一个程序页而的长度足2

40、K 7,换句话说,用C语言编写的任 何一个药数最后生成的代码不能超过2K 7. 一个良好的程序设计应该冇一个諧晰的组织结 构,把不同的功能用不同的函数实现足嚴好的方法,因此-个函数2K字长的限制般不会 对程序代码的编写产生太多影响。如果为实现特定的功能确实要连续编写很长的程序,这时 就必须把这些连续的代码拆分成若干函数,以保证每个函数最后编译出的代码不超过一个页 面空间,11.6.2调用层次的控制中档系列PIC 也的硬件堆栈深度为8级,考虔中断响应需片用一级堆栈,所有函数调用嵌套的最大深度不要超过7级。编程员必须自己控制了程序调用时的嵌套深 度以符合这“限制要求。PICC在最后编译连接成功后可

41、以生成一个连接定位映射文件(*.map),在此文件 中冇详细的函数调用嵌套指示图"call graph",建议大家要留意一下其信息大致如下(取H于一示范程序的编译结果),Call graph:*_main size OZO offset 0_RightShift_C* .Task size 0,1 offset 0Iwtoftftmul size 0,0 offset 0ftunpacklftun pack2ftadd size 0,0 offset 0ftunpacklftun pack2ftdenorm例1V4 C瓯数调用层次图上而所举的信息表明報个程序在止常调用了程序

42、时嵌套晟多为两级(没冇考虑中断)-因为 main函数不可能返回,故其不用计算在嵌套级数中。梵中有些函数调用是编译代码时自动 加入的库函数,这些函数调用从C原程序中无法忸接看HI,但在此嵌套指示图上则一U r 然。11.6.3 函数类型声明PICC在编译时将严格进行函数调用时的类型检査。-个良好的习惯是在编写程序代码前先声眄所冇用到的函数类型例如:void Task(void);unsigned char Temperature(void);void BJN2BCD(unsigned char);void TimeDisplay(unsigned char, unsigned char);这些类

43、型声明确定了函数的入口参数和返回值类型,这样编译器在编译代码时就能保证生成1E确的机器码。笔者在实际1一作中有时碰到些用户声称发现C编译器生成了错误的代码, 最后究比原因就足因为没有弔先声明函数类型所毀。鯉议大家在编写一个函数的原代码时,立即将此函数的类舉声明复制到原文件的起始处,见列11-1;或足复制到专门的包含头文件中,再在每个原程疗模块中引用。11.6.4中断函数的实现PICC可以实现C语言的中断服务程序。中断服务程序有-个特殊的定义方法:void interrupt ISR(void);英中的函数名JSRFJ以改成任愆合法的7母或数7组合,但英入口参数和返回参数类型 必须是、oid&q

44、uot;熨,亦即没有入口参数和返回参数,且中间必须有-个关键述interrupts 中断函数可以被放置在原程序的任意位置因为已冇关键词"interrupt声明,PICC在最后进厅代码连接时会自动将其定位到0x0004中断入口处,实现中断服务响应。编译器也会实现中断函数的返回指令"retfie"。个简单的中断服务示范函数如下:void interrupt ISR(void) 中断服务程序if (T0IE && T0IF)T0IF = 0;在此加入TMR0中断服务判TMR0中断淸除TMRO中断标志if (TMR1IE && TMR1IF

45、) 判 TMR1 中断TMR1IFO;在此加入TMR1中断服务淸除TMR1中断标志中断结束并返回例115 C语言中断函数举例PICC会白动加入代码实现中断现场的保护,并在中断结束时向动恢复现场,所以編程 员无需彖编写汇编程序那样加入中断现场保护和恢复的额外指令语句。但如果在中断服务程 序中需要修改某些全局变录时,足否需要保护这些变尿的初值将山编程员自己决定和实施。用C语言编写中断服务程序必须遵循高效的原则:&O1540;代码尽量简短,中断服务强调的是一个“快"7。&O1540;避免在中断内使用函数调用。虽然PICC允许在中断里调用其它函数,但为了解决 递归调用的问题,

46、此函数必须为中断服务独家匕用。既如此,不妨耙原本要写住其它函数内的代码耳接写在中断厳务程序中。&O1540;避免在中断内进行数学运算。数学运算将很有可能用到库函数和许多中间变昂.就算不出现递归调用的问题,光在中断入口和hi口处为r保护和恢复这些中间临时变虽就需耍大疑的开俏,严审影响中断服务的效率。中档系列PIC的中断入口只有个,因此林个稅序中只能有个中斷服务函数.11.6.5标准库函数PICC提供了较完舉的C标准库函数支持,其中包括数学运算函数和7符串操作函数。任程用中使用这些现成的库函数时需要注意的足入口参数必须在bankO中。如果需姜用到数学厨数,则应在程序前”#include &

47、quot; 包含头文件:如果要使用7符串操作函数,就需要包含”#indude "头文件。在这些头文件中提供了函数 类型的由明。通过克接査看这些头文件就可以知道PICC提供了哪些标准库函数。C语言中帘用的格式化打印函'printf/sprintrri> Al!-的程序中时要特别printf/sprintf足一个非常大的两数,一旦使用,你的程序代码2度就会增加很多。除非是在 编写试脸性质的代码,可以考虑使用格式化打印函数以简化测试程序;一般的最终产品设计 都足自己编写最稍简的代码实现特定格式的数据显示和输出。本来,在屮片机应用中输出的 数据格式都相对简单而且固定,实现起来应

48、该很容易。对于标准C语育的控制台输入(scanf) /输也(printf)函数,PICC需要用户自己编写 共底层函数getch()和putch()o在 讪M系统中实现scanf/printf本来就没什么太多意义,如 果一定更丈现,只耍编写好特定的getch()和putch()函数,你就可以通过任何按口输入或输出格式化的数据。11.7PICC定义特殊区域值PICC提供了相关的预处理指令以实现在原程序中定义的配置7和标记单元.11.7.1定义工作配置7在原程序中定义PIC 占机一匸作配置7的重要性在就而章节中已经阐述。在用PICC写程序时司样可以在c原程序中定义,具体方式如下:_CONFIG (H

49、S & UNPROTECT & PWRTEN & BORDIS & WDTEN);上而的关键词”_CONFIG"(注意前而冇两个下划线符)&门用于足芯片配置?的设定,后而括号中的各项配置位符号在特定型号¥上;川的头文件中已经定义(注意不是pic.h 头文件),相互之间用逻辑嵌与"操作符组合在一起。这样定义的配庫7信息最后将和程序代码一起放入同一个HEX文件。在这里列出了适用于16F7X系列丄迪配置位符号预定义,英它型号或系列的单片机配置7定义方式类似,便用前査阅一下对应的头文件U卩可。 广振荡器配置*/#define RC#

50、define HSOx3FFF / RC 振荡Ox3FFE / HS 模式#define XT#define LP严看门狗配置*Ox3FFD / XT 模式Ox3FFC / LP 模式#define WDTEN 0x3FFF / 百门狗打开#define WDTDIS/*上电延时定时器配置*/#define PWRTENOx3FFB /看门狗关闭0x3FF7 /上电延时定时器打开#define PWRTDIS0x3FFF /上电延时定时器关闭/低电压复位配置”#define BOREN 0x3FFF /低电爪复位允许希define BORDIS广代码慄护配置5*70X3FBF /低电压复位禁止

51、#define UNPROTECT 0x3FFF /没有代码保护#define PROTECT0x3FEF /程序代码保护例11-6头文件预定义的配置倍息符号11.7.2定义芯片标记单元PIC il'HJfl中的标记单元定义可以用卜而的_IDLOC (注意前而有两个卜划线符)预处理指令实现,方法如下:_IDLOC (1234);其特殊之处是括号内的值全部为16进制数.不需姜用"OxVI导。这样上面的定义就设定了标记单元内容为01020304.11.8MPLAB-IDE中实现PICC的编译选项设置在11.3节中已经介绍了如何实现PICC和MPLAB-IDE开发平台的挂接。一冃项

52、目建龙成功、程序编写完成后即可以通过MP3B环境下的项LI管理具实现程序的编译、连接和调试。它们的含义分别是:一项口难护(Make) : MPLAB检査项口中的原程序文件,只編译那些在卜.次编译后乂披修改过的原程序,最后进行连接:一项口就锂(Build All):项U中的所有原程序文件,不管是否有修改,都将彼車新编译次.最后进行连接。也可以通过Project菜单选择”Make"或''Build All"实现项LI编译。不管采用何种方式,在自动编译过程前一般都要设定一些编译选项.11.8.1 选择单丿机型号在选择PICC作为语言匸貝并建立了项LJ后,同样通过菜

53、单项Configure&O1616;Select Device 在MPLAB环境中选择貝体片机型号。请回顾-下例111的代码,我们在原程序一开始 使用厂#include "实现了相关里M的一些预定义符号的直接引用,但没冇具体指明足哪-个型号。实际上,''pic.h"头文件只是一个简单的管理1具(条件判别),它会按照 MPLAB所选择的特定型号的口昭,,把真1E对应的头文件包含进来。有兴趣者可以克接用 文本编辑匚貝打开pic.h文件查看苴是如何根据不同的巴片扭黑号包含对应的头文件。这样对编程员而直,程序中只需加上句include "即可。11

54、.8.2 PICC普通编译选项(General)设定参考第三章3.2.7节的内容和图3-20的指示说明,启动编译选项设定对话框。在使用PICC讲古具时对话框的内容和用MPAMS汇编具相比完全不同.图11-3为PICC編译环境下普通迭项设定的界面。在此界而中用户唯-能改变的足编译器査找头文件时的指定路能(Include Path).实际上如果编译器安装没冇问题,在此界面中这些普通选项的设定无需任何改动,编译器会自动 到缺省认定的路径中(编译器安装后的相关路径)E找编译所需的各类文件。图113 PICC普通选项设定图11-4 PICC全局选项设定11.8.3 PICC 全局选项设定(PICC Gl

55、obal)全局选项将影响项目中所冇C和汇编原程序的编译,详细的设定内容见图11-4»宾中必须关注的有:&O1540; Compile for MPLAB ICD:如果你准备用ICD调试C语言编译后的代码,那么此项 就必须打钩选中。这样编译后的结果就能保证ICD本身使用的芯片资源(一小部分的程字和数据空间)不被应用程序所占用。&O1540; Treat 'char as signed:为了提高编译后的代码效率,PICC缺省认定'chaF型变杲也 足无符号数。如果在设计中需要使用带符号的'chaL世变屋,此项就应该被选中。&O1540; F

56、loating point 'double' width:同样为了提高编译后的代码效率,PICC缺省认定'double'型的双粘度浮点数变最的实现长度为24位(等同于普通float型浮点数)。在这里可以选择使其长度达32位。这样数值计算的将度将得到提岛,但代码氏度将增加,计算速度也会降低,所以请在权衡利弊后作岀你自己的决定。11.8.4 C编译器选项设定(PICC Compiler)项LJ中所冇的C原程序都将通过C编译器编译成机器码,这些选项决定了 C编译器足 如何匸作的。所有选项乂分为两组:普通选项(General)和高级选项(Advanced),分别见 图

57、11-5A 和 11-5B.C编译器的普通选项最重要的就是针对代码优化的设定。如果没有峙殊原悯,应该设定全局优化级别为9级(最高级别优化),同时使用汇编级优化,这样最终得到的代码效率最 高(长度和执行速度两方而)按笔者的使用经验,仅从代码氏度灰比较,使用昴岛级别优 化后代冯民度至少可以减少20% (2K字以上的程序)。而且PICC的优化器相为可靠,-般(A)常用选项(B) 高级选项图115 C编译器选项设定不会因为使用优化从而使生成的程序出现错误。碰到的一些问题也基本都足用户编写的原程 序有漏洞所导致,例如-些变僦应该足volatile熨但编程员没有明福定义,在优化询程序可 以正幣运行,一耳使用优化,程序运行就出现异常。显然,把出现的这些问题归邢到编译器 足亳无道理的。使用优化后可能对廉程序级的调试带来些不便之处。因PICC可能会歲组编译后的代 码,例如多处重复的代码可能会改成同个/程序调用以节约程序空间,这样在调试过程中 跟踪原程序时可能会也现程序乱跳的现象,这咸本是正常的。若为了强调更自观的代码调试过程,你可以将优化级别降低其至关闭所冇优化功能,这样调试时程序的运行就可以按部就 班了。C编译器的

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论