打印

请教一下pic频率测量,谢谢

[复制链接]
4213|6
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 zhaosr19 于 2011-9-5 19:53 编辑

使用内部晶振16MHz,由于硬件上设计的问题
现在可以使用21脚,INT中断,读取Timer1的值算频率,但算出的来的很不准,输入27Hz的占空比13%方波,测出的频率什么值都有,太怪了
测量原理是:Timer1一直在跑,int脚(21脚)在方波下降沿读取timer1的值,第一次不计算频率,第二次中断后开始,每次减掉前面一次的值,算频率g_fFreq

请问:1)这种方法测量频率有问题?我的频率范围在0-200Hz
2)帮忙看看代码有问题?



#include <htc.h>
#ifndef _XTAL_FREQ
#define _XTAL_FREQ 16000000/* 内部晶振16MHz */
#endif
#define _XTAL_FREQ_1_16 1000000/* 1/16 * _XTAL_FREQ */
#define _TIMER1_FREQ 500000.0 /* 1/4 Focs,8分频 */
volatile unsigned short long g_uslT1 =0;
volatile unsigned short long g_uslT2 =0,g_uslT3=0;
unsigned char ucFlagTemp1 =0, ucFlagTemp2 =0, g_fFreq = 0;
void Init(void)
{
TRISB = 0b00100001;
ANSELB = 0x00;
WPUB = 0b00100001;/* 1 pull-up enabled */
/* portb电平变化中断
IOCB = 0b00100001;/* 1 = Interrupt-on-change enabled */
/* timer1 */
T1CON = 0b00110101;/* 1/4 *Fosc ,预分频8*/
T1GCON = 0x00;

/* interrupt */
INTCON = 0b10100000; /* T0IE =1,T0IF = 0 */
//RBIE = 1;
INTE = 1;
INTF = 0;
T0IE = 0;
__delay_us(1000);/*1000us*/
}
void interrupt my_isr(void)
{
if (INTF == 1 && INTE == 1)
    {
        ucFlagTemp1 =TMR1H;
        ucFlagTemp2 =TMR1L;        
        INTF = 0;
        if (0 != g_uslT1)
        {
            g_uslT2 = ucFlagTemp1*256+ucFlagTemp2;
            //g_uslT2 = TMR1H;
            //g_uslT2 = g_uslT2<<8+TMR1L;
            if (g_uslT2 > g_uslT1)
            {
                g_uslT3 = g_uslT2 - g_uslT1;
            }
            else/* 溢出处理 */
            {
                g_uslT3 = g_uslT2 +65535- g_uslT1;
            }
            
        }
        
        g_uslT1 = ucFlagTemp1*256+ucFlagTemp2;
        //g_uslT1 = TMR1H;
        //g_uslT1 = g_uslT1<<8 + TMR1L;
    }
}
void main(void)
{
unsigned char ucAdValInit = 0;
volatile unsigned char ucAdVal = 0;
volatile unsigned short long uslT1 =0;
volatile unsigned short long uslT2 =0;
volatile float fFreq = 0;
unsigned char ucAngle = 0;
unsigned char ucTemp = 0;
unsigned char i = 0 ,j = 0;
//ubFlagBit.ucFlag = 0b11000000;
Init();
while(1)
{
//Light_LAMP();
/*
ubFlagBit.uOutFlag = 0;
ubFlagBit.bFlag = 0;
ubFlagBit.ucFlagNorOrHigh =0;
*/
ucAngle = 0;
if (0 != g_uslT3)
{
g_fFreq = _TIMER1_FREQ/g_uslT3;/* 计算频率 */
g_uslT3 = 0;
}
}
return;
}
沙发
兰天白云| | 2011-9-1 08:02 | 只看该作者
如果诚心需要帮助,请加点注释,其他工程师都是很忙的,也给他们省点时间 待测频率0~200,包括0?

1:频率高的话,一般固定时间,在固定的时间内数脉冲个数,然后计算
2:频率低的话,一般固定脉冲个数,在设定的脉冲数内计时,然后计算,前提是脉冲周期严格相等
3:注意溢出控制

使用特权

评论回复
板凳
zhaosr19|  楼主 | 2011-9-1 12:07 | 只看该作者
谢谢,
我刚才增加了一些注释,
这个程序功能比较简单,我也说明原理来,应该很快就可以看懂

我担心Timer1定时器,我的设置有问题,或计算有问题,想请大侠帮忙看一下,谢谢

使用特权

评论回复
地板
virtualtryon| | 2011-9-1 22:04 | 只看该作者
有一个很严重的问题:
ucFlagTemp1 =TMR1H;
ucFlagTemp2 =TMR1L;
这两条语句编译以后出来的汇编有可能有好几条了.
先选TMR1的BANK,再取到工作寄存器,选ucFlagTemp1的bank,存入ucFlagTemp1所以第一条语句可能会有>=4条汇编指令.
指令周期为0.25uS,TIMER1的时钟为2uS,从存完TMR1H到存TMR1L会有至少1us的时间差。
如果TIMR1的时钟到了Q3以上,那就有问题了。
TIMR1在存高低字节的时候有一个时钟发生。
举个例子,读TMR1H时TMR1=0X00FF.
TMR1H存为0x00
等存TMR1L时,TMR1已经加一了成为0x0100
那么TMR1L存为0x00,最终LZ得到的是0x0000,所以LZ不妨改为
TMR1ON=0;
ucFlagTemp1 =TMR1H;
        ucFlagTemp2 =TMR1L;   
TMR1ON=1;
如果是18系列单片机,有一个RD16的位,可以设置将TMR1H缓存。

使用特权

评论回复
5
virtualtryon| | 2011-9-1 22:19 | 只看该作者
g_uslT3 = g_uslT2 +65535- g_uslT1;
这一条语句最好是这样写
g_uslT3 = 65535-g_uslT1+g_uslT2;
g_fFreq = _TIMER1_FREQ/g_uslT3;/
在做这个运算的时候,有可能会发生中断,会导致高,低字节被破坏。
对于这种中断和主程序两个任务共享的资源,最好做一下访问互斥处理。、
在中断中可以做一个简单的判断
if(g_uslT3 == 0)
此外,考虑一下精度。
LZ要测量信号的最小周期为5ms,保存现场的时间差不多在2us左右,所以中断的测量误差可以忽略。

g_fFreq = _TIMER1_FREQ/g_uslT3;/* 计算频率 */
上面这个运算做一下四舍五入
g_fFreq = _TIMER1_FREQ + g_uslT3 >> 1;
g_fFreq = g_fFreq / u_uslT3;

使用特权

评论回复
6
zhaosr19|  楼主 | 2011-9-2 00:18 | 只看该作者
谢谢,现在读准了,非常准

unsigned char g_fFreq;
g_fFreq = _TIMER1_FREQ/g_uslT3+0.5;

g_fFreq = (_TIMER1_FREQ + g_uslT3>>1)/g_uslT3
这个有什么区别? 前面算出的频率是实际频率的2倍,但下面这个算法是准的

使用特权

评论回复
7
virtualtryon| | 2011-9-2 12:16 | 只看该作者
没有差别.
_TIMER1_FREQ/g_uslT3+0.5;
前面要浮点数运算.
单片机做浮点数运算不合适.
耗时.

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

22

主题

59

帖子

0

粉丝