Signal generator
Contents
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:
- An arduino (nano works, due is overkill but works too)
- AD9851
- LCD screen (using 16x2 screen)
- keypad
- Circuit Supplies (see below)
- 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
- Next time make sure the orientation of the arduino nano is such that you can plug the usb in without obstruction
- Put two ad9851 boards, synchronize the phase between them