/*
This sketch will setup an Arduino (Nano in my example)
with the an I2C 20x4 LCD and a AD9851 DDS module (from ebay)
and a rotary encoder.
The encoder can be spun from 1.8MHz to 30MHz.
When you push the encoder push button it will allow to
change the decade position of the number being changed/tuned.
This code is heavily commented so that you can try to
understand what is going on.
This tries to avoid the use of the delay command since that is
a blocking statement. Use of the millis function and variable
checking as a timer are used instead if/when needed.
The base sketch takes about 8k of memory.
This could be easily used a the builing block for a HF radio.
-------------------
DDS AD9850/AD9850 info from:
Mike Bowthorpe
http://www.ladyada.net/rant/2007/02/cotw-ltc6903/
http://www.geocities.com/leon_heller/dds.html
Function for sending the byte word by Peter Marks http://marxy.org
---Encoder info---
https://forum.sparkfun.com/viewtopic.php?p=65052
http://hifiduino.files.wordpress.com/2010/10/rotarynodebounce.jpg
read a rotary encoder with interrupts
Encoder hooked up with common to GROUND,
Encoder Pin A to pin 2,
Encoder Pin B to pin 4 (or pin 3 see below)
uses Arduino pullups on A & B channel outputs
turning on the pullups saves having to hook up resistors
to the A & B channel outputs
---I2C LCD INFO---
http://arduino-info.wikispaces.com/LCD-Blue-I2C
YWROBOT
LCD header pins/cable color code
VCC = Red -- 5v
GND = Black -- Gnd
SDA = Yellow -- A4
SCL = Green/White -- A5
*/
//---Libraries
#include <Wire.h> // I2C library
#include <LiquidCrystal_I2C.h> // I2C LCD library
LiquidCrystal_I2C lcd(0x27,20,4); // set the LCD address to 0x27 for a 20 chars and 4 line display
//--Define constants
#define encoder0PinA 2 // Setup the encoder pins
#define encoder0PinB 4 // Setup the encoder pins
#define encoderbtn 6 // encoder pushbutton
#define DDS_CLOCK 180000000 // 30MHz x 6 (onboard rock)
//---define variables
volatile long encoder0Pos = 10000000; // setup a value to count with
volatile long oldencoder0Pos = encoder0Pos ; // used to compare
int varVal; // variable for reading the pin status
int varHz = 1; // 1=Hz, 2=KHz, 3=10KHz, 4=100KHz, 5=MHz tuning step mode
int long varMult = 1; // used to plug in as the multiplier
char varBuf1[8]; // used to convert an int to a string array for the freq display
char varBuf2[10]; // used to format the freq display with commas
unsigned long varCurrentMillis; // current time via the Millis function
long varPreviousMillis = 0; // used as a timer to avoid bounce
long varInterval = 500; // interval used with a timer (milliseconds)
byte ddsLOAD = 8; // AD9851 LOAD Arduino D8
byte ddsCLOCK = 9; // AD9851 CLOCK Arduino D9
byte ddsDATA = 10; // AD9851 FQ_UD Ardunio D10
long varTuning_word; // Used to hold the word for the DDS
void setup() {
//---Setup encoder
pinMode(encoderbtn, INPUT);
pinMode(encoder0PinA, INPUT);
digitalWrite(encoder0PinA, HIGH); // turn on pullup resistor
pinMode(encoder0PinB, INPUT);
digitalWrite(encoder0PinB, HIGH); // turn on pullup resistor
attachInterrupt(0, doEncoder, CHANGE); // encoder pin on interrupt 0 - pin 2
//---Initial screen setup
lcd.init(); // initialize the lcd
lcd.backlight(); // turn on the lcd backlight
lcd.clear(); // good practice to make sure that display is clear
lcd.setCursor(0,0); // column,row
lcd.print("Freq: ");
sprintf(varBuf1,"%8lu", encoder0Pos); // convert initial freq for display
sprintf(varBuf2,"%1c%1c,%1c%1c%1c,%1c%1c%1c", varBuf1[0], varBuf1[1], varBuf1[2], varBuf1[3], varBuf1[4], varBuf1[5], varBuf1[6], varBuf1[7], varBuf1[8]);
lcd.print(varBuf2);
lcd.setCursor(18,0); // column,row
lcd.print("Hz"); // prints out step; should variablize
//---setup pins for AD9851
pinMode (ddsDATA, OUTPUT); // sets pin 10 as OUPUT
pinMode (ddsCLOCK, OUTPUT); // sets pin 9 as OUTPUT
pinMode (ddsLOAD, OUTPUT); // sets pin 8 as OUTPUT
sendFrequency(encoder0Pos); // send the initial freq to the DDS
}
void loop(){
encoderStatus(); // check for encoder button press
lcdStatus(); // function for lcd updates
if (encoder0Pos != oldencoder0Pos) // only update the DDS if the freq has changed
sendFrequency(encoder0Pos); // function to update the DDS
}
void sendFrequency(long encoder0Pos){ // function to update the DDS
varTuning_word = (encoder0Pos * pow(2, 32)) / DDS_CLOCK; // set value for the DDS
digitalWrite (ddsLOAD, LOW); // take load pin low
//--start loop--
for(int i = 0; i < 32; i++) // loop through the bits
{
if ((varTuning_word & 1) == 1) // test for binary 1
outOne(); // function to send 1 serial
else
outZero(); // function to send 0 serial
varTuning_word = varTuning_word >> 1;
}
//--end loop--
byte_out(0x09); // send the end command
digitalWrite (ddsLOAD, HIGH); // take the load pin high
}
void byte_out(unsigned char byte) // spin through a byte (8 bits)
{ // send it a bit a time
int i;
for (i = 0; i < 8; i++)
{
if ((byte & 1) == 1)
outOne();
else
outZero();
byte = byte >> 1;
}
}
void outOne(){ // send 1 to the DDS
digitalWrite(ddsCLOCK, LOW); // set the ddsCLOCK pin low
digitalWrite(ddsDATA, HIGH); // set the ddsDATA pin high
digitalWrite(ddsCLOCK, HIGH); // set the ddsCLOCK pin HIGH
digitalWrite(ddsDATA, LOW); // set the ddsDATA ping low
}
void outZero(){ // send 0 to the DDS
digitalWrite(ddsCLOCK, LOW); // set the ddsCLOCK pin low
digitalWrite(ddsDATA, LOW); // set the ddsDATA pin low
digitalWrite(ddsCLOCK, HIGH); // set the ddsCLOCK pin high
}
void encoderStatus (){ // this is checking for the button press and setting the MHZ, KHz, Hz cursor
varCurrentMillis = millis(); // set variable = time
if(varCurrentMillis - varPreviousMillis > varInterval) {
varPreviousMillis = varCurrentMillis;
varVal = digitalRead(encoderbtn); // read input value and store it in val
if (varVal == LOW) { // check if the button is pressed
if (varHz == 6) { // 1 Hz, 2 100Hz, 3 KHz, 4 10KHz, 5 100KHz, 6 MHz
varHz = 0; // reset from MHz to Hz
}
varHz = varHz + 1; // move one position
}
}
}
void lcdStatus (){
sprintf(varBuf1,"%8lu", encoder0Pos);
sprintf(varBuf2,"%1c%1c,%1c%1c%1c,%1c%1c%1c", varBuf1[0], varBuf1[1], varBuf1[2], varBuf1[3], varBuf1[4], varBuf1[5], varBuf1[6], varBuf1[7], varBuf1[8]);
if (encoder0Pos != oldencoder0Pos){
lcd.setCursor(6,0); // column,row
lcd.print(varBuf2); // prints out the freq
}
switch (varHz) {
case 1: // if 1 move the cursor to Hz
lcd.setCursor(18,0); // column,row
lcd.print("H1"); // prints out step
varMult = 1;
break;
case 2: // if 2 move the cursor to 100 Hz
lcd.setCursor(18,0); // column,row
lcd.print("H2"); // prints out step
//varMult = 1000/2; // original encoder with detents
varMult = 100; // AA0ZZ encoder, no detents
break;
case 3: // if 2 move the cursor to KHz
lcd.setCursor(18,0); // column,row
lcd.print("K1"); // prints out step
//varMult = 1000/2; // original encoder with detents
varMult = 1000; // AA0ZZ encoder, no detents
break;
case 4: // if 3 move the cursor to 10KHz
lcd.setCursor(18,0); // column,row
lcd.print("K2"); // prints out step
//varMult = 10000/2; // original encoder with detents
varMult = 10000; // AA0ZZ encoder, no detents
break;
case 5: // if 4 move the cursor to 100KHz
lcd.setCursor(18,0); // column,row
lcd.print("K3"); // prints out step
//varMult = 100000/2; // original encoder with detents
varMult = 100000; // AA0ZZ encoder, no detents
break;
case 6: // if 5 move the cursor to MHz
lcd.setCursor(18,0); // column,row
lcd.print("MH"); // prints out step
//varMult = 500000; // original encoder with detents
varMult = 1000000; // AA0ZZ encoder, no detents
break;
}
if (encoder0Pos <= 1800000) { // lower bounds limit
encoder0Pos = 1800000;
}
if (encoder0Pos >= 30000000) { // upper bounds limit
encoder0Pos = 30000000;
}
}
void doEncoder(){
oldencoder0Pos = encoder0Pos; // reset the variables so that we can compare them next time
if (digitalRead(encoder0PinA) == HIGH) { // found a low-to-high on channel A
if (digitalRead(encoder0PinB) == LOW) { // check channel B to see which way
// encoder is turning
encoder0Pos = encoder0Pos - varMult; // CCW
}
else {
encoder0Pos = encoder0Pos + varMult; // CW
}
}
else // found a high-to-low on channel A
{
if (digitalRead(encoder0PinB) == LOW) { // check channel B to see which way
// encoder is turning
encoder0Pos = encoder0Pos + varMult; // CW
}
else {
encoder0Pos = encoder0Pos - varMult; // CCW
}
}
}
/* to read the other two transitions - just use another attachInterrupt()
in the setup and duplicate the doEncoder function into say,
doEncoderA and doEncoderB.
You also need to move the other encoder wire over to pin 3 (interrupt 1).
*/