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
|
|