GERADOR DE ONDAS E VFO


Montamos em uma caixa patola com uma nova tampa - painel de aluminio

Ola amigos ...vamos descrever um gerador de ondas quadradas, triangulares e senoidais com o ARDUINO NANO e o modulo DDS AD9833. E de quebra teremos tambem um VFO até 12 MHz.
Este projeto foi baseado no seguinte link :
LINK para a pagina do John

Fizemos algumas modificações em hardware e em software como adaptar para funcionar como VFO, adicionar amplificador de RF e de AF e mais algumas pequenas alterações.
Bugs e alguns problemas podem aparecer pois alterei o programa e não sou expert neste assunto sou apenas um curioso !

Como o modulo DDS AD9833 é pequeno (15x20mm)  e gera frequencias e sinais com precisão, elegi como um equipamento facil de montar e com muitos recursos. E principalmente por ser um otimo VFO até 12MHz.

O circuito :



Funcionamento :
O processamento de dados é feito pelo arduino nano, a interface de entrada de dados é feita pelos dois encoders um seleciona a casa decimal 
(a cor da casa decimal selecionada muda de verde para amarelo) e se apertado para acionar a chave interna ele muda a forma de onda (mostrada no display). O outro encoder altera a casa decimal (frequencia) para cima ou para baixo  e sua chave não tem função.


Foto : Gerador setado para onda senoide o cursor de alteração numérica, em amarelo, esta na centena da unidade

Foto : Cursor esta na unidade do milhão

Foto: gerando onda de forma triangular


Foto: gerando onda de forma quadrada

O arduino conversa com o AD9833 e com o display com uma comunicação serial ...note que as conexões de data e clock tem conexão comum, o arduino quando quer enviar um dado ao display aciona a entrada CS (Chip Select) ou seja seleciona que é para este dispositivo que vão os comandos.
Após setar a frequencia desejada o arduino envia os dados ao AD9833 que vai gerar a forma de onda e a frequencia setadas enviado este sinal pela saida ... se a frequencia for abaixo de 1MHz ...o sinal vai para o LM386 ... que tem um trimpot na entrada que serve para limitar o sinal para que não aja saturação ou deformação. Já potenciometro na saida, que vai no painel controla o nivel de saida do LM386 ou de audio. Ao passar de 1MHz o arduino envia um sinal a porta D4 de 5V que aciona o transistor que por sua vez atua o rele, mudando o LM386 para um filtro passa baixas de 13,5MHz e depois para um amplificador de RF. A sida de sinal em AF ou RF é no mesmo ponto, chaveada pelo rele.



Material :
Compramos a maioria dos componentes na China  via Ebay :
Arduino nano U$2.5
Display com 
ST7735 de driver U$4 tem dois modelos.. veja as fotos ..o modelo "QDTech" exige a troca de library este display é de 128x160 1/8" SPI serial TFT.
Para identificar o display correto é necessario ver fotos, da parte trazeira com atenção, pois os chineses anunciam uma coisa e vendem outra.

Foto : display QDTech funciona mas não recomendo


Foto display recomendado (possui uma linha apenas de terminais pretos)
Encoder com chave U$1
Ad9833 DDS modulo U$6
Duvidas e ajuda escreva para py2ohhZZZyahoo.com.br  (ZZZ =@)

Fotos da montagem


Foto : caixa patola interno da esquerda para direita fonte de 5V, arduino nano, AD9833 dds e LM386, rele com o filtro LPF (capacitores ceramicos) e embaxo o amplificador de RF.
Na tampa temos o display , potenciometro e a placa em que soldamos os encoders.


Foto : detalhe da parte interna da caixa patola.


Foto : com maior detalhe

Video da montagem
VIDEO POSTADO NO YOUTUBE CANAL PY2OHH

Arquivo .INO para download
Sketch do arduino (programa)

/*
AD9833 Waveform Module vwlowen.co.uk
modificado por py2ohh miguel sept 2016
*/

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


#define dc  8              //era A0 Define pins for TFT display.          
#define cs 9               // era a1 10
#define rst 12             //era 9 a2

#include <Adafruit_GFX.h>      // Core graphics library

// include Adafruit library OR QDTech library depending on the display's controller chip.

 #include <Adafruit_ST7735.h>            // Hardware-specific library
  Adafruit_ST7735 tft = Adafruit_ST7735(cs, dc, rst);

//  #include <Adafruit_QDTech.h>              // Hardware-specific library
  //Adafruit_QDTech tft = Adafruit_QDTech(cs, dc, rst); 


// https://github.com/zigwart/Adafruit_QDTech


#define BLACK 0x000                         // Define the display colours we'll be using
#define BLUE 0x001F                         // so they're constants regardless of which
#define GREEN 0x07E0                        // display library we use.
#define YELLOW 0xFFE0
#define GREY 0x632C
#define BRANCO 0xFFFF

const int SINE = 0x2000;                    // Define AD9833's waveform register value.
const int SQUARE = 0x2020;                  // When we update the frequency, we need to
const int TRIANGLE = 0x2002;                // define the waveform when we end writing.   

int wave = 0;
int waveType = SINE;
int wavePin = 7;

int freqUpPin = 2;                          // Define rotary encoder pins.
int freqDownPin = 3;
int stepUpPin = 5;
int stepDownPin = 6;

int rfPin =4;

const float refFreq = 25000000.0;           // On-board crystal reference frequency

const int FSYNC = 10;                       //era 10 Standard SPI pins for the AD9833 waveform generator.
const int CLK = 13;                         // CLK and DATA pins are shared with the TFT display.
const int DATA = 11;

Rotary r = Rotary(freqUpPin, freqDownPin);    // Rotary encoder for frequency connects to interrupt pins
Rotary i = Rotary(stepUpPin, stepDownPin);    // Rotart encoder for setting increment.

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

unsigned long incr = 1;
unsigned long oldIncr = 1;

void setup() {

  pinMode(freqUpPin, INPUT_PULLUP);      // Set pins for rotary encoders as INPUTS and enable
  pinMode(freqDownPin, INPUT_PULLUP);    // internal pullup resistors.
  pinMode(stepUpPin, INPUT_PULLUP);
  pinMode(stepDownPin, INPUT_PULLUP);
  pinMode(wavePin, INPUT_PULLUP); 
 
  pinMode(rfPin, OUTPUT);
 
   // Can't set SPI MODE here because the display and the AD9833 use different MODES.
  SPI.begin();
  delay(50);
 
  // Initialize either Adafruit OR QDTech display
  //QDTech display
    // tft.init();
 
  //Adafruit display     
  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(BLACK);
  tft.drawFastVLine(25, tft.height()-55, 4, GREY);         // Display 'static' cosmetic text.
  tft.drawFastVLine(50, tft.height()-55, 4, GREY);
  tft.drawFastVLine(58, tft.height()-55, 4, GREY);
  tft.drawFastVLine(93, tft.height()-55, 4, GREY);
  tft.drawFastVLine(105, tft.height()-55, 4, GREY);
  tft.drawFastVLine(144, tft.height()-55, 4, GREY);
 
  tft.drawFastHLine(25, tft.height()-52, 25, GREY);
  tft.drawFastHLine(58, tft.height()-52, 37, GREY);
  tft.drawFastHLine(105, tft.height()-52, 40, GREY);
 
  tft.setTextColor(GREY);
  tft.setCursor(23, tft.height()-48);
  tft.print(" MHz   kHz     Hz");
 
  tft.setCursor(15, tft.height() -20);
  tft.setTextSize(1);
  tft.drawFastHLine(0, tft.height() - 23, tft.width()-10, BLUE);
  tft.setTextColor(BRANCO);
  tft.println("AD9833 10 Hz a 12 MHz ");
  tft.print(" Gerador de forma de onda");
 
 
// Configure interrupt for rotary encoder and enable.
  PCICR |= (1 << PCIE2);
  PCMSK2 |= (1 << PCINT18) | (1 << PCINT19);
  sei();
 
  AD9833reset();                                   // Reset AD9833 module after power-up.
  delay(50);
  AD9833setFrequency(freq, SINE);                  // Set the frequency and Sine Wave output
  updateDisplay();
}

void updateDisplay() {
 
  // To complicate things, the display uses SPI MODE0 but the AD9833 uses SPI MODE3 so it's
  // necessary to switch modes before each SPI transfer.
  SPI.setDataMode(SPI_MODE0);     
 
  tft.fillRect(50, 10, 100, 12, BLACK);            // Clear text.
 
  tft.setTextColor(YELLOW);
  tft.setCursor(47, 10);
  tft.setTextSize(1);
  switch (waveType) {
    case SINE: tft.print("  SENOIDAL "); break;
    case SQUARE: tft.print("  QUADRADA "); break;
    case TRIANGLE: tft.print(" TRIANGULAR"); break;
  }
 
  tft.fillRect(25, 50, 140, 14, BLACK);           // Clear frequency numerals.
  tft.setTextColor(GREEN);
  tft.setTextSize(2);
  tft.setCursor(25, 50);
  format(freq);                                   // Show frequency in formatted form. 

}

void format(unsigned long value) {
  
   // Break the frequency value down into individual digits & into variable 'digit'.
   // If a digit corresponds with the currently-selected x10 increment, change the
   // text colour to YELLOW. All other digits and commas are GREEN.
  
  unsigned long j = 10000000; //era 1000000
 
  for (int i=7; i>=0; i--) {
    int digit = (value / j) % 10;
    incr == j ? tft.setTextColor(YELLOW): tft.setTextColor(GREEN);
    tft.print(digit);
    if ( (i == 6) || (i == 3)) {                  // Add commas at millions and thousands
      tft.setTextColor(GREEN);
      tft.print(".");
    }  
    j /= 10;
  }
}


void loop() {
 
  if (oldIncr != incr) {
    updateDisplay();
    oldIncr= incr;
  }
 
  // Check 'increment' rotary encoder. Increase or decrease 'increment' by a factor of x10
  // if encoder has been turned.
  unsigned char result = i.process();
  if (result) {
    if (result == DIR_CW)  {if (incr < 1000000) incr *= 10;}
    if (result == DIR_CCW) {if (incr >= 10) incr /= 10;}
    updateDisplay();
  }
 
  if (freq > 1000000) {
    digitalWrite(rfPin, HIGH);
  }
  else {
    digitalWrite(rfPin, LOW);
  }
 
  // Check if push button on 'increment' rotary encoder is pushed and set Wave Type accordingly.
  if (digitalRead(wavePin) == LOW) {
    wave += 1;
    if (wave > 2) wave = 0;
    switch (wave) {
      case 0: waveType = SINE; break;
      case 1: waveType = SQUARE; break;
      case 2: waveType= TRIANGLE; break;
    }   
    AD9833setFrequency(freq, waveType);     // Set AD9833 to frequency and selected wave type.
    updateDisplay();
    delay(200);
  }
 
  if (freq != freqOld) {                    // If frequency has changed, interrupt rotary encoder
    AD9833setFrequency(freq, waveType);     // must have been turned so update AD9833 and display.
    updateDisplay();
    freqOld = freq;                         // Remember new frequency to avoid unwanted display
  }                                         // and AD9833 updates.
}

// AD9833 documentation advises a 'Reset' on first applying power.
void AD9833reset() {
  WriteRegister(0x100);   // Write '1' to AD9833 Control register bit D8.
  delay(10);
}

// Set the frequency and waveform registers in the AD9833.
void AD9833setFrequency(long frequency, int Waveform) {
 
  long FreqWord = (frequency * pow(2, 28)) / refFreq;

  int MSB = (int)((FreqWord & 0xFFFC000) >> 14);    //Only lower 14 bits are used for data
  int LSB = (int)(FreqWord & 0x3FFF);
 
  //Set control bits 15 ande 14 to 0 and 1, respectively, for frequency register 0
  LSB |= 0x4000;
  MSB |= 0x4000;
 
  WriteRegister(0x2100);  
  WriteRegister(LSB);                  // Write lower 16 bits to AD9833 registers
  WriteRegister(MSB);                  // Write upper 16 bits to AD9833 registers.
  WriteRegister(0xC000);               // Phase register
  WriteRegister(Waveform);             // Exit & Reset to SINE, SQUARE or TRIANGLE

}

void WriteRegister(int dat) {
 
  // Display and AD9833 use different SPI MODES so it has to be set for the AD9833 here.
  SPI.setDataMode(SPI_MODE2);         
 
  digitalWrite(FSYNC, LOW);           // Set FSYNC low before writing to AD9833 registers
  delayMicroseconds(10);              // Give AD9833 time to get ready to receive data.
 
  SPI.transfer(highByte(dat));        // Each AD9833 register is 32 bits wide and each 16
  SPI.transfer(lowByte(dat));         // bits has to be transferred as 2 x 8-bit bytes.

  digitalWrite(FSYNC, HIGH);          //Write done. Set FSYNC high
}


// Interrupt service routine for the 'frequency' rotary encoder.

ISR(PCINT2_vect) {

  unsigned char result = r.process();
  if (result) {
    if (result == DIR_CW) {                   // Clockwise rotation so add increment to frequency
       if ((freq + incr) < 12000000) freq+=incr; //era 6000000
      
    } else {
        if (freq > incr) {                    // Counter-clockwise rotation so subtract increment
          freq -= incr;                       // from frequency unless it would result in a negative
        } else {                              // number.
          if (freq >= 1) incr /= 10;
          if (incr < 1) incr = 1;             // Compensate for math rounding error.
        } 
    }
  }
}

++++++++++++++++++++++++++++++++++++
 
73 de py2ohh miguel