The DDS has two modes, SDR & VFO. It is tuned in programable steps (at the moment 10kHz for SDR & 100Hz for VFO) . The shield RF Bus outputs VFO and I & Q which are derived like this:
freq = indicated frequency on display D5 HIGH SDR mode freq x4 clk0 = VFO x4 output freq x4 clk1 = Johnson /4 = I & Q x1 outputs D5 LOW VFO mode freq x1 clk0 = VFO output
The input for tuning is a rotary encoder. An LCD display shows the band and the tuned frequency.
Three bands are generated, 40, 30, & 20m. These are selected in turn by the rotary encoder push button. Selecting a new band tunes a preset band frequency.
The 5 connections across the left are for the Rotary Encoder and its Button and 4 for the I2C LCD display. This is documented in the source code.
The schematic diagram is:
Software
The software is a "Universal VFO" this software uses the new "Si5351.h" library from NT7S and implements a VFO switchable to 40, 30, & 20m bands. At the top of the code are defined constants, SDR & VFO, which determine the output frequency, x4 or x1. This value is set to SDR if you want an output x4 the indicated frequency on the VFO output, and a x4 drive to the onboard CD74ACT74 Johnson counter to generate I & Q signals. The VFO output in SDR mode can be used, for example, for the Softrock Lite SDR board. In VFO mode it changes to a single x1 output on the VFO RF bus pin.
The mode, SDR or VFO, is selected by a High or Low on pin D5, selected at start-up by the red jumper link. The constant STEP4 or STEP1 determine the step of the tuning, which can be, for example, 10kHz for driving an SDR, or 100Hz for a direct tuned radio.
The board thus has a VFO output which can be x1 or x4 the indicated frequency. When the hardware /4 Johnson counter is implemented and used, then two further outputs I & Q, provide outputs with the Si5351 running at x4 the frequency displayed. The VFO output is from CLK0, the IQ drive is from CLK1. Board outputs VFO, I & Q are on the RF bus (see previous post below).
Thus this design, with small changes in the software can satisfy diverse applications. Here's the code:
// Universal VFO, outputs for 40-30-20m
// V0.9
// Function
// Generates VHF & IQ outputs.
// Si 5351 output is x1 or x4 displayed frequency.
// Frequency selected in programmable steps by rotary encoder. Button changes bands 40-30-20m
// Change of band sets new frequency to band
// LCD displays frequency in kHz and band
// -----
// Shield has RF bus with VFO, I & Q outputs
// Two modes, set by link (pin D5):
// SDR (D5 HIGH) outputs x4 from clk0 to VFO, & x1 quadrature to I & Q. 10kHz steps
// VFO (D5 LOW) outputs x1 on clk0 to VFO out, 100Hz steps
// -----
// DDS I2C SI5351
// SCL = A5
// SDA = A4
// I2C address 0x60
// ------
// display I2C LCD 16 * 2
// o A5 SCL (y)
// o A4 SDA (or)
// o +5 (r)
// o GND (bwn)
// I2C address 0x27
// -----
// encoder KY-040
// o D2 DT (y)
// o D3 CLK (g)
// o D4 SW (or)
// o +5 (r)
// o GND (bwn)
//-----
// D12-D13 TX-RX enable
// D10-D11 band switch relays 40-30-20
// -----
// I2C, Si5351, LCD and Rotary Encoder libraries
#include "Wire.h"
#include "si5351.h"
#include "LiquidCrystal_I2C.h"
#include "Rotary.h"
// XTAL freq, PLL multiplier, outputs 0-2, IQ Mult, steps (Hz)
#define XTAL 25
#define MULT 36
#define SDR 4
#define VFO 1
#define STEP4 10000
#define STEP1 100
// SDR/VFO link pin 5, button pin 4, Rotary Encoder pins 2 & 3 (A & B)
#define LINK 5
#define BUTTON 4
#define ROTA 2
#define ROTB 3
// dds object
Si5351 dds;
// lcd object
LiquidCrystal_I2C lcd(0x27, 16, 2);
// Rotary Encoder object
Rotary rot = Rotary(ROTA, ROTB);
// start frequencies (Hz)
float bandStart[3] = {
7000000, 10050000, 14000000};
char bandName[3][4] = {
"40m", "30m", "20m"}; // band 0 = 40m, 1 = 30m, 2 = 20m
// band & freq, freq steps, mode
byte band;
float freq;
float steps;
byte mode;
void setup()
{
// encoder, button, link, relays & TXRX pin config
pinMode(ROTA, INPUT_PULLUP);
pinMode(ROTB, INPUT_PULLUP);
pinMode(BUTTON, INPUT_PULLUP);
pinMode(LINK, INPUT_PULLUP); // default input = HIGH
mode = digitalRead(LINK); // HIGH (default) = SDR, LOW = VFO
if(mode == HIGH) steps = STEP4; // set steps STEP4 (SDR) or STEP1 (VFO)
else steps = STEP1;
dds.init(SI5351_CRYSTAL_LOAD_8PF); // init si5351
lcd.init(); // init LCD & backlight
lcd.backlight();
// startup band & frequency
band = 0; // 40m
freq = bandStart[band];
freqOut(freq);
dispMenu(); // display menu
dispFreq(freq); // display freq
dispBand(bandName[band]); // display band
}
void loop()
{
unsigned char dir; // tuning direction CW/CCW
byte step;
if(digitalRead(BUTTON) == LOW) // button pressed?
{
while(!digitalRead(BUTTON)); // wait for release
if(band == 2) band = 0; // go around
else band++;
freq = bandStart[band]; // set centre freq of new band
freqOut(freq);
dispFreq(freq); // update freq & band display
dispBand(bandName[band]);
}
dir = rot.process(); // read encoder
if(dir != DIR_NONE) // turned?
{
if(dir == DIR_CW) freq += steps; // increment +/- STEP
if(dir == DIR_CCW) freq -= steps;
freqOut(freq);
dispFreq(freq); // update freq display
}
}
// freq out x IQ
void freqOut(float freq)
{
if(mode == HIGH) // SDR mode
{
dds.set_freq(freq * SDR, 0, SI5351_CLK0); // VFO out, x4
dds.set_freq(freq * SDR, 1, SI5351_CLK1); // CD74AC74 quadrature to I & Q
}
else
{
dds.set_freq(freq * VFO, 0, SI5351_CLK0); // direct VFO out, x1
}
}
// display menu
void dispMenu()
{
lcd.setCursor(1, 0); // display caption
if(mode == HIGH) lcd.print("SDR");
else lcd.print("VFO");
lcd.setCursor(1, 1);
lcd.print("Band");
}
// display freq
void dispFreq(float f)
{
float pf;
pf = f /1000; // kHz
lcd.setCursor(5, 0);
lcd.print(" "); // clear last freq display
if(pf < 9999.9) lcd.setCursor(6, 0);
else lcd.setCursor(5, 0);
lcd.print(pf, 1);
lcd.setCursor(13, 0);
lcd.print("kHz");
}
// display band
void dispBand(char b[])
{
lcd.setCursor(5, 1);
lcd.print(" "); // clear last band display
lcd.print(b);
}
No comments:
Post a Comment