Monday 29 January 2018

80/40/20m Base Rig Notes



Current notes for the 80/40/20m SSB rig.













VFO/BFO Test Code

#include <UTFT.h>
#include <si5351.h>

extern uint8_t SmallFont[];

volatile int updatedisplay = 0;
volatile long freq = 6200000;
volatile long currentfreq = 0;
volatile long oldfreq = 0;
volatile long remainder = 0;
const uint32_t bandStart = 1000000;
const uint32_t bandEnd =   30000000;

volatile long BFOfreq = 8000000;
volatile long BFOcurrentfreq = 0;
volatile long BFOoldfreq = 0;
volatile long BFOremainder = 0;

float OnesHz = 0;
float TensHz = 0;
float HundredsHz = 0;
float OneskHz = 0;
float TenskHz = 0;
float HundredskHz = 0;
float OnesMHz = 0;
float TensMhz = 0;

float BFOOnesHz = 0;
float BFOTensHz = 0;
float BFOHundredsHz = 0;
float BFOOneskHz = 0;
float BFOTenskHz = 0;
float BFOHundredskHz = 0;
float BFOOnesMHz = 0;
float BFOTensMhz = 0;

volatile long radix = 100;
volatile long oldradix = 100;

volatile long BFOradix = 100;
volatile long BFOoldradix = 100;

int mode = 1;
int button_delay = 100;

volatile uint32_t LSB_IF_freq = 8011500;    // Crystal filter centre freq
volatile uint32_t LSB_BFO_freq = 8013000;   // Crystal filter centre freq

// Rotary encoder pins and other inputs
static const int pushPin = 4;
static const int rotBPin = 3;
static const int rotAPin = 2;

// Rotary encoder variables, used by interrupt routines
volatile int rotState = 0;
volatile int rotAval = 1;
volatile int rotBval = 1;


// Usage: TFT(<model code>, SDA, SCL, CS, RST, RS/A0);
UTFT TFT(ITDB18SP, 11, 10, 9, 12, 8);   // Remember to change the model parameter to suit your display module!
Si5351 si5351;


void setup()
{
  // Set up frequency and radix switches
  pinMode(rotAPin, INPUT);
  pinMode(rotBPin, INPUT);
  pinMode(pushPin, INPUT);
  pinMode(A0, INPUT);
  pinMode(A1, INPUT);
  pinMode(A2, INPUT);
  pinMode(A3, INPUT);


  // Set up pull-up resistors on inputs
  digitalWrite(rotAPin, HIGH);
  digitalWrite(rotBPin, HIGH);
  digitalWrite(pushPin, HIGH);
  digitalWrite(A0, HIGH);
  digitalWrite(A1, HIGH);
  digitalWrite(A2, HIGH);
  digitalWrite(A3, HIGH);

  // Set up interrupt pins
  attachInterrupt(digitalPinToInterrupt(rotAPin), ISRrotAChange, CHANGE);
  attachInterrupt(digitalPinToInterrupt(rotBPin), ISRrotBChange, CHANGE);

  // Setup the LCD
  TFT.InitLCD();
  TFT.clrScr();
  TFT.setFont(SmallFont);
  SetupScreen();

  // Initialize the DDS
  si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0);
  si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA);
  si5351.drive_strength(SI5351_CLK0, SI5351_DRIVE_8MA);
  si5351.drive_strength(SI5351_CLK2, SI5351_DRIVE_8MA);
}


void loop()
{
  currentfreq = getfreq();                // Interrupt safe method to get the current frequency

  if (currentfreq != oldfreq)
  {
    UpdateFreq();
    SendFrequency();
    oldfreq = currentfreq;
  }


  BFOcurrentfreq = BFOgetfreq();                // Interrupt safe method to get the current frequency

  if (BFOcurrentfreq != BFOoldfreq)
  {
    UpdateFreq();
    SendFrequency();
    BFOoldfreq = BFOcurrentfreq;
  }


  if (digitalRead(A3) == LOW)
  {
    delay(10);
    if (digitalRead(A3) == LOW)
    {
      if (mode == 1)            // VFO
      {
        if (radix == 1000000)
          radix = 100000;
        else if (radix == 100000)
          radix = 10000;
        else if (radix == 10000)
          radix = 1000;
        else if (radix == 1000)
          radix = 100;
        else if (radix == 100)
          radix = 10;
        else if (radix == 10)
          radix = 1;
        else
          radix = 1000000;
      }
      if (mode == 2)              // BFO
      {
        if (BFOradix == 1000000)
          BFOradix = 100000;
        else if (BFOradix == 100000)
          BFOradix = 10000;
        else if (BFOradix == 10000)
          BFOradix = 1000;
        else if (BFOradix == 1000)
          BFOradix = 100;
        else if (BFOradix == 100)
          BFOradix = 10;
        else if (BFOradix == 10)
          BFOradix = 1;
        else
          BFOradix = 1000000;
      }
    }
    delay(button_delay);
    UpdateFreq();
  }

  if (digitalRead(A1) == LOW)
  {
    delay(10);
    if (digitalRead(A1) == LOW)
    {
      if (mode == 1)            // VFO
      {
        if (radix == 1)
          radix = 10;
        else if (radix == 10)
          radix = 100;
        else if (radix == 100)
          radix = 1000;
        else if (radix == 1000)
          radix = 10000;
        else if (radix == 10000)
          radix = 100000;
        else if (radix == 100000)
          radix = 1000000;
        else
          radix = 1;
      }
      if (mode == 2)              // BFO
      {
        if (BFOradix == 1)
          BFOradix = 10;
        else if (BFOradix == 10)
          BFOradix = 100;
        else if (BFOradix == 100)
          BFOradix = 1000;
        else if (BFOradix == 1000)
          BFOradix = 10000;
        else if (BFOradix == 10000)
          BFOradix = 100000;
        else if (BFOradix == 100000)
          BFOradix = 1000000;
        else
          BFOradix = 1;
      }
    }
    UpdateFreq();
    delay(button_delay);
  }

  if (digitalRead(A0) == LOW)
  {
    delay(10);
    if (digitalRead(A0) == LOW)
    {
      mode++;
      if (mode == 3)
        mode = 1;
    }
    delay(button_delay);
    UpdateFreq();
  }


  if (digitalRead(A2) == LOW)
  {
    delay(10);
    if (digitalRead(A2) == LOW)
    {
      mode--;
      if (mode == 0)
        mode = 2;
    }
    delay(button_delay);
    UpdateFreq();
  }
}


long getfreq()
{
  long temp_freq;
  cli();
  temp_freq = freq;
  sei();
  return temp_freq;
}


long BFOgetfreq()
{
  long temp_freq;
  cli();
  temp_freq = BFOfreq;
  sei();
  return temp_freq;
}


// Interrupt routines
void ISRrotAChange()
{
  if (digitalRead(rotAPin))
  {
    rotAval = 1;
    UpdateRot();
  }
  else
  {
    rotAval = 0;
    UpdateRot();
  }
}


void ISRrotBChange()
{
  if (digitalRead(rotBPin))
  {
    rotBval = 1;
    UpdateRot();
  }
  else
  {
    rotBval = 0;
    UpdateRot();
  }
}


void UpdateRot()
{
  switch (rotState)
  {

    case 0:                                         // Idle state, look for direction
      if (!rotBval)
        rotState = 1;                               // CW 1
      if (!rotAval)
        rotState = 11;                              // CCW 1
      break;

    case 1:                                         // CW, wait for A low while B is low
      if (!rotBval)
      {
        if (!rotAval)
        {
          if (mode == 1)
          {
            // either increment radixindex or freq
            freq = (freq + radix);
            if (freq > bandEnd)
              freq = bandEnd;
          }
          if (mode == 2)
          {
            BFOfreq = (BFOfreq + BFOradix);
            if (BFOfreq > bandEnd)
              BFOfreq = bandEnd;
          }
          rotState = 2;                             // CW 2
        }
      }
      else if (rotAval)
        rotState = 0;                             // It was just a glitch on B, go back to start
      break;

    case 2:                                         // CW, wait for B high
      if (rotBval)
        rotState = 3;                               // CW 3
      break;

    case 3:                                         // CW, wait for A high
      if (rotAval)
        rotState = 0;                               // back to idle (detent) state
      break;

    case 11:                                        // CCW, wait for B low while A is low
      if (!rotAval)
      {
        if (!rotBval)
        {
          // either decrement radixindex or freq
          if (mode == 1)
          {
            freq = (freq - radix);
            if (freq < bandStart)
              freq = bandStart;
          }
          if (mode == 2)
          {
            BFOfreq = (BFOfreq - BFOradix);
            if (BFOfreq < bandStart)
              BFOfreq = bandStart;
          }
          rotState = 12;                            // CCW 2
        }
      }
      else if (rotBval)
        rotState = 0;                             // It was just a glitch on A, go back to start
      break;

    case 12:                                        // CCW, wait for A high
      if (rotAval)
        rotState = 13;                              // CCW 3
      break;

    case 13:                                        // CCW, wait for B high
      if (rotBval)
        rotState = 0;                               // back to idle (detent) state
      break;
  }
}


void SetupScreen()
{
  TFT.setColor(VGA_BLUE);
  TFT.fillRect(0, 0, 159, 13);
  TFT.drawRect(0, 0, 159, 127);
  TFT.setColor(VGA_WHITE);
  TFT.setBackColor(VGA_BLUE);
  TFT.print("ZL2CTM Base Rig", CENTER, 1);
}


void UpdateFreq()
{
  TFT.setBackColor(VGA_BLACK);
  if (mode == 1)
    TFT.setColor(VGA_AQUA);
  else if (mode == 2)
    TFT.setColor(VGA_GRAY);
  TFT.print("VFO", 10, 20);

  if (mode == 2)
    TFT.setColor(VGA_AQUA);
  else if (mode == 1)
    TFT.setColor(VGA_GRAY);
  TFT.print("BFO", 10, 40);

  TensMhz = freq / 10000000;                                // TensMHz = 12345678 / 10000000 = 1
  remainder = freq - (TensMhz * 10000000);                  // remainder = 12345678 - 10000000 = 2345678
  OnesMHz = remainder / 1000000;                            // OnesMhz = 2345678 / 1000000 = 2
  remainder = remainder - (OnesMHz * 1000000);              // remainder = 2345678 - (2 * 1000000) = 345678
  HundredskHz = remainder / 100000;                         // HundredskHz = 345678 / 100000 = 3
  remainder = remainder - (HundredskHz * 100000);           // remainder = 345678 - (3 * 100000) = 45678
  TenskHz = remainder / 10000;                              // TenskHz = 45678 / 10000 = 4
  remainder = remainder - (TenskHz * 10000);                // remainder = 45678 - (4 * 10000) = 5678
  OneskHz = remainder / 1000;                               // OneskHz = 5678 / 1000 = 5
  remainder = remainder - (OneskHz * 1000);                 // remainder = 5678 - (5 * 1000) = 678
  HundredsHz = remainder / 100;                             // HundredsHz = 678 / 100 = 6
  remainder = remainder - (HundredsHz * 100);               // remainder = 678 - (6 * 100) = 78
  TensHz = remainder / 10;                                  // TensHz = 78 / 10 = 7
  remainder = remainder - (TensHz * 10);                    // remainder = 78 - (7 * 10) = 8
  OnesHz = remainder;                                       // OnesHz = 8

  BFOTensMhz = BFOfreq / 10000000;                                // TensMHz = 12345678 / 10000000 = 1
  BFOremainder = BFOfreq - (BFOTensMhz * 10000000);                  // remainder = 12345678 - 10000000 = 2345678
  BFOOnesMHz = BFOremainder / 1000000;                            // OnesMhz = 2345678 / 1000000 = 2
  BFOremainder = BFOremainder - (BFOOnesMHz * 1000000);              // remainder = 2345678 - (2 * 1000000) = 345678
  BFOHundredskHz = BFOremainder / 100000;                         // HundredskHz = 345678 / 100000 = 3
  BFOremainder = BFOremainder - (BFOHundredskHz * 100000);           // remainder = 345678 - (3 * 100000) = 45678
  BFOTenskHz = BFOremainder / 10000;                              // TenskHz = 45678 / 10000 = 4
  BFOremainder = BFOremainder - (BFOTenskHz * 10000);                // remainder = 45678 - (4 * 10000) = 5678
  BFOOneskHz = BFOremainder / 1000;                               // OneskHz = 5678 / 1000 = 5
  BFOremainder = BFOremainder - (BFOOneskHz * 1000);                 // remainder = 5678 - (5 * 1000) = 678
  BFOHundredsHz = BFOremainder / 100;                             // HundredsHz = 678 / 100 = 6
  BFOremainder = BFOremainder - (BFOHundredsHz * 100);               // remainder = 678 - (6 * 100) = 78
  BFOTensHz = BFOremainder / 10;                                  // TensHz = 78 / 10 = 7
  BFOremainder = BFOremainder - (BFOTensHz * 10);                    // remainder = 78 - (7 * 10) = 8
  BFOOnesHz = BFOremainder;                                       // OnesHz = 8


  // VFO
  if (TensMhz == 0)
  {
    TFT.setColor(VGA_BLACK);
    TFT.printNumI(TensMhz, 75, 20);
  }
  if (TensMhz > 0)
  {
    if (mode == 1)
      TFT.setColor(VGA_AQUA);
    else if (mode == 2)
      TFT.setColor(VGA_GRAY);
    TFT.printNumI(TensMhz, 75, 20);
  }
  if (mode == 1)
    TFT.setColor(VGA_AQUA);
  else if (mode == 2)
    TFT.setColor(VGA_GRAY);
  TFT.printNumI(OnesMHz, 84, 20);
  TFT.print(".", 93, 20);
  TFT.printNumI(HundredskHz, 102, 20);
  TFT.printNumI(TenskHz, 111, 20);
  TFT.printNumI(OneskHz, 120, 20);
  TFT.printNumI(HundredsHz, 129, 20);
  TFT.printNumI(TensHz, 138, 20);
  TFT.printNumI(OnesHz, 147, 20);



  // BFO
  if (BFOTensMhz == 0)
  {
    TFT.setColor(VGA_BLACK);
    TFT.printNumI(BFOTensMhz, 75, 40);
  }
  if (BFOTensMhz > 0)
  {
    if (mode == 2)
      TFT.setColor(VGA_AQUA);
    else if (mode == 1)
      TFT.setColor(VGA_GRAY);
    TFT.printNumI(BFOTensMhz, 75, 40);
  }
  if (mode == 2)
    TFT.setColor(VGA_AQUA);
  else if (mode == 1)
    TFT.setColor(VGA_GRAY);
  TFT.printNumI(BFOOnesMHz, 84, 40);
  TFT.print(".", 93, 40);
  TFT.printNumI(BFOHundredskHz, 102, 40);
  TFT.printNumI(BFOTenskHz, 111, 40);
  TFT.printNumI(BFOOneskHz, 120, 40);
  TFT.printNumI(BFOHundredsHz, 129, 40);
  TFT.printNumI(BFOTensHz, 138, 40);
  TFT.printNumI(BFOOnesHz, 147, 40);

  if (mode == 1)
  {
    //Radix
    TFT.setColor(VGA_BLACK);
    if (oldradix == 1)
      TFT.drawLine(147, 31, 151, 31);
    else if (oldradix == 10)
      TFT.drawLine(138, 31, 142, 31);
    else if (oldradix == 100)
      TFT.drawLine(129, 31, 133, 31);
    else if (oldradix == 1000)
      TFT.drawLine(120, 31, 124, 31);
    else if (oldradix == 10000)
      TFT.drawLine(111, 31, 115, 31);
    else if (oldradix == 100000)
      TFT.drawLine(102, 31, 106, 31);
    else if (oldradix == 1000000)
      TFT.drawLine(84, 31, 88, 31);

    TFT.setColor(VGA_AQUA);
    //TFT.setFont(SmallFont);
    if (radix == 1)
      TFT.drawLine(147, 31, 151, 31);
    else if (radix == 10)
      TFT.drawLine(138, 31, 142, 31);
    else if (radix == 100)
      TFT.drawLine(129, 31, 133, 31);
    else if (radix == 1000)
      TFT.drawLine(120, 31, 124, 31);
    else if (radix == 10000)
      TFT.drawLine(111, 31, 115, 31);
    else if (radix == 100000)
      TFT.drawLine(102, 31, 106, 31);
    else if (radix == 1000000)
      TFT.drawLine(84, 31, 88, 31);

    oldradix = radix;
  }

  if (mode == 2)
  {
    //Radix
    TFT.setColor(VGA_BLACK);
    if (BFOoldradix == 1)
      TFT.drawLine(147, 51, 151, 51);
    else if (BFOoldradix == 10)
      TFT.drawLine(138, 51, 142, 51);
    else if (BFOoldradix == 100)
      TFT.drawLine(129, 51, 133, 51);
    else if (BFOoldradix == 1000)
      TFT.drawLine(120, 51, 124, 51);
    else if (BFOoldradix == 10000)
      TFT.drawLine(111, 51, 115, 51);
    else if (BFOoldradix == 100000)
      TFT.drawLine(102, 51, 106, 51);
    else if (BFOoldradix == 1000000)
      TFT.drawLine(84, 51, 88, 51);

    TFT.setColor(VGA_AQUA);
    //TFT.setFont(SmallFont);
    if (BFOradix == 1)
      TFT.drawLine(147, 51, 151, 51);
    else if (BFOradix == 10)
      TFT.drawLine(138, 51, 142, 51);
    else if (BFOradix == 100)
      TFT.drawLine(129, 51, 133, 51);
    else if (BFOradix == 1000)
      TFT.drawLine(120, 51, 124, 51);
    else if (BFOradix == 10000)
      TFT.drawLine(111, 51, 115, 51);
    else if (BFOradix == 100000)
      TFT.drawLine(102, 51, 106, 51);
    else if (BFOradix == 1000000)
      TFT.drawLine(84, 51, 88, 51);

    BFOoldradix = BFOradix;
  }
}


void SendFrequency()
{
  //si5351.set_freq(((freq - BFOfreq) * 100ULL), SI5351_PLL_FIXED, SI5351_CLK0);
  si5351.set_freq((freq * 100ULL), SI5351_PLL_FIXED, SI5351_CLK0);
  si5351.set_freq((BFOfreq * 100ULL), SI5351_PLL_FIXED, SI5351_CLK2);
}




Rig Code (not finished)

#include <UTFT.h>
#include <si5351.h>

extern uint8_t SmallFont[];

volatile int updatedisplay = 0;
volatile long freq = 14200000;
volatile long currentfreq = 0;
volatile long oldfreq = 0;
volatile long remainder = 0;
const uint32_t bandStart = 3000000;
const uint32_t bandEnd =   15000000;


float OnesHz = 0;
float TensHz = 0;
float HundredsHz = 0;
float OneskHz = 0;
float TenskHz = 0;
float HundredskHz = 0;
float OnesMHz = 0;
float TensMhz = 0;

volatile long radix = 1000;
volatile long oldradix = 1000;
int button_delay = 150;

volatile uint32_t LSB_BFO_freq = 8000000;    // Crystal filter centre freq
volatile uint32_t USB_BFO_freq = 8000000;   // Crystal filter centre freq

// Rotary encoder pins and other inputs
static const int pushPin = 4;
static const int rotBPin = 3;
static const int rotAPin = 2;

// Rotary encoder variables, used by interrupt routines
volatile int rotState = 0;
volatile int rotAval = 1;
volatile int rotBval = 1;


// Usage: TFT(<model code>, SDA, SCL, CS, RST, RS/A0);
UTFT TFT(ITDB18SP, 11, 10, 9, 12, 8);   // Remember to change the model parameter to suit your display module!
Si5351 si5351;


void setup()
{
  // Set up frequency and radix switches
  pinMode(rotAPin, INPUT);
  pinMode(rotBPin, INPUT);
  pinMode(pushPin, INPUT);
  pinMode(A0, INPUT);
  pinMode(A1, INPUT);
  pinMode(A2, INPUT);
  pinMode(A3, INPUT);

  // Set up pull-up resistors on inputs
  digitalWrite(rotAPin, HIGH);
  digitalWrite(rotBPin, HIGH);
  digitalWrite(pushPin, HIGH);
  digitalWrite(A0, HIGH);
  digitalWrite(A1, HIGH);
  digitalWrite(A2, HIGH);
  digitalWrite(A3, HIGH);

  // Set up interrupt pins
  attachInterrupt(digitalPinToInterrupt(rotAPin), ISRrotAChange, CHANGE);
  attachInterrupt(digitalPinToInterrupt(rotBPin), ISRrotBChange, CHANGE);

  // Setup the LCD
  TFT.InitLCD();
  TFT.clrScr();
  TFT.setFont(SmallFont);
  SetupScreen();

  // Initialize the DDS
  si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0);
  si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA);
  si5351.drive_strength(SI5351_CLK0, SI5351_DRIVE_8MA);
  si5351.drive_strength(SI5351_CLK2, SI5351_DRIVE_8MA);
}

void loop()
{
  currentfreq = getfreq();                // Interrupt safe method to get the current frequency

  if (currentfreq != oldfreq)
  {
    UpdateFreq();
    SendFrequency();
    oldfreq = currentfreq;
  }


  if (digitalRead(A3) == LOW)
  {
    delay(10);
    if (digitalRead(A3) == LOW)
    {
      if (radix == 1000000)
        radix = 100000;
      else if (radix == 100000)
        radix = 10000;
      else if (radix == 10000)
        radix = 1000;
      else if (radix == 1000)
        radix = 100;
      else if (radix == 100)
        radix = 10;
      else if (radix == 10)
        radix = 1;
      else
        radix = 1000000;
    }
    delay(button_delay);
    UpdateFreq();
  }


  if (digitalRead(A1) == LOW)
  {
    delay(10);
    if (digitalRead(A1) == LOW)
    {
      if (radix == 1)
        radix = 10;
      else if (radix == 10)
        radix = 100;
      else if (radix == 100)
        radix = 1000;
      else if (radix == 1000)
        radix = 10000;
      else if (radix == 10000)
        radix = 100000;
      else if (radix == 100000)
        radix = 1000000;
      else
        radix = 1;
    }
    delay(button_delay);
    UpdateFreq();
  }
}


long getfreq()
{
  long temp_freq;
  cli();
  temp_freq = freq;
  sei();
  return temp_freq;
}


// Interrupt routines
void ISRrotAChange()
{
  if (digitalRead(rotAPin))
  {
    rotAval = 1;
    UpdateRot();
  }
  else
  {
    rotAval = 0;
    UpdateRot();
  }
}


void ISRrotBChange()
{
  if (digitalRead(rotBPin))
  {
    rotBval = 1;
    UpdateRot();
  }
  else
  {
    rotBval = 0;
    UpdateRot();
  }
}


void UpdateRot()
{
  switch (rotState)
  {

    case 0:                                         // Idle state, look for direction
      if (!rotBval)
        rotState = 1;                               // CW 1
      if (!rotAval)
        rotState = 11;                              // CCW 1
      break;

    case 1:                                         // CW, wait for A low while B is low
      if (!rotBval)
      {
        if (!rotAval)
        {
          freq = (freq + radix);
          if (freq > bandEnd)
            freq = bandEnd;
          rotState = 2;                             // CW 2
        }
      }
      else if (rotAval)
        rotState = 0;                             // It was just a glitch on B, go back to start
      break;

    case 2:                                         // CW, wait for B high
      if (rotBval)
        rotState = 3;                               // CW 3
      break;

    case 3:                                         // CW, wait for A high
      if (rotAval)
        rotState = 0;                               // back to idle (detent) state
      break;

    case 11:                                        // CCW, wait for B low while A is low
      if (!rotAval)
      {
        if (!rotBval)
        {
          freq = (freq - radix);
          if (freq < bandStart)
            freq = bandStart;
          rotState = 12;                            // CCW 2
        }
      }
      else if (rotBval)
        rotState = 0;                             // It was just a glitch on A, go back to start
      break;

    case 12:                                        // CCW, wait for A high
      if (rotAval)
        rotState = 13;                              // CCW 3
      break;

    case 13:                                        // CCW, wait for B high
      if (rotBval)
        rotState = 0;                               // back to idle (detent) state
      break;
  }
}


void SetupScreen()
{
  TFT.setColor(VGA_BLUE);
  TFT.fillRect(0, 0, 159, 13);
  TFT.drawRect(0, 0, 159, 127);
  TFT.setColor(VGA_WHITE);
  TFT.setBackColor(VGA_BLUE);
  TFT.print("ZL2CTM Base Rig", CENTER, 1);

  TFT.setBackColor(VGA_BLACK);
  TFT.setColor(VGA_AQUA);
  TFT.print("VFO-A", 10, 20);
  TFT.setColor(VGA_GRAY);
  TFT.print("VFO-B", 10, 40);
  TFT.setColor(VGA_GRAY);
  TFT.print("MEM-10", 10, 60);
  TFT.setColor(VGA_GRAY);
  TFT.print("SCAN-12", 10, 80);
}

void UpdateFreq()
{
  TensMhz = freq / 10000000;                                // TensMHz = 12345678 / 10000000 = 1
  remainder = freq - (TensMhz * 10000000);                  // remainder = 12345678 - 10000000 = 2345678
  OnesMHz = remainder / 1000000;                            // OnesMhz = 2345678 / 1000000 = 2
  remainder = remainder - (OnesMHz * 1000000);              // remainder = 2345678 - (2 * 1000000) = 345678
  HundredskHz = remainder / 100000;                         // HundredskHz = 345678 / 100000 = 3
  remainder = remainder - (HundredskHz * 100000);           // remainder = 345678 - (3 * 100000) = 45678
  TenskHz = remainder / 10000;                              // TenskHz = 45678 / 10000 = 4
  remainder = remainder - (TenskHz * 10000);                // remainder = 45678 - (4 * 10000) = 5678
  OneskHz = remainder / 1000;                               // OneskHz = 5678 / 1000 = 5
  remainder = remainder - (OneskHz * 1000);                 // remainder = 5678 - (5 * 1000) = 678
  HundredsHz = remainder / 100;                             // HundredsHz = 678 / 100 = 6
  remainder = remainder - (HundredsHz * 100);               // remainder = 678 - (6 * 100) = 78
  TensHz = remainder / 10;                                  // TensHz = 78 / 10 = 7
  remainder = remainder - (TensHz * 10);                    // remainder = 78 - (7 * 10) = 8
  OnesHz = remainder;                                       // OnesHz = 8

  if (TensMhz == 0)
  {
    TFT.setColor(VGA_BLACK);
    TFT.printNumI(TensMhz, 75, 20);
  }
  if (TensMhz > 0)
  {
    TFT.setColor(VGA_AQUA);
    TFT.printNumI(TensMhz, 75, 20);
  }

  TFT.setColor(VGA_AQUA);
  TFT.printNumI(OnesMHz, 84, 20);
  TFT.print(".", 93, 20);
  TFT.printNumI(HundredskHz, 102, 20);
  TFT.printNumI(TenskHz, 111, 20);
  TFT.printNumI(OneskHz, 120, 20);
  TFT.printNumI(HundredsHz, 129, 20);
  TFT.printNumI(TensHz, 138, 20);
  TFT.printNumI(OnesHz, 147, 20);

  //Radix
  TFT.setColor(VGA_BLACK);
  //TFT.setFont(SmallFont);
  if (oldradix == 1)
    TFT.drawLine(147, 31, 151, 31);
  else if (oldradix == 10)
    TFT.drawLine(138, 31, 142, 31);
  else if (oldradix == 100)
    TFT.drawLine(129, 31, 133, 31);
  else if (oldradix == 1000)
    TFT.drawLine(120, 31, 124, 31);
  else if (oldradix == 10000)
    TFT.drawLine(111, 31, 115, 31);
  else if (oldradix == 100000)
    TFT.drawLine(102, 31, 106, 31);
  else if (oldradix == 1000000)
    TFT.drawLine(84, 31, 88, 31);

  TFT.setColor(VGA_AQUA);
  //TFT.setFont(SmallFont);
  if (radix == 1)
    TFT.drawLine(147, 31, 151, 31);
  else if (radix == 10)
    TFT.drawLine(138, 31, 142, 31);
  else if (radix == 100)
    TFT.drawLine(129, 31, 133, 31);
  else if (radix == 1000)
    TFT.drawLine(120, 31, 124, 31);
  else if (radix == 10000)
    TFT.drawLine(111, 31, 115, 31);
  else if (radix == 100000)
    TFT.drawLine(102, 31, 106, 31);
  else if (radix == 1000000)
    TFT.drawLine(84, 31, 88, 31);

  oldradix = radix;
}

void SendFrequency()
{
  if (freq <= 8000000)
  {
    // VFO
    si5351.set_freq(((freq + LSB_BFO_freq) * 100ULL), SI5351_PLL_FIXED, SI5351_CLK0);
    // BFO
    si5351.set_freq((LSB_BFO_freq * 100ULL), SI5351_PLL_FIXED, SI5351_CLK2);
  }
  
  if (freq > 8000000)
  {
    // VFO
    si5351.set_freq(((freq - USB_BFO_freq) * 100ULL), SI5351_PLL_FIXED, SI5351_CLK0);
    // BFO
    si5351.set_freq((USB_BFO_freq * 100ULL), SI5351_PLL_FIXED, SI5351_CLK2);
  }
}

Friday 24 November 2017

80m SSB Tramping Rig Arduino Code

#include <Wire.h>
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <si5351.h>

const uint32_t bandStart = 3500000;     // start of 80m
const uint32_t bandEnd =   3900000;    // end of 80m
const uint32_t bandInit =  3690000;     // where to initially set the frequency
volatile long oldfreq = 0;
volatile long currentfreq;
volatile int updatedisplay = 0;

volatile uint32_t freq = bandInit ;
volatile uint32_t vfo = bandInit;
volatile uint32_t radix = 1000;

volatile uint32_t LSB_IF_freq = 9000000;   // Crystal filter centre freq
volatile uint32_t LSB_BFO_freq = 9001500;   // Crystal filter centre freq

// Rotary encoder pins and other inputs
static const int pushPin = 9;
static const int rotBPin = 3;
static const int rotAPin = 2;

// Rotary encoder variables, used by interrupt routines
volatile int rotState = 0;
volatile int rotAval = 1;
volatile int rotBval = 1;
volatile int rotAcc = 0;

// Instantiate the Objects
Adafruit_SSD1306 display(4);
Si5351 si5351;

void setup()
{

  // Set up frequency and radix switches
  pinMode(rotAPin, INPUT);
  pinMode(rotBPin, INPUT);
  pinMode(pushPin, INPUT);

  // set up pull-up resistors on inputs
  digitalWrite(rotAPin, HIGH);
  digitalWrite(rotBPin, HIGH);
  digitalWrite(pushPin, HIGH);

  attachInterrupt(digitalPinToInterrupt(rotAPin), ISRrotAChange, CHANGE);
  attachInterrupt(digitalPinToInterrupt(rotBPin), ISRrotBChange, CHANGE);

  // Initialize the display with the I2C addr 0x3C
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.setTextColor(WHITE);
  display.clearDisplay();
  display.display();
  delay(1000);
  UpdateDisplay();
  delay(1000);

  // Initialize the DDS
  si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0);
  si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA);
  si5351.set_freq(bandInit, SI5351_PLL_FIXED, SI5351_CLK0);
  si5351.drive_strength(SI5351_CLK0, SI5351_DRIVE_8MA);
  si5351.drive_strength(SI5351_CLK2, SI5351_DRIVE_8MA);
  si5351.set_freq((freq * 100ULL), SI5351_PLL_FIXED, SI5351_CLK0);
  si5351.set_freq((LSB_BFO_freq * 100ULL), SI5351_PLL_FIXED, SI5351_CLK2);
}


void loop()
{
  currentfreq = getfreq();                // Interrupt safe method to get the current frequency

  if (currentfreq != oldfreq)
  {
    UpdateDisplay();
    SendFrequency();
    oldfreq = currentfreq;
  }

  if (!digitalRead(pushPin)) {
    delay(10);
    while (!digitalRead(pushPin))
    {
      if (updatedisplay == 1)
      {
        UpdateDisplay();
        updatedisplay = 0;
      }
    }
    delay(50);
  }
}

long getfreq()
{
  long temp_freq;
  cli();
  temp_freq = freq;
  sei();
  return temp_freq;
}


// Interrupt routines
void ISRrotAChange()
{
  if (digitalRead(rotAPin))
  {
    rotAval = 1;
    UpdateRot();
  } else
  {
    rotAval = 0;
    UpdateRot();
  }
}


void ISRrotBChange()
{
  if (digitalRead(rotBPin))
  {
    rotBval = 1;
    UpdateRot();
  } else
  {
    rotBval = 0;
    UpdateRot();
  }
}


void UpdateRot()
{
  switch (rotState)
  {

    case 0:                                         // Idle state, look for direction
      if (!rotBval)
        rotState = 1;                               // CW 1
      if (!rotAval)
        rotState = 11;                              // CCW 1
      break;

    case 1:                                         // CW, wait for A low while B is low
      if (!rotBval)
      {
        if (!rotAval)
        {
          // either increment radixindex or freq
          if (digitalRead(pushPin) == LOW)
          {
            updatedisplay = 1;
            if (radix == 1000000)
              radix = 100000;
            else if (radix == 100000)
              radix = 10000;
            else if (radix == 10000)
              radix = 1000;
            else if (radix == 1000)
              radix = 100;
            else if (radix == 100)
              radix = 10;
            else if (radix == 10)
              radix = 1;
            else
              radix = 1000000;
          } else
          {
            freq = (freq + radix);
            if (freq > 3900000)
              freq = 3900000;
          }
          rotState = 2;                             // CW 2
        }
      } else
      {
        if (rotAval)
          rotState = 0;                             // It was just a glitch on B, go back to start
      }
      break;

    case 2:                                         // CW, wait for B high
      if (rotBval)
        rotState = 3;                               // CW 3
      break;

    case 3:                                         // CW, wait for A high
      if (rotAval)
        rotState = 0;                               // back to idle (detent) state
      break;

    case 11:                                        // CCW, wait for B low while A is low
      if (!rotAval)
      {
        if (!rotBval)
        {
          // either decrement radixindex or freq
          if (digitalRead(pushPin) == LOW)
          {
            updatedisplay = 1;
            if (radix == 1)
              radix = 10;
            else if (radix == 10)
              radix = 100;
            else if (radix == 100)
              radix = 1000;
            else if (radix == 1000)
              radix = 10000;
            else if (radix == 10000)
              radix = 100000;
            else if (radix == 100000)
              radix = 1000000;
            else
              radix = 1;
          } else
          {
            freq = (freq - radix);
            if (freq < 3500000)
              freq = 3500000;
          }
          rotState = 12;                            // CCW 2
        }
      } else
      {
        if (rotBval)
          rotState = 0;                             // It was just a glitch on A, go back to start
      }
      break;

    case 12:                                        // CCW, wait for A high
      if (rotAval)
        rotState = 13;                              // CCW 3
      break;

    case 13:                                        // CCW, wait for B high
      if (rotBval)
        rotState = 0;                               // back to idle (detent) state
      break;
  }
}


void UpdateDisplay()
{
  display.clearDisplay();
  display.setCursor(0, 0);
  display.setTextSize(1);
  display.println("ZL2CTM Tramping Rig");

  display.setCursor(0, 20);
  display.setTextSize(2);
  display.println(freq);

  if (freq > 9999999)
    display.setCursor(12, 30);
  if (freq < 9999999)
    display.setCursor(0, 30);
  switch (radix)
  {
    case 1:
      display.println("      -");
      break;
    case 10:
      display.println("     -");
      break;
    case 100:
      display.println("    -");
      break;
    case 1000:
      display.println("   -");
      break;
    case 10000:
      display.println("  -");
      break;
    case 100000:
      display.println(" -");
      break;
    case 1000000:
      display.println("-");
      break;
  }
  display.setCursor(0, 48);
  display.setTextSize(2);
  display.println("LSB");
  display.setCursor(60, 48);
  if ((freq >= 3500000) && (freq <= 3900000))
    display.println("80m");
  display.display();
}


void SendFrequency()
{
  // VFO
  si5351.set_freq(((LSB_IF_freq - freq + 1500 - 70) * 100ULL), SI5351_PLL_FIXED, SI5351_CLK0);

  // BFO
  si5351.set_freq((LSB_BFO_freq * 100ULL), SI5351_PLL_FIXED, SI5351_CLK2);
}

80m SSB Tramping Rig Schematics

Here the are the schematics for the rig. Some parts have not been optimised as per the notes on the schematics.











Wednesday 6 September 2017

Superhet Configuration

Hi. Just put up a video on YouTube of a superhet experiment using the mixer below. The mixer is using two J310s as a pseudo dual gate MOSFET. It seems to work well. Plan is to make a second one as use that as the product detector.

73's
Charlie



Thursday 17 August 2017

Design Goals

Thanks for all the feedback. In an effort to keep the cost down where we can, here is a list of the features we'll look to design into the radio:

  • 80m, 40m and 20m pre-selector BPFs
  • 2N3904 based RF receive amp with manual attenuation
  • 2N3904 based IF amps (not bilateral at this stage)
  • Diode low power signal switching (no relays)
  • Junkbox Yaesu XF-92A 9000kHz SSB crystal filter
  • J310 or some other discrete component based mixers. Otherwise homebrew DBMs
  • Teensy (any version should work, I'll use a 3.1 as I have one in the junkbox)
    • FFT spectrum display
    • Software LPFs: CW-N (200Hz), CW-W (700Hz) and perhaps a notch
    • 2.8kHz LPF for the mic audio
    • Compression on the mic audio if we can 
    • 700Hz sinewave for CW modulation
    • Software based audio AGC
    • Drive external analogue S meter
    • Memory channels
    • VFO and Memory scan
  • Si5351 for VFO and BFO. Teensy controlled
  • 0.96" OLED
  • 2N3906 PA pre-driver
  • IRF520 or IRF510 PA (will start with cheaper 520 first)
  • 80m, 40m and 20m LPFs (probably wafer switch selected)

Again, I am not an expert in homebrew, so I'll just do what has worked for me in the past. Hopefully, we can make it an interactive process where we all learn.

I'll be away for a couple of weeks. Once I'm back, we'll start with a basic Teensy setup to drive the screen, rotary encoder and Si5351. It'll have basis audio pass through for initial test. That will then form the basis for the rest of the radio.

Until then.

73's
Charlie, ZL2CTM  



Saturday 12 August 2017

New Radio Project.

Hi. This is a new blog, and am just testing it. Below is a picture of the Teensy driving the OLED screen and the Si5351. On the screen is a FFT spectrum showing 0-22kHz. More to come!