And the schematic and code.
/*
* AD9850 based frequency generator using a rotary encoder for frequency tuning.
*
* Based on AD9851 DDS sketch by Andrew Smallbone at [url=http://www.rocketnumbernine.com]www.rocketnumbernine.com[/url]
*
* Author: Tim Sinaeve
* Mods by JdeV
* 26-03-2024
* Arduino Nano usnig "old bootloader"
*/
#include <LiquidCrystal.h>
#include "avdweb_Switch.h" // [url]https://github.com/avandalen/avdweb_Switch[/url]
// Pin definitions
const byte ENCODER_BUTTON_PIN = 0;
const byte ENCODER_A_PIN = 2;
const byte ENCODER_B_PIN = 3;
// Objects
Switch encButton = Switch(ENCODER_BUTTON_PIN);
// RS En D4 D5 D6 D7
LiquidCrystal lcd(12, 13, 10, 1, 4, 5);
int phasePin = A0;
const byte SELECTOR_SW = 11;
int currentStateSW;
const byte AD9850_WORD_LOAD_CLOCK_PIN1 = A1; // (CLK)
const byte AD9850_FREQUENCY_UPDATE_PIN1 = A2; // (FQ)
const byte AD9850_SERIAL_DATA_LOAD_PIN1 = A3; // (DATA)
const byte AD9850_RESET_PIN1 = A4; // (RST)
const byte AD9850_WORD_LOAD_CLOCK_PIN2 = 6; // (CLK)
const byte AD9850_FREQUENCY_UPDATE_PIN2 = 7; // (FQ)
const byte AD9850_SERIAL_DATA_LOAD_PIN2 = 8; // (DATA)
const byte AD9850_RESET_PIN2 = 9; // (RST)
const unsigned long AD9850_CLOCK_FREQUENCY1 = 125000000;
const unsigned long AD9850_CLOCK_FREQUENCY2 = 125000000;
uint8_t adPhase1 = 0; // Phase output in increments of 180°, 90°, 45°, 22.5°, 11.25° and any combination thereof.
uint8_t adPhase2 = 0; // just a place holder
// Settings
const unsigned long MIN_FREQ1 = 0;
const unsigned long MAX_FREQ1 = 21000000; // max 20MHz
const unsigned long MAX_DELTA1 = 10000000;
const unsigned long DEFAULT_DELTA1 = 1;
const unsigned long MIN_FREQ2 = 0;
const unsigned long MAX_FREQ2 = 21000000; // max 20MHz
const unsigned long MAX_DELTA2 = 10000000;
const unsigned long DEFAULT_DELTA2 = 1;
unsigned long delta1 = 1;
unsigned long freq1 = 0;
unsigned long reading1 = 0;
unsigned long delta2 = 1;
unsigned long freq2 = 0;
unsigned long reading2 = 0;
// Timing for polling the encoder
unsigned long lastTime1;
unsigned long currentTime1;
unsigned long lastTime2;
unsigned long currentTime2;
// Storing the readings
boolean encA;
boolean encB;
//boolean encButton1;
boolean lastA = false;
void setup() {
pulse(AD9850_RESET_PIN1, AD9850_RESET_PIN2);
// configure arduino data pins for output AD9850_1
pinMode(AD9850_FREQUENCY_UPDATE_PIN1, OUTPUT);
pinMode(AD9850_WORD_LOAD_CLOCK_PIN1, OUTPUT);
pinMode(AD9850_SERIAL_DATA_LOAD_PIN1, OUTPUT);
pinMode(AD9850_RESET_PIN1, OUTPUT);
// configure arduino data pins for output AD9850_2
pinMode(AD9850_FREQUENCY_UPDATE_PIN2, OUTPUT);
pinMode(AD9850_WORD_LOAD_CLOCK_PIN2, OUTPUT);
pinMode(AD9850_SERIAL_DATA_LOAD_PIN2, OUTPUT);
pinMode(AD9850_RESET_PIN2, OUTPUT);
// set the two pins as inputs with internal pullups
pinMode(ENCODER_A_PIN, INPUT_PULLUP);
pinMode(ENCODER_B_PIN, INPUT_PULLUP);
// Tot select between 2 AD9850s
pinMode(SELECTOR_SW, INPUT_PULLUP);
// setup AD9850 module 1
pulseHigh1(AD9850_RESET_PIN1);
pulseHigh1(AD9850_WORD_LOAD_CLOCK_PIN1);
pulseHigh1(AD9850_FREQUENCY_UPDATE_PIN1);
// setup AD9850 module 2
pulseHigh2(AD9850_RESET_PIN2);
pulseHigh2(AD9850_WORD_LOAD_CLOCK_PIN2);
pulseHigh2(AD9850_FREQUENCY_UPDATE_PIN2);
lcd.begin(16, 2);
lcd.setCursor(1, 0);
lcd.print("AD9850 SIG GEN");
lcd.setCursor(3 , 1);
lcd.print("DUAL 10MHz");
delay(2000);
lcd.clear();
lcd.print("Enter Freq:");
lcd.setCursor(3, 1);
lcd.print("0000000 Hz");
// Set up the timing of the polling
currentTime1 = millis();
lastTime1 = currentTime1;
currentTime2 = millis();
lastTime2 = currentTime2;
// Serial.begin(115200);
} //setup()
void loop() {
// Phase angle adjustable in 11.25° steps only on channel 1.
int adPhase_x = analogRead(phasePin);
if ((adPhase_x <= 1024) && (adPhase_x >= 970)){ adPhase1 = 180;}
if ((adPhase_x <= 969) && (adPhase_x >= 909)){ adPhase1 = 168.75;}
if ((adPhase_x <= 908) && (adPhase_x >= 848)){ adPhase1 = 157.5;}
if ((adPhase_x <= 847)&& (adPhase_x >= 787)){ adPhase1 = 146.25;}
if ((adPhase_x <= 786) && (adPhase_x >= 726)){ adPhase1 = 135;}
if ((adPhase_x <= 725) && (adPhase_x >= 665)){ adPhase1 = 123.75;}
if ((adPhase_x <= 664) && (adPhase_x >= 604)){ adPhase1 = 112.5;}
if ((adPhase_x <= 603) && (adPhase_x >= 543)){ adPhase1 = 101.25;}
if ((adPhase_x <= 542) && (adPhase_x >= 482)){ adPhase1 = 90;}
if ((adPhase_x <= 481)&& (adPhase_x >= 421)){ adPhase1 = 78.75;}
if ((adPhase_x <= 420) && (adPhase_x >= 360)){ adPhase1 = 67.5;}
if ((adPhase_x <= 359) && (adPhase_x >= 299)){ adPhase1 = 56.25;}
if ((adPhase_x <= 298) && (adPhase_x >= 238)){ adPhase1 = 45;}
if ((adPhase_x <= 237)&& (adPhase_x >= 177)){ adPhase1 = 33.75;}
if ((adPhase_x <= 176) && (adPhase_x >= 116)){ adPhase1 = 22.5;}
if ((adPhase_x <= 115) && (adPhase_x >= 55)){ adPhase1 = 11.25;}
if ((adPhase_x <= 54) && (adPhase_x >= 0)){ adPhase1 = 0;}
sendFrequency1(freq1,adPhase1); // Send it here to update phase angle adjustments.
/****** AD9850 1 or 2 selected--******/
currentStateSW = digitalRead(SELECTOR_SW);
if (currentStateSW == HIGH) {
if (readEncoderValue1()) {
lcd.setCursor(15, 1); // Top Line for AD9850-1
lcd.print(" ");
lcd.setCursor(15, 0);
lcd.print((char)127); // (char)127 = ASCII <-
freq1 = reading1;
char s[16];
lcd.setCursor(0, 0);
if (freq1 < 1000) {
sprintf(s, "%10ld Hz ", freq1);
}
if ((freq1 >= 1000) && (freq1 < 1000000)) {
sprintf(s, "%10ld KHz", freq1);
}
if (freq1 >= 1000000) {
sprintf(s, "%10ld MHz", freq1);
}
lcd.setCursor(0, 0);
lcd.print(freq1);
lcd.setCursor(0, 0);
lcd.print(s);
int p = int(log10(delta1));
lcd.setCursor(9 - p, 0);
lcd.cursor();
sendFrequency1(freq1,adPhase1);
}
} else {
if (readEncoderValue2()) {
lcd.setCursor(15, 0); // Bottom Line for AD9850-2
lcd.print(" ");
lcd.setCursor(15, 1);
lcd.print((char)127); // (char)127 = ASCII <-
freq2 = reading2;
char s[16];
lcd.setCursor(0, 1);
if (freq2 < 1000) {
sprintf(s, "%10ld Hz ", freq2);
}
if ((freq2 >= 1000) && (freq2 < 1000000)) {
sprintf(s, "%10ld KHz", freq2);
}
if (freq2 >= 1000000) {
sprintf(s, "%10ld MHz", freq2);
}
lcd.setCursor(0, 2);
lcd.print(freq2);
lcd.setCursor(0, 1);
lcd.print(s);
int p = int(log10(delta2));
lcd.setCursor(9 - p, 1);
sendFrequency2(freq2);
}
}
} //Loop()
/*********************************************************/
// frequency calc from datasheet page 8 = <sys clock> * <frequency tuning word>/2^32
void sendFrequency1(double frequency1, uint8_t p1) { // function gets frequency and phase
int32_t f1 = frequency1 * 4294967296.0 / AD9850_CLOCK_FREQUENCY1; // calculation for value to send
adPhase1 = p1 << 3; // bitshift for phase
for (int i=0; i<4; i++, f1>>=8) { // shift out the double (=4bytes) via DDS-pins
shiftOut(AD9850_SERIAL_DATA_LOAD_PIN1, AD9850_WORD_LOAD_CLOCK_PIN1, LSBFIRST, f1 & 0xFF);
}
shiftOut(AD9850_SERIAL_DATA_LOAD_PIN1, AD9850_WORD_LOAD_CLOCK_PIN1, LSBFIRST, adPhase1 & 0xFF); // shift out phase-value
pulseHigh1(AD9850_FREQUENCY_UPDATE_PIN1);
} // sendFrequency1)
/*********************************************************/
void pulseHigh1(byte pin1) {
digitalWrite(pin1, HIGH);
digitalWrite(pin1, LOW);
} //pulseHigh1()
/*********************************************************/
// frequency calc from datasheet page 8 = <sys clock> * <frequency tuning word>/2^32
void sendFrequency2(double frequency2) {
int32_t f2 = frequency2 * 4294967295 / AD9850_CLOCK_FREQUENCY2;
for (int b2 = 0; b2 < 4; b2++, f2 >>= 8) {
shiftOut(AD9850_SERIAL_DATA_LOAD_PIN2, AD9850_WORD_LOAD_CLOCK_PIN2, LSBFIRST, f2 & 0xFF);
}
shiftOut(AD9850_SERIAL_DATA_LOAD_PIN2, AD9850_WORD_LOAD_CLOCK_PIN2, adPhase2, LSBFIRST & 0xFF); // shift out phase-value
pulseHigh2(AD9850_FREQUENCY_UPDATE_PIN2);
} //sendFrequency2()
/*********************************************************/
void pulseHigh2(byte pin2) {
digitalWrite(pin2, HIGH);
digitalWrite(pin2, LOW);
} //pulseHigh2()
/*********************************************************/
boolean readEncoderValue1() {
boolean b = false;
if (encButton.longPress()) {
reading1 = 1000;
delta1 = 100;
b = true;
}
if (encButton.doubleClick()) {
reading1 = MIN_FREQ1;
delta1 = 1;
b = true;
}
if (encButton.poll()) {
if (encButton.on()) {
delta1 *= 10;
if (delta1 == MAX_DELTA1)
delta1 = 1;
b = true;
}
}
// read elapsed time
currentTime1 = millis();
if (currentTime1 >= (lastTime1 + 5)) {
// read encoder movement
encA = digitalRead(ENCODER_A_PIN);
encB = digitalRead(ENCODER_B_PIN);
// check if A has gone from high to low
if ((!encA) && (lastA)) {
// check if B is high
if (encB) {
// clockwise
if (reading1 + delta1 <= MAX_FREQ1) {
reading1 = reading1 + delta1;
}
} else {
// anti-clockwise
if ((delta1 = 10000000) && (reading1 > delta1)) {
reading1 = reading1;
} else if ((delta1 = 1000000) && (reading1 > delta1)) {
reading1 = reading1;
} else if ((delta1 = 100000) && (reading1 > delta1)) {
reading1 = reading1;
} else if ((delta1 = 10000) && (reading1 > delta1)) {
reading1 = reading1;
} else if ((delta1 = 1000) && (reading1 > delta1)) {
reading1 = reading1;
} else if ((delta1 = 100) && (reading1 > delta1)) {
reading1 = reading1;
} else if ((delta1 = 10) && (reading1 > delta1)) {
reading1 = reading1;
} else if ((reading1 = 1) && (delta1 = 1)) {
reading1 = 1;
}
if (reading1 - delta1 >= MIN_FREQ1) {
reading1 = reading1 - delta1;
} else {
reading1 = 0;
}
}
}
// store reading of A and millis for next loop
lastA = encA;
lastTime1 = currentTime1;
}
b = b || (freq1 != reading1);
return b;
} //readEncoderValue1()
/*********************************************************/
boolean readEncoderValue2() {
boolean b2 = false;
if (encButton.longPress()) {
reading2 = 1000;
delta2 = 100;
b2 = true;
}
if (encButton.doubleClick()) {
reading2 = MIN_FREQ2;
delta2 = 1;
b2 = true;
}
if (encButton.poll()) {
if (encButton.on()) {
delta2 *= 10;
if (delta2 == MAX_DELTA2)
delta2 = 1;
b2 = true;
}
}
// read elapsed time
currentTime2 = millis();
if (currentTime2 >= (lastTime2 + 5)) {
// read encoder movement
encA = digitalRead(ENCODER_A_PIN);
encB = digitalRead(ENCODER_B_PIN);
// check if A has gone from high to low
if ((!encA) && (lastA)) {
// check if B is high
if (encB) {
// clockwise
if (reading2 + delta2 <= MAX_FREQ2) {
reading2 = reading2 + delta2;
}
} else {
// anti-clockwise
if ((delta2 = 10000000) && (reading2 > delta2)) {
reading2 = reading2;
} else if ((delta2 = 1000000) && (reading2 > delta2)) {
reading2 = reading2;
} else if ((delta2 = 100000) && (reading2 > delta2)) {
reading2 = reading2;
} else if ((delta2 = 10000) && (reading2 > delta2)) {
reading2 = reading2;
} else if ((delta2 = 1000) && (reading2 > delta2)) {
reading2 = reading2;
} else if ((delta2 = 100) && (reading2 > delta2)) {
reading2 = reading2;
} else if ((delta2 = 10) && (reading2 > delta2)) {
reading2 = reading2;
} else if ((reading2 = 1) && (delta2 = 1)) {
reading2 = 1;
}
if (reading2 - delta2 >= MIN_FREQ2) {
reading2 = reading2 - delta2;
} else {
reading2 = 0;
}
}
}
// store reading of A and millis for next loop
lastA = encA;
lastTime2 = currentTime2;
}
b2 = b2 || (freq2 != reading2);
return b2;
} //readEncoderValue2()
void pulse(int pin1, int pin2){
digitalWrite(pin1, HIGH);
digitalWrite(pin2, HIGH);
digitalWrite(pin1, LOW);
digitalWrite(pin2, LOW);
digitalWrite(pin1, HIGH);
digitalWrite(pin2, HIGH);
digitalWrite(pin1, LOW);
digitalWrite(pin2, LOW);
}
// END