6.PICC 中的子程序和函数
中档系列的PIC 单片机程序空间有分页的概念,但用C 语言编程时基本不用太多关心代码的分页问题。因为所有函数或子程序调用时的页面设定(如果代码超过一个页面)都由编译器自动生成的指令实现。
函数的代码长度限制,一个良好的程序设计应该有一个清晰的组织结构,把不同的功能用不同的函数实现是最好的方法,因此一个函数 2K字长的限制一般不会对程序代码的编写产生太多影响。如果为实现特定的功能确实要连续编写很长的程序,这时就必须把这些连续的代码拆分成若干函数,以保证每个函数最后编译出的代码不超过一个页面空间。
调用层次的控制,中档系列PIC 单片机的硬件堆栈深度为8 级,考虑中断响应需占用一级堆栈,所有函数调用嵌套的最大深度不要超过7 级。编程员必须自己控制子程序调用时的嵌套深度以符合这一限制要求。
中断函数的实现,PICC可以实现C 语言的中断服务程序。中断服务程序有一个特殊的定义方法:
void interrupt ISR(void);
其中的函数名“ISR ”可以改成任意合法的字母或数字组合,但其入口参数和返回参数类型必须是“void ”型,亦即没有入口参数和返回参数,且中间必须有一个关键词“interrupt ”。中断函数可以被放置在原程序的任意位置。因为已有关键词“interrupt ”声明,PICC在最后进行代码连接时会自动将其定位到0x0004中断入口处,实现中断服务响应。编译器也会实现中断函数的返回指令“retfie”。
一个简单的中断服务示范函数如下:
void interrupt ISR(void) //中断服务程序
{
if (T0IE && T0IF) //判TMR0 中断
{
T0IF = 0; // 清除TMR0 中断标志
//在此加入TMR0 中断服务
}
if (TMR1IE && TMR1IF) //判TMR1 中断
{
TMR1IF = 0; //清除TMR1 中断标志
//在此加入TMR1 中断服务
}
} //中断结束并返回
PICC会自动加入代码实现中断现场的保护,并在中断结束时自动恢复现场,所以编程员无需象编写汇编程序那样加入中断现场保护和恢复的额外指令语句。但如果在中断服务程序中需要修改某些全局变量时,是否需要保护这些变量的初值将由编程员自己决定和实施。用C 语言编写中断服务程序必须遵循高效的原则:
1.代码尽量简短,中断服务强调的是一个“快”字。2.避免在中断内使用函数调用。3.避免在中断内进行数**算。
标准库函数,PICC提供了较完整的C 标准库函数支持,其中包括数**算函数和字符串操作函数。前加“#include ” 包含头文件,“#include ”头文件。
PICC 定义特殊区域值:
1 定义工作配置字,在用 PICC写程序时同样可以在C 原程序中定义,具体方式如下:
__CONFIG (HS & UNPROTECT & PWRTEN & BORDIS & WDTEN);
上面的关键词“__CONFIG ”(注意前面有两个下划线符)专门用于是芯片配置字的设定,后面括号中的各项配置位符号在特定型号单片机的头文件中已经定义(注意不是pic.h头文件),相互之间用逻辑“与”操作符组合在一起。这样定义的配置字信息最后将和程序代码一起放入同一个HEX文件。
在这里列出了适用于16F7x 系列单片机配置位符号预定义,其它型号或系列的单片机配置字定义方式类似,使用前查阅一下对应的头文件即可。
/*振荡器配置*/
#define RC 0x3FFF // RC 振荡
#define HS 0x3FFE // HS 模式
#define XT 0x3FFD // XT 模式
#define LP 0x3FFC // LP 模式
/*看门狗配置*/
#define WDTEN 0x3FFF // 看门狗打开
#define WDTDIS 0x3FFB // 看门狗关闭
/*上电延时定时器配置*/
#define PWRTEN 0x3FF7 // 上电延时定时器打开
#define PWRTDIS 0x3FFF // 上电延时定时器关闭
/*低电压复位配置*/
#define BOREN 0x3FFF // 低电压复位允许
#define BORDIS 0x3FBF // 低电压复位禁止
/*代码保护配置*/
#define UNPROTECT 0x3FFF // 没有代码保护
#define PROTECT 0x3FEF // 程序代码保护
头文件预定义的配置信息符号
定义芯片标记单元
PIC 单片机中的标记单元定义可以用下面的__IDLOC(注意前面有两个下划线符)预处理指令实现,方法如下:
__IDLOC (1234);
其特殊之处是括号内的值全部为16进制数,不需要用“0x”引导。这样上面的定义就设定了标记单元内容为01020304 。
C 和汇编混合编程
在C 原程序中直接嵌入汇编指令是最直接最容易的方法。如果只需要嵌入少量几条的汇编指令,PICC提供了一个类似于函数的语句:
asm(“clrwdt”);
双引号中可以编写任何一条PIC 的标准汇编指令。例如:
for (;;) {
asm("clrwdt"); //清看门狗
Task();
ClockRun();
asm("sleep"); //休眠
asm("nop"); //空操作延时
}
如果需要编写一段连续的汇编指令,PICC支持另外一种语法描述:用“#asm”开始汇编指令段,用“#endasm ”结束。一句话:用了C 语言后,就不要再老想着用汇编。尽量使用全局变量进行参数传递。
类似于纯汇编文件的代码也可以在C 语言框架下实现,方法是基于C 标准语法定义所有的变量和函数名,包括需要传递的形式参数、返回参数和局部变量,但函数内部的指令基本用嵌入汇编指令编写,只有最后的返回参数用C 语句实现。这样做后函数的运行效率和纯汇编编写时几乎一模一样,但各参数的传递统一用C 标准实现,这样管理和维护就比较方便。例如下面的实现一个字节变量的偶校验位计算。
bit EvenParity(unsigned char data)
{
#asm
swapf ?a_EvenParity+0,w //入口参数data 的寻址符为 ?a_EvenParity+0
xorwf ?a_EvenParity+0,f
rrf ?a_EvenParity+0,w
xorwf ?a_EvenParity+0,f
btfsc ?a_EvenParity+0,2
incf ?a_EvenParity+0,f
#endasm
//至此,data 的最低位即为偶校验位
if (data&0x01) return(1);
else return(0);
}