Genarator DDS na AD9833
Prosty generator funkcyjny na module AD9833.
Generator zbudowany jest na gotowym module (Chiny – ok. 7 USD) z układem AD9833, sterowanym Arduino NANO V3, a nastawy prezentowane są na wyświetlaczu alfanumerycznym LCD 2×16 znaków (I2C). Układ generuje przebiegi sinusoidalne, trójkątne i prostokątne – bez regulacji poziomu sygnału wyjściowego. Manipulatorem jest enkoder, który służy do wyboru (wciśnięcie) i regulacji (pokrętło) nastaw.
Schemat układu (odręczny) – poniżej :
Układ generuje przyzwoite przebiegi w zakresie do ok. 2 MHz – powyżej tej częstotliwości jakość i poziom sygnału drastycznie spada, choć sam generator nadal pracuje nawet do ok. 10 MHz.
Kod programu jest dosyć rozbudowany, ale podstawowe procedury – obsługi AD9833 – są łatwe do aplikacji w dowolnym układzie. Sporo miejsca w kodzie zajmują procedury obsługi Menu, zmiany nastaw i wykrywania wartości granicznych oraz prezentacji wyników na LCD. Sama obsługa AD9833 (sposób zapisu, rejestry, sterowanie) to fragmenty kodu znalezione w Necie – nie chciałem się już zagłębiać w dokumentację układu, a gotowej biblioteki nie znalazłem. Soft nie został też gruntownie przetestowany i nie wykluczam jakiś błędów – jak coś znajdziecie, proszę pisać w komentarzach …
Kod :
#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal_I2C.h>
#include <SPI.h>
#include <Encoder_Polling.h>
#define FSYNC 6 //definicja PIN D6 - FSYNC AD9833
// POZOSTAŁE WYKORZYSTANE PINY DLA AD9833 Z ARDUINO NANO V3
// D11 - MOSI - SDATA
// D13 - SCK - SCLK
// PINY DLA MAGISTRALI I2C
// A4 - SDA
// A5 - SCL
// LiquidCrystal_I2C lcd(adres,En,Rw,Rs,D4,D5,D6,D7,polaryzacja);
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);
#define Light 3 // podswietlanie - pin 3
//********** definicja pinow dla Encodera
int encoderPin_A = 4;
int encoderPin_B = 3;
int PushButt = 2; // pin encodera PushButton
//********** parametry startowe i zmienne
long czastota = 500000; // ustawienie czestotliwosci startowej
String czestotliwosc;
long krok = 10; // krok poczatkowy
int sinus = 0x2000;
int trojkat = 0x2002;
int prostokat = 0x2020;
int typprzebiegu;
int znacznik = 10; // pomocniczy znacznik - uzywany przy MENU
void setup()
{
lcd.begin (16,2); // def. LCD
lcd.setBacklightPin(Light,POSITIVE); // def. rodzaj LCD
lcd.setBacklight(HIGH); // wlacz podsw. LCD
pinMode(FSYNC, OUTPUT); // konfiguracja pinu FSYNC
SPI.begin(); // start SPI
digitalWrite(FSYNC, HIGH); // ustawienie pinu FSYNC
SPI.setDataMode(SPI_MODE2); // tryb pracy SPI
encoder_begin(encoderPin_A, encoderPin_B); // start Encoder
pinMode(PushButt, INPUT); // konfiguracja pinu PushButtona
typprzebiegu = sinus; // wystartuj z przebiegiem SINUS
}
//*********************** Glowna petla programu *********************
void loop()
{
obsluga_buttona(); // sprawdx button w encoderze
obsluga_encodera(); // sprawdz obrot w encoderze
WriteFrequencyAD9833(czastota); // zapisz/odswiez f w AD9833
prezentacja(); // wyswietl wszystko na LCD
delay(100); // pauza aby LCD nie mrugal
// mozna badac flage zajetosci w LCD
// ale istotnie komplikuje to soft
}
//*********************** Koniec glownej petli *********************
//*********************** Procedury ******************************
void obsluga_encodera()
{
if (znacznik == 10) // znacznik 10 zmiana sygnalu sin/prost/trojk
{
int dir = encoder_data(); // sprawdz Encoder
if(dir > 0) // sprawdzam tylko w prawo
{
if(typprzebiegu == sinus) // jesli jest sin to zmien
{typprzebiegu = trojkat; // na trojk
delay(80);} // odczekaj - drgania
else if (typprzebiegu == trojkat) //jesli trojk ustaw prost
{typprzebiegu = prostokat;delay(80);}
else if (typprzebiegu == prostokat)//jesli prost ustaw sin
{typprzebiegu = sinus;delay(80);}
else
{}
}
else
{}
} //*****************koniec warunku dla znacznik == 10
if (znacznik == 20) // warunek dla znacznik 20 - zmiana kroku
{
int dir = encoder_data(); // sprawdz Encoder
if(dir > 0) // jeśli w prawo to ...
{
if (krok < 1000000) // jesli krok mniejszy niz 1MHz
{krok = krok * 10;delay(100);} // zwieksz krok-mnozenie razy 10
else // jesli 1MHz nie rob nic
{}
}
else if (dir <0 ) // jeśli w lewo to ...
{
if (krok >= 10) // dodatkowy warunek
// sprawdzenie czy nie dziele 0
{krok = krok / 10; // zmniejsz krok - dzielenie przez 10
delay(100);
}
else
{krok = 1;delay(100);} // ustaw krok 1 Hz
}
else
{}
} //****************koniec warunku dla znacznik == 20
else if (znacznik == 30) // warunek dla znacznik 30 - zmiana czestotliwosci
{
int dir = encoder_data(); // sprawdz Encoder
if(dir > 0) // jeśli w prawo to ...
{czastota = czastota + krok;} // zwieksz f o krok
else if (dir <0 ) // jeśli w lewo to ...
if ((czastota-krok) >= 0)
{
{czastota = czastota - krok;} // zmniejsz f o krok
}
else
{}
else
{}
} //********************koniec warunku dla znacznik == 30
else
{}
} //****************koniec procedury obslugi encodera
void obsluga_buttona()
{
if (digitalRead(PushButt))
{} // nie wcisniety - wyjscie
else
{ // wcisniety - ustalam w ktorej
// pozycji menu i co zmienic
if (znacznik == 10) // jesli znacznik = 10 ...
{znacznik = 20;delay(120);} // ... zmien na 20
else if (znacznik == 20) // jesli znacznik = 20 ...
{znacznik = 30;delay(120);} // ... zmien na 30
else
{znacznik = 10;delay(120);} // jesli nie 10 i nie 20 to musi
// byc 30 wiec ustaw znow 10
}
}
void prezentacja() // wyswietlenie wszystkich wartosci
{
lcd.clear(); // wyczysc LCD
if(znacznik == 10) // znacznik = 10
{lcd.print("*");} // ... wyswietl "*" przed
else
{lcd.print(" ");} // znacznik <> 10 - spacja
if(typprzebiegu == sinus) // jaki aktualnie mamy przebieg
{lcd.print("sinus");} // jesli sinus - wyswietl
else if (typprzebiegu == prostokat)
{lcd.print("prost");} // jesli prostokat - wyswietl
else if (typprzebiegu == trojkat)
{lcd.print("trojk");} // jesli trojkat - wyswietl
else
{}
if(znacznik == 20) // jesli znacznik = 20
{
lcd.print(" *");}
else
{lcd.print(" ");}
lcd.print("K=");
if(krok == 1000000)
{
lcd.print(krok / 1000000);
lcd.print("MHz");
}
else if(krok >= 1000)
{
lcd.print(krok / 1000);
lcd.print("KHz");
}
else
{
lcd.print(krok);
lcd.print("Hz");
}
lcd.setCursor(0, 1);
if(znacznik == 30) // jesli znacznik = 30
{
lcd.print("*");
}
else
{
lcd.print(" ");
}
// ponizej troche zakrecone zabiegi aby na LCD wyswietlana czestotliwosc
// wygladala przyzwoicie - "F=xx.xxx.xxx Hz"
czestotliwosc = String(czastota); //zamiana typu danych na string
int dlugosc = czestotliwosc.length(); // ustalam dlugosc ciagu
if (dlugosc == 6) // jesli dlugosc 6 - mniej niz 1 MHz
{
czestotliwosc = "0" + czestotliwosc; // to dopisz zero przed
}
else if(dlugosc == 5) // jesli dlugosc 5 - dziesiatki KHz
{
czestotliwosc = "00" + czestotliwosc;
}
else if(dlugosc == 4) // jesli dlugosc 4 - jednostki KHz
{
czestotliwosc = "000" + czestotliwosc;
}
else if(dlugosc == 3) // jesli dlugosc 3 - setki Hz
{
czestotliwosc = "0000" + czestotliwosc;
}
else if(dlugosc == 2) // jesli dlugosc 2 - dziesiatki Hz
{
czestotliwosc = "00000" + czestotliwosc;
}
else if(dlugosc == 1) // jesli dlugosc 1 - jednostki Hz
{
czestotliwosc = "000000" + czestotliwosc;
}
else
{}
lcd.print(" F=" + czestotliwosc.substring(0,1) + "." + czestotliwosc.substring(1,4) + "." + czestotliwosc.substring(4,7) + " Hz");
}
// procedura obslugi AD9833 - zassana z Net`u - delikatnie zmodyfikowana
//
void WriteFrequencyAD9833(long frequency)
{
int MSB;
int LSB;
int phase = 0;
long calculated_freq_word;
float AD9833Val = 0.00000000;
AD9833Val = (((float)(frequency)) / 25000000); // zegar DDS 25 MHz
calculated_freq_word = AD9833Val * 0x10000000;
MSB = (int)((calculated_freq_word & 0xFFFC000) >> 14);
LSB = (int)(calculated_freq_word & 0x3FFF);
LSB |= 0x4000;
MSB |= 0x4000;
phase &= 0xC000;
WriteRegisterAD9833(0x2100);
WriteRegisterAD9833(LSB);
WriteRegisterAD9833(MSB);
WriteRegisterAD9833(phase);
WriteRegisterAD9833(typprzebiegu);
}
void WriteRegisterAD9833(int dat)
{
digitalWrite(FSYNC, LOW);
SPI.transfer(highByte(dat));
SPI.transfer(lowByte(dat));
digitalWrite(FSYNC, HIGH);
}