Search

Title

Author

Article Body

Manager


Manager - AB7RG
Manager Notes

Have Fun with Arduino

Created by Tom Sowden, W6KAN on 2021-08-30

 

Have Fun with Arduino


Tom Sowden, W6KAN


Thinking you can “code” Arduino as a beginner can be daunting.  Diving in with both feet can be exciting and not difficult.  Arduino code is all open source and most of the heavy lifting is done by experienced code writers above most of our pay grades.  The challenge is it obtain enough knowledge to change the code (“sketch” in Arduino vernacular).  A good starting point is YouTube where there are basic instructions like the "blinking led" sketch.   Paul McWhorter has a series for beginners that is easy to follow and will give you the basics (https://www.youtube.com/watch?v=fJWR7dBuc18).  The Arduino libraries are full of all kinds of projects, many of which are for ham radio applications.  Playing around with these is a good way to improve your skills.  You can’t hurt anything so “why not” – its free! 


I wanted to update my old Heathkit SB104 by stabilizing the drift with its internal VFO.  In searching the many Arduino libraries I found Richard Visokey’s Arduino DDS project (http://www.ad7c.com/projects/ad9850-dds-vfo/).  Putting together a DDS using his "sketch" for my SB104 looked doable.  In sourcing the parts the Arduino Uno was easily the first purchase on eBay.  The Uno and needed LCD’s are also available at reasonable prices.

 

After I finished the project I wrote an article that was published by QEX. QEX is a great technical publication but has a small subscriber base. The article got very few reviews so updating it to a broader venue seemed worthwhile.  The SB104 project is a good example of using open source sketches modified to achieve a specific objective.


 
 

 

The UNO is like a small computer and can be programed to do amazing things.  Going on line at www.arduino.cc is a good starting point to get familiar with the platform.  To start off I downloaded Richard’s sketch.  One of the first lessons learned was how to "compile" the "sketch." This feature in Arduino’s software converts the code to machine language.  It can be frustrating with different “error” messages impeding the process.  Most of these are related to files missing in your library.  For example, the first lines in the Visokey sketch are:

#include <LiquidCrystal.h>
#include <rotary.h>
#include <EEPROM.h>

 

These lines tell the Arduino to find the codes in your computer’s library so they will be included in the sketch.  Libraries are necessary to take care of many routine functions.  They are downloaded with the Arduino software.  The common files like LiquidCrystal.h and EEPROM.h will be in your library.  Often others are missing so you have to find and upload them to your library.  The error messages printed out if the “compile” fails will show you the missing ones.  YouTube videos on Arduino libraries will better explain the process (https://www.youtube.com/watch?v=mROoHDmRPFs).  I found a missing file on my first compiling effort, which was “rotary.h”.  You can search for this on the internet or let me know and I will send you the file (w6kan@arrl.net).  Be sure you upload “rotary.h not “rotary.H”.  They are not the same.

 

To produce a sine wave from the digital Arduino output requires a module like the DDS 60 or the AD9850.  One of the first problems I experienced was the very low output with the AD9850.  Its peak-to-peak voltage is too low to activate the Heathkit’s mixing circuit, and will not work without adding a buffer to amplify the output.  The DDS 60 has a built in op-amp which enhances the sine wave output to a workable level. The two modules operate at different clock speeds so the sketch has to be changed as noted herein. 

 

Once you get the sketch to “compile” you can go to work changing it to fit your objective.  I use my SB104 exclusively on 40 meters so I wanted my readout on the LCD display to show the 40 meter frequency – not the actual 5MHz plus DDS output.  This required some changes I needed to make with the "sketch." 

 

 

 

DDS 60
 

The hookup for the DDS 60 is slightly different then noted on the Visokey write-up.  DDS 60 pin hookup is a follows:
Pin 1 (left starting pin 1) to Arduino pin 9 
Pin2 to Arduino pin 8
Pin 3 to Arduino pin 10
Pin 4 goes to ground
(Pin 5 is a 5vdc output) – not used unless you need 5vdc
Pin 6 is the sine wave output
Pin 7 not used
Pine 8 is the input Vcc (I recommend 9vdc)
Note..the RST is not connected. 
All other connections were not changed.

 


Richhard Visokey’s wiring diagram has the connections to the AD 9850.

 


The 9851 chip in the DDS 60 looked to be compatible with the Arduino to output a sine wave at the desired frequency.  I emailed its creator at http://midnightdesignsolutions.com/dds60/ and got an immediate response – “Hi Tom, The DDS-60 uses a 30 MHz oscillator module, and the AD9851 chip has a 6x multiplier inside it that knocks the effective sample rate up to 180 MHz  Hope this helps! 73’s, George  - N2APB”.    With this information the algorithm in the DDS sketch needed to be changed to accommodate the clock rate.  The change for the slower clock rate is noted below.
// frequency calc from datasheet page 8 = <sys clock> * <frequency tuning word>/2^32
void sendFrequency(double frequency) {
  int32_t freq = frequency * 4294967295 / 30000000; // note the 30 MHz or clock rate of the DDS 60 -  it was 125000000 for the 9850.

 

The finished DDS – from left to right the LCD display, the Arduino board, and the DDS 60.  The voltage control chip is mounted on the back panel. 

 

The built in buffer in the DDS 60 made a huge difference and the Heathkit came to life with the higher injection voltage.  The challenge now was how to adjust the LCD readout to show the “mixed” (40 meter) frequency and not the VFO.  The crystal used in the SB104 IF for 40 meters is 12,500 kHz.  Getting the readout on the Arduino was not necessary since the Heathkit has a counter built in and shows the actual frequency.  However the display only goes to 100Hz.  Playing around with the sketch I came up with the following change:  

delay (500); // this was inserted as ½ second pause so I could momentarily see the VFO frequency (5+MHz).
 
  lcd.clear(); // clears the LCD display of previous inputs
 
  lcd.setCursor (7,0);  // this sets the cursor on line one in the center??
 
  lcd.print (1250290 - rx); //this was my solution to see the 40M frequency to the cycle (see text*)
  lcd.setCursor (7,1);//this puts  the step frequency in the middle of the LCD and calculates the 40M frequency.
}

While the 40 meter IF crystal in the SB1o4 was cut for 12,500 kHz it changed over the years.  Also it drifts until the radio warms up.  By experimenting I finally got an accurate readout to display using 12,502,290.  Note the “rx” is the DDS output (5MHz+).  The code calculates the difference and prints out the 40 meter frequency to the cycle.  The “step” frequency is shown below the 40M readout.  By pushing in the encoder it changes.

 

Don’t be hesitant to make a change.  If you save a copy of the sketch you know is good you can always reload and feed it to the Arduino.  There is no downside to experimenting.  An infinite amount of help is out there so you are never alone in doing you I have found that using Google to find sketch solutions works great.  Note the two readouts – the Heathkit in the lower picture and the Arduino above in Hz.  The step is set at 100Hz, as shown. 

 


The Arduino Readout
 

 

My modified sketch is shown in Exhibit A (next page).  You can copy this and paste it into the Arduino program.  It should compile if you have the correct “rotary.h” file in your library.  Please email me with any questions or problems you have (w6kan@arrl.net).
Exhibit A

// 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 10   // Pin 10 - connect to serial data load pin (DATA)
#define RESET 11  // Pin 11 - 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 = 5336290; //starting frequency.
int_fast32_t rx2 = 1; // variable to hold the updated frequency
int_fast32_t increment = 10; // starting VFO update increment in HZ.
int buttonstate = 0;
String hertz = "10 Hz";
int  hertzPosition = 5;
byte 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 = 1;  // 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
  digitalWrite(A0, 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));
    rx = freq.toInt();
  }
}


void loop() {
  if (rx != rx2) {
    showFreq();
    sendFrequency(rx);
    rx2 = rx;
  }

  buttonstate = digitalRead(A0);
  if (buttonstate == LOW) {
    setincrement();
  };

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


ISR(PCINT2_vect) {
  unsigned char result = r.process();
  if (result) {
    if (result == DIR_CW) {
      rx = rx + increment;
    }
    else {
      rx = rx - increment;
    };
    if (rx >= 30000000) {
      rx = rx2;
    }; // UPPER VFO LIMIT
    if (rx <= 1000000) {
      rx = rx2;
    }; // LOWER VFO LIMIT
  }
}

 

// frequency calc from datasheet page 8 = <sys clock> * <frequency tuning word>/2^32
void sendFrequency(double frequency) {
  int32_t freq = frequency * 4294967295/30000000; // 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 == 10) {
    increment = 50;
    hertz = "50 Hz";
    hertzPosition = 5;
  }
  else if (increment == 50) {
    increment = 100;
    hertz = "100 Hz";
    hertzPosition = 4;
  }
  else if (increment == 100) {
    increment = 500;
    hertz = "500 Hz";
    hertzPosition = 4;
  }
  else if (increment == 500) {
    increment = 1000;
    hertz = "1 Khz";
    hertzPosition = 6;
  }
  else if (increment == 1000) {
    increment = 2500;
    hertz = "2.5 Khz";
    hertzPosition = 4;
  }
  else if (increment == 2500) {
    increment = 5000;
    hertz = "5 Khz";
    hertzPosition = 6;
  }
  else if (increment == 5000) {
    increment = 10000;
    hertz = "10 Khz";
    hertzPosition = 5;
  }
  else if (increment == 10000) {
    increment = 100000;
    hertz = "100 Khz";
    hertzPosition = 4;
  }
  else if (increment == 100000) {
    increment = 1000000;
    hertz = "1 Mhz";
    hertzPosition = 6;
  }
  else {
    increment = 10;
    hertz = "10 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 / 1000000);
  hundredthousands = ((rx / 100000) % 10);
  tenthousands = ((rx / 10000) % 10);
  thousands = ((rx / 1000) % 10);
  hundreds = ((rx / 100) % 10);
  tens = ((rx / 10) % 10);
  ones = ((rx / 1) % 10);
  lcd.setCursor(0, 0);
  lcd.print("                ");
  if (millions > 9) {
    lcd.setCursor(1, 0);
  }
  else {
    lcd.setCursor(2, 0);
  }
  lcd.print(millions);
  lcd.print(".");
  lcd.print(hundredthousands);
  lcd.print(tenthousands);
  lcd.print(thousands);
  lcd.print(".");
  lcd.print(hundreds);
  lcd.print(tens);
  lcd.print(ones);
  lcd.print(" Mhz  ");
  timepassed = millis();
  memstatus = 0; // Trigger memory write
  delay (500);

  lcd.clear();

  lcd.setCursor (5,0);

  lcd.print (12502000 - rx);
 
  lcd.setCursor (7,1);
  lcd.print(hertz);
};

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);
  memstatus = 1;  // Let program know memory has been written
};

 

 

N3OWM2021-10-04
Have Fun with Arduino
Hi, great write up!! I have been trying understand the projects yours is the first one I have seen with wiring diagrams. Turned the light on (brain light) Now some of this is making sense! I have the MEGA 2560 kit.
KT4WO2021-09-07
Re: Have Fun with Arduino
Forgot to add.
I did a project like the one above with the Ebay
9850 board. It worked well but the "Super DDS"
was "neater" than my point to point mess...haha
Reply to a comment by : KT4WO on 2021-09-07

I did a project like this for my DRIFTY Kenwood TS-180S. I used this board: https://www.pongrance.com/DDS2018.html Before I could not have a SSB QSO without keeping my hand on the VFO, now it's set and forget! I tried a "Huff Puff" board and it worked OK on voice but the 10hz jumps every few sec. just killed any digi-mode decode(PSK,etc) A DDS VFO project is worth the time for older rigs.
KT4WO2021-09-07
Have Fun with Arduino
I did a project like this for my DRIFTY Kenwood TS-180S.
I used this board:

https://www.pongrance.com/DDS2018.html

Before I could not have a SSB QSO without keeping my
hand on the VFO, now it's set and forget!

I tried a "Huff Puff" board and it worked OK on voice but
the 10hz jumps every few sec. just killed any digi-mode
decode(PSK,etc)

A DDS VFO project is worth the time for older rigs.