#include "AD9833.h"
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <math.h>
#define SCREEN_WIDATA_PINH 128
#define SCREEN_HEIGHT 64
#define SET_FREQUENCY_HZ A2
#define SET_FREQUENCY_KHZ A3
#define SET_FREQUENCY_MHZ A6
#define ENABLE_DISABLE_OUTPUT_PIN A7
#define FNC_PIN 4
#define CLK_PIN 7
#define DATA_PIN 8
#define BTN_PIN 9
#define OUT_SIGNAL_PIN 5
long counter = 1;
int clockPin;
int clockPinState;
long debounce = 220;
unsigned long time_delay_enc = 0;
unsigned long time_delay_hz = 0;
unsigned long time_delay_khz = 0;
unsigned long time_delay_mhz = 0;
unsigned long time_delay_out = 0;
unsigned long moduleFrequency = 1000;
bool btn_state = false;
bool set_frequency_hz = 1;
bool set_frequency_khz = 0;
bool set_frequency_mhz = 0;
String waveSelect = "SIN";
int encoder_btn_count = 0;
const byte sineTable[64] = {
128, 140, 153, 165, 178, 190, 201, 212,
222, 230, 238, 244, 249, 252, 254, 255,
254, 252, 249, 244, 238, 230, 222, 212,
201, 190, 178, 165, 153, 140, 128, 115,
102, 90, 77, 65, 54, 43, 33, 25,
17, 11, 6, 3, 1, 0, 1, 3,
6, 11, 17, 25, 33, 43, 54, 65,
77, 90, 102, 115, 128
};
const byte triangleTable[64] = {
0, 8, 16, 24, 32, 40, 48, 56,
64, 72, 80, 88, 96, 104, 112, 120,
128, 136, 144, 152, 160, 168, 176, 184,
192, 200, 208, 216, 224, 232, 240, 248,
255, 247, 239, 231, 223, 215, 207, 199,
191, 183, 175, 167, 159, 151, 143, 135,
127, 119, 111, 103, 95, 87, 79, 71,
63, 55, 47, 39, 31, 23, 15, 7
};
Adafruit_SSD1306 display(SCREEN_WIDATA_PINH, SCREEN_HEIGHT, &Wire, -1);
AD9833 gen(FNC_PIN);
void setup() {
Serial.begin(115200);
gen.Begin();
pinMode(CLK_PIN, INPUT_PULLUP);
pinMode(DATA_PIN, INPUT_PULLUP);
pinMode(BTN_PIN, INPUT_PULLUP);
clockPinState = digitalRead(CLK_PIN);
pinMode(SET_FREQUENCY_HZ, INPUT);
pinMode(SET_FREQUENCY_KHZ, INPUT);
pinMode(SET_FREQUENCY_MHZ, INPUT);
pinMode(ENABLE_DISABLE_OUTPUT_PIN, INPUT);
pinMode(OUT_SIGNAL_PIN, OUTPUT);
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("SSD1306 allocation failed"));
while (true); // حلقه بینهایت در صورت خطای نمایشگر
}
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(WHITE);
display.setCursor(30, 0);
display.println("AD9833");
display.setCursor(17, 20);
display.println("Function");
display.setCursor(13, 40);
display.println("Generator");
display.display();
delay(2000);
update_display();
}
void loop() {
// ۱. دریافت لاگ از اپلیکیشن اندروید یا پایتون
if (Serial.available() > 0) {
String logCommand = Serial.readStringUntil('\n');
logCommand.trim();
if (logCommand == "MODE:SIN") {
waveSelect = "SIN";
encoder_btn_count = 0;
update_display();
}
else if (logCommand == "MODE:SQR") {
waveSelect = "SQR";
encoder_btn_count = 1;
update_display();
}
else if (logCommand == "MODE:TRI") {
waveSelect = "TRI";
encoder_btn_count = 2;
update_display();
}
else if (logCommand == "OUT:ON") {
btn_state = true;
gen.EnableOutput(btn_state);
update_display();
}
else if (logCommand == "OUT:OFF") {
btn_state = false;
gen.EnableOutput(btn_state);
update_display();
}
else if (logCommand.startsWith("FREQ:")) {
long targetFreq = logCommand.substring(5).toInt();
if (targetFreq > 0 && targetFreq <= 12000000) {
counter = targetFreq;
set_frequency_hz = 1;
set_frequency_khz = 0;
set_frequency_mhz = 0;
update_display();
}
}
// پاسخ به درخواست وضعیت گوشی
else if (logCommand == "GET:STATUS") {
send_status_to_app();
}
}
// ۲. سختافزار: کنترل چرخش انکودر
clockPin = digitalRead(CLK_PIN);
if (clockPin != clockPinState && clockPin == 1) {
if (digitalRead(DATA_PIN) != clockPin) {
counter--;
} else {
counter++;
}
if (counter < 1) counter = 1;
update_display();
}
clockPinState = clockPin;
// ۳. سختافزار: کنترل دکمه فشاری انکودر (تغییر موج)
if (digitalRead(BTN_PIN) == LOW && millis() - time_delay_enc > debounce) {
encoder_btn_count++;
if (encoder_btn_count > 2) encoder_btn_count = 0;
if (encoder_btn_count == 0) waveSelect = "SIN";
if (encoder_btn_count == 1) waveSelect = "SQR";
if (encoder_btn_count == 2) waveSelect = "TRI";
update_display();
time_delay_enc = millis();
}
// ۴. سختافزار: کنترل دکمههای تنظیم فرکانس
if (analogRead(SET_FREQUENCY_HZ) < 100 && millis() - time_delay_hz > debounce) {
set_frequency_hz = 1; set_frequency_khz = 0; set_frequency_mhz = 0;
update_display();
time_delay_hz = millis();
}
if (analogRead(SET_FREQUENCY_KHZ) < 100 && millis() - time_delay_khz > debounce) {
set_frequency_hz = 0; set_frequency_khz = 1; set_frequency_mhz = 0;
update_display();
time_delay_khz = millis();
}
if (analogRead(SET_FREQUENCY_MHZ) < 100 && millis() - time_delay_mhz > debounce) {
set_frequency_hz = 0; set_frequency_khz = 0; set_frequency_mhz = 1;
update_display();
time_delay_mhz = millis();
}
if (analogRead(ENABLE_DISABLE_OUTPUT_PIN) < 100 && millis() - time_delay_out > debounce) {
btn_state = !btn_state; // تغییر وضعیت روشن/خاموش
gen.EnableOutput(btn_state);
update_display();
time_delay_out = millis();
}
// ۵. تولید خروجی آنالوگ نرمافزاری و رسم نمودار زنده در پلاتر
if (btn_state && moduleFrequency <= 5000) {
if (waveSelect == "SQR") {
tone(OUT_SIGNAL_PIN, moduleFrequency);
int sqrValue = (millis() % (1000 / moduleFrequency) < (500 / moduleFrequency)) ? 255 : 0;
Serial.println(sqrValue);
}
else {
static byte index = 0;
int waveValue = 0;
if (waveSelect == "SIN") {
waveValue = sineTable[index];
analogWrite(OUT_SIGNAL_PIN, waveValue);
} else if (waveSelect == "TRI") {
waveValue = triangleTable[index];
analogWrite(OUT_SIGNAL_PIN, waveValue);
}
Serial.println(waveValue);
index = (index + 1) % 64;
delayMicroseconds(1000000 / (moduleFrequency * 64));
}
} else if (!btn_state || moduleFrequency > 5000) {
noTone(OUT_SIGNAL_PIN);
if (!btn_state) analogWrite(OUT_SIGNAL_PIN, 0);
}
}
// تابع ارسال لاگ وضعیت به اندروید (فیدبک سیستم)
void send_status_to_app() {
String outState = btn_state ? "ON" : "OFF";
String statusLog = "UPDATE:" + waveSelect + "," + String(moduleFrequency) + "," + outState;
// این لاگ به اپلیکیشن میگوید که الان مدار روی چه تنظیمی قرار دارد
// Serial.println(statusLog); // در زمان اتصال واقعی به ماژول بلوتوث این خط از کامنت خارج میشود
}
void update_display() {
display.clearDisplay();
display.setTextSize(1);
display.setCursor(10, 0);
display.println("Function Generator");
display.setTextSize(2);
display.setCursor(0, 20);
if (set_frequency_hz == 1) moduleFrequency = counter;
if (set_frequency_khz == 1) moduleFrequency = counter * 1000;
if (set_frequency_mhz == 1) moduleFrequency = counter * 1000000;
if (moduleFrequency > 12000000) {
moduleFrequency = 12000000;
counter = 12;
}
if (waveSelect == "SIN") {
display.println("SIN");
gen.ApplySignal(SINE_WAVE, REG0, moduleFrequency);
}
if (waveSelect == "SQR") {
display.println("SQR");
gen.ApplySignal(SQUARE_WAVE, REG0, moduleFrequency);
}
if (waveSelect == "TRI") {
display.println("TRI");
gen.ApplySignal(TRIANGLE_WAVE, REG0, moduleFrequency);
}
display.setCursor(45, 20);
display.println(counter);
display.setCursor(90, 20);
if (set_frequency_hz == 1) display.println("Hz");
if (set_frequency_khz == 1) display.println("Khz");
if (set_frequency_mhz == 1) display.println("Mhz");
display.setTextSize(1);
display.setCursor(65, 45);
if (btn_state) {
display.print("Output ON");
} else {
display.print("Output OFF");
}
display.display();
// ارسال خودکار فیدبک به اپلیکیشن موبایل با هر بار تغییر سختافزاری
send_status_to_app();
}