DDS-генератор синусоїдального сигналу
У цьому проекті розглянемо виготовлення генератора синусоїдального сигналу за допомогою методу прямого синтезу (DDS-метод). Для реалізації цього проекту нам не знадобиться ніякого додаткового обладнання, крім самого контролера Arduino. Частотний діапазон генератора від 0 до 16 кГц, з точністю до 1 мкГц! Цей пристрій може стати в нагоді не тільки для генерування звукових сигналів, але й у тестовому та вимірювальному обладнанні радіолюбителя. Наприклад, у телекомунікаційному обладнанні DDS генератор можна використовувати для ЧМ та ФМ модуляції (FSK та PSK).
DDS-метод
У програмній частині проекту, для реалізації DDS методу, нам знадобиться 4 речі:
акумулятор і tuning word, який у нашому випадку складається з двох long integer змінних;
таблиця значень синусоїдального сигналу (один період);
цифро-аналоговий перетворювач, який забезпечується внутрішнім ШІМ Arduino (analogWrite);
генератор тактових імпульсів (використовуємо внутрішній hard-таймер від ATMega).
Більшість значущих байтів акумулятора використовується для адресів таблиці синусоїдального сигналу. Увесь циклічний процес працює за перериванням від внутрішнього тактового генератора.
Програмне забезпечення
Для роботи цього скетча на Arduino Diecimila або Duemilenove підключіть потенціометр до аналогового виходу 0 та до GND і +5В. Вихід генератора знаходиться на виході 11, куди ви можете підключити активні колонки або ФНЧ-фільтр, описаний нижче.
/* * * DDS Sine Generator з ATMEGS 168 * Timer2 генерує 31250 KHz Clock Interrupt * * KHM 2009 / Martin Nawrath * Kunsthochschule fuer Medien Koeln * Academy of Media Arts Cologne */ #include "avr/pgmspace.h" // таблиця з 256 значень синуса / один період синуса / зберігається в флеш-пам'яті PROGMEM prog_uchar sine256[] = { 127,130,133,136,139,143,146,149,152,155,158,161,164,167,170,173,176,178,181,184,187,190,192,195,198,200,203,205,208,210,212,215,217,219,221,223,225,227,229,231,233,234,236,238,239,240, 242,243,244,245,247,248,249,249,250,251,252,252,253,253,253,254,254,254,254,254,254,254,253,253,253,252,252,251,250,249,249,248,247,245,244,243,242,240,239,238,236,234,233,231,229,227,225,223, 221,219,217,215,212,210,208,205,203,200,198,195,192,190,187,184,181,178,176,173,170,167,164,161,158,155,152,149,146,143,139,136,133,130,127,124,121,118,115,111,108,105,102,99,96,93,90,87,84,81,78, 76,73,70,67,64,62,59,56,54,51,49,46,44,42,39,37,35,33,31,29,27,25,23,21,20,18,16,15,14,12,11,10,9,7,6,5,5,4,3,2,2,1,1,1,0,0,0,0,0,0,0,1,1,1,2,2,3,4,5,5,6,7,9,10,11,12,14,15,16,18,20,21,23,25,27,29,31, 33,35,37,39,42,44,46,49,51,54,56,59,62,64,67,70,73,76,78,81,84,87,90,93,96,99,102,105,108,111,115,118,121,124 }; #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) int ledPin = 13; // LED pin 7 int testPin = 7; int t2Pin = 6; byte bb; double dfreq; // const double refclk=31372.549; // =16MHz / 510 const double refclk=31376.6; // виміряна // змінні, які використовуються всередині сервісу переривання оголошені як volatile volatile byte icnt; // змінна всередині переривання volatile byte icnt1; // змінна всередині переривання volatile byte c4ms; // лічильник, що збільшується кожні 4мс volatile unsigned long phaccu; // фазовий акумулятор volatile unsigned long tword_m; // dds tuning word m void setup() { pinMode(ledPin, OUTPUT); // налаштовуємо цифровий пін як вихід Serial.begin(115200); // підключаємось до послідовного порту Serial.println("DDS Test"); pinMode(6, OUTPUT); // налаштовуємо цифровий пін як вихід pinMode(7, OUTPUT); // налаштовуємо цифровий пін як вихід pinMode(11, OUTPUT); // pin11= PWM output / frequency output Setup_timer2(); // вимикаємо переривання, щоб уникнути спотворення часу cbi (TIMSK0,TOIE0); // вимкнути Timer0 !!! delay() тепер недоступний sbi (TIMSK2,TOIE2); // включити Timer2 Interrupt dfreq=1000.0; // початкова вихідна частота = 1000.o Гц tword_m=pow(2,32)*dfreq/refclk; // розрахувати нове tuning word DDS } void loop() { while(1) { if (c4ms > 250) { // таймер / чекаємо повну секунду c4ms=0; dfreq=analogRead(0); // читаємо Poti на аналоговому піні 0 для регулювання вихідної частоти з 0..1023 Гц cbi (TIMSK2,TOIE2); // вимкнути Timer2 Interrupt tword_m=pow(2,32)*dfreq/refclk; // розрахувати нове tuning word DDS sbi (TIMSK2,TOIE2); // включити Timer2 Interrupt Serial.print(dfreq); Serial.print(" "); Serial.println(tword_m); } sbi(PORTD,6); // Тест / встановлюємо PORTD,7 в високий стан для спостереження за таймингом з осцилографом cbi(PORTD,6); // Тест / скидаємо PORTD,7 в низький стан для спостереження за таймингом з осцилографом } } //****************************************************************** // налаштування таймера2 // встановити розподільник на 1, режим PWM на фазі, 16000000/510 = 31372.55 Гц такт void Setup_timer2() { // Таймер2 Clock Prescaler на: 1 sbi (TCCR2B, CS20); cbi (TCCR2B, CS21); cbi (TCCR2B, CS22); // Режим PWM Таймера2 встановлений на Phase Correct PWM cbi (TCCR2A, COM2A0); // очищаємо порівняння sbi (TCCR2A, COM2A1); sbi (TCCR2A, WGM20); // Режим 1 / Phase Correct PWM cbi (TCCR2A, WGM21); cbi (TCCR2B, WGM22); } //****************************************************************** // Сервіс переривання таймера2 на 31372,550 КГц = 32 мкс // це базовий час REFCLOCK для DDS генератора // FOUT = (M (REFCLK)) / (2 exp 32) // час виконання: 8 мікросекунд (включаючи push і pop) ISR(TIMER2_OVF_vect) { sbi(PORTD,7); // Тест / встановлюємо PORTD,7 в високий стан для спостереження за таймингом з осцилографом phaccu=phaccu+tword_m; // м'який DDS, фазовий акумулятор з 32 біт icnt=phaccu >> 24; // використовуємо верхні 8 біт для фазового акумулятора як інформацію про частоту // читаємо значення з таблиці синуса ROM і відправляємо на PWM DAC OCR2A=pgm_read_byte_near(sine256 + icnt); if(icnt1++ == 125) { // збільшуємо змінну c4ms кожні 4 мілісекунди c4ms++; icnt1=0; } cbi(PORTD,7); // скидаємо PORTD,7 }
Результат
Нижче представлена осциллограмма, на верхній частині якої зображено ШІМ-сигнал на 11 виході, а в нижній частині цей же сигнал після фільтра низьких частот (ФНЧ). Синусоїда виглядає не дуже чистою, але це в основному через обмежену роздільну здатність цифрового осцилографа.
Спектрограма показала несподівано хороший результат. Великий пік - це на частоті близько 1000 Гц. Усі небажані спотворення знаходяться нижче 50 дБ, що виникли через те, що використовувався 8 бітний ЦАП (1/256 = 48 дБ).
Вихідний фільтр низьких частот
Для початку ви можете підключити 11 пін контролера до активних колонок. Але, скоріше за все, вам ще знадобиться ФНЧ-фільтр, який також буде фільтрувати частоту дискретизації 32 кГц. Нижче представлена схема такого фільтра з частотою зрізу 12 кГц.
Апаратна реалізація DDS
Ця програмна реалізація алгоритму DDS має деякі недоліки, пов'язані з обмеженою швидкістю алгоритму програми, а також можливостями мікроконтролера ATMega. Спеціалізовані DDS-мікросхеми позбавлені цих недоліків і покривають діапазон від 0 до 100 МГц.
WSPR
Сповіщувач про проходження слабкого сигналу (Weak Signal Propagation Reporter) - програмне забезпечення, що дозволяє передавати та приймати сигнали радіомаяків, залучаючи не тільки передавач, але і інтернет. За допомогою цього DDS-генератора можна генерувати 4 тонових послідовності частотою 1497.8 1499.3 1500.7 1502.2 Гц.
Оригінал статті англійською мовою (переклад Колтиков А.В. для сайту cxem.net)