;测量信号频率 ;MCU:89C51 ;晶振频率:12MHz ;外部频率信号接到 T0(P3.4) 引脚。
;测量误差 <= T1中断周期 / 测量周期 ;在本例中,测量误差 <= 0.1ms / 1.024s = 1/10240 ;本程序可用于测量较高频率的信号(高达1MHz),也可用于较低频率的信号(低达0.2Hz)。 ;且上面的测量误差公式与信号频率无关。
;在转帖本程序时请保留以下作者信息: ;作者:icmap 于 2008-12-27 深圳。 ;BLOG:http://blog.csdn.net/icmap
bseg at 00h bDataReady: dbit 1 ;中断函数已获得数据 bAllowGetData: dbit 1 ;允许中断函数更新数据 bFirstData: dbit 1 ;中断第一次获得数据,只有一组数据,还不能用于计算。 bRequestGetFreq: dbit 1
dseg at 30h dTime0: ds 1 ;单位 0.1ms dTime1: ds 1 ;单位 25.6ms dTime2: ds 1 ;单位 6.5536s
dTimeA0: ds 1 dTimeA1: ds 1 dTimeA2: ds 1 dCountA0: ds 1 dCountA1: ds 1 dCountA2: ds 1
dCountA0Old: ds 1 dCountA1Old: ds 1
dTimeB0: ds 1 dTimeB1: ds 1 dTimeB2: ds 1 dCountB0: ds 1 dCountB1: ds 1 dCountB2: ds 1
dGetFreqTimeOld: ds 1 ;单位 25.6ms
;==============================================================================; ;Code 段 cseg at 0 //程序起始地址 a_Start: mov ie,#00h ;关闭中断 sjmp a_Main
cseg at 001bh //定时器1中断 ljmp a_T1Int
;==============================================================================; a_Main: mov sp,#7 ;设置堆栈指针,只能在调用任何函数之前设定SP。 mov psw,#0 ;将RS0,RS1及其它标志位清0。
mov r0,#07fh ;内存00h~07fh的内容清零。 clr a a_Main01: mov @r0,a djnz r0,a_Main01
mov tmod,#25h ;T0:作计数器,16位 ;T1:作定时器,自动重装
; mov tmod,#21h ;测试时,可以将 T0 作为定时器来模拟外部计数
mov th1,#156 setb TR0 setb TR1
mov ie,#88h
;初始化状态 clr bDataReady ;中断函数还未获得数据 setb bFirstData ;还未获得两组数据 setb bAllowGetData ;允许中断函数获得数据 clr bRequestGetFreq ;没有到计算频率的时候 mov dGetFreqTimeOld,dTime1 ;重置等待时间的起点
a_Loop: ;主循环 lcall _GetFreq sjmp a_Loop
;------------------------------------- ;每隔大约 1 秒计算一次频率 ;如果想提高精度,可以加大间隔时间。 _GetFreq: jb bRequestGetFreq,a_GetFreq01 mov a,dTime1 clr c subb a,dGetFreqTimeOld clr c subb a,#40 ;40*25.6ms = 1.024s jc a_GetFreqEnd ;时间未到,直接结束 setb bRequestGetFreq ;等待时间已到,请求获得频率
a_GetFreq01: ;已请求获得频率,下面根据获得的数据计算频率 jnb bDataReady,a_GetFreqEnd ;如果中断函数未获得数据,则结束。 clr bDataReady clr bAllowGetData ;暂时禁止中断函数获得数据 jb bFirstData,a_GetFreq10 ;第一次获得数据时,只有一组数据,不计算,跳过 ;已经有两组数据,可以计算 nop ;在这里添加计算代码,具体除法的实现请自行解决。 nop ;当 dTimeA0 的单位为 0.1ms 时,频率计算公式如下 nop ;频率=10000*(dCountA-dCountB)/(dTimeA-dTimeB)
a_GetFreq10: mov dTimeB0,dTimeA0 mov dTimeB1,dTimeA1 mov dTimeB2,dTimeA2 mov dCountB0,dCountA0 mov dCountB1,dCountA1 mov dCountB2,dCountA2 clr bFirstData
clr bRequestGetFreq ;等待下次计算频率时机的到来 mov dGetFreqTimeOld,dTime1 ;重置等待时间的起点
a_GetFreqEnd: setb bAllowGetData ;重新允许中断函数获得数据 ret
;------------------------------------- ;定时器1中断处理函数 ;本函数的目的是将 Count 与时间对应起来。 ;也就是得到 Count 发生的时间。 a_T1Int: push psw push acc push b
mov a,dTime0 add a,#1 mov dTime0,a mov a,dTime1 addc a,#0 mov dTime1,a mov a,dTime2 addc a,#0 mov dTime2,a
jnb bAllowGetData,a_T1Int90 mov a,tl0 mov b,a xrl a,dCountA0Old jz a_T1Int90 ;如果 Count 没变化,则跳过。 ;目的是取得与 Count 尽量对应的时间。 ;误差不大于本中断的周期。 mov dCountA0Old,b
mov dTimeA0,dTime0 mov dTimeA1,dTime1 mov dTimeA2,dTime2
mov dCountA1,th0 ;读 Count mov dCountA0,tl0 mov a,th0 xrl a,dCountA1 jz a_T1Int01 mov dCountA0,tl0 a_T1Int01:
mov a,dCountA1 clr c subb a,dCountA1Old jnc a_T1Int02 inc dCountA2 a_T1Int02: mov dCountA1Old,dCountA1
setb bDataReady
a_T1Int90: pop b pop acc pop psw a_T1IntEnd: reti
END |