⽤Arduino平台设计和制作的简易频率计
  ⼤家好,我是潮打空城,前⼏天本⼈所在的⼤学在临近期末考试的时候出其意外的布置了⼀道频率计设计的题⽬。这个题⽬拿到⼿之后,我初步判断要⽤到单⽚机,如果⽤其他种类的单⽚机,都不可避免的需要进⾏程序控制字的编程,着实需要对照⼿册好好钻研⼀番,然⽽在考试周这种⽐较特殊的情况下,为了投机取巧,只好选⽤了提供了⼤量的库函数的Arduino平台进⾏频率计的设计。
  题⽬的要求如下:
  针对这种要求我设计了这样的解决⽅案:
  从框图能够看出来,核⼼的实现功能的部分由单⽚机负责,所有的硬件电路都是给单⽚机服务,提供⽅便。
  所以从出题⾓度来说,这个设计的核⼼在软件上,⽽不在硬件上,所以我还是先介绍软件的部分。
  软件/逻辑部分的框图:
  由于该设计功能⽐较单⼀,⽤⽐较简单的流程图就可以将逻辑描述清楚。对于这种较为简单的功能,实际上也可以尝试做⼀个纯模拟或者模拟+数字的系统进⾏实现,如果要动⽤单⽚机,⼀定要加⼊单⽚机⽐较优势的部分功能。但是本⼈⽐较懒,⽤Arduino开发可以省很多事,所以就不想那么多了。
  下⾯是具体实现所⽤的代码:
1void InputVoltageMeasurement()                    //测量输⼊电压,并发送⾄串⼝
2 {
3int SingleCycleSamplingPoints;                //采样点数
4if(Frequency<=100)                            //保证在频率范围内都能够采到⾄少100个点
5        SingleCycleSamplingPoints=1/(SamplingTime_s*Frequency);            //根据频率智能计算采样点数
6else
7        SingleCycleSamplingPoints=100/(SamplingTime_s*Frequency);
8float Peak=(((analogRead(InputVoltagePin))/1023.0)*5.0)/InputPartialRatio;    //在众多的采样点中到峰值
9for(int p=1;p<SingleCycleSamplingPoints;p++)
10    {
11float CurrentValue=(((analogRead(InputVoltagePin))/1023.0)*5.0)/InputPartialRatio;
12if(CurrentValue>Peak)
13            Peak=CurrentValue;
14    }
15    InputVoltage=Peak;                            //认为输⼊电压就是峰值电压
16    Serial.println();                            //串⼝输出
17    Serial.print("InputVoltage=");
18    Serial.print(InputVoltage);
19    Serial.println("V");
20 }
  输⼊信号并不是直流信号,讲道理如果要⽤单⽚机直接测量需要⽤到峰值检波电路,本⼈曾经焊过⼀个,可以实现结果,值得注意的⼀点就是对于低频⼀定要加滤波,但是在后期进⾏硬件联调的时候,发现这个模块⼀和其它模块级联就⼯作不正常,最后作罢,索性纯粹⽤软件进⾏峰值检波。
  这段代码的核⼼函数是Arduino官⽅库中的analogRead()函数,以此作为单⽚机和外界联系的桥梁。
  ⽤软件进⾏峰值检波的思路是这样:考虑⼀个交流信号⼀个周期的情况,在其中采⼤量的点,如果点的数量⽐较多,那么就很有可能能够采到⼀个周期中的峰值,这样就实现了交流信号的峰值检波。
1void FrequencyDutyMeasurement()                    //测量输⼊信号的频率、占空⽐,并将测量值发送到串⼝
2 {
3volatile int i=digitalRead(SquareWave);      //先看现在是⾼电平还是低电平
4while(1)
5  {
6if(i!=digitalRead(SquareWave))                //发现第⼀次跳变
7    {
8        i=digitalRead(SquareWave);
9        CurrentTime=micros();                    //记录第⼀次跳变的时间
10goto S1;
11    }
12  }
13  S1:FirstTime=CurrentTime;
14int m=1;                                        //存放跳变的次数,次数越多精度越⾼,测量时间也就会越长
15if(Shift==1)                                    //分档处理,低频的时候点取少,⾼频的时候点取多
16  {
17while(1)
18    {
19if(i!=digitalRead(SquareWave))
frequency函数计算频数20        {
21            m++;
22            i=digitalRead(SquareWave);
23if(m==200)
24            {
25                CurrentTime=micros();            //记录200次跳变的时间
26goto S2;
27            }
28        }
29    }
30  S2:SecondTime=CurrentTime;
31  Frequency=100000000/(SecondTime-FirstTime);    //计算频率
32  }
33if(Shift==2)
34  {
35while(1)
36    {
37if(i!=digitalRead(SquareWave))
38        {
39            m++;
40            i=digitalRead(SquareWave);
41if(m==1000)
42            {
43                CurrentTime=micros();            //记录1000次跳变的时间
44goto S3;
45            }
46        }
47    }
48  S3:SecondTime=CurrentTime;
49  Frequency=500000000/(SecondTime-FirstTime);    //计算频率
50  }
51  Serial.println();                            //将频率值发送⾄串⼝
52  Serial.print("The frequency is ");
53  Serial.print(Frequency);
54  Serial.println("HZ");
55if(Frequency<=1000)                            //根据测得的频率智能换挡
56    Shift=1;
57else
58    Shift=2;
59
60volatile int j=digitalRead(SquareWave);        //先看现在是⾼电平还是低电平
61while(1)
62  {
63if(j!=digitalRead(SquareWave))              //发现第⼀次跳变
64    {
65      j=digitalRead(SquareWave);
66      CurrentTime=micros();                      //记录第⼀次跳变的时间
67goto S4;
68    }
69  }
70  S4:FirstTime=CurrentTime*1.0;
71int n=0;
72while(1)
73  {
74if(j!=digitalRead(SquareWave))
75      {
76        n++;
77        j=digitalRead(SquareWave);
78if(n==99)
79        {
80            CurrentTime=micros();
81            Time_99=CurrentTime-FirstTime;            //记录99次跳变的时间
82        }
83if(n==199)
84        {
85            CurrentTime=micros();
86            Time_199=CurrentTime-FirstTime;            //记录199次跳变的时间
87goto S5;
88        }
89      }
90  }
91  S5:
92if(j)                                                //根据第⼀次测的电平值来计算占空⽐
93    DutyRatio=((Time_199-2*Time_99)*Frequency)/10000.0;
94else
95    DutyRatio=100-((Time_199-2*Time_99)*Frequency)/10000.0;
96  Serial.print("The duty ratio is ");                //串⼝输出占空⽐
97  Serial.print(DutyRatio);
98  Serial.println("%");
99 }
  经过了硬件的整形,这时所有的输⼊信号都被整成了振幅为5V的⽅波信号,所以就可以被单⽚机测量频率。
  这⾥所⽤的核⼼的函数是Arduino官⽅库⾥⾯的时间函数micros(),这个函数可以返回Arduino开机的时间。
  ⽤这个函数进⾏频率和占空⽐的测量是这样:这个函数可以认为是提供了表的作⽤,也就是我们都有
的钟点的概念。众所周知,单⽚机可以检测上下沿电平的变化,⽽我们通过micros()函数也可以知道发⽣电平跳转时的时刻,考虑⼀个单周期的情况,如果发⽣了两个电平跳变,对⽅波来说,实际上这两个跳变的时间差就等于周期的⼀半的时间,所以我们就能够知道周期的长短,知道了周期,就能够通过简单的数学变换得到频率。⽽占空⽐,可以测量三次电平跳变,其中总有⼀段时间是⾼电平持续的时间,只要知道⾼电平持续的时间以及周期,根据占空⽐的定义就可以算出占空⽐。
  理论上是这样,实际上还是这样吗?未必,通过这种思路写的代码,也许能够测量出⼀个值,但是这个值本⾝就不稳定,⽆法说明问题。出现这种现象的原因就是对于频率⽐较⾼的信号,测量单周期的时间对单⽚机来说太难了,解决办法就是,把很多的周期的时间加到⼀起进⾏计算,这样单⽚机就能够反应的过来。
  这种思路进⾏测量会导致⼀个⽐较明显的缺陷,那就是输⼊信号越快,那单⽚机也得测得快,这样⼀来,频率⼀⾼,输出的东西太快,⼈眼会看不清,这就是⼀种缺陷。
1void OverVoltageAlarm()                                //过压报警
2 {
3    tone(Buzzer,1000);                                //蜂鸣器报警
4    delay(1000);                                    //延时1s
5    noTone(Buzzer);                                    //蜂鸣器停⽌报警
6for(int k=0;k<3;k++)                            //屏幕显⽰三次过压警告
7    {
8        Serial.println("OVER VOLTAGE!!!");
9    }
10 }
  这段代码相对来说就⽐较简单了,核⼼函数是Arduino官⽅扩展库的tone()和noTone()函数,这两个函数是专门⽤来输出蜂鸣器驱动信号的。
  整体代码如下:
1/*
2代码名称:FrequencyMeter
3代码功能:实现对输⼊信号的电压、频率及占空⽐的测量。
4作者:马彭康
5时间:2016 6 25
6版本:4.0
7作者邮箱:1710945152@qq
8*/
9
10/***********************声明功能函数********************/
11
12void InputVoltageMeasurement();                    //测量输⼊电压,并发送⾄串⼝
13void FrequencyDutyMeasurement();                //测量输⼊信号的频率、占空⽐,并将测量值发送到串⼝
14void OverVoltageAlarm();                        //过压报警
15/*********************************************************/
16
17/************************定义引脚************************/
18
19#define InputVoltagePin A0
20#define SquareWave 2
21#define Buzzer 3
22
23/*********************************************************/
24
25/***********************定义变量*************************/
26
27float InputVoltage;
28volatile unsigned long CurrentTime;                //当前时间
29volatile unsigned long FirstTime;                //第⼀次跳变的时间
30volatile unsigned long SecondTime;                //第⼆次跳变的时间
31 unsigned long Frequency;
32 unsigned long DutyRatio;                        //占空⽐
33volatile unsigned long Time_99;                  //计数99次跳变的时间
34volatile unsigned long Time_199;                //计数199次跳变的时间
35int Shift;                                        //测量档位
36
37/*********************************************************/
38
39/***********************定义常量*************************/
40
41#define InputPartialRatio (0.3429)                //定义输⼊分压⽐
42#define SamplingTime_s (0.0001)                    //ADC采样⼀次所花费的时间
43
44/*********************************************************/
45
46void setup()
47 {
48    pinMode(SquareWave,INPUT);
49    Serial.begin(9600);
50    Serial.println("This is a frequency meter!");
51    Shift=1;                                    //默认档位是1挡
52    Frequency=20;                                //默认频率是20HZ
53 }
54
55void loop()
56 {
57    InputVoltageMeasurement();
58if(InputVoltage<=7.5)
59        FrequencyDutyMeasurement();
60else
61        OverVoltageAlarm();
62 }
63
64void InputVoltageMeasurement()                    //测量输⼊电压,并发送⾄串⼝
65 {
66int SingleCycleSamplingPoints;                //采样点数
67if(Frequency<=100)                            //保证在频率范围内都能够采到⾄少100个点
68        SingleCycleSamplingPoints=1/(SamplingTime_s*Frequency);            //根据频率智能计算采样点数
69else
70        SingleCycleSamplingPoints=100/(SamplingTime_s*Frequency);
71float Peak=(((analogRead(InputVoltagePin))/1023.0)*5.0)/InputPartialRatio;    //在众多的采样点中到峰值 72for(int p=1;p<SingleCycleSamplingPoints;p++)
73    {
74float CurrentValue=(((analogRead(InputVoltagePin))/1023.0)*5.0)/InputPartialRatio;
75if(CurrentValue>Peak)
76            Peak=CurrentValue;
77    }
78    InputVoltage=Peak;                            //认为输⼊电压就是峰值电压
79    Serial.println();                            //串⼝输出
80    Serial.print("InputVoltage=");
81    Serial.print(InputVoltage);
82    Serial.println("V");
83 }
84
85void FrequencyDutyMeasurement()                    //测量输⼊信号的频率、占空⽐,并将测量值发送到串⼝
86 {
87volatile int i=digitalRead(SquareWave);      //先看现在是⾼电平还是低电平
88while(1)
89  {
90if(i!=digitalRead(SquareWave))                //发现第⼀次跳变
91    {
92        i=digitalRead(SquareWave);
93        CurrentTime=micros();                    //记录第⼀次跳变的时间

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。