《PIC单片机的C语言 (2).ppt》由会员分享,可在线阅读,更多相关《PIC单片机的C语言 (2).ppt(44页珍藏版)》请在三一办公上搜索。
1、PIC单片机的 C语言编程,一、PIC 单片机C 语言编程简介 用C 语言来开发单片机系统软件最大的好处是编写代码效率高、软件调试直观、维护升级方便、代码的重复利用率高等,因此C 语言编程在单片机系统设计中已得到越来越广泛的运用。PIC 单片机的软件开发,同样可以用C 语言实现。Microchip 公司没有自行开发PIC单片机的C 语言编译器,但其他公司有开发众多支持PIC 单片机的C 语言编译器,常见的有Hitech、CCS、IAR、Bytecraft 等公司。其中最常用的是Hitech 公司的PICC 编译器,它稳定可靠,编译生成的代码效率高,在用PIC 单片机开发者中得到广泛认可。,Hi
2、tech-PICC 编译器基本上符合ANSI C标准,但是不支持函数的递归调用。其主要原因是因为PIC 单片机特殊的堆栈结构。PIC 单片机的堆栈是硬件实现的,其深度已随芯片固定,无法实现需要大量堆栈操作的递归算法。二、PICC C编译器的安装 PICC C编译器可以运行在Windows操作系统上,可以在MPLAB IDE集成开发环境下进行项目开发。下面介绍在MPLAB IDE 7.00集成环境下安装和设置PICCV8.05 PL1 PICC编译器,以及在此环境下编译和调试源程序的基本方法。,PICC C编译器的安装,安装PICCV8.05 PL1 运行文件picc8.05-pl1.exe即可
3、进入安装过程,在安装过程中会出现下图所示的编译器安装路径和磁盘空间需求情况的提示画面。,注意:应使用英文路径和文件名,不要使用中文。图中的C:HT-PIC是编译器缺省安装路径,若要改变安装路径,可以通过Browse按钮选择安装路径。指定路径后,单击图中的Next按钮,出现如下所示的开始安装PICC V8.05PL1编译器画面。,单击图中Next按钮,会进入PICC C编译器安装过程。经过一段时间后,出现如下所示的提示画面。单击画面上的“是”按钮,安装即成功,并要求重新启动。,激活PICC C编译器 安装完PICCV8.05PL1编译器,其安装目录下还有一个picc_mplab6_setup.e
4、xe文件,这是PICCV8.05PL1编译器运行在MPLABV6.0以上版本的支持文件。,PICCV8.05PL1编译器在安装完成后应在激活后使用,如果不激活只能作为DEMO版使用,DEMO版使用有时间限制。PICCV8.05PL1编译器的激活方法是:单击PICC编译器开始菜单中的Compiler Activation项,可出现如下所示的编译器激活窗口。,找到激活编译器所需的Serial、Company Name、Registration和Activation项的相关信息,分别填入编译器激活窗口对应的文本框中,单击“下一步”按钮,编译器即被激活。PICC C编译器的启动运行 PICC C编译器
5、安装完成后,便可以在MPLAB IDE集成开发环境下运行。设置PICC的编译环境设置PICC的编译环境 为便于PICC编译器的编译环境设置,应先在MPLAB IDE集成开发环境下创建一个PICC C编译器的项目。创建项目 具体操作步骤如下:,第1步:启动MPLAB IDE7.00集成开发环境窗口。第2步:选择集成开发环境中的Project-New菜单项,打开如下所示的新项目对话框,在此对话框中设置项目名和项目路径。,第3步:在项目名文本框中输入项目名led,在项目路径文本框中输入项目路径D:led,也可以通过单击Browse按钮选择项目名的保存路径。,第4步:单击新项目对话框的OK按钮,出现如
6、下所示的项目树窗口,说明项目已经建立。,选择器件 在开始其他工作之前,应先选择开发过程中所需器件,其操作过程如下所示。首先在MPLAB集成开发环境中打开Configure菜单,然后单击Select Device菜单项,弹出如下所示的器件选择对话框。此处可以选择PICl6F877A作为开发芯片。,设置项目 选择语言组件 在创建项目和选定器件后,接下来就可以设置工程的编译环境。首先介绍语言组件的设置,操作步骤如下:,第1步:在MPLAB集成开发环境中打开Project-Select Language Toolsuite菜单,弹出如下的选择语言组件对话框。,第2步:上图所示对话框用来选择开发工具组件
7、,在Active Toolsuite下拉列表框中选择HI-TECH PICC Toolsuite作为开发工具组件,然后在Toolsuite Contents列表框中选择PICCCompiler项,如下图所示。,第3步:单击对话框中的Browse按钮,弹出如下图所示的打开文件对话框。在缺省PICC V8.05PL1编译器安装目录C:HT-PICbin下,选择plcc.exe作为编译程序。然后单击“打开”按钮,可以发现PICC Compiler项已选择picc.exe作为编译程序。,同样,PICC Assembler和PICC Linker都选择picc.exe作为汇编和链接程序,如下图所示。,第
8、4步:选择完成后,单击上图OK按钮,完成选择工具组件的工作。,设置语言组件 其操作步骤如下:第1步:在MPLAB中打开Project-Set Language Toolsuite菜单,弹出如下图所示的设置语言组件对话框。,第2步:在设置语言组件对话框中,选择HI-TECH PICC Toolsuite项,单击HI-TECH PICC Toolsuite项前的“+”号,打开目录树,如下图所示。,上图中,子目录Executables下,PICC Compiler、PICC Assembler和PICC Linker在选择语言工具组件中都已选择picc.exe作为编译、汇编、链接的处理程序。第3步:
9、在目录树的子目录Default Search Paths&Directories下,Include Search Path和Library Search Path 分别选择C:HT-PICinclude和C:HT-PIClib作为搜索路径。,第4步:设置完包含文件路径和库文件路径后,在设置语言组件的对话框中,单击OK按钮,设置语言组件完成。至此PICC的项目设置完毕,可以对项目进行编译、调试和运行了。,三、PICC 基本编程,PICC 中的基本变量类型 PICC 支持的基本变量类型见下表:,PICC 中的高级变量 基于上表的基本变量,除了bit 型位变量外,PICC 完全支持数组、结构和联合等
10、复合型高级变量,这和标准的C 语言所支持的高级变量类型没有什么区别。例如:数组:unsigned int data10;结构体:struct commInData unsigned char inBuff8;unsigned char getPtr,putPtr;联合体:union int_Byte unsigned char c2;unsigned int i;,PICC 对数据寄存器bank 的管理 在PIC单片机编程时,单片机数据寄存器的bank是由编程员管理的,因此在定义变量时必须决定这些变量具体放在哪一个bank 中,缺省时,所定义的变量将被定位在bank0中,例如下面所定义的这些变
11、量:unsigned char buffer32;bit flag1,flag2;float val8;以上变量都是分布在bank0 中。定义在其它bank 内的变量前面必须加上相应的bank 序号,例如:bank1 unsigned char buffer32;/变量定位在bank1 中 bank2 bit flag1,flag2;/变量定位在bank2 中 bank3 float val8;/变量定位在bank3 中,volatile 修饰词 PICC 中还有一个特殊的变量修饰词“volatile”,用来说明一个变量的值是会随机变化的,即使程序没有刻意对它进行任何赋值操作。在单片机中,作为
12、输入的IO 端口其内容将是随意变化的;很多特殊功能寄存器的值也将随着指令的运行而动态改变。所有这种类型的变量必须将它们明确定义成“volatile”类型,例如:volatile unsigned char STATUS 0 x03;volatile bit commFlag;,标准库函数 PICC 提供了较完整的C 标准库函数支持,其中包括数学运算函数和字符串操作函数。在程序中使用这些现成的库函数时需要注意的是入口参数必须在bank0 中。如果需要用到数学函数,则用“#include”包含头文件;如果要使用字符串操作函数,就需要包含“#include”头文件。在这些头文件中提供了函数类型的声明
13、。直接查看这些头文件就可以知道PICC 提供了哪些标准库函数。C 语言中的格式化输出函数“printf/sprintf”用在单片机的程序中时要特别谨慎。printf/sprintf 是一个非常大的函数,一旦使用,你的程序代码长度就会增加很多。,C 和汇编混合编程,用C 语言进行单片机应用程序开发时经常要使用汇编语句。比如,单片机的一些特殊指令操作在标准的C 语言语法中没有直接对应的描述,例如PIC 单片机的清看门狗指令“clrwdt”和休眠指令“sleep”。这样,一个项目中就会出现C 和汇编混合编程的情形,我们在此讨论一些混合编程的基本方法和技巧。在C 原程序中直接嵌入汇编指令是最直接最容易
14、的方法。如果只需要嵌入少量几条的汇编指令,PICC 提供了一个类似于函数的语句:asm(“clrwdt”);,双引号中可以编写任何一条PIC 的标准汇编指令。例如:for(;)asm(clrwdt);/清看门狗asm(sleep);/休眠asm(“nop”);/空操作延时 如果需要编写一段连续的汇编指令,PICC 支持另外一种语法描述:用“#asm”开始汇编指令段,用“#endasm”结束。例如下面的一段嵌入汇编指令实现了将0 x200 x7F 间的RAM 全部清零:#asm movlw 0 x20 movwf _FSR clrf _INDF incf _FSR,f btfss _FSR,7
15、goto$-3#endasm,四、编程实例,PICC的头文件 PICC支持下的C程序代码中一定要包含pic.h头文件,该文件安装在HT-PICinclude目录下。它是很多头文件的集合,C编译器在pic.h中根据用户选择的芯片自动载入相应的其它头文件,例如用户选择的芯片是PIC16F877,则pic.h会把pic1687x.h载入;例如用户选择的芯片是PIC16F877A,则pic.h会把pic168xa.h载入。载入的头文件中其实是声明芯片的寄存器和一些函数。例如以下程序片段:static volatile unsigned char TMR00 x01;static volatile un
16、signed char PCL0 x02;static volatile unsigned char STATUS0 x03;,static volatile unsigned char PORTA 0 x05;static volatile unsigned char PORTB 0 x06;static unsigned char bank1 OPTION 0 x81;static unsigned char bank1 TRISA0 x85;static unsigned char bank1 TRISB 0 x86;符号表示地址的意思。这些声明和PIC汇编中的定义寄存器是差不多的,例如
17、:TMR0 EQU 0 x01;PCL EQU 0 x02;STATUS EQU 0X03;对于特殊功能寄存器的位地址在头文件中也有定义,比如PORTB端口的位地址定义如下:/*PORTB bits*/,static volatile bit RB7(unsigned)其中RB7代表PORTB的bit7。我们可以模仿pic.h文件的定义方法定义RAM中文件寄存器的方法,定义bank0的20h,21h,22h为文件寄存器以及他们的位地址。假设我们把 20h取名RAM20H,其位地址分别是RAM20H7RAM20H0 21h取名RAM21H,其位地址分别是RAM21H7RAM21H0 22h取名R
18、AM22H,其位地址分别是RAM22H7RAM22H0,定义如下:static volatile unsigned char RAM20H 0 x20;static volatile unsigned char RAM21H 0 x21;static volatile unsigned char RAM22H 0 x22;RAM20H的位地址定义如下:static volatile bit RAM20H7(unsigned),RAH21H和RAH22H的位地址也可以类似定义:static volatile bit RAM21H7(unsigned),将这些定义加入到相应的头文件中,或者直接写在
19、源程序中,这样就可以直接对通过变量RAM20H对bank0的20h单元进行字节操作,可以通过RAM20H7RAM20H0对bank0的20h单元进行位操作。例如:#includemain()RAM20H=0 x20;/把0 x20赋值给20hRAM21H=0 x21;/把0 x21赋值给21hRAM22H=RAM20H+RAM21H;while(1);以上程序中假设RAM20H、RAM21H和RAM22H的声明已经加入到相应的头文件中,那么在源程序中就可以直接使用。,简单的PICC程序【例1】由于PIC16F877指令系统中没有乘、除指令,用汇编语言编写乘、除运算难度较大,特别是多字节运算时。
20、可以用C语言编写就十分简便。#includemain()RAM20H=0 x20;RAM21H=0 x05;RAM22H=RAM20H*RAM21H;while(1);C和汇编的混合编程【例2】该例能将文件寄存器20h的低4位放入21h,高4位放入22h。,#includemain()RAM20H=0 x35;#asm MOVF 20H,W ANDLW 0FH MOVWF 21H SWAPF 20H,W ANDLW 0FH MOVWF 22H#endasmwhile(1);【说明】上例中,为了程序的可读性好就要减少汇编指令的使用量,由于C语言中没有SWAPF功能的语句,程序编程如下:,#inc
21、ludemain()RAM20H=0 x35;RAM22H=RAM20H;RAM21H=RAM20H【说明】程序中SWAPF 0 x22,F中的F已经定义过了,可以直接使用F。【例3】比较大小:判断21h和20h中数值的大小,大的放入到22h中。,#includemain()if(RAM21H RAM20H)RAM22H=RAM21H;elseRAM22H=RAM20H;while(1);【例4】对RAM中从30h开始的50个单元清零。用汇编编制:COUNT EQU 20H;指定20H寄存器为循环变量FSR EQU 04H;指定FSR为04HINDF EQU 00H;指定INDF为00H,MO
22、VLW D50;给循环变量赋初值 MOVWF COUNT MOVLW 30H;将30H FSR MOVWF FSR NEXT CLRF INDF;间接寻址 INCF FSR,1;地址指针内容加1,指向下一单元 DECFSZ COUNT,1;计数值减1,结果为0就;跳过下一条指令到STOP处 GOTO NEXT;跳转回去并执行下一次循环 STOP GOTO STOP;停机,混合编制:#includechar COUNT;/定义全局变量main()COUNT=50;FSR=0 x30;#asm INDF EQU 00H NEXT CLRF INDF INCF _FSR,F DECFSZ _COUN
23、T,F GOTO NEXT#endasm while(1);【说明】只要是C语言中定义的变量,在汇编中对其寻址时一定要用下划线。,实际上在头文件中INDF也有定义可以直接使用,那么上面的程序可以修改如下:#includechar COUNT;/定义全局变量main()COUNT=50;FSR=0 x30;INDF=0;#asm NEXT CLRF _INDF INCF _FSR,F DECFSZ _COUNT,F GOTO NEXT#endasm while(1);,用C编制【说明】假如程序员了解了FSR和INDF的工作原理后,上面的程序可以完全用C语言实现。#includechar COUN
24、T;/定义全局变量main()COUNT=50;FSR=0 x30;INDF=0;NEXT:INDF=0;FSR+;if(COUNT-0)goto NEXT;while(1);【说明】用C语言编写可读性强,但是占用的资源会多。,PICC的指向RAM的指针,【例5】将30h,31h,32h单元中最大的数放入40h#include static volatile unsigned char add1 0 x30;static volatile unsigned char add2 0 x31;static volatile unsigned char add3 0 x32;static volat
25、ile unsigned char add 0 x40;main()/绝对地址定位 add1=0 x51;add2=0 x5;add3=0 x60;if(add1add2)add=add1;else add=add2;if(add3add)add=add3;while(1);,【说明】本例中add1、add2、add3和add本质上都是地址,因此它们都是指向RAM的指针。在PICC中用指针进行寻址是通过FSR间接寻址的,因此add1=0 x51以及add=add1都是以间接寻址方式进行赋值的。以上程序中的指针add1、add2、add3和add都是指向bank0,假如要定义指向其他bank的指
26、针也是可以的。例如:#include/将绝对地址定位在不同的体static volatile unsigned char add1 0 x30;/实际地址=30hbank1 static volatile unsigned char add2 0 x31;/实际地址=(80h+31h)=0B1h,bank2 static volatile unsigned char add3 0 x32;/实际地址=80h*2+32h=132hbank3 static volatile unsigned char add 0 x40;/实际地址=80h*3+40h=1c0hint x,y,z;void com
27、parison()if(xy)z=x;else z=y;main()add1=0 x31;add2=0 x34;add3=0 x28;x=add1;y=add2;comparison();x=z;y=add3;comparison();add=z;while(1);,也可以直接对三个常数进行比较大小,然后将最大的数存放在于bank3的某单元中。#include/符号定义#define x_data 0 xe0;#define y_data 0 x60;#define z_data 0 x90;bank3 static volatile unsigned char add 0 x40;/实际地址=80h*3+40h=1c0hint x,y,z;void comparison()if(xy)z=x;else z=y;,main()x=x_data;y=y_data;comparison();x=z;y=z_data;comparison();add=z;while(1);,