;==================================
;50%占空比输入:FP_PULSE_IN
;输入量:
;输出量:nowf
;说明:我们对定时器A的计数器0的I/O引脚的输入信号记数来得到一个值N,通过计算来得到当前频率nowf
;nowf=F_MAX*1,000,000,000/(N*FP_PULSE*1000*100)=F_MAX*40000/(N*FP_PULSE)
;我们采用查询的方法,对于采样的N值,我们采用中位值平均滤波法(又称防脉冲干扰平均滤波法)来提高精度
;我们不用冒泡排序法,而是采用插值排序,边采样边排序,算法的时间复杂度要小,空间上可省去数据暂存temp
;我们对<3Hz的频率不于考虑。也就是说N>#$65b9aa时丢弃
;用到了中断,优先级1(最低)
;==================================
FP_PULSE_IN:
;--初始化定时器寄存器
; bfset #$0100,x:USER1FLAG
; bcc first_one
; bfset #$00c0,x:TMRA0_SCR
; bra bg
;first_one:
bfset #$04c0,x:TMRA0_SCR ;上升沿和下降沿都捕捉
;bg:
move #$3000,x:TMRA0_CTRL ;将位5清零,否则会出现只计数一次的情况
bftsth #$2000,x:TMRA0_SCR ;判断是否溢出,如果溢出则加1
jcc try_result
move x:n_high16,x0
add #1,x0
move x0,x:n_high16
cmp #$cb,x0 ;1分频的最大值的两倍
jcc clr_h ;如果一没有脉冲过来我们变清空,从头开始记数
bfclr #$2000,x:TMRA0_SCR ;清溢出标志位
try_result:
move x:cap_low,x0 ;来判断是否有计算完了一个脉冲的值
cmp #0,x0
jeq cmp_continue ;如果等于0,则说明
jmp result_ok
cmp_continue:
move x:cap_high,x0 ;来判断是否有计算完了一个脉冲的值
cmp #0,x0
jeq FP_PULSE_IN_END ;两个值都为0,则说明没有记录完一个数据
;--下面的情况则是,记录完一个周期,我们可以把最终结果放到b中,给下面的处理程序用
result_ok:
move x:cap_low,b0
move x:cap_high,b1
;--*****由于使用中断所以产生了延时,分情况来调整采样数据
move x:F_MAX,x0
cmp #30001,x0
bcs small_data
move #$44,y0 ;*****我们用加数来调整结果
bra just_quick
small_data: move #$45,y0
just_quick:
move #0,y1
add y,b
move #0,x:cap_low
move #0,x:cap_high ;清掉最终结果存放区,等着下一次采集数据的存放
move #0,x:n_high16 ;清除高16位
move #0,x:int_num ;重新清0,从头开始计算
; bfset #$0400,x:TMRA0_SCR ;开中断
;最后的数据放在b里面
;--下面开始软件滤波,我们选用插值法,排序完成后我们滤去极大极小两个值,然后在求平均值
move x:n_num,x0
add #1,x0
move x0,x:n_num ;更新数据个数,原来为0的现在更新为1个
cmp #1,x0
jne big_one
;--说明是第一次有数据进来
move #$0161,r1
nop
move b0,x:(r1)+ ;指针往后挪一位,指向高字节
nop
move b1,x:(r1) ;采集的第一个数完成,则回到主循环,等待采集下一个数据
jmp FP_PULSE_IN_END
big_one:
;--说明不是第一次来数据了
move x:n_num,x0 ;取当前采样的数据个数值,r1作为偏移地址来用
sub #1,x0
lsl x0 ;一个数据占了两个16位,所以要*2
add #$0160,x0
move x0,r1 ;指针指向采集数据队列的最后一个数据的高位
loop:
nop
move x:(r1)-,y0 ;待比较的数据放y0,此时r1的值已经改变了
move b1,y1
cmp y0,y1 ;先比较高16位
jeq order_cmp_goon
jcc just_caiy_big ;采样值大的情况
;--采样值小的情况,必须还得把最后的数据(比当前采样值大的那个数)往后挪一个位置
;--只比较了高位,调整一下指针,然后转到order_cmp_goon中的数据往后移位执行即可
move r1,x0
sub #1,x0
move x0,r1
jmp just_loop ;开始移动大的数据
order_cmp_goon:
nop
move x:(r1)-,y0 ;再比较低16位
move b0,y1
cmp y0,y1
jcc caiy_big ;采样值大的情况
;--采样值小的情况,必须还得把最后的数据(比当前采样值大的那个数)往后挪一个位置
just_loop: ;--高、低位都比较过了
move r1,x0
add #1,x0
move x0,r1
nop
move x:(r1)+,y0 ;原先数据列最后一个数据的低16位
nop
move x:(r1)+,y1 ;高16位
nop
move y0,x:(r1)+
nop
move y1,x:(r1) ;此时指针已经是新的数据列的最后一个数的高位了
;--考虑是否已经到了最前面了,如果是,就直接把数据插入,然后判断是否已经记满了10个数据了
move r1,x0
cmp #$164,x0
jeq only_one
;说明还没比较到最开头的一个数据,还可以比较
move r1,x0
sub #4,x0
move x0,r1 ;使指针指向除去大数以后的新数列的最后一个数据的高位
jmp loop
only_one: move r1,x0
sub #2,x0
move x0,r1
nop
move b1,x:(r1)-
move b0,x:(r1)
jmp over_y_n
just_caiy_big:
;--;采样值大的情况,只需要插入当前位置后一个位置即可
;--现在的指针只是比较了一个高位的指针,和下面caiy-big进入的情况不同,它已经比较了低位了
move r1,x0
sub #1,x0
move x0,r1
caiy_big:
move r1,x0
add #4,x0
move x0,r1 ;此时指针指向最后空位数据的高位
nop
move b1,x:(r1)-
move b0,x:(r1) ;此时刚刚采集的数据已经插入了
over_y_n:
move x:n_num,x0 ;取数据长度
cmp #10,x0 ;看是否采集完成
jcc work_sum ;数据采集已全完成,准备求平均值
jmp FP_PULSE_IN_END ;一次采集完成
work_sum:
move #0,b0 ;初始化为0
move #0,b1
move #0,x0 ;用来存放加法循环的比较次数
move #$0166,r1 ;第一个数为最小的,是我们要过滤掉的
nop
move x:(r1),y1 ;我们取四个数据的高位和低位来凑成准确的两个数据
move #$0168,r1
nop
move x:(r1),b1
nop
move #$016d,r1
nop
move x:(r1),y0
move #$016f,r1
nop
move x:(r1),b0
add y,b
;下面计算平均值
move #0,b2
rep #1 ;让下面一条指令执行4次,也就是除以#16
asr b ;****最终的结果n在b中,是个32位的数****
;--如果得到的频率<5Hz则丢弃
move #$65,x0 ;n最大可能值为#$65b9aa
cmp b1,x0
jcs FP_PULSE_IN_END
jeq may_big
jmp start_change
may_big:
move b0,x0
cmp #$b9ab,x0
jcc FP_PULSE_IN_END
start_change:
;--以下为N值得到nowf的计算程序,我们可以采用自动发波的方法,把n值归到16bit即可,这样可以避免出现32位/32位的情况
move #0,b2 ;是为了用算术右移而清的0
move #0,y0 ;用来存放移动次数的
move y0,x:plus_in
just_in:
move b1,y1
cmp #0,y1
jeq start_now
asr b
add #1,y0 ;我们可以把y0作为下面的分频来看待,调整后的n在b0
move y0,x:plus_in
jmp just_in
start_now:
;--低通滤波程序,要变化快,则小的数变大,大的数变小,但是和一定为1024
move b0,x:jieguo
move #51,y0 ;原先是#51
move x:jieguo,x0
mpysu x0,y0,a
asr a
move #973,y0 ;原先是#973
move x:jieguo2,x0
mpysu x0,y0,b
asr b
add b,a ;无符号计算要注意
rep #6
asl a
move a1,x:jieguo2
move a1,b0
;--低通滤波结束
move x:F_MAX,y0 ;我们先做上限频率*#20000(可以提高精度)这里我们应该乘#40000是因为采集的是一个周期的时间,放到最后调整
move #20000,x0
mpysu x0,y0,a
asr a
asl a ;--调整结果,我们实际得到的是一个周期,所以要*2
;--n是16bit,准备调用div_32by16
move a0,y0
move a1,a0
move b0,x0 ;b0只有低16位有用,高16位为0,因为上面调整过了
;--得到的结果在a0,y0中
jsr div_32by16
move a0,b1
move y0,b0 ;将结果放入b中最大值#$2faf0800
move x:plus_in,y1 ;是n值除2的次数,也可以看成分频数
cmp #0,y1 ;如果为0则无须乘该数,否则出错
jeq direct
move #1,x0
move #2,y0
do y1,end
impy y0,x0,a
move a1,x0
end:
move x0,y1
move x:FP_PULSE,y0
mpysu y0,y1,a
asr a ;得到的结果肯定a1=0,最大值10
jmp chu
direct: move x:FP_PULSE,a0
chu:
;--准备调用div_32by16
move a0,x0
move b1,a0
move b0,y0
jsr div_32by16
move a0,a1
move y0,a0 ;得到的结果最大为#40000,所以只有a0有用
;--下面我们开始四舍五入调整结果
move x:F_MAX,x0
cmp #10001,x0
jcs small_max
move #20,x0
bra startdiv
small_max:
move #10,x0
startdiv:
asl a
bfclr #$1,sr
rep #16
div x0,a
move a0,y1
add x0,a
; asr a ;我们不调整了,直接判断四舍五入
move a1,y0
cmp y0,x0
jcc small_ok
add #1,y1
small_ok:
mpysu x0,y1,a
asr a ;得到最终结果
;--保证不要超过最大频率
move x:F_MAX,x0
move a0,y0
cmp y0,x0
jcc no_problem
move x0,a0 ;如果超过了最大频率则赋予最大频率值
no_problem:
move a0,x:iout00
jmp clr_data
clr_h:
move #0,x:iout00 ;没有脉冲过来则清0
clr_data:
move #0,x:n_high16 ;将用来捕捉高16位的数据单元清空,为了下次捕捉
;--初始化接收数据区,最好是系统复位或者是开机时候初始化一下,因为要放到主程序里采集数据所以放在前面初始化不行
move #0,x:n_num
move #0,x:n_buf1_1
move #0,x:n_buf1_2
move #0,x:n_buf2_1
move #0,x:n_buf2_2
move #0,x:n_buf3_1
move #0,x:n_buf3_2
move #0,x:n_buf4_1
move #0,x:n_buf4_2
move #0,x:n_buf5_1
move #0,x:n_buf5_2
move #0,x:n_buf6_1
move #0,x:n_buf6_2
move #0,x:n_buf7_1
move #0,x:n_buf7_2
move #0,x:n_buf8_1
move #0,x:n_buf8_2
move #0,x:n_buf9_1
move #0,x:n_buf9_2
move #0,x:n_buf10_1
move #0,x:n_buf10_2
move #0,x:n_high16
move #0,x:int_num
move #0,x:int_cap1
move #0,x:int_cap3
move #0,x:int_cap3_h
move #0,x:plus_in
FP_PULSE_IN_END: