马老师,您书中第324页中的将频率送显示缓冲区的程序不怎么理解,能解释一下吗?
书中有这样一段程序:void freq_to_disbuff(void) // 将频率值转化为BCD码并送入显示缓冲区
{
char i,j=7;
for (i=0;i<=4;i++)
{
dis_buff = freq % 10;
freq = freq / 10;
}
dis_buff = freq;
}
为什么不是这样:
void freq_to_disbuff(void) // 将频率值转化为BCD码并送入显示缓冲区
{
char i;
for (i=0;i<=7;i++)
{
dis_buff = freq % 10;
freq = freq / 10;
}
}
这样不是吧freq变量的每一位都分别赋值给dis_buff----dis_buff了吗? 哦,再认真看了很久,是不是因为freq<=255000,这样做的目的高3位就不用算了。 呵呵,不好意思,人比较笨,还是想不通,结合现实程序来看,是不是应该这样:
void freq_to_disbuff(void) // 将频率值转化为BCD码并送入显示缓冲区
{
char i,j=7;
for (i=2;i<=6;i++)
{
dis_buff = freq % 10;
freq = freq / 10;
}
dis_buff = freq;
}
我认为,这样的话,如果freq=12345kz,则数码管应该显示12345.00
而且第326页讲到被测最高频率为255KHZ,我觉得是不是应该是25.5KHZ?
初学,很多地方凭感觉,也不知道对不对,请马老师指导,谢谢! 这段程序
for (i=0;i<=4;i++)
{
dis_buff = freq % 10;
freq = freq / 10;
}
dis_buff = freq;
一共用到了 dis_buff,所以应该是能够显示255,000Hz的,而不是25,500Hz 首先,把测量和显示要分开考虑.
326页讲到被测最高频率为255KHZ,不是由于显示限制的,是测量方法限制的.T/C0为8位的计数器,门宽为1MS,所以当被测输入频率大于255K的时,1MS时间中的脉冲个数将超出255个,造成T/C0的溢出,这时频率测量就不准确了.
然后我们分析显示:
注意要显示的是变量freg中的数,freg为整数int型,最大为65535,所以只有5位,而且是100ms中的脉冲个数!
分析以下函数
void freq_to_disbuff(void) // 将频率值转化为BCD码并送入显示缓冲区
{
char i,j=7;
for (i=0;i<=4;i++)
{
dis_buff = freq % 10;
freq = freq / 10;
}
dis_buff = freq;
}
将freq转换成BCD码送入disbuff,只要转换5位可以了,而且个位在disbuff中(3楼有误),最高位在disbuff中,disbuff和disbuff永远为0(实际上,循环后面的dis_buff = freq;是多余的一句了,它也是0)
然后看单位和小数点的位置.freg是100ms的脉冲个数,乘上10为Hz,除上1000为KHz.所以小数点点在disbuff的位置,表示单位为KHz.(注意在显示扫描子程序中的if(posit == 5) PORTA = PORTA | 0X80).
实际上在这个例子中是不需要8位显示的,只要5位显示就够了,因为freg最大为65535.另外如果freg为65535的话,显示为655.35KHz,但实际上在测量中是不会出现的,因为测量方法决定了最高的测量频率为255K(1ms的T/C0最大为255,100ms的累加最大也就是25500!).因此你看到显示大于255K,那么程序中肯定有错误了!
至于如何改进该例子,提高测量频率的上限,在326页有提示.如果你真正理解透了,自己可以设计出更完善的频率计. 原来是按100ms算的,没看完整程序,以为freq至少是六位数 偶的程序,不同于其它一些教科书中的程序,单一不实用,仅仅是为了说明一个简单的问题或算法.
我的例子,来源和从多年实际产品设计之中提取出来的,尽管不长,但有很多的技巧和方法,涉及到很多的方面,不是那么容易真正理解透的.
不过有的时候,也故意增加几个小转弯,如上面的显示,如果把显示器的位控换一下,把disbuff对应为个位,那么freq_to_disbuff(void)函数就简单一些,也容易看懂.但这也同时说明,硬件设计也不能忽视,设计的好,合理,程序编写起来就方便和简短.
另外,比如乘10和除1000,在程序中都不出现,只是用定小数点的位就实现了.这样程序简短,优化.但看懂需要多转几个弯了.
有点吹嘘的嫌疑了:) 呵呵,马老师讲得好详细,现在终于明白了,谢谢! 就是这个程序,不知道马老师是否验证过,我用这个程序竟然会测到负数。 #include <tiny2313.h>
#include <stdio.h>
#define uchar unsigned char
bit time_1ms_ok;
unsigned inttime0_old,time0_new;
unsigned int freq;
unsigned char freq_time;
// Timer 0 output compare A interrupt service routine
interrupt void timer0_compa_isr(void) //TO为定时用
{
time0_new=TCNT1;
time_1ms_ok=1;
}
void measure_freq(void);
void main(void)
{
TCCR0A=0x02;
TCCR0B=0x01;
TCNT0=0x00;
OCR0A=0x80; //改变此值,可是测得的频率无变化
OCR0B=0x00;
TCCR1A=0x00;
TCCR1B=0x06;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;
GIMSK=0x00;
MCUCR=0x00;
// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x01;
USICR=0x00;
// USART initialization
// Communication Parameters: 8 Data, 1 Stop, No Parity
// USART Receiver: On
// USART Transmitter: On
// USART Mode: Asynchronous
// USART Baud Rate: 4800
UCSRA=0x00;
UCSRB=0x18;
UCSRC=0x06;
UBRRH=0x00;
UBRRL=0x33;
ACSR=0x80;
#asm("sei")
time0_old=0;
while (1)
{
measure_freq();
};
}
void measure_freq(void)
{
if(time_1ms_ok)
{
if(time0_new>=time0_old)
freq=freq + (time0_new - time0_old);
else
freq=freq + (65536 - time0_old + time0_new);
time0_old=time0_new;
if(++freq_time>=100) //改变此值50,测得的频率无变化
{
freq_time=0;
printf("a%i\n\r",freq);
freq=0;
}
time_1ms_ok=0;
}
} 8、9楼,我先问你,你读懂了我的程序吗? 326页讲到被测最高频率为255KHZ,不是由于显示限制的,是测量方法限制的.T/C0为8位的计数器,门宽为1MS,所以当被测输入频率大于255K的时,1MS时间中的脉冲个数将超出255个,造成T/C0的溢出,这时频率测量就不准确了.
请问马老师,如果我用T1(65535)和T3(65535),那不是测量的频率信号就非常高了吗?
============================================================
你学围棋,开始学了多少个基本定式和可能变化?
使用AVR也一样,首先要掌握基本的定式和变化。仅背出高手之间的几个比赛棋局是没有用的,那样你成不了高手,业余的高手也成不了。 我现在就象“唐僧”一样,唠唠叨叨的,重复来重复去的,“感化”、“教导”那些手里有了“经书”、以及到这里取经的“子弟”们:学习是学会思考,学会推理,和学习方法,而不是单单的一段程序和代码。
没有一段程序是万能的,没有一个系统适合所有的情况,死搬硬套是不行的。
回答11楼的问题:
1。不管采用任何的方法,你的系统所能测试的频率上限首先是你使用的系统时钟所限定的,理论上是1/2的系统时钟频率。如果系统时钟为4M的话,那么理论上只能测到2M频率。因为AVR的硬件引脚电路要判断出输入频率的变化,至少要比输入频率快1倍。
2。实际上,这个理论上的上限还是不容易做到的,要看你实际采用的测试方法,以及如何与整个系统中做其它处理的部分(计算、显示等)正确的配合。
具体说楼上所问的问题,例子中使用4M系统时钟,那么理论最高可以测到2M,那么从255K提高到1M以上还是可能的。可以有几种方式(在测量频度不变保持不变,10次/秒):
a/仍然采用8位,把门宽减少,如0.5ms或0.2ms,同时增加freq_time的次数,如200,或500
b/仍然采用8位,门宽不变1ms,freq_time的次数还是100,使用T0的溢出中断,在T0中断中记录中断次数,每一次中断,表示T0的值增加256个。
c/把T0换成t1,采用16位。
上面3种方法,原则上都是可以的,但都不能简单的改动一下程序就可以实现的。
对于a方法,由于门宽减少,所以给其它处理带来麻烦,因为要保证每秒10次的连续不间断的频率测量,其它的处理,包括计算、显示、通信等必须在门宽内完成,否则会造成测量失误(看下面例子)。
对于b方法,增加了一个中断,系统处理和计算也要复杂的多,因此初学和一般水平的人编写不出合理的代码。
所以在书中我给出的是比较方便的C方法(P326),同时也提醒需要将time0_new和time0_old定义成16位整型变量。
=========================================================
实际上,对于c方法也还是要考虑的,因为16位变量的处理计算比8位要多出很多的时间,你还要考虑在1ms门宽时间内,是否能保证,而且是有一定余量的完成。
如果没有能力做判断和经验调试的话,可以1/提高系统频率,采用8M或16M,这里到不是为了提高测量频率,而是提高处理计算的时间,在门宽内完成。2/适当增加门宽的时间,如2ms,调整freq_time为50(采用16位T1,有余量情况下)。3/采用间断测量频率的方法,比如100ms测量频率,然后停止T0(T1)的计数,花上几十、甚至几百ms做其它处理,然后让T0(T1)从头开始计数,进入下一次的频率测量。
==========================================================
以上就是我对书上思考题的回答。
这也是我不给出书上思考和练习的答案的道理。实际的系统在现实的工程项目中设计和要求是不一样的,没有标准统一的答案,也没有标准统一的代码。但东西是死的,但人是活的,你必须真正掌握基本的原理和方法,然后根据实际情况灵活的去运用才行。
书上的例子,还是面向最基本的方法,目的是要真正理解和掌握。想想看,就这么短的一段代码,都不能完全真正的掌握和理解,我放上一段复杂的例子、大段的代码,又有什么用处?
=============================================================
拿9楼贴上的代码讲,CHENBINGSTER实际根本没有掌握最根本的东西,就抄搬书上的代码,实际出问题后,他不是检讨自己是否真正理解了,而是首先怀疑“马老师是否验证过”。这也是现在很多“取经”人的通病。
我没有验证过他使用tiny2313,基本设置是否正确(看来是设计项目了,自己水平根本不够,只是想简单搬个代码),就看他的这段代码:
void measure_freq(void)
{
if(time_1ms_ok)
{
if(time0_new>=time0_old)
freq=freq + (time0_new - time0_old);
else
freq=freq + (65536 - time0_old + time0_new);
time0_old=time0_new;
if(++freq_time>=100) //改变此值50,测得的频率无变化
{
freq_time=0;
printf("a%i\n\r",freq); 《==================注意这里与书上的不同!!!!
freq=0;
}
time_1ms_ok=0;
}
}
看似与书上例子一样,但有一句变了,使用了printf("a%i\n\r",freq);(不客气的讲:使用printf("a%i\n\r",freq)的,大部分的人水平都一般,或者上了那些不实际做东西的教师或教课书的当了),这样的话,就是测量原理没问题的话,也不能正确测量频率的。分析如下:
首先必须看懂和明白:根据我例子中所使用的测量方案和方法,这段代码必须在1ms门宽时间内完成的,否则会影响到频率的测量!因为T0是连续、不间断的在计数的,记录频率脉冲是不停止的,换句话讲:测量是连续不断的,每1ms必须读掉TCNT0的值,否则T0会溢出,造成下次读的T0不准,测量就不准了(没看懂的,再慢慢仔细去消化吧)。
另外,前面代码中设置USART的BPS为4800,按输出一个字节10BIT算,1秒内可以输出480个字节,那么1ms输出0.48个字节。
下面我要问CHENBINGSTER了,你知道printf()具体做那些事情吗?printf("a%i\n\r",freq)要输出几个字节?是什么格式的?执行需要多少时间?
而我代码中的"freq_to_disbuff();"执行的时间是多少?
你使用了printf("a%i\n\r",freq),偷梁换柱,还问我“否验证过”。现在我明确的告诉你,没有验证过你的代码,我也不需要验证,你的代码(不是我的代码)肯定不能正确测量频率的。 多谢马老师的详细分析,这一看下来我对测频的原理和方法也有了更深入的掌握。之前看您书上的测频方法(使用t1捕捉)头挺晕的,涉及到很多因素,包括在门宽内需将数据处理完,我还不能完全理解,就参考了下上面的代码自己写了程序,勉强令人满意。打算继续深入理解这种方法,做出更好的频率计。
8楼那哥们估计c的基本功太不扎实了。 经过马老师的感化,苦练葵花宝典,现在的程序能测频率的
#include <tiny2313.h>
#include <delay.h>
#include <stdio.h>
#define RXB8 1
#define TXB8 0
#define UPE 2
#define OVR 3
#define FE 4
#define UDRE 5
#define RXC 7
#define FRAMING_ERROR (1<<FE)
#define PARITY_ERROR (1<<UPE)
#define DATA_OVERRUN (1<<OVR)
#define DATA_REGISTER_EMPTY (1<<UDRE)u
bit time_1ms_ok;
unsigned char freq_time,b;
unsigned int time0_old,time0_new;
unsigned int freq;
interrupt void timer0_compa_isr(void)
{
time0_new=TCNT1;
time_1ms_ok=1;
}
void measure_freq(void);
void uart_communciation(void);
void main(void)
{
#pragma optsize-
CLKPR=0x80;
CLKPR=0x00;
#ifdef _OPTIMIZE_SIZE_
#pragma optsize+
#endif
// Port D initialization
// Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// 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: 62.500 kHz
// Mode: CTC top=OCR0A
// OC0A output: Disconnected
// OC0B output: Disconnected
TCCR0A=0x02;
TCCR0B=0x03;
TCNT0=0x00;
OCR0A=0x40;
OCR0B=0x00;
// Timer/Counter 1 initialization
// Clock source: T1 pin Falling Edge
// 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=0x06;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;
// External Interrupt(s) initialization
// INT0: Off
// INT1: Off
// Interrupt on any change on pins PCINT0-7: Off
GIMSK=0x00;
MCUCR=0x00;
// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x01;
// Universal Serial Interface initialization
// Mode: Disabled
// Clock source: Register & Counter=no clk.
// USI Counter Overflow Interrupt: Off
USICR=0x00;
// USART initialization
// Communication Parameters: 8 Data, 1 Stop, No Parity
// USART Receiver: On
// USART Transmitter: On
// USART Mode: Asynchronous
// USART Baud Rate: 4800
UCSRA=0x00;
UCSRB=0x18;
UCSRC=0x06;
UBRRH=0x00;
UBRRL=0x33;
// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
ACSR=0x80;
// Global enable interrupts
#asm("sei")
while (1)
{
measure_freq();
};
}
void measure_freq(void)
{
if(time_1ms_ok)
{
if(time0_new>=time0_old)
freq=freq + (time0_new - time0_old);
else
freq=freq + (65535 - time0_old + time0_new);
time0_old=time0_new;
if(++freq_time>=200)
{
#asm("cli")//关掉中断,执行下面的程序
freq_time=0;
//printf("b%u\n\r",freq); //printf的确不能用的,现在改有UDR了
b=freq/10000;
b=(freq-b*10000)/1000;
b=(freq-b*10000-b*1000)/100;
b=(freq-b*10000-b*1000-b*100)/100;
b=(freq-b*10000-b*1000-b*100-b*10)%10;
uart_communciation();
freq=0;
TIFR=0X01; //清中断标志位
#asm("sei")// Global enable interrupts
TCNT0=0; //TCNT0从0开始计
}
time_1ms_ok=0;
}
}
void uart_communciation(void)
{
while(!(UCSRA&(1<<UDRE)));
UDR= 0x41;
while(!(UCSRA&(1<<UDRE)));
UDR= b+48;
while(!(UCSRA&(1<<UDRE)));
UDR= b+48;
while(!(UCSRA&(1<<UDRE)));
UDR= b+48;
while(!(UCSRA&(1<<UDRE)));
UDR= b+48;
while(!(UCSRA&(1<<UDRE)));
UDR= b+48;
while(!(UCSRA&(1<<UDRE)));
UDR= 0X0D;
while(!(UCSRA&(1<<UDRE)));
UDR= 0X0A;
//delay_ms(100);
// UCSR0B=0X18;
} 有人自宫了…… 经典!! 我刚好学到书上这一章,赶紧来补补课。
页:
[1]