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);
}