Categories
Statistics
Flag Counter
Since 08.08.2014
Counts only, if "DNT = disabled".

Your IP is 3.140.198.43
ec2-3-140-198-43.us-east-2.c
Info
Valid HTML 4.01 Transitional Creative Commons Lizenzvertrag
rss
เราจะทำแบบวิศวกรผู้ยิ่งใหญ่
We love the King
20. April 2024
Your valuable opinion :
4.5 stars

Avg. 4.98 from 44 votes.



Arduino-Project-VISIONARY.php   15501 Bytes    05-12-2018 13:10:09


Arduino/Genuino Project "VISIONARY"


A 199 MHz DIY Spectrum Analyser with an LMK61E2 & SI4432




VISIONARY in action



UNITRANGES • REMARKS
Frequency1 - 200 MHz
InputBNC, 50 Ω
Amplitude Resolution0.5 dB
Dynamic Range-121 dBm to +10 dBm
Resolution Bandwidth2.6 kHz to 620 kHz
Power Consumption7.5 V, < 500 mA
Reference Output100.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




Block Diagram 'VISIONARY'


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.



VISIONARY inside

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.

LMK61E2-What's inside ?

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.

SI4432-What's inside ?

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.

Cascaded SAW
Two SAW Bandpass Filters
Cascaded SAW
Three SAW Bandpass Filters




✈ 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




OSD Menue
OSD Menue - Keyboard
OSD Menue
OSD Menue - Settings


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




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 ?

 
t1 = 6491 d

t2 = 319 ms

★ ★ ★  Copyright © 2006 - 2024 by changpuak.ch  ★ ★ ★

Impressum