amoBBS 阿莫电子论坛

 找回密码
 注册
搜索
bottom↓
查看: 8850|回复: 39

请教:如何用MEGA16产生1M的频率信号?

[复制链接]
发表于 2009-12-1 10:31:49 | 显示全部楼层 |阅读模式
请教各位大侠:小弟今日受命用MEGA16单片机产生三路信号,频率分别为1MHZ,1KHZ和1HZ。我在程序中采用了定时器0的比较匹配输出,本意想定时0.5us中断,进入后将一个端口取反,从而生成1M,剩下的1K和1HZ就在此基础上累加就可以。实验中为了较好的分频效果,特意将晶振从7.3728M换为了20M。但是实验结果很不理想,频率误差很大。我将OCR0改为了2,得到的频率才有223KHZ。仔细想想,觉得应该是中断程序中几条指令耽误了定时器的时间。不知我的想法正确否?如果真是这样,那意味这只能采取汇编指令来写了?望各位指点。
程序如下,在CVAVR环境下编译:
#include <mega16.h>
#define mb PORTB.0
#define kb PORTB.1
#define nb PORTB.2  
#define lg PORTB.3
unsigned int num1=0,num2=0;
// Timer 0 output compare interrupt service routine
interrupt [TIM0_COMP] void timer0_comp_isr(void)
{
mb=~mb;
if(++num1>=1000)
{
        num1=0;
        kb=~kb;
        if(++num2>=1000)
        {
                num2=0;
                nb=~nb;
        }
}

}

// Declare your global variables here

void main(void)
{
// Declare your local variables here

// Input/Output Ports initialization
// Port A initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In  
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T  
PORTA=0x00;
DDRA=0x00;

// Port B initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=Out Func1=Out Func0=Out  
// State7=T State6=T State5=T State4=T State3=T State2=0 State1=0 State0=0  
PORTB=0x00;
DDRB=0x0F;

// Port C initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In  
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T  
PORTC=0x00;
DDRC=0x00;

// Port D initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In  
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T  
PORTD=0x00;
DDRD=0x00;

// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: 7372.800 kHz
// Mode: CTC top=OCR0
// OC0 output: Disconnected
TCCR0=0x09;
TCNT0=0x00;
OCR0=0x02;

// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: Timer 1 Stopped
// Mode: Normal top=FFFFh
// OC1A output: Discon.
// OC1B output: Discon.
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer 1 Overflow Interrupt: Off
// Input Capture Interrupt: Off
// Compare A Match Interrupt: Off
// Compare B Match Interrupt: Off
TCCR1A=0x00;
TCCR1B=0x00;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;

// Timer/Counter 2 initialization
// Clock source: System Clock
// Clock value: Timer 2 Stopped
// Mode: Normal top=FFh
// OC2 output: Disconnected
ASSR=0x00;
TCCR2=0x00;
TCNT2=0x00;
OCR2=0x00;

// External Interrupt(s) initialization
// INT0: Off
// INT1: Off
// INT2: Off
MCUCR=0x00;
MCUCSR=0x00;

// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x02;

// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
ACSR=0x80;
SFIOR=0x00;

// Global enable interrupts
#asm("sei")

while (1)
      {
      //lg=1;

      };
}
发表于 2009-12-1 10:38:50 | 显示全部楼层
0.5us中断,够强悍。。。
发表于 2009-12-1 10:53:20 | 显示全部楼层
你的思路有问题应该用定时器自动产生不用软件来管理这个是ICCAVR的向导产生的程序

//TIMER0 initialize - prescale:1024
// WGM: CTC
// desired value: 2MHz
// actual value:  0.000MHz (200000100.0%)
void timer0_init(void)
{
TCCR0 = 0x00; //stop timer
TCNT0 = 0x100; //set count value
TCCR0 = 0x05; //start timer
}

//TIMER1 initialize - prescale:1
// WGM: 4) CTC, TOP=OCRnA
// desired value: 2KHz
// actual value:  2.000KHz (0.0%)
void timer1_init(void)
{
TCCR1B = 0x00; //stop
TCNT1H = 0xF8; //setup
TCNT1L = 0x31;
OCR1AH = 0x07;
OCR1AL = 0xCF;
OCR1BH = 0x07;
OCR1BL = 0xCF;
TCCR1A = 0x40;
TCCR1B = 0x01; //start Timer
}
发表于 2009-12-2 11:12:33 | 显示全部楼层
借这个例子给正在学习AVR和其他单片机的人上上课,希望不要采用应试教育的学习方法,用机械的或搬几段例程的手段来学习嵌入式系统应用,或从事设计产品。

首先必须真正掌握和理解AVR内部各个功能的工作原理,然后根据实际的需要进行理论的分析,找出能实现,以及最佳的方案。

LZ要用M16产生1M、1K、1Hz的方波,仅采用1个定时器来完成是有问题的,分析如下:

1。M16最高工作频率为16M,就按LZ超频使用20M计算,那么M16在1us中也只能执行20条指令,0.5us只能执行10条指令。
2。M16定时中断间隔为0.5us一次,中断服务中还要做那么多的运算和判断(都是16位的)以及控制3个I/O反转,10条指令能够吗?等你一次中断服务完成返回,黄花菜早就凉透了。
3。就是、假如M16能工作在100M,而且0.5us中执行50条指令刚好能完成中断服务的话,那么你的这个系统其他什么事情也不能做了,给这3个方波发生吊死了,根本谈不上什么效率了。

所以以上方案是彻头彻尾的书本教条方案,就是采用32位的处理器也是最笨的方法。

下面给出一个方法,在我的实验板上就可以实现的。系统只要使用4M晶体就可以了。
1。首先解决1M方波的产生。
   1M方波的产生,采用8位T/C0或T/C2都可以,使用比较匹配输出方式,(2分频,比较匹配时输出取反,见我教材P257,例8.5)。这个方法相当AVR硬件方式分频输出,初始化T/C开始工作后,根本不需要软件去管理,也不需要使用中断。

2。然后解决1K、1Hz
   使用T/C1产生1K,(还是使用比较匹配输出产生),500us中断一次,在中断中使用软件计数器,产生1Hz方波(这时可以采用LZ的方法)

3。这样的设计,M16还有很多的时间可以做其他的事情。

下面留作业与思考:如果需要用M16产生同步的1M、1K、1Hz的三个方波,系统应该如何设计和实现?并根据你的方案,从理论上分析同步误差的情况。
发表于 2009-12-3 12:19:20 | 显示全部楼层
提示:

实际上利用M16的3个定时器,在16M系统时钟时,可以产生完全同步的1M、1K、1Hz的方波,而且不需要任何的中断处理以及软件的干预(除了3个定时器的初始化工作外)。

希望有兴趣、想真正学习AVR使用的朋友,尝试给出设计思路和方案,(参考M16关于定时器部分的描述)。

这个题到是能充分体现AVR的优势,标准51是做不到的。
 楼主| 发表于 2009-12-4 10:14:34 | 显示全部楼层
三个波形已经出来了。3个定时器全部使用输出比较,输出端触发输出。时钟用16M。
T0所用时钟不分频(16M),定时时间0.5us,输出端PB3为1M波形;
T2所用时钟256分频(62.5KHZ),定时时间0.5ms,输出端PD7为1K波形;
T1所用时钟1024分频(15.625KHZ),定时时间0.5s,输出端PD4为1HZ波形。
然后在最后我往SFIOR寄存器写了0x03,同时复位两个分频器,以求同步。
用CVAVR产生的程序:
void main(void)
{
// Declare your local variables here

// Input/Output Ports initialization
// Port A initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTA=0x00;
DDRA=0x00;

// Port B initialization
// Func7=In Func6=In Func5=In Func4=In Func3=Out Func2=Out Func1=In Func0=In
// State7=T State6=T State5=T State4=T State3=0 State2=0 State1=T State0=T
PORTB=0x00;
DDRB=0x0C;

// Port C initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTC=0x00;
DDRC=0x00;

// Port D initialization
// Func7=Out Func6=In Func5=Out Func4=Out Func3=In Func2=In Func1=In Func0=In
// State7=0 State6=T State5=0 State4=0 State3=T State2=T State1=T State0=T
PORTD=0x00;
DDRD=0xB0;

// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: 16000.000 kHz
// Mode: CTC top=OCR0
// OC0 output: Toggle on compare match
TCCR0=0x19;
TCNT0=0x00;
OCR0=0x07;

// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: 15.625 kHz
// Mode: CTC top=OCR1A
// OC1A output: Toggle
// OC1B output: Toggle
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer 1 Overflow Interrupt: Off
// Input Capture Interrupt: Off
// Compare A Match Interrupt: Off
// Compare B Match Interrupt: Off
TCCR1A=0x50;
TCCR1B=0x0D;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x1E;
OCR1AL=0x84;
OCR1BH=0x00;
OCR1BL=0x00;

// Timer/Counter 2 initialization
// Clock source: System Clock
// Clock value: 62.500 kHz
// Mode: CTC top=OCR2
// OC2 output: Toggle on compare match
ASSR=0x00;
TCCR2=0x1E;
TCNT2=0x00;
OCR2=0x1F;

// External Interrupt(s) initialization
// INT0: Off
// INT1: Off
// INT2: Off
MCUCR=0x00;
MCUCSR=0x00;

// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x00;

// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
ACSR=0x80;
SFIOR=0x03;

while (1)
      {
      // Place your code here

      };
}
 楼主| 发表于 2009-12-4 10:44:57 | 显示全部楼层
但是还有一个问题:如何验证三个波形是完全同步的?
我用了两通道的示波器测试,发现时间宽度不好设置,总是只能看到一种波形。当把时间宽度变长,1K的波形看出来了,但1M的波形出来的比1K的周期还要长!(怪事!)。我把1K的通道设为边沿触发模式,发现1M的下降沿和1K的上升沿是一起的,好像并不完全同步。
发表于 2009-12-4 12:47:09 | 显示全部楼层
这样试试:

首先设置3个计数器不工作(无计数脉冲输入)
然后:
TCNT1 = 0;
TCNT2 = 0;

// 以下5句必须紧跟在一起
TCNT0 = 1;   
SFIOR=0x03;                           //这条指令执行完表示同时开始计数  
设置T0采用系统时钟开始计数;          //注意:这条指令非常关键,它的执行时间应该为1个系统时钟,因此TCNT0少计一个脉冲,所以把TCNT0的初值设置为1
设置T2采用256分频时钟输入开始计数;     //T2、T1由于硬件分频器计数已经开始工作,同时也提供足够的设置时间,所以此时t2\t1开始自己的计数没有问题的
设置T1采用1024分频时种输入开始计数;

while (1)
      {
      // Place your code here

      };
}
发表于 2009-12-4 14:01:37 | 显示全部楼层
mark
 楼主| 发表于 2009-12-4 14:30:22 | 显示全部楼层
实验了,示波器在时间宽度为500ns时,1M的下降沿和1K的上升沿基本在一起。
CVAVR的程序如下:
// Timer/Counter 0 initialization
// Clock source: System Clock
// Mode: CTC top=OCR0
// OC0 output: Toggle on compare match
TCCR0=0x18; //close clock
TCNT0=0x00;
OCR0=0x07;

// Timer/Counter 1 initialization
// Clock source: System Clock
// Mode: CTC top=OCR1A
// OC1A output: Toggle
// OC1B output: Toggle
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer 1 Overflow Interrupt: Off
// Input Capture Interrupt: Off
// Compare A Match Interrupt: Off
// Compare B Match Interrupt: Off
TCCR1A=0x50;
TCCR1B=0x08;    //close clock
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x1E;
OCR1AL=0x84;
OCR1BH=0x00;
OCR1BL=0x00;

// Timer/Counter 2 initialization
// Clock source: System Clock
// Mode: CTC top=OCR2
// OC2 output: Toggle on compare match
ASSR=0x00;
TCCR2=0x18; //close clock
TCNT2=0x00;
OCR2=0x1F;

// External Interrupt(s) initialization
// INT0: Off
// INT1: Off
// INT2: Off
MCUCR=0x00;
MCUCSR=0x00;

// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x00;

// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
ACSR=0x80;

TCNT1=0;
TCNT2=0;
TCNT0=1;
SFIOR=0x03;
TCCR0=0x19;  // Clock value: 16000.000 kHz??????
TCCR2|=0x06;  // Clock value: 62.500 kHz
TCCR1B|=0x05;  // Clock value: 15.625 kHz

while (1)
      {
      // Place your code here

      };
注释中打问号的那句话,如果用直接赋值的方法汇编后是两条指令,如果用位或的方法(后两条)汇编后是三条指令。还是达不到一个系统时钟的要求。
发表于 2009-12-4 20:43:44 | 显示全部楼层
可以考虑内嵌一句汇编

如果使用C编译后是2条指令,那么设置TCNT0的初值为2。
TCNT0的初值应该是“TCCR0=0x19;  // Clock value: 16000.000 kHz”这条语句执行的时钟数,最多为4了。你把相应的汇编指令贴出,查手册看执行所需要的时钟数。
 楼主| 发表于 2009-12-5 12:56:54 | 显示全部楼层
试了一下,还是老样子。
;      82 TCCR0=0x19;  // Clock value: 16000.000 kHz
        LDI  R30,LOW(25)
        OUT  0x33,R30
;      83 TCCR2|=0x06;  // Clock value: 62.500 kHz
        IN   R30,0x25
        ORI  R30,LOW(0x6)
        OUT  0x25,R30
;      84 TCCR1B|=0x05;  // Clock value: 15.625 kHz
        IN   R30,0x2E
        ORI  R30,LOW(0x5)
        OUT  0x2E,R30
查了一下手册,LDI和OUT两条指令都是1个时钟。我把TCNT0从2改到4,都是一样的。
发表于 2009-12-6 09:47:33 | 显示全部楼层
根据你的结果:示波器在时间宽度为500ns时,1M的下降沿和1K的上升沿基本在一起。

那么1K的下一个下降沿也应该与1M的下降沿“基本”在一起的。

TCNT0的初值就是补上启动TCNT0计数指令执行的周期数,这个1-4个系统时钟小的变化,需要在示波器的时间宽度为0.05us,或更小才能看清楚。

这样的方法,肯定可以达到同步的。不过这已经不重要了。重要的:这样简单的问题,为什么许多人都不会这样的考虑!?

我在上星期把这个产生3个方波的例子在课上给本科(学AVR)和研究生(学STM32)都讲过,70个学生没有一个给出这样的方案,提出方法的都是与LZ位的相同。
发表于 2009-12-6 11:10:34 | 显示全部楼层
其实很明显的在PDF里就有提到,不要使用定时器中断来产生方波,是资源完全浪费的说啊。
发表于 2009-12-6 11:20:36 | 显示全部楼层
【13楼】 machao
我在上星期把这个产生3个方波的例子在课上给本科(学AVR)和研究生(学STM32)都讲过,70个学生没有一个给出这样的方案,提出方法的都是与LZ位的相同。
-----------------------------------------------------------------------------
这就是应试教育的结果:学了什么,就只会用什么,甚至学了也不会用。

====================================================================
其实就是学了也不会用的。
在我教材中,就有使用CTC比较匹配输出取反产生方波的例子(不过就是产生一个方波啦),就是只使用T/C硬件完成的。至少要知道在不考虑同步情况下,使用3个T/C或2个T/C来产生3个波吧?
发表于 2009-12-6 12:45:22 | 显示全部楼层
书签。
受益匪浅。
发表于 2009-12-12 12:54:49 | 显示全部楼层
mark
发表于 2009-12-12 16:26:44 | 显示全部楼层
听课
发表于 2009-12-12 16:38:40 | 显示全部楼层
受教
发表于 2009-12-12 16:58:32 | 显示全部楼层
mark
发表于 2010-1-4 23:25:33 | 显示全部楼层
顶!
发表于 2010-1-8 16:18:43 | 显示全部楼层
mark
发表于 2010-1-8 17:00:07 | 显示全部楼层
mark
发表于 2010-1-9 15:51:28 | 显示全部楼层
【4楼】 machao
积分:5232
派别:
等级:------
来自:
提示:

实际上利用M16的3个定时器,在16M系统时钟时,可以产生完全同步的1M、1K、1Hz的方波,而且不需要任何的中断处理以及软件的干预(除了3个定时器的初始化工作外)。

希望有兴趣、想真正学习AVR使用的朋友,尝试给出设计思路和方案,(参考M16关于定时器部分的描述)。

这个题到是能充分体现AVR的优势,标准51是做不到的。

==========================================================================================

在90年代末我就用AT89S52产生一个2MHZ和几个2KHZ以下的频率信号,CPU占用很少的,AT89S52我认为是标准51!
发表于 2010-1-9 16:07:56 | 显示全部楼层
受益匪浅
发表于 2010-1-30 08:39:08 | 显示全部楼层
MARK
发表于 2010-1-30 08:49:45 | 显示全部楼层
mark,以后某天用
发表于 2010-3-7 14:22:04 | 显示全部楼层
受益匪浅    使用比较匹配输出方式,
发表于 2010-3-8 18:12:49 | 显示全部楼层
【24楼】 coody
积分:1085
派别:
等级:------
来自:

在90年代末我就用AT89S52产生一个2MHZ和几个2KHZ以下的频率信号,CPU占用很少的,AT89S52我认为是标准51!

====================================================================================================
89s52是标准的51,现在请教几个问题:

1。能否说明采用的方法:如系统的时钟是多少?采用那个部件?工作方法和原理等。
2。2M和2K的信号精度是多少,误差如何?
3。CPU占用很少不是指的指令很少吧,应该是当CPU连续不断的产生“一个2MHZ和几个2KHZ以下的频率信号”后,还能有多少时间做其它的事?

要拿出实际的东西才算,否则就是空话。
发表于 2010-4-14 13:30:57 | 显示全部楼层
受益匪浅
发表于 2010-8-2 19:07:39 | 显示全部楼层
AT89S52的T2可以在P1.0输出一个时钟,晶振使用8MHz、16MHz、24MHz都能准确的产生2MHz的信号,精度就是晶振的精度。
发表于 2010-8-4 01:04:37 | 显示全部楼层
好啊,受教!
发表于 2010-8-17 09:07:38 | 显示全部楼层
mark
发表于 2011-4-29 14:09:55 | 显示全部楼层
cool
发表于 2011-7-16 13:02:33 | 显示全部楼层
MARK
发表于 2011-11-11 08:47:08 | 显示全部楼层
这个问题很好
我也遇到相同的问题,用machao老师的例程Page 258
void main()
{
//DDRD=0x80;
DDRB=0x08;

TCCR0=0x19;
TCNT0=0x00;
OCR0=52;

while(1);

}

将C/T2改成  C/T0 后发现频率并不准确,而使用C/T2频率很准确。
真是百思不得其解。
为什么同样的例程,改成T0就不行..非要用OCR2么?

======================2011年11月11日编辑,编辑理由:以上得到验证,我是错误的==================

首先报告下最后实验结果,我可能把某个I/O口不小心碰短路了,造成频率不稳定。改成杜邦线引出后频率很准确。

看来我以前上课那阵学习“电子测量技术”讲到“测量误差的产生原因”中“人为操作不当”真是害人啊。

这个例程很准确,不管使用OCR0还是OCR2都很准确,特意找了频率计和示波器一起观察的。

也许频率计的阻抗可能小些,一同接入的时候会对波形有影响。

以上分析完毕,望坛友们一定多检查问题,多去思考为什么会造成这个问题出现的原因。
发表于 2012-2-27 21:38:51 | 显示全部楼层
mark,
发表于 2012-3-24 11:38:10 | 显示全部楼层
小问题才能体现基础!
发表于 2012-11-10 23:35:53 | 显示全部楼层
好呀 留个记号。
友情提示:标题不合格、重复发帖,将会被封锁ID。详情请参考:论坛通告:封锁ID、获得注册邀请码、恢复被封ID、投诉必读
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|阿莫电子论坛(原ourAVR/ourDEV) ( 公安备案:44190002001997(交互式论坛) 工信部备案:粤ICP备09047143号 )

GMT+8, 2019-9-18 22:48

阿莫电子论坛, 原"中国电子开发网"

© 2004-2018 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

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