矿石收音机论坛

 找回密码
 加入会员

QQ登录

只需一步,快速开始

搜索
查看: 7062|回复: 20

简易等精度频率计开源

[复制链接]
     
发表于 2020-5-10 10:26:59 | 显示全部楼层 |阅读模式
本帖最后由 翌阳 于 2020-5-10 11:36 编辑

很简单的等精度频率计,几经改进,增加了功能,又减少了功能,改了线路,换了器件,折腾好久,最后还是归到极简。好用,高精度,准确,这才是最重要的。高基准时钟,不准确也是没用,所以,回归到最容易得到的10M温补晶振上。显示重做了LED显示,而没用现成的模块。
开源网址:https://github.com/elezen/Cymomer
这里也贴个程序和电路图:
  1. /*
  2. */
  3. #define FREQ 10000000L
  4. #define NORMAL 100
  5. #define FAST 10
  6. #define KEYDLY 50
  7. #define KEYLONG 500
  8. //#define FREQ 40499753L
  9. #include <mcs51/at89x52.h>
  10. #define CLR P3_7
  11. #define GATECTL P3_6
  12. #define GATE P3_3
  13. #define KEY P1_0
  14. #define KEY2 P1_1
  15. #define false 0
  16. #define true 1

  17. __code unsigned char LedSegs[]={3, 159, 37, 13, 153, 73, 65, 31, 1, 9, 17, 193, 99, 133, 97, 113};
  18. __code unsigned char LedDigit[]={0x1,0x2,0x4,0x8,0x10,0x20,0x40,0x80};
  19. __idata char leds[8];
  20. char led_i,num_i=0;
  21. __bit ledReady,overflow=false,gatelost=false;
  22. char led_d;
  23. unsigned char msTick=0,gateDelay=0,speed=NORMAL,keydly=KEYDLY;
  24. unsigned int delay=0;
  25. unsigned long mainFreq=FREQ;
  26. __code char cymomer[]  = {0x63,0x89,0x55,0xc5,0x55,0x21,0xf5,0xff};
  27. __code char counter[]={0x63,0xc5,0xc7,0xd5,0xe1,0x21,0xf5,0xff};
  28. __code char freq[]  = {0x71,0xf5,0x21,0x19,0xff,0xff,0xff,0xff};
  29. __code char fast[]  = {0x43,0x11,0xe1,0x61,0xff,0x2, 0x9f,0x4b};
  30. __code char normal[] ={0x43,0x11,0xe1,0x61,0xff,0x9e, 0x3,0x4b};

  31. typedef union{
  32.   unsigned long l;
  33.   struct{char b0,b1,b2,b3;};
  34. } TmLong;
  35. TmLong inCnt,freqCnt;
  36. unsigned long incount,freqcount;

  37. void counter0() __interrupt 1{
  38.     ++inCnt.b3;
  39. }
  40. void counter1() __interrupt 3{
  41.     ++freqCnt.b3;
  42. }
  43. void serialPort() __interrupt 4{
  44.     if(RI)RI=0;
  45.     if(TI){
  46.         TI=0;
  47.         if(!ledReady){
  48.             SBUF=leds[led_i];
  49.             P3_2=0;
  50.             ledReady=1;
  51.         }else{
  52.             P3_2=1;
  53.         }
  54.     }
  55. }
  56. void timer2(void) __interrupt 5{
  57.     TF2=0;
  58.     ledReady=0;
  59.     if(++led_i>7)led_i=0;
  60.     SBUF=LedDigit[led_i];
  61.     if(--msTick==0){
  62.         msTick=speed;
  63.         if(gateDelay>0)gateDelay--;
  64.     }
  65. }



  66. void putstr(char *s){
  67.     unsigned char i=7;
  68.     do{
  69.         leds[i]=*s;
  70.         s++;
  71.     }while(i--);
  72. }
  73. void putlong(unsigned long a){
  74.     char i,c;
  75.     __idata unsigned char buf[8];
  76.     for(i=0;i<8;i++){
  77.         if(a!=0||i==0){
  78.             c=LedSegs[a%10];
  79.             if(i==3||i==6)c&=0xfe;
  80.             buf[i]=c;
  81.             a/=10;
  82.         }else{
  83.             buf[i]=0xff;
  84.         }
  85.     }
  86.     if(a>0)overflow=true;
  87.     if(overflow)buf[7]&=0xfe;
  88.     if(gatelost)buf[0]&=0xfe;
  89.     for(i=0;i<8;i++)leds[i]=buf[i];
  90. }



  91. unsigned long getCount(){
  92.     inCnt.b0=P0;
  93.     inCnt.b1=TL0;
  94.     inCnt.b2=TH0;
  95.     return inCnt.l;
  96. }
  97. unsigned long getFreq(){
  98.     if(freqcount==0)return 0;
  99.     return (unsigned long)(((unsigned long long)incount)*mainFreq/freqcount);
  100. }
  101. void reset(){
  102.     GATECTL=0;
  103.     TR0=0;TR1=0;
  104.     inCnt.b0=P0;
  105.     inCnt.b1=TL0;
  106.     inCnt.b2=TH0;
  107.     incount=inCnt.l;
  108.     inCnt.b3=0;
  109.     freqCnt.b0=P2;
  110.     freqCnt.b1=TL1;
  111.     freqCnt.b2=TH1;
  112.     freqcount=freqCnt.l;
  113.     freqCnt.b3=0;
  114.     CLR=1;
  115.     TH0=TL0=TH1=TL1=0;
  116.     overflow=false;
  117.     CLR=0;
  118.     TR0=1;TR1=1;
  119.     GATECTL=1;
  120. }
  121. __bit longdown=false;

  122. unsigned char readKey(){
  123.     static __bit longdown=false;
  124.     static unsigned int keylong=KEYLONG;
  125.     unsigned char k=0,key=0;
  126.     static unsigned char gotkey=0;
  127.     if(!KEY)key=1;if(!KEY2)key=2;
  128.     if(key!=0){
  129.         gotkey=key;
  130.         if(keydly>0)keydly--;
  131.         if(keylong>0)keylong--;
  132.         else{
  133.             if(!longdown){
  134.                 longdown=true;
  135.                 k=key+2;
  136.             }
  137.         }
  138.     }else{
  139.         if(keydly==0 && !longdown)k=gotkey;
  140.         keydly=KEYDLY;
  141.         keylong=KEYLONG;
  142.         longdown=false;
  143.     }
  144.      return k;
  145. }
  146. enum{Freq,Counter} mode=Freq;
  147. void setmode(){
  148.     switch(readKey()){
  149.         case 1:  //+
  150.             switch(mode){
  151.                 case Freq:
  152.                     speed=NORMAL;
  153.                     putstr(normal);
  154.                     reset();
  155.                     gateDelay=16;
  156.                     delay=1000;
  157.                     break;
  158.                 case Counter:
  159.                     GATECTL=!GATECTL;
  160.                     break;
  161.             }
  162.             break;
  163.         case 2:  //-
  164.             switch(mode){
  165.                 case Freq:
  166.                     speed=FAST;
  167.                     putstr(fast);
  168.                     reset();
  169.                     gateDelay=16;
  170.                     delay=1000;
  171.                     break;
  172.                 case Counter:
  173.                     reset();
  174.                     break;
  175.             }
  176.             break;
  177.         case 3: //Freq<->counter
  178.         case 4:
  179.             if(mode!=Freq){
  180.                 mode=Freq;
  181.                 putstr(freq);
  182.             }else{
  183.                 mode=Counter;
  184.                 putstr(counter);
  185.             }
  186.             reset();
  187.             delay=1000;
  188.             break;
  189.     }

  190. }


  191. void main(void){

  192.     PT0=1;PT1=1;
  193.     PT2=1;
  194.     SCON=0;
  195.     REN=0;
  196.     ES=1;
  197.     GATECTL=0;
  198.     CLR=1;
  199.     TMOD=0x55;
  200.     TR0=0;TR1=0;ET0=1;ET1=1;
  201.     TH0=0;
  202.     TL0=0;
  203.     TH1=0;
  204.     TL1=0;
  205.     RCAP2H=0xFD;   //10M  1/1000s
  206.     RCAP2L=0xBE;
  207.     T2CON=0;
  208.     T2MOD=0;
  209.     TR2=1;
  210.     ET2=1;
  211.     EA=1;
  212.     putstr(cymomer);
  213.     delay=1000;
  214.     while(1){
  215.         PCON|=1;
  216.         setmode();
  217.         if(delay>0){delay--;continue;}
  218.         switch(mode){
  219.             case Freq:
  220.                 if(!GATE&&!GATECTL||gateDelay==0){
  221.                     gatelost=gateDelay==0;
  222.                     reset();
  223.                     putlong(getFreq());
  224.                     gateDelay=16;
  225.                 }else if(gateDelay==5){
  226.                     GATECTL=0;
  227.                 }
  228.                 break;
  229.             case Counter:
  230.                 putlong(getCount());
  231.                 delay=50;
  232.                 break;
  233.         }

  234.     }
  235. }

复制代码

编译用SDCC,51 small,加入 small 的 longlong.lib  库。
cymomor.jpg

评分

5

查看全部评分

     
发表于 2020-5-10 15:50:32 | 显示全部楼层
真心不错。谢谢楼主!现在都stc啦,为什么还用at呢?

学习下程序先。

如果不用74ls74,触发一下,会有什么不同呢?
回复 支持 反对

使用道具 举报

     
 楼主| 发表于 2020-5-10 17:46:11 来自手机 | 显示全部楼层
peiguoqing 发表于 2020-5-10 15:50
真心不错。谢谢楼主!现在都stc啦,为什么还用at呢?

学习下程序先。

stc也兼容吧,为啥用at呢,只因为我手里有。
回复 支持 反对

使用道具 举报

     
发表于 2020-5-11 20:30:11 | 显示全部楼层
真心不错,74hc393用74hc590代替又可以多出几个I/O口
回复 支持 反对

使用道具 举报

     
发表于 2020-5-11 20:36:09 | 显示全部楼层
温补晶振是稳定的,但有偏差怎么办(比如偏差20Hz)
回复 支持 反对

使用道具 举报

     
发表于 2020-5-11 20:39:26 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
回复 支持 反对

使用道具 举报

     
 楼主| 发表于 2020-5-11 20:49:38 | 显示全部楼层
本帖最后由 翌阳 于 2020-5-11 22:35 编辑
fsj5098 发表于 2020-5-11 20:36
温补晶振是稳定的,但有偏差怎么办(比如偏差20Hz)


那这个晶振就是已经报废了的啊。图上的那个10M恒温,在微调电压设为中点2V时,偏差小于200ppb,也就是2Hz以内。再校准一下,就可以到0偏差了。
回复 支持 反对

使用道具 举报

     
发表于 2020-5-12 00:39:24 | 显示全部楼层
我是在软件里设置基准频率,比如基准晶振为10.0000024MHz,就设基准为10.0000024
回复 支持 反对

使用道具 举报

     
 楼主| 发表于 2020-5-12 07:31:06 来自手机 | 显示全部楼层
fsj5098 发表于 2020-5-12 00:39
我是在软件里设置基准频率,比如基准晶振为10.0000024MHz,就设基准为10.0000024

对,晶振无微调就这样做。还可以加个eeprom来实现软件校准。
回复 支持 反对

使用道具 举报

     
发表于 2020-5-12 08:22:14 | 显示全部楼层
精品!顶你
回复 支持 反对

使用道具 举报

     
发表于 2020-5-13 11:15:38 | 显示全部楼层
393是啥作用?能解释下原理么
回复 支持 反对

使用道具 举报

     
 楼主| 发表于 2020-5-13 11:42:39 | 显示全部楼层
world_all 发表于 2020-5-13 11:15
393是啥作用?能解释下原理么

外置硬件分频计数。为啥外置啊?单片机不是有吗?因为啊,单片机的计数器频率不够高。
回复 支持 反对

使用道具 举报

     
 楼主| 发表于 2020-5-14 09:01:49 | 显示全部楼层
把108行处从    return (unsigned long)(((unsigned long long)incount)*mainFreq/freqcount);
改成    return (unsigned long)((((unsigned long long)incount)*mainFreq*10/freqcount+5)/10);
计算上增加了四舍五入,结果更精确些。
但这些也不好说,四舍五入,还是全舍,那就看个人需要了。0.9Hz是显示0呢?还是认为是1Hz呢?
回复 支持 反对

使用道具 举报

     
发表于 2020-5-15 13:05:44 来自手机 | 显示全部楼层
200ppb的晶体,好找吗?多少钱一个,我见某宝上.0.3ppm的都是42元
回复 支持 反对

使用道具 举报

     
发表于 2020-5-15 13:43:16 | 显示全部楼层
world_all 发表于 2020-5-13 11:15
393是啥作用?能解释下原理么

393分频也做计数高8位,即外部8位,内部16位,共24位。楼主图是把信号分频256后送入计数器,因为C52计数器只有几百KHz,好像是晶振1/24?。但楼主用的是HC393,换成AC/AHC/F393测频要高一些,如AC393特定环境输入频率能达150MHz。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 加入会员

本版积分规则

小黑屋|手机版|矿石收音机 ( 蒙ICP备05000029号-1 )

蒙公网安备 15040402000005号

GMT+8, 2024-4-29 04:43

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

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