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

void loop() {

 if(changed_f)     //this flag is changed to yes(1) when the encoder changes
    //synt.simple_set_frequency(CLK0, frequency*F_MULT);
    si5351.set_freq((vfo * SI5351_FREQ_MULT) + bfo, SI5351_PLL_FIXED, SI5351_CLK0);
    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;
      case 10:
        radix = 100;
      case 100:
        radix = 1000;
      case 1000:
        radix = 10000;
      case 10000:
        radix = 100000;
      case 100000:
        radix = 1;

// show frequency
void display_frequency() {
    //lastVFO = vfo;
    char string[80];   // print format stuff  - I always have to look this up :)
          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
  else if (result == DIR_CCW)
void setUpDisplay(){
    //display.Color565(245, 179, 190);
    display.drawFastVLine(0, 0, 25, S6D02A1_WHITE);
    display.drawFastVLine(159, 0, 25, S6D02A1_WHITE);
    display.setCursor(1, 5);
//    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()
      return 1;
  return 0;

void display_radix()
  display.setCursor(110, 85);  
  //display.println("USB     RIT off     100Hz");
  switch (radix)
    case 1:
      display.print("    1");
    case 10:
      display.print("   10");
    case 100:
      display.print("  100");
    case 1000:
      display.print("   1k");
    case 10000:
      display.print("  10k");
    case 100000:
      //display.setCursor(10, 1);
      display.print(" 100k");
      //case 1000000:
      //display.setCursor(9, 1);
      //display.print("1000k"); //1MHz increments


  1. hello
    watched some videos of your VFO, it is possible to join these codes with the SDR Teensy? the VFO functions of the SDR Teensy are very limited

    73 PP5DI

  2. The real reason I started this project was to replace the VFO in the video. I ran out of room with the Uno and eventually will upgrade the code for the Teensy to do LPF, BPF, T/R switching plus dual VFO's.

  3. I'm waiting to get my SI5351 module, but I am already playing with the code, I added two VFO's, 6 steps selectable frequency, S-Meter bar graph, a new skin friendly. I took their advice on tft.print really was much better, but still the spectrum has legs turning the encoder.
      This is a fantastic SDR project, low cost and easy comprehension for beginners

  4. Reading with much interest ,TY all

  5. Reading with much interest ,TY all