/*Print Date: 12/12/17
**************************************************
***** AD9851 50KHz to 70MHz Scalar Network Analyzer *****
** Yana: Yet Another Network Analyzer **
Yana Program: Copyright K1TRB Thomas Berger (C)2016
with enhancements by Rudi Reuter DL5FA
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License version 3 as
published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
The GNU General Public License is available at
.
Libraries used have separate copyrights.
If you modify this program and distribute it, please send a full copy of this
original program along with your modification. Be sure all modifications are
well documented in the code.
K1TRB 5-9-15, 6-17-15
02-12-16 Switched to AD9851
11-20-15 Constant min/max on graph (removed)
11/30/15 Moved from sweep to PHSNA
12/12/15 Switched to ILI9340 and ST7735
03/11/16 All boxed up ready for final firmware
03/25/16 Calibration, SNA in dbm, Power Meter added
03/31/16 SWR working with simplified short calibration
04/09/16 Assumed at testing
02/08/17 Added raw output in PWR
02/11/17 button interrupt driven
2017-04-25 DL5FA, VNAJ remote control, DC_IN voltage display (color coded)
2017-09-16 Corrected string length error in tftPrintCMenu().
2017-11-16 Removed vnaJ code.
2017-11-30 added L, C measurement
2017-12-04 added graph transfer to PC
2017-12-12 repaired fix_freqs()
To use as a vfo, operate in the Center or Start Frq Menu
on the first frequency line.
====> User tweak values: <====
CALIB_ATTEN30db 28.64 // ideal value for build attenuator
// use this value or your measured value
TUNE_WORD 180000000L // ideal value for 6x crystal frequency
// measure output freq at 10MHz and adjust this value
L_STD 1234 // 1.2uH // standard L in nH
C_STD 617 // 680pF // standard C in pF
CURSOR_STEP 100L // cursor step size: you may like 10
These constants are declared below.
**************************************************
Operations:
Turn: Turn the knob
Click: Press the knob for a quick Click
Push: Push until the screen goes black
Hold: Hold until screen turns blue
Actions:
Turn: changes value in a field
e.g. changes frequency
Click: changes the increment position on a line
e.g. changes frequency increment
Pause scan
Push: advances to next field
Hold: Go Home
From Home execute command
**************************************************
Wiring:
UNO (same on Pro Mini and Nano)
This is the table of jumpers used to wire
Yana.
===> Wire runs:
(Gnd)Brown and 5v(Red) go to the
power rail on the breadboard. The rail
is powered by the 5v regulator on the UNO.
V+/- (Battery) can be 7-10v.
V+(Red) and V-(Black) run separately to the
UNO and the AD8307 module. The AD8307 should
have its own regulator on board for noise
reasons. The Display has an on board
3.3v regulator. The on-board 3.3v regulator
on the UNO is not used.
Grouped wire runs (7-10v):
Use separate wiring.
Bat voltage: (red/black)
UNO
AD8307
==> 5v from UNO: (red)
Use UNO 5v power rail: Vcc.
UNO
AD9851
Display
Signal Ground: (brown)
Use UNO ground rail.
UNO
AD9851
AD8307
Rotary
Display
SD-card
===> Connections:
***Power: (To DC plug on UNO, Vin on Nano)
UNO Bat Name
Vin V+ Bat+(7-10v) Red
Gnd V- Bat-(7-10v) Black
***Rotary Encoder/Button:
Jumper
UNO Rotary: Function: Color:
2 CLK Rotary Orange
3 DT Rotary Yellow
A0 BTN Button White
Gnd Gnd Rotary/Button Brown
***AD8307 Detector:
UNO AD8307 Function
A1 Output Det out White
Gnd Gnd (sig) Det Gnd Brown
Vin Vin Bat volt Red
V- Gnd (Vcc) Bat Gnd Black
***AD9851 DDS:
UNO DDS
5v Vcc 10 AD9851 D-side Red
Gnd D2 7 AD9851 D-side Brown
Gnd 1 AD9851 D-side Brown
4 W-CLK 9 AD9851 Green
5 FQ-UD 8 AD9851 Blue
6 DATA 7 AD9851 White
7 RESET 6 AD9851 Gray
Xfrmr:DDS:
Gnd Gnd 5 AD9851 White
- Zout2 1 AD9851 Red
***Display SPI:
--Board needs stepdown from 5v to 3.3v
--for all inputs.
--Divider instructions:
--in from UNO --> 220ohm --> LCD in
-- |
-- 470ohm
-- |
-- Gnd
--Voltage divider computation:
--5v x (470/(470+220)) = 3.4v
--Vcc = 5V
UNO LCD LCD Name Routine
ST7735 ILI9341
>>>.... step down through divider
8 ---> SS CS CS TFT_CS Yellow
9 ---> RST RESET RESET TFT_RST Orange
10 ---> A0 D/C DC TFT_DC Purple
11 ---> SDA SDI MOSI TFT_SID Blue
12 SDO MISO *** not used
13 ---> SCK SCK CLK TFT_SCLK Gray
<<<.... end of step down
5v ---> *** LED through 100ohm to Vcc (5v)
GND ---> GND GND GND Brown
5v ---> Vcc Vcc Vcc (from UNO) Red
***SD-Card: (On display) !Currently not implemented!
A3 ---> SD-CS *** divider White
12 ---> SD-MISO Green
11 ---> SD-MOSI *** divider Blue
13 ---> SD-SCK *** divider Gray
**************************************************
Sources:
K1TRB website:
http://personal.colby.edu/personal/t/trberger/pages/hamradio.htm
Use this LiquidCrystal (1602) (not this project):
https://bitbucket.org/fmalpartida/new-liquidcrystal/downloads
Graphics library for ST7735 128 x 160 (credit to Adafruit and others):
https://www.arduino.cc/en/Reference/TFTLibrary
https://www.arduino.cc/en/Reference/SPI
DDS library:
http://m0xpd.blogspot.co.uk/2014/03/dds-and-duedds-libraries.html
https://github.com/m0xpd/DDS
Rotary encoder with interrupts
https://github.com/brianlow/Rotary
Button:
https://www.arduino.cc/en/Tutorial/Button
Adjust power of AD9850, Change Filter:
http://www.rudiswiki.de/wiki/AmateurRadioDDSgenerator
Functionality:
http://pages.suddenlink.net/wa5bdu/PHSNA_QQ_Spring_14.pdf
https://groups.yahoo.com/neo/groups/PHSNA/info
http://midnightdesignsolutions.com/nat/
**************************************************
Major Parts: (On eBay) (2/12/16)
http://www.ebay.com/itm/1MHZ-600MHZ-RF-Signal-Power-Detector-Logarithmic-
Environmental-Field-Detection-/231629060443?hash=item35ee2af95b:g:HWAAAOSw37tV-rJV
$11.95 AD8307
http://www.ebay.com/itm/AD9851-DDS-Signal-Generator-Module-0-70MHz-2-Sine-Wave-
and-2-Square-Wave-M-/172051686440?hash=item280f146428:g:Sa0AAOSwKtlWi5YO
$12.49 + $1.98 = $14.47 AD9851
http://www.ebay.com/itm/1-8-inch-1-8-TFT-LCD-Display-module-ST7735S-128x160-51-
AVR-STM32-ARM-8-16-bit-/371462246346?hash=item567ce00fca:g:eT4AAOxy79JSZNBv
$3.78 128x160 TFT
http://www.ebay.com/itm/12mm-Rotary-Encoder-Push-Button-Switch-Keyswitch-
Electronic-Components-Easy-Use-/171909824262?hash=item28069fbf06:g:B7UAAOSw~gRVk2YH
$0.99 Rotary Encoder w/ button
http://www.ebay.com/itm/Mini-USB-Nano-ATmega328P-5V-16MHz-Micro-controller-board-
For-Arduino-ATMEGA128P-/291635663891?hash=item43e6d71413:g:FZIAAOSwfZ1WZ~mk
$2.56 Arduino Nano clone
http://www.ebay.com/itm/10pcs-3296W-103-3296-W-10K-ohm-Trim-Pot-Trimmer-
Potentiometer-Square-Shape-/381100607740?hash=item58bb5dc8fc:g:PjYAAOSwg3FUmq7T
$0.99 10K trimmer
http://www.ebay.com/itm/IPX-IPEX-to-SMA-Female-Jack-U-FL-RF-Pigtail-Antenna-
Jumper-Cable-25CM-/361036269737?hash=item540f7048a9:g:SBUAAOSwxH1UAUSM
$0.99 sma jack with pigtail
http://www.ebay.com/itm/Waterproof-100-x-68-x-50mm-Plastic-Electronic-Project-
Box-Enclosure-Case-/161918708804?hash=item25b31b6c44:g:YiUAAOSwZ1lWb8DQ
$2.15 Project Box 100x68x50 (mm)
Total $35.90
Searching eBay.com may save a bit more.
Other stuff
http://www.ebay.com/itm/New-MB102-3-3V-5V-Power-Supply-Module-Set-Breadboard-
Jumper-Cables-for-Arduino-/141856352405?hash=item21074c2895:g:E40AAOSwyQtVsfwq
$4.03 + $0.34 Breadboard, power, jumpers
Output circuit: (14 turns tapped 10 turns above ground)
In ---||---|
.1uF C
C -------- Out
DDS C BN43-2402
C 14t:10t
|
--------------------- GND
*/
/**************************************************/
/* The Program YANA */
/* T. Berger 2016-03-15 */
/**************************************************/
// a space is needed ahead of "#include" for it to work.
#include // SPI interface
#include // Rotary Encoder
#include // works for AD9851
#include // TFT library ST7735
#include // eeprom routines
/********************************************/
/* Screen Properties Defined Here */
/********************************************/
// The ST7735 values for a grid of 12x20 squares
// landscape would be: 160 x 128 display.
// portrait values are: 128 x 160 display
// The first three definitions simplify switching to
// another display.
#define TFT_NAME Adafruit_ST7735
#define TFT_FUNC Adafruit_ST7735
#define TFT_START tft.initR(INITR_BLACKTAB)
#define SCREEN_UPRIGHT 2L // 0,2 Portrait 1,3 Landscape coded: 2
#define X_GRID_STEP 12L // grid pixels wide (for 10)
#define X_GRID_END 128L // screen pixels wide
#define Y_GRID_STEP 20L // grid pixels high (for 7)
#define Y_GRID_END 140L // graph pixels high
#define X_START 7L // (X_GRID_END-(10*X_GRID_STEP))-1: start x here
#define X_STEPS 120L // 10*X_GRID_STEP: pixel x steps in grid
#define X_STEPSFL 120.0
#define Y_START 0L
#define Y_END 141L // 7*Y_GRID_STEP+1: pixel y steps in grid
#define BIG_PRT 2 // menu print size
#define SMALL_PRT 1 // graph print size
// Color definitions for ST7735 and ILI9340
#define Color_BLACK ST7735_BLACK //0x0000
//#define Color_BLUE ST7735_BLUE //0x001F
#define Color_RED ST7735_RED //0xF800
#define Color_GREEN ST7735_GREEN //0x07E0
#define Color_CYAN ST7735_CYAN //0x07FF
#define Color_MAGENTA ST7735_MAGENTA //0xF81F
#define Color_YELLOW ST7735_YELLOW //0xFFE0
#define Color_WHITE ST7735_WHITE //0xFFFF
// Colors not in library for ST7735
// http://stackoverflow.com/questions/13720937/c-defined-16bit-high-color
#define Color_ORANGE 0xFD20
#define Color_BROWN 0x6260
#define Color_GRAY 0xC618
#define Color_BLUE 0x00FF
//very light gray C618
//light gray A514
//mid gray 8410
//gray 4208
/********************************************/
/* EEPROM Allocations */
/* */
/* Values are uint32_2 (two bytes) */
/* Order is low-byte, high-byte */
/* */
/* 10 Calibration frequencies are: */
/* .05,.1,1,10,20,30,40,50,60,70MHz */
/* Each freq gets a through reading. */
/* -30db reading at 1MHz */
/* Power reading of -10dbm@10MHz */
/* Each freq gets a SWR reading. */
/* Organization: */
/* 0 = CALIB_SNA = first freq */
/* ... */
/* 9 = CALIB_END_F = last freq */
/* 10 = CALIB_30db = 30db adc value@1MHz */
/* 11 = CALIB_PWR = adc: -10dbm@1MHz */
/* 12 = CALIB_SWR */
/* ... */
/* 21 = CALIB_END_SWR */
/********************************************/
// if CALIB_SNA is changed from 0, then the array index in rawADC,
// ereadINT, and ewriteINT must be changed in some places
// so just leave it be
#define CALIB_SNA 0 // start eeprom address for calibration values
#define CALIB_F_SIZE 10 // number of frequencies
#define CALIB_30db 10 // location where 30db value is stored
#define CALIB_F_30db 2 // 1MHz location in freqs[]
#define CALIB_ATTEN30db 28.57 // build value // Value of 30db attenuator
#define CALIB_PWR 11 // location for -10dbm adc value
#define CALIB_F_PWR 2 // 1MHz location in freqs[]
#define CALIB_SWR 12 // start of SWR table
#define CALIB_SIZE 22 // number of calibration values ([10]SNA+[1]30db+[1]PWR+[10]SWR)
#define NEXT_BASE CALIB_SNA+(2*CALIB_SIZE) // start of next block of eeprom
/********************************************/
/* Various Constants */
/********************************************/
// for changing dbm to volts
#define DBM_VOLT 0.6505149978 // 0 volt offset for dbm to volts
// precision is much greater than accuracy
// Yana states
// The program is a finite state machine
#define STATE_INSTR 'I' // State controls SNA/SWR/PWR/L/C
#define STATE_F_MODE 'F' // State controls S/F C/R
#define STATE_G_MODE 'G' // State controls Graph Dims
#define STATE_OP 'O' // State controls RUN/CAL/LS/CS
#define STATE_F1 'B' // State controls first frequency
#define STATE_F2 'E' // State controls second frequency
#define STATE_L 'L' // State controls left mark
#define STATE_R 'R' // State controls right mark
#define STATE_W 'W' // State: working on running a machine
// Yana Op modes
#define OP_MAX 1 // largest operation number
// Yana Instrument Modes
#define MODE_INSTR_SNA 0 // SNA Grid
#define MODE_INSTR_SWR 1 // SWR Grid
#define MODE_INSTR_PWR 2 // PWR Values
#define MODE_INSTR_SNL 3 // Measure L
#define MODE_INSTR_SNC 4 // Measure C
#define INSTR_MAX 4 // largest instrument number
// Tuning mode display
#define TUNE_SNA 0
#define TUNE_SWR 1
#define TUNE_PWR 2
#define TUNE_L 3
#define TUNE_C 4
// Frequency Modes
#define MODE_FREQ_SF 0 // start/finish frequency menu
#define MODE_FREQ_CR 1 // center radius frequency menu
#define SC_MAX 1 // largest tuning type mode
// Graph Mode Ranges
#define MODE_GR_SMALL 0 // SMAll dimension
#define MODE_GR_BIG 1 // BIG dimension
#define GR_MAX 1 // largest graphing dimension mode
// Operation Modes
#define MODE_OP_RUN 0 // run the selected instrument
#define MODE_OP_CAL 1 // Calibrate the instrument
// Rotary Encoder pins on UNO
#define ENCODER_B 2 // Encoder pin B
#define ENCODER_A 3 // Encoder pin A
// The AD9851 pins on the UNO.
// On AD9851 infor pins, Vcc=10, and GND=5
// selected so pins line up with Nano pins when modules are side by side
#define W_CLK 4 //on UNO 9 on module
#define FQ_UD 5 // 8
#define DDS_DATA 6 // 7
#define DDS_RESET 7 // 6
// The real 6xclock frequency is entered here
#define TUNE_WORD 180000000L // 180000000 ideal value
// TFT pins on UNO
#define TFT_CS 8 //Yellow
#define TFT_RST 9 //Orange
#define TFT_DC 10 //Purple
// The following pins are predefined in the driver
//#define TFT_MOSI 11 //Blue (11,12,13 coded in driver)
//#define TFT_MISO 12 //
//#define TFT_CLK 13 //Gray
// Button pin on UNO
#define ENCODER_BTN A0 // Encoder Button
// AD8307 Detector and supply Voltages into these ADC pins on UNO
#define VOLTAGE_IN A1 // Detector voltage
#define DC_AIN A7 // supply voltage
#define DC_TO_VOLT 102.4 // convert to Volt
#define L_STD 1234 // standard L in nH
#define C_STD 617 // standard C in pF
// Tuning Frequency Limits
#define F_MIN 50000L // Lower frequency limit
#define F_MAX 70000000L // Upper frequency limit
#define F_MID 35025000L // mid range
#define F_INCR_MAX 1000000L // max size increment in F change
#define PWR10_MAX 10000000L // maximum power of 10 in frequency
// LC limits (nH, pF)
#define LC_MIN 1L
#define LC_MAX 10000000L
//char Temp;
unsigned int intTemp; // Loop variable for scanning
// Timing limits
#define PUSH_TIME 20 // Push button until screen goes blank
#define HOLD_TIME 2*PUSH_TIME // Push button until the frequency menu appears
#define SWEEPDELAY_TIME 100 // 1.0 sec delay after each sweep
#define MAX_SWEEP_WAIT 2 // max 1.0 seconds to wait
// Big Print Lines
#define LINE_2B 2 // Control Mode Line
#define LINE_3B 3 // Line for Text for Frequency Center or Start
#define LINE_4B 4 // Line for Count for Frequency Center or Start
#define LINE_5B 5 // Line for Text for Text for Frequency Radius or Finish
#define LINE_6B 6 // Line for count for for Frequency Radius or Finish
#define LINE_7B 7 // Line for Text for Left
#define LINE_8B 8 // Line for count for Left
#define LINE_9B 9 // Line for Text for Right
#define LINE_10B 10 // Line for count for Right
// Command Line Field positions
#define LNPOS_INSTR 1 // instrument command field
#define LNPOS_FREQ 5 // frequency mode command field
#define LNPOS_GR 7 // graph span command field
#define LNPOS_OP 10 // operation command field
// The graph is 140 pixels high
// Graph is divided into 7 grids of 20 pixels each in height
// Floating constants are needed to convert
// swr per pixel and
// db per pixel
// on a 10db grid, 2.0 pixels per db would work
// on a 3db grid, 20.0/3.0 = 6.66667 per db would work
// On 1 swr unit per grid, 1 unit per 20.0 would work
// On .5 swr per grid, 1 unit per 40.0 would work
#define DB_BIG_SNA 2.0 // (7 x 10) 10 in modes_gr
#define DB_SMALL_SNA 6.66667 // (7 x 3) 3 in modes_gr
#define UNIT_BIG_SWR 20.0 // (7 x 2) 2 in modes_gr
#define UNIT_SMALL_SWR 40.0 //4.0 // (7 x 0.5) .5 in modes_gr
// power of 10 for cursor stepping
#define CURSOR_STEP 100L // cursor step size
// string length (for conversions)
#define STRLEN 16 // Maximum string length for conversion
/********************************************/
/* Hardware Declarations */
/********************************************/
// Instantiate TFT
TFT_NAME tft = TFT_FUNC(TFT_CS, TFT_DC, TFT_RST);
// Instantiate AD9851 and set addresses
// The Library must support the AD9851 in 6xF mode
DDS dds(W_CLK, FQ_UD, DDS_DATA, DDS_RESET);
// Instantiate the Rotary encoder and set pins
Rotary r = Rotary(ENCODER_A, ENCODER_B);
/********************************************/
/* Variable Declarations */
/********************************************/
/* Sweep Values Data Type */
/********************************************/
struct sweep_type {
long f; // current dds setting
long f1; // Frequency Center or Start
long f2; // Frequency radius or finish
long ml; // Left mark
long mr; // Right mark
long ic; // Increment step size
};
/********************************************/
/* Initial sweep parameter values: */
/* {f,f1,f2,ml,mr,ic,ir} */
/* Set for HF */
/********************************************/
sweep_type sweep = { 1000000L, // current frequency
1000000L, // center/start frequency
31000000L, // radius/finish frequency
1800000L, // left mark
29700000L, // right mark
1000000L, // Increment step size
};
// Temporary save parameters
long tmpf, tmpic, tmpf1, tmpf2, tmprcnt, tmprinc, tmprcntmx, tmprcntmn;
// Flags
// boolean tuning // == true when in graph tuning mode
// boolean turned_t // flag: Turn (declared below)
// boolean down_b // flag: Button Down (declared below)
// boolean down_c // flag: Click (not needed)
boolean down_p = false; // flag: Push
boolean down_h = false; // flag: Hold
boolean usb_on = false; // flag: true when usb is on
// Field Commands
// Home screen states
// yana_state STATE_INSTR: Set instrument
// yana_state STATE_F_MODE: Set freq mode (start/finish or center/radius)
// yana_state STATE_G_MODE: Set graphing range (graph y-axis scale/division)
// yana_state STATE_OP: Set operation (run, cal, usb)
// yana_state STATE_F1: Set center or start (begin) frequency
// yana_state STATE_F2: Set span radius or finish (end) frequency
// yana_state STATE_L: Set left freq
// yana_state STATE_R: Set right freq
// State not on home screen
// yana_state STATE_W: Working: SNA, SWR, PWR, Remote Control
char yana_state; // current yana_state
char modes_instr[5][4] = {"SNA", "SWR", "PWR", "SNL", "SNC"}; // Yana instruments
char modes_freq[2][2] = {"S", "C"}; //start/finish or center/radius
char modes_gr[3][2][3] = {{" 3", "10"}, {".5", " 1"}, {"__", "__"}}; //scales for graphs
char modes_op[2][2] = {"R", "C"}; // operations: run, calibrate
// Yana instrument operations: SNA, SWR, PWR
// startup state is assigned here
int8_t mode_instr = MODE_INSTR_SNA; // SNA;
int8_t mode_instr_1 = MODE_INSTR_SNA; // field marker
int8_t mode_freq = MODE_FREQ_SF; // frequency setting modes
int8_t mode_freq_now = MODE_FREQ_SF; // current mode (to detect changes)
int8_t mode_gr = MODE_GR_BIG; // graph range
int8_t mode_op = MODE_OP_RUN; // operations: run, cal, set std, usb
int8_t mode_tune = TUNE_SNA; // L, C, SNA separator
long l_std = L_STD; // nH
long c_std = C_STD; // pF
// The calibrations frequencies
long freqs[CALIB_F_SIZE] =
{ 50000L, 100000L, 1000000L, 10000000L, 20000000L, 30000000L,
40000000L, 50000000L, 60000000L, 70000000L
}; // .05,.1,1,10,20,30,40,50,60,70MHz
int calibrations[CALIB_SIZE]; // calibration values
float perdb; // adc units per db
float orig0dbmfl; // adc relative +0dbm value
float orig0rlfl; // adc on bridge: RL=0 value
char unitsw[5][3] = {" W", "mW", "uW", "nW", "pW"}; // units for watts
char unitsv[3][3] = {" V", "mV", "uV"}; // units for volts
char outstr[STRLEN]; // buffer for float to string conversion
long maxfr, minfr; // temporary variable
/***********************************************************************/
/* Interrupt set values from rotary encoder and button */
/* */
/* Variables used both inside and outside an interrupt service routine */
/* must be declared volatile */
/***********************************************************************/
volatile boolean turned_t; // flag: Knob turned
volatile long rotary_count; // frequency
volatile long rotary_increment; // frequency increment
volatile long rotary_count_min; // min frequency
volatile long rotary_count_max; // max frequency
volatile boolean down_b; // button was pushed
/**************************************************************/
/* rotary encoder and button functions */
/* */
/* ISR(PCINT2_vect) rotary interrupt */
/* set_rotary_count(short dir) rotary counter */
/* set_rotary_params(rc,rs,rmx,rmn) rotary parameters */
/* display_count(line,loc,dig,crs,cnt) display rotary count */
/* get_button() read a button press */
/* ISR(PCINT1_vect) button interrupt */
/* down_b interrupt when button is pushed */
/**************************************************************/
// **** Begin interrupt service routines ****
/****************************************/
/* Interrupt service */
/* */
/* dir = 1 Increment */
/* dir = -1 Decrement */
/* turned_t = true: knob was turned */
/****************************************/
/* */
/* Interrupt service routine for */
/* encoder knob turn */
/* */
/* ISR(PCINT2_vect) */
/* */
/* returns +1 for CW & -1 for CCW */
/* If it's not this way, swap the */
/* A,B connections to the rotary */
/****************************************/
ISR(PCINT2_vect) {
unsigned char result = r.process();
if (result == DIR_CW)
set_rotary_count(1);
else if (result == DIR_CCW)
set_rotary_count(-1);
}
/****************************************/
/* set_rotary_count(short dir) */
/* */
/* AD9851 not changed here */
/* if turned, set t=1 and increment */
/* do not change if at limit */
/* parameters: */
/* dir = direction +1=CW & -1=CCW */
/* */
/* variables changed by this routine: */
/* rotary_count, turned_t */
/****************************************/
void set_rotary_count(short dir)
{
if (rotary_increment != 0)
{
if ((dir == 1) && (rotary_count + rotary_increment <= rotary_count_max))
{
rotary_count += rotary_increment;
}
else if ((dir == -1) && (rotary_count >= rotary_increment + rotary_count_min))
{
rotary_count -= rotary_increment;
}
turned_t = true;
}
}
/***************************************/
/* Interrupt service routine for */
/* button */
/* */
/* Only the down_b flag is set */
/***************************************/
ISR(PCINT1_vect)
{
boolean button = digitalRead(ENCODER_BTN);
if (!button)
{
down_b = true; // button is down
}
}
// **** End interrupt service routines ****
/***********************************/
/* Available SRAM */
/* For Testing xxx */
/***********************************/
/* === Commented Out ===
int freeRam ()
{
extern int __heap_start, *__brkval;
int v;
return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}
void showFree()
{
display_count(2,1,4,SMALL_PRT,1000000000L,freeRam());
}
// showFree(); // testing ram left
// test: used for inserting stubs
int zzz,a1,a2,a3;
float n1,n2,n3;
zzz=10;
display_count(zzz,1,8,SMALL_PRT,sweep.ic,a1); zzz++;
display_count(zzz,1,8,SMALL_PRT,sweep.ic,a2); zzz++;
display_count(zzz,1,8,SMALL_PRT,sweep.ic,a3); zzz++;
display_db(zzz,12,SMALL_PRT,(int)mcurs);
while(!get_button()){}
doPrint(cntln,18,SMALL_PRT,Color_WHITE,"Tune");
while(!get_button()){}
zzz === test ... to see values ===
int zzz;
zzz=10;
display_count(zzz,1,8,SMALL_PRT,sweep.ic,(int)(10000.0*perdb)); // zzz++;
doPrint(zzz,17,SMALL_PRT,Color_WHITE,"perdb"); zzz++;
display_count(zzz,1,8,SMALL_PRT,sweep.ic,(int)(10000.0*orig0dbmfl));// zzz+
+;
doPrint(zzz,18,SMALL_PRT,Color_WHITE,"pwr0");// zzz++;
// display_count(zzz,1,8,SMALL_PRT,sweep.ic,a3); zzz++;
// display_db(zzz,12,SMALL_PRT,(int)mcurs);
// while(!get_button()){}
while(!get_button()){}
zzz === end test ... to see values ===
=== End Commented Out === */
/*************************************/
/* set_rotary_params(rc,rs,rmn,rmx) */
/* */
/* Set the rotary parameters */
/* parameters: */
/* rc = rotary_count */
/* rs = rotary_increment */
/* rmx = rotary_count_max */
/* rmn = rotary_count_min */
/*************************************/
void set_rotary_params( long rc, long rs, long rmx, long rmn)
{
noInterrupts(); // an interrupt here would be nasty
rotary_count = rc;
rotary_increment = rs;
rotary_count_max = rmx;
rotary_count_min = rmn;
turned_t = false;
interrupts();
}
/***********************************/
/* Compute the power 10^E */
/***********************************/
long ten_to_the(uint8_t E)
{
long N = 1;
while (E > 0)
{
N = N * 10;
E--;
}
return (N);
}
/***********************************/
/* Compute the power of 10 in N */
/***********************************/
long pwr10in( long N)
{
long pwr = PWR10_MAX;
if (N == 0)
{
return (0);
}
else
{
while (N / pwr == 0)
{
pwr = pwr / 10;
}
}
return (pwr);
}
/*************************************/
/* Compute the number of digits in N */
/*************************************/
uint8_t digitsInN( long N)
{
uint8_t dig = 0;
while (N > 0)
{
N = N / 10;
dig++;
}
return (dig);
}
/**********************************************************/
/* float_fix(float int int) */
/* change floating point to string */
/* val floating point value */
/* len length of final string including decimal point */
/* dacc digits after decimal */
/* final string length is len-dacc */
/**********************************************************/
void float_fix(float val, int len, int dacc)
{
int tmp;
dtostrf(val, len, dacc, outstr);
while (outstr[0] == ' ')
{
for (tmp = 0; tmp < 15; tmp++)
{
outstr[tmp] = outstr[tmp + 1];
}
}
len = len - dacc;
outstr[len] = 0;
}
/*******************************************/
/* Screen Writing Routines */
/* */
/* doPrint(line,loc,siz,clr,*txt ) */
/* display_count(line,loc,dig,siz,crs,cnt) */
/* CurrentField(line,loc,siz,txt) */
/* tftPrintCMenu() */
/* tftPrintFMenu() */
/* NextFMenuLine(menu,line,loc) */
/* tftPrintGrid() */
/* tftLabelGrid() */
/*******************************************/
/************************************/
/* doPrint(line,loc,siz,clr,*txt ) */
/* */
/* Print text at a location */
/* parameters: */
/* line=line on screen */
/* loc= position in line */
/* siz= character size */
/* clr= text color */
/* txt= text to print */
/* Black background */
/************************************/
void doPrint(uint8_t line, uint8_t loc, uint8_t siz, uint16_t clr, const char *txt )
{
tft.setTextSize(siz);
tft.setTextColor(clr, Color_BLACK); // text and background
tft.setCursor(siz * 6 * (loc - 1), siz * 8 * (line - 1));
tft.print(txt);
}
/*******************************************/
/* display_count(line,loc,dig,siz,crs,cnt) */
/* */
/* Displays the counter */
/* */
/* chars are in a 6x8 block */
/* 128w x 160 h (screen) */
/* Character counts on the screen: */
/* Landscape */
/* size 1 2 3 4 5 7 8 14 17 */
/* wide 26 13 9 6 5 4 3 2 1 */
/* high 16 8 5 4 3 2 2 1 1 */
/* Portrait */
/* size 1 2 3 4 5 7 8 14 17 */
/* wide 20 10 */
/* high 20 10 */
/* If crs > dig then no cursor is shown. */
/* parameters: */
/* line: line of display (1 to height) */
/* loc: start location: 0 to width-1 */
/* dig: size of field in digits */
/* frequencies: 8 */
/* */
/* siz: size of characters (1 or 2) */
/* incr: cursor power of 10 */
/* cnt: The number to display */
/*******************************************/
void display_count(uint8_t line, uint8_t loc, uint8_t dig, uint8_t siz, long
incr, long cnt)
{
uint8_t crs;
long f, d = 1; // 8 digits
char n = ' '; // Leading zero blanking: changes to '0'
crs = digitsInN(incr); // cursor position
tft.setTextSize(siz);
tft.setTextColor(Color_YELLOW, Color_BLACK); // text and background
tft.setCursor(siz * 6 * (loc - 1), siz * 8 * (line - 1));
// compute d: the power of ten in the field
d = ten_to_the(dig - 1);
// Pick out each digit and print it
while (d >= 1)
{
f = (cnt % (10 * d)) / d; // current digit
if (dig == crs)
{
tft.setTextColor(Color_GREEN, Color_BLUE); // Set cursor
}
if (f > 0)
{
tft.print(f);
n = '0'; // done with leading zeros
}
else
{
if (d == 1 && cnt == 0) // if cnt==0 all spaces leading
{
n = '0';
}
tft.print(n); //leading = space, after = 0
}
if (dig == crs)
{
tft.setTextColor(Color_YELLOW, Color_BLACK); // unset cursor
}
if ((d == 1000) || (d == 1000000)) // comma insertion
{
if (n == '0')
tft.print(',');
else
tft.print(' '); // no comma, still leading
}
dig--;
d = d / 10;
}
}
/*************************************/
/* display db */
/*************************************/
void display_db(uint8_t line, uint8_t loc, uint8_t siz, int cnt)
{
int f, d = 100; // 8 digits
char n = ' '; // Leading zero blanking: changes to '0'
tft.setTextSize(siz);
tft.setTextColor(Color_YELLOW, Color_BLACK); // text and background
tft.setCursor(siz * 6 * (loc - 1), siz * 8 * (line - 1));
if (cnt < 0)
{
tft.print('-');
cnt = -cnt;
}
else
{
tft.print(' ');
}
while (d >= 1)
{
f = (cnt % (10 * d)) / d; // current digit
if (f > 0)
{
tft.print(f);
n = '0'; // done with leading zeros
}
else
{
if (d == 1 && n == ' ') // if cnt==0 all spaces leading
{
n = '0';
}
tft.print(n); //leading = space, after = 0
}
if (d == 10) // decimal insertion
{
tft.print('.');
}
d = d / 10;
}
}
/************************************/
/* CurrentField(line,loc,siz,txt) */
/* */
/* MARK the current field */
/* */
/* line: line of field name */
/* loc: start location on line */
/* siz: character size (1,2) */
/* txt: First letter field name */
/************************************/
void CurrentField(uint8_t line, uint8_t loc, uint8_t siz, char txt)
{
tft.setTextSize(siz);
tft.setTextColor(Color_RED, Color_GREEN); // text and background
tft.setCursor(siz * 6 * (loc - 1), siz * 8 * (line - 1));
tft.print(txt);
}
/***************************************/
/* Display supply voltage, color coded */
/***************************************/
void dsp_supp_volt()
{
unsigned int dc_in = 0;
float dc_volt;
uint16_t voltColor = Color_GREEN;
dc_in = dc_in + analogRead(DC_AIN);
dc_volt = dc_in / DC_TO_VOLT;
float_fix(dc_volt, 6, 2);
if (dc_volt < 7.5) voltColor = Color_YELLOW; // less than 20% capacity
if (dc_volt < 7.2) voltColor = Color_RED; // less than 5% capacity
doPrint(1, 15, SMALL_PRT, voltColor, outstr);
doPrint(1, 19, SMALL_PRT, voltColor, "V");
}
/************************************/
/* Print Command menu */
/* */
/* tftPrintCMenu() */
/************************************/
void tftPrintCMenu()
{
int i;
char strHeader[13] = "*Yana K1TRB "; // don't forget null at end
doPrint(1, 3, SMALL_PRT, Color_BLUE, strHeader);
dsp_supp_volt();
doPrint(LINE_2B, 1, BIG_PRT, Color_ORANGE, modes_instr[mode_instr_1]);
doPrint(LINE_2B, 5, BIG_PRT, Color_ORANGE, modes_freq[mode_freq]);
doPrint(LINE_2B, 7, BIG_PRT, Color_ORANGE, modes_gr[mode_instr][mode_gr]);
doPrint(LINE_2B, 10, BIG_PRT, Color_ORANGE, modes_op[mode_op]);
}
/*************************************/
/* */
/* print frequencies on home scrreen */
/* */
/*************************************/
void tftPrintFFreqs()
{
doPrint(LINE_3B, 8, BIG_PRT, Color_WHITE, "F");
doPrint(LINE_5B, 8, BIG_PRT, Color_WHITE, "F");
doPrint(LINE_7B, 8, BIG_PRT, Color_WHITE, "F");
doPrint(LINE_9B, 8, BIG_PRT, Color_WHITE, "F");
display_count(LINE_4B, 1, 8, BIG_PRT, sweep.ic, sweep.f1);
display_count(LINE_6B, 1, 8, BIG_PRT, sweep.ic, sweep.f2);
display_count(LINE_8B, 1, 8, BIG_PRT, sweep.ic, sweep.ml);
display_count(LINE_10B, 1, 8, BIG_PRT, sweep.ic, sweep.mr);
}
/************************************/
/* Print sweep frequency menu */
/* */
/* tftPrintFMenu() */
/************************************/
void tftPrintFMenu()
{
if (mode_freq == MODE_FREQ_CR)
{
doPrint(LINE_3B, 1, BIG_PRT, Color_WHITE, "Center "); // Center freq
doPrint(LINE_5B, 1, BIG_PRT, Color_WHITE, "Radius "); // Radius freq
}
else
{
doPrint(LINE_3B, 1, BIG_PRT, Color_WHITE, "Start "); // Start freq
doPrint(LINE_5B, 1, BIG_PRT, Color_WHITE, "Finish "); // Finish freq
}
doPrint(LINE_3B, 8, BIG_PRT, Color_WHITE, "Frq"); // Center freq
doPrint(LINE_5B, 8, BIG_PRT, Color_WHITE, "Frq"); // Radius freq
doPrint(LINE_7B, 1, BIG_PRT, Color_WHITE, "Left Frq"); // Left Mark freq
doPrint(LINE_9B, 1, BIG_PRT, Color_WHITE, "Right Frq"); // Right Mark freq
tftPrintFFreqs();
}
/************************************/
/* Next Menu Line */
/* */
/* NextFMenuLine(menu,line,loc) */
/* */
/* Print frequency menu (i.e. */
/* no cursor) then add cursor to */
/* current field */
/* menu is the leading character */
/* in the cursor field */
/* line is the line of this item */
/************************************/
void NextFMenuLine(char menu, uint8_t line)
{
yana_state = menu;
tftPrintFFreqs();
CurrentField(line, 8, BIG_PRT, 'F');
tftPrintCMenu();
}
/************************************/
/* Print a grid on the screen */
/* */
/* tftPrintGrid() */
/************************************/
void tftPrintGrid()
{
tft.fillScreen(Color_BLACK);
for (uint16_t y = Y_START; y < Y_END; y += Y_GRID_STEP)
{
tft.drawFastHLine(X_START, y, Y_GRID_END, Color_GRAY);
}
for (uint16_t x = X_START; x < X_GRID_END; x += X_GRID_STEP)
{
tft.drawFastVLine(x, Y_START, Y_END, Color_GRAY);
}
tft.drawFastVLine((X_START + X_GRID_END) / 2, Y_START, Y_END, Color_RED);
}
/************************************/
/* Label the Grid */
/* */
/* tftLabelGrid() */
/* */
/* gt is grid type */
/************************************/
void tftLabelGrid()
{
if (mode_instr == MODE_INSTR_SNA)
{
doPrint(20, 18, SMALL_PRT, Color_WHITE, "SNA");
if (mode_gr == MODE_GR_BIG)
{
doPrint(1, 1, SMALL_PRT, Color_YELLOW, "0");
doPrint(5, 1, SMALL_PRT, Color_YELLOW, "20");
doPrint(10, 1, SMALL_PRT, Color_YELLOW, "40");
doPrint(15, 1, SMALL_PRT, Color_YELLOW, "60");
}
else
{
doPrint(1, 1, SMALL_PRT, Color_YELLOW, "0");
doPrint(5, 1, SMALL_PRT, Color_YELLOW, "6");
doPrint(10, 1, SMALL_PRT, Color_YELLOW, "12");
doPrint(15, 1, SMALL_PRT, Color_YELLOW, "18");
}
}
else if (mode_instr == MODE_INSTR_SWR)
{
doPrint(20, 18, SMALL_PRT, Color_WHITE, "SWR");
if (mode_gr == MODE_GR_BIG)
{
doPrint(1, 1, SMALL_PRT, Color_YELLOW, "1");
doPrint(5, 1, SMALL_PRT, Color_YELLOW, "3");
doPrint(10, 1, SMALL_PRT, Color_YELLOW, "5");
doPrint(15, 1, SMALL_PRT, Color_YELLOW, "7");
}
else
{
doPrint(1, 1, SMALL_PRT, Color_YELLOW, "1");
doPrint(5, 1, SMALL_PRT, Color_YELLOW, "2");
doPrint(10, 1, SMALL_PRT, Color_YELLOW, "3");
doPrint(15, 1, SMALL_PRT, Color_YELLOW, "4");
}
}
else // if(mode_op==MODE_INSTR_PWR)
{
doPrint(20, 18, SMALL_PRT, Color_WHITE, "PWR");
doPrint(LINE_2B, 4, BIG_PRT, Color_WHITE, "Power"); // dbm Power
doPrint(LINE_8B, 1, BIG_PRT, Color_WHITE, "Frequency"); // DDS Freq
}
}
/********************************************/
/* incr_crsr Move the cursor along the line */
/********************************************/
void incr_crsr()
{
sweep.ic = sweep.ic / CURSOR_STEP;
if (sweep.ic < 1) // loop increment
{
sweep.ic = F_INCR_MAX;
}
}
/********************************************/
/* Read the button with debouncing */
/* */
/* get_button() */
/* */
/* Quick click just records and exits */
/* Screen clears on Push */
/* Cleared screen changed to menu on Hold */
/* return value 1: button depressed */
/* */
/* click: return 1, p=false,h=false */
/* push: return 1, p=true, h=false */
/* hold: return 1, p=false, h=true */
/* down_h==true: it's a hold */
/********************************************/
boolean get_button()
{
short short_p = 0;
down_p = false; // ready for push or hold
down_h = false;
if (!digitalRead(ENCODER_BTN)) // ! means it's pressed
{
delay(20);
if (!digitalRead(ENCODER_BTN)) // It's down: keep checking
{
short_p = 0;
// Wait for Push
while ((!digitalRead(ENCODER_BTN)) && (short_p < PUSH_TIME))
{
short_p++;
delay(20);
};
// Process Push or hold
if (short_p >= PUSH_TIME)
{
tft.fillScreen(Color_BLACK); // signal a Push
down_p = true; // Push flag set
short_p = 0;
while ((!digitalRead(ENCODER_BTN)) && (short_p < HOLD_TIME))
{
short_p++;
delay(20);
}
// Process Hold
if (short_p >= HOLD_TIME)
{
tft.fillScreen(Color_BLUE); // signal a Hold
down_h = true; // Hold flag
down_p = false; // it's not a Push
while (!digitalRead(ENCODER_BTN))
{
delay(1);
}; // wait for button release
delay(1);
}
}
return 1; // the button was depressed
} // down < 20 doesn't count
} // don't count just a bounce
return 0; // the button was not depressed
}
/**********************************************************/
/* Set sweep parameters in bounds */
/* */
/* void fix_freqs() */
/* */
/* switching true==switch center/radius <-> start/finish */
/**********************************************************/
void fix_freqs(boolean switching)
{
long mxfr, mnfr;
// start/finish mode
if (mode_freq == MODE_FREQ_SF) // in start finish mode
{
if (switching)
{
// coming from center/radius
// keep start finish related to center/radius
mxfr = (sweep.f1 - sweep.f2); // new start
sweep.f2 = sweep.f1 + sweep.f2; // new finish
sweep.ml = sweep.f1 - sweep.ml; // new mark positions
sweep.mr = sweep.f1 + sweep.mr;
sweep.f1 = mxfr; // set new start
}
// Keep frequencies between F_MIN and F_MAX
if ((sweep.f1 < F_MIN) || (sweep.f1 > F_MAX))
{
sweep.f1 = F_MIN;
}
if ((sweep.f2 < sweep.f1) || (sweep.f2 > F_MAX))
{
sweep.f2 = F_MAX;
}
sweep.f = sweep.f1;
// check the marks
if ((sweep.ml < sweep.f1) || (sweep.ml > sweep.f2))
{
sweep.ml = sweep.f1; // set it at the beginning
}
if ((sweep.mr < sweep.ml) || (sweep.mr > sweep.f2))
{
sweep.mr = sweep.f2; // set it at the end
}
} // end start/finish mode
// center/radius mode
else // mode_freq == MODE_FREQ_CR
{
if (switching)
{
// coming from start/finish
// keep center/radius related to start/finish
mxfr = (sweep.f2 - sweep.f1) / 2L; // radius
sweep.f1 = (sweep.f2 + sweep.f1) / 2L; // center
sweep.f2 = mxfr;
if (sweep.ml <= sweep.f1)
{
sweep.ml = sweep.f1 - sweep.ml; // former position
}
else
{
sweep.ml = sweep.f1 - mxfr; // set at edge
}
if (sweep.mr >= sweep.f1)
{
sweep.mr = sweep.mr - sweep.f1; // former position
}
else
{
sweep.mr = sweep.f1 + mxfr; // set at edge
}
}
if (sweep.f1 > F_MAX)
{
sweep.f1 = F_MAX;
}
else if (sweep.f1 < F_MIN)
{
sweep.f1 = F_MIN;
}
mxfr = F_MAX - sweep.f1; // determine max radius
mnfr = sweep.f1 - F_MIN;
if (mxfr > mnfr)
{
mxfr = mnfr;
}
// mxfr is now max radius
// make sure radius less than mxfr
if (sweep.f2 > mxfr)
{
sweep.f2 = mxfr;
}
sweep.f = sweep.f1;
// check marks
if (sweep.ml > sweep.f2)
{
sweep.ml = sweep.f2; // set at end
}
if (sweep.mr > sweep.f2)
{
sweep.mr = sweep.f2; // set at end
}
}
mode_freq_now = mode_freq; // no longer need to worry about change of mode
}
/**********************************************************/
/* Setup for menu entries */
/* */
/* void go_home() */
/* void set_for_f1() */
/**********************************************************/
/* Set for frequency entry (yana_state STATE_F1) */
/**********************************************************/
void set_for_f1()
{
set_rotary_params(sweep.f1, sweep.ic, F_MAX, F_MIN);
sweep.f = sweep.f1; // tuned freq = top line freq
dds.setFrequency(sweep.f); // frequency is tuned on the frequency line
tftPrintFMenu();
tftPrintCMenu();
// show results
NextFMenuLine(STATE_F1, LINE_3B);
}
/**********************************************************/
/* Read and average adc */
/**********************************************************/
int avg_adc(int pin)
{
int index, adcval = 0;
for (index = 0; index < 8; index++)
{
adcval = adcval + analogRead(pin);
delay(10);
}
return (adcval / 8);
}
/**********************************************************/
/* Write an int to eeprom low-byte, high-byte order */
/**********************************************************/
void ewriteInt(int addr, int val)
{
int tmp;
tmp = val % 0x100; // Low byte
EEPROM.write(addr, tmp);
tmp = val / 0x100; // High byte
EEPROM.write(addr + 1, tmp);
}
/**********************************************************/
/* Read an int from eeprom low-byte, high-byte order */
/**********************************************************/
int ereadInt(int addr)
{
int tmp1, tmp2;
tmp1 = EEPROM.read(addr); // Low byte
tmp2 = EEPROM.read(addr + 1); // high byte
tmp1 = 0x100 * tmp2 + tmp1; // hex shift the high byte
return (tmp1);
}
/**********************************************************/
/* go_home Go to home screen, home position */
/**********************************************************/
void go_home(boolean fix_cr)
{
fix_freqs(fix_cr);
set_rotary_params(MODE_OP_RUN, 1, OP_MAX, 0);
yana_state = STATE_OP;
mode_op = MODE_OP_RUN;
tft.fillScreen(Color_BLACK);
tftPrintFMenu();
tftPrintCMenu();
CurrentField(LINE_2B, LNPOS_OP, BIG_PRT, modes_op[mode_op][0]);
}
/**********************************************************/
/* Calibration */
/**********************************************************/
/**********************************************************/
/* Install a through cable from RF to DET. */
/* Calibration Frequencies: */
/* .05 .1, 1, 10, 20, 30, 40, 50, 60, 70MHz (10) */
/* At each frequency, read the raw adc output. */
/* Save these values in an array. */
/* */
/* Install a 30db attenuator from RF to DET. */
/* Read the value at 1MHz */
/* Save this value in the array. */
/* */
/* Show the values and allow saving. */
/* If saved, store the values in eeprom */
/**********************************************************/
void calibrate()
{
int crntval, adcval;
int rawADC[CALIB_SIZE];
int8_t index1, index2, index3;
// Pause here to show data
tft.fillScreen(Color_BLACK); // signal a Push
doPrint(1, 1, SMALL_PRT, Color_WHITE, "Current Calibration");
for (index3 = 0; index3 < CALIB_SIZE; index3++)
{
rawADC[index3] = ereadInt(2 * index3); // transfer calibrations to rawADC
}
for (index3 = 0; index3 < CALIB_PWR; index3++)
{
display_count(index3 + 2, 1, 4, SMALL_PRT, 1000000000L, rawADC[index3]);
display_count(index3 + 2, 6, 4, SMALL_PRT, 1000000000L, rawADC[CALIB_PWR + index3]);
}
index3 = index3 + 3;
doPrint(index3, 1, SMALL_PRT, Color_WHITE, "Click: SNA"); index3++;
doPrint(index3, 1, SMALL_PRT, Color_WHITE, "Push: SWR"); index3++;
doPrint(index3, 1, SMALL_PRT, Color_WHITE, "Hold: PWR"); index3++;
while ((!get_button())) {} // wait for button release
// PWR Calibration
if (down_h) // do power
{
tft.fillScreen(Color_BLACK);
index3 = 2;
doPrint(index3, 1, SMALL_PRT, Color_WHITE, "PWR"); index3++;
doPrint(index3, 1, SMALL_PRT, Color_WHITE, "Attach -10dbm to DET"); index3++;
doPrint(index3, 1, SMALL_PRT, Color_WHITE, "Then Click"); index3++;
while ((!get_button())) {} // wait for button release
dds.setFrequency(freqs[CALIB_F_PWR]); // calibration freq
delay(100);
rawADC[CALIB_PWR] = avg_adc(VOLTAGE_IN); // -10dbm adc value
dds.setFrequency(sweep.f); // back on freq
delay(10);
} // end PWR calibration (down_h)
// SWR Calibration
else if (down_p) // do SWR
{
index3 = 2;
doPrint(index3, 1, SMALL_PRT, Color_WHITE, "SWR"); index3++;
doPrint(index3, 1, SMALL_PRT, Color_WHITE, "Attach Bridge"); index3++;
doPrint(index3, 1, SMALL_PRT, Color_WHITE, "With Short"); index3++;
doPrint(index3, 1, SMALL_PRT, Color_WHITE, "Then Click"); index3 = index3++;
while ((!get_button())) {} // wait for button release
if (down_h) {
tft.fillScreen(Color_BLACK);
}
doPrint(index3, 1, SMALL_PRT, Color_WHITE, "Working ..."); index3++;
// set and read averaged adc values
for (index1 = 0; index1 < CALIB_F_SIZE; index1++)
{
dds.setFrequency(freqs[index1]);
delay(100);
rawADC[CALIB_SWR + index1] = avg_adc(VOLTAGE_IN);
}
} // end SWR calibration (down_p)
// (Click) SNA Calibration
else // click do SNA
{
// Pause here for "through" installation
tft.fillScreen(Color_BLACK);
index3 = 2;
doPrint(index3, 1, SMALL_PRT, Color_WHITE, "SNA"); index3++;
doPrint(index3, 1, SMALL_PRT, Color_WHITE, "Attach Through"); index3++;
doPrint(index3, 1, SMALL_PRT, Color_WHITE, "Then Click"); index3++;
while (!get_button()) {} // wait for button release
if (down_h) {
tft.fillScreen(Color_BLACK);
}
doPrint(index3, 1, SMALL_PRT, Color_WHITE, "Working ..."); index3++;
index3++;
// set and read averaged adc values
for (index1 = 0; index1 <= CALIB_F_SIZE - 1; index1++)
{
dds.setFrequency(freqs[index1]);
delay(100);
rawADC[index1] = avg_adc(VOLTAGE_IN);
}
// Pause here for "30db" installation
doPrint(index3, 1, SMALL_PRT, Color_WHITE, "Attach 30db Atten"); index3++;
doPrint( index3, 1, SMALL_PRT, Color_WHITE, "Then Click"); index3++;
while ((!get_button())) {} // wait for button release
dds.setFrequency(freqs[CALIB_F_30db]);
delay(100);
rawADC[CALIB_30db] = avg_adc(VOLTAGE_IN); // -30db adc value at 1MHz
dds.setFrequency(sweep.f); // back on freq
delay(10);
} // end SNA calibration (Click)
// Pause here to show data
tft.fillScreen(Color_BLACK); // signal a Push
doPrint(2, 1, SMALL_PRT, Color_WHITE, "New Values");
for (index3 = 0; index3 < CALIB_PWR; index3++)
{
display_count(index3 + 3, 1, 4, SMALL_PRT, 1000000000L, rawADC[index3]);
display_count(index3 + 3, 6, 4, SMALL_PRT, 1000000000L, rawADC[CALIB_PWR + index3]);
}
index3 = index3 + 3;
doPrint(index3, 1, SMALL_PRT, Color_WHITE, "Click: Skip"); index3++;
doPrint(index3, 1, SMALL_PRT, Color_WHITE, "Push: Save"); index3++;
while ((!get_button())) {} // wait for button release
if (down_h) {
tft.fillScreen(Color_BLACK);
}
if (down_p) // store the readings
{
for (index1 = 0; index1 < CALIB_SIZE; index1++)
{
ewriteInt(CALIB_SNA + 2 * index1, rawADC[index1]);
calibrations[index1] = rawADC[index1]; // use calibrations now
}
doPrint(index3, 1, SMALL_PRT, Color_WHITE, "--> Saved");
}
else
{
doPrint(index3, 1, SMALL_PRT, Color_WHITE, "!!! NOT Saved");
}
delay(2000);
go_home(false); // start over; no change to cr/sf
}
/**********************************************************/
/* Scale to db */
/* */
/* scale adc to db */
/* -as frequency increases, magnitude falls */
/* this calculation adjusts for the change */
/* -it normalizes so 0db at constant level */
/**********************************************************/
float scale(long freq1, int adc) //, int base)
{
int index;
long temp1, temp2;
double magnitude;
// find position of freq between calibration frequencies
for (index = 0; freq1 > freqs[index]; index++) {};
// problem: 70MHz overflows float (double) so is rounded, only 7 digits are retained
// lose the digit as late as possible
temp1 = freq1 - freqs[index - 1];
temp2 = freqs[index] - freqs[index - 1];
magnitude = (float)temp1 / (float)temp2; // fraction of the way
// first: fraction of the way (currently: magnitude)
// second: the change in calibrations[]
// third: stack the result onto the calibration to obtain a normalized calibration value
// fourth: subtract the adc reading and change to db
if (mode_instr == MODE_INSTR_SNA ) //
{
// use the calibrations for Yana (loss in db)
magnitude = (((magnitude * // fraction of the way
((float)calibrations[index] - (float)calibrations[index - 1])) // change in table
+ (float)calibrations[index - 1]) // start value
- (float)adc) * perdb; // adc reading and scale to db
}
else // mode_instr == MODE_INSTR_SWR
{
// use the SWR calibrations for the bridge on Yana (return loss in db)
magnitude = ((( magnitude *
((float)calibrations[CALIB_SWR + index] -
(float)calibrations[CALIB_SWR + index - 1]))
+ (float)calibrations[CALIB_SWR + index - 1])
- (float)adc) * perdb; //+orig0rlfl;
}
return (magnitude);
}
/**********************************************************/
/* Change db to vswr */
/* */
/* One unit change in adc is about .19db */
/* change in return loss. */
/* At swr 1.1 a change by .19db moves to 1.1023 */
/* At swr 8.0 a change by .19db moves to 8.75 */
/* Expect swr 8 graphs to be jagged */
/**********************************************************/
float tovswr(float rl)
{
float temp;
temp = pow(10.0, -rl / 20.0); // this is Gamma
temp = (1 + temp) / (1 - temp); // this is vswr
//Serial.println(temp); // debug
if(temp < 1.0) temp = 8; // Limit range
if(temp > 8.0) temp = 8;
return (temp);
}
/**********************************************************/
/* Change db to sceen coordinates */
/**********************************************************/
int getcoord(float dbval)
{
float tmp;
if (mode_gr == MODE_GR_BIG)
{
if (mode_instr == MODE_INSTR_SWR)
{
tmp = ((tovswr(dbval) - 1.0) * UNIT_BIG_SWR); //20.0; // Shift up 1 to SWR 1 is at the top
}
else // SNA
{
tmp = dbval * DB_BIG_SNA; // 2.0;
}
}
else // MODE_GR_SMALL
{
if (mode_instr == MODE_INSTR_SWR)
{
tmp = ((tovswr(dbval) - 1.0) * UNIT_SMALL_SWR); // 40.0; // Shift up one
}
else // SNA
{
tmp = dbval * (DB_SMALL_SNA); // 6.6667
}
}
tmp = round(tmp);
return ((int)tmp);
}
/**********************************************************/
/* Refine f1min in Scan */
/**********************************************************/
long scale_lc(long fg, long f)
// fg = scan step size, f = f1min
{
// boolean found = false; xxxy
uint16_t templc;
long currentf, fgaplc, fmn, fmn1;
int adcval, adcval0;
currentf = f-4*fg; // start 4 gap steps lower
if ((currentf < F_MIN) || ((f + 8*fg) > F_MAX))
{
return fg; // no refinement
}
else
{
fgaplc = fg/8; // subdivide gap by 8
dds.setFrequency(currentf);
adcval = avg_adc(VOLTAGE_IN);
adcval0 = adcval;
fmn = currentf;
// search for smaller minimum
// starting 4*8 fgaplc below first guess f
for (templc = 0; templc <= 64; templc++)
{
dds.setFrequency(currentf);
adcval = avg_adc(VOLTAGE_IN);
fmn1 = currentf;
if (adcval < adcval0)
{
fmn = currentf;
adcval0 = adcval;
}
else if (adcval > adcval0)
{
fmn1 = currentf - fgaplc; // highest min freq
templc = 65; // done searching
}
currentf = currentf + fgaplc;
}
fmn = (fmn + fmn1)/2L;
return fmn;
}
}
//void save_params()
//{
// tmpf = sweep.f;
// tmpic = sweep.ic;
// tmpf1 = sweep.f1;
// tmpf2 = sweep.f2;
//}
void restore_params()
{
sweep.f = tmpf;
sweep.ic = tmpic;
sweep.f1 = tmpf1;
sweep.f2 = tmpf2;
}
/**********************************************************/
/* */
/* Scan */
/* sweep the screen: used by SNA,SNL,SNC, and SWR */
/* */
/* Steps: */
/* 1. Set sweep limits and get marks */
/* 2. Set rotary parameters for sweep */
/* 3. Start outer state W loop */
/* 4. Prepare button press & knob turn */
/* 5. Put grid on screen and draw marks, center */
/* 6. Get db values for marks */
/* 7. Display battery voltage */
/* 8. Compute step size and align to grid */
/* 9. Set min and max initial values */
/* 10. Obtain values at first point on screen */
/* 11. Perform one sweep */
/* 11a. Set frequency */
/* 11b. read & scale adc */
/* 11c. Adjust min and max */
/* 11d. Plot a graph segment */
/* 11e. Set next frequency */
/* 11f. Move prior coordinates */
/* 12. Wait for graphing speed delay */
/* 13. Calculate next speed if knob turned */
/* 15. Process button press */
/* 15a. Enter tuning mode */
/* 15b. If hold go home */
/* 15c. If push and LC then process */
/* 15d. If click do nothing */
/* 16. If tuning then process */
/* 16a. Center frequency & get value */
/* 16b. If SNA get values */
/* 16c. If SWR get values */
/* 16d. If LC, refine f and get value */
/* 16e. Display scan data */
/* 16f. Set rotary parameters */
/* 16g. Do tuning loop */
/* 16gi. Process if knob turn */
/* 16gia. Move frequency and read value */
/* 16gib. Adjust value for mode */
/* 16gic. Display new values */
/* 16gii. Check button */
/* 16giia. If hold then go home */
/* 16giib. If push then turn off tuning */
/* 16giic. If click, increment cursor */
/* End of above loops */
/* */
/**********************************************************/
void scan()
{
long start, finish, lmark, rmark, tempf; // frequencies
long fgap, frem; // sweep frequency increment
long f1min, f1max; // marked frequencies in plot
int xcoord, xcoord0, ycoord, ycoord0; // coordinates for plotting
int adcval;
int cntln; // lines for display
float rangefl; // float variables
float tempfl; //
float wmin, wmax, ytest, mcurs; // min, max, and cursor in sweep
float lmarkyfl, rmarkyfl;
int toggle = 1; // delay time on sweep
boolean setting, tuning = false; // used for tuning along the curve
char lcstr[3] = "xx";
// save_params();
// 1. Set limits: if in center/radius, switch to start/finish parameters
if (mode_freq == MODE_FREQ_SF)
{
start = sweep.f1;
finish = sweep.f2;
lmark = sweep.ml;
rmark = sweep.mr;
}
else // mode_freq == MODE_FREQ_CR
{
start = (sweep.f1 - sweep.f2);
finish = (sweep.f1 + sweep.f2);
lmark = (sweep.f1 - sweep.ml);
rmark = (sweep.f1 + sweep.mr);
}
// 3. start outer state W loop
yana_state = STATE_W;
while (yana_state == STATE_W) // sweeping SNA or SWR
{
// 2. set rotary parameters for sweep
sweep.f = (start + finish) / 2L;
sweep.ic = 1000000L;
set_rotary_params(sweep.f, sweep.ic, finish, start); // adjust start and finish
// 4. Prepare for button press & knob turn
noInterrupts();
down_b = false; // no button press
turned_t = false; // no knob turn
interrupts();
// 5. Put the grid on the screen & draw marks, center
tftPrintGrid();
tftLabelGrid();
// floating value for sweep range
// used to calculate distance into sweep range
rangefl = (float)finish - (float)start;
//draw left mark
// compute the fraction into the range then add it onto the start
tempfl = (((float)lmark - (float)start) / rangefl) * X_STEPSFL;
xcoord = (int)tempfl + X_START; //
tft.drawFastVLine(xcoord, Y_START, Y_END, Color_GREEN);
//draw right mark
tempfl = (((float)rmark - (float)start) / rangefl) * X_STEPSFL;
xcoord = (int)tempfl + X_START; //
tft.drawFastVLine(xcoord, Y_START, Y_END, Color_GREEN);
// 6. Get the db values for the marks
// get db on left mark
dds.setFrequency(lmark);
delay(10);
adcval = avg_adc(VOLTAGE_IN);
lmarkyfl = scale(lmark, adcval); // lmark in db
// get db on right mark
dds.setFrequency(rmark);
delay(10);
adcval = avg_adc(VOLTAGE_IN);
rmarkyfl = scale(rmark, adcval); // rmark in db
// 7. display battery voltage
dsp_supp_volt();
// 8. Compute step size and align to grid
// compute the sweep step size:
// tempf seems necessary to force the compiler
// to do the arithmetic
tempf = (finish - start);
fgap = tempf / X_STEPS; // frequency change for stepping the frequency
frem = (tempf % X_STEPS) / 2L; // 1/2 remainder
// adjust starting point so that grid marks come out right
sweep.f = start + frem;
// 9. Set min and max initial values
// maximum and minimum attenuation
wmin = 0.0; // move it down to min signal (max value)
wmax = 1000.0; // move it up to max signal (min value)
// 10. Obtain values at prior point on screen
dds.setFrequency(sweep.f);
delay(10);
xcoord0 = X_START; // screen coords for start of first segment
adcval = avg_adc(VOLTAGE_IN);
ycoord0 = getcoord(scale(sweep.f, adcval)); // screen coordinate
// 11. Perform one sweep
// *** below is one sweep ***
for (xcoord = X_START; xcoord <= X_GRID_END; xcoord++)
{
// 11a. Set frequency
dds.setFrequency(sweep.f);
// 11b. read & scale adc
ycoord = analogRead(VOLTAGE_IN);
ytest = scale(sweep.f, ycoord); // value in db or swr
if (usb_on)
{
Serial.print(sweep.f);
Serial.print(",");
Serial.println(ytest);
}
ycoord = getcoord(ytest); // screen y-value
if (ycoord < 0) // set lower limit 0
{
ycoord = 0;
}
// 11c. Adjust min and max
if (ytest > wmin)
{
wmin = ytest;
f1min = sweep.f;
}
if (ytest < wmax)
{
wmax = ytest;
f1max = sweep.f;
}
// 11d. Plot a graph segment
// Can choose either segments or points at compile time
tft.drawLine(xcoord0, ycoord0, xcoord, ycoord, Color_WHITE); // plot line segments
// xxx tft.drawPixel(xcoord, ycoord, Color_WHITE); // just plot points
// 11e. Set next frequency
sweep.f = fgap * (xcoord - X_START) + start; // increment frequency
// 11f. Move prior coordinates
xcoord0 = xcoord; // position for next segment start
ycoord0 = ycoord;
}
usb_on = false;
// *** One sweep has finished ***
// 12. Wait graphing speed delay
if ((toggle > 0) && !(down_b)) // process graph speed change
{
for (xcoord = 0; xcoord <= SWEEPDELAY_TIME*toggle; xcoord++) // scan delay
{
if (turned_t) // in case turned, skip this wait time
{
xcoord = SWEEPDELAY_TIME*toggle + 1;
}
else
{
delay(20); // was 10
}
}
}
// 13. Calculate next speed if turned
// turn_t == 1 when knob has been turned
// toggle is the current sweep speed
if (turned_t) // increment graphing speed
{
toggle++;
if (toggle == MAX_SWEEP_WAIT+1)
{
toggle = 0;
}
noInterrupts();
turned_t = false;
interrupts();
}
// 15. Process button press
if (down_b) // stop graphing: button press
{
tuning = true;
// 15a. Check for button states push or hold
if (get_button())
{
// 15b. If hold go home
if (down_h) // hold: quit sweeping, go home
{
tuning = false; // not tuning
go_home(false); // start over: no change to cr/sf
}
// 15c. If push and LC then process standard
else if ((down_p) && ((mode_tune == TUNE_L) || (mode_tune == TUNE_C)))
{
// 15ci. Set units for standard
// save_params();
tmpf = sweep.f;
tmpic = sweep.ic;
tmpf1 = sweep.f1;
tmpf2 = sweep.f2;
if (mode_tune == TUNE_L)
{
sweep.f = c_std;
lcstr[0] = 'p';
lcstr[1] = 'F';
}
else if (mode_tune == TUNE_C)
{
sweep.f = l_std;
lcstr[0] = 'n';
lcstr[1] = 'H';
}
// 15cii. Show current values for standard
sweep.ic = CURSOR_STEP;
sweep.f1 = LC_MIN;
sweep.f2 = LC_MAX;
set_rotary_params(sweep.f,sweep.ic,sweep.f2,sweep.f1);
doPrint(16, 2, SMALL_PRT, Color_WHITE,"New Standard Value");
doPrint(17, 14, SMALL_PRT, Color_WHITE, lcstr);
setting = true;
while (setting) // tuning an LC value
{
display_count(17, 1, 8, SMALL_PRT, sweep.ic, sweep.f);
// 15ciia. Set new value from knob turn
if (turned_t) // set standard to tuned value
{
noInterrupts();
sweep.f = rotary_count;
turned_t = false;
interrupts();
if (mode_tune == TUNE_L)
{
c_std = sweep.f; // use C to measure L
}
else if (mode_tune == TUNE_C)
{
l_std = sweep.f; // use L to measure C
}
} // end turning knob
// 15ciib. Check for button
if (get_button())
{
if (down_h) // hold: go home
{
setting = false;
tuning = false;
restore_params();
go_home(false); // no change to sf/cr
}
else if (down_p) // push: go back to graphing
{
tuning = false;
setting = false;
restore_params();
}
else // click: move cursor
{
// 15ciic. Increment the cursor
incr_crsr();
noInterrupts();
rotary_increment = sweep.ic;
interrupts();
display_count(17, 1, 8, SMALL_PRT, sweep.ic, sweep.f);
}
} // end button in LC setting
} // end setting LC standard value
} // end processing L, C standard
else if (down_p) // push but not SNL or SNC
{
usb_on = true; // one trace out
tuning = false; // back to graphing
setting = false;
}
//else // it's a click: do graph tuning mode
} // end if get_button
// 16. Graph tuning mode
if (tuning)
{
// 16a. Center frequency & get value
sweep.f = (finish + start)/2L; //
dds.setFrequency(sweep.f); // get the value at center
delay(10);
ycoord0 = avg_adc(VOLTAGE_IN);
mcurs = scale(sweep.f, ycoord0);
// 16b. If SNA get values
if (mode_instr == MODE_INSTR_SNA) // set values for sna
{
lmarkyfl = -lmarkyfl * 10.0; // left value
rmarkyfl = -rmarkyfl * 10.0; // right value
mcurs = -mcurs * 10.0; // value at midscreen
wmin = -wmin * 10.0; // min value
wmax = -wmax * 10.0; // max value
}
else // mode_instr == MODE_INSTR_SWR // set values for swr
// 16c. If SWR get values
{
lmarkyfl = tovswr(lmarkyfl) * 10.0; // left swr
rmarkyfl = tovswr(rmarkyfl) * 10.0; // right swr
mcurs = tovswr(mcurs) * 10.0; // swr at midscreen
wmin = tovswr(wmin) * 10.0; // swr at min
wmax = tovswr(wmax) * 10.0; // swr at max
}
// 16d. If LC, refine f and get value
// If LC, refine resonant frequency
if ((mode_tune == TUNE_L) || (mode_tune == TUNE_C))
{
doPrint(16, 1, SMALL_PRT, Color_WHITE, "** Wait **");
f1min = scale_lc(fgap,f1min); // refining freq takes time
dds.setFrequency(f1min);
ycoord = avg_adc(VOLTAGE_IN);
wmin = -10.0*scale(f1min, ycoord); // value in db
}
// 16e. Display scan data
// Show scan Data
cntln = 16;
display_count(cntln, 1, 8, SMALL_PRT, sweep.ic, sweep.f);
display_db(cntln, 12, SMALL_PRT, (int)mcurs);
doPrint(cntln, 18, SMALL_PRT, Color_WHITE, "Tune");
cntln++;
display_count(cntln, 1, 8, SMALL_PRT, 1000000000L, f1min);
display_db(cntln, 12, SMALL_PRT, (int)wmin);
doPrint(cntln, 18, SMALL_PRT, Color_WHITE, "Min");
cntln++;
// show fmax, db max, max name
if ((mode_tune == TUNE_SNA) || (mode_tune == TUNE_SWR) )
{
display_count(cntln, 1, 8, SMALL_PRT, 1000000000L, f1max);
display_db(cntln, 12, SMALL_PRT, (int)wmax);
doPrint(cntln, 18, SMALL_PRT, Color_WHITE, "Max");
}
else if (mode_tune == TUNE_L) // show fmin, L value, nH
{
wmax = round(pow(10,21)/((4*PI*PI*(double)f1min*(double)f1min)*c_std));
display_count(cntln, 1, 8, SMALL_PRT, 1000000000L, wmax);
doPrint(cntln, 12, SMALL_PRT, Color_WHITE, " nH ");
}
else if (mode_tune == TUNE_C) // show fmin, C value, pF
{
wmax = round(pow(10,21)/((4*PI*PI*(double)f1min*(double)f1min)*l_std));
display_count(cntln, 1, 8, SMALL_PRT, 1000000000L, wmax);
doPrint(cntln, 12, SMALL_PRT, Color_WHITE, " pF ");
}
// lmark value, rmark value
cntln++;
display_count(cntln, 1, 8, SMALL_PRT, 1000000000L, lmark);
display_db(cntln, 12, SMALL_PRT, (int)lmarkyfl);
doPrint(cntln, 18, SMALL_PRT, Color_WHITE, "L");
cntln++;
display_count(cntln, 1, 8, SMALL_PRT, 1000000000L, rmark);
display_db(cntln, 12, SMALL_PRT, (int)rmarkyfl);
doPrint(cntln, 18, SMALL_PRT, Color_WHITE, "R ");
// 16f. Set rotary parameters
set_rotary_params(sweep.f, sweep.ic, finish, start); // sets turned_t = false
// 16g. Do tuning loop
cntln = 16;
while (tuning)
{
// 16gi. Process if knob turn
// while waiting check for rotary turn
if (turned_t) // tuning on curve
{
// 16gia. Move frequency and read value
// Tune along the trace
// steps are about, but less than one pixel on trace
noInterrupts();
sweep.f = rotary_count;
turned_t = false;
interrupts();
dds.setFrequency(sweep.f);
delay(10);
ycoord0 = (float)avg_adc(VOLTAGE_IN); // do a multiple read
mcurs = scale(sweep.f, ycoord0);
// 16gib. Adjust value for mode
if (mode_instr == MODE_INSTR_SNA)
{
mcurs = -mcurs * 10.0;
}
else // mode_instr==MODE_INSTR_SWR
{
mcurs = tovswr(mcurs) * 10.0;
}
// 16gic. Display new values
display_count(cntln, 1, 8, SMALL_PRT, sweep.ic, sweep.f);
display_db(cntln, 12, SMALL_PRT, (int)mcurs);
doPrint(cntln, 18, SMALL_PRT, Color_WHITE, "Tune");
} // end turned_t
// 16gii. Check button
// process button in tuning mode
if (get_button()) // process the button
{
if (down_h) // hold: quit sweeping
{
// 16giia. If hold then go home
tuning = false;
go_home(false); // start over: no change to cr/sf
}
else if (down_p) // push: back to sweeping
{
// 16giib. If push then turn off tuning
tuning = false; // turn off tuning (back to sweeping)
}
else // click
{
// 16giic. If click, increment cursor
incr_crsr();
noInterrupts();
rotary_increment = sweep.ic;
interrupts();
display_count(cntln, 1, 8, SMALL_PRT, sweep.ic, sweep.f);
}
} // end process the button in tuning mode
} // end of tuning loop
} // end if tuning
} // end of if down_b
} // end STATE_W sweeping
}
/**********************************************************/
/* do_pwr() */
/* */
/* present the power screen */
/**********************************************************/
void do_pwr()
{
long counter; //
int adcval, tmp, cnt, raw;
float pwrdbm, pwrval, pwrvlt;
// if in center/radius, switch to start/finish parameters
if (mode_freq == MODE_FREQ_SF)
{
counter = (sweep.f1 + sweep.f2) / 2;
set_rotary_params(counter, sweep.ic, sweep.f2, sweep.f1);
}
else // mode_freq == MODE_FREQ_CR
{
counter = sweep.f1;
set_rotary_params(counter, sweep.ic, sweep.f1 + sweep.f2, sweep.f1 - sweep.f2);
}
tft.fillScreen(Color_BLACK);
display_count(LINE_9B, 1, 8, BIG_PRT, sweep.ic, counter);
yana_state = STATE_W;
while (yana_state == STATE_W) // reading PWR
{
noInterrupts();
down_b = false; // no button press
interrupts();
dsp_supp_volt(); // show supply voltage
raw = avg_adc(VOLTAGE_IN);
pwrdbm = (perdb * (float)raw) - orig0dbmfl; //dbm value
pwrval = pow(10.0, pwrdbm / 10.0); // the power
pwrvlt = pow(10.0, pwrdbm / 20.0 - DBM_VOLT); // the power
// display the dbm
dtostrf(pwrdbm, 4, 1, outstr);
doPrint(LINE_3B, 2, BIG_PRT, Color_YELLOW, outstr);
doPrint(LINE_3B, 8, BIG_PRT, Color_WHITE, "dbm");
// display power in watts
tmp = 0;
pwrval = pwrval / 1000.0;
if (pwrval > 0.0)
{
while ((int)pwrval == 0)
{
pwrval = pwrval * 1000.0;
tmp++;
}
}
// display pwrval
float_fix(pwrval, 7, 3);
doPrint(LINE_5B, 3, BIG_PRT, Color_YELLOW, outstr);
doPrint(LINE_5B, 9, BIG_PRT, Color_WHITE, unitsw[tmp]);
// display voltage
tmp = 0;
if (pwrvlt > 0.0)
{
while ((int)pwrvlt == 0)
{
pwrvlt = pwrvlt * 1000.0;
tmp++;
}
}
// display pwrvlt
float_fix(pwrvlt, 7, 3);
doPrint(LINE_6B, 3, BIG_PRT, Color_YELLOW, outstr);
doPrint(LINE_6B, 9, BIG_PRT, Color_WHITE, unitsv[tmp]);
tftLabelGrid();
// display raw ADC reading
display_count(LINE_7B, 3, 3, BIG_PRT, 1000000000L, raw);
// now delay .1 sec while polling the button
for (tmp = 0; tmp < 20; tmp++)
{
if ((turned_t) || (down_b))
{
tmp = 21;
}
else
{
delay(10);
}
} // end delay
if (turned_t) // check for freq change
{
// Set DDS & display frequency on the bottom
noInterrupts();
counter = rotary_count;
turned_t = false;
interrupts();
dds.setFrequency(counter);
delay(10);
// display frequency
display_count(LINE_9B, 1, 8, BIG_PRT, sweep.ic, counter);
}
if (get_button())
{
noInterrupts();
down_b = true; // button press
interrupts();
}
if (down_b)
{
if (down_h) // quit: power
{
go_home(false); // start over; no change to cr/sf
}
if (down_p) // quit: power
{
go_home(false); // start over; no chnage to cr/sf
}
else // click: increment cursor
{
incr_crsr();
noInterrupts();
rotary_increment = sweep.ic;
turned_t = false;
interrupts();
// display frequency
display_count(LINE_9B, 1, 8, BIG_PRT, sweep.ic, counter);
}
} // end down_b
} // end command STATE_W
}
/**********************************************************/
/* setup */
/**********************************************************/
void setup()
{
int tmp;
Serial.begin(115200); // set serial port
// start the display
TFT_START;
// fix screen rotation
tft.setRotation(SCREEN_UPRIGHT);
// start up the DDS...
dds.init();
// trim your xtal with TUNE_WORD...
dds.trim(TUNE_WORD); //set TUNE_WORD for 6x actual osc freq
// Set the encoder button pin
pinMode(ENCODER_BTN, INPUT); // using Analog pin
digitalWrite(ENCODER_BTN, HIGH); // enable pullup
// Set the ADC voltage reference
analogReference(DEFAULT); // INTERNAL = 1.1 V AREF, DEFAULT = 5 V AREF
// Enable pin change interrupt for the button
PCICR |= 0x02;
PCMSK1 |= 0b00000001;
// Enable pin change interrupt for the rotary encoder
PCICR |= (1 << PCIE2);
PCMSK2 |= (1 << PCINT18) | (1 << PCINT19);
sei(); // turn on interrupts
// get the calibration data
for (tmp = 0; tmp < CALIB_SIZE; tmp++)
{
calibrations[tmp] = ereadInt(CALIB_SNA + 2 * tmp);
}
// calculate the perdb adc coordinates (adcval/db)
// value from 4/6/16 calibration: .189 adc step/db
tmp = calibrations[CALIB_F_30db] - calibrations[CALIB_30db]; // range for 30db at 1MHz
perdb = CALIB_ATTEN30db / ((float)tmp); // adc units per db
// calculate the 0dbm origin (adcval/db) with 30db from Out to Det
// subtract this value from converted adc readings to get actual dbm
// value from 4/6/16 calibration: .9948 is org0dbmfl
tmp = calibrations[CALIB_PWR]; // -10dbm adc value
orig0dbmfl = 10.0 + (perdb * (float)tmp); // adc relative value at +0dbm
// Intro Screen
tft.fillScreen(Color_BLACK); // background black
doPrint(3, 3, BIG_PRT, Color_YELLOW, "*Yana");
doPrint(5, 3, BIG_PRT, Color_BLUE, "K1TRB");
doPrint(7, 3, BIG_PRT, Color_BLUE, ">2017");
delay(700);
tft.fillScreen(Color_BLACK);
// Set up for yana_state STATE_OP: operations
go_home(false); // start over; no change to cr/sf
}
/**********************************************************/
/* loop, main program */
/**********************************************************/
void loop()
{
/*******************************************************/
/* yana_state STATE_INSTR: Set the Analyzer Instrument */
/*******************************************************/
while (yana_state == STATE_INSTR)
{
// Check for knob rotation
if (turned_t)
{
noInterrupts();
mode_instr_1 = rotary_count;
turned_t = false;
interrupts();
switch(mode_instr_1)
{
case MODE_INSTR_SNA:
mode_tune = TUNE_SNA;
mode_instr = MODE_INSTR_SNA;
break;
case MODE_INSTR_SWR:
mode_tune = TUNE_SWR;
mode_instr = MODE_INSTR_SWR;
break;
case MODE_INSTR_PWR:
mode_tune = TUNE_PWR;
mode_instr = MODE_INSTR_PWR;
break;
case MODE_INSTR_SNL:
mode_tune = TUNE_L;
mode_instr = MODE_INSTR_SNA;
break;
case MODE_INSTR_SNC:
mode_tune = TUNE_C;
mode_instr = MODE_INSTR_SNA;
break;
}
tftPrintCMenu();
CurrentField(LINE_2B, LNPOS_INSTR, BIG_PRT, modes_instr[mode_instr][0]);
}
if (get_button())
{
/* Process hold: */
if (down_h)
{
go_home(false); // start over; no change to cr/sf
}
/* Process push: */
else if (down_p) // push:
{
set_for_f1();
}
/* Process a click: increment f increment */
else // click, increment changed
{
// set for frequency modes STATE_F_MODE
yana_state = STATE_F_MODE;
mode_freq_now = mode_freq; // to know this mode when leaving STATE_F_MODE
set_rotary_params(mode_freq, 1, SC_MAX, 0);
tftPrintCMenu();
CurrentField(LINE_2B, LNPOS_FREQ, BIG_PRT, modes_freq[mode_freq][0]);
}
}
} // end of STATE_INSTR
/*****************************************************************/
/* yana_state STATE_F_MODE: Choose Center/Radius or Start/Finish */
/*****************************************************************/
while (yana_state == STATE_F_MODE)
{
// Check for knob rotation
if (turned_t)
{
noInterrupts();
mode_freq = rotary_count;
turned_t = false;
interrupts();
tftPrintCMenu();
CurrentField(LINE_2B, LNPOS_FREQ, BIG_PRT, modes_freq[mode_freq][0]);
}
if (get_button())
{
/* Process hold: stay in yana_state STATE_F1: f start */
if (down_h)
{
go_home(mode_freq_now != mode_freq); // start over; no change to cr/sf
}
/* Process push: go to yana_state STATE_F1 freq Center or Start */
else if (down_p) // push: go to next yana_state
{
fix_freqs((mode_freq_now != mode_freq)); // switching sf/cr?
set_for_f1();
}
/* Process a click: increment f increment */
else // click, increment changed
{
// set for instrument STATE_G_MODE
if (mode_freq_now != mode_freq)
{
tftPrintFMenu();
}
fix_freqs(mode_freq_now != mode_freq); // switching between S/F, C/R
tftPrintFFreqs();
yana_state = STATE_G_MODE;
set_rotary_params(mode_gr, 1, GR_MAX, 0);
tftPrintCMenu();
CurrentField(LINE_2B, LNPOS_GR, BIG_PRT, modes_gr[mode_instr][mode_gr][0]);
} // end click
} // end get_button
} // end of STATE_F_MODE
/***********************************************************/
/* yana_state STATE_G_MODE: Set the Analyzer Graph Mode */
/***********************************************************/
while (yana_state == STATE_G_MODE)
{
// Check for knob rotation
if (turned_t)
{
noInterrupts();
mode_gr = rotary_count;
turned_t = false;
interrupts();
tftPrintCMenu();
CurrentField(LINE_2B, LNPOS_GR, BIG_PRT, modes_gr[mode_instr][mode_gr][0]);
}
if (get_button())
{
/* Process hold: goto yana_state STATE_OP */
if (down_h)
{
go_home(false); // start over; no change to cr/sf
}
/* Process push: go to yana_state STATE_F1 freq Center or Start */
else if (down_p) // : go to next yana_state
{
// goto yana_state STATE_F1
set_for_f1();
}
/* Process a click: increment f increment */
else // click, increment changed
{
// set for instrument STATE_OP
go_home(false); // start over; no change to cr/sf
}
}
} // end of STATE_G_MODE
/*******************************************************/
/* yana_state STATE_OP: Set the Analyzer Operation */
/*******************************************************/
while (yana_state == STATE_OP)
{
// Check for knob rotation
if (turned_t)
{
noInterrupts();
mode_op = rotary_count;
turned_t = false;
interrupts();
tftPrintCMenu();
CurrentField(LINE_2B, LNPOS_OP, BIG_PRT, modes_op[mode_op][0]);
}
if (get_button())
{
/* Process hold: execute mode_instr chosen instrument */
if (down_h) // it's a hold:
{
switch(mode_op) // do the operation
{
case MODE_OP_RUN :
if (mode_instr == MODE_INSTR_SNA)
{
scan() ; // scan in SNA instrument
}
else if (mode_instr == MODE_INSTR_SWR)
{
scan(); // scan in SWR instrument
}
else //mode_instr == MODE_INSTR_PWR
{
do_pwr();
}
break;
case MODE_OP_CAL :
calibrate();
break;
}
}
/* Process push: go to yana_state STATE_F1 freq Center or Start */
else if (down_p) // push: go to next yana_state
{
// Go to the STATE_F1 frequency setting
set_for_f1();
}
/* Process a click: increment f increment */
else // click, increment changed
{
// set for instrument STATE_INSTR
yana_state = STATE_INSTR;
set_rotary_params(mode_instr, 1, INSTR_MAX, 0);
tftPrintCMenu();
CurrentField(LINE_2B, LNPOS_INSTR, BIG_PRT, modes_instr[mode_instr][0]);
}
}
} // end of STATE_OP
/*******************************************************/
/* yana_state STATE_F1: Set the Analyzer Frequency */
/*******************************************************/
while (yana_state == STATE_F1) //yana_state is center/start freq
{
/* Check for knob rotation */
if (turned_t) // freq was changed
{
noInterrupts();
sweep.f1 = rotary_count;
turned_t = false;
interrupts();
display_count(LINE_4B, 1, 8, BIG_PRT, sweep.ic, sweep.f1);
dds.setFrequency(sweep.f1); // set the frequency of dds
}
/* Check for button down */
if (get_button()) // check for button down
{
/* Process hold: goto yana_state STATE_OP */
if (down_h) // it's a hold:
{
go_home(false); // start over; no change to cr/sf
}
/* Process push: go to yana_state STATE_F2 freq radius/finish */
else if (down_p) // push: go to next yana_state
{
// Go to the yana_state STATE_F2 screen (Radius or Finish)
fix_freqs(false); // no change to SF/CR
// set limits for sweep.f2
if (mode_freq == MODE_FREQ_CR)
{
minfr = 0; // lower limit of radius
if ((F_MIN + F_MAX)/2L > sweep.f1)
// F_Min is closer
{
maxfr = sweep.f1 - F_MIN;
}
else // F_MAX closer
{
maxfr = F_MAX - sweep.f1;
}
}
else // mode_freq==MODE_FREQ_SF
{
maxfr = F_MAX;
minfr = sweep.f1;
}
set_rotary_params(sweep.f2, sweep.ic, maxfr, minfr);
tftPrintFMenu();
tftPrintCMenu();
NextFMenuLine(STATE_F2, LINE_5B); // switch to STATE_F2
}
/* Process a click: increment f increment */
else // click, increment changed
{
incr_crsr();
noInterrupts();
rotary_increment = sweep.ic;
turned_t = false;
interrupts();
display_count(LINE_4B, 1, 8, BIG_PRT, sweep.ic, sweep.f1);
// display_count(LINE_6B,1,8,BIG_PRT,sweep.ic,sweep.f2);
}
}
} // end of STATE_F1
/***********************************************/
/* yana_state STATE_F2: Set the analyzer span */
/***********************************************/
while (yana_state == STATE_F2) //yana_state is r freq
{
/* Check for knob rotation */
if (turned_t) // freq was changed
{
noInterrupts();
sweep.f2 = rotary_count; // retrieve new r freq
turned_t = false;
interrupts();
display_count(LINE_6B, 1, 8, BIG_PRT, sweep.ic, sweep.f2);
// dds doesn't move on change to finish f
}
/* Check for button down */
if (get_button())
{
/* Process a hold: goto yana_state STATE_OP */
if (down_h) // hold:
{
go_home(false); // start over; no change to cr/sf
}
// Process a push: go to yana_state STATE_L: left marker
else if (down_p) // push: go to next yana_state
{
fix_freqs(false); // No change to SF/CR
// set rotary parameters
// set limits for sweep.ml
if (mode_freq == MODE_FREQ_CR)
{
set_rotary_params(sweep.ml, sweep.ic, sweep.f2, 0);
}
else // mode_freq==MODE_FREQ_SF
{
set_rotary_params(sweep.ml, sweep.ic, sweep.f2, sweep.f1);
}
tftPrintFMenu();
tftPrintCMenu();
NextFMenuLine(STATE_L, LINE_7B);
}
/* Process a click: increment radius/finish increment */
else // click, increment changed
{
incr_crsr();
noInterrupts();
rotary_increment = sweep.ic;
turned_t = false;
interrupts();
display_count(LINE_6B, 1, 8, BIG_PRT, sweep.ic, sweep.f2);
}
}
} // end of STATE_F2
/***********************************************/
/* yana_state STATE_L: Set Left Marker */
/***********************************************/
while (yana_state == STATE_L) //yana_state is left
{
// Check for knob rotation
if (turned_t) // rotating changes repeat
{
noInterrupts();
sweep.ml = rotary_count; // retrieve new L freq
turned_t = false;
interrupts();
display_count(LINE_8B, 1, 8, BIG_PRT, sweep.ic, sweep.ml);
// dds doesn't move on change to L
}
// Check for button down
if (get_button())
{
/* Process a hold: goto yana_state STATE_OP */
if (down_h) // hold:
{
go_home(false); // start over; no change to cr/sf
}
// Process a push: go to yana_state R: right marker
else if (down_p)
{
fix_freqs(false); // No change to SF/CR
// set rotary parameters of sweep.mr
if (mode_freq == MODE_FREQ_CR)
{
set_rotary_params(sweep.mr, sweep.ic, sweep.f2, 0);
}
else // mode_freq==MODE_FREQ_SF
{
set_rotary_params(sweep.mr, sweep.ic, sweep.f2, sweep.ml);
}
tftPrintFMenu();
tftPrintCMenu();
NextFMenuLine(STATE_R, LINE_9B);
}
/* Process a click: increment radius/finish increment */
else // click, increment changed
{
incr_crsr();
noInterrupts();
rotary_increment = sweep.ic;
turned_t = false;
interrupts();
display_count(LINE_8B, 1, 8, BIG_PRT, sweep.ic, sweep.ml);
}
}
} // end of STATE_L
/***********************************************/
/* yana_state STATE_R: Set Right Marker */
/***********************************************/
while (yana_state == STATE_R) //yana_state is left
{
// Check for knob rotation
if (turned_t) // rotating changes repeat
{
noInterrupts();
sweep.mr = rotary_count; // retrieve new M freq
turned_t = false;
interrupts();
display_count(LINE_10B, 1, 8, BIG_PRT, sweep.ic, sweep.mr);
// dds doesn't move on change to M
}
// Check for button down
if (get_button())
{
/* Process a hold: Go Home */
if (down_h) // hold
{
go_home(false); // start over; no change to cr/sf
}
// Process a push: go to yana_state STATE_F1
else if (down_p)
{
fix_freqs(false); // No change to SF/CR
set_for_f1();
}
/* Process a click: increment radius/finish increment */
else // click, increment changed
{
incr_crsr();
noInterrupts();
rotary_increment = sweep.ic;
turned_t = false;
interrupts();
display_count(LINE_10B, 1, 8, BIG_PRT, sweep.ic, sweep.mr);
}
}
} // end of STATE_R
} // end loop