Делаем генератор частоты на базе Ардуино микроконтроллера

Генератор частоты на Ардуино – прибор, который занимается преобразованием электрической энергии источника постоянного тока в энергию, не поддающуюся затуханию, для расчета и частоты и образованной формы электрических колебаний. Приспособление приобрело популярность среди начинающих создателей электронных устройств, разработчиков компьютерных девайсов и радиоприемников. Выходное напряжение получается из 3 форм: прямоугольник, синусоида и пила.

Источник электрического тока передает возбужденные волны контуру колебаний, поэтому образуются волновые движения. Они постепенно затухают, потому что сопротивление поглощает энергетическую волну. Во избежание затухания в контур подается дополнительная энергия для восполнения потерянной. Такая процедура проводится с использованием положительной обратной связи. С помощью связи в контур поступает частица сигнала, совпадающего с колебанием обратной связи.

Такой прибор, как генератор сигналов на Ардуино, легко сделать в домашних условиях. Основа конструкции – микроконтроллер Arduino.

Где применяется генератор частоты на Ардуино

Роль частотного генератора в мире электроники – настройка и определение технической характеристики тактов сигнальных волн. Другое применение – для регулировки узлов и элементов приемников, передающих радио-колебания. Кроме того, генератор импульсов, построенный на Ардуино, используют как модулятор или источник питания для устройств, которые обладают измерительными свойствами.

Частотные измерители могут изменять выходные сигналы с определенным скачком.

Поэтому устройства с такими свойствами играют немаловажную роль в конструировании электронных приборов. Перечислим другие значительные функции Ардуино-генератора:

  1. Поиск расположения мест, где можно проложить кабели и трубопроводы. Причем поисковая работа проводится на дальних расстояниях.
  2. Поисковые работы для находки мультичастотной технологии с помощью процесса излучения сразу нескольких частотных волн.
  3. Создание аналоговых синтезаторов. Синтезирующие устройства применяются для сборки электронных устройств без использования множества блоков. Все сигнальные волны мелькают между разными блоками строго по стандартам.

Комплектующие

Для создания генератора прямоугольных импульсов на Ардуино понадобятся следующие компоненты:

  • Arduino Uno R3 в антистатической упаковке;
  • шилд DFRobot LCD Keypad Shield;
  • модуль генератора сигналов AD9850 DDS;
  • проводы для присоединения частей конструкции – 8 штук;
  • USB-провод или кабель.

Также во втором случае можно собрать устройство на основе AD9850 DDS модуля и 1,8-дюймового TFT-дисплея (контроллер ST7735).

В таком случае схема соединения будет выглядеть так:

Код

Листинг программы для проекта «генератор импульсов с регулировкой частоты на Ардуино» для первого нашего варианта:

#include <LiquidCrystal.h> // Подключение библиотек
LiquidCrystal lcd(8, 9, 4, 5, 6, 7); // Инициализация LCD с указанием пинов
float freq      = 10000; // Оглашение переменных -- Частота
float bigStep = 1000; // Шаг изменения частоты при нажатии вверх/вниз
float littleStep = 10; // Шаг изменения частоты при нажатии вправо/влево
int lcd_key     = 0;
int adc_key_in  = 0;
#define btnRIGHT  0 // Создание директив для кнопок
#define btnUP     1
#define btnDOWN   2
#define btnLEFT   3
#define btnSELECT 4
#define btnNONE   5
#define W_CLK 15 // Пин A1 Arduino - подключен к CLK
#define FQ_UD 16 // Пин A2 Arduino - подключен к FQ (FU)
#define DATA 17  // Пин A3 Arduino - подключен к DATA
#define RESET 18 // Пин A4 Arduino - подключен к RST
#define pulseHigh(pin) {digitalWrite(pin, HIGH); digitalWrite(pin, LOW); }
//------------------------------------------------------
int read_LCD_buttons(){ // Функция считывания нажатия кнопок
adc_key_in = analogRead(0); 
if (adc_key_in > 1000) return btnNONE;
if (adc_key_in < 50)   return btnRIGHT;
if (adc_key_in < 150)  return btnUP;
if (adc_key_in < 315)  return btnDOWN;
if (adc_key_in < 600)  return btnLEFT;
if (adc_key_in < 850)  return btnSELECT;
return btnNONE;}
//-------------------------------------------------------
void tfr_byte(byte data){ // Функция побитной отправки байта
for (int i=0; i<8; i++, data>>=1) { // данных в модуль генератора
  digitalWrite(DATA, data & 0x01);
  pulseHigh(W_CLK);}} // Подача импульса на CLK после каждого бита
//-------------------------------------------------------
void sendFrequency(double frequency) { // Преобразование и отправка
  int32_t freq = frequency * 4294967295/125000000;  // значения частоты
  for (int b=0; b<4; b++, freq>>=8) {tfr_byte(freq & 0xFF);}
  tfr_byte(0x000);   // Отправка завершательного контрольного байта
  pulseHigh(FQ_UD);}  // Обновление частоты генератора
//----------------------------------------------------
void setup()
{
lcd.begin(16, 2); // Старт библиотеки. Указанием количества символов и строк
pinMode(FQ_UD, OUTPUT);
pinMode(W_CLK, OUTPUT);
pinMode(DATA, OUTPUT);
pinMode(RESET, OUTPUT);
pulseHigh(RESET); // Отправка импульсов для запуска модуля генератора
pulseHigh(W_CLK);
pulseHigh(FQ_UD);
}
//----------------------------------------------------
void loop()
{
lcd.setCursor(0,0); // Далее вывод текущего значения частоты
lcd.print("Freq: ");
lcd.setCursor(6,0);
lcd.print("           ");
lcd.setCursor(6,0);
if (freq<1000){lcd.print(freq);
lcd.print("Hz");}
if ((freq>=1000)&&(freq<1000000)){lcd.print(freq / 1000);
lcd.print("kHz");}
if ((freq>=1000000)&&(freq<50000000)){lcd.print(freq / 1000000);
lcd.print("MHz");}
lcd.setCursor(0,1);
lcd.print("   Genie v1.0");
if (freq<100){bigStep = 10; // Определение шага грубой и точной
littleStep = 1;} // настройки в зависимости от частоты
if ((freq>=100)&&(freq<1000)){bigStep = 100;
littleStep = 1;}
if ((freq>=1000)&&(freq<10000)){bigStep = 1000;
littleStep = 10;}
if ((freq>=10000)&&(freq<100000)){bigStep = 10000;
littleStep = 100;}
if ((freq>=100000)&&(freq<1000000)){bigStep = 100000;
littleStep = 1000;}
if ((freq>=1000000)&&(freq<10000000)){bigStep = 1000000;
littleStep = 10000;}
if ((freq>=10000000)&&(freq<40000000)){bigStep = 10000000;
littleStep = 100000;}
lcd_key = read_LCD_buttons();  // Считывание клавиш
switch (lcd_key) // Далее обработка нажатий клавиш      
{
   case btnRIGHT:
     {freq += littleStep;
     break;}
   case btnLEFT:
     {freq -= littleStep;
     break;}
   case btnUP:
     {freq += bigStep;
     break;}
   case btnDOWN:
     {if (freq == bigStep){freq -= (bigStep/10);}
     else {freq -= bigStep;}
     break;}
   case btnSELECT:
     {break;}
   case btnNONE:
     {break;}
}
if (freq<1) freq=1; // Ограничение значений частоты
if (freq>40000000) freq=40000000;
sendFrequency(freq);  // Вызов функции отправки частоты
delay (200); // Пауза 200 мс
}

Во втором случае программный код будет таким (позаимствовано на просторах интернета):

/* Based on AD9851 code from Andrew Smallbone - modified for AD9850
   http://www.rocketnumbernine.com/2011/10/25/programming-the-ad9851-dds-synthesizer 
 */

#include <Adafruit_GFX.h>       // Core graphics library https://github.com/adafruit/Adafruit-GFX-Library
#include <Adafruit_ST7735.h>    // Hardware-specific library https://github.com/adafruit/Adafruit-ST7735-Library
#include <SPI.h>

#include <Rotary.h>            //  Rotary encoder: https://github.com/brianlow/Rotary 

int TFT_LED = 9;
#define TFT_SCLK 13             // 1.8" TFT Display.
#define TFT_MOSI 11             //
#define TFT_CS   10
#define TFT_RST  A1  
#define TFT_DC   A0

Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS,  TFT_DC, TFT_RST);

#define AD9850_CLOCK 125000000         // Module crystal frequency. Tweak here for accuracy.

#define W_CLK 8                        // AD9850 Module pins.    
#define FQ_UD 7       
#define DATA  6       
#define RESET 5     

#define stepPin1 A3                    // Set 'Step' rotary encoder pins
#define stepPin2 A2
int forceHzStep = A4;                  // 'Step' rotary encoder's push button - Set 1 Hz steps.
int forcekHz = 4;                     // Interrupt-driven encoder's push button - force 1kHz freq.

Rotary i = Rotary(stepPin1, stepPin2); // Rotart encoder for setting increment.
Rotary r = Rotary(2, 3);               // Rotary encoder for frequency connects to interrupt pins

long unsigned int freq = 1000;         // Set initial frequency.
long unsigned int freqOld = freq;

long int timer;


char* stepText[11] = {"  1 Hz", " 10 Hz", " 50 Hz", "100 Hz", "500 Hz", "  1 kHz", "2.5 kHz",
                     "  5 kHz", " 10 kHz", "100 kHz", "500 kHz"};

int stepPointer = 0; 
unsigned long  incr = 0;
String units = stepText[stepPointer];

#define pulseHigh(pin) {digitalWrite(pin, HIGH); digitalWrite(pin, LOW); }


 // transfers a byte, a bit at a time, LSB first to the 9850 via serial DATA line
void tfr_byte(byte data) {
  for (int i = 0; i < 8; i++, data >>= 1) {
    digitalWrite(DATA, data & 0x01);
    pulseHigh(W_CLK);   //after each bit sent, CLK is pulsed high
  }
}

void sendFrequency(double frequency) {
  int32_t freq1 = frequency * 4294967295/AD9850_CLOCK;  // note 125 MHz clock on 9850
  for (int b = 0; b < 4; b++, freq1 >>= 8) {
    tfr_byte(freq1 & 0xFF);
  }
  tfr_byte(0x000);                     // Final control byte, all 0 for 9850 chip
  pulseHigh(FQ_UD);                    // Done!  Should see output
}


void setup() {

  pinMode(2, INPUT_PULLUP);            // Pins for interrupt-driven rotary encoder and push buttons
  pinMode(3, INPUT_PULLUP);
  pinMode(forceHzStep, INPUT_PULLUP);   
  pinMode(forcekHz, INPUT_PULLUP);  
  
  pinMode(FQ_UD, OUTPUT);              // Configure pins for output to AD9850 module.
  pinMode(W_CLK, OUTPUT);
  pinMode(DATA, OUTPUT);
  pinMode(RESET, OUTPUT);
  
  pinMode(TFT_RST, OUTPUT);            // Configure pins for output to TFT display.
  pinMode(TFT_DC, OUTPUT);
  pinMode(TFT_LED, OUTPUT);
 
  analogWrite(TFT_LED, 255);           // Adjust backlight brightness.
 
 // Configure interrupt and enable for rotary encoder.
  PCICR |= (1 << PCIE2);
  PCMSK2 |= (1 << PCINT18) | (1 << PCINT19);
  sei();
  

  tft.initR(INITR_BLACKTAB);           // initialize a ST7735S chip, black tab
  
  tft.setRotation(3); 
   tft.setTextWrap(false);              // Allow text to run off right edge
  tft.fillScreen(ST7735_BLACK);
   
  tft.setCursor(15, tft.height() -20);
  tft.setTextSize(1);
  tft.drawFastHLine(0, tft.height() - 23, tft.width()-10, ST7735_BLUE);
  tft.setTextColor(ST7735_BLUE);
  tft.println("AD9850 1 Hz to 5 MHz ");
  tft.print("   sinewave generator");
  
  // Initialise the AD9850 module. 
  pulseHigh(RESET);
  pulseHigh(W_CLK);
  pulseHigh(FQ_UD);    // this pulse enables serial mode - Datasheet page 12 figure 10  
  
  updateDisplay();       // Update the TFT display.
}

void getStep() {
  switch(stepPointer) {
    case 0:  incr = 1; break;
    case 1:  incr = 10; break;
    case 2:  incr = 50; break;
    case 3:  incr = 100; break;
    case 4:  incr = 500; break;
    case 5:  incr = 1000; break;
    case 6:  incr = 2500; break;
    case 7:  incr = 5000; break;
    case 8:  incr = 10000; break;
    case 9:  incr = 100000; break;
    case 10: incr = 500000; break;
  } 
}


void updateDisplay() {
  getStep();                          // 
  units = stepText[stepPointer];
  
  tft.fillRect(0, 15, 160, 20, ST7735_BLACK);
  
  tft.setTextColor(ST7735_YELLOW);
  tft.setCursor(10, 20);
  tft.setTextSize(1);
  tft.print("Step: ");
  tft.setTextSize(2);
  tft.setCursor(60, 15);
  tft.print(units);
  
  tft.fillRect(0, 40, 160, 60, ST7735_BLACK);
  tft.setTextColor(ST7735_GREEN);
  tft.setTextSize(2);  
  if (freq < 1000) {
    tft.setCursor(78, 50);
    if (freq < 1000) tft.print(" ");
    if (freq < 100) tft.print(" ");
    tft.print(freq); 
    tft.setCursor(58, 75);
    tft.print(" Hz");
  } else
  if (freq < 1000000) {
   tft.setCursor(40, 50);
   if (freq < 10000) tft.print(" ");
   tft.print((float)freq/1000, 3); 
   tft.setCursor(58, 75);
   tft.print(" kHz");
  }  else {
   format(freq);
   tft.setCursor(58, 75);
   tft.print(" MHz");
  }
}

void format(long value) {
  int M = (value/1000000);
  int T100 = ((value/100000)%10);
  int T10 = ((value/10000)%10);
  int T1 = ((value/1000)%10);
  int U100 = ((value/100)%10);
  int U10 = ((value/10)%10);
  int U1 = ((value/1)%10);
  tft.setCursor(25, 50);
  tft.print(M);tft.print(".");tft.print(T100);tft.print(T10);tft.print(T1);
  tft.print(",");tft.print(U100);tft.print(U10);tft.print(U1);
} 

void loop() {
  // Check 'Step' rotary encoder.
  unsigned char result = i.process();
  if (result) {
    if (result == DIR_CW)  {if (stepPointer < 10) stepPointer++;}
    if (result == DIR_CCW) {if (stepPointer > 0) stepPointer--;} 
    updateDisplay();
  }
  
  if (digitalRead(forceHzStep) == LOW) {
    stepPointer = 0;
    updateDisplay();
    delay(50);
  }
  
  if (digitalRead(forcekHz) == LOW) {
    freq = 1000;
    sendFrequency(freq);
    updateDisplay();
    delay(350);
  }
  if (freqOld != freq) {
    sendFrequency(freq);
    updateDisplay();
    freqOld = freq;
  }
}

ISR(PCINT2_vect) {
  unsigned char result = r.process();
  if (result) {
    if (result == DIR_CW) {
      if ((freq + incr) <= 10000000) freq += incr;
    } else {
      if ((freq - incr) >= 10) freq -= incr;
    }
    if (freq <= 10)  freq = 10;
    if (freq >=10000000) freq = 10000000;
  }
}

Для первого варианта вам нужно не забыть про библиотеку LiquidCrystal, которую можно найти на сайте производителя по этой ссылке.

Для второго варианта нужны следующие технические спецификации:

AD9850

AD9850 DDS модуль генератор

И библиотеки для Arduino:

Adafruit_GFX Library
Adafruit_ST7735 Library
Rotary Encoder Library

Сборка

Алгоритм сборки проекта Arduino-генератор импульсов:

  1. Скачиваем и устанавливаем последнюю версию бесплатной среды разработки для программирования микропроцессора Ардуино. На нашем сайте указано, как правильно произвести первичную настройку. Также пользователь найдет все возможные ответы на свои вопросы по работе с данной средой.
  2. С помощью USB-кабеля подключаем микропроцессор к компьютерному устройству. Далее перемещаем программу, код которой указан в разделе выше, в память платформы.

Настройка

Если по окончанию загрузки пользователь получил сообщение «Done uploading», значит, генератор сигналов на Ардуино с дисплеем готов к работе. Следующий шаг – соединение модулей.

Выходные сигнальные волны снимаются с контактов генератора: QOUT1, QOUT2 (прямоугольный), ZOUT1 и ZOUT2 (синусоидальный).

После сборочных работ следует тщательно проверить, правильно ли подключены все контакты. Если все правильно подключено – подаем питание в устройство из электросети.

По истечению пары секунд на дисплее загорится стандартное значение частоты – 10 кГц. Значение можно изменить в любое время – для этого в листинге выше запрограммированы кнопки вверх, вниз, влево и вправо.

Проверка

В первом случае после конструирования должен получиться стандартный мотор-редуктор Ардуино синусоидальных и прямоугольных волновых сигналов, диапазон которых регулируется от 0 до 40 МГц. Проверить управление легче легкого – есть 2 кнопки – вверх и вниз, для настройки грубого характера, а другие – влево и вправо – настраивают аппарат на точную проверку. Настроить шаг можно в зависимости от установленной частоты на аппарате.

Во втором случае итоговое решение будет выглядеть так:

Кроме того, перед переносом программы, указанной в разделе «Программное обеспечение», нужно проверить правильность кода с помощью компилирования. Аппаратная часть прибора легко соединяется с использованием отдельных модулей, поэтому частотный генератор на базе микропроцессора Ардуино может сделать начинающий разработчик электронных устройств.

Ардуино+