如果您是像我這樣想要調(diào)整不同電子電路的電子愛(ài)好者,那么擁有一個(gè)像樣的函數(shù)發(fā)生器有時(shí)是必不可少的。但是擁有一個(gè)是個(gè)問(wèn)題,因?yàn)檫@樣的基本設(shè)備可能要花一大筆錢(qián)。構(gòu)建自己的測(cè)試設(shè)備不僅更便宜,而且是提高知識(shí)的好方法。
因此,在本文中,我們將使用 Arduino 和 AD9833 DDS 函數(shù)發(fā)生器模塊構(gòu)建一個(gè)簡(jiǎn)單的信號(hào)發(fā)生器,它可以在輸出端產(chǎn)生最大頻率為 12 MHz 的正弦波、方波和三角波。最后,我們將在示波器的幫助下測(cè)試輸出頻率。
什么是 DDS 函數(shù)發(fā)生器?
顧名思義,函數(shù)發(fā)生器是一種可以通過(guò)設(shè)置輸出特定頻率的特定波形的設(shè)備。例如,假設(shè)您有一個(gè) 想要測(cè)試輸出頻率響應(yīng)的LC 濾波器,您可以在函數(shù)發(fā)生器的幫助下輕松做到這一點(diǎn)。您需要做的就是設(shè)置所需的輸出頻率和波形,然后您可以調(diào)低或調(diào)高它以測(cè)試響應(yīng)。這只是一個(gè)例子,隨著列表的繼續(xù),你可以用它做更多的事情。
DDS 代表直接數(shù)字合成。它是一種波形發(fā)生器,使用數(shù)模轉(zhuǎn)換器(DAC) 從頭構(gòu)建信號(hào)。此方法專(zhuān)門(mén)用于生成正弦波。但是我們使用的IC可以產(chǎn)生方波或三角波信號(hào)。DDS 芯片內(nèi)部發(fā)生的操作是數(shù)字的,因此它可以非??焖俚厍袚Q頻率,也可以非??焖俚貜囊粋€(gè)信號(hào)切換到另一個(gè)信號(hào)。該設(shè)備具有良好的頻率分辨率和寬頻譜。
了解 AD9833 函數(shù)發(fā)生器 IC 的工作原理
我們項(xiàng)目的核心是由模擬設(shè)備設(shè)計(jì)和開(kāi)發(fā)的AD9833可編程波形發(fā)生器 IC。它是一種低功耗、可編程波形發(fā)生器,能夠產(chǎn)生最大頻率為 12 MHz 的正弦波、三角波和方波。這是一款非常獨(dú)特的 IC,只需一個(gè)軟件程序即可改變輸出頻率和相位。它有一個(gè) 3 線 SPI 接口,這就是為什么與這個(gè) IC 通信變得非常簡(jiǎn)單和容易的原因。該IC的功能框圖如下所示。
該IC的工作非常簡(jiǎn)單。如果我們看一下上面的功能框圖,我們會(huì)發(fā)現(xiàn)我們有一個(gè)相位累加器,它的工作是存儲(chǔ)從 0 到 2π 的所有可能的正弦波數(shù)字值。接下來(lái),我們有 SIN ROM,其工作是將相位信息轉(zhuǎn)換為以后可以直接映射到幅度的信息。SIN ROM 使用數(shù)字相位信息作為查找表的地址,并將相位信息轉(zhuǎn)換為幅度。最后,我們有一個(gè) 10 位數(shù)模轉(zhuǎn)換器,它的工作是從 SIN ROM 接收數(shù)字?jǐn)?shù)據(jù)并將其轉(zhuǎn)換為相應(yīng)的模擬電壓,這就是我們從輸出中得到的電壓。在輸出端,我們還有一個(gè)開(kāi)關(guān),只需一點(diǎn)軟件代碼就可以打開(kāi)或關(guān)閉它。我們將在本文后面討論。AD9833 數(shù)據(jù)表,您也可以查看它以獲取更多信息。
構(gòu)建基于 AD9833 的函數(shù)發(fā)生器所需的組件
下面列出了構(gòu)建基于 AD9833 的函數(shù)發(fā)生器所需的組件,我們使用非常通用的組件設(shè)計(jì)了這個(gè)電路,這使得復(fù)制過(guò)程非常容易。
Arduino 納米 - 1
AD9833 DDS 函數(shù)發(fā)生器 - 1
128 X 64 OLED 顯示屏 - 1
通用旋轉(zhuǎn)編碼器 - 1
DC 桶形千斤頂 - 1
LM7809 穩(wěn)壓器 - 1
470uF 電容 - 1
220uF 電容 - 1
104pF 電容 - 1
10K 電阻 - 6
輕觸開(kāi)關(guān) - 4
螺絲端子 5.04mm - 1
女頭 - 1
12V 電源 - 1
基于 AD9833 的函數(shù)發(fā)生器 - 原理圖
AD9833 和基于 Arduino 的函數(shù)發(fā)生器的完整電路圖如下所示。
我們將使用帶有 Arduino 的 AD9833來(lái)生成我們想要的頻率。在本節(jié)中,我們將借助原理圖解釋所有細(xì)節(jié);讓我簡(jiǎn)要概述一下電路發(fā)生的情況。讓我們從AD9833 模塊開(kāi)始。 AD9833模塊為函數(shù)發(fā)生器模塊,按照原理圖與Arduino連接。為了給電路供電,我們使用了 LM7809 穩(wěn)壓器 IC,它帶有一個(gè)不錯(cuò)的去耦電容,這是必要的,因?yàn)殡娫丛肼晻?huì)干擾輸出信號(hào),從而導(dǎo)致不需要的輸出。與往常一樣,Arduino 是這個(gè)項(xiàng)目的大腦。為了顯示設(shè)定頻率和其他有價(jià)值的信息,我們連接了一個(gè) 128 X 64 OLED 顯示模塊。為了改變頻率范圍,我們使用了三個(gè)開(kāi)關(guān)。第一個(gè)將頻率設(shè)置為 Hz,第二個(gè)將輸出頻率設(shè)置為 KHz,第三個(gè)將頻率設(shè)置為 MHz,我們還有另一個(gè)按鈕可用于啟用或禁用輸出。最后,我們有旋轉(zhuǎn)編碼器,我們必須連接一些上拉電阻,否則這些開(kāi)關(guān)將不起作用,因?yàn)槲覀冋跈z查池方法上的按鈕按下事件。旋轉(zhuǎn)編碼器用于改變頻率,旋轉(zhuǎn)編碼器內(nèi)部的輕觸開(kāi)關(guān)用于選擇設(shè)定的波形。
基于 AD9833 的函數(shù)發(fā)生器 - Arduino 代碼
此項(xiàng)目中使用的完整代碼可在此頁(yè)面底部找到。添加所需的頭文件和源文件后,應(yīng)該可以直接編譯Arduino文件了。您可以從下面給出的鏈接下載ad9833 Arduino 庫(kù)和其他庫(kù),或者您可以使用板管理器方法安裝庫(kù)。
ino中的代碼說(shuō)明。文件如下。首先,我們首先包含所有必需的庫(kù)。AD9833 DDS 模塊庫(kù)首先是 OLED 庫(kù),我們的一些計(jì)算需要數(shù)學(xué)庫(kù)。
ino中的代碼說(shuō)明。文件如下。首先,我們首先包含所有必需的庫(kù)。AD9833 DDS 模塊庫(kù)首先是 OLED 庫(kù),我們的一些計(jì)算需要數(shù)學(xué)庫(kù)。
?
#include// AD9833 模塊庫(kù) #include // OLED 線庫(kù) #include // OLED 支持庫(kù) #include // OLED 庫(kù) #include // 數(shù)學(xué)庫(kù)
?
接下來(lái),我們?yōu)榘粹o、開(kāi)關(guān)、旋轉(zhuǎn)編碼器和 OLED 定義所有必要的輸入和輸出引腳。
?
#define SCREEN_WIDATA_PINH 128 // OLED 顯示寬度(以像素為單位) #define SCREEN_HEIGHT 64 // OLED 顯示高度,以像素為單位 #define SET_FREQUENCY_HZ A2 // 以Hz為單位設(shè)置頻率的按鈕 #define SET_FREQUENCY_KHZ A3 // 以 Khz 為單位設(shè)置頻率的按鈕 #define SET_FREQUENCY_MHZ A6 // 以 Mhz 為單位設(shè)置頻率的按鈕 #define ENABLE_DISABLE_OUTPUT_PIN A7 // 啟用/禁用輸出的按鈕 #define FNC_PIN 4 // AD9833 模塊需要的 Fsync #define CLK_PIN 8 // 編碼器的時(shí)鐘引腳 #define DATA_PIN 7 // 編碼器的數(shù)據(jù)引腳 #define BTN_PIN 9 // 編碼器上的內(nèi)部按鈕
?
此后,我們定義了此代碼中所需的所有必要變量。首先,我們定義一個(gè)整數(shù)變量計(jì)數(shù)器來(lái)存儲(chǔ)旋轉(zhuǎn)編碼器的值。接下來(lái)的兩個(gè)變量clockPin和clockPinState存儲(chǔ)理解編碼器方向所需的引腳狀態(tài)。我們有一個(gè)時(shí)間變量來(lái)保存當(dāng)前的定時(shí)器計(jì)數(shù)器值,這個(gè)變量用于按鈕去抖動(dòng)。接下來(lái),我們有一個(gè)無(wú)符號(hào)長(zhǎng)變量moduleFrequency,它保存了計(jì)算的頻率,它將被應(yīng)用。接下來(lái),我們有debounce delay??梢愿鶕?jù)需要調(diào)整此延遲。接下來(lái),我們有三個(gè)布爾變量set_frequency_hz,set_frequency_Khz和?set_frequency_Mhz?這三個(gè)變量用于確定模塊的當(dāng)前設(shè)置。我們將在本文后面更詳細(xì)地討論它。接下來(lái),我們有存儲(chǔ)輸出波形狀態(tài)的變量,默認(rèn)輸出波形是正弦波。最后,我們有encoder_btn_count變量,它保存用于設(shè)置輸出波形的編碼器按鈕計(jì)數(shù)。
?
整數(shù)計(jì)數(shù)器 = 1;// 如果旋轉(zhuǎn)編碼器轉(zhuǎn)動(dòng),此計(jì)數(shù)器值將增加或減少 整數(shù)時(shí)鐘引腳;// 旋轉(zhuǎn)編碼器使用的引腳狀態(tài)占位符 整數(shù)時(shí)鐘引腳狀態(tài);// 旋轉(zhuǎn)編碼器使用的引腳狀態(tài)占位符 無(wú)符號(hào)長(zhǎng)時(shí)間 = 0; // 用于去抖動(dòng) 無(wú)符號(hào)長(zhǎng)模塊頻率;//用于設(shè)置輸出頻率 長(zhǎng)時(shí)間去抖 = 220; // 去抖動(dòng)延遲 布爾 btn_state; // 用于啟用 AD98333 模塊的禁用輸出 布爾 set_frequency_hz = 1; // AD9833模塊的默認(rèn)頻率 bool set_frequency_khz; bool set_frequency_mhz; 字符串 waveSelect = "SIN"; // 模塊的啟動(dòng)波形 int 編碼器_btn_count = 0; // 用于檢查編碼器按鈕是否按下 接下來(lái),我們有兩個(gè)對(duì)象,一個(gè)用于 OLED 顯示器,另一個(gè)用于 AD9833 模塊。 Adafruit_SSD1306 顯示(SCREEN_WIDATA_PINH,SCREEN_HEIGHT,&Wire,-1); AD9833 gen(FNC_PIN);
?
接下來(lái),我們有我們的setup()函數(shù),在該 setup 函數(shù)中,我們首先啟用 Serial 進(jìn)行調(diào)試。我們借助begin()方法初始化 AD9833 模塊。接下來(lái),我們將所有分配的旋轉(zhuǎn)編碼器引腳設(shè)置為輸入。而我們將時(shí)鐘引腳的值存儲(chǔ)在clockPinState變量中,這是旋轉(zhuǎn)編碼器的必要步驟。
接下來(lái),我們將所有按鈕引腳設(shè)置為輸入,并在display.begin()方法的幫助下啟用 OLED 顯示,我們還使用if 語(yǔ)句檢查是否有任何錯(cuò)誤。完成后,我們清除顯示并打印啟動(dòng)啟動(dòng)畫(huà)面,我們添加 2 秒的延遲,這也是啟動(dòng)畫(huà)面的延遲,最后,我們調(diào)用update_display()函數(shù)清除屏幕并更新再次顯示。update_display()方法的細(xì)節(jié)將在本文后面討論。
?
無(wú)效設(shè)置(){ 序列號(hào).開(kāi)始(9600);// 啟用串行@9600 波特 gen.Begin(); // 這必須是聲明 AD9833 對(duì)象后的第一個(gè)命令 pinMode(CLK_PIN,輸入);// 將引腳設(shè)置為輸入 pinMode(DATA_PIN,輸入); pinMode(BTN_PIN,INPUT_PULLUP); clockPinState = digitalRead(CLK_PIN); pinMode(SET_FREQUENCY_HZ, INPUT);// 將引腳設(shè)置為輸入 pinMode(SET_FREQUENCY_KHZ,輸入); pinMode(SET_FREQUENCY_MHZ,輸入); pinMode(ENABLE_DISABLE_OUTPUT_PIN,輸入); if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // 地址 0x3D 為 128x64 Serial.println(F("SSD1306 分配失敗")); 為了 (;;); } display.clearDisplay(); //清屏 display.setTextSize(2); // 設(shè)置文本大小 display.setTextColor(白色);//設(shè)置液晶顏色 display.setCursor(30, 0); // 設(shè)置光標(biāo)位置 display.println("AD9833"); // 打印這個(gè)文本 display.setCursor(17, 20); // 設(shè)置光標(biāo)位置 display.println("函數(shù)"); // 打印這個(gè)文本 display.setCursor(13, 40); // 設(shè)置光標(biāo)位置 display.println("生成器"); // 打印這個(gè)文本 顯示.顯示();// 更新顯示 延遲(2000);// 延遲 2 秒 更新顯示();// 調(diào)用 update_display 函數(shù) }
?
接下來(lái),我們有我們的loop()函數(shù),所有主要功能都寫(xiě)在循環(huán)部分。
首先,我們讀取旋轉(zhuǎn)編碼器的時(shí)鐘引腳并將其存儲(chǔ)在我們之前聲明的 clockPin 變量中。接下來(lái),在if語(yǔ)句中,我們檢查 pin 的先前值和 pin 的當(dāng)前值是否相似,我們還檢查 pin 的當(dāng)前值。如果全部為真,我們檢查數(shù)據(jù)引腳,如果為真,則表示編碼器逆時(shí)針旋轉(zhuǎn),我們?cè)赾ounter--命令的幫助下減少計(jì)數(shù)器值。否則,我們使用counter ++命令增加計(jì)數(shù)器值。最后,我們用另一個(gè)if語(yǔ)句將最小值設(shè)置為 1。接下來(lái),我們用當(dāng)前的clockPin更新clockPinState未來(lái)使用的價(jià)值。
?
無(wú)效循環(huán)() { clockPin = digitalRead(CLK_PIN); if (clockPin != clockPinState && clockPin == 1) { if (digitalRead(DATA_PIN) != clockPin) { 柜臺(tái) - ; } 別的 { counter ++;// 編碼器順時(shí)針旋轉(zhuǎn),因此遞增 } 如果 (計(jì)數(shù)器 < 1 ) 計(jì)數(shù)器 = 1; Serial.println(計(jì)數(shù)器); 更新顯示(); }
?
接下來(lái),我們有我們的代碼來(lái)檢測(cè)按鈕按下。在本節(jié)中,我們借助一些嵌套的 if 語(yǔ)句來(lái)檢測(cè)編碼器內(nèi)部的按鈕,if (digitalRead(BTN_PIN) == LOW && millis() - time > denounce),?在此語(yǔ)句中,我們首先檢查按鈕是否引腳是否為低電平,如果它為低電平,則它被按下。然后我們?cè)俅螜z查帶有去抖動(dòng)延遲的計(jì)時(shí)器值,如果兩個(gè)語(yǔ)句都為真,那么我們聲明它是一個(gè)成功的按鈕按下動(dòng)作,如果這樣我們?cè)黾觘ncoder_btn_count值。接下來(lái),我們聲明另一個(gè) if 語(yǔ)句將最大計(jì)數(shù)器值?設(shè)置為 2,我們需要它,因?yàn)槲覀冋谑褂盟鼇?lái)設(shè)置輸出波形。連續(xù)三個(gè) if 語(yǔ)句就是這樣做的,如果值為 0,則選擇正弦波,如果為 1,則為方波,如果值為 2,則為三角波。在所有這三個(gè)?if 語(yǔ)句中,我們使用?update_display()?函數(shù)更新顯示。?最后,我們用當(dāng)前的計(jì)時(shí)器計(jì)數(shù)器值更新時(shí)間變量。
?
//如果我們檢測(cè)到一個(gè)LOW信號(hào),按鈕被按下 if ( digitalRead(BTN_PIN) == LOW && millis() - time > debounce) { 編碼器_btn_count++;// 增加值 if (encoder_btn_count > 2) // 如果值大于 2 將其重置為 0 { 編碼器_btn_count = 0; } if (encoder_btn_count == 0) { // 如果值為 0 則選擇正弦波 波選擇=“罪”;// 用 sin 值更新字符串變量 更新顯示();// 更新顯示 } if (encoder_btn_count == 1) { // 如果值為 1 則選擇方波 波選擇 = "SQR"; // 用 SQR 值更新字符串變量 更新顯示();// 更新顯示 } if (encoder_btn_count == 2) { // 如果值為 1 則選擇三角波 波選擇=“三”;// 用 TRI 值更新字符串變量 update_display();// 更新顯示 } 時(shí)間=毫秒();// 更新時(shí)間變量 }
?
接下來(lái),我們定義所有必要的代碼,這些代碼需要設(shè)置所有具有去抖動(dòng)延遲的按鈕。由于按鈕連接到 Arduino 的模擬引腳,我們使用模擬讀取命令來(lái)識(shí)別按鈕按下,如果模擬讀取值低于 30,則我們檢測(cè)其成功按下按鈕,我們等待 200 毫秒檢查它是實(shí)際的按鈕按下還是僅是噪音。?如果此陳述為真,我們?yōu)椴紶栕兞糠峙溆糜谠O(shè)置函數(shù)發(fā)生器的 Hz、Khz 和 Mhz 值的值。接下來(lái),我們更新顯示并更新時(shí)間變量。我們?yōu)榕c Arduino 連接的所有四個(gè)按鈕執(zhí)行此操作。
?
if (analogRead(SET_FREQUENCY_HZ) < 30 && millis() - time > debounce) { set_frequency_hz = 1; //更新布爾值 設(shè)置頻率khz = 0; set_frequency_mhz = 0; update_display();// 更新顯示 time = millis();// 更新時(shí)間變量 } if (analogRead(SET_FREQUENCY_KHZ) < 30 && millis() - time > debounce){ set_frequency_hz = 0; //更新布爾值 set_frequency_khz = 1; set_frequency_mhz = 0; 模塊頻率 = 計(jì)數(shù)器 * 1000; update_display();// 更新顯示 time = millis();// 更新時(shí)間變量 } if (analogRead(SET_FREQUENCY_MHZ) < 30 && millis() - time > debounce ) { // 使用去抖延遲檢查模擬引腳 set_frequency_hz = 0; //更新布爾值 設(shè)置頻率khz = 0; set_frequency_mhz = 1; 模塊頻率 = 計(jì)數(shù)器 * 1000000; update_display();// 更新顯示 time = millis();// 更新時(shí)間變量 } if (analogRead(ENABLE_DISABLE_OUTPUT_PIN) < 30 && millis() - time > debounce ) {// 使用去抖延遲檢查模擬引腳 btn_state = ! btn_state; // 反轉(zhuǎn)按鈕狀態(tài) gen.EnableOutput(btn_state); // 根據(jù)按鈕狀態(tài)啟用/禁用函數(shù)發(fā)生器的輸出 update_display();// 更新顯示 time = millis();// 更新時(shí)間變量 } }
?
最后,我們有了update_display()函數(shù)。在此功能中,我們所做的不僅僅是更新此顯示器,因?yàn)轱@示器的某些部分無(wú)法在 OLED 中更新。要更新它,您必須使用新值重新繪制它。這使得編碼過(guò)程變得更加困難。
在這個(gè)函數(shù)中,我們從清除顯示開(kāi)始。接下來(lái),我們?cè)O(shè)置所需的文本大小。此后,我們?cè)O(shè)置光標(biāo)并使用display.println("Function Function"); 打印函數(shù)生成器;命令。在display.setCursor(0, 20)?函數(shù)??的幫助下,?我們?cè)俅螌⑽谋敬笮≡O(shè)置為 2,將光標(biāo)設(shè)置為 (0,20 ) 。
這是我們打印波浪信息的地方。
?
display.clearDisplay(); // 首先清除顯示 display.setTextSize(1); //設(shè)置文字大小 display.setCursor(10, 0); // 設(shè)置光標(biāo)位置 display.println("函數(shù)生成器"); //打印文本 display.setTextSize(2);//設(shè)置文字大小 display.setCursor(0, 20);//設(shè)置光標(biāo)位置
?
接下來(lái),我們檢查布爾變量以獲取頻率詳細(xì)信息并更新moduleFrequency變量中的值。我們對(duì) Hz、kHz 和 MHz 值執(zhí)行此操作。接下來(lái),我們檢查waveSelect變量并確定選擇了哪個(gè)波?,F(xiàn)在,我們有了設(shè)置波形類(lèi)型和頻率的值。
?
if (set_frequency_hz == 1 && set_frequency_khz == 0 && set_frequency_mhz == 0 ) { // 檢查設(shè)置頻率的按鈕是否被按下 模塊頻率 = 計(jì)數(shù)器;//用當(dāng)前計(jì)數(shù)器值更新moduleFrequency變量 } if (set_frequency_hz == 0 && set_frequency_khz == 1 && set_frequency_mhz == 0 ) { // 檢查是否按下了設(shè)置 KHz 頻率的按鈕 moduleFrequency = counter * 1000;//用當(dāng)前計(jì)數(shù)器值更新 moduleFrequency 變量,但我們乘以 1000 將其設(shè)置為 KHZ } if (set_frequency_hz == 0 && set_frequency_khz == 0 && set_frequency_mhz == 1) { // 檢查是否按下了以MHz為單位的頻率設(shè)置按鈕 模塊頻率 = 計(jì)數(shù)器 * 1000000; 如果(模塊頻率> 12000000) { 模塊頻率 = 12000000;// 不要讓頻率超過(guò) 12Mhz 計(jì)數(shù)器 = 12; } } if (waveSelect == "SIN") { // 正弦波被選中 display.println("SIN"); gen.ApplySignal(SINE_WAVE, REG0, moduleFrequency); Serial.println(moduleFrequency); } if (waveSelect == "SQR") {// Sqr 波被選中 display.println("SQR"); gen.ApplySignal(SQUARE_WAVE, REG0, moduleFrequency); Serial.println(moduleFrequency); } if (waveSelect == "TRI") {// 三波被選中 display.println("TRI"); gen.ApplySignal(TRIANGLE_WAVE, REG0, moduleFrequency); // 更新 AD9833 模塊。 Serial.println(moduleFrequency); }
?
我們?cè)俅卧O(shè)置光標(biāo)并更新計(jì)數(shù)器值。我們?cè)俅螜z查布爾值以更新顯示器上的頻率范圍,我們必須這樣做,因?yàn)?OLED 的工作原理非常奇怪。
?
display.setCursor(45, 20); display.println(計(jì)數(shù)器); // 在顯示器上打印計(jì)數(shù)器信息。 如果(set_frequency_hz == 1 && set_frequency_khz == 0 && set_frequency_mhz == 0){ display.setCursor(90, 20); display.println("Hz"); // 在顯示器上打印 Hz 顯示.顯示();// 當(dāng)所有設(shè)置更新顯示 } 如果(set_frequency_hz == 0 && set_frequency_khz == 1 && set_frequency_mhz == 0){ display.setCursor(90, 20); display.println("Khz"); 顯示.顯示();// 當(dāng)所有設(shè)置更新顯示 } if (set_frequency_hz == 0 && set_frequency_khz == 0 && set_frequency_mhz == 1) { display.setCursor(90, 20); display.println("Mhz"); 顯示.顯示();// 當(dāng)所有設(shè)置更新顯示 }
?
接下來(lái),我們檢查按鈕按下變量以將輸出打開(kāi)/輸出關(guān)閉到 OLED。由于 OLED 模塊,這再次需要完成。
?
如果(btn_state){ display.setTextSize(1); display.setCursor(65, 45); display.print("輸出開(kāi)啟"); // 打印輸出到顯示器 顯示.顯示(); display.setTextSize(2); } 別的 { display.setTextSize(1); display.setCursor(65, 45); display.print("輸出關(guān)閉"); // 打印輸出到顯示器 顯示.顯示(); display.setTextSize(2); }
?
這標(biāo)志著我們編碼過(guò)程的結(jié)束。如果此時(shí)有疑惑,可以查看代碼中的注釋進(jìn)一步理解。
測(cè)試基于 AD9833 的函數(shù)發(fā)生器
為了測(cè)試電路,使用上述設(shè)置。如您所見(jiàn),我們已將 12V 直流電源適配器連接到 DC 筒形插孔,并將 Hantek 示波器連接到電路的輸出端。我們還將示波器連接到筆記本電腦,以可視化和測(cè)量輸出頻率。
完成后,我們?cè)谛D(zhuǎn)編碼器的幫助下將輸出頻率設(shè)置為 5Khz,并測(cè)試輸出正弦波,果然,輸出端是 5Khz 正弦波。
接下來(lái),我們將輸出波形改為三角波,但頻率保持不變,輸出波形如下圖所示。
然后我們把輸出改成方波,觀察輸出,是一個(gè)完美的方波。
我們還改變了頻率范圍并測(cè)試了輸出,它運(yùn)行良好。
進(jìn)一步增強(qiáng)
該電路只是概念驗(yàn)證,需要進(jìn)一步增強(qiáng)。首先,我們需要一塊優(yōu)質(zhì)的 PCB 和一些優(yōu)質(zhì)的 BNC 連接器用于輸出,否則我們無(wú)法獲得更高的頻率。模塊的幅度非常低,因此為了增強(qiáng)它,我們需要一些運(yùn)算放大器電路來(lái)放大輸出電壓。可以連接電位計(jì)以改變輸出幅度??梢赃B接一個(gè)用于抵消信號(hào)的開(kāi)關(guān);這也是必備功能。此外,代碼需要大量改進(jìn),因?yàn)樗悬c(diǎn)錯(cuò)誤。最后,OLED 顯示器需要更換,否則無(wú)法編寫(xiě)易于理解的代碼。
#include
庫(kù) #include
#include
#include
#include
#define SCREEN_WIDATA_PINH 128 // OLED 顯示屏寬度,以像素為單位
#define SCREEN_HEIGHT 64 // OLED 顯示屏高度,以像素為單位
#define SET_FREQUENCY_HZ A2 // 設(shè)置頻率的按鈕,以 Hz 為單位
#define SET_FREQUENCY_KHZ A3 // 以 Khz 為單位設(shè)置頻率
的按鈕 #define SET_FREQUENCY_MHZ A6 // 以 Mhz 為單位設(shè)置頻率的按鈕
#define ENABLE_DISABLE_OUTPUT_PIN A7 // 啟用/禁用輸出的按鈕
#define FNC_PIN 4 // AD9833 模塊所需的 Fsync
#define CLK_PIN 8 // 編碼器時(shí)鐘引腳
#define DATA_PIN 7 // 編碼器數(shù)據(jù)引腳
#define BTN_PIN 9 // 編碼器內(nèi)部按鈕
int counter = 1; // 如果旋轉(zhuǎn)編碼器轉(zhuǎn)動(dòng)
int clockPin; // 這個(gè) Counter 值會(huì)增加或減少 // 旋轉(zhuǎn)編碼器使用的占位符 por 引腳狀態(tài)
int clockPinState; // 旋轉(zhuǎn)編碼器使用的占位符 por 引腳狀態(tài)
unsigned long time = 0; // 用于去抖動(dòng)
unsigned long moduleFrequency; // 用于設(shè)置輸出頻率
在
long debounce = 220; // 去抖動(dòng)延遲
bool btn_state; // 用于啟用 AD98333 模塊的禁用輸出
bool set_frequency_hz = 1; // AD9833模塊的默認(rèn)頻率
bool set_frequency_khz;?
bool set_frequency_mhz;?
字符串 waveSelect = "SIN"; // 模塊啟動(dòng)波形
int encoder_btn_count = 0; // 用于檢查編碼器按鈕按下
Adafruit_SSD1306 display(SCREEN_WIDATA_PINH, SCREEN_HEIGHT, &Wire, -1);?
AD9833 gen(FNC_PIN);?
無(wú)效設(shè)置(){
? 序列.開(kāi)始(9600);
? gen.Begin(); // 這必須是聲明 AD9833 對(duì)象
? pinMode(CLK_PIN, INPUT) 后的第一個(gè)命令;
? pinMode(DATA_PIN,輸入);
? pinMode(BTN_PIN,INPUT_PULLUP);
? clockPinState = digitalRead(CLK_PIN);?
? pinMode(SET_FREQUENCY_HZ,輸入);
? pinMode(SET_FREQUENCY_KHZ,輸入);
? pinMode(SET_FREQUENCY_MHZ,輸入);
? pinMode(ENABLE_DISABLE_OUTPUT_PIN,輸入);
? if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // 地址 0x3D for 128x64?
? ? Serial.println(F("SSD1306 allocation failed"));?
? ? 為了 (;;);?
? }?
? display.clearDisplay(); // 清屏
? display.setTextSize(2); // 設(shè)置文本大小
? display.setTextColor(WHITE); // 設(shè)置 LCD 顏色
? display.setCursor(30, 0); // 設(shè)置光標(biāo)位置
? display.println("AD9833"); // 打印這個(gè)文本
? display.setCursor(17, 20); // 設(shè)置光標(biāo)位置
? display.println("Function"); // 打印這個(gè)文本
? display.setCursor(13, 40); // 設(shè)置光標(biāo)位置
? display.println("Generator"); // 打印這個(gè)文本
? 顯示.顯示();// 更新顯示
? 延遲(2000);// 延遲 2 秒
? update_display(); // 調(diào)用 update_display 函數(shù)
}?
void loop()?
{?
? clockPin = digitalRead(CLK_PIN);?
? if (clockPin != clockPinState && clockPin == 1) {?
? ? if (digitalRead(DATA_PIN) != clockPin) {
? ? ? 計(jì)數(shù)器 --;?
? ? }?
? ? else {?
? ? ? counter ++;// 編碼器順時(shí)針旋轉(zhuǎn),所以遞增
? ? }?
? ? if (counter < 1) counter = 1;?
? ? Serial.println(計(jì)數(shù)器);?
? ? 更新顯示();
? }
? 時(shí)鐘引腳狀態(tài) = 時(shí)鐘引腳;// 記住最后的 CLK_PIN 狀態(tài)
? //如果我們檢測(cè)到 LOW 信號(hào),按鈕被按下
? if (digitalRead(BTN_PIN) == LOW && millis() - time > debounce) {?
? ? encoder_btn_count++; // 增加值
? ? if (encoder_btn_count > 2) // 如果值大于 2 將其重置為 0?
? ? {?
? ? ? encoder_btn_count = 0;?
? ? }?
? ? if (encoder_btn_count == 0) { // 如果值為 0 則選擇正弦波
? ? ? waveSelect = "SIN"; // 用 sin 值更新字符串變量
? ? ? update_display(); // 更新顯示
? ? }?
? ? if (encoder_btn_count == 1) { // 如果值為 1 則選擇方波
? ? ? waveSelect = "SQR"; // 使用 SQR 值更新字符串變量
? ? ? update_display(); // 更新顯示
? ? }
? ? if (encoder_btn_count == 2) { // 如果值為 1 則選擇三角波
? ? ? waveSelect = "TRI"; // 用 TRI 值更新字符串變量
? ? ? update_display();// 更新顯示
? ? }?
? ? time = millis(); // 更新時(shí)間變量
? }?
? // 使用analogread方法檢查按鈕按下動(dòng)作
? // 稍微延遲以幫助消除讀數(shù)
? if (analogRead(SET_FREQUENCY_HZ) < 30 && millis() - time > debounce) { // 檢查analogpin有去抖延遲
? ? //更新布爾值
? ? set_frequency_hz = 1;?
? ? 設(shè)置頻率khz = 0;
? ? set_frequency_mhz = 0;?
? ? update_display();// 更新顯示
? ? time = millis();// 更新時(shí)間變量
? }?
? if (analogRead(SET_FREQUENCY_KHZ) < 30 && millis() - time > debounce) { // 使用 debounce delay 檢查analogpin?
? ? //更新布爾值
? ? set_frequency_hz = 0;?
? ? set_frequency_khz = 1;?
? ? set_frequency_mhz = 0;?
? ? 模塊頻率 = 計(jì)數(shù)器 * 1000;
? ? update_display();// 更新顯示
? ? 時(shí)間 = millis();// 更新時(shí)間變量
? }?
? if (analogRead(SET_FREQUENCY_MHZ) < 30 && millis() - time > debounce ) { // 使用 debounce delay 檢查analogpin?
? ? //update布爾值
? ? set_frequency_hz = 0;?
? ? 設(shè)置頻率khz = 0;
? ? set_frequency_mhz = 1;
? ? 模塊頻率 = 計(jì)數(shù)器 * 1000000;
? ? update_display();// 更新顯示
? ? 時(shí)間 = millis();// 更新時(shí)間變量
? }?
? if (analogRead(ENABLE_DISABLE_OUTPUT_PIN) < 30 && millis() - time > debounce ) {// 使用 debounce delay 檢查analogpin?
? ? btn_state = ! btn_state; // 反轉(zhuǎn)按鈕狀態(tài)
? ? gen.EnableOutput(btn_state); // 根據(jù)按鈕狀態(tài)啟用/禁用函數(shù)發(fā)生器的輸出
? ? update_display();// 更新顯示
? ? time = millis();// 更新時(shí)間變量
? }?
}?
void update_display()?
{?
? display.clearDisplay(); // 首先清除顯示
? display.setTextSize(1); //設(shè)置文本大小
? display.setCursor(10, 0); // 設(shè)置光標(biāo)位置
? display.println("Function Generator"); //打印文本
? display.setTextSize(2);//設(shè)置文本大小
? display.setCursor(0, 20);//設(shè)置光標(biāo)位置
? if (set_frequency_hz == 1 && set_frequency_khz == 0 && set_frequency_mhz == 0 ) { / / 檢查是否按下了以Hz為單位設(shè)置頻率的按鈕
? ? moduleFrequency = counter; //更新模塊頻率變量與當(dāng)前計(jì)數(shù)器值
? }
? if (set_frequency_hz == 0 && set_frequency_khz == 1 && set_frequency_mhz == 0 ) { // 檢查是否按下了設(shè)置 KHz 頻率的按鈕
? ? moduleFrequency = counter * 1000;//更新模塊頻率變量與當(dāng)前計(jì)數(shù)器值但我們相乘1000 to set it on KHZ?
? }?
? if (set_frequency_hz == 0 && set_frequency_khz == 0 && set_frequency_mhz == 1) { // 檢查設(shè)置頻率的按鈕是否被按下
? ? moduleFrequency = counter * 1000000;?
? ? if (moduleFrequency > 12000000)?
? ? {?
? ? ? moduleFrequency = 12000000; // 不要讓頻率大于 12Mhz
? ? ? 計(jì)數(shù)器 = 12;?
? ? }?
? }?
? if (waveSelect == "SIN"
? ? display.println("SIN");?
? ? gen.ApplySignal(SINE_WAVE, REG0, moduleFrequency);?
? ? Serial.println(moduleFrequency);?
? }?
? if (waveSelect == "SQR") {// 選擇 Sqr 波
? ? display.println("SQR");?
? ? gen.ApplySignal(SQUARE_WAVE, REG0, moduleFrequency);?
? ? Serial.println(moduleFrequency);?
? }?
? if (waveSelect == "TRI") {// 選擇三波
? ? display.println("TRI");?
? ? gen.ApplySignal(TRIANGLE_WAVE, REG0, moduleFrequency); // 更新 AD9833 模塊。
? ? Serial.println(moduleFrequency);?
? }?
? display.setCursor(45, 20);?
? display.println(計(jì)數(shù)器); // 在顯示屏上打印計(jì)數(shù)器信息。
? if (set_frequency_hz == 1 && set_frequency_khz == 0 && set_frequency_mhz == 0 ) {?
? ? display.setCursor(90, 20);?
? ? display.println("Hz"); // 在顯示器上打印 Hz?
? ? display.display(); // 當(dāng)所有設(shè)置更新顯示
? }?
? if (set_frequency_hz == 0 && set_frequency_khz == 1 && set_frequency_mhz == 0 ) {?
? ? display.setCursor(90, 20);?
? ? display.println("Khz");?
? ? 顯示.顯示();// 當(dāng)所有設(shè)置更新顯示
? }?
? if (set_frequency_hz == 0 && set_frequency_khz == 0 && set_frequency_mhz == 1) {?
? ? display.setCursor(90, 20);?
? ? display.println("Mhz");?
? ? 顯示.顯示();
? if (btn_state) {??
? ? display.setTextSize(1);?
? ? display.setCursor(65, 45);?
? ? display.print("輸出開(kāi)啟"); // 將輸出打印到顯示器
? ? display.display();?
? ? display.setTextSize(2);?
? }
? 其他 {?
? ? display.setTextSize(1);?
? ? display.setCursor(65, 45);?
? ? display.print("輸出關(guān)閉"); // 打印輸出到顯示器
? ? display.display();?
? ? display.setTextSize(2);?
? }?
}
評(píng)論
查看更多