A 199 MHz DIY Spectrum Analyser with an LMK61E2 & SI4432
UNIT
RANGES • REMARKS
Frequency
1 - 200 MHz
Input
BNC, 50 Ω
Amplitude Resolution
0.5 dB
Dynamic Range
-121 dBm to +10 dBm
Resolution Bandwidth
2.6 kHz to 620 kHz
Power Consumption
7.5 V, < 500 mA
Reference Output
100.0 MHz, -28.7 dBm
✈ Motivation
In our labs, a lot of exclusive testgear (e.g. spectrum analysers) is just used to see if a carrier is there or not.
Those devices very likely may endure that emotionally, but freeing them for a more sophisticated task was the self given challenge.
We therefore created a project, based on the Arduino/Genuino Due, a shield "VISIONARY" as well as
a 5.0" 40-pin TFT Display - 800x480 pixels with Touchscreen (from Adafruit). Thats all it needs for a cute Spectrum Analyser.
Anyway, we also added a RF Reference Source to
allow for self - testing capability.
✈ Block Diagram and Circuit Description
Starting at the left, the signal coming from the "antenna" passes a 190 MHz lowpassfilter (SCLF-190 from Mini Circuits). This filter
has it's 3dB corner frequency at 200 MHz. It passes then a switch, which may attenuate the signal by 30 dB.
Following is a mixer, we used the ADE-42MH+ from Mini Circuits. It
needs +13 dBm power at the LO-port. For isolation and broadband matching, we placed an attenuator after the mixer
as well as an amplifier at the LO-input. The local oscillator is made with an LMK61E2 from Texas Instruments.
When used with a transformer (MABAES0061 from Macom)
at the output (LVPECL and not recommended by TI) the output power is about +7 dBm. This is then amplified by a
GVA-81+ (10.5 dB, 19.1 dBm from Mini Circuits).
Selection is done by two SAW Bandpass Filters from sawcomponents (433.92 MHz).
The further Intermediate Frequency processing is done by a SI4432 ISM Receiver. It allows for various resolution bandwidths
from 2.6 kHz up to 620 kHz as well as a logarithmic detector with a resolution of 0.5 dB and approx. 120 dB dynamic range.
View inside
✈ The Local Oscillator : LMK61E2
As local oscillator a LMK61E2 from Texas Instruments is used.
The "Ultra-Low Noise" is achieved with mainly two tricks. First the use of a low noise vco and second, the use of frequency dividers.
As the minimum divider value is 5, this will lower the phase noise by at least P = 20 * log10 (5) = 14 dB.
This is in the hp8640 class - just 60 dB cheaper. To get access to the maximum frequency range, we used the LVPECL configuration and connected
the outputs via a transformer. This is not recommended by TI.
Last but not least it comes in a homebrewer-friendly 8-pin QFM (7x5mm) case.
Building blocks of the LMK61E2. Drawing courtesy of Texas Instruments.
✈ RBW - The Resolution Bandwidth
The Resolution Bandwidth is defined as the span of the final filter that is applied to the input signal.
Smaller RBW's provide finer frequency resolution and the ability to differentiate signals which are close together.
The Si4432 offers various values from 2.6 kHz up to 620 kHz. A lower value offers a lower noise-floor.
For an eye-friendly display, we have chosen RBW = SPAN / DOTS. This ensures, that every dot covers the equivalent frequency range.
This setting is updated automatically, but you can change the RBW after the SPAN has been changed.
A lower value offers better resolution and lower noise-floor, but the drawback is an increased sweep-time.
✈ Sweep Time
The Sweep Time is the time needed for one measurement from the START to the STOP frequency.
This time depends on the SPAN as well as on the RBW and the filter characeristic. It can be calculated as follows :
It is obvious, that a larger span needs more time to be swept. If the RBW is smaller, the filter needs more time
to settle - the rise time of a filter is inversely proportional to its
bandwidth. The k-factor is a dimensionless proportionality constant of the filter used.
For an analog filter, a typical value is k = 3. For digital filters (as used in Keysight X-Series signal analysers) k = 2.
When using an FSP-3 from Rohde & Schwarz you will notice, that most settings yield a k = 1.
For the calculation of the Sweeptime, we generously assume k = 3.
✈ The IF Filter Section
The complete IF section is handled by a 433 MHz ISM Receiver. We used the SI4432 which offers a programmeable resolution bandwidth
of 2.6 kHz up to 620 kHz, as well as a dynamic range of approx. 120 dB. A lot of boards with that IC are available out there.
As this circuit is very easygoing, we designed it into the board.
Building blocks of the SI4432. Drawing courtesy of SILICON LABS.
We first tried 3 serial SAW bandpass filters as described in
KD2BOA's Blog, and an AD8307,
but the results where not that overwhelming. This method works only for two saw filters. And they should be 50 Ω. There
was no matching 3 dB pad in between. Just some nH trace inductance.
✈ Test Sketch for Arduino/Genuino DUE
Double click on code to select ...
// /////////////////////////////////////////////////////////////////////
//
// ARDUINO/Genuino (DUE) Sketch for "VISIONARY"
// https://www.changpuak.ch/electronics/Arduino-Project-VISIONARY.php
// VISIONARY 9.9.1 SKETCH BY CHANGPUAK.CH
// WRITTEN 05.12.2561 BY ALEXANDER SSE FRANK
// SI4432, FINAL RELEASE
//
// /////////////////////////////////////////////////////////////////////
#include <Wire.h>
#include "Adafruit_GFX.h"
#include "Adafruit_RA8875.h"
#include <SPI.h>
#include "stdlib.h"
#define RA8875_INT 4
#define RA8875_CS 10
#define RA8875_RESET 9
#define RA8875_MENUE_COLOR 0xFFFF
// COLOUR = RED << 11 | GREEN << 6 | BLUE
#define RA8875_NH 0x0 | 9 << 11 | 9 << 6 | 9 // DARK GRAY
#define RA8875_SEL 0xFFFF // WHITE
#define RA8875_UNSEL 0x0 | 0xB << 11 | 0xB << 6 | 0xB // LIGHT GRAY
Adafruit_RA8875 tft = Adafruit_RA8875(RA8875_CS, RA8875_RESET);
uint16_t tx, ty;
float xScale = 1024.0F/tft.width();
float yScale = 1024.0F/tft.height();
boolean touchEnable = true;
boolean touched = false ;
const int LMK61E2ADR = 0x59 ;
// const float REF = 50.00000 ;
const float REF = 100.00000 ; // Doubler Enabled :-)
const float MAXAnfang = 0.009 ;
const float MAXEnde = 200.0 ;
const float MAXspan = 200.0 ;
float ZF = 433.92 ;
float center = 100.0 ;
float span = 20.0 ;
float Start = 90.0 ;
float Stop = 110.0 ;
float bandwidth = 34.6 ;
float curr_freq = 0.0 ;
// float CAL_GAIN = 1.0 ;
// float CAL_OFF[201] ; // EVERY MHz ?
int sweep_delay = 99 ;
// GRID
const int dX = 58 ;
const int dY = 45 ;
const int oX = 40 ;
const int oY = 9 ;
const int DOTS = 10 * dX - 2 ; // FOR ADAFRUIT RA8875
float increment = span / DOTS ;
unsigned int i = 0; // i RUNS FROM 0 ... DOTS
unsigned int TouchedMe ;
const int KeyW = 91 ;
const int KeyH = 61 ;
const int KeyD = 20 ;
const int radius = 9 ;
unsigned int LastXPressed = 0;
unsigned int LastYPressed = 0;
// ATTENUATOR
const int AttenuatorPin = A2 ;
boolean AttSet = 0 ; // 0 = OFF = 0dB, 1 = ON = AttLoss dB
const float ThruLoss = 10.0 ; // dB, Mixer, 2xSAW Filter, Attenuator (0dB)
const float AttLoss = 30.0 ;
const float MaxIFLevel = - 30.0 ; // dBm
float TopLevel = MaxIFLevel + AttLoss + ThruLoss ;
// MAX.HOLD
boolean MaxHoldOnOff = 0 ; // 0 = OFF, 1 = ON
// AVERAGING
boolean AveragingOnOff = 0 ; // 0 = OFF, 1 = ON
unsigned int AveragingFactor = 2 ;
float Sample[DOTS+2];
float OldSample[DOTS+2];
// REGISTERS LMK61E2
byte RegLO[73] ;
// PINS SI4432
const int SI_nSEL = A3 ;
const int SI_SCLK = A4 ;
const int SI_SDI = A5 ;
const int SI_SDO = A6 ;
const int SI_GPIO = A7 ;
byte SI4432REG[129] ;
void setup()
{
delay(4999);
Serial.begin(115200);
Wire.begin();
// Initialise the display
if (!tft.begin(RA8875_800x480)) {
Serial.println("RA8875 Not Found!");
while (1);
}
pinMode(RA8875_INT, INPUT);
digitalWrite(RA8875_INT, HIGH);
tft.displayOn(true);
tft.GPIOX(true); // Enable TFT
tft.PWM1config(true, RA8875_PWM_CLK_DIV1024); // Backlight
tft.PWM1out(255);
tft.fillScreen(RA8875_BLACK);
Serial.println("Found RA8875.");
Serial.println("Initialisation complete.");
Serial.println("------------------------");
tft.touchEnable(true);
// REGISTERS LMK61E2 FOR 200 MHz
RegLO[0x10] = 0x00 ; // XO_CAPCTRL_BY1
RegLO[0x11] = 0x80 ; // XO_CAPCTRL_BY0
// NO HAVE
RegLO[0x15] = 0x01 ; // DIFFCTL
RegLO[0x16] = 0x00 ; // OUTDIV_BY1
RegLO[0x17] = 0x17 ; // OUTDIV_BY0
// NO HAVE
RegLO[0x19] = 0x00 ; // PLL_NDIV_BY1
RegLO[0x1A] = 0x2E ; // PLL_NDIV_BY0
RegLO[0x1B] = 0x00 ; // PLL_FRACNUM_BY2
RegLO[0x1C] = 0x00 ; // PLL_FRACNUM_BY1
RegLO[0x1D] = 0x00 ; // PLL_FRACNUM_BY0
RegLO[0x1E] = 0x00 ; // PLL_FRACDEN_BY2
RegLO[0x1F] = 0x00 ; // PLL_FRACDEN_BY1
RegLO[0x20] = 0x01 ; // PLL_FRACDEN_BY0
RegLO[0x21] = 0x0F ; // PLL_MASHCTRL
RegLO[0x22] = 0x28 ; // PLL_CTRL0
RegLO[0x23] = 0x03 ; // PLL_CTRL1
RegLO[0x24] = 0x04 ; // PLL_LF_R2
RegLO[0x25] = 0x00 ; // PLL_LF_C1
RegLO[0x26] = 0x00 ; // PLL_LF_R3
RegLO[0x27] = 0x00 ; // PLL_LF_C3
// NO HAVE
RegLO[0x2A] = 0x00 ; // PLL_CALCTRL
// NO HAVE
RegLO[0x2F] = 0x00 ; // NVMSRC
RegLO[0x30] = 0x00 ; // NVMCNT
RegLO[0x31] = 0x10 ; // NVMCTL
RegLO[0x32] = 0x00 ; // NVMLCRC
RegLO[0x33] = 0x00 ; // MEMADR
RegLO[0x34] = 0x00 ; // NVMDAT
RegLO[0x35] = 0x00 ; // RAMDAT
// NO HAVE
RegLO[0x38] = 0x00 ; // NVMUNLK
// NO HAVE
RegLO[0x42] = 0x00 ; // INT_LIVE
// NO HAVE
RegLO[0x48] = 0x02 ; // SWRST
// ATTENUATOR, SET TO AttLoss dB
pinMode(AttenuatorPin, OUTPUT);
digitalWrite(AttenuatorPin, AttSet);
Serial.println("Found LMK61E2.");
// STARTUP SCREEN
IDN();
Serial.println("Initialisation complete.");
Serial.println("------------------------");
// SOME INFORMATION ...
tft.textMode();
tft.textEnlarge(2);
tft.textTransparent(RA8875_WHITE);
tft.textSetCursor(10, 10);
tft.textWrite("ARDUINO / GENUINO (DUE)");
tft.textSetCursor(10, 60);
tft.textWrite("PROJECT 'VISIONARY'");
tft.textEnlarge(1);
tft.textSetCursor(10, 130);
tft.textWrite("LOCAL OSCILLATOR IS A LMK61E2.");
tft.textSetCursor(10, 160);
tft.textWrite("IF1: 433.92 MHz, IF PROCESSING: SI4432");
tft.textSetCursor(10, 210);
tft.textWrite("MINIMUM FREQUENCY : 9 kHz");
tft.textSetCursor(10, 240);
tft.textWrite("MAXIMUM FREQUENCY : 199.9 MHz");
tft.textSetCursor(10, 290);
tft.textWrite("HARDWARE VERSION : 9.9");
tft.textSetCursor(10, 320);
tft.textWrite("SOFTWARE VERSION : 9.91 COMPILED 05.12.2561");
tft.textSetCursor(10, 370);
tft.textWrite("SOFTWARE BY AJARN CHANGPUAK (ALEXANDER SSE FRANK)");
tft.textSetCursor(10, 420);
tft.textWrite("USING ADAFRUIT RA8875 & GFX LIBRARIES.");
// ///////////////////////////////////////////////////////////////////
// INITIALISATION SI4432. SEE PAGE ?? OF THE DATASHEET
// ///////////////////////////////////////////////////////////////////
pinMode(SI_nSEL, OUTPUT);
pinMode(SI_SCLK, OUTPUT);
pinMode(SI_SDI, OUTPUT);
pinMode(SI_SDO, INPUT_PULLUP);
pinMode(SI_GPIO, INPUT_PULLUP);
digitalWrite(SI_SCLK, LOW);
digitalWrite(SI_SDI, LOW);
Serial.println("Found SI4432. MEMORY DUMP : \n");
// INIT SI4432
// LETs READ ALL REGISTERS TO CHECK COMMUNICATION ...
for (int i=0; i<128;i++)
{
SI4432REG[i] = SI4432_Read_Byte(i) ;
Serial.print("ADR : ") ;
SerialHexOutput(i) ;
Serial.print(", VALUE : ") ;
SerialHexOutput(SI4432REG[i]) ;
Serial.println(" ") ;
}
// Enable receiver chain
SI4432_Write_Byte(0x07, 0x05);
// Clock Recovery Gearshift Value
SI4432_Write_Byte(0x1F, 0x00);
// IF Filter Bandwidth
bandwidth = SI4432_SET_RBW(bandwidth) ;
// REG 0x20 is updated with the IF Filter bandwidth
// Register 0x75 Frequency Band Select
byte sbsel = 1 ; // recommended setting
byte hbsel = 0 ; // low bands
byte fb = 19 ; // 430–439.9 MHz
byte FBS = (sbsel << 6 ) | (hbsel << 5 ) | fb ;
SI4432_Write_Byte(0x75, FBS) ;
// Register 0x76 Nominal Carrier Frequency
// WE USE 433.92 MHz
// Si443x-Register-Settings_RevB1.xls
SI4432_Write_Byte(0x76, 0x62) ;
// Register 0x77 Nominal Carrier Frequency
SI4432_Write_Byte(0x77, 0x00) ;
ZF = 433.92 ;
// RX MODEM SETTINGS
SI4432_Write_Byte(0x1C, 0x81) ;
SI4432_Write_Byte(0x1D, 0x3C) ;
SI4432_Write_Byte(0x1E, 0x02) ;
SI4432_Write_Byte(0x1F, 0x03) ;
// SI4432_Write_Byte(0x20, 0x78) ;
SI4432_Write_Byte(0x21, 0x01) ;
SI4432_Write_Byte(0x22, 0x11) ;
SI4432_Write_Byte(0x23, 0x11) ;
SI4432_Write_Byte(0x24, 0x01) ;
SI4432_Write_Byte(0x25, 0x13) ;
SI4432_Write_Byte(0x2A, 0xFF) ;
SI4432_Write_Byte(0x2C, 0x28) ;
SI4432_Write_Byte(0x2D, 0x0C) ;
SI4432_Write_Byte(0x2E, 0x28) ;
Serial.print("\nRX-IF SET TO : ");
Serial.print(ZF,2);
Serial.println(" MHz");
Serial.println("\nInitialisation complete.");
Serial.println("------------------------");
delay(9999);
tft.fillScreen(RA8875_BLACK);
MenueInfoButton() ;
DrawCheckerBoard() ;
ShowVerticalScale() ;
ShowStart(center-span/2) ;
ShowStop(center+span/2) ;
Show9() ;
ShowVerticalScale() ;
ShowStatus() ;
tft.touchEnable(true) ;
SetFreq(100.0 + ZF) ;
bandwidth = SI4432_SET_RBW(bandwidth) ;
}
void SI4432_Write_Byte(byte ADR, byte DATA )
{
ADR |= 0x80 ; // RW = 1
digitalWrite(SI_SCLK, LOW);
digitalWrite(SI_nSEL, LOW);
shiftOut(SI_SDI , SI_SCLK , MSBFIRST , ADR );
shiftOut(SI_SDI , SI_SCLK , MSBFIRST , DATA );
digitalWrite(SI_nSEL, HIGH);
}
byte SI4432_Read_Byte( byte ADR )
{
byte DATA ;
digitalWrite(SI_SCLK, LOW);
digitalWrite(SI_nSEL, LOW);
shiftOut(SI_SDI , SI_SCLK , MSBFIRST , ADR );
DATA = shiftIn(SI_SDO , SI_SCLK , MSBFIRST );
digitalWrite(SI_nSEL, HIGH);
return DATA ;
}
// ////////////////////////////////////////////////////////////////////////
// SEE https://www.silabs.com/documents/public/application-notes/AN440.pdf
// ////////////////////////////////////////////////////////////////////////
float SI4432_SET_RBW(float WISH)
{
byte ndec = 5 ;
byte dwn3 = 0 ;
byte fils = 1 ;
float rxosr = 12.5 ;
float REAL = 2.6 ; // AS CLOSE AS POSSIBLE TO "WISH" :-)
// YES, WE KNOW THIS IS SLOW
if (WISH > 2.6) { ndec = 5 ; fils = 2 ; REAL = 2.8 ; }
if (WISH > 2.8) { ndec = 5 ; fils = 3 ; REAL = 3.1 ; }
if (WISH > 3.1) { ndec = 5 ; fils = 4 ; REAL = 3.2 ; }
if (WISH > 3.2) { ndec = 5 ; fils = 5 ; REAL = 3.7 ; }
if (WISH > 3.7) { ndec = 5 ; fils = 6 ; REAL = 4.2 ; }
if (WISH > 4.2) { ndec = 5 ; fils = 7 ; REAL = 4.5 ; }
if (WISH > 4.5) { ndec = 4 ; fils = 1 ; REAL = 4.9 ; }
if (WISH > 4.9) { ndec = 4 ; fils = 2 ; REAL = 5.4 ; }
if (WISH > 5.4) { ndec = 4 ; fils = 3 ; REAL = 5.9 ; }
if (WISH > 5.9) { ndec = 4 ; fils = 4 ; REAL = 6.1 ; }
if (WISH > 6.1) { ndec = 4 ; fils = 5 ; REAL = 7.2 ; }
if (WISH > 7.2) { ndec = 4 ; fils = 6 ; REAL = 8.2 ; }
if (WISH > 8.2) { ndec = 4 ; fils = 7 ; REAL = 8.8 ; }
if (WISH > 8.8) { ndec = 3 ; fils = 1 ; REAL = 9.5 ; }
if (WISH > 9.5) { ndec = 3 ; fils = 2 ; REAL = 10.6 ; }
if (WISH > 10.6) { ndec = 3 ; fils = 3 ; REAL = 11.5 ; }
if (WISH > 11.5) { ndec = 3 ; fils = 4 ; REAL = 12.1 ; }
if (WISH > 12.1) { ndec = 3 ; fils = 5 ; REAL = 14.2 ; }
if (WISH > 14.2) { ndec = 3 ; fils = 6 ; REAL = 16.2 ; }
if (WISH > 16.2) { ndec = 3 ; fils = 7 ; REAL = 17.5 ; }
if (WISH > 17.5) { ndec = 2 ; fils = 1 ; REAL = 18.9 ; }
if (WISH > 18.9) { ndec = 2 ; fils = 2 ; REAL = 21.0 ; }
if (WISH > 21.0) { ndec = 2 ; fils = 3 ; REAL = 22.7 ; }
if (WISH > 22.7) { ndec = 2 ; fils = 4 ; REAL = 24.0 ; }
if (WISH > 24.0) { ndec = 2 ; fils = 5 ; REAL = 28.2 ; }
if (WISH > 28.2) { ndec = 2 ; fils = 6 ; REAL = 32.2 ; }
if (WISH > 32.2) { ndec = 2 ; fils = 7 ; REAL = 34.7 ; }
if (WISH > 34.7) { ndec = 1 ; fils = 1 ; REAL = 37.7 ; }
if (WISH > 37.7) { ndec = 1 ; fils = 2 ; REAL = 41.7 ; }
if (WISH > 41.7) { ndec = 1 ; fils = 3 ; REAL = 45.2 ; }
if (WISH > 45.2) { ndec = 1 ; fils = 4 ; REAL = 47.9 ; }
if (WISH > 47.9) { ndec = 1 ; fils = 5 ; REAL = 56.2 ; }
if (WISH > 56.2) { ndec = 1 ; fils = 6 ; REAL = 64.1 ; }
if (WISH > 64.1) { ndec = 1 ; fils = 7 ; REAL = 69.2 ; }
if (WISH > 69.2) { ndec = 0 ; fils = 1 ; REAL = 75.2 ; }
if (WISH > 75.2) { ndec = 0 ; fils = 2 ; REAL = 83.2 ; }
if (WISH > 83.2) { ndec = 0 ; fils = 3 ; REAL = 90.0 ; }
if (WISH > 90.0) { ndec = 0 ; fils = 4 ; REAL = 95.3 ; }
if (WISH > 95.3) { ndec = 0 ; fils = 5 ; REAL = 112.1 ; }
if (WISH > 112.1) { ndec = 0 ; fils = 6 ; REAL = 127.9 ; }
if (WISH > 127.9) { ndec = 0 ; fils = 7 ; REAL = 137.9 ; }
if (WISH > 137.9) dwn3 = 1 ;
if (WISH > 137.9) { ndec = 1 ; fils = 4 ; REAL = 142.8 ; }
if (WISH > 142.8) { ndec = 1 ; fils = 5 ; REAL = 167.8 ; }
if (WISH > 167.8) { ndec = 1 ; fils = 9 ; REAL = 181.1 ; }
if (WISH > 181.1) { ndec = 0 ; fils = 15 ; REAL = 191.5 ; }
if (WISH > 191.5) { ndec = 0 ; fils = 1 ; REAL = 225.1 ; }
if (WISH > 225.1) { ndec = 0 ; fils = 2 ; REAL = 248.8 ; }
if (WISH > 248.8) { ndec = 0 ; fils = 3 ; REAL = 269.3 ; }
if (WISH > 269.3) { ndec = 0 ; fils = 4 ; REAL = 284.9 ; }
if (WISH > 284.9) { ndec = 0 ; fils = 8 ; REAL = 335.5 ; }
if (WISH > 335.5) { ndec = 0 ; fils = 9 ; REAL = 361.8 ; }
if (WISH > 361.8) { ndec = 0 ; fils = 10 ; REAL = 420.2 ; }
if (WISH > 420.2) { ndec = 0 ; fils = 11 ; REAL = 468.4 ; }
if (WISH > 468.4) { ndec = 0 ; fils = 12 ; REAL = 518.8 ; }
if (WISH > 518.8) { ndec = 0 ; fils = 13 ; REAL = 577.0 ; }
if (WISH > 577.0) { ndec = 0 ; fils = 14 ; REAL = 620.7 ; }
byte BW = (dwn3 << 7) | (ndec << 4) | fils ;
SI4432_Write_Byte(0x1C , BW ) ;
// Clock Recovery Oversampling Rate, AN440, p. 29
rxosr = 500.0 * ( 1.0 + 2.0 * dwn3 ) / ( pow(2.0, (ndec-3.0)) * REAL );
byte integer = (int)rxosr ;
byte fractio = (int)((rxosr - integer) * 8 ) ;
byte memory = (integer << 3) | (0x07 & fractio) ;
SI4432_Write_Byte(0x20 , memory ) ;
return REAL ;
}
void SerialHexOutput(byte value)
{
Serial.print("0x");
if (value < 0x10) Serial.print("0");
Serial.print(value,HEX);
}
/*
• FVCO = FREF x D x [(INT + NUM/DEN)] where
• FVCO: PLL/VCO Frequency (4.6 GHz to 5.6 GHz)
• FREF: 50 MHz reference input
• D: PLL input frequency doubler, 1=Disabled, 2=Enabled
• DIVIDER = INT + NUM/DEN
• INT: PLL feedback divider integer value (12 bits, 1 to 4095)
• NUM: PLL feedback divider fractional numerator value,
• DEN: PLL feedback divider fractional denominator value
• freq = FVCO / OUTDIV
*/
void SetFreq(float freq)
{
freq += ZF ;
// Serial.println(freq,2);
unsigned int OUTDIV = (int)(5600 / freq ) ; // floor :-)
float FVCO = freq * OUTDIV ;
float DIVIDER = FVCO / REF ;
unsigned int INT = (int)DIVIDER ;
float REST = DIVIDER - INT ;
unsigned int NUM = (int)(REST * 10000);
unsigned int DEN = 10000 ;
// The 9-bit Output Divider
RegLO[0x16] = (OUTDIV & 0x100) >> 8 ;
RegLO[0x17] = (OUTDIV & 0x0FF) ;
// The 12-bit N integer divider value for PLL
RegLO[0x19] = (INT & 0x0F00) >> 8 ;
RegLO[0x1A] = (INT & 0x00FF) ;
// The 22-bit Fractional Divider Numerator
RegLO[0x1B] = (NUM & 0x03F0000) >> 16 ;
RegLO[0x1C] = (NUM & 0x000FF00) >> 8 ;
RegLO[0x1D] = (NUM & 0x0000FF) ;
// The 22-bit Fractional Divider Denominator
if (NUM == 0x00)
{
DEN = 0x01 ;
RegLO[0x1E] = 0x00 ;
RegLO[0x1F] = 0x00 ;
RegLO[0x20] = 0x01 ;
}
else
{
RegLO[0x1E] = (DEN & 0x03F0000) >> 16 ;
RegLO[0x1F] = (DEN & 0x000FF00) >> 8 ;
RegLO[0x20] = (DEN & 0x0000FF) ;
}
Wire.beginTransmission(LMK61E2ADR);
Wire.write(0x10);
Wire.write(RegLO[0x10]);
Wire.write(RegLO[0x11]);
Wire.endTransmission();
Wire.beginTransmission(LMK61E2ADR);
Wire.write(0x15);
Wire.write(RegLO[0x15]);
Wire.write(RegLO[0x16]);
Wire.write(RegLO[0x17]);
Wire.write(RegLO[0x18]);
Wire.write(RegLO[0x19]);
Wire.endTransmission();
Wire.beginTransmission(LMK61E2ADR);
Wire.write(0x1A);
Wire.write(RegLO[0x1A]);
Wire.write(RegLO[0x1B]);
Wire.write(RegLO[0x1C]);
Wire.write(RegLO[0x1D]);
Wire.write(RegLO[0x1E]);
Wire.write(RegLO[0x1F]);
Wire.endTransmission();
Wire.beginTransmission(LMK61E2ADR);
Wire.write(0x20);
Wire.write(RegLO[0x20]);
Wire.write(RegLO[0x21]);
Wire.write(RegLO[0x22]);
Wire.write(RegLO[0x23]);
Wire.write(RegLO[0x24]);
Wire.write(RegLO[0x25]);
Wire.write(RegLO[0x26]);
Wire.write(RegLO[0x27]);
Wire.endTransmission();
Wire.beginTransmission(LMK61E2ADR);
Wire.write(0x48);
Wire.write(RegLO[0x48]);
Wire.endTransmission();
}
void IDN()
{
Wire.beginTransmission(LMK61E2ADR);
Wire.write(0x00);
Wire.endTransmission();
Wire.requestFrom(LMK61E2ADR,6);
RegLO[0] = Wire.read();
RegLO[1] = Wire.read();
RegLO[2] = Wire.read();
RegLO[3] = Wire.read();
RegLO[8] = Wire.read();
RegLO[9] = Wire.read();
Serial.write("VNDRID R0: ");SerialHexOutput(RegLO[0]);Serial.write("\n");
Serial.write("VNDRID R1: ");SerialHexOutput(RegLO[1]);Serial.write("\n");
Serial.write("PRODID R2: ");SerialHexOutput(RegLO[2]);Serial.write("\n");
Serial.write("REVID R3: ");SerialHexOutput(RegLO[3]);Serial.write("\n");
Serial.write("I2CADR R8: ");SerialHexOutput(RegLO[8]);Serial.write("\n");
Serial.write("EEREV R9: ");SerialHexOutput(RegLO[9]);Serial.write("\n");
}
char *dtostrf (double val,signed char width,unsigned char prec,char *sout)
{
char fmt[20];
sprintf(fmt, "%%%d.%df", width, prec);
sprintf(sout, fmt, val);
return sout;
}
void ShowStart(float Anfang)
{
char Wert[10];
tft.textSetCursor(0, 421);
tft.textMode();
tft.textEnlarge(1);
tft.textTransparent(RA8875_WHITE);
tft.textWrite("START:");
dtostrf(Anfang,7,3,Wert);
tft.textWrite(Wert);
tft.textWrite(" MHz");
}
void ShowStop(float Ende)
{
char Wert[10];
tft.textSetCursor(359, 421);
tft.textMode();
tft.textEnlarge(1);
tft.textTransparent(RA8875_WHITE);
tft.textWrite("STOP:");
dtostrf(Ende,7,3,Wert);
tft.textWrite(Wert);
tft.textWrite(" MHz");
}
void ShowStatus()
{
char string[4];
// CLEAR TEXTAREA
tft.fillRect(0, 480, 620, 489, RA8875_BLACK);
// NOW WRITE NEW VALUES
tft.textMode();
tft.textEnlarge(1);
tft.textTransparent(RA8875_UNSEL);
tft.textSetCursor(0, 452);
tft.textWrite("AVG:");
if (AveragingOnOff == 0) tft.textWrite("OFF");
if (AveragingOnOff == 1) tft.textWrite("ON");
tft.textSetCursor(150, 452);
tft.textWrite("AVGS:");
if (AveragingFactor == 2) tft.textWrite("2");
if (AveragingFactor == 4) tft.textWrite("4");
tft.textSetCursor(290, 452);
tft.textWrite("MAXH:");
if (MaxHoldOnOff == 0) tft.textWrite("OFF");
if (MaxHoldOnOff == 1) tft.textWrite("ON");
tft.textSetCursor(460, 452);
tft.textWrite("ATT:");
if (AttSet == 0) tft.textWrite("0 dB ");
if (AttSet == 1)
{
dtostrf(AttLoss, 2, 0, string);
tft.textWrite(string);
tft.textWrite(" dB");
}
}
void Show9()
{
tft.textMode();
tft.textEnlarge(0);
tft.textTransparent(RA8875_YELLOW);
tft.textSetCursor(640, 435);
tft.textWrite("PROJECT 'VISIONARY'");
tft.textSetCursor(642, 457);
tft.textWrite(" WWW.CHANGPUAK.CH");
}
void ShowInputBuffer(String number)
{
tft.textMode();
tft.textEnlarge(2);
tft.textTransparent(RA8875_BLUE);
tft.textSetCursor(oX+2*KeyW+23 , oY+80+4);
tft.textWrite(" 1 ");
tft.graphicsMode();
}
void MenueInfoButton()
{
char Wert[10];
// CENTER
tft.fillRect(630, 9, 165, 40, RA8875_MENUE_COLOR);
tft.drawRect(630, 9, 165, 110, RA8875_MENUE_COLOR);
// SPAN
tft.fillRect(630, 129, 165, 40, RA8875_MENUE_COLOR);
tft.drawRect(630, 129, 165, 110, RA8875_MENUE_COLOR);
// BANDWIDTH
tft.fillRect(630, 249, 165, 40, RA8875_MENUE_COLOR);
tft.drawRect(630, 249, 165, 110, RA8875_MENUE_COLOR);
// SETTINGS
tft.setTextColor(RA8875_BLACK, RA8875_MENUE_COLOR);
tft.fillRect(630, 369, 165, 45, RA8875_MENUE_COLOR);
tft.textMode();
tft.textEnlarge(1);
tft.textTransparent(RA8875_BLACK);
// CENTER
tft.textSetCursor(665, 12);
tft.textWrite("CENTER");
// SPAN
tft.textSetCursor(680, 132);
tft.textWrite("SPAN");
// BANDWIDTH
tft.textSetCursor(640, 252);
tft.textWrite("BANDWIDTH");
// SETTINGS
tft.textSetCursor(645, 374);
tft.textWrite("SETTINGS");
// NUMERICAL VALUES
tft.textTransparent(RA8875_WHITE);
// CENTER FREQUENCY
dtostrf(center,6,2,Wert);
tft.textSetCursor(665, 66);
tft.textWrite(Wert);
// SPAN
dtostrf(span,6,2,Wert);
tft.textSetCursor(665, 186);
tft.textWrite(Wert);
// BANDWIDTH
dtostrf(bandwidth,6,2,Wert);
tft.textSetCursor(665, 306);
tft.textWrite(Wert);
}
void DrawCheckerBoard()
{
// VERTICAL
for (int y=0; y<9; y++)
{
// HORIZONTAL
for (int x=0; x<10; x++)
{
tft.drawRect(oX + x*dX, y*dY+oY, dX, dY, RA8875_NH);
tft.fillRect(oX + x*dX+1, y*dY+1+oY, dX-2, dY-2, RA8875_BLACK);
}
tft.drawRect(oX, oY, 10 * dX, 9 * dY, RA8875_WHITE);
}
tft.drawRect(oX, oY, 10 * dX, 9 * dY, RA8875_WHITE);
}
void DrawRectangleMenue()
{
// VERTICAL
for (int y=0; y<9; y++)
{
// HORIZONTAL
for (int x=0; x<10; x++)
{
tft.drawRect(oX, oY, 10 * dX, 9 * dY, RA8875_WHITE);
tft.fillRect(oX+1, oY+1, 10 * dX - 2, 9 * dY - 2, RA8875_BLACK);
}
}
}
void ShowVerticalScale()
{
// CLEAR TEXTAREA
tft.fillRect(0, 0, oX-2, 420, RA8875_BLACK);
// NOW WRITE NEW VALUES
tft.textMode();
tft.textEnlarge(0);
char string[6];
int level ;
int Top ;
if (AttSet == 0) Top = (int)( ThruLoss + MaxIFLevel );
if (AttSet == 1) Top = (int)( ThruLoss + MaxIFLevel + AttLoss );
for (int y=0; y<=9; y++)
{
tft.textSetCursor(0, y * dY);
tft.textTransparent(RA8875_WHITE);
level = Top - y * 10 ;
itoa(level,string,10);
if (level == 0)
{
for (int z=6; z>0; z--) string[z] = string[z-1] ;
string[0] = 32 ; // space
}
if (level >= 0)
{
for (int z=6; z>0; z--) string[z] = string[z-1] ;
}
if (level > 0) string[0] = 43 ; // plus
if (abs(level) < 100)
{
for (int z=6; z>0; z--) string[z] = string[z-1] ;
string[0] = 32 ; // space
}
tft.textWrite(string);
}
}
void DrawLine()
{
long Y1, Y2 ;
if (AttSet == 0) TopLevel = MaxIFLevel + ThruLoss ;
if (AttSet == 1) TopLevel = MaxIFLevel + AttLoss + ThruLoss ;
// HORIZONTAL
for (int x=0; x < DOTS-2 ; x+=1)
{
if (Sample[x] >= TopLevel) Sample[x] = TopLevel - 0.5 ;
if (Sample[x+1] >= TopLevel) Sample[x+1] = TopLevel - 0.5 ;
if (Sample[x] <= (TopLevel - 90.0)) Sample[x] = TopLevel - 89.5 ;
if (Sample[x+1] <= (TopLevel - 90.0)) Sample[x+1] = TopLevel - 89.5 ;
Y1 = (int)(dY * abs((TopLevel - Sample[x]) * 0.1 ));
Y2 = (int)(dY * abs((TopLevel - Sample[x+1]) * 0.1 ));
tft.drawLine(x+oX+2, Y1+oY, x+oX+3, Y2+oY, RA8875_YELLOW);
tft.drawLine(x+oX+2, Y1+oY+1, x+oX+3, Y2+oY+1, RA8875_YELLOW);
}
}
unsigned int CalcDistance(int X1, int Y1, int X2, int Y2)
{
return sqrt((Y2-Y1)*(Y2-Y1)+(X2-X1)*(X2-X1));
}
void GetKeyPosition()
{
boolean pressed = false;
LastXPressed = 0;
LastYPressed = 0;
tft.graphicsMode();
digitalWrite(RA8875_INT, HIGH);
tft.touchEnable(true);
/* Wait around for touch events */
while (digitalRead(RA8875_INT)) { delay(1); } // DANGEROUS !!!!
if (tft.touched())
{
if (!pressed)
{
delay(290);
tft.touchRead(&tx, &ty);
pressed = true;
// Serial.print(tx); Serial.print(", "); Serial.println(ty);
}
}
LastXPressed = tx ;
LastYPressed = ty ;
}
String GetKeyBoard()
{
boolean pressed = false;
String Result = "N" ;
unsigned int Distance ;
unsigned int Error = 999 ;
unsigned int HalfW = KeyW / 2 ;
unsigned int HalfH = KeyH / 2 ;
unsigned int HalfD = KeyD / 2 ;
GetKeyPosition();
tx = LastXPressed ;
ty = LastYPressed ;
// CALCULATE RADII AND FIND KEY WITH MINIMUM DISTANCE TO CENTER
// THE COORDINATES HAVE BEEN FOUND WTH THE "CALIBRATEUR"
// 0 ///////////////////////////////////////////////////////
Distance = CalcDistance(178, 450, tx, ty);
if ( Distance < Error ) { Error = Distance ; Result = "0" ; }
// 1 ///////////////////////////////////////////////////////
Distance = CalcDistance(306, 450, tx, ty);
if ( Distance < Error ) { Error = Distance ; Result = "1" ; }
// 2 ///////////////////////////////////////////////////////
Distance = CalcDistance(439, 450, tx, ty);
if ( Distance < Error ) { Error = Distance ; Result = "2" ; }
// 3 ///////////////////////////////////////////////////////
Distance = CalcDistance(568, 450, tx, ty);
if ( Distance < Error ) { Error = Distance ; Result = "3" ; }
// 4 ///////////////////////////////////////////////////////
Distance = CalcDistance(699, 450, tx, ty);
if ( Distance < Error ) { Error = Distance ; Result = "4" ; }
// 5 ///////////////////////////////////////////////////////
Distance = CalcDistance(162, 600, tx, ty);
if ( Distance < Error ) { Error = Distance ; Result = "5" ; }
// 6 ///////////////////////////////////////////////////////
Distance = CalcDistance(308, 600, tx, ty);
if ( Distance < Error ) { Error = Distance ; Result = "6" ; }
// 7 ///////////////////////////////////////////////////////
Distance = CalcDistance(440, 600, tx, ty);
if ( Distance < Error ) { Error = Distance ; Result = "7" ; }
// 8 ///////////////////////////////////////////////////////
Distance = CalcDistance(565, 600, tx, ty);
if ( Distance < Error ) { Error = Distance ; Result = "8" ; }
// 9 ///////////////////////////////////////////////////////
Distance = CalcDistance(678, 600, tx, ty);
if ( Distance < Error ) { Error = Distance ; Result = "9" ; }
// . ///////////////////////////////////////////////////////
Distance = CalcDistance(176, 740, tx, ty);
if ( Distance < Error ) { Error = Distance ; Result = "." ; }
// C ///////////////////////////////////////////////////////
Distance = CalcDistance(376, 740, tx, ty);
if ( Distance < Error ) { Error = Distance ; Result = "C" ; }
// E ///////////////////////////////////////////////////////
Distance = CalcDistance(621, 740, tx, ty);
if ( Distance < Error ) { Error = Distance ; Result = "E" ; }
// Serial.print(Distance,DEC);
// Serial.print(" KEY : ");
// Serial.println(Result);
return Result;
}
float GetNewValue()
{
float Value ;
String Wert = " " ;
String NewValue = " ";
boolean Fertig = false ;
tft.textEnlarge(2);
while (Fertig < 1)
{
tft.graphicsMode();
NewValue = GetKeyBoard();
tft.textMode();
if (NewValue == "C")
{
Wert = " " ;
tft.graphicsMode();
tft.fillRect(oX+20, oY+80, 5*KeyW+4*KeyD, KeyH, RA8875_BLACK);
tft.drawRoundRect(oX+20,oY+80,5*KeyW+4*KeyD,KeyH,radius,RA8875_WHITE);
tft.textMode();
}
if (NewValue == "E") Fertig = true ;
if ((NewValue >= "0") && (NewValue <= "9")) Wert += NewValue ;
if (NewValue == ".") Wert += NewValue ;
if (Wert.length() > 9) Fertig = true ;
tft.textTransparent(RA8875_BLUE);
tft.textSetCursor(oX+150, oY+84);
tft.print(Wert);
// Serial.println(Wert);
}
Value = Wert.toFloat();
return Value ;
}
void UpDateSettings1()
{
boolean DONE = false ;
unsigned int Error ;
unsigned int Distance ;
String Result = "N" ;
do
{
ShowSettingsMenue1() ;
GetKeyPosition();
tx = LastXPressed ;
ty = LastYPressed ;
// IDENTIFY KEY PRESSED
Error = 999 ;
// DONE
Distance = CalcDistance(700, 180, tx, ty);
if ( Distance < Error ) { Error = Distance ; Result = "D" ; }
// ATTENUATOR 0dB
Distance = CalcDistance(500, 330, tx, ty);
if ( Distance < Error ) { Error = Distance ; Result = "A" ; }
// ATTENUATOR AttLoss dB
Distance = CalcDistance(660, 330, tx, ty);
if ( Distance < Error ) { Error = Distance ; Result = "B" ; }
// MAX HOLD ON
Distance = CalcDistance(500, 480, tx, ty);
if ( Distance < Error ) { Error = Distance ; Result = "X" ; }
// MAX HOLD OFF
Distance = CalcDistance(660, 480, tx, ty);
if ( Distance < Error ) { Error = Distance ; Result = "Y" ; }
// AVERAGING ON
Distance = CalcDistance(500, 660, tx, ty);
if ( Distance < Error ) { Error = Distance ; Result = "M" ; }
// AVERAGING OFF
Distance = CalcDistance(660, 630, tx, ty);
if ( Distance < Error ) { Error = Distance ; Result = "N" ; }
// AVERAGING SAMPLES = 2
Distance = CalcDistance(500, 780, tx, ty);
if ( Distance < Error ) { Error = Distance ; Result = "2" ; }
// AVERAGING SAMPLES = 4
Distance = CalcDistance(660, 780, tx, ty);
if ( Distance < Error ) { Error = Distance ; Result = "4" ; }
// Serial.println(Result);
// EVALUATE THE KEY PRESSED
if (Result=="D")
{
DONE = true ;
}
// ATTENUATOR ON/OFF
if (Result=="A")
{
AttSet = 0 ;
digitalWrite(AttenuatorPin, LOW);
ShowVerticalScale();
}
if (Result=="B")
{
AttSet = 1 ;
digitalWrite(AttenuatorPin, HIGH);
ShowVerticalScale();
}
// MAX HOLD ON/OFF
if (Result=="X")
{
MaxHoldOnOff = 1 ;
}
if (Result=="Y")
{
MaxHoldOnOff = 0 ;
}
// AVERAGING ON/OFF
if (Result=="M")
{
AveragingOnOff = 1 ;
}
if (Result=="N")
{
AveragingOnOff = 0 ;
}
// AVERAGING FACTOR 2/4
if (Result=="2")
{
AveragingFactor = 2 ;
}
if (Result=="4")
{
AveragingFactor = 4 ;
}
} while(!DONE);
}
float GetNewRBW() // NOT USED
{
float Value ;
boolean Fertig = false ;
boolean pressed = false;
unsigned int Distance ;
unsigned int Error = 999 ;
GetKeyPosition();
tx = LastXPressed ;
ty = LastYPressed ;
// CALCULATE RADII AND FIND KEY WITH MINIMUM DISTANCE TO CENTER
// 100 kHz ////////////////////////////////////////////////////
Distance = CalcDistance(250, 300, tx, ty);
if ( Distance < Error ) { Error = Distance ; Value = 100.0 ; }
// 150 kHz ////////////////////////////////////////////////////
Distance = CalcDistance(250, 450, tx, ty);
if ( Distance < Error ) { Error = Distance ; Value = 150.0 ; }
// 200 kHz ////////////////////////////////////////////////////
Distance = CalcDistance(250, 600, tx, ty);
if ( Distance < Error ) { Error = Distance ; Value = 200.0 ; }
// 300 kHz ////////////////////////////////////////////////////
Distance = CalcDistance(250, 750, tx, ty);
if ( Distance < Error ) { Error = Distance ; Value = 300.0 ; }
// 400 kHz ////////////////////////////////////////////////////
Distance = CalcDistance(600, 300, tx, ty);
if ( Distance < Error ) { Error = Distance ; Value = 400.0 ; }
// 500 kHz ////////////////////////////////////////////////////
Distance = CalcDistance(600, 450, tx, ty);
if ( Distance < Error ) { Error = Distance ; Value = 500.0 ; }
// 600 kHz ////////////////////////////////////////////////////
Distance = CalcDistance(600, 600, tx, ty);
if ( Distance < Error ) { Error = Distance ; Value = 600.0 ; }
// 700 kHz ////////////////////////////////////////////////////
Distance = CalcDistance(600, 750, tx, ty);
if ( Distance < Error ) { Error = Distance ; Value = 700.0 ; }
if ( Value > 300.0 ) Value = 300.0 ;
return Value ;
}
void DrawKeyBoard()
{
tft.graphicsMode();
// RESULT BOX
tft.drawRoundRect(oX+20,oY+80,5*KeyW+4*KeyD,KeyH,radius,
RA8875_WHITE);
// 0
tft.drawRoundRect(oX+20,oY+80+KeyH+KeyD,KeyW,KeyH,radius,
RA8875_WHITE);
// 1
tft.drawRoundRect(oX+20+KeyW+KeyD,oY+80+KeyH+KeyD,KeyW,KeyH,
radius,RA8875_WHITE);
// 2
tft.drawRoundRect(oX+20+2*KeyW+2*KeyD,oY+80+KeyH+KeyD,KeyW,
KeyH,radius,RA8875_WHITE);
// 3
tft.drawRoundRect(oX+20+3*KeyW+3*KeyD,oY+80+KeyH+KeyD,KeyW,
KeyH,radius,RA8875_WHITE);
// 4
tft.drawRoundRect(oX+20+4*KeyW+4*KeyD,oY+80+KeyH+KeyD,KeyW,
KeyH,radius,RA8875_WHITE);
// 5
tft.drawRoundRect(oX+20,oY+80+2*KeyH+2*KeyD,KeyW,KeyH,radius,
RA8875_WHITE);
// 6
tft.drawRoundRect(oX+20+KeyW+KeyD,oY+80+2*KeyH+2*KeyD,KeyW,
KeyH,radius,RA8875_WHITE);
// 7
tft.drawRoundRect(oX+20+2*KeyW+2*KeyD,oY+80+2*KeyH+2*KeyD,
KeyW,KeyH,radius,RA8875_WHITE);
// 8
tft.drawRoundRect(oX+20+3*KeyW+3*KeyD,oY+80+2*KeyH+2*KeyD,
KeyW,KeyH,radius,RA8875_WHITE);
// 9
tft.drawRoundRect(oX+20+4*KeyW+4*KeyD,oY+80+2*KeyH+2*KeyD,
KeyW,KeyH,radius,RA8875_WHITE);
// .
tft.drawRoundRect(oX+20, oY+80+3*KeyH+3*KeyD,KeyW,KeyH,
radius,RA8875_WHITE);
// CLR
tft.drawRoundRect(oX+20+KeyW+KeyD,oY+80+3*KeyH+3*KeyD,
KeyW*2+KeyD,KeyH,radius,RA8875_WHITE);
// ENTER
tft.drawRoundRect(oX+20+3*KeyW+3*KeyD,oY+80+3*KeyH+3*KeyD,
KeyW*2+KeyD,KeyH,radius,RA8875_WHITE);
// DISPLAY DIGITS / TEXT
tft.textMode();
tft.textEnlarge(2);
tft.textSetCursor(oX+7+KeyW/2, oY+80+KeyH+KeyD+4);
tft.textWrite("0");
tft.textSetCursor(oX+7+KeyW+KeyD+KeyW/2, oY+80+KeyH+KeyD+4);
tft.textWrite("1");
tft.textSetCursor(oX+7+2*KeyW+2*KeyD+KeyW/2, oY+80+KeyH+KeyD+4);
tft.textWrite("2");
tft.textSetCursor(oX+7+3*KeyW+3*KeyD+KeyW/2, oY+80+KeyH+KeyD+4);
tft.textWrite("3");
tft.textSetCursor(oX+7+4*KeyW+4*KeyD+KeyW/2, oY+80+KeyH+KeyD+4);
tft.textWrite("4");
tft.textSetCursor(oX+7+KeyW/2, oY+80+2*KeyH+2*KeyD+4);
tft.textWrite("5");
tft.textSetCursor(oX+7+KeyW+KeyD+KeyW/2, oY+80+2*KeyH+2*KeyD+4);
tft.textWrite("6");
tft.textSetCursor(oX+7+2*KeyW+2*KeyD+KeyW/2, oY+80+2*KeyH+2*KeyD+4);
tft.textWrite("7");
tft.textSetCursor(oX+7+3*KeyW+3*KeyD+KeyW/2, oY+80+2*KeyH+2*KeyD+4);
tft.textWrite("8");
tft.textSetCursor(oX+7+4*KeyW+4*KeyD+KeyW/2, oY+80+2*KeyH+2*KeyD+4);
tft.textWrite("9");
tft.textSetCursor(oX+7+KeyW/2, oY+80+3*KeyH+3*KeyD+4);
tft.textWrite(".");
tft.textSetCursor(oX+7+KeyW+KeyD+KeyW/2+10, oY+80+3*KeyH+3*KeyD+4);
tft.textWrite("CLEAR");
tft.textSetCursor(oX+7+3*KeyW+3*KeyD+KeyW/2+10, oY+80+3*KeyH+3*KeyD+4);
tft.textWrite("ENTER");
}
void ShowSettingsMenue1()
{
char string[4];
tft.graphicsMode();
// DONE
tft.textTransparent(RA8875_WHITE);
tft.drawRoundRect(oX+450, oY+10, 4*29, KeyH, radius,
RA8875_WHITE);
// 0 dB
if (AttSet == 0)
tft.drawRoundRect(oX+284, oY+10+KeyH+KeyD, 4*30,
KeyH, radius, RA8875_SEL);
if (AttSet == 1)
tft.drawRoundRect(oX+284, oY+10+KeyH+KeyD, 4*30,
KeyH, radius, RA8875_UNSEL);
// AttLoss dB
if (AttSet == 0)
tft.drawRoundRect(oX+414, oY+10+KeyH+KeyD, 5*30,
KeyH, radius, RA8875_UNSEL);
if (AttSet == 1)
tft.drawRoundRect(oX+414, oY+10+KeyH+KeyD, 5*30,
KeyH, radius, RA8875_SEL);
// MAX.HOLD ON
if (MaxHoldOnOff == 0)
tft.drawRoundRect(oX+284, oY+10+2*KeyH+2*KeyD, 4*30,
KeyH, radius, RA8875_UNSEL);
if (MaxHoldOnOff == 1)
tft.drawRoundRect(oX+284, oY+10+2*KeyH+2*KeyD, 4*30,
KeyH, radius, RA8875_SEL);
// MAX.HOLD OFF
if (MaxHoldOnOff == 0)
tft.drawRoundRect(oX+414, oY+10+2*KeyH+2*KeyD, 5*30,
KeyH, radius, RA8875_SEL);
if (MaxHoldOnOff == 1)
tft.drawRoundRect(oX+414, oY+10+2*KeyH+2*KeyD, 5*30,
KeyH, radius, RA8875_UNSEL);
// AVERAGING ON
if (AveragingOnOff == 0)
tft.drawRoundRect(oX+284, oY+10+3*KeyH+3*KeyD, 4*30,
KeyH, radius, RA8875_UNSEL);
if (AveragingOnOff == 1)
tft.drawRoundRect(oX+284, oY+10+3*KeyH+3*KeyD, 4*30,
KeyH, radius, RA8875_SEL);
// AVERAGING OFF
if (AveragingOnOff == 0)
tft.drawRoundRect(oX+414, oY+10+3*KeyH+3*KeyD, 5*30,
KeyH, radius, RA8875_SEL);
if (AveragingOnOff == 1)
tft.drawRoundRect(oX+414, oY+10+3*KeyH+3*KeyD, 5*30,
KeyH, radius, RA8875_UNSEL);
// AVERAGING SWEEPS 2
if (AveragingFactor == 2)
tft.drawRoundRect(oX+284, oY+10+4*KeyH+4*KeyD, 4*30,
KeyH, radius, RA8875_SEL);
if (AveragingFactor == 4)
tft.drawRoundRect(oX+284, oY+10+4*KeyH+4*KeyD, 4*30,
KeyH, radius, RA8875_UNSEL);
// AVERAGING SWEEPS 4
if (AveragingFactor == 2)
tft.drawRoundRect(oX+414, oY+10+4*KeyH+4*KeyD, 5*30,
KeyH, radius, RA8875_UNSEL);
if (AveragingFactor == 4)
tft.drawRoundRect(oX+414, oY+10+4*KeyH+4*KeyD, 5*30,
KeyH, radius, RA8875_SEL);
// DISPLAY DIGITS / TEXT
tft.textMode();
tft.textEnlarge(2);
tft.textTransparent(RA8875_WHITE);
tft.textSetCursor(oX+10+450, oY+15);
tft.textWrite("DONE");
tft.textTransparent(RA8875_WHITE);
tft.textSetCursor(oX+20, oY+12+KeyH+KeyD+4);
tft.textWrite("Attenuator");
tft.textSetCursor(oX+290+4, oY+12+KeyH+KeyD+4);
if (AttSet == 0) tft.textTransparent(RA8875_SEL);
if (AttSet == 1) tft.textTransparent(RA8875_UNSEL);
tft.textWrite("0 dB ");
tft.textSetCursor(oX+430+4, oY+12+KeyH+KeyD+4);
if (AttSet == 0) tft.textTransparent(RA8875_UNSEL);
if (AttSet == 1) tft.textTransparent(RA8875_SEL);
// AttLoss
dtostrf(AttLoss, 2, 0, string);
tft.textWrite(string);
tft.textWrite(" dB");
tft.textTransparent(RA8875_WHITE);
tft.textSetCursor(oX+20, oY+12+2*KeyH+2*KeyD+4);
tft.textWrite("Max. Hold");
tft.textSetCursor(oX+290+20, oY+12+2*KeyH+2*KeyD+4);
if (MaxHoldOnOff == 0) tft.textTransparent(RA8875_UNSEL);
if (MaxHoldOnOff == 1) tft.textTransparent(RA8875_SEL);
tft.textWrite("ON");
tft.textSetCursor(oX+430+8, oY+12+2*KeyH+2*KeyD+4);
if (MaxHoldOnOff == 1) tft.textTransparent(RA8875_UNSEL);
if (MaxHoldOnOff == 0) tft.textTransparent(RA8875_SEL);
tft.textWrite("OFF");
tft.textTransparent(RA8875_WHITE);
tft.textSetCursor(oX+20, oY+12+3*KeyH+3*KeyD+4);
tft.textWrite("Averaging");
tft.textSetCursor(oX+290+20, oY+12+3*KeyH+3*KeyD+4);
if (AveragingOnOff == 0) tft.textTransparent(RA8875_UNSEL);
if (AveragingOnOff == 1) tft.textTransparent(RA8875_SEL);
tft.textWrite("ON");
tft.textSetCursor(oX+430+8, oY+12+3*KeyH+3*KeyD+4);
if (AveragingOnOff == 0) tft.textTransparent(RA8875_SEL);
if (AveragingOnOff == 1) tft.textTransparent(RA8875_UNSEL);
tft.textWrite("OFF");
tft.textTransparent(RA8875_WHITE);
tft.textSetCursor(oX+20, oY+12+4*KeyH+4*KeyD+4);
tft.textWrite("Avg.Sweeps");
tft.textSetCursor(oX+290+20, oY+12+4*KeyH+4*KeyD+4);
if (AveragingFactor == 2) tft.textTransparent(RA8875_SEL);
if (AveragingFactor == 4) tft.textTransparent(RA8875_UNSEL);
tft.textWrite("2");
tft.textSetCursor(oX+430+8, oY+12+4*KeyH+4*KeyD+4);
if (AveragingFactor == 4) tft.textTransparent(RA8875_SEL);
if (AveragingFactor == 2) tft.textTransparent(RA8875_UNSEL);
tft.textWrite("4");
}
float SI4432_RSSI()
{
// SEE DATASHEET PAGE 61
byte RSSI_RAW = SI4432_Read_Byte( 0x26 ) ;
float dBm = 0.5 * RSSI_RAW - 120.0 ;
// Serial.println(dBm,2);
return dBm ;
}
void Measure( unsigned int index)
{
curr_freq = Start + index * increment ;
SetFreq(curr_freq);
delayMicroseconds(sweep_delay);
Sample[index] = SI4432_RSSI() ; // UNIT IS dBm
}
void loop()
{
tft.graphicsMode();
tft.touchEnable(true);
TouchedMe = 0;
unsigned int KeyPressed = 9 ;
unsigned int LastAction = 0 ;
unsigned int Distance ;
unsigned int Error = 9999 ;
if (! digitalRead(RA8875_INT))
{
if (tft.touched())
{
tft.touchEnable(false);
tft.touchRead(&tx, &ty);
// CENTER = 1
Distance = CalcDistance(890, 240, tx, ty);
if ( Distance < Error ) { Error = Distance ; KeyPressed = 1 ; }
// SPAN = 2
Distance = CalcDistance(890, 460, tx, ty);
if ( Distance < Error ) { Error = Distance ; KeyPressed = 2 ; }
// BANDWIDTH = 3
Distance = CalcDistance(890, 680, tx, ty);
if ( Distance < Error ) { Error = Distance ; KeyPressed = 3 ; }
// SETTINGS = 4
Distance = CalcDistance(890, 804, tx, ty);
if ( Distance < Error ) { Error = Distance ; KeyPressed = 4 ; }
/*
Serial.print("Loop Touched @ ");
Serial.print(tx); Serial.print(", "); Serial.println(ty);
Serial.println(KeyPressed);
*/
}
}
switch (KeyPressed)
{
case 1: // "c"
// NEW CENTER FREQUENCY
DrawRectangleMenue() ;
tft.textMode();
tft.textEnlarge(2);
tft.textSetCursor(oX+20,oY+15);
tft.textTransparent(RA8875_YELLOW);
tft.textWrite("ENTER NEW CENTER [MHz] :");
DrawKeyBoard();
center = GetNewValue();
if ((center + span/2) > MAXEnde) center = MAXEnde - span/2 ;
if ((center + span/2) < MAXAnfang) center = MAXAnfang + span/2 ;
tft.fillScreen(RA8875_BLACK);
TouchedMe = 0;
i = 0; // START NEW SWEEP, CONFIG. CHANGED
MenueInfoButton();
DrawCheckerBoard();
ShowVerticalScale();
ShowStart(center-span/2);
ShowStop(center+span/2);
Show9();
ShowStatus();
LastAction = millis() + 60000 ;
break;
case 2: // "s"
// NEW SPAN FREQUENCY
DrawRectangleMenue() ;
tft.textMode();
tft.textEnlarge(2);
tft.textSetCursor(oX+20,oY+15);
tft.textTransparent(RA8875_YELLOW);
tft.textWrite("ENTER NEW SPAN [MHz] :");
DrawKeyBoard();
span = GetNewValue();
if (span >= MAXspan)
{
span = MAXspan ;
center = span / 2.0 ;
}
if (span <= 0.0) span = 0.0 ; // minus symbol no have on keyboard
Start = center - span / 2.0 ;
Stop = center + span / 2.0 ;
increment = span / DOTS ;
// SPAN IS MHz, RBW IS kHz
bandwidth = SI4432_SET_RBW( span * 1000.0 / DOTS ) ;
// SWEEP-TIME = k * SPAN / RBW^2
// ALL FREQUENCIES ARE IN kHz, RESULT IS IN MICROSECONDS
sweep_delay=(int)(1000.0*(3.0*span*1000.0)/(bandwidth*bandwidth*DOTS));
tft.fillScreen(RA8875_BLACK);
TouchedMe = 0;
i = 0; // START NEW SWEEP, CONFIG. CHANGED
MenueInfoButton();
DrawCheckerBoard();
ShowVerticalScale();
ShowStart(center-span/2.0);
ShowStop(center+span/2.0);
Show9();
ShowStatus();
LastAction = millis() + 60000 ;
break;
case 3: // "b"
// NEW RESOLUTION BANDWIDTH
DrawRectangleMenue() ;
tft.textMode();
tft.textEnlarge(2);
tft.textSetCursor(oX+20,oY+15);
tft.textTransparent(RA8875_YELLOW);
tft.textWrite("ENTER NEW RBW [kHz] :");
DrawKeyBoard();
bandwidth = SI4432_SET_RBW(GetNewValue()) ;
// SWEEP-TIME = k * SPAN / RBW^2
// ALL FREQUENCIES ARE IN kHz, RESULT IS IN MICROSECONDS
sweep_delay=(int)(1000.0*(3.0*span*1000.0)/(bandwidth*bandwidth*DOTS));
tft.fillScreen(RA8875_BLACK);
TouchedMe = 0;
i = 0; // START NEW SWEEP, CONFIG. CHANGED
MenueInfoButton();
DrawCheckerBoard();
ShowVerticalScale();
ShowStart(center-span/2.0);
ShowStop(center+span/2.0);
Show9();
ShowStatus();
LastAction = millis() + 60000 ;
break;
case 4 : // "S"
// SETTINGS 1 /////////////////////////////////
DrawRectangleMenue() ;
tft.textMode();
tft.textEnlarge(2);
tft.textSetCursor(oX+20,oY+15);
tft.textTransparent(RA8875_YELLOW);
tft.textWrite("SETTINGS 01 :");
UpDateSettings1();
TouchedMe = 0;
/* SETTINGS 2 /////////////////////////////////
DrawRectangleMenue() ;
tft.textMode();
tft.textEnlarge(2);
tft.textSetCursor(oX+20,oY+15);
tft.textTransparent(RA8875_YELLOW);
tft.textWrite("SETTINGS 02 :");
UpDateSettings2();
TouchedMe = 0;
*/ // ////////////////////////////////////////
i = 0; // START NEW SWEEP, CONFIG. CHANGED
MenueInfoButton();
DrawCheckerBoard();
ShowVerticalScale();
ShowStart(center-span/2.0);
ShowStop(center+span/2.0);
Show9();
ShowStatus();
break;
default:
// DO NOTHING
break ;
}
if (i >= DOTS)
{
i = 0;
DrawCheckerBoard();
DrawLine();
}
Measure(i);
i += 1 ;
// Serial.println(i,DEC);
// delay(1);
}
// /////////////////////////////////////////////////////////////////////
// END OF FILE.
// /////////////////////////////////////////////////////////////////////
✈ OSD - Menue
As we use a 5.0" 40-pin TFT Display - 800x480 pixels with Touchscreen (from Adafruit), we can create a dynamic menue, i.e.
the firmware mayst be adapted to any situation without the frontpanel (buttons) to be changed. The sketch calculates the distance
from where the user pressed to the center of the "key". The one with the shortest distance is then chosen. Compare lines
810 - 855.
✈ In Memoriam Rex Visionaria
H.M. Bhumibol Adulyadej, 5.12.1927 - 13.10.2016, a truely visionary Monarch
In this picture, His Majesty is teaching students of Wang Klai Kangwon School
✈ Share your thoughts
The webmaster does not read these comments regularely. Urgent questions should be send via email.
Ads or links to completely uncorrelated things will be removed.
Your Browser says that you allow tracking. Mayst we suggest that you check that DNT thing ?