Merge pull request 'Try CesarSound VFO+OLED code' (#9) from cesarsound into main
Reviewed-on: #9
This commit is contained in:
commit
f3c9350910
BIN
lib/Rotary-master.zip
Normal file
BIN
lib/Rotary-master.zip
Normal file
Binary file not shown.
76
lib/Rotary/README.md
Normal file
76
lib/Rotary/README.md
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
Rotary Encoder Arduino Library
|
||||||
|
==============================
|
||||||
|
|
||||||
|
Arduino library for reading rotary encoders that output a 2-bit [gray code](http://en.wikipedia.org/wiki/Gray_code).
|
||||||
|
|
||||||
|
Rotary r = Rotary(2, 3);
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
r.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
result = r.process();
|
||||||
|
if (result) {
|
||||||
|
Serial.println(result == DIR_CW ? "Right" : "Left");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
This is a repackaged version of Ben Buxton's excellent [rotary library](http://www.buxtronix.net/2011/10/rotary-encoders-done-properly.html) organized for the Arduino 1.x IDE, keyword highlighting, polling example, Arduino library capitalization conventions.
|
||||||
|
|
||||||
|
Features
|
||||||
|
--------
|
||||||
|
* Debounce handling with support for high rotation speeds
|
||||||
|
* Correctly handles direction changes mid-step
|
||||||
|
* Checks for valid state changes for more robust counting / noise immunity
|
||||||
|
* Interrupt based or polling in loop()
|
||||||
|
* Counts full-steps (default) or half-steps
|
||||||
|
* Supports use with pull-up (default) and pull-down resistors
|
||||||
|
|
||||||
|
Installation / Usage
|
||||||
|
--------------------
|
||||||
|
1. Download and unzip to Arduino\\libraries\\Rotary. So for example Rotary.h will be in Arduino\\libraries\\Rotary\\Rotary.h.
|
||||||
|
2. Restart Arduino IDE
|
||||||
|
3. File -> Examples -> Rotary
|
||||||
|
|
||||||
|
Note: Resistor usage is specified through `void begin(bool internalPullup=true, bool flipLogicForPulldown=false)`.
|
||||||
|
* `r.begin()` enables the Arduino's internal weak pull-ups for the rotary's pins
|
||||||
|
* `r.begin(false)` disables the Arduino's internal weak pull-ups for the given pins and configures the rotary for use with external _pull-ups_
|
||||||
|
* `r.begin(false, true)` disables the internal pull-ups and flips the pin logic for use with external _pull-downs_
|
||||||
|
|
||||||
|
Background
|
||||||
|
----------
|
||||||
|
A typical mechanical rotary encoder emits a two bit gray code on 3 output pins. Every step in the output (often accompanied by a physical 'click') generates a specific sequence of output codes on the pins.
|
||||||
|
|
||||||
|
There are 3 pins used for the rotary encoding - one common and two 'bit' pins.
|
||||||
|
|
||||||
|
The following is the typical sequence of code on the output when moving from one step to the next:
|
||||||
|
|
||||||
|
Position Bit1 Bit2
|
||||||
|
- - - - - - - - - - -
|
||||||
|
Step1 0 0
|
||||||
|
1/4 1 0
|
||||||
|
1/2 1 1
|
||||||
|
3/4 0 1
|
||||||
|
Step2 0 0
|
||||||
|
|
||||||
|
From this table, we can see that when moving from one 'click' to the next, there are 4 changes in the output code.
|
||||||
|
|
||||||
|
- From an initial 0 - 0, Bit1 goes high, Bit0 stays low.
|
||||||
|
- Then both bits are high, halfway through the step.
|
||||||
|
- Then Bit1 goes low, but Bit2 stays high.
|
||||||
|
- Finally at the end of the step, both bits return to 0.
|
||||||
|
|
||||||
|
Detecting the direction is easy - the table simply goes in the other direction (read up instead of down).
|
||||||
|
|
||||||
|
To decode this, we use a simple state machine. Every time the output code changes, it follows state, until finally a full steps worth of code is received (in the correct order). At the final 0-0, it returns a value indicating a step in one direction or the other.
|
||||||
|
|
||||||
|
It's also possible to use 'half-step' mode. This just emits an event at both the 0-0 and 1-1 positions. This might be useful for some encoders where you want to detect all positions. In Rotary.h, uncomment `#define HALF_STEP` to enable half-step mode.
|
||||||
|
|
||||||
|
If an invalid state happens (for example we go from '0-1' straight to '1-0'), the state machine resets to the start until 0-0 and the next valid codes occur.
|
||||||
|
|
||||||
|
The biggest advantage of using a state machine over other algorithms is that this has inherent debounce built in. Other algorithms emit spurious output with switch bounce, but this one will simply flip between sub-states until the bounce settles, then continue along the state machine. A side effect of debounce is that fast rotations can cause steps to be skipped. By not requiring debounce, fast rotations can be accurately measured. Another advantage is the ability to properly handle bad state, such as due to EMI, etc. It is also a lot simpler than others - a static state table and less than 10 lines of logic.
|
||||||
|
|
||||||
|
License
|
||||||
|
-------
|
||||||
|
GNU GPL Version 3
|
102
lib/Rotary/Rotary.cpp
Normal file
102
lib/Rotary/Rotary.cpp
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
/* Rotary encoder handler for arduino.
|
||||||
|
*
|
||||||
|
* Copyright 2011 Ben Buxton. Licenced under the GNU GPL Version 3.
|
||||||
|
* Contact: bb@cactii.net
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Arduino.h"
|
||||||
|
#include "Rotary.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The below state table has, for each state (row), the new state
|
||||||
|
* to set based on the next encoder output. From left to right in,
|
||||||
|
* the table, the encoder outputs are 00, 01, 10, 11, and the value
|
||||||
|
* in that position is the new state to set.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define R_START 0x0
|
||||||
|
|
||||||
|
#ifdef HALF_STEP
|
||||||
|
// Use the half-step state table (emits a code at 00 and 11)
|
||||||
|
#define R_CCW_BEGIN 0x1
|
||||||
|
#define R_CW_BEGIN 0x2
|
||||||
|
#define R_START_M 0x3
|
||||||
|
#define R_CW_BEGIN_M 0x4
|
||||||
|
#define R_CCW_BEGIN_M 0x5
|
||||||
|
const unsigned char ttable[6][4] = {
|
||||||
|
// R_START (00)
|
||||||
|
{R_START_M, R_CW_BEGIN, R_CCW_BEGIN, R_START},
|
||||||
|
// R_CCW_BEGIN
|
||||||
|
{R_START_M | DIR_CCW, R_START, R_CCW_BEGIN, R_START},
|
||||||
|
// R_CW_BEGIN
|
||||||
|
{R_START_M | DIR_CW, R_CW_BEGIN, R_START, R_START},
|
||||||
|
// R_START_M (11)
|
||||||
|
{R_START_M, R_CCW_BEGIN_M, R_CW_BEGIN_M, R_START},
|
||||||
|
// R_CW_BEGIN_M
|
||||||
|
{R_START_M, R_START_M, R_CW_BEGIN_M, R_START | DIR_CW},
|
||||||
|
// R_CCW_BEGIN_M
|
||||||
|
{R_START_M, R_CCW_BEGIN_M, R_START_M, R_START | DIR_CCW},
|
||||||
|
};
|
||||||
|
#else
|
||||||
|
// Use the full-step state table (emits a code at 00 only)
|
||||||
|
#define R_CW_FINAL 0x1
|
||||||
|
#define R_CW_BEGIN 0x2
|
||||||
|
#define R_CW_NEXT 0x3
|
||||||
|
#define R_CCW_BEGIN 0x4
|
||||||
|
#define R_CCW_FINAL 0x5
|
||||||
|
#define R_CCW_NEXT 0x6
|
||||||
|
|
||||||
|
const unsigned char ttable[7][4] = {
|
||||||
|
// R_START
|
||||||
|
{R_START, R_CW_BEGIN, R_CCW_BEGIN, R_START},
|
||||||
|
// R_CW_FINAL
|
||||||
|
{R_CW_NEXT, R_START, R_CW_FINAL, R_START | DIR_CW},
|
||||||
|
// R_CW_BEGIN
|
||||||
|
{R_CW_NEXT, R_CW_BEGIN, R_START, R_START},
|
||||||
|
// R_CW_NEXT
|
||||||
|
{R_CW_NEXT, R_CW_BEGIN, R_CW_FINAL, R_START},
|
||||||
|
// R_CCW_BEGIN
|
||||||
|
{R_CCW_NEXT, R_START, R_CCW_BEGIN, R_START},
|
||||||
|
// R_CCW_FINAL
|
||||||
|
{R_CCW_NEXT, R_CCW_FINAL, R_START, R_START | DIR_CCW},
|
||||||
|
// R_CCW_NEXT
|
||||||
|
{R_CCW_NEXT, R_CCW_FINAL, R_CCW_BEGIN, R_START},
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Constructor. Each arg is the pin number for each encoder contact.
|
||||||
|
*/
|
||||||
|
Rotary::Rotary(char _pin1, char _pin2) {
|
||||||
|
// Assign variables.
|
||||||
|
pin1 = _pin1;
|
||||||
|
pin2 = _pin2;
|
||||||
|
// Initialise state.
|
||||||
|
state = R_START;
|
||||||
|
// Don't invert read pin state by default
|
||||||
|
inverter = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Rotary::begin(bool internalPullup, bool flipLogicForPulldown) {
|
||||||
|
|
||||||
|
if (internalPullup){
|
||||||
|
// Enable weak pullups
|
||||||
|
pinMode(pin1,INPUT_PULLUP);
|
||||||
|
pinMode(pin2,INPUT_PULLUP);
|
||||||
|
}else{
|
||||||
|
// Set pins to input.
|
||||||
|
pinMode(pin1, INPUT);
|
||||||
|
pinMode(pin2, INPUT);
|
||||||
|
}
|
||||||
|
inverter = flipLogicForPulldown ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char Rotary::process() {
|
||||||
|
// Grab state of input pins.
|
||||||
|
unsigned char pinstate = ((inverter ^ digitalRead(pin2)) << 1) | (inverter ^ digitalRead(pin1));
|
||||||
|
// Determine new state from the pins and state table.
|
||||||
|
state = ttable[state & 0xf][pinstate];
|
||||||
|
// Return emit bits, ie the generated event.
|
||||||
|
return state & 0x30;
|
||||||
|
}
|
38
lib/Rotary/Rotary.h
Normal file
38
lib/Rotary/Rotary.h
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* Rotary encoder library for Arduino.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef Rotary_h
|
||||||
|
#define Rotary_h
|
||||||
|
|
||||||
|
#include "Arduino.h"
|
||||||
|
|
||||||
|
// Enable this to emit codes twice per step.
|
||||||
|
// #define HALF_STEP
|
||||||
|
|
||||||
|
// Values returned by 'process'
|
||||||
|
// No complete step yet.
|
||||||
|
#define DIR_NONE 0x0
|
||||||
|
// Clockwise step.
|
||||||
|
#define DIR_CW 0x10
|
||||||
|
// Counter-clockwise step.
|
||||||
|
#define DIR_CCW 0x20
|
||||||
|
|
||||||
|
class Rotary
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Rotary(char, char);
|
||||||
|
unsigned char process();
|
||||||
|
void begin(bool internalPullup=true, bool flipLogicForPulldown=false);
|
||||||
|
|
||||||
|
inline unsigned char pin_1() const { return pin1; }
|
||||||
|
inline unsigned char pin_2() const { return pin2; }
|
||||||
|
private:
|
||||||
|
unsigned char state;
|
||||||
|
unsigned char pin1;
|
||||||
|
unsigned char pin2;
|
||||||
|
unsigned char inverter;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
37
lib/Rotary/examples/Interrupt/Interrupt.ino
Normal file
37
lib/Rotary/examples/Interrupt/Interrupt.ino
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
Rotary Encoder - Interrupt Example
|
||||||
|
|
||||||
|
The circuit:
|
||||||
|
* encoder pin A to Arduino pin 2
|
||||||
|
* encoder pin B to Arduino pin 3
|
||||||
|
* encoder ground pin to ground (GND)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Rotary.h>
|
||||||
|
|
||||||
|
Rotary r = Rotary(2, 3);
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(9600);
|
||||||
|
r.begin();
|
||||||
|
PCICR |= (1 << PCIE2);
|
||||||
|
PCMSK2 |= (1 << PCINT18) | (1 << PCINT19);
|
||||||
|
sei();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ISR(PCINT2_vect) {
|
||||||
|
unsigned char result = r.process();
|
||||||
|
if (result == DIR_NONE) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
else if (result == DIR_CW) {
|
||||||
|
Serial.println("ClockWise");
|
||||||
|
}
|
||||||
|
else if (result == DIR_CCW) {
|
||||||
|
Serial.println("CounterClockWise");
|
||||||
|
}
|
||||||
|
}
|
50
lib/Rotary/examples/InterruptProMicro/InterruptProMicro.ino
Normal file
50
lib/Rotary/examples/InterruptProMicro/InterruptProMicro.ino
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
Rotary Encoder - Interrupt Example
|
||||||
|
This is the same as "Interrupt.ino" but works
|
||||||
|
the Pro Micro from Sparkfun which uses the
|
||||||
|
Atmega32U4 microcontroller. This controller
|
||||||
|
does not have the PCIE2 or PCMSK2 registers used in the
|
||||||
|
Interrupt example. Instead these are moved
|
||||||
|
to the PCIE0 and PCMSK0 registers instead.
|
||||||
|
|
||||||
|
These registers support interrupts on Pro Micro
|
||||||
|
pins 8, 9, 10 or 11. This example wires the encoder
|
||||||
|
to pins 8 and 9.
|
||||||
|
|
||||||
|
Note that the interrupt vector also changes to
|
||||||
|
correspond to the PCIE0 interrupt.
|
||||||
|
|
||||||
|
The circuit:
|
||||||
|
* encoder pin A to Arduino pin 8
|
||||||
|
* encoder pin B to Arduino pin 9
|
||||||
|
* encoder ground pin to ground (GND)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Rotary.h>
|
||||||
|
|
||||||
|
Rotary r = Rotary(8, 9);
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(9600);
|
||||||
|
r.begin();
|
||||||
|
PCICR |= (1 << PCIE0);
|
||||||
|
PCMSK0 |= (1 << PCINT4) | (1 << PCINT5);
|
||||||
|
sei();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ISR(PCINT0_vect) {
|
||||||
|
unsigned char result = r.process();
|
||||||
|
if (result == DIR_NONE) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
else if (result == DIR_CW) {
|
||||||
|
Serial.println("ClockWise");
|
||||||
|
}
|
||||||
|
else if (result == DIR_CCW) {
|
||||||
|
Serial.println("CounterClockWise");
|
||||||
|
}
|
||||||
|
}
|
25
lib/Rotary/examples/Polling/Polling.ino
Normal file
25
lib/Rotary/examples/Polling/Polling.ino
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
Rotary Encoder - Polling Example
|
||||||
|
|
||||||
|
The circuit:
|
||||||
|
* encoder pin A to Arduino pin 2
|
||||||
|
* encoder pin B to Arduino pin 3
|
||||||
|
* encoder ground pin to ground (GND)
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Rotary.h>
|
||||||
|
|
||||||
|
Rotary r = Rotary(2, 3);
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(9600);
|
||||||
|
r.begin(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
unsigned char result = r.process();
|
||||||
|
if (result) {
|
||||||
|
Serial.println(result == DIR_CW ? "Right" : "Left");
|
||||||
|
}
|
||||||
|
}
|
6
lib/Rotary/keywords.txt
Normal file
6
lib/Rotary/keywords.txt
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
Rotary KEYWORD1
|
||||||
|
process KEYWORD2
|
||||||
|
begin KEYWORD2
|
||||||
|
DIR_NONE LITERAL1
|
||||||
|
DIR_CW LITERAL1
|
||||||
|
DIR_CCW LITERAL1
|
|
@ -15,3 +15,5 @@ framework = arduino
|
||||||
lib_deps =
|
lib_deps =
|
||||||
etherkit/Etherkit Si5351@^2.1.4
|
etherkit/Etherkit Si5351@^2.1.4
|
||||||
paulstoffregen/Encoder@^1.4.4
|
paulstoffregen/Encoder@^1.4.4
|
||||||
|
adafruit/Adafruit GFX Library@^1.11.9
|
||||||
|
adafruit/Adafruit SSD1306@^2.5.9
|
||||||
|
|
625
src/main.cpp
625
src/main.cpp
|
@ -1,395 +1,310 @@
|
||||||
//----------- History ---------------
|
/**********************************************************************************************************
|
||||||
/*
|
10kHz to 225MHz VFO / RF Generator with Si5351 and Arduino Nano, with Intermediate Frequency (IF) offset
|
||||||
/* This code has been modified from that written by Jeff Glass (KK9JEF) and documented in the following locations:
|
(+ or -), RX/TX Selector for QRP Transceivers, Band Presets and Bargraph S-Meter. See the schematics for
|
||||||
/* - https://kk9jef.wordpress.com/2015/11/09/40m-direction-conversion-receiver-in-the-polyakov-style/
|
wiring and README.txt for details. By J. CesarSound - ver 2.0 - Feb/2021.
|
||||||
/* - https://github.com/JeffersGlass/DDS_VFO
|
***********************************************************************************************************/
|
||||||
/*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
//Libraries
|
||||||
#include <Encoder.h>
|
#include <Wire.h> //IDE Standard
|
||||||
#include <Wire.h>
|
#include <Rotary.h> //Ben Buxton https://github.com/brianlow/Rotary
|
||||||
//#include <LiquidCrystal.h>
|
#include <si5351.h> //Etherkit https://github.com/etherkit/Si5351Arduino
|
||||||
#include <si5351.h>
|
#include <Adafruit_GFX.h> //Adafruit GFX https://github.com/adafruit/Adafruit-GFX-Library
|
||||||
|
#include <Adafruit_SSD1306.h> //Adafruit SSD1306 https://github.com/adafruit/Adafruit_SSD1306
|
||||||
|
|
||||||
|
//User preferences
|
||||||
|
//------------------------------------------------------------------------------------------------------------
|
||||||
|
#define IF 0 //Enter your IF frequency, ex: 455 = 455kHz, 10700 = 10.7MHz, 0 = to direct convert receiver or RF generator, + will add and - will subtract IF offfset.
|
||||||
|
#define BAND_INIT 7 //Enter your initial Band (1-21) at startup, ex: 1 = Freq Generator, 2 = 800kHz (MW), 7 = 7.2MHz (40m), 11 = 14.1MHz (20m).
|
||||||
|
#define XT_CAL_F 33000 //Si5351 calibration factor, adjust to get exatcly 10MHz. Increasing this value will decreases the frequency and vice versa.
|
||||||
|
#define S_GAIN 303 //Adjust the sensitivity of Signal Meter A/D input: 101 = 500mv; 202 = 1v; 303 = 1.5v; 404 = 2v; 505 = 2.5v; 1010 = 5v (max).
|
||||||
|
#define tunestep A0 //The pin used by tune step push button.
|
||||||
|
#define band A1 //The pin used by band selector push button.
|
||||||
|
#define rx_tx A2 //The pin used by RX / TX selector switch, RX = switch open, TX = switch closed to GND. When in TX, the IF value is not considered.
|
||||||
|
#define adc A3 //The pin used by Signal Meter A/D input.
|
||||||
|
#define rotary1 2 //First pin for rotary encoder.
|
||||||
|
#define rotary2 3 //Second pin for rotary encoder.
|
||||||
|
//------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
//----------- Variables & Declarations ---------------
|
Rotary r = Rotary(rotary1, rotary2);
|
||||||
/*
|
Adafruit_SSD1306 display = Adafruit_SSD1306(128, 64, &Wire);
|
||||||
* The current and desired LISTENING FREQUENCY, which is not always the frequency being output by the Si5351.
|
Si5351 si5351(0x60); //Si5351 I2C Address 0x60
|
||||||
* In 'testing' and 'basic' modes, the output freqeuncy is equal to currFreq
|
|
||||||
* In 'polyakov' mode, the output frequency is half of curFreq
|
|
||||||
* In BFO mode, .........
|
|
||||||
* These adjustments are mode in the setFrequency_5351 function depending on the current mode held in currMode
|
|
||||||
*/
|
|
||||||
|
|
||||||
long currFreq = 1800000; //in HZ
|
unsigned long freq, freqold, fstep;
|
||||||
long ifFreq = 8865000; //in HZ
|
long interfreq = IF, interfreqold = 0;
|
||||||
|
long cal = XT_CAL_F;
|
||||||
|
unsigned int smval;
|
||||||
|
byte encoder = 1;
|
||||||
|
byte stp, n = 1;
|
||||||
|
byte count, x, xo;
|
||||||
|
bool sts = 0;
|
||||||
|
unsigned int period = 100;
|
||||||
|
unsigned long time_now = 0;
|
||||||
|
|
||||||
//-----Enumerations of frequency steps and their labels for each mode----//
|
void set_frequency(short dir) {
|
||||||
|
if (encoder == 1) { //Up/Down frequency
|
||||||
enum modes{mode_testing = 0, mode_basic, mode_polyakov, mode_bfo, mode_if};
|
if (dir == 1) freq = freq + fstep;
|
||||||
const int NUM_MODES = 5;
|
if (freq >= 225000000) freq = 225000000;
|
||||||
int currMode = mode_basic;
|
if (dir == -1) freq = freq - fstep;
|
||||||
|
if (fstep == 1000000 && freq <= 1000000) freq = 1000000;
|
||||||
const char* modeNames[NUM_MODES] = {"TEST", "VFO", "POLYA", "BFO", "IF"};
|
else if (freq < 10000) freq = 10000;
|
||||||
|
|
||||||
long steps[][10] = { //don't forget to update the MAX_STEPS_INDEX array below
|
|
||||||
{10000000, 5000000, 1000000, 500000, 100000, 10000, 1000, 10, 1}, //testing
|
|
||||||
{10000, 1000, 100, 10}, //basic
|
|
||||||
{1000, 100, 10, 1}, //polyakov
|
|
||||||
{1000, 100, 10, 1}, //bfo
|
|
||||||
{1000, 100, 10, 1} //IF Mode
|
|
||||||
};
|
|
||||||
|
|
||||||
const int NUM_STEP_OPTIONS[NUM_MODES] = {
|
|
||||||
10, //testing
|
|
||||||
4, //basic
|
|
||||||
4, //polyakov
|
|
||||||
4, //bfo
|
|
||||||
4 //if
|
|
||||||
};
|
|
||||||
const char* stepNames[][10] = {
|
|
||||||
{" 10MHz", " 5MHz", " 1MHz", "500Khz", "100KHz", " 10KHz", " 1KHz", " 100Hz", " 10Hz", " 1 Hz"}, //basic
|
|
||||||
{" 10KHz", " 1KHz", " 100 Hz", " 10 Hz"}, //basic
|
|
||||||
{" 1KHz", " 100 Hz", " 10 Hz", " 1 Hz"}, //polyakov
|
|
||||||
{" 1KHz", " 100 Hz", " 10 Hz", " 1 Hz"}, //BFO
|
|
||||||
{" 1KHz", " 100 Hz", " 10 Hz", " 1 Hz"} //IF
|
|
||||||
};
|
|
||||||
|
|
||||||
int stepIndex = 0; // holds the index of the currently selected step value
|
|
||||||
|
|
||||||
//-----AMATEUR BAND DEFININTIONS----------------//
|
|
||||||
//See function "getCurrentBand" below as well
|
|
||||||
const int NUM_BANDS = 9;
|
|
||||||
const char* bandNames[NUM_BANDS] = {"160m", "80m", "40m", "30m", "20m", "17m", "15m", "12m", "10m"};
|
|
||||||
const char* OUT_OF_BAND_LABEL = "OOB";
|
|
||||||
|
|
||||||
long bandEdges[NUM_BANDS][2] = {
|
|
||||||
{1800000, 2000000}, //160m
|
|
||||||
{3500000, 4000000}, //80m
|
|
||||||
{7000000, 7300000}, //40m
|
|
||||||
{10100000, 10150000}, //30m
|
|
||||||
{14000000, 14350000}, //20m
|
|
||||||
{18068000, 18168000}, //17m
|
|
||||||
{21000000, 21450000}, //15m
|
|
||||||
{24890000, 24990000}, //12m
|
|
||||||
{28000000, 29700000} //10m
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Holds the last-seen frequency within each band. The list below is also the default location at bootup.
|
|
||||||
* This array is updated when the BAND button is used to change between bands.
|
|
||||||
* If the used has scrolled outside of a defined band and then presses the BAND button, they will
|
|
||||||
* still be advanced to the next band, but the band-return location will not be updated
|
|
||||||
*/
|
|
||||||
|
|
||||||
long lastBandFreq[NUM_BANDS] = {
|
|
||||||
1800000, //160m
|
|
||||||
3500000, //80m
|
|
||||||
7000000, //40m
|
|
||||||
10100000, //30m
|
|
||||||
14000000, //20m
|
|
||||||
18068000, //17m
|
|
||||||
21000000, //15m
|
|
||||||
24890000, //12m
|
|
||||||
28000000 //10m
|
|
||||||
};
|
|
||||||
|
|
||||||
/*Information on bandplan permissions and recommended communication modes is contained in the
|
|
||||||
* methods getPermission and getBandplanModes below
|
|
||||||
*/
|
|
||||||
|
|
||||||
//---------------------------------------------
|
|
||||||
|
|
||||||
long lastButtonPress[] = {0,0,0,0,0,0,0}; //holds the last timestamp, from millis(), that a pin changed state. Directly references the arduino output pin numbers, length may need to be increased
|
|
||||||
boolean buttonActive[] = {false, false, false, false, false, false, false};
|
|
||||||
|
|
||||||
long encoderPosition = 0;
|
|
||||||
boolean displayNeedsUpdate;
|
|
||||||
|
|
||||||
const long MIN_FREQ = 8500;
|
|
||||||
const long MAX_FREQ = 150000000;
|
|
||||||
|
|
||||||
//---------LCD SETUP-------//
|
|
||||||
// int PIN_RS = 7;
|
|
||||||
// int PIN_EN = 8;
|
|
||||||
// int PIN_DB4 = 9;
|
|
||||||
// int PIN_DB5 = 10;
|
|
||||||
// int PIN_DB6 = 11;
|
|
||||||
// int PIN_DB7 = 12;
|
|
||||||
//LiquidCrystal lcd(PIN_RS, PIN_EN, PIN_DB4, PIN_DB5, PIN_DB6, PIN_DB7);
|
|
||||||
|
|
||||||
//--------Si5351 Declaration---------------//
|
|
||||||
|
|
||||||
Si5351 si5351;
|
|
||||||
//SDA is on pin A4 for Arduino Uno
|
|
||||||
//SCL is on pin A5 for Arduino Uno
|
|
||||||
|
|
||||||
//--------Tuning Knob Interrupt Pins-------//
|
|
||||||
//Encoder knob(2, 3), pushbutton on 1
|
|
||||||
|
|
||||||
Encoder encoder(2, 3);
|
|
||||||
const int PIN_BUTTON_ENCODER = 1;
|
|
||||||
|
|
||||||
//Button Pins//
|
|
||||||
const int PIN_BUTTON_MODE = 4;
|
|
||||||
const int PIN_BUTTON_BAND = 0;
|
|
||||||
const int BUTTON_DEBOUNCE_TIME = 10; //milliseconds
|
|
||||||
|
|
||||||
//SWR Sensor Pins
|
|
||||||
const int PIN_SWR_FORWARD = A1;
|
|
||||||
const int PIN_SWR_REVERSE = A0;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// void displayInfo(){
|
|
||||||
// lcd.clear();
|
|
||||||
|
|
||||||
// // frequency information be centeredw within 11 spaces on the second line:
|
|
||||||
// if (currFreq >= 100000000) lcd.setCursor(3, 0);
|
|
||||||
// else if (currFreq > 10000000) lcd.setCursor(4, 0);
|
|
||||||
// else lcd.setCursor(5, 0);
|
|
||||||
// int mhz = int(currFreq/ 1000000);
|
|
||||||
// int khz = int((currFreq - (mhz*1000000)) / 1000);
|
|
||||||
// int hz = int(currFreq % 1000);
|
|
||||||
|
|
||||||
// int khzPad = 0;
|
|
||||||
// if (khz < 100) khzPad++;
|
|
||||||
// if (khz < 10) khzPad++;
|
|
||||||
|
|
||||||
// int hzPad = 0;
|
|
||||||
// if (hz < 100) hzPad++;
|
|
||||||
// if (hz < 10) hzPad++;
|
|
||||||
|
|
||||||
// lcd.print(mhz);
|
|
||||||
// lcd.print(".");
|
|
||||||
// for (int i = 0; i < khzPad; i++) lcd.print("0");
|
|
||||||
// lcd.print(khz);
|
|
||||||
// lcd.print(".");
|
|
||||||
// for (int i = 0; i < hzPad; i++) lcd.print("0");
|
|
||||||
// lcd.print(hz);
|
|
||||||
|
|
||||||
// //The current amateur band is printed in the top-right corner
|
|
||||||
// int currBand = getCurrentBand();
|
|
||||||
// if (currBand >= 0){
|
|
||||||
// char* currBandName = bandNames[currBand];
|
|
||||||
// lcd.setCursor(20-strlen(currBandName), 0);
|
|
||||||
// lcd.print(currBandName);
|
|
||||||
// }
|
|
||||||
// else{
|
|
||||||
// lcd.setCursor(20-strlen(OUT_OF_BAND_LABEL), 0);
|
|
||||||
// lcd.print(OUT_OF_BAND_LABEL);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// //The license needed to operate on this frequency (ARRL, USA ONLY) is printed just below the band label
|
|
||||||
// lcd.setCursor (19, 1);
|
|
||||||
// lcd.print(getPermission());
|
|
||||||
|
|
||||||
// //Step Information should take the middle 11 spaces on the 3nd line
|
|
||||||
// //The first 5 symbols are "STEP:", leaving 6 chars for step info.
|
|
||||||
// lcd.setCursor(4, 2);
|
|
||||||
// lcd.print("STEP:");
|
|
||||||
// lcd.print(stepNames[currMode][stepIndex]);
|
|
||||||
|
|
||||||
// //Callsign is printed at the beginning of the 4th line
|
|
||||||
// lcd.setCursor(0, 3);
|
|
||||||
// lcd.print("KK9JEF");
|
|
||||||
|
|
||||||
// //The mode is printed on the 4th line with no label
|
|
||||||
// //lcd.setCursor(6, 3);
|
|
||||||
// lcd.setCursor(20-strlen(modeNames[currMode]), 3);
|
|
||||||
// lcd.print(modeNames[currMode]);
|
|
||||||
|
|
||||||
// //DEBUG
|
|
||||||
// //lcd.setCursor(0,0);
|
|
||||||
// //lcd.print(getCurrentBand());
|
|
||||||
|
|
||||||
// /*float fwd = analogRead(PIN_SWR_FORWARD);
|
|
||||||
// float rev = analogRead(PIN_SWR_REVERSE);
|
|
||||||
// float gamma = rev/fwd;
|
|
||||||
// float swr = (1 + abs(gamma)) / (1 - abs(gamma));
|
|
||||||
|
|
||||||
// lcd.setCursor(0, 1);
|
|
||||||
// lcd.print(int(fwd));
|
|
||||||
// lcd.setCursor(4, 1);
|
|
||||||
// lcd.print(int(rev));
|
|
||||||
// lcd.setCursor(8, 1);
|
|
||||||
// lcd.print(gamma);
|
|
||||||
// lcd.setCursor(14, 1);
|
|
||||||
// lcd.print(swr);*/
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
boolean checkButtonPress(int pin){
|
|
||||||
long time = millis();
|
|
||||||
if (buttonActive[pin] && digitalRead(pin) == HIGH){
|
|
||||||
buttonActive[pin] = false;
|
|
||||||
lastButtonPress[pin] = time;
|
|
||||||
}
|
}
|
||||||
else if (digitalRead(pin) == LOW && !buttonActive[pin] && time > lastButtonPress[pin] + BUTTON_DEBOUNCE_TIME){
|
if (encoder == 1) { //Up/Down graph tune pointer
|
||||||
buttonActive[pin] = true;
|
if (dir == 1) n = n + 1;
|
||||||
lastButtonPress[pin] = time;
|
if (n > 42) n = 1;
|
||||||
return true;
|
if (dir == -1) n = n - 1;
|
||||||
}
|
if (n < 1) n = 42;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setFrequency_5351(long newFreq){
|
|
||||||
switch (currMode){
|
|
||||||
case mode_testing:
|
|
||||||
si5351.set_freq(newFreq * 100ULL, SI5351_CLK0);
|
|
||||||
break;
|
|
||||||
case mode_basic:
|
|
||||||
si5351.set_freq(newFreq * 100ULL, SI5351_CLK0);
|
|
||||||
break;
|
|
||||||
case mode_polyakov:
|
|
||||||
si5351.set_freq((newFreq / 2) * 100ULL, SI5351_CLK0);
|
|
||||||
break;
|
|
||||||
case mode_bfo:
|
|
||||||
si5351.set_freq(newFreq * 100ULL, SI5351_CLK0);
|
|
||||||
break;
|
|
||||||
case mode_if:
|
|
||||||
si5351.set_freq((newFreq + ifFreq) * 100UL, SI5351_CLK0); //VFO+IF
|
|
||||||
//VFO-IF
|
|
||||||
//IF-VFO
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Returns the index of the current amateur radio band based on currFreq. Does not include the 60m band
|
ISR(PCINT2_vect) {
|
||||||
//Returns -1 if out of band, but within the HF amateur turning range
|
char result = r.process();
|
||||||
//returns -2 if out of band and lower than the lowest defined band
|
if (result == DIR_CW) set_frequency(1);
|
||||||
//returns -3 if out of band and higher than the highest defined band
|
else if (result == DIR_CCW) set_frequency(-1);
|
||||||
int getCurrentBand(){
|
|
||||||
if (currFreq < bandEdges[0][0]) return -2; //we are lower than the lower edge of the lowest defined band
|
|
||||||
if (currFreq > bandEdges[NUM_BANDS-1][1]) return -3; //We are higher than the upper edge of the highest defined band
|
|
||||||
for (int i = 0; i < NUM_BANDS; i++){
|
|
||||||
if (currFreq >= bandEdges[i][0] && currFreq <= bandEdges[i][1]){return i;} //We are within a band
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
char getPermission(){
|
void tunegen() {
|
||||||
if (getCurrentBand() < 0) return ' ';
|
si5351.set_freq((freq + (interfreq * 1000ULL)) * 100ULL, SI5351_CLK0);
|
||||||
|
}
|
||||||
|
|
||||||
//160m
|
void displayfreq() {
|
||||||
if (currFreq >= 1800000 && currFreq <= 2000000) return 'G';
|
unsigned int m = freq / 1000000;
|
||||||
|
unsigned int k = (freq % 1000000) / 1000;
|
||||||
|
unsigned int h = (freq % 1000) / 1;
|
||||||
|
|
||||||
//80m
|
display.clearDisplay();
|
||||||
if (currFreq >= 3525000 && currFreq <= 3600000) return 'T';
|
display.setTextSize(2);
|
||||||
if ((currFreq >= 3525000 && currFreq <= 3600000) || (currFreq >= 3800000 && currFreq <= 4000000)) return 'G';
|
|
||||||
if ((currFreq >= 3525000 && currFreq <= 3600000) || (currFreq >= 3700000 && currFreq <= 4000000)) return 'A';
|
|
||||||
if (currFreq >= 3500000 && currFreq <= 4000000) return 'E';
|
|
||||||
|
|
||||||
//40m
|
char buffer[15] = "";
|
||||||
if (currFreq >= 7025000 && currFreq <= 7125000) return 'T';
|
if (m < 1) {
|
||||||
if ((currFreq >= 7025000 && currFreq <= 7125000) || (currFreq >= 7175000 && currFreq <= 7300000)) return 'G';
|
display.setCursor(41, 1); sprintf(buffer, "%003d.%003d", k, h);
|
||||||
if (currFreq >= 7025000 && currFreq <= 7300000) return 'A';
|
}
|
||||||
if (currFreq >= 7000000 && currFreq <= 7300000) return 'E';
|
else if (m < 100) {
|
||||||
|
display.setCursor(5, 1); sprintf(buffer, "%2d.%003d.%003d", m, k, h);
|
||||||
|
}
|
||||||
|
else if (m >= 100) {
|
||||||
|
unsigned int h = (freq % 1000) / 10;
|
||||||
|
display.setCursor(5, 1); sprintf(buffer, "%2d.%003d.%02d", m, k, h);
|
||||||
|
}
|
||||||
|
display.print(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
//30m
|
void setstep() {
|
||||||
if (currFreq >= 10100000 && currFreq <= 10150000) return 'G';
|
switch (stp) {
|
||||||
|
case 1: stp = 2; fstep = 1; break;
|
||||||
|
case 2: stp = 3; fstep = 10; break;
|
||||||
|
case 3: stp = 4; fstep = 1000; break;
|
||||||
|
case 4: stp = 5; fstep = 5000; break;
|
||||||
|
case 5: stp = 6; fstep = 10000; break;
|
||||||
|
case 6: stp = 1; fstep = 1000000; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//20m
|
void bandpresets() {
|
||||||
if ((currFreq >= 14025000 && currFreq <= 14150000) || (currFreq >= 14225000 && currFreq <= 14350000)) return 'G';
|
switch (count) {
|
||||||
if ((currFreq >= 14025000 && currFreq <= 14150000) || (currFreq >= 14175000 && currFreq <= 14350000)) return 'A';
|
case 1: freq = 100000; tunegen(); break;
|
||||||
if (currFreq >= 14000000 && currFreq <= 14350000) return 'E';
|
case 2: freq = 800000; break;
|
||||||
|
case 3: freq = 1800000; break;
|
||||||
|
case 4: freq = 3650000; break;
|
||||||
|
case 5: freq = 4985000; break;
|
||||||
|
case 6: freq = 6180000; break;
|
||||||
|
case 7: freq = 7200000; break;
|
||||||
|
case 8: freq = 10000000; break;
|
||||||
|
case 9: freq = 11780000; break;
|
||||||
|
case 10: freq = 13630000; break;
|
||||||
|
case 11: freq = 14100000; break;
|
||||||
|
case 12: freq = 15000000; break;
|
||||||
|
case 13: freq = 17655000; break;
|
||||||
|
case 14: freq = 21525000; break;
|
||||||
|
case 15: freq = 27015000; break;
|
||||||
|
case 16: freq = 28400000; break;
|
||||||
|
case 17: freq = 50000000; break;
|
||||||
|
case 18: freq = 100000000; break;
|
||||||
|
case 19: freq = 130000000; break;
|
||||||
|
case 20: freq = 144000000; break;
|
||||||
|
case 21: freq = 220000000; break;
|
||||||
|
}
|
||||||
|
si5351.pll_reset(SI5351_PLLA);
|
||||||
|
stp = 4; setstep();
|
||||||
|
}
|
||||||
|
|
||||||
//17m
|
void inc_preset() {
|
||||||
if (currFreq >= 18068000 && currFreq <= 18168000) return 'G';
|
count++;
|
||||||
|
if (count > 21) count = 1;
|
||||||
|
bandpresets();
|
||||||
|
delay(50);
|
||||||
|
}
|
||||||
|
|
||||||
//15m
|
void bandlist() {
|
||||||
if (currFreq >= 21025000 && currFreq <= 21200000) return 'T';
|
display.setTextSize(2);
|
||||||
if ((currFreq >= 21025000 && currFreq <= 21200000) || (currFreq >= 21275000 && currFreq <= 21450000)) return 'G';
|
display.setCursor(0, 25);
|
||||||
if ((currFreq >= 21025000 && currFreq <= 21200000) || (currFreq >= 21225000 && currFreq <= 21450000)) return 'A';
|
if (count == 1) display.print("GEN"); if (count == 2) display.print("MW"); if (count == 3) display.print("160m"); if (count == 4) display.print("80m");
|
||||||
if (currFreq >= 21000000 && currFreq <= 21450000) return 'E';
|
if (count == 5) display.print("60m"); if (count == 6) display.print("49m"); if (count == 7) display.print("40m"); if (count == 8) display.print("31m");
|
||||||
|
if (count == 9) display.print("25m"); if (count == 10) display.print("22m"); if (count == 11) display.print("20m"); if (count == 12) display.print("19m");
|
||||||
|
if (count == 13) display.print("16m"); if (count == 14) display.print("13m"); if (count == 15) display.print("11m"); if (count == 16) display.print("10m");
|
||||||
|
if (count == 17) display.print("6m"); if (count == 18) display.print("WFM"); if (count == 19) display.print("AIR"); if (count == 20) display.print("2m");
|
||||||
|
if (count == 21) display.print("1m");
|
||||||
|
if (count == 1) interfreq = 0; else if (!sts) interfreq = IF;
|
||||||
|
}
|
||||||
|
|
||||||
//12m
|
void drawbargraph() {
|
||||||
if (currFreq >= 24890000 && currFreq <= 24990000) return 'G';
|
byte y = map(n, 1, 42, 1, 14);
|
||||||
|
display.setTextSize(1);
|
||||||
|
|
||||||
//10m
|
//Pointer
|
||||||
if (currFreq >= 28000000 && currFreq <= 28500000) return 'T';
|
display.setCursor(0, 48); display.print("TU");
|
||||||
if (currFreq >= 28000000 && currFreq <= 29700000) return 'G';
|
switch (y) {
|
||||||
|
case 1: display.fillRect(15, 48, 2, 6, WHITE); break;
|
||||||
|
case 2: display.fillRect(20, 48, 2, 6, WHITE); break;
|
||||||
|
case 3: display.fillRect(25, 48, 2, 6, WHITE); break;
|
||||||
|
case 4: display.fillRect(30, 48, 2, 6, WHITE); break;
|
||||||
|
case 5: display.fillRect(35, 48, 2, 6, WHITE); break;
|
||||||
|
case 6: display.fillRect(40, 48, 2, 6, WHITE); break;
|
||||||
|
case 7: display.fillRect(45, 48, 2, 6, WHITE); break;
|
||||||
|
case 8: display.fillRect(50, 48, 2, 6, WHITE); break;
|
||||||
|
case 9: display.fillRect(55, 48, 2, 6, WHITE); break;
|
||||||
|
case 10: display.fillRect(60, 48, 2, 6, WHITE); break;
|
||||||
|
case 11: display.fillRect(65, 48, 2, 6, WHITE); break;
|
||||||
|
case 12: display.fillRect(70, 48, 2, 6, WHITE); break;
|
||||||
|
case 13: display.fillRect(75, 48, 2, 6, WHITE); break;
|
||||||
|
case 14: display.fillRect(80, 48, 2, 6, WHITE); break;
|
||||||
|
}
|
||||||
|
|
||||||
return 'X';
|
//Bargraph
|
||||||
|
display.setCursor(0, 57); display.print("SM");
|
||||||
|
switch (x) {
|
||||||
|
case 14: display.fillRect(80, 58, 2, 6, WHITE);
|
||||||
|
case 13: display.fillRect(75, 58, 2, 6, WHITE);
|
||||||
|
case 12: display.fillRect(70, 58, 2, 6, WHITE);
|
||||||
|
case 11: display.fillRect(65, 58, 2, 6, WHITE);
|
||||||
|
case 10: display.fillRect(60, 58, 2, 6, WHITE);
|
||||||
|
case 9: display.fillRect(55, 58, 2, 6, WHITE);
|
||||||
|
case 8: display.fillRect(50, 58, 2, 6, WHITE);
|
||||||
|
case 7: display.fillRect(45, 58, 2, 6, WHITE);
|
||||||
|
case 6: display.fillRect(40, 58, 2, 6, WHITE);
|
||||||
|
case 5: display.fillRect(35, 58, 2, 6, WHITE);
|
||||||
|
case 4: display.fillRect(30, 58, 2, 6, WHITE);
|
||||||
|
case 3: display.fillRect(25, 58, 2, 6, WHITE);
|
||||||
|
case 2: display.fillRect(20, 58, 2, 6, WHITE);
|
||||||
|
case 1: display.fillRect(15, 58, 2, 6, WHITE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void layout() {
|
||||||
|
display.setTextColor(WHITE);
|
||||||
|
display.drawLine(0, 20, 127, 20, WHITE);
|
||||||
|
display.drawLine(0, 43, 127, 43, WHITE);
|
||||||
|
display.drawLine(105, 24, 105, 39, WHITE);
|
||||||
|
display.drawLine(87, 24, 87, 39, WHITE);
|
||||||
|
display.drawLine(87, 48, 87, 63, WHITE);
|
||||||
|
display.drawLine(15, 55, 82, 55, WHITE);
|
||||||
|
display.setTextSize(1);
|
||||||
|
display.setCursor(59, 23);
|
||||||
|
display.print("STEP");
|
||||||
|
display.setCursor(54, 33);
|
||||||
|
if (stp == 2) display.print(" 1Hz"); if (stp == 3) display.print(" 10Hz"); if (stp == 4) display.print(" 1kHz");
|
||||||
|
if (stp == 5) display.print(" 5kHz"); if (stp == 6) display.print("10kHz"); if (stp == 1) display.print(" 1MHz");
|
||||||
|
display.setTextSize(1);
|
||||||
|
display.setCursor(92, 48);
|
||||||
|
display.print("IF:");
|
||||||
|
display.setCursor(92, 57);
|
||||||
|
display.print(interfreq);
|
||||||
|
display.print("k");
|
||||||
|
display.setTextSize(1);
|
||||||
|
display.setCursor(110, 23);
|
||||||
|
if (freq < 1000000) display.print("kHz");
|
||||||
|
if (freq >= 1000000) display.print("MHz");
|
||||||
|
display.setCursor(110, 33);
|
||||||
|
if (interfreq == 0) display.print("VFO");
|
||||||
|
if (interfreq != 0) display.print("L O");
|
||||||
|
display.setCursor(91, 28);
|
||||||
|
if (!sts) display.print("RX"); if (!sts) interfreq = IF;
|
||||||
|
if (sts) display.print("TX"); if (sts) interfreq = 0;
|
||||||
|
bandlist(); drawbargraph();
|
||||||
|
display.display();
|
||||||
|
}
|
||||||
|
|
||||||
|
void sgnalread() {
|
||||||
|
smval = analogRead(adc); x = map(smval, 0, S_GAIN, 1, 14); if (x > 14) x = 14;
|
||||||
|
}
|
||||||
|
|
||||||
|
void statup_text() {
|
||||||
|
display.setTextSize(1); display.setCursor(13, 18);
|
||||||
|
display.print("Si5351 VFO/RF GEN");
|
||||||
|
display.setCursor(6, 40);
|
||||||
|
display.print("JCR RADIO - Ver 2.0");
|
||||||
|
display.display(); delay(2000);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
// inialize LCD, display welcome message
|
Wire.begin();
|
||||||
//lcd.begin(20, 4);
|
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
|
||||||
//delay(250);
|
display.clearDisplay();
|
||||||
//lcd.setCursor(4, 1);
|
display.setTextColor(WHITE);
|
||||||
//lcd.print("VFO STARTING");
|
display.display();
|
||||||
|
|
||||||
|
pinMode(2, INPUT_PULLUP);
|
||||||
|
pinMode(3, INPUT_PULLUP);
|
||||||
|
pinMode(tunestep, INPUT_PULLUP);
|
||||||
|
pinMode(band, INPUT_PULLUP);
|
||||||
|
pinMode(rx_tx, INPUT_PULLUP);
|
||||||
|
|
||||||
|
//statup_text(); //If you hang on startup, comment
|
||||||
|
|
||||||
si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0);
|
si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0);
|
||||||
si5351.set_freq(currFreq * 100ULL, SI5351_CLK0);
|
si5351.set_correction(cal, SI5351_PLL_INPUT_XO);
|
||||||
si5351.output_enable(SI5351_CLK0, 1);
|
|
||||||
si5351.drive_strength(SI5351_CLK0, SI5351_DRIVE_8MA);
|
si5351.drive_strength(SI5351_CLK0, SI5351_DRIVE_8MA);
|
||||||
|
si5351.output_enable(SI5351_CLK0, 1); //1 - Enable / 0 - Disable CLK
|
||||||
si5351.output_enable(SI5351_CLK1, 0);
|
si5351.output_enable(SI5351_CLK1, 0);
|
||||||
si5351.output_enable(SI5351_CLK2, 0);
|
si5351.output_enable(SI5351_CLK2, 0);
|
||||||
delay(750);
|
|
||||||
|
|
||||||
//knob.write(0);
|
PCICR |= (1 << PCIE2);
|
||||||
pinMode(PIN_BUTTON_ENCODER, INPUT);
|
PCMSK2 |= (1 << PCINT18) | (1 << PCINT19);
|
||||||
digitalWrite(PIN_BUTTON_ENCODER, HIGH);
|
sei();
|
||||||
|
|
||||||
pinMode(PIN_BUTTON_MODE, INPUT);
|
count = BAND_INIT;
|
||||||
digitalWrite(PIN_BUTTON_MODE, HIGH);
|
bandpresets();
|
||||||
pinMode(PIN_BUTTON_BAND, INPUT);
|
stp = 4;
|
||||||
digitalWrite(PIN_BUTTON_BAND, HIGH);
|
setstep();
|
||||||
|
|
||||||
pinMode(PIN_SWR_FORWARD, INPUT);
|
|
||||||
pinMode(PIN_SWR_REVERSE, INPUT);
|
|
||||||
|
|
||||||
//lcd.clear();
|
|
||||||
//lcd.setCursor(2, 7);
|
|
||||||
//lcd.print("WELCOME!");
|
|
||||||
//delay(500);
|
|
||||||
//displayInfo();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
//if (displayNeedsUpdate) {displayInfo();}
|
if (freqold != freq) {
|
||||||
//delay(80);
|
time_now = millis();
|
||||||
|
tunegen();
|
||||||
//detect whether encoder has changed position
|
freqold = freq;
|
||||||
long reading = encoder.read();
|
|
||||||
long encoderChange = reading - encoderPosition;
|
|
||||||
encoderPosition = reading;
|
|
||||||
|
|
||||||
displayNeedsUpdate = false;
|
|
||||||
|
|
||||||
//step up or down or change step size, for either button presses or encoder turns
|
|
||||||
if ((encoderChange > 0)){currFreq += steps[currMode][stepIndex]; currFreq = min(currFreq, MAX_FREQ); setFrequency_5351(currFreq); displayNeedsUpdate = true;}
|
|
||||||
if ((encoderChange < 0)){currFreq -= steps[currMode][stepIndex]; currFreq = max(currFreq, MIN_FREQ); setFrequency_5351(currFreq); displayNeedsUpdate = true;}
|
|
||||||
|
|
||||||
//pressing the encoder button increments through the possible step sizes for each mode
|
|
||||||
if (checkButtonPress(PIN_BUTTON_ENCODER)){stepIndex = (stepIndex + 1) % (NUM_STEP_OPTIONS[currMode]); displayNeedsUpdate = true;}
|
|
||||||
|
|
||||||
//pressing the mode button cycles through the available modes
|
|
||||||
if (checkButtonPress(PIN_BUTTON_MODE)){currMode = (currMode+1) % NUM_MODES; stepIndex = 0; setFrequency_5351(currFreq); displayNeedsUpdate = true;}
|
|
||||||
|
|
||||||
/*The mode button: if currFreq is inside an amateur band, save that frequency as the one to return to when
|
|
||||||
* the user returns to this band, and jump to the return frequency for the next higher band. Otherwise,
|
|
||||||
* just jump to the next higher band
|
|
||||||
*/
|
|
||||||
if (checkButtonPress(PIN_BUTTON_BAND)){
|
|
||||||
int currBand = getCurrentBand();
|
|
||||||
if (currBand >= 0){
|
|
||||||
lastBandFreq[currBand] = currFreq;
|
|
||||||
currFreq = lastBandFreq[(getCurrentBand() + 1) % NUM_BANDS];
|
|
||||||
setFrequency_5351(currFreq);
|
|
||||||
}
|
}
|
||||||
else if (currBand == -2 || currBand == -3){
|
|
||||||
currFreq = lastBandFreq[0];
|
if (interfreqold != interfreq) {
|
||||||
setFrequency_5351(currFreq);
|
time_now = millis();
|
||||||
|
tunegen();
|
||||||
|
interfreqold = interfreq;
|
||||||
}
|
}
|
||||||
else if (currBand == -1){
|
|
||||||
for (int i = 0; i < NUM_BANDS; i++){
|
if (xo != x) {
|
||||||
if (currFreq < lastBandFreq[i]){currFreq = lastBandFreq[i]; setFrequency_5351(currFreq); break;}
|
time_now = millis();
|
||||||
|
xo = x;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (digitalRead(tunestep) == LOW) {
|
||||||
|
time_now = (millis() + 300);
|
||||||
|
setstep();
|
||||||
|
delay(300);
|
||||||
}
|
}
|
||||||
displayNeedsUpdate = true;
|
|
||||||
|
if (digitalRead(band) == LOW) {
|
||||||
|
time_now = (millis() + 300);
|
||||||
|
inc_preset();
|
||||||
|
delay(300);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (digitalRead(rx_tx) == LOW) {
|
||||||
|
time_now = (millis() + 300);
|
||||||
|
sts = 1;
|
||||||
|
} else sts = 0;
|
||||||
|
|
||||||
|
if ((time_now + period) > millis()) {
|
||||||
|
displayfreq();
|
||||||
|
layout();
|
||||||
|
}
|
||||||
|
sgnalread();
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user