guxiangdeyun 发表于 2007-10-17 22:15:09

在固定快速PWM模式下,因引入变量的传递而使频率改变的问题(急,求救,马老师)

马老师
您好!好不容易找到这个网站,感觉好象是找到救星了,希望您能够给我指点一二,在下不胜感激!
我的问题是这样的,我做的是用ATmega8控制spwm波形,频率保持不变,但要求对幅值进行调整,调整办法是通过匹配值乘或除一个系数.我的部分程序是这样的:
#include <iom8v.h>
#include <macros.h>

#pragma data:code
// 128点正弦波样本表
const unsigned int auc_SinParam = {0, 6, 13, 19, 25, 31, 38, 44, 50, 56, 62, 69, 75, 81, 87, 92,
98, 104, 110, 115, 121, 127, 132, 137, 143, 148, 153, 158, 163, 168, 172, 177,
181, 186, 190, 194, 198, 202, 206, 210, 213, 217, 220, 223, 226, 229, 231, 234,
236, 239, 241, 243, 245, 246, 248, 249, 251, 252, 253, 253, 254, 255, 255, 255,
255, 255, 255, 254, 253, 253, 252, 251, 249, 248, 246, 245, 243, 241, 239, 236,
234, 231, 229, 226, 223, 220, 217, 213, 210, 206, 202, 198, 194, 190, 186, 181,
177, 172, 168, 163, 158, 153, 148, 143, 137, 132, 127, 121, 115, 110, 104, 98,
92, 87, 81, 75, 69, 62, 56, 50, 44, 38, 31, 25, 19, 13, 6, 0,
0, 6, 13, 19, 25, 31, 38, 44, 50, 56, 62, 69, 75, 81, 87, 92,
98, 104, 110, 115, 121, 127, 132, 137, 143, 148, 153, 158, 163, 168, 172, 177,
181, 186, 190, 194, 198, 202, 206, 210, 213, 217, 220, 223, 226, 229, 231, 234,
236, 239, 241, 243, 245, 246, 248, 249, 251, 252, 253, 253, 254, 255, 255, 255,
255, 255, 255, 254, 253, 253, 252, 251, 249, 248, 246, 245, 243, 241, 239, 236,
234, 231, 229, 226, 223, 220, 217, 213, 210, 206, 202, 198, 194, 190, 186, 181,
177, 172, 168, 163, 158, 153, 148, 143, 137, 132, 127, 121, 115, 110, 104, 98,
92, 87, 81, 75, 69, 62, 56, 50, 44, 38, 31, 25, 19, 13, 6, 0
};
#pragma data:data
void timer2_ovf_isr(void);
void port_init(void);
void init_devices(void);
void adc_init(void);
unsigned char x_SW = 1,X_LUT = 0;
unsigned int adc_data;
float volt = 1.5;   ????????
void main(void)
{
init_devices();
   TCCR2 = 0x69; // 快速PWM模式,分频系数=1,正向控制OC0
   TIMSK = 0x40; // T/C0溢出中断允许
   SEI(); // 使能全局中断
   //for (;;)
   while(1)
    {}
}
#pragma interrupt_handler timer2_ovf_isr:5
void timer2_ovf_isr(void)
{   
   if (X_LUT >255)
    { X_LUT= 0; // 样点指针调整
      }
                //把AD采样值送给匹配寄存器

OCR2=(int)((float)((auc_SinParam)/volt+0.5));//把上面的数表除以1.5,四舍五入后取整,按理频率不会改变,但是以外的是滤波以后的正玄波频
                                                       率改变了,但是当把上面的数表重新除以1.5,四舍五入后取整重新建立一个新数表代替前面的
                                                         数表的,赋 给匹配值,这个结果很好,频率没有改变,这到底是怎么回事情啊 ???????????
   

// 取样点指针到比较匹配寄存器
       
       X_LUT += x_SW; // 新样点指针

}

void port_init(void)
{
PORTB = 0xFF;
DDRB= 0xFF;
PORTC = 0x00; //m103 output only
DDRC= 0x00;
PORTD = 0xFF;
DDRD= 0xFB;
}

//call this routine to initialise all peripherals
void init_devices(void)
{
//stop errant interrupts until set up
CLI(); //disable all interrupts

MCUCR = 0x00;
GICR= 0x00;
TIMSK = 0x00;
port_init();
adc_init();
} //timer interrupt sources
//re-enable interrupts
//all peripherals are now initialised

而且假如把float volt = 1.5;改成int volt=1 , OCR2=(int)((float)((auc_SinParam)/volt+0.5));改成OCR2=(int)(auc_SinParam)/volt);频率竟然改变了,真的很奇怪,不就把赋了个变量么?问题怎么会这么严重,难道非要写成
OCR2=auc_SinParam;才对吗,哪个volt是AD采样值,必须的用.
救救我吧,马老师!

machao 发表于 2007-10-18 00:24:43

在我的讲义中已经明确的说明一个原则:中断服务程序应尽可能的短。

你的中断中使用了 OCR2=(int)((float)((auc_SinParam)/volt+0.5))这样的语句,这是浮点运算,需要占用大量的时间。你的中断服务时间太长,估计已经超过了一次T2的比较匹配甚至是全程计数的时间了。

举例:T2从溢出中断后,继续从0开始加1。你的中断经过长时间计算,得到OCR2 = 5,中断返回后,实际T2的数已经过了5了,波形就不对了。

解决办法:
1。提高AVR工作频率
2。采用整形数运算
3。在RAM中建立一个工作表,得到一个新的AD后,在主程序中进行计算,得到新的样值表,放入RAM的工作表中,然后在T2中断中调用,这样在T2中断中,只要使用赋值语句了。
4。根据你的样本表,一个正弦波最多为128个样点构成,根据你的应用条件频率不变,如果要求精度不高的话,可以使用64或32个样点。比如使用64点,当AD采样一次后,根据新的AD值,换算出新的64点的样本值,并且在T2的64次中断后使用新的样本表。


另外,你的程序中还有另外的问题,更换样值表应该是在一个完整的正弦波输出后。如果你的一个周期的正弦波是由128点组成,那么应该在第128点输出后才能使用新的样本表。而你的程序,在中断中的volt是随便变化的,还是每隔规定的点(如128次)后才改变的?

你程序中的样本表为什么使用INT型?为什么为256个?

guxiangdeyun 发表于 2007-10-19 16:14:50

谢谢马老师的指教,真没想到这么快能够得到马老师的回复,感谢您百忙之中热心的抽出时间来看我的程序,还对我的问题进行了耐心的分析和详解,真是万分感谢!您对该程序提出的问题一针见血!按照您的建议,我把浮点运算放在主程序里面了,情况大为改观,但又出现的一个新问题,就是AD 转换时间不够的问题,把AD转换程序层层细分,出现的竟然是一个不变的方波,真是让我郁闷之及!这是我改过的全程序,特向马老师请教:

#include <iom8v.h>
#include <macros.h>
#pragma data:code
// 128点正弦波样本表
const unsigned int auc_SinParam = {0, 6, 13, 19, 25, 31, 38, 44, 50, 56, 62, 69, 75, 81, 87, 92,
98, 104, 110, 115, 121, 127, 132, 137, 143, 148, 153, 158, 163, 168, 172, 177,
181, 186, 190, 194, 198, 202, 206, 210, 213, 217, 220, 223, 226, 229, 231, 234,
236, 239, 241, 243, 245, 246, 248, 249, 251, 252, 253, 253, 254, 255, 255, 255,
255, 255, 255, 254, 253, 253, 252, 251, 249, 248, 246, 245, 243, 241, 239, 236,
234, 231, 229, 226, 223, 220, 217, 213, 210, 206, 202, 198, 194, 190, 186, 181,
177, 172, 168, 163, 158, 153, 148, 143, 137, 132, 127, 121, 115, 110, 104, 98,
92, 87, 81, 75, 69, 62, 56, 50, 44, 38, 31, 25, 19, 13, 6, 0,
0, 6, 13, 19, 25, 31, 38, 44, 50, 56, 62, 69, 75, 81, 87, 92,
98, 104, 110, 115, 121, 127, 132, 137, 143, 148, 153, 158, 163, 168, 172, 177,
181, 186, 190, 194, 198, 202, 206, 210, 213, 217, 220, 223, 226, 229, 231, 234,
236, 239, 241, 243, 245, 246, 248, 249, 251, 252, 253, 253, 254, 255, 255, 255,
255, 255, 255, 254, 253, 253, 252, 251, 249, 248, 246, 245, 243, 241, 239, 236,
234, 231, 229, 226, 223, 220, 217, 213, 210, 206, 202, 198, 194, 190, 186, 181,
177, 172, 168, 163, 158, 153, 148, 143, 137, 132, 127, 121, 115, 110, 104, 98,
92, 87, 81, 75, 69, 62, 56, 50, 44, 38, 31, 25, 19, 13, 6, 0
};
#pragma data:data
void timer2_ovf_isr(void);
void port_init(void);
void init_devices(void);
void adc_init(void);
unsigned char x_SW = 1,X_LUT = 0;
//unsigned int volt = 1000;
float volt=1;
int x;
//unsigned int i;

void main(void)
{ long temp;
unsigned int ui_ADCL,ui_ADCH,adc_data;
init_devices();
   
// DDRB |= 0x08; // PB3(OC2)输出
   TCCR2 = 0x69; // 快速PWM模式,分频系数=1,正向控制OC0
   TIMSK = 0x40; // T/C0溢出中断允许
   SEI(); // 使能全局中断
   while(1)   
{   
if (X_LUT =1)                         // 实行两个半波采样一次,就是256个点AD转换一次
{
    ADCSRA=(1<<ADSC)|(1<<ADEN);//启动A/D转换,是单次转换模式!
   }
   
   if (X_LUT =100)
{
   while(!(ADCSRA&(1<<ADIF)));// 等待AD转换结束
    ADCSRA |= (1<<ADIF);         //写1清除标志位
   }
if (X_LUT =120)
{
   ui_ADCL = ADCL;         //Read 8 low bits first (important)
   ui_ADCH = ADCH ;          //read 2 high bits and shift into top byte
        adc_data =(ui_ADCH<<8)|ui_ADCL;
   }
   if (X_LUT =150)
{
   temp=(long)adc_data*2556;
   
   }
    if (X_LUT >253)
{
   
    volt=(float)(temp/1024)/1000;
   }
//x=(int)(((int)((unsigned long)(auc_SinParam)*1000000/volt)+500)/1000);顺便提一个问题,浮点运算转整型运算
                                                                                       为什么会不行?
x = (int)((float)(auc_SinParam)/volt+0.5);
}
}
#pragma interrupt_handler timer2_ovf_isr:5
void timer2_ovf_isr(void)

{   
if (X_LUT <127)
        {
       PORTB =(1<<PORTB2);
       
        }
   if (X_LUT >255)
   { X_LUT= 0; // 样点指针调整
      
        }
        if (X_LUT >127)
        {
           PORTB&=~(1<<PORTB2);
       }
        //把AD采样值送给匹配寄存器
   OCR2 =x;   // 取样点指针到比较匹配寄存器
       X_LUT += x_SW; // 新样点指针
}

void port_init(void)
{
PORTB = 0xFF;
DDRB= 0xFF;
PORTC = 0x00; //m103 output only
DDRC= 0x00;
PORTD = 0xFF;
DDRD= 0xFB;
}

//call this routine to initialise all peripherals
void init_devices(void)
{
//stop errant interrupts until set up
CLI(); //disable all interrupts

MCUCR = 0x00;
GICR= 0x00;
TIMSK = 0x00;
port_init();
adc_init();
} //timer interrupt sources
//re-enable interrupts
//all peripherals are now initialised

void adc_init(void)
{
ADCSR = 0x00; //disable adc
ACSR= 0x80; //关闭模拟比较器)
//SFIOR = 0x10; //高速转换
//ACSR=(1<<ACD);//关闭模拟比较器
ADCSRA = 0x81;//单次转换模式,转换使能,中断不使能,时钟分频为1
ADMUX = 0xc3;//select adc input AD3右对齐
}

我这里的样本表是两个正半波型,为了后续的与或运算服务!其实是256个样本表,不是128!
页: [1]
查看完整版本: 在固定快速PWM模式下,因引入变量的传递而使频率改变的问题(急,求救,马老师)