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