数码之家

 找回密码
 立即注册
搜索
查看: 3499|回复: 8

[C51] 89C52单片机制作“32位等精度频率计”学习笔记

[复制链接]
发表于 2020-9-9 13:48:16 | 显示全部楼层 |阅读模式

爱科技、爱创意、爱折腾、爱极致,我们都是技术控

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 cx_star 于 2020-9-9 13:53 编辑

水了两个贴修复CY7C68013A示波器虚拟示波器逻辑分析仪
红外解码程序:格力YB0F2、YAD0F,美的R05d,其他NEC
终于有点M币,把@fsj5098 的附件下下来了


仿制32位等精度频率计
STC89C52单片机等精度频率计(带仿真)

研究了一下,fsj5098 的第一个链接里面有源程序。当然这个源程序有点问题,
第二个链接的HEX是可以用的,也可以正确仿真。

研究了下,才明白“等精度”的意思,应该就是通过590分别对 “待测试 晶振 和 “基准”晶振计数,然后相除就能计算“待测试 晶振的频率。


———————第一部分:


主要芯片7474资料:
7474.png
主要看第4行和第5行,即在CLK上升沿时,Q和非Q  根据 D 的电平输出相应电平。
这个芯片不知道什么用,我给去掉了

主要芯片590资料:
590.png
第1、2行的意思是,590的14脚OE控制控制Q0至Q7是否输出590寄存器的值,以及9脚RCO是否在有进位时,输出一个低电平(用来级联)
第3行的意思是,590的13脚CPR(CLK上升沿时,将590计数器值存入590寄存器
第5行的意思是,590的10脚MRC(CLR)为低电平是,590计数器置零


———————第二部分:
fsj5098 程序分析
7474和590的控制逻辑不难,关键是jianfa_conpute函数。
我看了好一会,才懂原来是以加法来替代乘法运算。
以下为fsj5098 程序加了一点注释。第一部分为存储在uchar Jshu[4]中的32位整数乘con。第二部分为乘100
  1. else if((Jshu[3]==0)&&(Jshu[2]==0)&&(Jshu[1]==0))        //--放大10000倍
  2.         {
  3.                 Dwei = 4;
  4.         tempx[0] = 0x10;    //0x2710=10000
  5.                 tempx[1] = 0x27;
  6.                 tempx[2] = 0x00;
  7.                 con = Jshu[0];
  8.                 Jshu[0] = 0;

  9.         for(i=0;i<con;i++)//比如con为15,则循环15遍,使15个10000相加
  10.                 {
  11.                         sta = 0;
  12.                         for(j=0;j<4;j++)
  13.                         {
  14.                                 Jshu[j] += tempx[j] + sta;
  15.                 if(CY)        sta = 1;    //寄存器CY:进位标志
  16.                                 else        sta = 0;
  17.                         }
  18.                 }        
  19.         }
  20.         else if((Jshu[3]==0)&&(Jshu[2]==0))                                //--放大100倍
  21.         {
  22.                 Dwei = 6;
  23.                 for(i=0;i<4;i++)
  24.                         tempx[i] = Jshu[i];
  25.         for(i=0;i<99;i++)//循环100次
  26.                 {
  27.                         sta = 0;
  28.                         for(j=0;j<4;j++)
  29.                         {
  30.                                 Jshu[j] += tempx[j] + sta;
  31.                                 if(CY)        sta = 1;
  32.                                 else        sta = 0;
  33.                         }        
  34.                 }
  35.         }
复制代码


这部分应该是用减法来实现除法运算,太费脑了,没看懂
  1. while(1)
  2.         {
  3.                 for(i=0;i<4;i++)
  4.                         tempx[i] = Jshu[i];
  5.                 //----------------------------- 做减法
  6.                 sta = 0;
  7.                 for(j=0;j<4;j++)
  8.                 {
  9.                         Jshu[j] -= (Bshu[j] + sta);
  10.                         if(CY)        sta = 1;
  11.                         else        sta = 0;
  12.                 }
  13.                 //---------------------------- 有借位
  14.                 if(sta)
  15.                 {        
  16.                         wei ++;
  17.                         for(i=0;i<4;i++)                                                //--减数 * 10                        
  18.                                 Jshu[i] = tempx[i];
  19.                         for(i=0;i<9;i++)                                                //--自加9次--放大10倍
  20.                         {
  21.                                 sta = 0;
  22.                                 for(j=0;j<4;j++)
  23.                                 {
  24.                                         Jshu[j] += tempx[j] + sta;
  25.                                         if(CY)        sta = 1;
  26.                                         else        sta = 0;
  27.                                 }
  28.                         }
  29.                 }
  30.                 //----------------------------无借位 //--数值加1
  31.                 else
  32.                         temp[wei]++;

  33.                 if(!(Jshu[0]||Jshu[1]||Jshu[2]||Jshu[3]))
  34.                         break;

  35.                 if(wei>20)
  36.                         break;
  37.         }
复制代码


———————第三部分
我的程序和仿真(完整程序见附件,程序和附件全部基于fsj5098的附件改的
去掉了7474,没有用INT0
flag在定时器0中变为1(采样周期
  1.     while(1)
  2.         {
  3.                 if(flag==1)
  4.                 {
  5.                         CLKEN = 1;  //关闭590计数器
  6.                         flag=0;

  7.                         CLK = 0;
  8.                         CLK = 1; //上升沿:存入590寄存器
  9.                         
  10.                         for(i=0;i<8;i++)
  11.                         {
  12.                                 //P0 = 0xff;
  13.                                 P1 = ADD_data[i];
  14.                                 Cont1[i] = P0;
  15.                         }
  16.                         for(i=0;i<4;i++)
  17.                         {
  18.                                 d1 += Cont1[i];
  19.                                 d1<<=8;
  20.                                 d2 += Cont1[i+4];
  21.                                 d2<<=8;
  22.                         }
  23.                         printf("%lu %lu %fKHz\r\n",d1,d2,(float)(d1*10000.00/d2));
  24.                         
  25.                         CLR = 0;   // 低电平:重置590
  26.                         CLR = 1;
  27.                         timer2_ctr = 0;
  28.                         TR0 = 1;
  29.                         CLKEN = 0; // 使能590计数
  30.                 }
  31.         }
复制代码
我直接用*和/了……不知道会有什么影响

仿真图:
等精度频率计.png
30.72K.png
32.768.png
10M.png
1M.png

说明:
1、程序第一次输出测得频率不准原因:第一次计时在主while(1)循环外
2、后面也有时不准,不知道原因,麻烦高手解答
3、fsj5098的输出两组采样数值很固定,不知道怎么实现的


源程序及仿真.zip (61.49 KB, 下载次数: 6)

hex.zip (4.59 KB, 下载次数: 0)
我还是来骗M币的~


PS:
1、电脑很早就装了Proteus,但是今天才知道仿真,而且仿真还这么有意思~
2、提示Symbol $MKRORIGIN used but not found in libraries 用管理员权限运行就好了



补充内容 (2020-9-12 06:36):
百度了一下等精度频率计,才明白很多东西,以上谬论,各位见笑了,不要当真

打赏

参与人数 1家元 +100 收起 理由
家睦 + 100

查看全部打赏

 楼主| 发表于 2020-9-9 13:50:39 | 显示全部楼层
全部源程序

  1. #include "AT89X52.h"
  2. #include "stdio.h"

  3. typedef unsigned char uint8_t;
  4. typedef unsigned int uint16_t;       
  5. typedef unsigned long uint32_t;       

  6. sbit        CLR = P2^0;
  7. sbit        CLK = P2^1;
  8. sbit        CLKEN = P2^2;
  9. sbit        LED = P2^3;
  10. sbit        LED2 = P2^4;

  11. extern bit flag = 0;

  12. extern unsigned char Cont1[8] = {0};

  13. const unsigned char          ADD_data[8] = {0xf7,0xfb,0xfd,0xfe,0x7f,0xbf,0xdf,0xef};

  14. unsigned int timer2_ctr = 0;
  15. unsigned long int0_count = 0;

  16. void timer0_isr() interrupt 1
  17. {
  18. //!!!注意!!! 定时器2必须由软件对溢出标志位清零,硬件不能清零,这里与定时器0和定时器1不同!!!

  19.         TL0 = 0xB0;                //设置定时初值
  20.         TH0 = 0x3C;                //设置定时初值
  21.         timer2_ctr++;

  22.         if(timer2_ctr>=5)
  23.         {
  24.                 flag = 1;
  25.                 TR0 = 0;
  26.         }
  27. }
  28. void Uart_Isr() interrupt 4
  29. {
  30.     if (RI)
  31.         RI = 0;             //Clear receive interrupt flag
  32.     if (TI)
  33.         TI = 0;             //Clear transmit interrupt flag
  34. }


  35. //-----------------------------------------------------------------------------------
  36. //--外部中断0
  37. //-----------------------------------------------------------------------------------
  38. void exint0() interrupt 0           //(location at 0003H)
  39. {
  40.         int0_count++;
  41.         if(int0_count>=1000000000){
  42.                 int0_count = 0;
  43.                 flag = 1;
  44.         }
  45. }

  46. char putchar(char c){
  47.         TI = 0;
  48.         SBUF = c;
  49.         while(!TI);
  50.         TI = 0;
  51.         return c;
  52. }

  53. void Timer0Init(void)                //50毫秒@12.000MHz
  54. {
  55.         TMOD &= 0xF0;                //设置定时器模式
  56.         TMOD |= 0x01;
  57.         TL0 = 0xB0;                //设置定时初值
  58.         TH0 = 0x3C;                //设置定时初值
  59.         TF0 = 0;                //清除TF0标志
  60. //        TR0 = 1;                //定时器0开始计时
  61.         ET0=1;           //开启定时器0中断
  62. }

  63. void init_serialcom(void) //串口配置 2400        波特率
  64. {
  65.         SCON=0x50;      
  66.         TCON=0x40;
  67.         TMOD=0x20;    //直接赋值,init_serialcom应在Timer0Init前
  68.         TL1 = 0xf3;                //设定定时初值
  69.         TH1 = 0xf3;                //设定定时初值

  70.         IP=0x00;
  71.         IE=0x00;
  72.         PCON=0x00;

  73.         REN=1;
  74.         ES=1;

  75.         TR1 = 1;
  76.        
  77. //        IT0 = 1;                        //set INT0 int type (1:Falling下降沿触发 0:Low level低电平触发)
  78. //        EX0 = 1;                        //enable INT0 interrupt
  79.        
  80.         EA = 1;                         //open global interrupt switch

  81. }

  82. //-----------------------------------------------------------------------------------
  83. //--主函数
  84. /*
  85. C1=00007800
  86. C2=0098967F       //9999999
  87. F= 30720.0030Hz
  88. C1=00008000
  89. C2=00989680       //10000000
  90. F= 32768.0000Hz
  91. C1=000186A0
  92. C2=00989680
  93. F= 100000.000Hz
  94. C1=000F423B
  95. C2=0098964E
  96. F= 1000000.00Hz
  97. */
  98. //-----------------------------------------------------------------------------------
  99. void main(void)
  100. {
  101.         unsigned char i = 0 ;
  102.         unsigned char dbuff[8] = 0;
  103.         unsigned long d1,d2;

  104.     P0 = 0xff;  //读590口
  105.     P1 = 0xff;  //590的OE
  106.         P2 = 0xff;
  107.                
  108.         init_serialcom();
  109.         Timer0Init();
  110.         CLK = 1;
  111.         CLR = 1;
  112.        
  113.         printf("test\r\n");  
  114.        
  115.         CLR = 0;   // 重置590
  116.         CLR = 1;
  117.         timer2_ctr = 0;
  118.         TR0 = 1;
  119.         CLKEN = 0;
  120.        
  121.     while(1)
  122.         {
  123.                 if(flag==1)
  124.                 {
  125.                         CLKEN = 1;  //关闭590计数器
  126.                         flag=0;

  127.                         CLK = 0;
  128.                         CLK = 1; //上升沿:存入590寄存器
  129.                        
  130.                         for(i=0;i<8;i++)
  131.                         {
  132.                                 //P0 = 0xff;
  133.                                 P1 = ADD_data[i];
  134.                                 Cont1[i] = P0;
  135.                         }
  136.                         for(i=0;i<4;i++)
  137.                         {
  138.                                 d1 += Cont1[i];
  139.                                 d1<<=8;
  140.                                 d2 += Cont1[i+4];
  141.                                 d2<<=8;
  142.                         }
  143.                         printf("%lu %lu %fkHz\r\n",d1,d2,(float)(d1*10000.00/d2));
  144.                        
  145.                         CLR = 0;   // 低电平:重置590
  146.                         CLR = 1;
  147.                         timer2_ctr = 0;
  148.                         TR0 = 1;
  149.                         CLKEN = 0; // 使能590计数
  150.                 }
  151.         }

  152. }
复制代码

补充内容 (2020-9-18 10:13):
代码有个地方错了,160行应该在159行前面,162行应该在161行前面
回复 支持 反对

使用道具 举报

发表于 2020-9-10 09:23:58 | 显示全部楼层
原理图没有看明白这个频率计的信号输入脚是哪一个。能说一下吗
回复 支持 反对

使用道具 举报

 楼主| 发表于 2020-9-10 11:12:01 | 显示全部楼层
CHENGLCD 发表于 2020-9-10 09:23
原理图没有看明白这个频率计的信号输入脚是哪一个。能说一下吗

上面是 输入,下面是参考晶振。
其实无所谓,怎么接都行,只是在程序里面改下谁是被除数就行了
回复 支持 反对

使用道具 举报

发表于 2020-9-10 17:31:23 | 显示全部楼层
其实我也不懂单片机是一个坛友给我的只是仿真做了一个,74HC74是必须的,主要起到被测信号的同步,当然这个电路用的计数多达8片74HC594,已放弃了,改用2片32位计数芯片,可以直接测量100MHz以上频率,程序还是一样
回复 支持 反对

使用道具 举报

 楼主| 发表于 2020-9-10 20:42:15 来自手机浏览器 | 显示全部楼层
fsj5098 发表于 2020-9-10 17:31
其实我也不懂单片机是一个坛友给我的只是仿真做了一个,74HC74是必须的,主要起到被测信号的同步,当然这个 ...

我本来准备做一个的,但是590好贵,就放弃了……32位的计数芯片岂不是更贵…
至于7474的同步功能,因为你提供的C程序中未涉及到,我也不知道怎么用,所以把它删了。结果就是单片机每次读590计数都不一样,也导致结果出现误差(至少仿真时是这样)
回复 支持 反对

使用道具 举报

发表于 2020-9-10 23:04:42 | 显示全部楼层
74HC590也不贵,也就0.8元一片
回复 支持 反对

使用道具 举报

发表于 2020-9-12 15:32:54 | 显示全部楼层
fsj5098 发表于 2020-9-10 23:04
74HC590也不贵,也就0.8元一片

32位计数芯片是什么型号?
回复 支持 反对

使用道具 举报

发表于 2020-9-13 01:50:32 | 显示全部楼层
yuua68 发表于 2020-9-12 15:32
32位计数芯片是什么型号?

暂时保密:titter:
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

APP|手机版|小黑屋|关于我们|联系我们|法律条款|技术知识分享平台

闽公网安备35020502000485号

闽ICP备2021002735号-2

GMT+8, 2024-4-20 22:09 , Processed in 0.234001 second(s), 14 queries , Redis On.

Powered by Discuz!

© 2006-2023 smzj.net

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