文章
博客 网店

 Atmega48实现晶振频率测量仪


 AVR单片机的ICP(输入捕获)功能可用于频率的精确测量, 在输入信号的上升沿或者下降沿硬件将自动的把T/C1的计数值保存到ICR1寄存器,
 在发生两次输入捕获中断时可从两次中断时的时间印迹和T/C1的中断次数计算出两次中断的时间间隔,这便是信号的周期。当然、为了提高
 精度可一次测量多个周期。

 以下是Atmeag48制作的晶振频率计。原理图如下:



 用GAL16V8实现的256分频器ABEL HDL代码如下:

module COUNTER
title '8bit counter www.chipart.cn 2005-11'

COUNTER device 'P16V8R'; "使用GAL16V8

"CLK,OE的定义只能使用芯片约定的引脚
Clk,OC pin 1,11;
Q0,Q1,Q2,Q3,Q4,Q5,Q6,Q7 pin 19,18,17,16,15,14,13,12;

Output = [Q7,Q6,Q5,Q4,Q3,Q2,Q1,Q0];

equations
Output.clk = Clk; "输出与CLK有关
Output.oe = !OC; "输出受!OC才有效
WHEN !OC THEN "如果OC为低电平则
Output := Output + 1; "受时钟触发的锁存

end COUNTER "本模块程序结束


 该程序使用ABEL4编译,一般的编程器都支持GAL16V8的编程。ABEL 及 ABEL HDL请参考网络


实物照片(右边为与计算机间串行通信用的RS232电平转换电路)



测4MHz晶振时串口发送结果如上图所示
   本文主要目的为举例AVR单片机ICP功能及应用方法,对于高精度的频率测量没有深入的讨论,由于单片机所使用的晶振就是个普通的晶振,
 存在一定的误差,我想通过更换高精度温度补偿晶体会提高测量精度。

以下为Mega48源程序:

/*
文件名:main.c
功 能:晶体频率计程序
器 件:ATMEGA48-20PI
编 译:WINAVR20050214 

作者:芯艺 2005-12-6
*/

#include 
#include 
#include 

#include 
#include 

#define uchar unsigned char
#define uint unsigned int
#define ulong unsigned long 

uint g_aTimerValue[2]; //记录前后输入捕获时的时间印迹
uint g_bICP1Counter=0; //输入捕获中断次数计数器
uchar g_bCaptureFlag=0; //一次测量完成标记
uint g_bTimer1Counter=0; //定时器/计数器1溢出中断次数计数器 

char g_strFreq[10]; //用于打印数据的字符缓冲区

//误差不会太大的延时1ms函数 
void DelayMs(uint t)
{
   unsigned int i;
   for(i=0;i    _delay_loop_2((F_CPU/1e6)*250);
}
//定时器/计数器1溢出中断
SIGNAL(SIG_OVERFLOW1) 
{
   if(g_bICP1Counter)
   g_bTimer1Counter++;
}

//定时器计数器输入捕获中断
SIGNAL(SIG_INPUT_CAPTURE1)
{
   uint value=ICR1;
   if(g_bICP1Counter==0) //第一次中断 
   { 
      g_aTimerValue[0]=value;
      g_bICP1Counter=1; //输入捕获中断计数1
   }
   else if(g_bTimer1Counter>=100)
   {
      TIMSK1 =0; //禁止TIMER1相关中断
      TCCR1B = 0;
      g_aTimerValue[1]=value;
      g_bCaptureFlag=1; //测量完成标记置位
   }
   else
      g_bICP1Counter++; //输入捕获中断计数
}

//USART0异步写一字符
int put_char(char c)
{
   if(c=='\n')
   put_char('\r');

   while (!( UCSR0A & (1<    UDR0 = c;

   return 0;
}

//向USART0口打印字符串
void myprint(char *str)
{
   uchar i;
   for(i=0;i<100;i++)
   {
      if(str[i]==0)
         break;
      else
         put_char(str[i]);
   }
}

void Usart0Init(void)
{
   UBRR0L=77; //设置波特率9600 bit/s 12MHz:77 

   UCSR0B =_BV(TXEN0); //USART0发送允许

   UCSR0C = _BV(UCSZ01)|_BV(UCSZ00); //设置帧格式: 8 个数据位, 1 个停止位 
}

//一次测量操作
ulong GetFreq(void)
{
   double ret,tmp;

   g_bICP1Counter=0; 
   g_bTimer1Counter=0; 
   TCNT1=0;
   TIMSK1 =_BV(ICIE1)|_BV(TOIE1); //两个中断使能
   TCCR1B = _BV(CS10)|_BV(ICNC1); //不分频,开始计数,输入捕获滤波使能

   while(g_bCaptureFlag==0)
   DelayMs(1);

   //前后输入捕获中断时的定时器/计数器1值保存到了g_aTimerValue[2]
   //g_bTimer1Counter为第一次和最后一次输入捕获中断期间定时器/计数器1发生的溢出中断次数
   //g_bICP1Counter为测量期间输入捕获中断产生的次数
   g_bCaptureFlag=0;

   tmp=0x10000 * (g_bTimer1Counter-1);
   tmp+= (0x10000 - g_aTimerValue[0]) + g_aTimerValue[1];
   ret=12000000.0/tmp;

   return (ulong)(ret*g_bICP1Counter*256); 
}

//测试主程序
int main(void)
{
   ulong newfreq,oldfreq=0;

   Usart0Init(); 
   sei();

   while(1) 
   {
      newfreq=GetFreq();
      if(newfreq!=oldfreq)
      {   
         oldfreq=newfreq;
         myprint(ltoa(oldfreq,g_strFreq,10));
         myprint("\n");
      }
      else
         DelayMs(100);
   }//main loop 
}  


2009.06.19



芯艺工作室    蒙ICP备06005492号
Copyright© 2004-2023 ChipArt Studio All Rights Reserved