AD9850 Signal Generator code help please

Hi
I am fairly new to Arduino, so need help with following signal generator code please.
I am using the following:
AD9850 & ATmega328 16MHz Pro Mini, rotary encoder, 1602 LED display.

I am using a freely available code for a simple signal generator and tried to modify it to suit my needs.

I have tried to modify the code to add increments of 0.1hz & 1hz steps, Also saved set frequency to memory.

This I have achieved but has created another problem on power up.

After my modifications the Problem is:-

  1. on power up the output signal frequency is ten times bigger than the frequency shown on the LED display.

  2. When I move the rotary encoder in any direction the output frequency drops to the correct set frequency as shown on the LED display.

Can anyone help please?

/*
Main code by Richard Visokey AD7C - www.ad7c.com
Revision 2.0 - November 6th, 2013
*/

// Include the library code
#include <LiquidCrystal.h>
#include <rotary.h>
#include <EEPROM.h>

//Setup some items
#define W_CLK 8   // Pin 8 - connect to AD9850 module word load clock pin (CLK)
#define FQ_UD 9   // Pin 9 - connect to freq update pin (FQ)
#define DATA 11   // Pin 11 - connect to serial data load pin (DATA)
#define RESET 10  // Pin 10 - connect to reset pin (RST) 
#define pulseHigh(pin) {digitalWrite(pin, HIGH); digitalWrite(pin, LOW); }
Rotary r = Rotary(2,3); // sets the pins the rotary encoder uses.  Must be interrupt pins.
LiquidCrystal lcd(12, 13, 7, 6, 5, 4); // I used an odd pin combination because I need pin 2 and 3 for the interrupts.
int_fast32_t rx=000000000; // Base (starting) frequency of VFO.  This only loads once.  To force load again see ForceFreq variable below.
int_fast32_t rx2=1; // variable to hold the updated frequency
int_fast32_t increment = 1; // starting VFO update increment in HZ.
int_fast32_t iffreq = 000000000; // Intermedite Frequency - Amount to subtract (-) from base frequency. ********************************************
int buttonstate = 0;
int buttonstate2 = 0;
int GoIF = 1;
String hertz = "0.1 Hz";
int  hertzPosition = 4;
byte tenths,ones,tens,hundreds,thousands,tenthousands,hundredthousands,millions ;  //Placeholders
String freq; // string to hold the frequency
int_fast32_t timepassed = millis(); // int to hold the arduino miilis since startup
int memstatus = 1;  // value to notify if memory is current or old. 0=old, 1=current.


int ForceFreq = 0;  // Change this to 0 after you upload and run a working sketch to activate the EEPROM memory.  YOU MUST PUT THIS BACK TO 0 AND UPLOAD THE SKETCH AGAIN AFTER STARTING FREQUENCY IS SET!



void setup() {
  pinMode(A0,INPUT); // Connect to a button that goes to GND on push
  pinMode(A5,INPUT); // IF sense **********************************************
  digitalWrite(A0,HIGH);
  digitalWrite(A5,HIGH);
  lcd.begin(16, 2);
  PCICR |= (1 << PCIE2);
  PCMSK2 |= (1 << PCINT18) | (1 << PCINT19);
  sei();
  pinMode(FQ_UD, OUTPUT);
  pinMode(W_CLK, OUTPUT);
  pinMode(DATA, OUTPUT);
  pinMode(RESET, OUTPUT); 
  pulseHigh(RESET);
  pulseHigh(W_CLK);
  pulseHigh(FQ_UD);  // this pulse enables serial mode on the AD9850 - Datasheet page 12.
  lcd.setCursor(hertzPosition,1);    
  lcd.print(hertz); 
 
  // Load the stored frequency  
  if (ForceFreq == 0) {
    freq = String(EEPROM.read(0))+String(EEPROM.read(1))+String(EEPROM.read(2))+String(EEPROM.read(3))+String(EEPROM.read(4))+String(EEPROM.read(5))+String(EEPROM.read(6))+String(EEPROM.read(7));
    rx = freq.toInt();  
  }
}

void loop() {    
  // Update the display and frequency if the new Freq NEQ the old Freq  
  if (rx != rx2){    
        showFreq();
        float rxtenths = rx;
        sendFrequency(rxtenths/10);
        rx2 = rx;        
      } 
        
  // Rotate through the rate of tuning as you hold down the button
  buttonstate = digitalRead(A0);
  if(buttonstate == LOW) {
        setincrement();         
    };
    
  // Check for PIN low to drive IF offset Freq
  buttonstate = digitalRead(A5);  
    if (buttonstate != buttonstate2){
        if(buttonstate == LOW) {       
              lcd.setCursor(15,1);
              lcd.print("."); 
              GoIF = 0; 
              buttonstate2 = buttonstate; 
              sendFrequency(rx);         
          }
        else{
            lcd.setCursor(15,1);
            lcd.print(" ");
            GoIF = 1;
            buttonstate2 = buttonstate;
            sendFrequency(rx);       
          };
    };
    
    // Write the frequency to memory if not stored and 2 seconds have passed since the last frequency change.
    if(memstatus == 0){   
      if(timepassed+2000 < millis()){
        storeMEM();
        }
      }  
    
 }
 
 
// Interrupt routine to catch the rotary encoder
ISR(PCINT2_vect) {
  unsigned char result = r.process();
  if (result) {    
    if (result == DIR_CW){rx=rx+increment;}
    else {rx=rx-increment;};       
      if (rx >=2000){rx=rx2;}; // UPPER VFO LIMIT
      if (rx <=000000000){rx=rx2;}; // LOWER VFO LIMIT
  } 
}

// frequency calc from datasheet page 8 = <sys clock> * <frequency tuning word>/2^32
void sendFrequency(double frequency) {  
  if (GoIF == 1){frequency=frequency-iffreq;}; //If pin = low, subtract the IF frequency.
  int32_t freq = frequency * 4294967295/125000000;  // note 125 MHz clock on 9850.  You can make 'slight' tuning variations here by adjusting the clock frequency.
  for (int b=0; b<4; b++, freq>>=8) {
    tfr_byte(freq & 0xFF);
  }
  tfr_byte(0x000);   // Final control byte, all 0 for 9850 chip
  pulseHigh(FQ_UD);  // Done!  Should see output
}
// transfers a byte, a bit at a time, LSB first to the 9850 via serial DATA line
void tfr_byte(byte data)
{
  for (int i=0; i<8; i++, data>>=1) {
    digitalWrite(DATA, data & 0x01);
    pulseHigh(W_CLK);   //after each bit sent, CLK is pulsed high
  }
}

void setincrement(){
  if(increment == 1){increment = 10; hertz = "1  Hz"; hertzPosition=5;}
   else{increment = 1; hertz = "0.1  Hz"; hertzPosition=5;};  
   lcd.setCursor(0,1);
   lcd.print("                ");
   lcd.setCursor(hertzPosition,1); 
   lcd.print(hertz); 
   delay(250); // Adjust this delay to speed up/slow down the button menu scroll speed.
};

void showFreq(){
   millions =  int (rx/10000000);
    hundredthousands =  ((rx/1000000)%10);
    tenthousands = ((rx/100000)%10);
    thousands = ((rx/10000)%10);
    hundreds = ((rx/1000)%10);
    tens = ((rx/100)%10);
    ones = ((rx/10)%10);
    tenths = ((rx/1)%10);
    lcd.setCursor(0,0);
    lcd.print("                ");
   if (millions > 9){lcd.setCursor(1,0);}
    else{lcd.setCursor(2,0);}
   if (millions > 0){lcd.print(millions);lcd.print(",");}
    else{lcd.setCursor(4,0);}
   if (millions == 0 && hundredthousands == 0){lcd.setCursor(5,0);}
    else{lcd.print(hundredthousands);}
   if (millions == 0 && hundredthousands == 0 && tenthousands == 0){lcd.setCursor(6,0);}
    else{lcd.print(tenthousands);} 
   if (millions == 0 && hundredthousands == 0 && tenthousands == 0 && thousands == 0){lcd.setCursor(8,0);}
    else{lcd.print(thousands); lcd.print(",");} 
   if (millions == 0 && hundredthousands == 0 && tenthousands == 0 && thousands == 0 && hundreds == 0){lcd.setCursor(9,0);}
    else{lcd.print(hundreds);} 
   if (millions == 0 && hundredthousands == 0 && tenthousands == 0 && thousands == 0 && hundreds == 0 && tens == 0){lcd.setCursor(10,0);}
    else{lcd.print(tens);} 
    lcd.print(ones);
    lcd.print(".");
    lcd.print(tenths);
    lcd.print(" X1  ");  
    
     memstatus = 0; // Trigger memory write
};

void storeMEM(){
  //Write each frequency section to a EPROM slot.  Yes, it's cheating but it works!
   EEPROM.write(0,millions);
   EEPROM.write(1,hundredthousands);
   EEPROM.write(2,tenthousands);
   EEPROM.write(3,thousands);
   EEPROM.write(4,hundreds);       
   EEPROM.write(5,tens);
   EEPROM.write(6,ones);
   EEPROM.write(7,tenths);   
   memstatus = 1;  // Let program know memory has been written
};

Hi Can anyone help with the above problem please.

I think it is a code problem with the setup routine or the way it saves the frequency to memory.
:confused:

The code has several ways of doing things, bit of a mess.

Some of the calls to sendFrequency divide by 10, some don't.

There should be one call to sendFrequency, then it must be consistent.

You may hit the resolution limit of single floats in this code which is 2^-24 (1 part in 16 million).

I'd simply rewrite from scratch giving everything a good function name (for instance there is a storeMEM
but no loadMEM function).

Test each function individually.

Then combine with the business logic at the top level.

The frequency should be represented uniformly throughout the code, and only converted as needed,
you seem to maintain both a long number of tenths of a Hz, and a string representation. Store only
one, then you can never have two inconsistent versions in memory.

Hint - use String as little as possible (here char* is a better representation I think anyway, since its
a fixed field).

Hi
Thank you MarkT for your reply.

I am still trying to learn how to write the code, at the moment I am just trying to modify existing code to try and understand how it works.
Do you know of any better freely available code similar to the above.

Thanks

My advice will help you to learn how to code - the principle of splitting a problem up into small well-defined
pieces with a well-named function to do each bit is a good place to start.

For instance sendFrequency should be altered to know that the value its given is in tenths of a Hz,
all your functions should be uniform in this respect, keep everything as simple as possible (but
no simpler).

Pull out the EEPROM reading code into readMEM function perhaps?

You have an interrupt service routine (ISR) there, so you'll have to read up about them and
the things you need to get right (critical section in accessing the rx variable, for instance)

The original author is credited - that's where to ask more questions about this code I suspect.

google will find you other examples of DDS driver code. You could lookup the DDS60 project too.