每个喜欢在某个时间点修补电子设备的工程师都希望拥有自己的实验室设置。万用表、钳形表、示波器、LCR 表、函数发生器、双模电源和自动变压器是体面实验室设置的最低限度设备。虽然所有这些都可以购买,但我们也可以轻松地自己构建一些,例如函数发生器和双模电源。
在本文中,我们将学习如何快速轻松地使用 Arduino 构建自己的函数生成器。这个函数发生器又名波形发生器可以产生频率范围从 1Hz 到 2MHz 的方波 (5V/0V),波的频率可以通过旋钮控制,占空比硬编码为 50%,但很容易改变在节目中也是如此。除此之外,该发生器还可以产生带频率控制的自波。请注意,该发生器不是工业级的,不能用于严格的测试。但除此之外,它对于所有爱好项目都会派上用场,您无需在数周内等待货物到达。还有什么比使用我们自己构建的设备更有趣的事情。
所需材料
Arduino纳米
16*2 字母数字液晶显示屏
旋转编码器
电阻(5.6K,10K)
电容 (0.1uF)
Perf 板,Bergstik
焊接套件
电路原理图
这个Arduino 函数发生器的完整电路图如下所示。如您所见,我们有一个 Arduino Nano 作为我们项目的大脑和一个16x2 LCD来显示当前生成的频率值。我们还有一个旋转编码器,可以帮助我们设置频率。
完整的设置由 Arduino 本身的 USB 端口供电。由于某些我们将在本文后面讨论的原因,我之前使用的连接没有成功。因此,我不得不通过更改引脚顺序来稍微弄乱接线。无论如何,您不会有任何此类问题,因为它已经全部解决了,只需仔细按照电路了解哪个引脚连接到什么。您还可以参考下表来验证您的连接。
电路非常简单;我们在 D9 引脚上产生一个方波,可以这样使用,该方波的频率由旋转编码器控制。然后为了得到一个正弦波,我们在引脚 D5 上产生 SPWM 信号,它的频率必须与 PWM 频率相关,所以我们将此 PWM 信号提供给引脚 D2 作为中断,然后使用 ISR 来控制频率自波。
您可以在面包板上构建电路,甚至可以为其购买 PCB。但我决定将它焊接在 Perf 板上,以快速完成工作并使其长期使用可靠。所有连接完成后,我的电路板看起来像这样。
如果您想了解更多关于如何使用 Arduino 产生 PWM 和正弦波的信息,请阅读以下段落,否则您可以直接向下滚动到 Programming Arduino 部分。
产生变频方波
使用 Arduino 的人可能会熟悉, Arduino只需使用模拟写入功能即可产生 PWM 信号。但此功能仅限于控制 PWM 信号的占空比,而不是控制信号的频率。但是对于波形发生器,我们需要一个频率可以控制的 PWM 信号。这可以通过直接控制 Arduino 的定时器并基于它切换 GPIO 引脚来完成。但是有一些预先构建的库可以做同样的事情并且可以这样使用。我们使用的库是Arduino PWM 频率库。我们将在编码部分讨论更多关于这个库的信息。
这个库也有一些缺点,因为库改变了 Arduino 中的默认定时器 1 和定时器 2 设置。因此,您将不再能够在 Arduino 上使用伺服库或任何其他与计时器相关的库。此外,引脚 9、10、11 和 13 上的模拟写入功能使用定时器 1 和定时器 2,因此您将无法在这些引脚上产生 SPWM。
这个库的优点是它不会干扰 Arduino 的 Timer 0,它比 Timer 1 和 Timer 2 更重要。因此,您可以毫无问题地自由使用延迟函数和 millis() 函数。引脚 5 和 6 也由定时器 0 控制,因此我们在这些引脚上使用模拟写入或伺服控制操作不会有问题。最初我花了一些时间才弄清楚这一点,这就是为什么接线有点混乱的原因。
这里我们也建了一个简单的方波发生器,但是要改变波形的频率,你必须更换电阻或电容,而且很难得到所需的频率。
使用 Arduino 产生正弦波
众所周知,微控制器是数字设备,它们不能仅通过编码来产生正弦波。但是有两种从微控制器获得正弦波的流行方法,一种是利用 DAC,另一种是创建 SPWM。不幸的是,Arduino 板(Due 除外)没有内置 DAC 来产生正弦波,但您始终可以使用简单的 R2R 方法构建自己的 DAC,然后使用它来产生体面的正弦波。但为了减少硬件工作,我决定使用后一种方法来创建 SPWM 信号,然后将其转换为正弦波。
什么是 SPWM 信号?
术语 SPWM 代表正弦脉冲宽度调制。该信号与 PWM 非常相似,但对于 SPWM 信号,占空比被控制为获得类似于正弦波的平均电压。例如,100% 占空比时,平均输出电压为 5V,25% 时我们将有 1.25V,因此通过控制占空比,我们可以获得预定义的可变平均电压,这不过是一个正弦波。这种技术通常用于逆变器。
在上图中,蓝色信号是 SPWM 信号。请注意,波形的占空比从 0% 变化到 100%,然后又回到 0%。该图是为 -1.0 到 +1.0V 绘制的,但在我们的例子中,由于我们使用的是 Arduino,因此比例将从 0V 到 5V。我们将在下面的编程部分学习如何使用 Arduino 生成 SPWM。
将 SPWM 转换为正弦波
将 SPWM 单波转换为正弦波需要一个由至少 4 个电源开关组成的 H 桥电路。我们不会更深入地研究它,因为我们在这里没有使用它。这些 H 桥电路通常用于逆变器。它利用两个 SPWM 信号,其中一个与另一个相移,两个信号都应用于 H 桥中的电源开关,以使对角相对的开关同时打开和关闭。通过这种方式,我们可以得到一个看起来类似于正弦波的波形,但实际上不会更接近上图所示的任何波形(绿色波形)。为了获得纯自波输出,我们必须使用像低通滤波器这样的滤波器,它由电感器和电容器组成。
然而,在我们的电路中,我们不会使用正弦波为任何东西供电。我只是想从生成的 SPWM 信号中创建,所以我使用了一个简单的 RC 滤波器。您也可以尝试使用 LC-Filter 以获得更好的结果,但为了简单起见,我选择了 RC。我的电阻值是 620 欧姆,电容是 10uF。上图显示了来自引脚 5 的 SPWM 信号(黄色)和通过 RC 滤波器后获得的正弦波(蓝色)。
如果您不想改变频率,您也可以使用这个使用晶体管的简单正弦波发生器电路来产生正弦波。
添加 Arduino PWM 频率库
可以通过单击下面的链接下载 Arduino 频率库。
Arduino PWM 频率库
在撰写本文时,Arduino PWM 频率库 V_05 是最新的,它将以 ZIP 文件的形式下载。提取 ZIP 文件广告,您将获得一个名为 PWM 的文件夹。然后导航到 Arduino IDE 的 Libraries 文件夹,对于 Windows 用户,它将位于您的文档中,路径为C:UsersUserDocumentsArduinolibraries。将 PWM 文件夹粘贴到库文件夹中。有时您可能已经有一个 PWM 文件夹,在这种情况下,请确保将旧文件夹替换为新文件夹。
为波形发生器编程 Arduino
与往常一样,可以在本页底部找到该项目的完整程序。您可以这样使用代码,但请确保您已为 Arduino IDE 添加了可变频率库,如上所述,否则您将收到编译时错误。在本节中,让我们查看代码以了解正在发生的事情。
基本上我们想在引脚 9 上产生一个可变频率的 PWM 信号。该频率应使用旋转编码器设置,该值也应显示在 16*2 LCD 上。一旦在引脚 9 上创建了 PWM 信号,它将在引脚 2 上产生中断,因为我们已经将两个引脚都短路了。使用此中断,我们可以控制在引脚 5 上生成的 SPWM 信号的频率。
与往常一样,我们通过包含所需的库来开始我们的程序。液晶库是 Arduino 内置的,我们刚刚安装了 PWM 库。
#include
#include
接下来我们声明全局变量,并提及 LCD、Rotary Encoder 和信号引脚的引脚名称。如果您按照上面的电路图进行操作,则可以不受干扰。
const int rs = 14, en = 15, d4 = 4, d5 = 3, d6 = 6, d7 = 7; //注明LCD连接的管脚号
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
常量 int Encoder_OuputA = 11;
常量 int Encoder_OuputB = 12;
常量 int Encoder_Switch = 10;
常量 int signal_pin = 9;
常量 int Sine_pin = 5;
常量 int POT_pin = A2;
int Previous_Output;
整数乘数 = 1;
双角 = 0;
双倍增量 = 0.2;
int32_t 频率;//要设置的频率
int32_t lower_level_freq = 1; //最低可能的频率值为1Hz
int32_t upper_level_freq = 100000; //最大可能频率为100KHz
在setup函数中,我们初始化 LCD 和串行通信以进行调试,然后将 Encoder 的引脚声明为输入引脚。我们还在引导过程中显示一条介绍消息,以确保一切正常。
lcd.begin(16, 2); //初始化16*2 LCD
lcd.print("信号发生器"); //介绍消息行 1
lcd.setCursor(0, 1);
lcd.print("-CircuitDigest"); //介绍消息第 2 行
延迟(2000);
lcd.clear();
lcd.print("频率:00000Hz");
lcd.setCursor(0, 1);
lcd.print("Inc. by: 1");
序列号.开始(9600);//调试串口
//pin 模式声明
pinMode (Encoder_OuputA, INPUT);
pinMode (Encoder_OuputB, INPUT);
pinMode(编码器开关,输入);
另一条重要的线路是InitTimerSafe,它初始化定时器 1 和 2 以产生可变频率 PWM。一旦调用此函数,Arduino 的默认计时器设置将被更改。
InitTimersSafe(); //初始化定时器而不干扰定时器0
我们还在引脚 2 上运行了外部中断。因此,每当引脚 2 的状态发生变化时,都会触发一个中断,该中断将运行中断服务程序 (ISR) 功能。这里 ISR 函数的名称是generate_sine。
attachInterrupt(0,generate_sine,CHANGE);
接下来,在void 循环中,我们必须检查旋转编码器是否已转动。只有当它已经转动时,我们才需要调整 PWM 信号的频率。我们已经学习了如何将Rotary Encoder 与 Arduino连接。如果你是新来的,我建议你回到那个教程然后回到这里。
如果旋转编码器顺时针旋转,我们通过将频率值与乘数相加来增加频率值。这样我们可以将频率值增加/减少 1、10、100 甚至 1000。乘数的值可以通过按下旋转编码器来设置。如果编码器旋转,我们会改变频率值并在引脚 9 上产生一个 PWM 信号,如下所示。这里的值 32768 将 PWM 设置为 50% 周期。选择值 32768,因为 65536 的 50% 是 32768,因此您可以确定所需占空比的值。但这里的占空比固定为 50%。最后,函数SetPinFrequencySafe用于设置我们的信号引脚(即引脚 9)的频率。
pwmWriteHR(signal_pin, 32768); //默认设置占空比为50% -> for 16-bit 65536/2 = 32768
SetPinFrequencySafe(signal_pin, frequency);
在 ISR 函数中,我们编写代码来生成 SPWM 信号。有很多方法可以生成 SPWM 信号,甚至还有可用于 Arduino 的预构建库。我使用了最简单的方法来利用Arduino 中的 sin() 函数。如果您有兴趣,也可以尝试使用查找表方法。sin() 返回一个介于 -1 到 +1 之间的变量值(十进制),当根据时间绘制时,这会给我们一个正弦波。
现在我们要做的就是将这个 -1 到 +1 的值转换为 0 到 255 并将其提供给我们的模拟写入函数。为此,我将其乘以 255 只是为了忽略小数点,然后使用 map 函数将值从 -255 到 +255 转换为 0 到 +255。最后,使用模拟写入功能将该值写入引脚 5。每次调用 ISR 时,角度值都会增加 0.2,这有助于我们控制正弦波的频率
双正弦值 = sin(角度);
正弦值 *= 255;
int plot = map(sineValue, -255, +255, 0, 255);
Serial.println(plot);
类比写入(正弦引脚,绘图);
角度 += 增量;
在硬件上测试 Arduino 函数发生器
根据电路图构建您的硬件并上传本页底部给出的代码。现在,您已准备好测试您的项目。如果你有 DSO(示波器)会容易得多,但你也可以用 LED 测试它,因为频率范围非常高。
将探头连接到电路的方波和正弦波引脚。如果您没有示波器,请在这两个引脚上使用两个 LED。接通电路电源,您应该会在 LCD 上看到介绍性消息。然后改变旋转编码器并设置所需的频率,您应该能够在示波器上观察方波和正弦波,如下所示。如果您使用 LED,您应该注意到 LED 根据您设置的频率以不同的间隔闪烁。
#include
#include
const int rs = 14, en = 15, d4 = 4, d5 = 3, d6 = 6, d7 = 7; //注明LCD连接的管脚号
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
int Encoder_OuputA = 11;
int Encoder_OuputB = 12;
int Encoder_Switch = 10;
int Previous_Output;
整数乘数 = 1;
双角 = 0;
双倍增量 = 0.2;
常量 int signal_pin = 9;
常量 int Sine_pin = 5;
常量 int POT_pin = A2;
int32_t 频率;//要设置的频率
int32_t lower_level_freq = 1; //最低可能的频率值为1Hz
int32_t upper_level_freq = 100000; //最大可能频率为100KHz
无效 setup()
{
lcd.begin(16, 2); //初始化16*2 LCD
lcd.print("信号发生器"); //介绍消息行 1
lcd.setCursor(0, 1);
lcd.print("-CircuitDigest"); //介绍消息第 2 行
延迟(2000);
lcd.clear();
lcd.print("频率:00000Hz");
lcd.setCursor(0, 1);
lcd.print("Inc. by: 1");
序列号.开始(9600);//调试序列号
InitTimersSafe(); //初始化定时器而不干扰定时器 0
//pin 模式声明
pinMode (Encoder_OuputA, INPUT);
pinMode (Encoder_OuputB, INPUT);
pinMode(编码器开关,输入);
Previous_Output = digitalRead(Encoder_OuputA); //读取输出A的初始值
attachInterrupt(0,generate_sine,CHANGE);
}
void loop()
{
if (digitalRead(Encoder_OuputA) != Previous_Output)
{
if (digitalRead(Encoder_OuputB) != Previous_Output)
{
频率 = 频率 + 乘数;
// Serial.println(频率);
pwmWriteHR(signal_pin, 32768); //默认设置占空比为50% -> for 16-bit 65536/2 = 32768
SetPinFrequencySafe(signal_pin, frequency);
lcd.setCursor(0, 0);
lcd.print("频率:Hz");
lcd.setCursor(5, 0);
lcd.print(频率);
}
else
{
频率 = 频率 - 乘数;
// Serial.println(频率);
pwmWriteHR(signal_pin, 32768); //默认设置占空比为50% -> for 16-bit 65536/2 = 32768
SetPinFrequencySafe(signal_pin, frequency);
lcd.setCursor(0, 0);
lcd.print("频率:Hz");
lcd.setCursor(5, 0);
lcd.print(频率);
}
}
if (digitalRead(Encoder_Switch) == 0)
{
乘数 = 乘数 * 10;
如果(乘数> 1000)
乘数=1;
// Serial.println(乘数);
lcd.setCursor(0, 1);
lcd.print("Cng.by:");
lcd.setCursor(8, 1);
lcd.print(乘数);
延迟(500);
而(数字读取(编码器开关)== 0);
}
Previous_Output = digitalRead(Encoder_OuputA);
}
void generate_sine()
{
double sineValue = sin(angle);
正弦值 *= 255;
int plot = map(sineValue, -255, +255, 0, 255);
Serial.println(plot);
类比写入(正弦引脚,绘图);
角度 += 增量;
如果(角度> 180)
角度=0;
}
评论
查看更多