在PIC的单片机中有多种类型有内部RC振动器的功用,然后省去了晶振,不光节省了本钱,而且咱们还多了两个IO端口可以运用。
可是,因为RC振动器中电阻、电容的离散性很大,因而,在有内部RC振动器的单片机中,它的内部RAM中都会有一个名为OSCCAL的校准寄存器,经过置入不同的数值来微调RC振动器的振动频率。而且,单片机的程序存储器中,也会有一个特别的字来贮存工厂出产时测得的校准值。下面我以常用的12C508A和12F629为例加以阐明。
12C508A的复位矢量是程序的最高字0x1FF,这个字节出产商现已固定的烧写为MOVLW 0xXX,指令履行后,W寄存器中即为校准值XX,当咱们需求校按时,那么,在紧接着的地址0x0应该是一条这样的指令:MOVWF OSCCAL。接下去RC振动器就会以规范的振动频率运转了。
12F629的校准值也存放在最高字--0x3FF中,内容是RETLW 0xXX,但它的复位矢量却是0x0。这样,在咱们需求校准RC振动器时,在初始化过程中要加上下面两句:
CALL 0x3ff
MOVWF OSCCAL
当然,你还要留意寄存器的块挑选位。
曾经,我在做项目时,没太留意这个问题,这是因为在运用12C508A时,HI-TECH在进行编译时现已偷偷地替咱们做了这项作业。它会在程序的0x0处主动加一条MOVWF OSCCAL。用12F629做接纳解码替代2272时也没发生什么问题,可是在用被它作翻滚码解码器时却发现接纳间隔的离散性很大。经屡次试验总算找出是没对振动器的振动频率进行校对所至。
因而,需求别的编写用于校对的句子,我用了两种办法来完成这个意图:
1、用内嵌汇编的方式
#asm //此段汇编程序用于将坐落程序段3FFH的
call 3ffh //内部RC振动器的校准值放入校准寄存器,
bsf _STATUS,5 //在进行C言语调试时应屏蔽这段程序
movwf _OSCCAL
#endasm
2、用C言语规范方式
const unsigned char cs @ 0x3ff; //在函数体外
。..
OSCCAL=cs; //仿真时屏蔽此句
用这两种办法都有一个小缺点--仿真时,程序无法运转,这是因为C编译器并没有为咱们在0x3FF放置一条RETLW 0xXX的句子。因而,程序运转到这儿之后,并没有把一个常数(校准值)放入W寄存器然后回来,而是持续履行这条句子的下一句--0x0及其之后的程序,也就是说程序到此就乱了。因而如程序后边注释所示,在仿真时,应先屏蔽这几句程序。在程序调试完成后,需求烧写时,把注释符去掉,再编译一次就可以了。
我还有一种主意,不必屏蔽句子,那就是用函数来完成,就是在0x3FF起树立一个函数,函数体内只要一条句子,如下:
char jz()
{
return 0;
}
当然,还要考虑C函数回来时,一定会挑选寄存器0,实际上这个函数的开始地址应小于0x3FF。可是我找了我所能找到的参阅资料,并上网找了屡次,也没找到为函数肯定定位的办法,期望有知道的朋友点拨一下。
还有,12C508A是一次性编程的,而且0x1FF处的内容,咱们是无法改动的,也就是说你在此处编写任何指令,编程器都不会为你烧写,或者说即便烧写了也不会改动其间的内容。
可12F629是FLASH器材,可屡次编程,假如你没有成心挑选,正品的编程器(如Microchip的PICSTART PLUS)是不会对存有校准值的程序空间进行编程的。即便你无意中对这个程序空间进行了编程,你也可以用一条RETLW 0xXX放在0x3FF处再编程一次就可以了,但这个XX值可能是不正确的,需经试验确认(请参阅后边阐明)。
为了查验OSCCAL的值对振动器频率的影响,特编写了下面一个小程序进行验证:
#include
//*********************************************************
__CONFIG(INTIO & WDTDIS & PWRTEN & MCLRDIS & BOREN & PROTECT & CPD);
//内部RC振动器一般IO口;无效看门狗;上电延时;内部复位;掉电复位;代码维护;数据维护
//*********************************************************
#define out GPIO0 //界说输出端
#define jc GPIO3 //界说检测端
//*********************************************************
void interrupt zd(); //声明中止函数
//主函数***************************************************
void main()
{
CMCON=7;
OPTION=0B00000011; //分频比为1:16,
TRISIO=0B11111110;
GPIO=0B00000000;
WPU=0;
T0IF=0;
GIE=1;
T0IE=1;
while(1){
if(jc)OSCCAL=0xFF;
else OSCCAL=0;
}
}
//中止函数*************************************************
void interrupt zd()
{
T0IF=0;
out=!out;
}
程序其实很简单,就是在中止中让out脚的电平翻转,翻转的时刻为4096个指令周期,电平周期为8192个指令周期。而指令的周期又决定于RC时钟频率。在主程序中,不断的检测JC端口的电平,然后根据此端口电平的值修正OSCCAL寄存器的值。当然,最终从OUT脚的波形周期上反映出了OSCCAL寄存器的值改动。
经用示波器丈量(抱愧,手边没有频率计),JC端接地时,OUT端的电平周期为9.5毫秒左右;而JC端接正电源时,OUT端的电平周期为6毫秒左右。也就是说OSCCAL的值越大,单片机的时钟频率越高。而且,这个改变规模是很大的,因而,假如运用PIC单片机的内部RC振动器时,对其振动频率进行校对是十分必要的。这也是我在做翻滚码接纳解码器时,产品离散性很大的原因。望我们今后运用内部RC振动器时可以留意到此点。
但还有一点要留意,即便你对RC振动器进行了校对,你也别盼望这个4MHz的RC振动器肯定会很规范,实际上它仍是一个RC振动器,它的振动频率是电压、温度的函数,也就是说这个振动频率会跟着电压和温度的改变而改变,仅仅经校对后的值更挨近4MHz算了,这在产品开发的一开始就要留意的。