Saturday, June 20, 2015

Teensy 3.1 VFO with encoder and si5351 board

This view adds the encoder and an Si5351 breakout board. The one shown is an Adafruit board but for my own project I'm using Jason's (NT7S) latest breakout board. The display is from Banggood. The display needs to have J1 jumpered to enable 3v3 regulator.

***Just a note to say that I blew up my Teensy 3.1 using this configuration. It worked quite well for a while and then it just stopped working and became very hot. I'm not sure why? Maybe it was a problem with the display and the Si5351 board running from the Teensy 3v3 pin. The Teensy 3.1 3v3 pin has a 100ma max current available. I wasn't using any current limiting resistance in the backlight of the display, so according to Adafruit, I would be drawing about 50 ma. Truthfully, I didn't measure the current draw from the si5351 board.  Anyway, I'm now using a new Teensy 3.1 board without the breakout board and running the display and si5351 board from a separate 3.3v regulator. 

This all points out an issue that I probably should have brought up in the very beginning - I'm not that bright! 

**7/6/15 I blew up another Teensy. Now I'm really confused. Both of the blown ones get hot and have no voltage on the 3.3V pin. I assume the processor is fried. This even after removing the TFT display and the si5351 breakout board to an offline 3.3V regultor!!

**I think I found the problem. The back light (BL) of the display is connected to pin D0 which most likely can't source the current needed (~50ma). I will connect the back light along with the breakout board to an external 3.3v source. I will clean up this drawing soon.

**7/9/15 I made the following measurements.
Teensy with program running and no pins attached draws 31.7 mA.
Teensy with program running and all pins attached 36 mA. (BL & VCC & si5351 powered separately)
The TFT display plus the si5351 breakout board draw 25 mA.
The TFT back light draws 19 mA. The spec for the i/o pins is 25 mA but, that is a maximum rating. (According to this spec sheet on page 10).
However, Cosford on the PJRC Forum pointed out "Have a look at page 13. I think by default the Teensy core defaults the pins to high drive strength, which gives you 9mA to play with. But as you say, I think you can drive higher loads (possibly up to 25 mA as you've pointed out for short periods. Note, 'Maximum' current single pin limit)."
After all this, I could easily run the backlight(BL), the si5351 board and the TFT display(VCC) from the 3v3 output pin since it's only about 44 ma total. Teensy 3.3 volt pin is 100 mA maximum. Since I need to have a 5 volt regulator board for the transmitter anyway, I just added a 150ma 3v3 regulator to be safe.

I'll get this sorted out eventually. I like the Teensy 3.1 - it's small and fast, has lots of pins and plenty of program space. In this case, I'm glad the errors were self-inflicted.

**7/11/15 Updated drawing to reflect the use of an external 3v3 regulator. In my project I use a 5 volt regulator as input to the  3v3 regulator.




Today we will be stealing...I mean borrowing code from Rich Heslip, VE3MKC. Rich made up a really cool Teensy SDR which you can see in this video.
There are some things in this code that are not used just yet but it does compile and work with my Teensy 3.1, encoder and TFT display. You should be able to just cut and paste the code into a new sketch.

I had an awful time trying to figure out why the display flickered when the vfo changed frequency until I found a reference in an Adafruit tutorial which explained the setTextColor command was overloaded (meaning it can take one or two variables). The overloaded variable was the background color. So, in my code, "display.setTextColor(S6D02A1_Green , S6D02A1_BLUE)" in the function "display_frequency()" the display doesn't flicker. If I leave out the S6D02A1_BLUE part, it gets really ugly and upsetting. Actually, if I had followed my own advise and looked in the Adafruit_GFX.h file, I would have seen two commands for setTextColor.

#include <Adafruit_GFX.h>    // Core graphics library
#include <Adafruit_S6D02A1.h> // Hardware-specific library
#include <SPI.h>
#include <Wire.h>
#include <si5351.h>
//#include <Encoder.h> //not used
#include <Rotary.h>
//#include <Bounce2.h> //not used

// This examples uses the hardware SPI only. Non-hardware SPI
// is just too slow (~8 times slower!)

//#define  BACKLIGHT  0  // backlight control signal -  not used anymore
#define sclk 13 // Don't change
#define mosi 11  // Don't change
#define cs   2
#define dc   3
#define rst  1  // you can also connect this to the Arduino reset
#define ENCODER_BTN  16  //I use this instead of TuneSW

Adafruit_S6D02A1 display = Adafruit_S6D02A1(cs, dc, mosi, sclk, rst);  // Invoke custom library

Rotary tune(15, 14); //pins 15 and 14 used for encoder
Si5351 si5351;
//const int8_t TuneSW =18;    // low for fast tune - encoder pushbutton(I don't use this)
volatile uint32_t vfo = 1420000000ULL / SI5351_FREQ_MULT; //start freq - change to suit
volatile uint32_t LSB = 899950000ULL;
volatile uint32_t USB = 900150000ULL;
volatile uint32_t bfo = 900150000ULL; //start in usb
volatile uint32_t radix = 100;
volatile uint32_t lastVFO;
int myOldList[9]= {0,0,0,0,0,0,0,0,0};
uint32_t Bands[] {
1800000L, 3500000L, 7000000L, 10100000L, 14200000L, 18065000L, 21000000L, 24890000L, 28000000L
}; //not used at the moment
boolean changed_f = 0;
String tbfo = "USB";

void setup(void) {

  Serial.begin(9600); // debug console
  Wire.begin();
  //pinMode(BACKLIGHT, INPUT_PULLUP); // yanks up display BackLight signal - not used
  pinMode(ENCODER_BTN, INPUT_PULLUP);  // tuning rate = high "radix"
  attachInterrupt(14, chk_encoder, CHANGE);  //I attach interrupts to encoder pins
  attachInterrupt(15, chk_encoder, CHANGE);  //but use the same Interrupt Service Routine                                                                                           //(chk_encoder())
  si5351.init(SI5351_CRYSTAL_LOAD_8PF,0);
  //si5351.set_correction(157);
  // Set CLK0 to output vfo plus IF frequency with a fixed PLL frequency
  si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA);
  si5351.set_freq((vfo * SI5351_FREQ_MULT) + bfo, SI5351_PLL_FIXED, SI5351_CLK0);
  //volatile uint32_t vfoT = (vfo * SI5351_FREQ_MULT) + bfo; //test stuff
  tbfo = "USB"; //for lower left display display
  // Set CLK2 to output bfo frequency
  si5351.set_freq( bfo, 0, SI5351_CLK2);

  display.initR(INITR_BLACKTAB);   // initialize a S6D02A1S chip, black tab
  SPI.setClockDivider(SPI_CLOCK_DIV2); // crank up the spi
  uint16_t time = millis();
  display.setRotation(1); // 0 - Portrait, 1 - Lanscape
  display.fillScreen(S6D02A1_BLACK);
  display.setTextWrap(true);
  delay(500);
  //testdrawrects(S6D02A1_GREEN);
  delay(500);
  setUpDisplay();
  display_frequency();
  display_radix();
}

void loop() {

 if(changed_f)     //this flag is changed to yes(1) when the encoder changes
 {  
    display_frequency();
    //synt.simple_set_frequency(CLK0, frequency*F_MULT);
    si5351.set_freq((vfo * SI5351_FREQ_MULT) + bfo, SI5351_PLL_FIXED, SI5351_CLK0);
     si5351.drive_strength(SI5351_CLK0,SI5351_DRIVE_8MA);
    if (vfo >= 10000000ULL & tbfo != "USB")
    {
      bfo = USB;
      tbfo = "USB";
      si5351.set_freq( bfo, 0, SI5351_CLK2);
      Serial.println("We've switched from LSB to USB");
    }
    else if (vfo < 10000000ULL & tbfo != "LSB")
    {
      bfo = LSB;
      tbfo = "LSB";
      si5351.set_freq( bfo, 0, SI5351_CLK2);
      Serial.println("We've switched from USB to LSB");
    }
    changed_f = 0;        //and cleared back to no(0) after updates are made to the si5351
 }
 if (get_button())
  {
    switch (radix)
    {
      case 1:
        radix = 10;
        break;
      case 10:
        radix = 100;
        break;
      case 100:
        radix = 1000;
        break;
      case 1000:
        radix = 10000;
        break;
      case 10000:
        radix = 100000;
        break;
      case 100000:
        radix = 1;
        break;
    }
    display_radix();
  }
}

// show frequency
void display_frequency() {
    //lastVFO = vfo;
    char string[80];   // print format stuff  - I always have to look this up :)
    sprintf(string,"%d.%03d.%03d",vfo/1000000,(vfo-vfo/1000000*1000000)/1000,
          vfo%1000 );
    display.setCursor(35, 5);
    display.setTextColor(S6D02A1_GREEN,S6D02A1_BLUE); //full display blanking -
    display.setTextSize(2);                                                              //you really need the
    display.print(string);                                                                  //2nd parameter to keep the
}                                                                                                     //display from flickering

void set_frequency(short dir)
{                                        
  if(dir == 1)                 //This routine is called by the Interrupt Service Routine chk_encoder()
    vfo += radix;            //which sets the vfo to its new value up or down plus or minus the
  if(dir == -1)               //the step size(radix)  
    vfo -= radix;
  changed_f = 1;          //it also sets this yes/no flag to yes to tell the loop that there has been a
}                                  //a change in frequency

void chk_encoder(){                                               //This routine (ISR) is called by the Arduino
   //Serial.println("inside encoder");                       // every time the encoder changes. Interrupts
    unsigned char result = tune.process();               // tell microprocessors, "STOP WHAT YOU
    if (result) {                                                          // ARE DOING!....and take care of me before
    //Serial.println(result == DIR_CW ? 1 : -1);     //going any further"
    if (result == DIR_CW)                                      //calls set_frequency() with a +1 or -1
    set_frequency(1);
  else if (result == DIR_CCW)
    set_frequency(-1);  
  }
}
void setUpDisplay(){
    //display.fillRect(1,1,158,25,S6D02A1_BLUE);
    //display.Color565(245, 179, 190);
    display.fillScreen(S6D02A1_BLUE);
    //display.fillRect(0,5,158,23,S6D02A1_BLUE);
    display.fillRect(1,1,13,24,S6D02A1_RED);
    //display.fillRect(1,1,13,24,Color565);
    display.drawFastVLine(0, 0, 25, S6D02A1_WHITE);
    display.drawFastVLine(159, 0, 25, S6D02A1_WHITE);
    display.drawFastHLine(0,0,160,S6D02A1_WHITE);
    display.drawFastHLine(0,25,160,S6D02A1_WHITE);  
    display.setCursor(1, 5);
    display.setTextSize(2);
    display.setTextColor(S6D02A1_GREEN);
    display.println("A");
//    display.setCursor(1, 90);
//    display.setTextSize(1);
//    display.setTextColor(S6D02A1_GREEN);
//    display.println("USB     RIT off     100Hz");

}

/**************************************/
/* Read the button with debouncing    */
/**************************************/
boolean get_button()
{
  if(!digitalRead(ENCODER_BTN))
  {
    delay(20);
    if(!digitalRead(ENCODER_BTN))
    {
      while(!digitalRead(ENCODER_BTN));
      return 1;
    }
  }
  return 0;
}

void display_radix()
{
  //display.fillRect(0,90,158,24,S6D02A1_BLUE);
  display.setTextSize(1);
  display.setTextColor(S6D02A1_GREEN,S6D02A1_BLUE);
  display.setCursor(110, 85);  
  //display.println("USB     RIT off     100Hz");
  switch (radix)
  {
    case 1:
      display.print("    1");
      break;
    case 10:
      display.print("   10");
      break;
    case 100:
      display.print("  100");
      break;
    case 1000:
      display.print("   1k");
      break;
    case 10000:
      display.print("  10k");
      break;
    case 100000:
      //display.setCursor(10, 1);
      display.print(" 100k");
      break;
      //case 1000000:
      //display.setCursor(9, 1);
      //display.print("1000k"); //1MHz increments
      //break;
  }
  display.print("Hz");
}

Friday, May 15, 2015

AK2B NT7S KK7B W7ZOI WB6DHW Si5351 receiver

This video was made a couple of months back. There are links in the notes to the various boards used  if you watch it on YouTube.


Sunday, May 3, 2015

N2HTT's 3 Band VFO

In the world of the Si5351 there are some interesting projects evolving from hams around the world. In the U.S., Jason, NT7S has started an App Notes page to his Etherkit website. The first app  is an Antenna Analyzer.
Another item that has recently appeared is by Mike Aiello, N2HTT which is a 3 band VFO using a si5351 and an Arduino.

You get a much better understanding of how to use an Arduino with an Si5351, and of course, programming, by looking over the code of indviduals who really know what they are doing.


I made up my own version of the VFO using the same ATMega 2560 board with an Adafruit OLED and one of Jason's original breakout boards. The code for Mike's VFO can be found on the link above. The original code was for a 2560 but later code will work with an UNO.


Monday, April 6, 2015

Multi-Featured VFO

Download MultiFeaturedVFO.ino
Download Rotary.Zip (click on Down Arrow at top of page)
Jason's Arduino Si5351 Library (click on Download Zip) and install it directly into the new Arduino IDE as mentioned in my last post.
Hardware Layout for this program can be found here.
**4/17 Jason has a new library https://github.com/etherkit/Si5351Arduino
You can download a corrected version of MultiFeaturedVFO.ino here:
https://drive.google.com/file/d/0B7yCOxNJVhR5UE9OQS14djI0anc/view?usp=sharing

//------------------------------- Set Optional Features here --------------------------------------
//Remove comment (//) from the option you want to use. Pick only one
#define IF_Offset //Output is the display plus or minus the bfo frequency // this line will be used
//#define Direct_conversion //What you see on display is what you get
//#define FreqX4  //output is four times the display frequency
//--------------------------------------------------------------------------------------------------

You can output frequencies up to 160Mhz. Keep this in mind when you use the 4X frequency option.
You can also tune in 1Hz increments if you so desire.
Also, I use clk0 for VFO output and clk2 for BFO.

Jason's latest library has a calibration sketch that can be accessed by:

You can easily adjust the frequency correction using the Serial Monitor and the keyboard. Make note of the offset and put it into si5351.set_correction(); in the VFO sketch. The correction is no longer stored in the board EEPROM so do not comment out the set_correction line as I had mentioned in the past. Also when using the current hardware setup, nothing will change on the LCD while adjusting the frequency - only on a frequency counter.
Looking at the above screen you will notice three Si5351 libraries in my "user libraries" folder. The Adafruit_Si5351 is not a problem since the header file uses a different name. The "Si5351" and "Si5351Arduino-Jason" share the same header file name. I did get an error that said something like "multiple libraries were found for si5351.h files - you must die". Both were Jason's newer library but the one, "Si5351Arduino-Jason" had all the newer upadates and examples so I deleted the "Si5351" folder.

Wednesday, April 1, 2015

Arduino now allows you to add Zipped libraries


It seems with the newer version of the Arduino IDE you can now download a library and add it without having to unzip it (Add .ZIP Library...). It also adds the library to the proper place in the Arduino User directory.  


Further, it seems to not care about hyphenated folder names. I download the latest version of NT7S's Si5351 library into my downloads directory and by adding the library as above and it worked just fine. User libraries are now listed in a section called "Contributed libraries" which shows up when you click "Include Library" on the above menu. This should make things a little less confusing.



Sunday, March 29, 2015

Si5351 VHF VFO

Link to  VHF_VFO.ino
I can tune up to 160Mhz. Output with default current setting is about -10dbm.
 /*
  This entire program is taken from Jason Mildrum, NT7S and Przemek Sadowski, SQ9NJE and Ben     Buxton.
  
  ***The following code has been modified to use the updated library of NT7S. 
  As of this date, the library is still undergoing changes so be aware of that.
  This code was tested on an Arduino Uno ONLY and it works if you setup your
  Equally important is that you make sure that Jason's new library REPLACES any existing
  Si5351 libraries in places that the Arduino IDE will look for it. Remove the existing Si5351 folder   in the "Arduino/Libraries" folder and put it in another folder for safe keeping in case you need to use it again.  I made a folder called Lib_Temp to put user libraries not in use. If you get errors
  it will most likely be for this reason. Please spend the time to resolve this problem.
  MAKE SURE THE ARDUINO IDE IS CLOSED WHEN MAKEING CHANGES TO THE LIBRARY.
  For help on the proper place to put libraries look here:
  Jason's new library will break existing code using his older library.
  */

  #include <Rotary.h>
  #include <si5351.h>
  #include "Wire.h"
  #include <LiquidCrystal.h>
 
  #define OLED_RESET 4
  #define ENCODER_A    3                      // Encoder pin A
  #define ENCODER_B    2                      // Encoder pin B
  #define ENCODER_BTN  11
  #define LCD_RS 5
  #define LCD_E        6
  #define LCD_D4 7
  #define LCD_D5 8
  #define LCD_D6 9
  #define LCD_D7 10

  LiquidCrystal lcd(LCD_RS, LCD_E, LCD_D4, LCD_D5, LCD_D6, LCD_D7);       // LCD - pin assignement in
  Si5351 si5351;
  Rotary r = Rotary(ENCODER_A, ENCODER_B);

  volatile uint32_t frequency0 = 1000000000L / SI5351_FREQ_MULT; //10Mhz - change to suit your needs
  //just take care to count the right number of zeros.
//  volatile uint32_t frequency1 = 9000000L;
//  volatile uint32_t frequency2 = 10000000L;
  volatile uint32_t radix = 100;
  boolean changed_f = 0;

  /**************************************/
  /* Interrupt service routine for      */
  /* encoder frequency change           */
  /**************************************/
  ISR(PCINT2_vect) {
    unsigned char result = r.process();
    if (result == DIR_CW)
      set_frequency(1);
    else if (result == DIR_CCW)
      set_frequency(-1);
  }
  /**************************************/
  /* Change the frequency               */
  /* dir = 1    Increment               */
  /* dir = -1   Decrement               */
  /**************************************/
  void set_frequency(short dir)
  {
    if(dir == 1)
      frequency0 += radix;
    if(dir == -1)
      frequency0 -= radix;
    changed_f = 1;
  }
  /**************************************/
  /* Read the button with debouncing    */
  /**************************************/
  boolean get_button()
  {
    if(!digitalRead(ENCODER_BTN))
    {
      delay(20);
      if(!digitalRead(ENCODER_BTN))
      {
        while(!digitalRead(ENCODER_BTN));
        return 1;
      }
    }
    return 0;
  }

  /**************************************/
  /* Displays the frequency             */
  /**************************************/
    void display_frequency()
  {
    uint16_t f, g;
 
    lcd.setCursor(3, 0);
    f = frequency0 / 1000000;
    if(f<10)
      lcd.print(' ');
    lcd.print(f);
    lcd.print('.');
    f = (frequency0 % 1000000)/1000;
    if(f<100)
      lcd.print('0');
    if(f<10)
      lcd.print('0');
    lcd.print(f);
    lcd.print('.');
    f = frequency0 % 1000;
    if(f<100)
      lcd.print('0');
    if(f<10)
      lcd.print('0');
    lcd.print(f);
    lcd.print("Hz");
  }

  /**************************************/
  /* Displays the frequency change step */
  /**************************************/
  void display_radix()
  {
    lcd.setCursor(10, 1);
    switch(radix)
    {
      case 10:
        lcd.print("  10");
        break;
      case 100:
        lcd.print(" 100");
        break;
      case 1000:
        lcd.print("  1k");
        break;
      case 10000:
        lcd.print(" 10k");
        break;
      case 100000:
        lcd.print("100k");
        break;
        case 1000000:
        lcd.setCursor(9, 1);
        lcd.print("1000k"); //1MHz increments
        break;
    }
    lcd.print("Hz");
  }


  void setup()
  {
    lcd.begin(16, 2);                                                    // Initialize and clear the LCD
    lcd.clear();
    Wire.begin();
    // Start serial and initialize the Si5351 with load capacitance and crystal freq
//0 will provide default freq of 25Mhz. If you have a 27MHz crystal put in 27000000
si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0); //you could measure your crystal and put it here to    correct for errors.
// Set CLK0 to output 10 MHz with a fixed PLL frequency
si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA);
//set frequency with 1 Hz resolution
si5351.set_freq(frequency0 * SI5351_FREQ_MULT, SI5351_PLL_FIXED, SI5351_CLK0);
 
     pinMode(ENCODER_BTN, INPUT_PULLUP);
     PCICR |= (1 << PCIE2);           // Enable pin change interrupt for the encoder
     PCMSK2 |= (1 << PCINT18) | (1 << PCINT19);
     sei();
     display_frequency();  // Update the display
     display_radix();
  }


  void loop()
  {
    // Update the display if the frequency has been changed
    if(changed_f)
    {
      display_frequency();    
      si5351.set_freq(frequency0 * SI5351_FREQ_MULT, 0, SI5351_CLK0);
      changed_f = 0;
    }
 
    // Button press changes the frequency change step
    if(get_button())
    {
      switch(radix)
      {
        case 10:
          radix = 100;
          break;
        case 100:
          radix = 1000;
          break;
        case 1000:
          radix = 10000;
          break;
          case 10000:
          radix = 100000;
          break;
        case 100000: //change these lines to tune in 1MHz increments
          radix = 1000000;
          break;
        case 1000000:
          radix = 10;
          break;
      }
      display_radix();
    }
  }

Tuesday, February 24, 2015

Calibrating the Si5351

Jason's library has a very good calibration procedure found by clicking on 'File\Examples\Etherkit-si535\si5351_calibration'
You will need some way of measuring frequency. Once done you will end up with a number that will replace the 0 in this statement:
si5351.set_correction(0);

Initialization now takes two arguments in the new library. Instead of just the crystal load capacitance - it now takes the crystal frequency as well.

 si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0);

 A zero as the second parameter defaults to 25Mhz, as above.
 If you're using a 27Mhz crystal you could change it in the header file (si5351.h) or just put in 27000000.
Any and all comments are appreciated.

Tuesday, February 10, 2015

Sunday, February 8, 2015

KD1JV PFR-3 transmitter

I always wanted to try making making a cw transmitter like the one in KD1JV's PFR-3. It looks so simple. Steve uses the square wave comparator output of the AD9834 into an *74HC02 quad nand gate to buffer the output into 3 paralled BS170's. Since the Si5351 already puts out a square wave - why not? A quick test gave me 4 watts into a dummy load. I added a 40M LPF because I plan to lash this up with an already built CRX1 since it has some nice built-in conveniences, like TX/RX switching, muting and a nice twin-T sidetone oscillator.
Spectral plot of output.





Below is the finished product.

Receiver/Transmitter

Front Panel



Etherkit Si5351 breakout board
Close-up of transmitter

Sunday, February 1, 2015

Codebender Arduino IDE

Recently I came across this video from the Open Source Hardware group which describes a cloud based IDE (Integrated Development Environment) for Arduino. This may simplify things for some people who have had problems with setting up an Arduino Environment on their home computers. For me, it means keeping the code all in one place and the fact that I simply like it better than the Arduino IDE.



To add a new library in Codebender click on the codebender gear in the upper left corner of the page (if you are on the code page) or "Upload library" on the upper right if you are at the home page. The library must be zipped. If it is downloaded from NT7S Si5351 or Rotary it is already zipped for you. In that case, just drag them from your download folder onto the Codebender "Upload a personal library" pop up that occurs when you press the green button.
Codebender allows me to embed my sketch into my blog where you can have your way with it. If you have Codebender installed on your browser, you can compile and download to your Arduino from here. If you want just the LCD_VFO_Si5351_IF.ino file for the Arduino IDE  click here.

Saturday, January 31, 2015

IF offset added to Si5351 VFO sketch

4/10/15***This sketch will no longer work go to this page and download the new library and sketch.
This new listing will allow an IF offset to be added to the VFO sketch.
*These new lines, among others, have been added:

  volatile uint32_t vfo = 14200000L; // this is the start frequency
  volatile uint32_t LSB = 8999500L;  // change this ...
  volatile uint32_t USB = 9001500L; // ... and this to reflect your IF frequency
  volatile uint32_t bfo = 9001500L;   // inital start bfo is set to USB

The bfo will change from USB to LSB when the vfo is below 10 MHz and will be noted on the LCD.

By all means, experiment with this code. If you mess up just download it again.
I will from time to time update this code and will try to keep it simple, readable and distinctive as to things like different types of displays. The whole idea to learning something about programming is that you will not be stuck with things the way I do them or anyone else. Come to think of it, that is also one of the best things about home brewing!

*just changing these few lines will not work. You need to click "new listing" to download the new sketch.

Monday, January 26, 2015

Installing Libraries and running code for Si5351 Arduino VFO

**as of 4/11/15 Jason's library has changed and this program will not work if you just downloaded the library. Use the new library and sketch from this posting. With newer versions of the Arduino IDE, downloading and installing libraries is a little easier - see this posting. The main thing is, remove the old library before installing a new one. 
***Also, it is much easier now to add a new library. See this page.

We are going to use the NT7S Si5351 library from Github along with a sketch from SQ9NJE.

To quote the Adafruit Arduino Libraries tutorial, "User installed libraries should be installed in your sketchbook libraries folder so they can be used with all versions of the IDE. This way, if a new version of the IDE is released, you don't have to re-install all your favorite libraries! It would be a good idea to go through the tutorial if you are confused on this point.

Download and install the NT7S Si5351 library by clicking "Download Zip" on the right side of the page.
You will get a file called "Si5351Arduino-master.zip" Click on the file name and from the menu above and click on extract. Now you will have a file folder called "Si5351Arduino-master". This is not an acceptable file name as far as the Arduino IDE is concerned. Arduino doesn't like hypens. So right click on the folder name and change it to "Si5351".
Where you put this library folder is very important. Open the Arduino program and under "File" on the top left menu, click on "Preferences". This will show you where the IDE expects to see user sketches and user libraries ( Sketchbook location: ). The folder "Arduino" will list all your sketches and contains another folder called "libraries" This is where you will put the folder called Si5351 and Rotary.


The Si5351 folder contains two files which are most important, the one ending in ".cpp" and the one ending in ".h". The .h or header file describes the functions and variables for the cpp file that does all the work. If you are using another board like the one from Hans Summers  QRP Labs, it uses a crystal of 27MHz instead of the 25Mhz crystal used in the Adafruit or NT7S board. In which case you will need to edit the line in the si5351.h file that says:

#define SI5351_XTAL_FREQ        25000000
to
#define SI5351_XTAL_FREQ        27000000

*In the new version of Jason's library, it is no longer necessary to do this. You can put the crystal frequency in as the second initialization parameter i.e.
si5351.init(SI5351_CRYSTAL_LOAD_8PF, 27000000);

You could edit this with Notepad but it might be difficult. A much easier approach for editing files that contain code is to use a program called Notepad++. It's free and very easy to use. In our case just go to the Si5351 folder, open it and right click on si5351.h and click "edit with Notepad++", make the change as above and click on File, Save. 

**DO NOT edit with a word processor program like Word.

Download the *Rotary library from SQ9NJE's website by clicking on "Rotary Libraryor  "Biblioteka Rotary(if you haven't right clicked on the page and clicked "Translate to English").
Follow the instructions as above.

Download sketch from   LCD_VFO_Si5351.ino
(if you're using the latest NT7S library, use the sketch mentioned at the top of the post)

  /*
  This entire program is taken from Jason Mildrum, NT7S and Przemek Sadowski, SQ9NJE and Ben Buxton.
  There is not enough original code written by me to make it worth mentioning.
  http://nt7s.com/
  http://sq9nje.pl/
  Rotary library by Ben Buxton
   */

  #include <Rotary.h>
  #include <si5351.h>
  #include <Wire.h>
  #include <LiquidCrystal.h>


  #define F_MIN        1000000L              // Lower frequency limit
  #define F_MAX       30000000L           // change to suit your needs for upper limit
  #define ENCODER_A        3                 // Encoder pin A
  #define ENCODER_B        2                 // Encoder pin B
  #define ENCODER_BTN  11                //increments tuning step size
  #define LCD_RS 5
  #define LCD_E   6
  #define LCD_D4 7
  #define LCD_D5 8
  #define LCD_D6 9
  #define LCD_D7 10

  LiquidCrystal lcd(LCD_RS, LCD_E, LCD_D4, LCD_D5, LCD_D6, LCD_D7);     // LCD - pins
  Si5351 si5351;
  Rotary r = Rotary(ENCODER_A, ENCODER_B);

  volatile uint32_t frequency0 = 14000000L;       //You change these to your own frequencies
  volatile uint32_t frequency1 = 9000000L;
  volatile uint32_t frequency2 = 10000000L;
  volatile uint32_t radix = 100;                            //100Hz steps to start with  
  boolean changed_f = 0;                                     // frequency has changed flag

  /**************************************/
  /* Interrupt service routine for      */
  /* encoder frequency change           */
  /**************************************/
  ISR(PCINT2_vect) {
    unsigned char result = r.process();
    if (result == DIR_CW)
      set_frequency(1);                        
    else if (result == DIR_CCW)
      set_frequency(-1);                        
  }
  /**************************************/
  /* Change the frequency               */
  /* dir = 1    Increment               */
  /* dir = -1   Decrement               */
  /**************************************/
  void set_frequency(short dir)
  {
    if(dir == 1)
      frequency0 += radix;
    if(dir == -1)
      frequency0 -= radix;
 
    if(frequency0 > F_MAX) //if you comment out these four lines
      frequency0 = F_MAX;  //then you can avoid checking
    if(frequency0 < F_MIN) //upper and lower limits
      frequency0 = F_MIN;  //do the math, though, and set the limits for F_MAX
                                           //and F_MIN for the vfo you are using
 
    changed_f = 1;
  }
  /**************************************/
  /* Read the button with debouncing    */
  /**************************************/
  boolean get_button()
  {
    if(!digitalRead(ENCODER_BTN))
    {
      delay(20);
      if(!digitalRead(ENCODER_BTN))
      {
        while(!digitalRead(ENCODER_BTN));
        return 1;
      }
    }
    return 0;
  }

  /**************************************/
  /* Displays the frequency             */
  /**************************************/
    void display_frequency()
  {
    uint16_t f, g;
    lcd.setCursor(4, 0);
    f = frequency0 / 1000000;
    if(f<10)
      lcd.print(' ');
    lcd.print(f);
    lcd.print('.');
    f = (frequency0 % 1000000)/1000;
    if(f<100)
      lcd.print('0');
    if(f<10)
      lcd.print('0');
    lcd.print(f);
    lcd.print('.');
    f = frequency0 % 1000;
    if(f<100)
      lcd.print('0');
    if(f<10)
      lcd.print('0');
    lcd.print(f);
    lcd.print("Hz");
  }

  /**************************************/
  /* Displays the frequency change step */
  /**************************************/
  void display_radix()
  {
    lcd.setCursor(10, 1);
    switch(radix)
    {
      case 10:
        lcd.print("  10");
        break;
      case 100:
        lcd.print(" 100");
        break;
      case 1000:
        lcd.print("  1k");
        break;
      case 10000:
        lcd.print(" 10k");
        break;
      case 100000:
        lcd.print("100k");
        break;
     case 1000000: //change these lines to tune in 1MHz increments
        lcd.print("1M");
        break;
    }
    lcd.print("Hz");
  }


  void setup()
  {
    lcd.begin(16, 2);                                                    // Initialize and clear the LCD
    lcd.clear();
    Wire.begin();
    // Start serial and initialize the Si5351
    si5351.init(SI5351_CRYSTAL_LOAD_8PF);

   //Comment out the si5351.set_freq lines of the outputs you don't want to use
   // same for  si5351.drive_strength

   // Set CLK0 to output to 14 MHz ( frequency0) with a fixed PLL frequency
    si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA);
    si5351.set_freq(frequency0, SI5351_PLL_FIXED, SI5351_CLK0);

    // Set CLK1 to output 9 MHz (frequency1)
    si5351.set_freq(frequency1, 0, SI5351_CLK1);

    // Set CLK2 to output 10 MHz (frequency2)
    si5351.set_freq(frequency2, 0, SI5351_CLK2);

    si5351.drive_strength(SI5351_CLK0,SI5351_DRIVE_2MA); //change the 2 to a 4, 6, or 8
    si5351.drive_strength(SI5351_CLK1,SI5351_DRIVE_2MA); //for more power output
    si5351.drive_strength(SI5351_CLK2,SI5351_DRIVE_2MA);
 
     pinMode(ENCODER_BTN, INPUT_PULLUP);
     PCICR |= (1 << PCIE2);           // Enable pin change interrupt for the encoder
     PCMSK2 |= (1 << PCINT18) | (1 << PCINT19);
     sei();
     display_frequency();  // Update the display
     display_radix();
  }

  void loop()
  {
    // Update the display if the frequency has been changed
    if(changed_f)                   //changed_f  = 1 because encoder was moved
    {
      display_frequency();
     
      si5351.set_freq(frequency0, SI5351_PLL_FIXED, SI5351_CLK0);
      changed_f = 0;                // set back to 0 after updating si5351 with new freq.
    }
 
    // Button press changes the frequency change step
    if(get_button())                             //default radix was 100
    {                                                   //so the next time the button is pressed ...        
      switch(radix)
      {
        case 10:
          radix = 100;
          break;
        case 100:                               //case will be 100
          radix = 1000;                      //so step is increased to 10000
          break;
        case 1000:
          radix = 10000;
          break;
          case 10000:
          radix = 100000;
          break;
        case 100000: //change these lines to tune in 1MHz increments
          radix = 1000000;
          break;
        case 1000000:
          radix = 10;
          break;
      }
      display_radix();
    }
  }

Sunday, January 25, 2015

Si5351 Arduino VFO breadboarded

This is the project bread boarded with an Arduino  Duemilanove. On the left is the encoder  with switch. In the center is the Adafruit si5351 breakout board and of course, the LCD on the right.



scope input taken directly from output 0 of Adafruit board.


I should be able to post the sketch by tomorrow.

Saturday, January 24, 2015

Si5351 simple VFO hardware setup

I see that there is a need for a simple Si5351 sketch to get hams started using this great little cheap chip. I started by downloading, Przemek Sadowski, SQ9NJE 's code and his Si5351 library and was able to get it to work with very little problems. Przemek's code comes with a library called 'Rotary' (written by Ben Buxton) which is used to provide an interrupt driven rotary encoder for tuning. I eventually used the rotary library with Jason's (NT7S) Si5351 library but kept the bulk of Przemek's Arduino sketch. Just be careful if you try both si5351 libraries as they have the same name. This will cause a problem if you try and load them both into the Arduino Libraries folder :).

You will need: (I've listed Adafruit items but you can get them almost anywhere)

Arduino Uno
10K contrast potentiometer
LCD (compatible with Hitachi HD44780 driver) + contrast pot
Rotary encoder with push-button switch
Adafruit Si5351 Breakout Board

Here is the layout:
I use clk0 for the vfo and clk2 for the bfo.
*Your LCD may have a current limiting resistor already installed between pin 15 and the back light, In that case, you can supply +5v to directly to pin 15.






Use this updated sketch and library from this page that will allow for IF offsets, X4 output or Direct Conversion receivers
If you've never used Arduino before go here to the Programming Electronics Academy and watch the first the first and second videos (watch the second one first!). It can't be stressed how important it is to do a proper install of the Arduino IDE(Integrated Developement Environment) so that your programs and your downloaded libraries stay separate from the location of the Arduino IDE and the libraries that are included with each new version of a new IDE download.