Signal generator

From Quantum kot
Jump to navigation Jump to search

Signal generator on AD9851 (by Bipin)

Dropbox Folder: Cold Atoms\Pulse sequence\Bipin-signal-generator-interface


How it works

It creates sine waves. It Requires only +15V and GND

On the main interface (mode 1): pressing the 2 key goes up pressing the 8 key goes down pressing the 5 key selects to change the value

The visible channel will be the selected channel and have the phase/frequency of the output

While changing value you type in a number (decimals allowed) then you press A for Rad/Hz B for Deg/kHz C for MHz

For saved values: phase is rounded to the nearest degree frequency is rounded to the nearest Hz

Currently there is no limits on the frequency, plugging in too high or too low of a value just won't work with the AD9851 50kHz - 50MHz seems to be working fine

Components

We require:

  1. An arduino (nano works, due is overkill but works too)
  2. AD9851
  3. LCD screen (using 16x2 screen)
  4. keypad
  5. Circuit Supplies (see below)
  6. Box (Digikey: HM1074-ND, 5.91"L X 3.94"W )

PARTS

Quantity | Part

  2     | 104Z Capacitor = 0.1 micro Farad
  1     | 2.2C Capacitor = 2.2 pico Farad
  1     | Surface Mount 1000 = 100 Ohm Resistor
  1     | Through Hole 750 Ohm Resistor (OPTIONAL for LED)
  2     | 7805 Voltage Regulator
  2     | 7812 Voltage Regulator
  2     | 78L08 Voltage Regulator
  1     | AD9851 Board
  1     | Arduino Nano
1 or 2  | Amplifier
  1     | SMA output, and SMA to BNC if needed. 
  1     | switch if needed
  1     | LED (OPTIONAL, IT'S A POWER INDICATOR LIGHT)
  2     | 2 pin-head connector (one for power/GND, one for LCD power/GND)

CONNECTIONS AND PINS

  • Note there are some Eagle Files in the Dropbox Folder that may be helpful

PINS (nano code = signal_generator_bipin_interface_and_AD9851_nano)

AD9851 version 1: jumper on J1

bit_data (d7) -- nano 13

w_clk -- nano 12

fq_up -- nano 11

rest -- nano 10

connect a potentiometer to AD9851's VCC and I-R pins to vary the output amplitude (if required)


AD9851 version 2 (HC-SR08 DDS_AD9850 written on bottom):

data -- nano 13

w_clk -- nano 12

fu_ud -- nano 11

reset -- nano 10

There is a built in potentiometer on this AD9851 but it wasn't working when i tested it You should remove it and replace it with your own potentiometer to vary the output amplitude (THIS DOES NOT WORK, NOT SURE WHY)


keypad: 2,3,4,5,6,7,8,9 - just plug it in and switch order if keys don't work as expected

LCD display:

1 (VSS) -- GND

2 (VDD) -- 5V power - lower potentials will result in a dimmer screen

3 (V0) -- GND (if you change closer to VDD the screen goes dim)

4 (RS) -- nano A0

5 (RW) -- GND

6 (E) -- nano A1

7 (D0) NC

8 (D1) NC

9 (D2) NC

10(D3) NC

11(D4) -- nano A2

12(D5) -- nano A3

13(D6) -- nano A4

14(D7) -- nano A5

15(A) NC

16(K) -- GND

5V & GND need to be connected to the LCD

    • SEE ATTACHED IMAGE FOR CIRCUIT

Arduino Code

FileName: signal_generator_bipin_interface_and_AD9851_nano.ino

/*


This is a signal generator.
It creates sine waves (for now)
We require an arduino (nano here), an AD9851, a screen and a keypad
On the main interface (mode 1) pressing the 2 key goes up, 8 key goes down and 5 key selects to change the value
The visible channel will be the selected channel and have the phase/frequency of the output
While changing value you type in a number (decimals allowed) then you press A for Rad/Hz, B for Deg/kHz or C for MHz
phase is rounded to the nearest degree, frequency is rounded to the nearest Hz when saved
Currently there is no limit on the frequency, plugging in too high or too low of a value just won't work.
50kHz - 50MHz seems to be working fine 

PINS (nano)
AD9851:
bit_data (d7) = 13
w_clk = 12
fq_up = 11
rest = 10


keypad:
2,3,4,5,6,7,8,9 - just plug it in and switch order if keys don't work as expected

LCD display:
14,15,16,17,18,19 (A0-A5)

5V & GND need to be connected to the LCD


connect a potentiometer to AD9851's VCC and I-R pins to vary the output amplitude

*/

//libraries
#include <LiquidCrystal.h>
#include <math.h>
#include <Key.h>
#include <Keypad.h>
//#include <EEPROMex.h>

//***AD9851 PINS**********************************************************************
int ad9851_bit_data=13; //d7
int ad9851_w_clk=12;
int ad9851_fq_up=11;
int ad9851_rest=10;

//***LCD pins**********************************************************************
LiquidCrystal lcd(14, 15, 16, 17, 18, 19);



//save information
//int frequency_location[] = {0, 4, 8, 12};
//int phase_location[] = {16, 20, 24, 28};
int channel = 0;

//initializes the save information for the 4 channels, channels are saved as 0,1,2,3 but displayed as 1,2,3,4 respectively
long frequency[] = {0,0,0,0};
int phase[] = {0,0,0,0};


//keypad
const byte rows = 4; //four rows
const byte cols = 4; //four columns
char keys[rows][cols] = {
  {'1','2','3','A'}, //A = Hz, RAD
  {'4','5','6','B'}, //B = KHz, Deg
  {'7','8','9','C'}, //C = MHz
  {'*','0','.','D'}  //D = delete
};
//***KEYPAD PINS**********************************************************************
byte rowPins[rows] = {2, 3, 4, 5}; //connect to the row pinouts of the keypad
byte colPins[cols] = {6,7,8,9};
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, rows, cols );

//input
//int push_pin = 1;
//int turn_pin = 0;

int mode = 1;
int line = 0;
int porf = 0; // phase or frequency? phase = 0, freq = 1

String new_input = "";
boolean decimal_pressed = false;

//int pressed(int input) {
//  if (input == 0) {
//    return 1;
//  }
//  else {
//  return 0;
//  }
//}

//boolean turn() { 
//  if (pressed(analogRead(turn_pin)) != porf) {
//    porf = (porf+1)%2;
//    while (pressed(analogRead(turn_pin)) != porf) {
//      delay(200);
//    }
//    return true;
//  }
//  else {
//    return false;
//    }
//}

//AD9851
//reset for serial writing
void ad9851_reset_serial()
{
  digitalWrite(ad9851_w_clk,LOW);
  digitalWrite(ad9851_fq_up,LOW);
  //rest
  digitalWrite(ad9851_rest,LOW);
  digitalWrite(ad9851_rest,HIGH);
  digitalWrite(ad9851_rest,LOW);
  //w_clk
 digitalWrite( ad9851_w_clk,LOW);
  digitalWrite(ad9851_w_clk,HIGH);
  digitalWrite(ad9851_w_clk,LOW);
  //fq_up
  digitalWrite(ad9851_fq_up,LOW);
  digitalWrite(ad9851_fq_up,HIGH);
  digitalWrite(ad9851_fq_up,LOW);
  
}

//w0=0x00 will turn off 6 multi for 30MHz, w0=0x01 will turn on 6 multi for 30MHz
//we convert binary to decimal here for frequency and phase.
void ad9851_wr_serial(float pha_tem,double frequence)
{
unsigned char i,w;
long int y;
double x;
//get HEX we need
x=4294967295/180;//if it is not 180MHz, or the 6 multi is turned off, Here 180 must be changed!!!
frequence=frequence/1000000;// MHz
frequence=frequence*x;
y=frequence;//convertion from binary to decimal
w=(y>>=0);

//write bit to bit, from LSB to MSB
for(i=0;i<8;i++)
{
  if((w>>i)&0x01)
  {
    digitalWrite(ad9851_bit_data,HIGH);
  }
  else
  {
    digitalWrite(ad9851_bit_data,LOW);
  }
digitalWrite(ad9851_w_clk,HIGH);//necessary trigger after writing a byte 
digitalWrite(ad9851_w_clk,LOW);
}
//w3, all shifted to higher 8 bits
w=(y>>8);
for(i=0;i<8;i++)
{
  if((w>>i)&0x01)
  {
    digitalWrite(ad9851_bit_data,HIGH);
  }
  else
  {
    digitalWrite(ad9851_bit_data,LOW);
  }
digitalWrite(ad9851_w_clk,HIGH);
digitalWrite(ad9851_w_clk,LOW);
}
//w2
w=(y>>16);
for(i=0;i<8;i++)
{
  if((w>>i)&0x01)
  {
    digitalWrite(ad9851_bit_data,HIGH);
  }
  else
  {
    digitalWrite(ad9851_bit_data,LOW);
  }
digitalWrite(ad9851_w_clk,HIGH);
digitalWrite(ad9851_w_clk,LOW);
}
//w1
w=(y>>24);
for(i=0;i<8;i++)
{
  if((w>>i)&0x01)
  {
    digitalWrite(ad9851_bit_data,HIGH);
  }
  else
  {
    digitalWrite(ad9851_bit_data,LOW);
  }
digitalWrite(ad9851_w_clk,HIGH);
digitalWrite(ad9851_w_clk,LOW);
}

//write default value to three bits,other 5bits are for phase offset
digitalWrite(ad9851_bit_data,HIGH);
digitalWrite(ad9851_w_clk,HIGH);
digitalWrite(ad9851_w_clk,LOW);
digitalWrite(ad9851_bit_data,LOW);
digitalWrite(ad9851_w_clk,HIGH);
digitalWrite(ad9851_w_clk,LOW);
digitalWrite(ad9851_bit_data,LOW);
digitalWrite(ad9851_w_clk,HIGH);
digitalWrite(ad9851_w_clk,LOW);

//all following is for phase setting
int phase=pha_tem*8;
w=(phase>>=0);
for(i=0;i<5;i++)
{
  if((w>>i)&0x01)
  {
    digitalWrite(ad9851_bit_data,HIGH);
  }
  else
  {
    digitalWrite(ad9851_bit_data,LOW);
  }
digitalWrite(ad9851_w_clk,HIGH);
digitalWrite(ad9851_w_clk,LOW);
}


//start to enable
digitalWrite(ad9851_fq_up,HIGH);
digitalWrite(ad9851_fq_up,LOW);
//update for the system frequency
}



//screen displays the frequency and phase of the current selected channel
void mode_one_screen() {  // mode 1 = main screen. phase: 123, freq = 234.
  lcd.clear();
  channel = line/2;
  porf = line%2;
  lcd.setCursor(0, 0);
  lcd.print(" Phase: ");
  lcd.print(phase[channel]);
  //lcd.print(EEPROM.readFloat(phase_location[channel]));
  //lcd.print("DEG");
  lcd.print((char)223); //degree symbol
  lcd.setCursor(14, 0);
  lcd.print("C");
  lcd.print(channel + 1);
  lcd.setCursor(0, 1);
  lcd.print(" FQ: ");
  Serial.println(frequency[channel]);
  //lcd.print(frequency[channel]);
  if (frequency[channel]<1000) {
    lcd.print(frequency[channel]);
    lcd.print(" Hz");
    }  
  if (frequency[channel]>=1000 && frequency[channel]<1000000) {
    lcd.print(frequency[channel]/1000);
    lcd.print(".");
    lcd.print(frequency[channel]%1000);
    lcd.print(" kHz");
    }
  if (frequency[channel]>=1000000) {
    lcd.print(frequency[channel]/1000000);
    lcd.print(".");
    lcd.print((frequency[channel]%1000000)/1000);
    lcd.print(" MHz");
    }

  
  //lcd.print(EEPROM.readFloat(frequency_location[channel]));
  lcd.setCursor(12, 1);
  lcd.setCursor(0, porf);
  lcd.print(">");
  
  //for sending signal to AD9851
  ad9851_reset_serial();
  ad9851_wr_serial(phase[channel]/180.00,frequency[channel]);  //give phase and frequency here. e.g. (1,60000)means 1 pi phase offset, frequency 60000Hz
}


//initializes the screen
//start program
void setup() {
  lcd.begin(16, 2);
  //debugging serial connection:
  Serial.begin(9600);
  //AD9851 setup
  pinMode(ad9851_bit_data,OUTPUT);
  pinMode(ad9851_w_clk,OUTPUT);
  pinMode(ad9851_fq_up,OUTPUT);
  pinMode(ad9851_rest,OUTPUT);
  
  mode_one_screen();
}

//mode 1 main screen shows the current frequency and phase that is set
//when turned the line is increased by 1, essentially rotating through the channels
void loop() {  
  while (mode == 1) { //main screen. Button input. "phase: 1234, freq: 1234"
    //debugging
    //Serial.print("LINE:");
    //Serial.println(line);
    //Serial.print("CHANNEL:");
    //Serial.println(channel);
    //Serial.print("P(0) OR F(1)");
    //Serial.println(porf);
    char key = keypad.getKey();
    
    //if (turn() == true) {
    //  line = (line+1)%8;
    //  mode_one_screen();
    //  while (turn() == true) {
    //    delay(200);
    //  }
    //}
    
    //go up
    if (key == '2'){
      line = (line+7)%8;
      mode_one_screen();
    }
    //go down
    if (key == '8'){
      line = (line+1)%8;
      mode_one_screen();
    }

         
   //now if you press the button we go to mode 2 which is where we can change frequency and phase
   //the variables required and screen are initialized here
    if (key == '5'){ // if 5  pressed
      //Serial.println("BUTTON HAS BEEN PRESSED YAY");
      mode = 2;
      lcd.clear();
      new_input = "";
      decimal_pressed = false;
     
      if (porf == 1) {
        lcd.setCursor(0, 0);
        lcd.print("New frequency:C");
        lcd.print(channel+1);
        lcd.setCursor(0, 1);
      }
     
      if (porf == 0) {
        lcd.setCursor(0, 0);
        lcd.print("New phase:");
        lcd.setCursor(14,1);
        lcd.print("C");
        lcd.print(channel+1);
        lcd.setCursor(0, 1);
      }
     
      //while (analogRead(push_pin)==0) {
      //  delay(50);
      //}
    }
  }
 
  //while we have the mode 2 active, we will continiously loop here to see if any buttons have been pressed
  //the decimal key is only allowed to be pressed once, numbers will add to the screen and delete will remove a character
  //the ABC buttons will end the input and save the entered value in the respective units
  while (mode == 2) { //input screen. "New frequency/phase:"
    char key = keypad.getKey();
    if (key) {
     
      if (key == '.') { // "."
        if (decimal_pressed == false) {
          decimal_pressed = true;
          new_input += key;
        }
      }
 
      if (key == 'D') { // "Delete"
        if (new_input.charAt(-1)=='.') {
          decimal_pressed = false;
        }
        new_input.remove(new_input.length()-1);
      }
     
      int key_number = (key - '0');
     
      if ((key_number <= 9)&&(0 <= key_number)) {
        new_input = new_input + key;
      }

      lcd.setCursor(0, 1);
      lcd.print("                "); //essentially clears and reprints to avoid errors
      lcd.setCursor(0, 1);
      lcd.print(new_input);

      //these are the buttons that will end the input, clearing the screen saving to memeory and writing the new phase
      //A = 17, B = 18, C = 19
      if ((17 <= key_number)&&(key_number <= 19)) {
        lcd.clear();
        //new_input to (long?)
        if (porf == 0) {
          if (key_number == 17) {
            phase[channel] = (round((atof(new_input.c_str())) *57.2957795)%360);
            //EEPROM.writeFloat(phase_location[channel], ((atof(new_input.c_str())) / 3.1415927));
          }
          if (key_number == 18) {
            phase[channel] = (round((atof(new_input.c_str()))*1.00)%360);
            //EEPROM.writeFloat(phase_location[channel], ((atof(new_input.c_str())) / 180));
          }
          if (key_number != 19) {
            lcd.print("New phase for C");
            lcd.print(channel+1);
            lcd.setCursor(0, 1);
            lcd.print(phase[channel]);
            //lcd.print(EEPROM.readFloat(phase_location[channel]));
            //lcd.print(" DEG");
            lcd.print((char)223); //degree symbol
            //delay to allow you to read your input (2.5 sec)
            delay(2500);
            //save phase
            //send phase off
            mode = 1;
            mode_one_screen();

          }
        }
        //same as above, to save the input when we are dealing with frequency
        if (porf == 1) {
          //frequency[channel] = (atof(new_input.c_str())) * pow(10,(3*(key_number-17))); //this line gives small errors on the nano, power rounding errors
          if (key_number==17) {
            frequency[channel] = (atof(new_input.c_str()) *1);
          }
          if (key_number==18) {
            frequency[channel] = (atof(new_input.c_str()) *1000);
          }
          if (key_number==19) {
            frequency[channel] = (atof(new_input.c_str()) *1000000);
          }
          
          
          //weird thing on nano long saving that subtracts one for some reason when kHz or MHz is used, this is to componsate
          //if (key_number==18 || key_number==19) {
          //  frequency[channel] = (frequency[channel]+1);
          //}
          //EEPROM.writeFloat(frequency_location[channel], (atof(new_input.c_str())) * pow(10,(3*(key_number-17))));
          lcd.print("New freq for C");
          lcd.print(channel+1);
          lcd.print(":");
          lcd.setCursor(0, 1);
          //lcd.print(frequency[channel]);
          if (frequency[channel]<1000) {
            lcd.print(frequency[channel]);
            lcd.print(" Hz");
            }  
          if (frequency[channel]>=1000 && frequency[channel]<1000000) {
            lcd.print(frequency[channel]/1000);
            lcd.print(".");
            lcd.print(frequency[channel]%1000);
            lcd.print(" kHz");
            }
          if (frequency[channel]>=1000000) {
            lcd.print(frequency[channel]/1000000);
            lcd.print(".");
            lcd.print((frequency[channel]%1000000)/1000);
            lcd.print(" MHz");
            }
          
          //lcd.print(EEPROM.readFloat(frequency_location[channel]));
          //delay to allow you to read your new input 2.5 sec
          delay(2500);
          //save frequency
          //send frequency off
          mode = 1;
          mode_one_screen();
        }
      }
      
      //the cancel key will clear the screen and go back to the main mode 1 screen without saving
      if (key == '*') { //
        lcd.clear();
        mode = 1;
        mode_one_screen();
      }
 
     //delay so that button presses are only counted once 
      while (keypad.getKey()== key) {
        delay(50);
      }
    }
  }
}

Possible improvements

  1. Next time make sure the orientation of the arduino nano is such that you can plug the usb in without obstruction
  2. Put two ad9851 boards, synchronize the phase between them