Если вы решили всерьез заняться радиолюбительством, то вам в вашей мастерской никак не обойтись без генератора сигналов (функционального генератора, Function Generator). Промышленные образцы подобных генераторов могут стоить достаточно дорого, собственными силами генератор сигналов изготовить значительно дешевле.
В этой статье мы рассмотрим создание простейшего генератора сигналов на основе платы Arduino и DDS модуля AD9833, с помощью которого можно будет формировать синусоидальный, прямоугольный и треугольный сигналы с частотой до 12 МГц. Тестировать работу нашего генератора сигналов мы будем с помощью осциллографа, который можно также собрать на основе платы Arduino. Также на нашем сайте вы можете посмотреть проект генератора сигналов синусоидальной и прямоугольной формы только на основе платы Arduino, без использования дополнительных модулей.
Что такое генератор сигналов на основе прямого цифрового синтеза (DDS)
Как следует из названия, генератор сигналов может формировать различные виды сигналов заданной частоты. Аббревиатура DDS (Direct Digital Synthesis) означает прямой цифровой синтез. При этом способе любой сигнал можно сформировать в цифровом виде, а затем преобразовать его в аналоговый вид с помощью цифро-аналогового преобразователя (ЦАП). Чаще всего в современной электронике этот метод используется для формирования синусоидальных сигналов, но с его помощью можно формировать и прямоугольные, и треугольные сигналы, и вообще сигналы любой формы. Поскольку формирование сигналов происходит в цифровой форме в модуле DDS, то можно не только очень быстро переключаться между сигналами различной формы, но и также очень быстро изменять их частоту.
Принцип работы генератора сигналов AD9833
"Сердцем" нашего проекта будет микросхема AD9833, представляющая собой программируемый генератор сигналов и отличающаяся низким энергопотреблением. Микросхема (модуль) AD9833 способна формировать сигналы синусоидальной, прямоугольной и треугольной формы с максимальной частотой до 12 МГц. Таким образом, с помощью программы можно изменять частоту, фазу и форму сигналов на выходе данной микросхемы. Управляется данная микросхема по 3-х проводному интерфейсу SPI, что делает взаимодействие с ней достаточно простым. Функциональная схема микросхемы AD9833 приведена на следующем рисунке.
Принцип работы данной микросхемы достаточно прост. Если мы посмотрим на ее функциональную схему, то мы обнаружим в ее составе аккумулятор фазы (Phase Accumulator), чья работа состоит в сохранении всех возможных значений синусоидальной волны, начиная от 0 to 2π. Также в ее схеме присутствуют SIN ROM, который преобразует информацию о фазе в амплитуду, и 10-битный ЦАП, который принимает данные от SIN ROM и преобразует их в соответствующие аналоговые значения напряжения, которые и подаются на выход микросхемы. На выходе микросхемы присутствует программно управляемый выключатель – его можно включать и выключать. Его роль мы рассмотрим далее в статье.
Основные особенности модуля AD9833:
- цифровое программирование частоты и фазы;
- потребляемая мощность 12.65 мВт при напряжении 3 В;
- диапазон выходных частот от 0 МГц до 12.5 МГц;
- разрешение 28 бит (0.1 Гц при частоте опорного сигнала 25 МГц);
- синусоидальные, треугольные и прямоугольные выходные колебания;
- напряжение питания от 2.3 В до 5.5 В;
- трехпроводной интерфейс SPI;
- расширенный температурный диапазон: от –40°C до +105°C;
- опция пониженного энергопотребления.
Вкратце принцип работы данной микросхемы мы рассмотрели, более подробную информацию об этом вы можете посмотреть в даташите на микросхему AD9833.
Расположение выводов микросхемы AD9833 показано на следующем рисунке.
Назначение выводов микросхемы:
VCC – плюс питания для цифровых и аналоговых цепей генератора.
DGND – цифровая земля.
SDATA – вход данных интерфейса SPI. Передача осуществляется 16-битными словами.
SCLK – вход тактового сигнала SPI. Используется второй режим работы: (CPOL = 1, CPHA = 0).
FSYNC – выбор микросхемы. Перед началом передачи данных должен быть установлен в 0, по завершении в 1.
AGND – аналоговая земля.
OUT – выход генератора.
Необходимые компоненты
- Плата Arduino Nano (купить на AliExpress).
- AD9833 DDS Function Generator (генератор сигналов AD9833) (купить на AliExpress).
- OLED дисплей 128х64 (купить на AliExpress - для данного проекта можно покупать модель с 4-мя контактами поскольку используется его подключение по интерфейсу I2C).
- Инкрементальный энкодер c кнопкой (Rotary Encoder) (купить на AliExpress - не уверен в том, что в нем есть кнопка, но она точно есть в этом лоте - купить на AliExpress № 2, но он продается, к сожалению, только по 5 штук).
- Регулятор напряжения LM7809 (купить на AliExpress).
- Конденсаторы 470 мкФ и 220 мкФ (купить на AliExpress).
- Конденсатор 104 пФ (купить на AliExpress).
- Резистор 10 кОм – 6 шт. (купить на AliExpress).
- Тактильный переключатель (Tactile Switches) – 4 шт. (купить на AliExpress).
- Зажимной контакт (Screw Terminal) 5.04mm (купить на AliExpress).
- Разъем типа "мама" (Female Header) и разъем типа DC Barrel Jack.
- Источник питания с напряжением 12 В.
Реклама: ООО "АЛИБАБА.КОМ (РУ)" ИНН: 7703380158
Схема проекта
Схема генератора сигналов на основе платы Arduino и DDS модуле AD9833 представлена на следующем рисунке.
"Сердцем" схемы является модуль AD9833, который подключен к плате Arduino. Для питания схемы используется регулятор напряжения LM7809 с подключенными к нему развязывающими конденсаторами, которые используются для фильтрации нежелательных шумов, способных оказать негативное воздействие на формируемые сигналы.
Управляет работой всей схемы плата Arduino. Для отображения информации используется OLED дисплей 128х64. Для изменения частоты формируемого сигнала мы используем три переключателя: первый устанавливает частоту в Гц, второй – в кГц, а третий – в МГц. Также мы используем кнопку для включения или отключения выхода схемы. И, наконец, в схеме используется инкрементальный энкодер (rotary encoder) вместе с подключенными к нему подтягивающими резисторами (чтобы правильно работали переключатели). Инкрементальный энкодер используется для изменения частоты, а тактильный переключатель внутри него используется для выбора формы сигнала.
Объяснение программы для Arduino
Полный код программы приведен в конце статьи, здесь же мы кратко рассмотрим его основные фрагменты. Комментарии к коду программы переведены в конце статьи, в этом разделе я их оставил на английском языке.
Для написания кода программы нам прежде всего необходимо скачать необходимые библиотеки по следующим ссылкам:
• Download AD9833 Library by Bill Williams;
• Download SSD1306 OLED Library by Adafruit;
• Download Adafruit GFX library.
Далее в программе мы подключим заголовочные файлы используемых библиотек. Библиотека AD9833.h используется для работы с DDS модулем AD9833, а библиотека math.h – для выполнения ряда математических операций.
1 2 3 4 5 |
#include <AD9833.h> // LIbrary for AD9833 Module #include <Wire.h> // Wire Library for OLED #include <Adafruit_GFX.h> // Support Library for OLED #include <Adafruit_SSD1306.h> // OLED library #include <math.h> // Math Library |
Затем мы объявим все используемые в проекте контакты – для подключения кнопок, переключателей, энкодера, OLED дисплея.
1 2 3 4 5 6 7 8 9 10 |
#define SCREEN_WIDATA_PINH 128 // OLED display Width in pixels #define SCREEN_HEIGHT 64 // OLED display height, in pixels #define SET_FREQUENCY_HZ A2 // Pushbutton To Set Frequency in Hz #define SET_FREQUENCY_KHZ A3 // Pushbutton To Set Frequency in Khz #define SET_FREQUENCY_MHZ A6 // Pushbutton To Set Frequency in Mhz #define ENABLE_DISABLE_OUTPUT_PIN A7 // Pushbutton To Enable/Disable the Output #define FNC_PIN 4 // Fsync Required by the AD9833 Module #define CLK_PIN 8 // Clock Pin of the Encoder #define DATA_PIN 7 // Data Pin of the Encoder #define BTN_PIN 9 // Internal Push Button on the Encoder |
Далее мы объявим все необходимые переменные. В переменной counter мы будем хранить значение, считываемое с инкрементального энкодера. В переменных clockPin и clockPinState будет храниться направление вращения энкодера. Переменная time будет использоваться для борьбы с дребезгом контактов кнопок. В переменной moduleFrequency мы будем хранить рассчитанное значение частоты. Логические переменные set_frequency_hz, set_frequency_Khz и set_frequency_Mhz будут использоваться для хранения состояния модуля. В переменной waveSelect мы будем хранить тип формируемого сигнала, по умолчанию это будет синусоида. Переменная encoder_btn_count будет хранить информацию о нажатиях кнопки на энкодере, с помощью которой будет устанавливаться тип формируемого выходного сигнала.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
int counter = 1; // This Counter value will increase or decrease if when the rotary encoder is turned int clockPin; // Placeholder for pin status used by the rotary encoder int clockPinState; // Placeholder for pin status used by the rotary encoder unsigned long time = 0; // Used for debouncing unsigned long moduleFrequency; // used to set output frequency long debounce = 220; // Debounce delay bool btn_state; // used to enable disable output of the AD98333 Module bool set_frequency_hz = 1; // Defult frequency of the AD9833 Module bool set_frequency_khz; bool set_frequency_mhz; String waveSelect = "SIN"; // Startup waveform of the module int encoder_btn_count = 0; // used to check encoder button press Next, we have our two objects one is for the OLED display and another one is for the AD9833 module. Adafruit_SSD1306 display(SCREEN_WIDATA_PINH, SCREEN_HEIGHT, &Wire, -1); AD9833 gen(FNC_PIN); |
Затем в функции setup() мы инициализируем последовательную связь для целей отладки. Также мы инициализируем модуль AD9833 с помощью функции begin() и зададим режимы для используемых контактов (на ввод или вывод данных). Еще мы сохраняем состояние контакта CLK_PIN (к нему подключен энкодер) в переменной clockPinState.
Дополнительно мы инициализируем OLED дисплей с помощью функции display.begin(). После этого, если на этом этапе не произошло никаких ошибок, мы очищаем экран дисплея и выводим на него приветственное сообщение, после этого делаем задержку на 2 секунды и вызываем функцию update_display().
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
void setup() { Serial.begin(9600); // Enable Serial @9600 baud gen.Begin(); // This MUST be the first command after declaring the AD9833 object pinMode(CLK_PIN, INPUT); // Setting Pins as input pinMode(DATA_PIN, INPUT); pinMode(BTN_PIN, INPUT_PULLUP); clockPinState = digitalRead(CLK_PIN); pinMode(SET_FREQUENCY_HZ, INPUT);// Setting Pins as input pinMode(SET_FREQUENCY_KHZ, INPUT); pinMode(SET_FREQUENCY_MHZ, INPUT); pinMode(ENABLE_DISABLE_OUTPUT_PIN, INPUT); if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64 Serial.println(F("SSD1306 allocation failed")); for (;;); } display.clearDisplay(); // Clear the Screen display.setTextSize(2); // Set text Size display.setTextColor(WHITE); // set LCD Colour display.setCursor(30, 0); // Set Cursor Position display.println("AD9833"); // Print the this Text display.setCursor(17, 20); // Set Cursor Position display.println("Function"); // Print the this Text display.setCursor(13, 40); // Set Cursor Position display.println("Generator"); // Print the this Text display.display(); // Update the Display delay(2000); // Delay of 2 SEC update_display(); // Call update_display Function } |
Далее, в функции loop() мы будем считывать состояние контакта Clock pin инкрементального энкодера и сохранять его в переменной clockPin. Далее мы с помощью оператора if сравниваем предыдущее и текущее состояние данного контакта, также мы сравниваем его состояние с 1. Если это условие выполняется, мы проверяем состояние контакта DATA_PIN. С помощью этой проверки мы выясняем вращается ли ось энкодера по часовой стрелке (инкрементируем counter) или против часовой стрелки (декрементируем counter). Также мы обновляем clockPinState до текущего состояния.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
void loop() { clockPin = digitalRead(CLK_PIN); if (clockPin != clockPinState && clockPin == 1) { if (digitalRead(DATA_PIN) != clockPin) { counter --; } else { counter ++;// Encoder is rotating CW so increment } if (counter < 1 ) counter = 1; Serial.println(counter); update_display(); } |
Далее в коде программы мы должны обнаруживать нажатия кнопок. Мы будем обнаруживать нажатие кнопки, находящейся на энкодере, с помощью условия (digitalRead(BTN_PIN) == LOW && millis() - time > denounce), в этом условии мы сначала проверяем равно ли состояние контакта кнопки значению LOW (то есть нажата ли она). Далее в этом условии мы проверяем закончилась ли задержка, необходимая для борьбы с дребезгом контактов кнопки. Если оба условия выполняются, мы регистрируем успешное нажатие кнопки и инкрементируем значение переменной encoder_btn_count. Если значение переменной encoder_btn_count больше 2, то мы снова его сбрасываем в 0. Значение переменной encoder_btn_count определяет вид формируемого сигнала: 0 – синусоидальный сигнал, 1 – прямоугольный, 2 – треугольный. После выбора формы сигнала мы обновляем состояние дисплея с помощью функции update_display() и обновляем состояние переменной time.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
//If we detect a LOW signal, the button is pressed if ( digitalRead(BTN_PIN) == LOW && millis() - time > debounce) { encoder_btn_count++; // Increment the values if (encoder_btn_count > 2) // if value is greater than 2 reset it to 0 { encoder_btn_count = 0; } if (encoder_btn_count == 0) { // if the value is 0 sine wave is selected waveSelect = "SIN"; // update the string variable with sin value update_display(); // update the display } if (encoder_btn_count == 1) { // if the value is 1 square wave is selected waveSelect = "SQR"; // update the string variable with SQR value update_display(); // update the display } if (encoder_btn_count == 2) { // if the value is 1 Triangular wave is selected waveSelect = "TRI"; // update the string variable with TRI value update_display();// update the display } time = millis(); // update the time variable } |
Затем мы в коде программы мы устанавливаем для всех кнопок задержку, необходимую для борьбы с дребезгом их контактов (debounce delay). Поскольку кнопки подключены у нас к аналоговым контактам платы Arduino, мы будем считывать их состояние с помощью функции analogRead. Если считанное аналоговое значение (с выхода АЦП) меньше 30, мы регистрируем нажатие кнопки. Также мы выжидаем задержку 200 мс чтобы удостовериться в том, что это было действительно нажатие кнопки, а не влияние посторонних шумов. Если условие выполняется, то устанавливаем значения соответствующих логических переменных чтобы указать значения Гц, кГц и МГц для нашего генератора сигналов. Также мы обновляем состояние дисплея и переменной time. Эти операции мы производим для всех четырех кнопок, подключенных к плате Arduino.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
if (analogRead(SET_FREQUENCY_HZ) < 30 && millis() - time > debounce) { set_frequency_hz = 1; //update boolean values set_frequency_khz = 0; set_frequency_mhz = 0; update_display();// update the display time = millis();// update the time variable } if (analogRead(SET_FREQUENCY_KHZ) < 30 && millis() - time > debounce){ set_frequency_hz = 0; //update boolean values set_frequency_khz = 1; set_frequency_mhz = 0; moduleFrequency = counter * 1000; update_display();// update the display time = millis();// update the time variable } if (analogRead(SET_FREQUENCY_MHZ) < 30 && millis() - time > debounce ) { // check analog pin with debounce delay set_frequency_hz = 0; //update boolean values set_frequency_khz = 0; set_frequency_mhz = 1; moduleFrequency = counter * 1000000; update_display();// update the display time = millis();// update the time variable } if (analogRead(ENABLE_DISABLE_OUTPUT_PIN) < 30 && millis() - time > debounce ) {// check analog pin with debounce delay btn_state = ! btn_state; // Invert the button state gen.EnableOutput(btn_state); // Enable / Disable output of the function generator depending on button state update_display();// update the display time = millis();// update the time variable } } |
Теперь рассмотрим нашу функцию update_display(). Дело в том, что мы не сможем просто обновить небольшой кусок экрана дисплея, нам необходимо полностью перерисовать его. Поэтому внутри этой функции мы сначала очистим экран дисплея, затем мы установим размер текста, установим позицию дисплея и выполним команду display.println("Function Function"). Затем мы установим размер текста равный 2 и установим курсор в позицию (0,20) с помощью функции display.setCursor(0, 20).
1 2 3 4 5 6 |
display.clearDisplay(); // FIrst clear the display display.setTextSize(1); //set text Size display.setCursor(10, 0); // Set cursor position display.println("Function Generator"); //print the text display.setTextSize(2);//set text Size display.setCursor(0, 20);//Set cursor position |
Далее мы проверим состояние логических переменных, отвечающих за значение частоты, и обновим значение частоты в переменной moduleFrequency. Затем мы проверим состояние переменной waveSelect и определим какой вид сигнала необходимо формировать.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
if (set_frequency_hz == 1 && set_frequency_khz == 0 && set_frequency_mhz == 0 ) { // check if button for setting the frequency in Hz is pressed moduleFrequency = counter; //update the moduleFrequency variable with current counter value } if (set_frequency_hz == 0 && set_frequency_khz == 1 && set_frequency_mhz == 0 ) { // check if button for setting the frequency in KHz is pressed moduleFrequency = counter * 1000;//update the moduleFrequency variable with current counter value but we multiply 1000 to set it on KHZ } if (set_frequency_hz == 0 && set_frequency_khz == 0 && set_frequency_mhz == 1) { // check if button for setting the frequency in MHz is pressed moduleFrequency = counter * 1000000; if (moduleFrequency > 12000000) { moduleFrequency = 12000000; // do not let the frequency to be grater that 12Mhz counter = 12; } } if (waveSelect == "SIN") { // Sine wave is selected display.println("SIN"); gen.ApplySignal(SINE_WAVE, REG0, moduleFrequency); Serial.println(moduleFrequency); } if (waveSelect == "SQR") {// Sqr wave is selected display.println("SQR"); gen.ApplySignal(SQUARE_WAVE, REG0, moduleFrequency); Serial.println(moduleFrequency); } if (waveSelect == "TRI" ) {// Tri wave is selected display.println("TRI"); gen.ApplySignal(TRIANGLE_WAVE, REG0, moduleFrequency); // update the AD9833 module. Serial.println(moduleFrequency); } |
Затем мы снова установим позицию курсора и отобразим на экране дисплея обновленное значение переменной counter. Снова проверим состояние логических переменных, отвечающих за значение частоты, чтобы обновить значение частоты на кране дисплея.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
display.setCursor(45, 20); display.println(counter); // print the counter information on the display. if (set_frequency_hz == 1 && set_frequency_khz == 0 && set_frequency_mhz == 0 ) { display.setCursor(90, 20); display.println("Hz"); // print Hz on the display display.display(); // when all set update the display } if (set_frequency_hz == 0 && set_frequency_khz == 1 && set_frequency_mhz == 0 ) { display.setCursor(90, 20); display.println("Khz"); display.display(); // when all set update the display } if (set_frequency_hz == 0 && set_frequency_khz == 0 && set_frequency_mhz == 1) { display.setCursor(90, 20); display.println("Mhz"); display.display(); // when all set update the display } |
После этого мы проверим состояние переменной btn_state, показывающей нажата кнопка или нет, чтобы высветить на экране OLED дисплея сообщение "Output ON" или "Output OFF".
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
if (btn_state) { display.setTextSize(1); display.setCursor(65, 45); display.print("Output ON"); // print output on to the display display.display(); display.setTextSize(2); } else { display.setTextSize(1); display.setCursor(65, 45); display.print("Output OFF"); // print output off to the display display.display(); display.setTextSize(2); } |
Тестирование работы генератора сигналов
Чтобы проверить работу сконструированного нами генератора сигналов, мы подали питание 12V с адаптера на разъем DC barrel jack нашей конструкции, а также подключили осциллограф к выходу схемы. Также осциллограф мы подключили к компьютеру чтобы визуализировать получаемые нами сигналы.
После этого мы с помощью инкрементального энкодера установили частоту равную 5 кГц и протестировали формируемый сигнал синусоидальной формы.
После этого мы проверили формирование сигнала треугольной формы, также на частоте 5 кГц.
И, наконец, мы проверили формирование сигнала прямоугольной формы.
Также мы пробовали изменять значение частоты и выяснили, что и на других частотах схема работает превосходно.
Возможные улучшения проекта
Для улучшения внешнего вида проекта и уменьшения нежелательных связей между элементами схемы желательно конструировать ее на печатной плате. Также для повышения качества формируемого сигнала на высоких частотах необходимо использовать коннектор BNC типа. Амплитуда сигнала на выходе нашего генератора сигналов имеет небольшую величину, для ее увеличения целесообразно использовать операционный усилитель. Для регулировки амплитуды сигнала на выходе генератора неплохо бы использовать потенциометр. Также желателен переключатель, с помощью которого можно было бы управлять сдвигом сигнала. Также можно заменить OLED дисплей на какой-нибудь более удобный вид дисплея, чтобы не перерисовывать заново весь экран при каждом обновлении данных – из-за этого, в частности, существенно усложнился код нашей программы.
Исходный код программы (скетча)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 |
#include <AD9833.h> // библиотека для работы с модулем AD9833 #include <Wire.h> // библиотека для протокола I2C, в нашем случае она необходима для работы с OLED дисплеем #include <Adafruit_GFX.h> // Support Library for OLED #include <Adafruit_SSD1306.h> // OLED library #include <math.h> // библиотека математических функций #define SCREEN_WIDATA_PINH 128 // ширина OLED дисплея в пикселах #define SCREEN_HEIGHT 64 // высота OLED дисплея в пикселах #define SET_FREQUENCY_HZ A2 // кнопка для установки частоты в Гц (Hz) #define SET_FREQUENCY_KHZ A3 // кнопка для установки частоты в кГц (Khz) #define SET_FREQUENCY_MHZ A6 // кнопка для установки частоты в МГц (Mhz) #define ENABLE_DISABLE_OUTPUT_PIN A7 // кнопка для включения/выключения выхода #define FNC_PIN 4 // Fsync Required by the AD9833 Module #define CLK_PIN 8 // Clock Pin (тактовый контакт) энкодера #define DATA_PIN 7 // Data Pin (контакт данных) энкодера #define BTN_PIN 9 // внутренняя кнопка на энкодере int counter = 1; // этот счет будет увеличиваться и уменьшаться в зависимости от вращения энкодера int clockPin; // Placeholder por pin status used by the rotary encoder int clockPinState; // Placeholder por pin status used by the rotary encoder unsigned long time = 0; // переменная для борьбы с дребезгом контактов unsigned long moduleFrequency; // переменная для установки выходной частоты long debounce = 220; // задержка для борьбы с дребезгом контактов bool btn_state; // включение/выключение выхода модуля AD98333 bool set_frequency_hz = 1; // частота по умолчанию для модуля AD9833 bool set_frequency_khz; bool set_frequency_mhz; String waveSelect = "SIN"; // форма выходного сигнала (по умолчанию синусоидальная) int encoder_btn_count = 0; // переменная для проверки нажатия кнопки энкодера Adafruit_SSD1306 display(SCREEN_WIDATA_PINH, SCREEN_HEIGHT, &Wire, -1); AD9833 gen(FNC_PIN); void setup() { Serial.begin(9600); gen.Begin(); // это команда должна быть первой после того как вы объявили объект AD9833 pinMode(CLK_PIN, INPUT); pinMode(DATA_PIN, INPUT); 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); if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64 Serial.println(F("SSD1306 allocation failed")); for (;;); } 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); // задержка 2 сек. update_display(); // Call update_display Function } void loop() { clockPin = digitalRead(CLK_PIN); if (clockPin != clockPinState && clockPin == 1) { if (digitalRead(DATA_PIN) != clockPin) { counter --; // энкодер вращается против часовой стрелки } else { counter ++;// энкодер вращается по часовой стрелке } if (counter < 1 ) counter = 1; Serial.println(counter); update_display(); } clockPinState = clockPin; // запоминаем последнее состояние CLK_PIN //если мы обнаруживаем сигнал 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) { // if the value is 0 sine wave is selected waveSelect = "SIN"; // сигнал синусоидальной формы update_display(); // update the display } if (encoder_btn_count == 1) { // if the value is 1 square wave is selected waveSelect = "SQR"; // сигнал прямоугольной формы update_display(); // update the display } if (encoder_btn_count == 2) { // if the value is 1 Triangular wave is selected waveSelect = "TRI"; // сигнал треугольной формы update_display();// update the display } time = millis(); // обновляем переменную time } // проверяем нажатие кнопки с помощью функции analogread // вносим в условие проверки нажатия кнопки проверку задержки для борьбы с дребезгом контактов if (analogRead(SET_FREQUENCY_HZ) < 30 && millis() - time > debounce) { // check analogpin with debounce delay //update boolean values set_frequency_hz = 1; set_frequency_khz = 0; set_frequency_mhz = 0; update_display();// update the display time = millis();// update the time variable } if (analogRead(SET_FREQUENCY_KHZ) < 30 && millis() - time > debounce) { // check analogpin with debounce delay //update boolean values set_frequency_hz = 0; set_frequency_khz = 1; set_frequency_mhz = 0; moduleFrequency = counter * 1000; update_display();// update the display time = millis();// update the time variable } if (analogRead(SET_FREQUENCY_MHZ) < 30 && millis() - time > debounce ) { // check analogpin with debounce delay //update boolean values set_frequency_hz = 0; set_frequency_khz = 0; set_frequency_mhz = 1; moduleFrequency = counter * 1000000; update_display();// update the display time = millis();// update the time variable } if (analogRead(ENABLE_DISABLE_OUTPUT_PIN) < 30 && millis() - time > debounce ) {// check analogpin with debounce delay btn_state = ! btn_state; // инвертируем состояние кнопки gen.EnableOutput(btn_state); // включаем (Enable) / отключаем (Disable) выход генератора сигнала в зависимости от состояния кнопки update_display();// update the display time = millis();// update the time variable } } 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 && set_frequency_khz == 0 && set_frequency_mhz == 0 ) { // проверяем нажата ли кнопка для установки частоты в Герцах moduleFrequency = counter; // записываем в переменную moduleFrequency (она определяет частоту на выходу нашего генератора сигналов) значение counter } if (set_frequency_hz == 0 && set_frequency_khz == 1 && set_frequency_mhz == 0 ) { // проверяем нажата ли кнопка для установки частоты в килогерцах moduleFrequency = counter * 1000;//обновляем частоту на выходе генератора, для этого умножаем значение counter на 1000 поскольку нажата кнопка для установки частоты в килогерцах } if (set_frequency_hz == 0 && set_frequency_khz == 0 && set_frequency_mhz == 1) { // проверяем нажата ли кнопка для установки частоты в мегагерцах moduleFrequency = counter * 1000000; if (moduleFrequency > 12000000) { moduleFrequency = 12000000; // не позволяем стать частоте больше 12 МГц counter = 12; } } if (waveSelect == "SIN") { // выбрана синусоидальная волна display.println("SIN"); gen.ApplySignal(SINE_WAVE, REG0, moduleFrequency); Serial.println(moduleFrequency); } if (waveSelect == "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(counter); // print the counter information on teh display. if (set_frequency_hz == 1 && set_frequency_khz == 0 && set_frequency_mhz == 0 ) { display.setCursor(90, 20); display.println("Hz"); // печатаем Hz на дисплее display.display(); // when all set update the display } if (set_frequency_hz == 0 && set_frequency_khz == 1 && set_frequency_mhz == 0 ) { display.setCursor(90, 20); display.println("Khz"); display.display(); // when all set update the display } if (set_frequency_hz == 0 && set_frequency_khz == 0 && set_frequency_mhz == 1) { display.setCursor(90, 20); display.println("Mhz"); display.display(); // when all set update the display } if (btn_state) { display.setTextSize(1); display.setCursor(65, 45); display.print("Output ON"); // print output on to the display display.display(); display.setTextSize(2); } else { display.setTextSize(1); display.setCursor(65, 45); display.print("Output OFF"); // print output off to the display display.display(); display.setTextSize(2); } } |
1) При компиляции выдается ошибка "class AD9833' has no member named 'EnableOutput". Что нужно исправить?
2) В строке 32 скетча написано gen.Begin(); Begin - с заглавной буквы. Это правильно? Или нужно - со строчной?
https://github.com/Billwilliams1952/AD9833-Library-Arduino/blob/master/AD9833.h - скорее всего вместо EnableOutput надо использовать outputEnabled (опечатка) - такой элемент в классе AD9833 есть. А метод Begin в этом классе действительно написан с большой буквы, поэтому здесь опечатки нет
Возможно ли добавить регулировку скважности?
В библиотеке AD9833.h я прямых способов для этого не нашел, но если изменить ее код, то можно.
Для регулировки скважности можно использовать прямой вывод(без развязывающего конденсатора(только надо смотреть в даташит, какое выходное сопротивление у м/сх)), и компаратор , типа lm393, второй вход компаратора или через резистор переменный или , если надо программно регулировать, к примеру, через батарею резисторов в цепь коллектора и транзисторы с общим эмиттером с базами , подключенными к портам МК
Да, спасибо за конструктивный комментарий для нашего сайта
Спасибо за подробные описание текста программы
но собрать для прошивки не получается у меня с помощью Arduino IDE
как можно скачать прошивку ? Спасибо!
Анатолий, код программы приведен в статье, а свой комментарий вы оставили только с целью поставить спамную ссылку в своем профиле. Спамить ведь не хорошо
Да уж, я пока понял, что sck и data перепутаны - вспотел. Осликом увидел, что на data ровные импульсы. Но там еще какая-то лажа в коде. После 32кГц частота прыгает на 12,5Мгц
К сожалению, мы лично не собирали данный проект, поэтому возможные опечатки в коде программы мы не исправили. Но у статьи уже более 11 тыс. просмотров и больше никто на данный скачок частоты не жаловался. Может быть, данную проблему легко исправить. При подобных ошибках я всегда советую идти от простого к сложному - то есть начинать отладку программы с минимального фрагмента кода и потом постепенно добавлять в него новые фрагменты. Так легче всего найти ошибку в коде
Можно ли на основе этого генератора собрать импедансметр с этими же частотными возможностями?
К сожалению, мало знаком с особенностями работы импедансметров, поэтому не могу подсказать ничего конкретного по вашему вопросу
Данную схему копируют с сайта на сайт, но ни кто не удосужился ее проверить. вход SDATA модуля AD9833 необходимо подключать к выходу D11 Nano, а CLK (SCLK, SCK) модуля AD9833 к D13 Nano. На схеме все наоборот. В Данной схеме можно использовать OLED 128X32 c небольшой правкой кода. Но в целом решение не очень удачное и удобное. Что бы добраться до 923kHz крутить энкодер рука устанет. Да и использование трех кнопок для выбора диапазона тоже не очень разумное решение. Вообщем надо полностью переделывать код и кое-что в схемотехнике. Есть мысли поставить на выход усилитель и добавить управляющий таймер с управлением с этого-же энкодера
Спасибо за такой подробный комментарий. Лично у меня не было возможности собрать данный проект, поэтому я его только перевел с сайта индийских энтузиастов (ссылка на источник имеется). Судя по количеству просмотров, которые набирают статьи на моем сайте, посвященные созданию различных генераторов сигналов, мои статьи далеко не самые худшие среди аналогичных статей в рунете. Я просто вижу, что на других сайтах иногда ленятся и не переводят некоторые части статьи с первоисточника, например, объяснение кода программы мало кто переводит. Я же стараюсь переводить всю статью полностью.
Но опечатки на сайте индийских энтузиастов все таки иногда имеются (хотя проекты у них на сайте реально хорошие, одни из лучших в сети). Поэтому хочется сказать спасибо и вам, и другим пользователям нашего сайта, которые указывают на эти опечатки
Вы проделали большую работу. Спасибо
Спасибо что оценили мой труд. Буду признателен если потом вы отпишитесь здесь о том, удалось ли вам реализовать все те усовершенствования в проекте, которые вы хотите в нем сделать
Как на этой микросхеме реализовать SWEEP MODE?
К сожалению не знаю, не занимался данным вопросом