搜索
bottom↓
回复: 7

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

[复制链接]

出0入0汤圆

发表于 2009-10-16 09:38:55 | 显示全部楼层 |阅读模式
最近在做一个用外部中断测量频率同时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)

阿莫论坛20周年了!感谢大家的支持与爱护!!

知道什么是神吗?其实神本来也是人,只不过神做了人做不到的事情 所以才成了神。 (头文字D, 杜汶泽)

出0入0汤圆

 楼主| 发表于 2009-10-16 13:32:56 | 显示全部楼层
现在又发现,计数上限值就算是设置成0xffff,频率也不是完全准确,还是有不准确的数据,只是出现的概率好像小了点,而且低频时数据不准确的概率大,实在是不知道问题出在哪里,大家有遇到过这样的情况的吗?

出0入0汤圆

 楼主| 发表于 2009-10-19 13:33:46 | 显示全部楼层
再补充一下,频率不准出现的错误数值大约是正确频率的两倍左右,而且错误的数据好像还特别有周期性,而不是随机的,下面是proteus仿真中串口监控的测出的频率值

(原文件名:1.jpg)
继续在线等大侠解答

出0入0汤圆

 楼主| 发表于 2009-10-20 09:24:04 | 显示全部楼层
不用函数信号发生器产生信号而用DCLOCK产生信号就准确了,真奇怪,难道函数信号发生器和AVR的PWM同时仿真时有冲突??不用PWM时信号发生器很好用,不去管他了,不是程序的事就好,呵呵,纠结了四天的问题终于解决了(好像也没有解决什么,哈哈)!

出0入0汤圆

发表于 2011-6-25 07:47:52 | 显示全部楼层
mark

出0入0汤圆

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

出0入0汤圆

发表于 2011-10-29 10:12:40 | 显示全部楼层
mark一下,希望审核早日通过
头像被屏蔽

出0入0汤圆

发表于 2013-2-18 19:04:22 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片。注意:要连续压缩2次才能满足要求!!】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-7-24 08:20

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表