Wednesday 9 September 2015

Concept Session 2 - VFO & RTC

Here are the slides for the next Concept Session 2. This will cover the breadboarding of the VFO and RTC modules. Concept will conclude with the construction two shields for the Arduino UNO

Concept S2 VFO RTC 003

For the whole system to work there has to be a standard interconnect between shields. This is

Concept S2 VFO RTC 004

The important part is the new connector on the left, carrying the RF signals - VFO, RX and TX.

Receivers and Transmitters

Concept S2 VFO RTC 007

Concept S2 VFO RTC 008

A VFO needs three things, a VFO and BFO output and a quadrature I & Q output. Thus our Shield will look like this.

Concept S2 VFO RTC 009

Concept S2 VFO RTC 010

The VFO is based around the Si5351, which has 3 RF outputs, Outputs 1 & 2 are for VFO & BFO, output 3 is connected to a SN74AC74 as a Johnson counter to generate the IQ signals for an SDR.

Concept S2 VFO RTC 012

Concept S2 VFO RTC 013

The RTC is also a pre-built module using the DS3231 RTC chip and a backup battery.

Concept S2 VFO RTC 014

Build a VFO

Concept S2 VFO RTC 015

Code

This code runs the Si5351, with frequency display on the Arduion Monitor and new frequency input on the keyboard.

// My_VFO_KB is a keyboard input/screen display VFO
// Starter kit VFO using Si5351 module, freq in cHz
// Si5351 I2C bus
// SDA = A4
// SCL = A5

// I2C and Si5351 Libraries
#include "Wire.h"
#include "si5351.h"

// create dds object
Si5351 dds;

// start frequency (cHz)
uint32_t freq = 700000000; // 7MHz
uint32_t prevFreq = freq;

// setup runs once on upload
void setup(){
  // start serial (monitor "NEWLINE" & 9600 baud)
  Serial.begin(9600);
  
  // init dds si5351 module, "0" = default 25MHz XTAL
  dds.init(SI5351_CRYSTAL_LOAD_8PF, 0);

  // set 8mA output drive
  dds.drive_strength(SI5351_CLK0, SI5351_DRIVE_8MA);

  // enable VFO output CLK0, disable CLK1 & 2
  dds.output_enable(SI5351_CLK0, 1);
  dds.output_enable(SI5351_CLK1, 0);
  dds.output_enable(SI5351_CLK2, 0);
  
  freqOut(freq); // output freq
  dispFreq(freq); // display freq in Hz
}

void loop(){
   freq = getIn(); // get input freq cHz
   
  // new freq?
  if(freq != prevFreq)
  {
    freqOut(freq); // output freq
    dispFreq(freq); // display in Hz
    prevFreq = freq; // remember as previous freq
  }
}

// freq output in cHz on CLK0
void freqOut(uint32_t freq){
    dds.set_freq(freq, 0, SI5351_CLK0); // cHz
}

// get input Hz, return cHz
uint32_t getIn(){
  uint32_t in;
  
  while(Serial.available() > 0) // flush buffer
    Serial.read();
    
  while(Serial.available() == 0) // wait for input
    in = Serial.parseInt(); // read input in Hz and parse to integer
   
  return in * 100UL; // return in cHz
}

// display frequency on monitor
void dispFreq(uint32_t f){
  // display freq
  Serial.print("My_VFO = ");
  Serial.print((float)f / 100, 0); // convert to float & display in Hz
  Serial.println(" Hz");
}


Build an RTC

Concept S2 VFO RTC 020

Concept S2 VFO RTC 021

Concept S2 VFO RTC 022

Code

These two sketches first set the RTC time, then display it on an I2C connected LCD display

// My_RTC set
// enter YYMMDDwHHMMSS on Monitor, w = week day
// note 1 = mon. 01 = Jan
// hit ENTER exactly on the time you want to set

#include "Wire.h"
#include "DS3231.h"
#include "LiquidCrystal_I2C.h"

// LCD address, cols, rows
#define LCDADDR 0x27
#define LCDCOLS 16
#define LCDROWS 2

// RTC address
#define RTCADDR 0x68

// LCD object
LiquidCrystal_I2C lcd(LCDADDR, LCDCOLS, LCDROWS);

// rtc object
DS3231 rtc;

// RTC time and date
byte Second, prevSecond, Minute, Hour, DoW, Date, prevDate, Month, Year;

bool gotString;

void setup() {
  // Start the serial port
  Serial.begin(9600);

  // init LCD & backlight on
  lcd.init();
  lcd.backlight();

  // Start the I2C interface
  Wire.begin();

  dispMsg(0, 0, "Enter time:           ");
  dispMsg(0, 1, "\"YYMMDDwHHMMSS\"");

  gotString = false;
}

void loop() {
  char inString[20] = "";
  byte j = 0;
  
  while (!gotString) {
    if (Serial.available()) {
      inString[j] = Serial.read();

      if (inString[j] == '\n') {
        gotString = true;

        Serial.println(inString);

        // convert ASCII codes to bytes
        Year = ((byte)inString[0] - 48) * 10 + (byte)inString[1] - 48;
        Month = ((byte)inString[2] - 48) * 10 + (byte)inString[3] - 48;
        Date = ((byte)inString[4] - 48) * 10 + (byte)inString[5] - 48;
        DoW = ((byte)inString[6] - 48);
        Hour = ((byte)inString[7] - 48) * 10 + (byte)inString[8] - 48;
        Minute = ((byte)inString[9] - 48) * 10 + (byte)inString[10] - 48;
        Second = ((byte)inString[11] - 48) * 10 + (byte)inString[12] - 48;

        rtc.setYear(Year);
        rtc.setMonth(Month);
        rtc.setDate(Date);
        rtc.setDoW(DoW);
        rtc.setHour(Hour);
        rtc.setMinute(Minute);
        rtc.setSecond(Second);
      }
      j += 1;
    }
  }

  getRTC(); // get time
  
  if(Second != prevSecond) {
      dispTime(4, 1); // display it, if changed
      prevSecond = Second;
  }
  if(Date != prevDate) {
    dispDate(0, 0);
    prevDate = Date;
  }
}

// get time from RTC, convert bcd to decimal
void getRTC() {
  // Reset the RTC register pointer
  Wire.beginTransmission(RTCADDR);
  byte zero = 0x00;
  Wire.write(zero);
  Wire.endTransmission();

  // request 7 bytes from the RTC address
  Wire.requestFrom(RTCADDR, 7);

  // get the time data
  Second = bcdToDec(Wire.read()); // 0 - 59
  Minute = bcdToDec(Wire.read()); // 0 - 59
  Hour = bcdToDec(Wire.read() & 0b111111); //mask 12/24 bit
  DoW = bcdToDec(Wire.read()); //0 - 6 = Sunday - Saturday
  Date = bcdToDec(Wire.read()); // 1 - 31
  Month = bcdToDec(Wire.read()); // 0 = jan
  Year = bcdToDec(Wire.read()); // 20xx
}

// Convert binary coded decimal to normal decimal numbers
byte bcdToDec(byte val) {
  return ( (val / 16 * 10) + (val % 16) );
}

// display char msg at col c, row r
void dispMsg(uint8_t c, uint8_t r, char *m) {
  lcd.setCursor(c, r);
  lcd.print(m);
}

// display date and time
void dispDate(byte c, byte r) {
  lcd.clear();
  
  lcd.setCursor(c, r);
  switch (DoW) {
    case 1:
      lcd.print("Mon");
      break;
    case 2:
      lcd.print("Tue");
      break;
    case 3:
      lcd.print("Wed");
      break;
    case 4:
      lcd.print("Thu");
      break;
    case 5:
      lcd.print("Fri");
      break;
    case 6:
    lcd.print("Sat");
    break;
    case 7:
    lcd.print("Sun");
    break;
  }

  lcd.print(" ");
  lcd.print(Date);

  lcd.print(" ");
  switch (Month)
  {
    case 1:
      lcd.print("Jan");
      break;
    case 2:
      lcd.print("Feb");
      break;
    case 3:
      lcd.print("Mar");
      break;
    case 4:
      lcd.print("Apr");
      break;
    case 5:
      lcd.print("May");
      break;
    case 6:
      lcd.print("Jun");
      break;
    case 7:
      lcd.print("Jul");
      break;
    case 8:
      lcd.print("Aug");
      break;
    case 9:
      lcd.print("Sep");
      break;
    case 10:
      lcd.print("Oct");
      break;
    case 11:
      lcd.print("Nov");
      break;
    case 12:
      lcd.print("Dec");
      break;
  }
  lcd.print(" ");
  lcd.print("20");
  lcd.print(Year);
}

void dispTime(byte c, byte r) {
  lcd.setCursor(c, r);
  if (Hour < 10)
    lcd.print("0");
  lcd.print(Hour);
  lcd.print(":");
  if (Minute < 10)
    lcd.print("0");
  lcd.print(Minute);
  lcd.print(":");
  if (Second < 10)
    lcd.print("0");
  lcd.print(Second);
}


// My_RTC_LCD
// display time on LCD I2C

// CONNECTIONS
// RTC DS1307 or DS3231
// SCL = A5
// SDA = A4
// I2C address 0x57
// ------
// display LCD I2C
// o A5 SCL
// o A4 SDA
// o +5
// o GND
// I2C address 0x27

// libraries
#include "Wire.h"
#include "LiquidCrystal_I2C.h"

// RTC I2C address
#define RTCADDR 0x68

// LCD
#define LCDADDR 0x27
#define LCDCOLS 16
#define LCDROWS 2

// LCD object
LiquidCrystal_I2C lcd(LCDADDR, LCDCOLS, LCDROWS);

// RTC time and date
byte Second, prevSecond, Minute, Hour, DoW, Date, prevDate, Month, Year;

void setup() {
  // initialise the wire library for I2C comms
  Wire.begin();

  // init LCD & backlight on
  lcd.init();
  lcd.backlight();

  getRTC();
  dispDate(0, 0); //  display date & time
  dispTime(4, 1);
  prevSecond = Second; // save current second & date
  prevDate = Date;
}

void loop() {
  getRTC(); // get time
  if (Second != prevSecond) {
    dispTime(4, 1); // display it, if changed
    prevSecond = Second;
  }
  if (Date != prevDate) {
    dispDate(0, 0);
    prevDate = Date;
  }
}

// get time from RTC, convert bcd to decimal
void getRTC() {
  // Reset the RTC register pointer
  Wire.beginTransmission(RTCADDR);
  byte zero = 0x00;
  Wire.write(zero);
  Wire.endTransmission();

  // request 7 bytes from the RTC address
  Wire.requestFrom(RTCADDR, 7);

  // get the time data
  Second = bcdToDec(Wire.read()); // 0 - 59
  Minute = bcdToDec(Wire.read()); // 0 - 59
  Hour = bcdToDec(Wire.read() & 0b111111); // mask 12/24 bit
  DoW = bcdToDec(Wire.read()); //0 - 6 = Sunday - Saturday
  Date = bcdToDec(Wire.read()); // 1 - 31
  Month = bcdToDec(Wire.read()); // 0 = jan
  Year = bcdToDec(Wire.read()); // 20xx
}

// Convert binary coded decimal to normal decimal numbers
byte bcdToDec(byte val) {
  return ( (val / 16 * 10) + (val % 16) );
}

// display date and time
void dispDate(byte c, byte r) {
  lcd.clear();
  
  lcd.setCursor(c, r);
 switch (DoW) {
    case 1:
      lcd.print("Mon");
      break;
    case 2:
      lcd.print("Tue");
      break;
    case 3:
      lcd.print("Wed");
      break;
    case 4:
      lcd.print("Thu");
      break;
    case 5:
      lcd.print("Fri");
      break;
    case 6:
    lcd.print("Sat");
    break;
    case 7:
    lcd.print("Sun");
    break;
  }

  lcd.print(" ");
  lcd.print(Date);

  lcd.print(" ");
  switch (Month)
  {
    case 1:
      lcd.print("Jan");
      break;
    case 2:
      lcd.print("Feb");
      break;
    case 3:
      lcd.print("Mar");
      break;
    case 4:
      lcd.print("Apr");
      break;
    case 5:
      lcd.print("May");
      break;
    case 6:
      lcd.print("Jun");
      break;
    case 7:
      lcd.print("Jul");
      break;
    case 8:
      lcd.print("Aug");
      break;
    case 9:
      lcd.print("Sep");
      break;
    case 10:
      lcd.print("Oct");
      break;
    case 11:
      lcd.print("Nov");
      break;
    case 12:
      lcd.print("Dec");
      break;
  }
  lcd.print(" ");
  lcd.print("20");
  lcd.print(Year);
}

void dispTime(byte c, byte r) {
  lcd.setCursor(c, r);
  if (Hour < 10)
    lcd.print("0");
  lcd.print(Hour);
  lcd.print(":");
  if (Minute < 10)
    lcd.print("0");
  lcd.print(Minute);
  lcd.print(":");
  if (Second < 10)
    lcd.print("0");
  lcd.print(Second);
}


Concept S2 VFO RTC 026

No comments: