|
;================================== ;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:
|