xlyxl 发表于 2009-10-16 09:38:55

求助用atmega88采用外部中断测量频率同时pwm输出时,频率测不准的问题

最近在做一个用外部中断测量频率同时pwm输出的实验时发现了频率老是测量不准确
测量频率是用定时器2计时得到频率两个上升沿的时间,pwm采用快速pwm模式
不输出pwm或者将pwm的计数上限值设置为0xffff,频率就准确了
但是pwm计数上限值只要不是最大值0xffff,无论pwm设置成8、9、0位快速pwm模式还是用OCR1A或ICR1记录计数上限值,频率都测不准确,实在不知道是问题出在哪里,恳请大家帮忙分析一下,先谢谢大家
PS:之前通过ICP捕捉测量频率,也有这个问题
下面是源程序

main.c源程序
#include <iom88v.h>
#include <macros.h>
#include "main.h"
#include "serial_ports_drive.c"

void timer1_init(void)
{
        TCCR1B = 0x00; //stop
        TCNT1= 0x0000;
        TCCR1A = 0x32;//10位快速PWM模式
        ICR1=0xffff;
        OCR1B=0;
        TCCR1B = 0x19; //start Timer
}

void timer2_init(void)
{
        TCCR2B = 0x00; //stop
        TCNT2= 0x00;
        TCCR2A = 0x00;
}


#pragma interrupt_handler int0_isr:2
void int0_isr(void)
{
        if(Pulse_flag==0)
        {
                TCCR2B = 0x02; //start Timer
                Pulse_flag=1;
        }
        else
        {
                TCCR2B = 0x00;//停止定时器
                Cycle=((((unsigned long)flow_flag)<<8)+TCNT2);
                /*
                USART_Transmit_str("Freque_value:");
                USART_Transmit_num( Freque_value );
                USART_Transmit_str(" flow_flag:");
                USART_Transmit_num( flow_flag );
                USART_Transmit_str("TCNT2:");
                USART_Transmit_num( TCNT2 );
                USART_Transmit_str("\r\n");
                */
                flow_flag=0;
                TCNT2= 0x00;
                Pulse_flag=0;
        }
}

#pragma interrupt_handler timer2_ovf_isr:10
void timer2_ovf_isr(void)
{
        flow_flag ++ ;
}

void port_init(void)
{
        PORTB = 0b00000001;
        DDRB= 0b00000100;
        PORTC = 0b00000000; //m103 output only
        DDRC= 0b11111111;
        PORTD = 0b00000000;
        DDRD= 0b00000000;
}

void init_devices(void)
{
        CLI();
        port_init();
        timer2_init();
        timer1_init();
        MCUCR = 0x00;
        EICRA = 0x03; //上升沿触发中断
        EIMSK = 0x01;

        TIMSK0 = 0x00; //timer 0 interrupt sources
        TIMSK1 = 0x00; //timer 1 interrupt sources
        TIMSK2 = 0x01; //timer 2 interrupt sources

        PCMSK0 = 0x00; //pin change mask 0
        PCMSK1 = 0x00; //pin change mask 1
        PCMSK2 = 0x00; //pin change mask 2
        PCICR = 0x00; //pin change enable
        PRR = 0x00; //power controller
        SEI();
}



void main(void)
{
        usart_init();
        init_devices();
        USART_Transmit_str("HaHa,Baud Rate is 19200,OK!\r\n");
        while(1)
        {
                Freque_value=((unsigned long)1000000/Cycle); //频率
                OCR1B=Freque_value;
                USART_Transmit_str("Freque_value:");
                USART_Transmit_num( Freque_value );
                USART_Transmit_str("\r\n");//*/
        }
}

main.h源程序:
unsigned int flow_flag=0; // 用于标记 定时器溢出
unsigned long Cycle,Freque_value;//周期(单位um)
unsigned char Pulse_flag=0;//脉冲标志位


serial_ports_drive.c源程序:
//串口通信初始化(波特率9600,当系统频率为8M时)
void usart_init(void)
{
        UCSR0A = 0x02;                                        //使用波特率倍增,不使用多从机模式
        UCSR0C = 0x06;                                        //异步串口模式,禁止奇偶校验,1位停止位,8-bit桢模式
        UBRR0L = 0x33;                                        //设置波特率,19200(有一定误差)
        UBRR0H = 0x00;
        UCSR0B = 0x08;                                        //禁止完成中断,禁止发送完成中断,禁止发送寄存器空中断,禁止接收,允许发送
}

//向串口发送一个字节的数据
void USART_Transmit( unsigned char data )
{
        while(!(UCSR0A&(1<<UDRE0)));// 等待发送缓冲器为空
        UDR0 = data;// 将数据放入缓冲器,发送数据
}


//向串口发送字符串
void USART_Transmit_str( unsigned char *str )
{
        while (1)
        {
                if(*str == '\0')break;
                USART_Transmit(*str++);
        }
}


//向串口发送ASCII码数字
void USART_Transmit_num( unsigned int data )
{
        USART_Transmit(data/10000+48);
        USART_Transmit((data%10000)/1000+48);
        USART_Transmit((data%1000)/100+48);
        USART_Transmit((data%100)/10+48);
        USART_Transmit(data%10+48);
}

源程序和proteus仿真ourdev_492175.rar(文件大小:56K) (原文件名:1.rar)

xlyxl 发表于 2009-10-16 13:32:56

现在又发现,计数上限值就算是设置成0xffff,频率也不是完全准确,还是有不准确的数据,只是出现的概率好像小了点,而且低频时数据不准确的概率大,实在是不知道问题出在哪里,大家有遇到过这样的情况的吗?

xlyxl 发表于 2009-10-19 13:33:46

再补充一下,频率不准出现的错误数值大约是正确频率的两倍左右,而且错误的数据好像还特别有周期性,而不是随机的,下面是proteus仿真中串口监控的测出的频率值
http://cache.amobbs.com/bbs_upload782111/files_20/ourdev_493379.jpg
(原文件名:1.jpg)
继续在线等大侠解答

xlyxl 发表于 2009-10-20 09:24:04

不用函数信号发生器产生信号而用DCLOCK产生信号就准确了,真奇怪,难道函数信号发生器和AVR的PWM同时仿真时有冲突??不用PWM时信号发生器很好用,不去管他了,不是程序的事就好,呵呵,纠结了四天的问题终于解决了(好像也没有解决什么,哈哈)!

cuikai12345 发表于 2011-6-25 07:47:52

mark

holyrose 发表于 2011-10-28 09:36:50

把单片机的 CCP1 模块设置为捕捉模式,先把CCP1设置为捕捉脉冲的上升沿。当脉冲信号上升沿到来时。触发 CCP 中断,并在中断服务程序中记录下此时 TMR1 寄存器中 16位的值T1 :然后把 CCP1 模块设置成捕捉脉冲下降沿,当脉冲信号下降沿到来时,再次触发 CCP中断。并在中断服务程序中记录下此时TMR1 寄存器中 16 位的值 T2 ;最后把 CCP1模块设置成捕捉脉冲上升沿,当脉冲信号上升沿到来时,触发 CCP中断,并在中断服务程序中记录下此时 TMR1 寄存器中 16 位的值T3 ,这样就完成了 PWM信号一个周期的测量。

souching 发表于 2011-10-29 10:12:40

mark一下,希望审核早日通过

5768340 发表于 2013-2-18 19:04:22

页: [1]
查看完整版本: 求助用atmega88采用外部中断测量频率同时pwm输出时,频率测不准的问题