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