请教:如何用MEGA16产生1M的频率信号?
请教各位大侠:小弟今日受命用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 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;
};
} 0.5us中断,够强悍。。。 你的思路有问题应该用定时器自动产生不用软件来管理这个是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
} 借这个例子给正在学习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的三个方波,系统应该如何设计和实现?并根据你的方案,从理论上分析同步误差的情况。 提示:
实际上利用M16的3个定时器,在16M系统时钟时,可以产生完全同步的1M、1K、1Hz的方波,而且不需要任何的中断处理以及软件的干预(除了3个定时器的初始化工作外)。
希望有兴趣、想真正学习AVR使用的朋友,尝试给出设计思路和方案,(参考M16关于定时器部分的描述)。
这个题到是能充分体现AVR的优势,标准51是做不到的。 三个波形已经出来了。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
};
} 但是还有一个问题:如何验证三个波形是完全同步的?
我用了两通道的示波器测试,发现时间宽度不好设置,总是只能看到一种波形。当把时间宽度变长,1K的波形看出来了,但1M的波形出来的比1K的周期还要长!(怪事!)。我把1K的通道设为边沿触发模式,发现1M的下降沿和1K的上升沿是一起的,好像并不完全同步。 这样试试:
首先设置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
};
} mark 实验了,示波器在时间宽度为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
};
注释中打问号的那句话,如果用直接赋值的方法汇编后是两条指令,如果用位或的方法(后两条)汇编后是三条指令。还是达不到一个系统时钟的要求。 可以考虑内嵌一句汇编
如果使用C编译后是2条指令,那么设置TCNT0的初值为2。
TCNT0的初值应该是“TCCR0=0x19;// Clock value: 16000.000 kHz”这条语句执行的时钟数,最多为4了。你把相应的汇编指令贴出,查手册看执行所需要的时钟数。 试了一下,还是老样子。
; 82 TCCR0=0x19;// Clock value: 16000.000 kHz
LDIR30,LOW(25)
OUT0x33,R30
; 83 TCCR2|=0x06;// Clock value: 62.500 kHz
IN R30,0x25
ORIR30,LOW(0x6)
OUT0x25,R30
; 84 TCCR1B|=0x05;// Clock value: 15.625 kHz
IN R30,0x2E
ORIR30,LOW(0x5)
OUT0x2E,R30
查了一下手册,LDI和OUT两条指令都是1个时钟。我把TCNT0从2改到4,都是一样的。 根据你的结果:示波器在时间宽度为500ns时,1M的下降沿和1K的上升沿基本在一起。
那么1K的下一个下降沿也应该与1M的下降沿“基本”在一起的。
TCNT0的初值就是补上启动TCNT0计数指令执行的周期数,这个1-4个系统时钟小的变化,需要在示波器的时间宽度为0.05us,或更小才能看清楚。
这样的方法,肯定可以达到同步的。不过这已经不重要了。重要的:这样简单的问题,为什么许多人都不会这样的考虑!?
我在上星期把这个产生3个方波的例子在课上给本科(学AVR)和研究生(学STM32)都讲过,70个学生没有一个给出这样的方案,提出方法的都是与LZ位的相同。 其实很明显的在PDF里就有提到,不要使用定时器中断来产生方波,是资源完全浪费的说啊。 【13楼】 machao
我在上星期把这个产生3个方波的例子在课上给本科(学AVR)和研究生(学STM32)都讲过,70个学生没有一个给出这样的方案,提出方法的都是与LZ位的相同。
-----------------------------------------------------------------------------
这就是应试教育的结果:学了什么,就只会用什么,甚至学了也不会用。
====================================================================
其实就是学了也不会用的。
在我教材中,就有使用CTC比较匹配输出取反产生方波的例子(不过就是产生一个方波啦),就是只使用T/C硬件完成的。至少要知道在不考虑同步情况下,使用3个T/C或2个T/C来产生3个波吧? 书签。
受益匪浅。 mark 听课 受教 mark 顶!./emotion/em078.gif mark mark 【4楼】 machao
积分:5232
派别:
等级:------
来自:
提示:
实际上利用M16的3个定时器,在16M系统时钟时,可以产生完全同步的1M、1K、1Hz的方波,而且不需要任何的中断处理以及软件的干预(除了3个定时器的初始化工作外)。
希望有兴趣、想真正学习AVR使用的朋友,尝试给出设计思路和方案,(参考M16关于定时器部分的描述)。
这个题到是能充分体现AVR的优势,标准51是做不到的。
==========================================================================================
在90年代末我就用AT89S52产生一个2MHZ和几个2KHZ以下的频率信号,CPU占用很少的,AT89S52我认为是标准51! 受益匪浅 MARK mark,以后某天用 受益匪浅 使用比较匹配输出方式, 【24楼】 coody
积分:1085
派别:
等级:------
来自:
在90年代末我就用AT89S52产生一个2MHZ和几个2KHZ以下的频率信号,CPU占用很少的,AT89S52我认为是标准51!
====================================================================================================
89s52是标准的51,现在请教几个问题:
1。能否说明采用的方法:如系统的时钟是多少?采用那个部件?工作方法和原理等。
2。2M和2K的信号精度是多少,误差如何?
3。CPU占用很少不是指的指令很少吧,应该是当CPU连续不断的产生“一个2MHZ和几个2KHZ以下的频率信号”后,还能有多少时间做其它的事?
要拿出实际的东西才算,否则就是空话。 受益匪浅 AT89S52的T2可以在P1.0输出一个时钟,晶振使用8MHz、16MHz、24MHz都能准确的产生2MHz的信号,精度就是晶振的精度。 好啊,受教! mark cool MARK 这个问题很好
我也遇到相同的问题,用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都很准确,特意找了频率计和示波器一起观察的。
也许频率计的阻抗可能小些,一同接入的时候会对波形有影响。
以上分析完毕,望坛友们一定多检查问题,多去思考为什么会造成这个问题出现的原因。 mark, 小问题才能体现基础! 好呀 留个记号。
页:
[1]