搜索
bottom↓
回复: 17

马老师,您书中第324页中的将频率送显示缓冲区的程序不怎么理解,能解释一下吗?

[复制链接]

出0入0汤圆

发表于 2008-2-28 00:18:09 | 显示全部楼层 |阅读模式
书中有这样一段程序:

void freq_to_disbuff(void)                                // 将频率值转化为BCD码并送入显示缓冲区
{
                char i,j=7;
                for (i=0;i<=4;i++)
                {
                        dis_buff[j-i] = freq % 10;
                        freq = freq / 10;
                }
                dis_buff[2] = 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[0]----dis_buff[7]了吗?

出0入0汤圆

 楼主| 发表于 2008-2-28 00:37:25 | 显示全部楼层
哦,再认真看了很久,是不是因为freq<=255000,这样做的目的高3位就不用算了。

出0入0汤圆

 楼主| 发表于 2008-2-28 08:28:51 | 显示全部楼层
呵呵,不好意思,人比较笨,还是想不通,结合现实程序来看,是不是应该这样:
void freq_to_disbuff(void)                                // 将频率值转化为BCD码并送入显示缓冲区
{
                char i,j=7;
                for (i=2;i<=6;i++)
                {
                        dis_buff[j-i] = freq % 10;
                        freq = freq / 10;
                }
                dis_buff[0] = freq;
}
我认为,这样的话,如果freq=12345kz,则数码管应该显示12345.00
而且第326页讲到被测最高频率为255KHZ,我觉得是不是应该是25.5KHZ?
初学,很多地方凭感觉,也不知道对不对,请马老师指导,谢谢!

出0入0汤圆

发表于 2008-2-28 13:34:37 | 显示全部楼层
这段程序
for (i=0;i<=4;i++)
                {
                        dis_buff[j-i] = freq % 10;
                        freq = freq / 10;
                }
                dis_buff[2] = freq;

一共用到了 dis_buff[7][6][5][4][3][2],所以应该是能够显示255,000Hz的,而不是25,500Hz

出0入0汤圆

发表于 2008-2-28 14:42:56 | 显示全部楼层
首先,把测量和显示要分开考虑.

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[j-i] = freq % 10;
                        freq = freq / 10;
                }
                dis_buff[2] = freq;
}
将freq转换成BCD码送入disbuff,只要转换5位可以了,而且个位在disbuff[7]中(3楼有误),最高位在disbuff[2]中,disbuff[1]和disbuff[0]永远为0(实际上,循环后面的dis_buff[2] = freq;是多余的一句了,它也是0)

然后看单位和小数点的位置.freg是100ms的脉冲个数,乘上10为Hz,除上1000为KHz.所以小数点点在disbuff[5]的位置,表示单位为KHz.(注意在显示扫描子程序中的if(posit == 5) PORTA = PORTA | 0X80).

实际上在这个例子中是不需要8位显示的,只要5位显示就够了,因为freg最大为65535.另外如果freg为65535的话,显示为655.35KHz,但实际上在测量中是不会出现的,因为测量方法决定了最高的测量频率为255K(1ms的T/C0最大为255,100ms的累加最大也就是25500!).因此你看到显示大于255K,那么程序中肯定有错误了!

至于如何改进该例子,提高测量频率的上限,在326页有提示.如果你真正理解透了,自己可以设计出更完善的频率计.

出0入0汤圆

发表于 2008-2-28 15:30:31 | 显示全部楼层
原来是按100ms算的,没看完整程序,以为freq至少是六位数

出0入0汤圆

发表于 2008-2-28 16:02:55 | 显示全部楼层
偶的程序,不同于其它一些教科书中的程序,单一不实用,仅仅是为了说明一个简单的问题或算法.

我的例子,来源和从多年实际产品设计之中提取出来的,尽管不长,但有很多的技巧和方法,涉及到很多的方面,不是那么容易真正理解透的.

不过有的时候,也故意增加几个小转弯,如上面的显示,如果把显示器的位控换一下,把disbuff[0]对应为个位,那么freq_to_disbuff(void)函数就简单一些,也容易看懂.但这也同时说明,硬件设计也不能忽视,设计的好,合理,程序编写起来就方便和简短.

另外,比如乘10和除1000,在程序中都不出现,只是用定小数点的位就实现了.这样程序简短,优化.但看懂需要多转几个弯了.

有点吹嘘的嫌疑了:)

出0入0汤圆

 楼主| 发表于 2008-2-28 16:15:46 | 显示全部楼层
呵呵,马老师讲得好详细,现在终于明白了,谢谢!

出0入0汤圆

发表于 2009-8-5 20:57:40 | 显示全部楼层
就是这个程序,不知道马老师是否验证过,我用这个程序竟然会测到负数。

出0入0汤圆

发表于 2009-8-5 21:02:22 | 显示全部楼层
#include <tiny2313.h>
#include <stdio.h>                  
                     
#define uchar unsigned char  
bit time_1ms_ok;
unsigned int  time0_old,time0_new;
unsigned int freq;      
unsigned char freq_time;

// Timer 0 output compare A interrupt service routine
interrupt [TIM0_COMPA] 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;
   }
}

出0入0汤圆

发表于 2009-8-6 01:04:46 | 显示全部楼层
8、9楼,我先问你,你读懂了我的程序吗?

出0入0汤圆

发表于 2009-8-6 12:02:26 | 显示全部楼层
326页讲到被测最高频率为255KHZ,不是由于显示限制的,是测量方法限制的.T/C0为8位的计数器,门宽为1MS,所以当被测输入频率大于255K的时,1MS时间中的脉冲个数将超出255个,造成T/C0的溢出,这时频率测量就不准确了.

  请问马老师,如果我用T1(65535)和T3(65535),那不是测量的频率信号就非常高了吗?


============================================================
你学围棋,开始学了多少个基本定式和可能变化?
使用AVR也一样,首先要掌握基本的定式和变化。仅背出高手之间的几个比赛棋局是没有用的,那样你成不了高手,业余的高手也成不了。

出0入0汤圆

发表于 2009-8-6 14:14:30 | 显示全部楼层
我现在就象“唐僧”一样,唠唠叨叨的,重复来重复去的,“感化”、“教导”那些手里有了“经书”、以及到这里取经的“子弟”们:学习是学会思考,学会推理,和学习方法,而不是单单的一段程序和代码。

没有一段程序是万能的,没有一个系统适合所有的情况,死搬硬套是不行的。

回答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),偷梁换柱,还问我“否验证过”。现在我明确的告诉你,没有验证过你的代码,我也不需要验证,你的代码(不是我的代码)肯定不能正确测量频率的。

出0入0汤圆

发表于 2009-8-11 12:35:20 | 显示全部楼层
多谢马老师的详细分析,这一看下来我对测频的原理和方法也有了更深入的掌握。之前看您书上的测频方法(使用t1捕捉)头挺晕的,涉及到很多因素,包括在门宽内需将数据处理完,我还不能完全理解,就参考了下上面的代码自己写了程序,勉强令人满意。打算继续深入理解这种方法,做出更好的频率计。

8楼那哥们估计c的基本功太不扎实了。

出0入0汤圆

发表于 2009-11-2 20:50:51 | 显示全部楼层
经过马老师的感化,苦练葵花宝典,现在的程序能测频率的

#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[4];
unsigned int time0_old,time0_new;
unsigned int freq;

interrupt [TIM0_COMPA] 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[4]=freq/10000;
          b[3]=(freq-b[4]*10000)/1000;
          b[2]=(freq-b[4]*10000-b[3]*1000)/100;
          b[1]=(freq-b[4]*10000-b[3]*1000-b[2]*100)/100;
          b[0]=(freq-b[4]*10000-b[3]*1000-b[2]*100-b[1]*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[4]+48;
         
    while(!(UCSRA&(1<<UDRE)));
    UDR= b[3]+48;   
        
    while(!(UCSRA&(1<<UDRE)));
    UDR= b[2]+48;
      
     while(!(UCSRA&(1<<UDRE)));
    UDR= b[1]+48;
   
     while(!(UCSRA&(1<<UDRE)));
    UDR= b[0]+48;     
         
    while(!(UCSRA&(1<<UDRE)));
    UDR= 0X0D;
    while(!(UCSRA&(1<<UDRE)));
    UDR= 0X0A;
     //delay_ms(100);
     // UCSR0B=0X18;
      
   
    }

出0入618汤圆

发表于 2009-11-2 21:15:31 | 显示全部楼层
有人自宫了……
头像被屏蔽

出0入0汤圆

发表于 2011-12-26 10:09:02 | 显示全部楼层
经典!!

出0入0汤圆

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

本版积分规则

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

GMT+8, 2024-4-19 09:13

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

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