Chinaunix首页 | 论坛 | 博客
  • 博客访问: 138771
  • 博文数量: 35
  • 博客积分: 1410
  • 博客等级: 上尉
  • 技术积分: 289
  • 用 户 组: 普通用户
  • 注册时间: 2010-06-21 20:39
文章分类

全部博文(35)

文章存档

2010年(35)

我的朋友

分类: 嵌入式

2010-08-21 06:51:07

在PIC的单片机中有多种型号有内部RC振荡器的功能,从而省去了晶振,不但节省了成本,并且我们还多了两个IO端口可以使用。
  但是,由于RC振荡器中电阻、电容的离散性很大,因此,在有内部RC振荡器的单片机中,它的内部RAM中都会有一个名为OSCCAL的校准寄存器,通过置入不同的数值来微调RC振荡器的振荡频率。并且,单片机的程序存储器中,也会有一个特殊的字来储存工厂生产时测得的校准值。下面我以常用的12C508A和12F629为例加以说明。
  12C508A的复位矢量是程序的最高字0x1FF,这个字节生产商已经固定的烧写为LW 0xXX,指令执行后,W寄存器中即为校准值XX,当我们需要校准时,那么,在紧接着的地址0x0应该是一条这样的指令:MOVWF OSCCAL。接下去RC振荡器就会以标准的振荡频率运行了。
  12F629的校准值也存放在最高字--0x3FF中,内容是RETLW 0xXX,但它的复位矢量却是0x0。这样,在我们需要校准RC振荡器时,在初始化过程中要加上下面两句:
    CALL  0x3ff
    WF OSCCAL
  当然,你还要注意寄存器的块选择位。
  以前,我在做项目时,没太注意这个问题,这是因为在使用12C508A时,HI-TECH在进行编译时已经偷偷地替我们做了这项工作。它会在程序的0x0处自动加一条WF OSCCAL。用12F629做接收解码代替2272时也没发生什么问题,但是在用被它作滚动码解码器时却发现接收距离的离散性很大。经多次试验终于找出是没对振荡器的振荡频率进行校正所至。
  因此,需要另外编写用于校正的语句,我用了两种方法来实现这个目的:
  1、用内嵌汇编的形式
  #asm   //此段汇编程序用于将位于程序段3FFH的
   call 3ffh  //内部RC振荡器的校准值放入校准寄存器,
   bsf _STATUS,5  //在进行C语言调试时应屏蔽这段程序
   wf _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罢了,这在产品开发的一开始就要注意的。
阅读(7300) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~