每個(gè)喜歡在某個(gè)時(shí)間點(diǎn)修補(bǔ)電子設(shè)備的工程師都希望擁有自己的實(shí)驗(yàn)室設(shè)置。萬用表、鉗形表、示波器、LCR 表、函數(shù)發(fā)生器、雙模電源和自動(dòng)變壓器是體面實(shí)驗(yàn)室設(shè)置的最低限度設(shè)備。雖然所有這些都可以購買,但我們也可以輕松地自己構(gòu)建一些,例如函數(shù)發(fā)生器和雙模電源。
在本文中,我們將學(xué)習(xí)如何快速輕松地使用 Arduino 構(gòu)建自己的函數(shù)生成器。這個(gè)函數(shù)發(fā)生器又名波形發(fā)生器可以產(chǎn)生頻率范圍從 1Hz 到 2MHz 的方波 (5V/0V),波的頻率可以通過旋鈕控制,占空比硬編碼為 50%,但很容易改變?cè)诠?jié)目中也是如此。除此之外,該發(fā)生器還可以產(chǎn)生帶頻率控制的自波。請(qǐng)注意,該發(fā)生器不是工業(yè)級(jí)的,不能用于嚴(yán)格的測(cè)試。但除此之外,它對(duì)于所有愛好項(xiàng)目都會(huì)派上用場(chǎng),您無需在數(shù)周內(nèi)等待貨物到達(dá)。還有什么比使用我們自己構(gòu)建的設(shè)備更有趣的事情。
所需材料
Arduino納米
16*2 字母數(shù)字液晶顯示屏
旋轉(zhuǎn)編碼器
電阻(5.6K,10K)
電容 (0.1uF)
Perf 板,Bergstik
焊接套件
電路原理圖
這個(gè)Arduino 函數(shù)發(fā)生器的完整電路圖如下所示。如您所見,我們有一個(gè) Arduino Nano 作為我們項(xiàng)目的大腦和一個(gè)16x2 LCD來顯示當(dāng)前生成的頻率值。我們還有一個(gè)旋轉(zhuǎn)編碼器,可以幫助我們?cè)O(shè)置頻率。
完整的設(shè)置由 Arduino 本身的 USB 端口供電。由于某些我們將在本文后面討論的原因,我之前使用的連接沒有成功。因此,我不得不通過更改引腳順序來稍微弄亂接線。無論如何,您不會(huì)有任何此類問題,因?yàn)樗呀?jīng)全部解決了,只需仔細(xì)按照電路了解哪個(gè)引腳連接到什么。您還可以參考下表來驗(yàn)證您的連接。
電路非常簡(jiǎn)單;我們?cè)?D9 引腳上產(chǎn)生一個(gè)方波,可以這樣使用,該方波的頻率由旋轉(zhuǎn)編碼器控制。然后為了得到一個(gè)正弦波,我們?cè)谝_ D5 上產(chǎn)生 SPWM 信號(hào),它的頻率必須與 PWM 頻率相關(guān),所以我們將此 PWM 信號(hào)提供給引腳 D2 作為中斷,然后使用 ISR 來控制頻率自波。
您可以在面包板上構(gòu)建電路,甚至可以為其購買 PCB。但我決定將它焊接在 Perf 板上,以快速完成工作并使其長期使用可靠。所有連接完成后,我的電路板看起來像這樣。
如果您想了解更多關(guān)于如何使用 Arduino 產(chǎn)生 PWM 和正弦波的信息,請(qǐng)閱讀以下段落,否則您可以直接向下滾動(dòng)到 Programming Arduino 部分。
產(chǎn)生變頻方波
使用 Arduino 的人可能會(huì)熟悉, Arduino只需使用模擬寫入功能即可產(chǎn)生 PWM 信號(hào)。但此功能僅限于控制 PWM 信號(hào)的占空比,而不是控制信號(hào)的頻率。但是對(duì)于波形發(fā)生器,我們需要一個(gè)頻率可以控制的 PWM 信號(hào)。這可以通過直接控制 Arduino 的定時(shí)器并基于它切換 GPIO 引腳來完成。但是有一些預(yù)先構(gòu)建的庫可以做同樣的事情并且可以這樣使用。我們使用的庫是Arduino PWM 頻率庫。我們將在編碼部分討論更多關(guān)于這個(gè)庫的信息。
這個(gè)庫也有一些缺點(diǎn),因?yàn)閹旄淖兞?Arduino 中的默認(rèn)定時(shí)器 1 和定時(shí)器 2 設(shè)置。因此,您將不再能夠在 Arduino 上使用伺服庫或任何其他與計(jì)時(shí)器相關(guān)的庫。此外,引腳 9、10、11 和 13 上的模擬寫入功能使用定時(shí)器 1 和定時(shí)器 2,因此您將無法在這些引腳上產(chǎn)生 SPWM。
這個(gè)庫的優(yōu)點(diǎn)是它不會(huì)干擾 Arduino 的 Timer 0,它比 Timer 1 和 Timer 2 更重要。因此,您可以毫無問題地自由使用延遲函數(shù)和 millis() 函數(shù)。引腳 5 和 6 也由定時(shí)器 0 控制,因此我們?cè)谶@些引腳上使用模擬寫入或伺服控制操作不會(huì)有問題。最初我花了一些時(shí)間才弄清楚這一點(diǎn),這就是為什么接線有點(diǎn)混亂的原因。
這里我們也建了一個(gè)簡(jiǎn)單的方波發(fā)生器,但是要改變波形的頻率,你必須更換電阻或電容,而且很難得到所需的頻率。
使用 Arduino 產(chǎn)生正弦波
眾所周知,微控制器是數(shù)字設(shè)備,它們不能僅通過編碼來產(chǎn)生正弦波。但是有兩種從微控制器獲得正弦波的流行方法,一種是利用 DAC,另一種是創(chuàng)建 SPWM。不幸的是,Arduino 板(Due 除外)沒有內(nèi)置 DAC 來產(chǎn)生正弦波,但您始終可以使用簡(jiǎn)單的 R2R 方法構(gòu)建自己的 DAC,然后使用它來產(chǎn)生體面的正弦波。但為了減少硬件工作,我決定使用后一種方法來創(chuàng)建 SPWM 信號(hào),然后將其轉(zhuǎn)換為正弦波。
什么是 SPWM 信號(hào)?
術(shù)語 SPWM 代表正弦脈沖寬度調(diào)制。該信號(hào)與 PWM 非常相似,但對(duì)于 SPWM 信號(hào),占空比被控制為獲得類似于正弦波的平均電壓。例如,100% 占空比時(shí),平均輸出電壓為 5V,25% 時(shí)我們將有 1.25V,因此通過控制占空比,我們可以獲得預(yù)定義的可變平均電壓,這不過是一個(gè)正弦波。這種技術(shù)通常用于逆變器。
在上圖中,藍(lán)色信號(hào)是 SPWM 信號(hào)。請(qǐng)注意,波形的占空比從 0% 變化到 100%,然后又回到 0%。該圖是為 -1.0 到 +1.0V 繪制的,但在我們的例子中,由于我們使用的是 Arduino,因此比例將從 0V 到 5V。我們將在下面的編程部分學(xué)習(xí)如何使用 Arduino 生成 SPWM。
將 SPWM 轉(zhuǎn)換為正弦波
將 SPWM 單波轉(zhuǎn)換為正弦波需要一個(gè)由至少 4 個(gè)電源開關(guān)組成的 H 橋電路。我們不會(huì)更深入地研究它,因?yàn)槲覀冊(cè)谶@里沒有使用它。這些 H 橋電路通常用于逆變器。它利用兩個(gè) SPWM 信號(hào),其中一個(gè)與另一個(gè)相移,兩個(gè)信號(hào)都應(yīng)用于 H 橋中的電源開關(guān),以使對(duì)角相對(duì)的開關(guān)同時(shí)打開和關(guān)閉。通過這種方式,我們可以得到一個(gè)看起來類似于正弦波的波形,但實(shí)際上不會(huì)更接近上圖所示的任何波形(綠色波形)。為了獲得純自波輸出,我們必須使用像低通濾波器這樣的濾波器,它由電感器和電容器組成。
然而,在我們的電路中,我們不會(huì)使用正弦波為任何東西供電。我只是想從生成的 SPWM 信號(hào)中創(chuàng)建,所以我使用了一個(gè)簡(jiǎn)單的 RC 濾波器。您也可以嘗試使用 LC-Filter 以獲得更好的結(jié)果,但為了簡(jiǎn)單起見,我選擇了 RC。我的電阻值是 620 歐姆,電容是 10uF。上圖顯示了來自引腳 5 的 SPWM 信號(hào)(黃色)和通過 RC 濾波器后獲得的正弦波(藍(lán)色)。
如果您不想改變頻率,您也可以使用這個(gè)使用晶體管的簡(jiǎn)單正弦波發(fā)生器電路來產(chǎn)生正弦波。
添加 Arduino PWM 頻率庫
可以通過單擊下面的鏈接下載 Arduino 頻率庫。
Arduino PWM 頻率庫
在撰寫本文時(shí),Arduino PWM 頻率庫 V_05 是最新的,它將以 ZIP 文件的形式下載。提取 ZIP 文件廣告,您將獲得一個(gè)名為 PWM 的文件夾。然后導(dǎo)航到 Arduino IDE 的 Libraries 文件夾,對(duì)于 Windows 用戶,它將位于您的文檔中,路徑為C:UsersUserDocumentsArduinolibraries。將 PWM 文件夾粘貼到庫文件夾中。有時(shí)您可能已經(jīng)有一個(gè) PWM 文件夾,在這種情況下,請(qǐng)確保將舊文件夾替換為新文件夾。
為波形發(fā)生器編程 Arduino
與往常一樣,可以在本頁底部找到該項(xiàng)目的完整程序。您可以這樣使用代碼,但請(qǐng)確保您已為 Arduino IDE 添加了可變頻率庫,如上所述,否則您將收到編譯時(shí)錯(cuò)誤。在本節(jié)中,讓我們查看代碼以了解正在發(fā)生的事情。
基本上我們想在引腳 9 上產(chǎn)生一個(gè)可變頻率的 PWM 信號(hào)。該頻率應(yīng)使用旋轉(zhuǎn)編碼器設(shè)置,該值也應(yīng)顯示在 16*2 LCD 上。一旦在引腳 9 上創(chuàng)建了 PWM 信號(hào),它將在引腳 2 上產(chǎn)生中斷,因?yàn)槲覀円呀?jīng)將兩個(gè)引腳都短路了。使用此中斷,我們可以控制在引腳 5 上生成的 SPWM 信號(hào)的頻率。
與往常一樣,我們通過包含所需的庫來開始我們的程序。液晶庫是 Arduino 內(nèi)置的,我們剛剛安裝了 PWM 庫。
#include
#include
接下來我們聲明全局變量,并提及 LCD、Rotary Encoder 和信號(hào)引腳的引腳名稱。如果您按照上面的電路圖進(jìn)行操作,則可以不受干擾。
const int rs = 14, en = 15, d4 = 4, d5 = 3, d6 = 6, d7 = 7; //注明LCD連接的管腳號(hào)
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;
整數(shù)乘數(shù) = 1;
雙角 = 0;
雙倍增量 = 0.2;
int32_t 頻率;//要設(shè)置的頻率
int32_t lower_level_freq = 1; //最低可能的頻率值為1Hz
int32_t upper_level_freq = 100000; //最大可能頻率為100KHz
在setup函數(shù)中,我們初始化 LCD 和串行通信以進(jìn)行調(diào)試,然后將 Encoder 的引腳聲明為輸入引腳。我們還在引導(dǎo)過程中顯示一條介紹消息,以確保一切正常。
lcd.begin(16, 2); //初始化16*2 LCD
lcd.print("信號(hào)發(fā)生器"); //介紹消息行 1
lcd.setCursor(0, 1);
lcd.print("-CircuitDigest"); //介紹消息第 2 行
延遲(2000);
lcd.clear();
lcd.print("頻率:00000Hz");
lcd.setCursor(0, 1);
lcd.print("Inc. by: 1");
序列號(hào).開始(9600);//調(diào)試串口
//pin 模式聲明
pinMode (Encoder_OuputA, INPUT);
pinMode (Encoder_OuputB, INPUT);
pinMode(編碼器開關(guān),輸入);
另一條重要的線路是InitTimerSafe,它初始化定時(shí)器 1 和 2 以產(chǎn)生可變頻率 PWM。一旦調(diào)用此函數(shù),Arduino 的默認(rèn)計(jì)時(shí)器設(shè)置將被更改。
InitTimersSafe(); //初始化定時(shí)器而不干擾定時(shí)器0
我們還在引腳 2 上運(yùn)行了外部中斷。因此,每當(dāng)引腳 2 的狀態(tài)發(fā)生變化時(shí),都會(huì)觸發(fā)一個(gè)中斷,該中斷將運(yùn)行中斷服務(wù)程序 (ISR) 功能。這里 ISR 函數(shù)的名稱是generate_sine。
attachInterrupt(0,generate_sine,CHANGE);
接下來,在void 循環(huán)中,我們必須檢查旋轉(zhuǎn)編碼器是否已轉(zhuǎn)動(dòng)。只有當(dāng)它已經(jīng)轉(zhuǎn)動(dòng)時(shí),我們才需要調(diào)整 PWM 信號(hào)的頻率。我們已經(jīng)學(xué)習(xí)了如何將Rotary Encoder 與 Arduino連接。如果你是新來的,我建議你回到那個(gè)教程然后回到這里。
如果旋轉(zhuǎn)編碼器順時(shí)針旋轉(zhuǎn),我們通過將頻率值與乘數(shù)相加來增加頻率值。這樣我們可以將頻率值增加/減少 1、10、100 甚至 1000。乘數(shù)的值可以通過按下旋轉(zhuǎn)編碼器來設(shè)置。如果編碼器旋轉(zhuǎn),我們會(huì)改變頻率值并在引腳 9 上產(chǎn)生一個(gè) PWM 信號(hào),如下所示。這里的值 32768 將 PWM 設(shè)置為 50% 周期。選擇值 32768,因?yàn)?65536 的 50% 是 32768,因此您可以確定所需占空比的值。但這里的占空比固定為 50%。最后,函數(shù)SetPinFrequencySafe用于設(shè)置我們的信號(hào)引腳(即引腳 9)的頻率。
pwmWriteHR(signal_pin, 32768); //默認(rèn)設(shè)置占空比為50% -> for 16-bit 65536/2 = 32768
SetPinFrequencySafe(signal_pin, frequency);
在 ISR 函數(shù)中,我們編寫代碼來生成 SPWM 信號(hào)。有很多方法可以生成 SPWM 信號(hào),甚至還有可用于 Arduino 的預(yù)構(gòu)建庫。我使用了最簡(jiǎn)單的方法來利用Arduino 中的 sin() 函數(shù)。如果您有興趣,也可以嘗試使用查找表方法。sin() 返回一個(gè)介于 -1 到 +1 之間的變量值(十進(jìn)制),當(dāng)根據(jù)時(shí)間繪制時(shí),這會(huì)給我們一個(gè)正弦波。
現(xiàn)在我們要做的就是將這個(gè) -1 到 +1 的值轉(zhuǎn)換為 0 到 255 并將其提供給我們的模擬寫入函數(shù)。為此,我將其乘以 255 只是為了忽略小數(shù)點(diǎn),然后使用 map 函數(shù)將值從 -255 到 +255 轉(zhuǎn)換為 0 到 +255。最后,使用模擬寫入功能將該值寫入引腳 5。每次調(diào)用 ISR 時(shí),角度值都會(huì)增加 0.2,這有助于我們控制正弦波的頻率
雙正弦值 = sin(角度);
正弦值 *= 255;
int plot = map(sineValue, -255, +255, 0, 255);
Serial.println(plot);
類比寫入(正弦引腳,繪圖);
角度 += 增量;
在硬件上測(cè)試 Arduino 函數(shù)發(fā)生器
根據(jù)電路圖構(gòu)建您的硬件并上傳本頁底部給出的代碼?,F(xiàn)在,您已準(zhǔn)備好測(cè)試您的項(xiàng)目。如果你有 DSO(示波器)會(huì)容易得多,但你也可以用 LED 測(cè)試它,因?yàn)轭l率范圍非常高。
將探頭連接到電路的方波和正弦波引腳。如果您沒有示波器,請(qǐng)?jiān)谶@兩個(gè)引腳上使用兩個(gè) LED。接通電路電源,您應(yīng)該會(huì)在 LCD 上看到介紹性消息。然后改變旋轉(zhuǎn)編碼器并設(shè)置所需的頻率,您應(yīng)該能夠在示波器上觀察方波和正弦波,如下所示。如果您使用 LED,您應(yīng)該注意到 LED 根據(jù)您設(shè)置的頻率以不同的間隔閃爍。
#include
#include
const int rs = 14, en = 15, d4 = 4, d5 = 3, d6 = 6, d7 = 7; //注明LCD連接的管腳號(hào)
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
int Encoder_OuputA = 11;
int Encoder_OuputB = 12;
int Encoder_Switch = 10;
int Previous_Output;
整數(shù)乘數(shù) = 1;
雙角 = 0;
雙倍增量 = 0.2;
常量 int signal_pin = 9;
常量 int Sine_pin = 5;
常量 int POT_pin = A2;
int32_t 頻率;//要設(shè)置的頻率
int32_t lower_level_freq = 1; //最低可能的頻率值為1Hz
int32_t upper_level_freq = 100000; //最大可能頻率為100KHz
無效 setup()
{
lcd.begin(16, 2); //初始化16*2 LCD
lcd.print("信號(hào)發(fā)生器"); //介紹消息行 1
lcd.setCursor(0, 1);
lcd.print("-CircuitDigest"); //介紹消息第 2 行
延遲(2000);
lcd.clear();
lcd.print("頻率:00000Hz");
lcd.setCursor(0, 1);
lcd.print("Inc. by: 1");
序列號(hào).開始(9600);//調(diào)試序列號(hào)
InitTimersSafe(); //初始化定時(shí)器而不干擾定時(shí)器 0
//pin 模式聲明
pinMode (Encoder_OuputA, INPUT);
pinMode (Encoder_OuputB, INPUT);
pinMode(編碼器開關(guān),輸入);
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)
{
頻率 = 頻率 + 乘數(shù);
// Serial.println(頻率);
pwmWriteHR(signal_pin, 32768); //默認(rèn)設(shè)置占空比為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
{
頻率 = 頻率 - 乘數(shù);
// Serial.println(頻率);
pwmWriteHR(signal_pin, 32768); //默認(rèn)設(shè)置占空比為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)
{
乘數(shù) = 乘數(shù) * 10;
如果(乘數(shù)> 1000)
乘數(shù)=1;
// Serial.println(乘數(shù));
lcd.setCursor(0, 1);
lcd.print("Cng.by:");
lcd.setCursor(8, 1);
lcd.print(乘數(shù));
延遲(500);
而(數(shù)字讀取(編碼器開關(guān))== 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;
}
?
評(píng)論