The “Micro20 III” QRP SSB Transceiver for 14 MHz – The Software

/*****************************************************************/
/*             VFO for QRP SSB Nano-Transceiver 20m              */
/*                       "Micro20 III"                           */
/*                 w. ATMega168 and Si5351                       */
/*                    Display OLED SSD1306                       */
/*  ************************************************************ */
/*  MUC:              ATMEL AVR ATmega168, 8 MHz                 */
/*                                                               */
/*  Compiler:         GCC (GNU AVR C-Compiler)                   */
/*  Author:           Peter Rachow (DK7IH)                       */
/*  Last change:      OCT 2017                                   */
/*****************************************************************/

//PORTS
//OUTPUT

//INPUT
//PC0: =ADC0: Pull-up for key switches with various resistors against GND 
//PC1: =ADC1: AGC voltage for S-Meter
//PC2: =ADC2: Voltage measurement
//PC3: =ADC3: TX PWR meter



//PD1: TX/RX indicator
//PD5, PD6: Rotary encoder

//TWI
//PC4=SDA, PC5=SCL: I²C-Bus lines: 

#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/sleep.h>
#include <avr/eeprom.h>
#include <util/delay.h>
#include <util/twi.h>

#undef F_CPU 
#define F_CPU 80000000

/////////////////////
//Defines for Si5351
/////////////////////
#define SI5351_ADDRESS 0xC0 // 0b11000000 for my module. Others may vary! The 0x60 did NOT work with my module!

//Set of Si5351A register addresses
#define CLK_ENABLE_CONTROL       3
#define PLLX_SRC				15
#define CLK0_CONTROL            16 
#define CLK1_CONTROL            17
#define CLK2_CONTROL            18
#define SYNTH_PLL_A             26
#define SYNTH_PLL_B             34
#define SYNTH_MS_0              42
#define SYNTH_MS_1              50
#define SYNTH_MS_2              58
#define PLL_RESET              177
#define XTAL_LOAD_CAP          183

//SI5351 Declarations & frequency
void si5351_write(int, int);
void si5351_start(void);
void si5351_set_freq(int, unsigned long);
void set_oscillators(int, unsigned long, int);
unsigned long scan_frequency(unsigned long, unsigned long, int, int);     

//////////////////////////////////////
//   L   C   D   
//////////////////////////////////////


// Font 5x8 for OLED
const char fontchar[485] = {
0x00,0x00,0x00,0x00,0x00, // 20 space ASCII table for NOKIA LCD: 96 rows * 5 bytes= 480 bytes
0x00,0x00,0x5f,0x00,0x00, // 21 ! Note that this is the same set of codes for character you
0x00,0x07,0x00,0x07,0x00, // 22 " would find on a HD44780 based character LCD.
0x14,0x7f,0x14,0x7f,0x14, // 23 # Also, given the size of the LCD (84 pixels by 48 pixels),
0x24,0x2a,0x7f,0x2a,0x12, // 24 $ the maximum number of characters per row is only 14.
0x23,0x13,0x08,0x64,0x62, // 25 %
0x36,0x49,0x55,0x22,0x50, // 26 &
0x00,0x05,0x03,0x00,0x00, // 27 '
0x00,0x1c,0x22,0x41,0x00, // 28 (
0x00,0x41,0x22,0x1c,0x00, // 29 )
0x14,0x08,0x3e,0x08,0x14, // 2a *
0x08,0x08,0x3e,0x08,0x08, // 2b +
0x00,0x50,0x30,0x00,0x00, // 2c ,
0x08,0x08,0x08,0x08,0x08, // 2d -
0x00,0x60,0x60,0x00,0x00, // 2e .
0x20,0x10,0x08,0x04,0x02, // 2f /
0x3e,0x51,0x49,0x45,0x3e, // 30 0
0x00,0x42,0x7f,0x40,0x00, // 31 1
0x42,0x61,0x51,0x49,0x46, // 32 2
0x21,0x41,0x45,0x4b,0x31, // 33 3
0x18,0x14,0x12,0x7f,0x10, // 34 4
0x27,0x45,0x45,0x45,0x39, // 35 5
0x3c,0x4a,0x49,0x49,0x30, // 36 6
0x01,0x71,0x09,0x05,0x03, // 37 7
0x36,0x49,0x49,0x49,0x36, // 38 8
0x06,0x49,0x49,0x29,0x1e, // 39 9
0x00,0x36,0x36,0x00,0x00, // 3a :
0x00,0x56,0x36,0x00,0x00, // 3b ;
0x08,0x14,0x22,0x41,0x00, // 3c <
0x14,0x14,0x14,0x14,0x14, // 3d =
0x00,0x41,0x22,0x14,0x08, // 3e >
0x02,0x01,0x51,0x09,0x06, // 3f ?
0x32,0x49,0x79,0x41,0x3e, // 40 @
0x7e,0x11,0x11,0x11,0x7e, // 41 A
0x7f,0x49,0x49,0x49,0x36, // 42 B
0x3e,0x41,0x41,0x41,0x22, // 43 C
0x7f,0x41,0x41,0x22,0x1c, // 44 D
0x7f,0x49,0x49,0x49,0x41, // 45 E
0x7f,0x09,0x09,0x09,0x01, // 46 F
0x3e,0x41,0x49,0x49,0x7a, // 47 G
0x7f,0x08,0x08,0x08,0x7f, // 48 H
0x00,0x41,0x7f,0x41,0x00, // 49 I
0x20,0x40,0x41,0x3f,0x01, // 4a J
0x7f,0x08,0x14,0x22,0x41, // 4b K
0x7f,0x40,0x40,0x40,0x40, // 4c L
0x7f,0x02,0x0c,0x02,0x7f, // 4d M
0x7f,0x04,0x08,0x10,0x7f, // 4e N
0x3e,0x41,0x41,0x41,0x3e, // 4f O
0x7f,0x09,0x09,0x09,0x06, // 50 P
0x3e,0x41,0x51,0x21,0x5e, // 51 Q
0x7f,0x09,0x19,0x29,0x46, // 52 R
0x46,0x49,0x49,0x49,0x31, // 53 S
0x01,0x01,0x7f,0x01,0x01, // 54 T
0x3f,0x40,0x40,0x40,0x3f, // 55 U
0x1f,0x20,0x40,0x20,0x1f, // 56 V
0x3f,0x40,0x38,0x40,0x3f, // 57 W
0x63,0x14,0x08,0x14,0x63, // 58 X
0x07,0x08,0x70,0x08,0x07, // 59 Y
0x61,0x51,0x49,0x45,0x43, // 5a Z
0x00,0x7f,0x41,0x41,0x00, // 5b [
0x02,0x04,0x08,0x10,0x20, // 5c Yen Currency Sign
0x00,0x41,0x41,0x7f,0x00, // 5d ]
0x04,0x02,0x01,0x02,0x04, // 5e ^
0x40,0x40,0x40,0x40,0x40, // 5f _
0x00,0x01,0x02,0x04,0x00, // 60 `
0x20,0x54,0x54,0x54,0x78, // 61 a
0x7f,0x48,0x44,0x44,0x38, // 62 b
0x38,0x44,0x44,0x44,0x20, // 63 c
0x38,0x44,0x44,0x48,0x7f, // 64 d
0x38,0x54,0x54,0x54,0x18, // 65 e
0x08,0x7e,0x09,0x01,0x02, // 66 f
0x0c,0x52,0x52,0x52,0x3e, // 67 g
0x7f,0x08,0x04,0x04,0x78, // 68 h
0x00,0x44,0x7d,0x40,0x00, // 69 i
0x20,0x40,0x44,0x3d,0x00, // 6a j
0x7f,0x10,0x28,0x44,0x00, // 6b k
0x00,0x41,0x7f,0x40,0x00, // 6c l
0x7c,0x04,0x18,0x04,0x78, // 6d m
0x7c,0x08,0x04,0x04,0x78, // 6e n
0x38,0x44,0x44,0x44,0x38, // 6f o
0x7c,0x14,0x14,0x14,0x08, // 70 p
0x08,0x14,0x14,0x18,0x7c, // 71 q
0x7c,0x08,0x04,0x04,0x08, // 72 r
0x48,0x54,0x54,0x54,0x20, // 73 s
0x04,0x3f,0x44,0x40,0x20, // 74 t
0x3c,0x40,0x40,0x20,0x7c, // 75 u
0x1c,0x20,0x40,0x20,0x1c, // 76 v
0x3c,0x40,0x30,0x40,0x3c, // 77 w
0x44,0x28,0x10,0x28,0x44, // 78 x
0x0c,0x50,0x50,0x50,0x3c, // 79 y
0x44,0x64,0x54,0x4c,0x44, // 7a z
0x00,0x08,0x36,0x41,0x00, // 7b <
0x00,0x00,0x7f,0x00,0x00, // 7c |
0x00,0x41,0x36,0x08,0x00, // 7d >
0x10,0x08,0x08,0x10,0x08, // 7e Right Arrow ->
0x78,0x46,0x41,0x46,0x78, // 7f Left Arrow <-
0x00,0x06,0x09,0x09,0x06};  // 80 °

///////////////////////////
//     DECLARATIONS
///////////////////////////
//
//I²C
void TWIInit(void);
void TWIStart(void);
void TWIStop(void);
uint8_t TWIReadACK(void);
uint8_t TWIReadNACK(void);
uint8_t TWIGetStatus(void);

//OLED
void oled_command(int value);
void oled_init(void);
void oled_write_byte(int col, int page, int val);
void oled_cls(void);
void oled_putchar1(int, int, char, int);
void oled_putchar2(int, int, char, int);
void oled_putstring(int, int, char *, char, int);
void oled_putnumber(int, int, long, int, int, int);
void oled_clear_section(int, int, int);
int xp2(int xp);

//String
int int2asc(long num, int dec, char *buf, int buflen);
int strlen(char *s);

//Data display functions
void show_frequency(long);
void show_frequency_cls(void);
void show_tstep(int, int);
void show_sideband(int, int);
void show_voltage(int);
void show_meter(int);
void draw_meter_scale(int meter_type);
void show_store(int, int, unsigned long);
void show_txrx(int);
void show_split(int);
void show_split2(int);
void show_rcl(int);


//ADC
int get_adc(int);
int get_keys(void);

//EEPROM
void store_frequency(unsigned long);
unsigned long load_frequency(void);

//MISC
int main(void);
unsigned int menu(void);
int set_lo_frequencies(void);

/////////////////////////////////////
//   Global constants & variables
/////////////////////////////////////
int laststate = 0; //Last state of rotary encoder
int tuningknob = 0;
int split = 0;
	
//Tuning steps available
#define MAXTSTEPS 6 //Highest index of tsteps = n-1
#define STDTSTEP 2 //Standard tuning step
int n_tstep[] = {10, 50, 100, 250, 500, 1000, 5000};
int cur_tstep = STDTSTEP;

//SIDEBAND
#define MAXMODES 2
int sideband = 0;  //Current sideband in use USB=0, LSB=1
unsigned long f_bfo[] = {10697600, 10692700}; //LO FREQUENCIES 10.7MHz

//METER
int sv_old = 0;

///////////////////////////
//
//         TWI
//
///////////////////////////

void twi_init(void)
{
    //set SCL to 400kHz
    TWSR = 0x00;
    TWBR = 0x0C;
	
    //enable TWI
    TWCR = (1<<TWEN);
}

//Send start signal
void twi_start(void)
{
    TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
    while ((TWCR & (1<<TWINT)) == 0);
}

//send stop signal
void twi_stop(void)
{
    TWCR = (1<<TWINT)|(1<<TWSTO)|(1<<TWEN);
}

void twi_write(uint8_t u8data)
{
    TWDR = u8data;
    TWCR = (1<<TWINT)|(1<<TWEN);
    while ((TWCR & (1<<TWINT)) == 0);
}

uint8_t TWIGetStatus(void)
{
    uint8_t status;
    //mask status
    status = TWSR & 0xF8;
    return status;
}

////////////////////////////////
//
// Si5351A commands
//
///////////////////////////////
void si5351_write(int reg_addr, int reg_value)
{
   	 
   twi_start();
   twi_write(SI5351_ADDRESS);
   twi_write(reg_addr);
   twi_write(reg_value);
   twi_stop();
} 

// Set PLLs (VCOs) to internal clock rate of 900 MHz
// Equation fVCO = fXTAL * (a+b/c) (=> AN619 p. 3
void si5351_start(void)
{
  unsigned long a, b, c;
  unsigned long p1, p2;//, p3;
  
  
  // Init clock chip
  si5351_write(XTAL_LOAD_CAP, 0xD2);      // Set crystal load capacitor to 10pF (default), 
                                          // for bits 5:0 see also AN619 p. 60
  si5351_write(CLK_ENABLE_CONTROL, 0x00); // Enable all outputs
  si5351_write(CLK0_CONTROL, 0x0F);       // Set PLLA to CLK0, 8 mA output
  si5351_write(CLK1_CONTROL, 0x2F);       // Set PLLB to CLK1, 8 mA output
  si5351_write(CLK2_CONTROL, 0x2F);       // Set PLLB to CLK2, 8 mA output
  si5351_write(PLL_RESET, 0xA0);          // Reset PLLA and PLLB

  // Set VCOs of PLLA and PLLB to 900 MHz
  a = 36;           // Division factor 900/25 MHz
  b = 0;            // Numerator, sets b/c=0
  c = 1048757;      //Max. resolution, but irrelevant in this case (b=0)

  //Formula for splitting up the numbers to register data, see AN619
  p1 = 128 * a + (unsigned long) (128 * b / c) - 512;
  p2 = 128 * b - c * (unsigned long) (128 * b / c);
  //p3  = c;

  
  //Write data to registers PLLA and PLLB so that both VCOs are set to 900MHz intermal freq
  si5351_write(SYNTH_PLL_A, 0xFF);
  si5351_write(SYNTH_PLL_A + 1, 0xFF);
  si5351_write(SYNTH_PLL_A + 2, (p1 & 0x00030000) >> 16);
  si5351_write(SYNTH_PLL_A + 3, (p1 & 0x0000FF00) >> 8);
  si5351_write(SYNTH_PLL_A + 4, (p1 & 0x000000FF));
  si5351_write(SYNTH_PLL_A + 5, 0xF0 | ((p2 & 0x000F0000) >> 16));
  si5351_write(SYNTH_PLL_A + 6, (p2 & 0x0000FF00) >> 8);
  si5351_write(SYNTH_PLL_A + 7, (p2 & 0x000000FF));

  si5351_write(SYNTH_PLL_B, 0xFF);
  si5351_write(SYNTH_PLL_B + 1, 0xFF);
  si5351_write(SYNTH_PLL_B + 2, (p1 & 0x00030000) >> 16);
  si5351_write(SYNTH_PLL_B + 3, (p1 & 0x0000FF00) >> 8);
  si5351_write(SYNTH_PLL_B + 4, (p1 & 0x000000FF));
  si5351_write(SYNTH_PLL_B + 5, 0xF0 | ((p2 & 0x000F0000) >> 16));
  si5351_write(SYNTH_PLL_B + 6, (p2 & 0x0000FF00) >> 8);
  si5351_write(SYNTH_PLL_B + 7, (p2 & 0x000000FF));

}

void si5351_set_freq(int synth, unsigned long freq)
{
  unsigned long  a, b, c = 1048575;
  unsigned long f_xtal = 25000000;
  double fdiv = (double) (f_xtal * 36) / freq; //division factor fvco/freq (will be integer part of a+b/c)
  double rm; //remainder
  unsigned long p1, p2;
  
  a = (unsigned long) fdiv;
  rm = fdiv - a;  //(equiv. to fractional part b/c)
  b = rm * c;
  p1  = 128 * a + (unsigned long) (128 * b / c) - 512;
  p2 = 128 * b - c * (unsigned long) (128 * b / c);
    
  //Write data to multisynth registers of synth n
  si5351_write(synth, 0xFF);      //1048757 MSB
  si5351_write(synth + 1, 0xFF);  //1048757 LSB
  si5351_write(synth + 2, (p1 & 0x00030000) >> 16);
  si5351_write(synth + 3, (p1 & 0x0000FF00) >> 8);
  si5351_write(synth + 4, (p1 & 0x000000FF));
  si5351_write(synth + 5, 0xF0 | ((p2 & 0x000F0000) >> 16));
  si5351_write(synth + 6, (p2 & 0x0000FF00) >> 8);
  si5351_write(synth + 7, (p2 & 0x000000FF));
}

void set_oscillators(int txrx, unsigned long vfo_freq, int bfo)
{
    if(txrx) //TX, cause PIND1 is 1
	{
	    si5351_set_freq(SYNTH_MS_0, vfo_freq + f_bfo[bfo]);		   
		si5351_set_freq(SYNTH_MS_1, f_bfo[bfo]);
	}	
	else //RX
	{
	   si5351_set_freq(SYNTH_MS_0, f_bfo[bfo]);
	   si5351_set_freq(SYNTH_MS_1, vfo_freq + f_bfo[bfo]);   
	}    	
		
}

unsigned long scan_frequency(unsigned long f1, unsigned long f2, int ts, int bfo)     
{
     unsigned long f;
     int key;
     int sval = 0;
     
     if(f1 == f2)
     {
		 return 0;
	 }	 
         
     if(f1 > f2)
     {
		 f = f1;
		 f1 = f2;
		 f2 = f;
	 }	 
          
     while(1)
     {
         for(f = f1; f <= f2; f += ts)
         {
		     set_oscillators(0, f, bfo);
		     show_frequency(f);
		     key = get_keys();
		     if(key == 2)
		     {
				 return f;
		     }	 
		     
		     if(key == 1)
		     {
				 return 0;
		     }	 
		     
		     sval = 390 - get_adc(1); //ADC voltage on ADC3 SVAL
		 	 show_meter(sval >> 2); //S-Meter
		 	 
		 	 //Hold, when S value hi!
		 	 if((sval >> 2) > 60)
		 	 {
				 _delay_ms(200);
			 }	 
		 	 

		 }    
	 }	 
	 return 0;
}

////////////////////////////////
//
// OLED commands
//
///////////////////////////////

//Send comand to OLED
void oled_command(int value)
{
   twi_start();
   twi_write(0x78); //Device address
   twi_write(0x00);
   twi_write(value);
   twi_stop();
} 

//Initialize OLED
void oled_init(void)
{
    oled_command(0xae);
	
    oled_command(0xa8);//Multiplex ratio
    oled_command(0x3F);
	
    oled_command(0xd3);
    oled_command(0x00);
    oled_command(0x40);
    oled_command(0xa0);
    oled_command(0xa1);
	
	oled_command(0x20); //Adressing mode
    oled_command(0x00); //HOR
	
    oled_command(0xc0);
    oled_command(0xc8);
    oled_command(0xda);
    oled_command(0x12);
    oled_command(0x81);
    oled_command(0xfF);
    oled_command(0xa4); //Display ON with RAM content
    oled_command(0xa6); //Normal display (Invert display = A7)
    oled_command(0xd5);
    oled_command(0x80);
    oled_command(0x8d);
    oled_command(0x14);
	//oled_command(0x40); //Set display start line
    oled_command(0xAF); //Display ON
   
} 

//Print character in normal size
//Write 8 vertical bits to given col (0..127) and page (0..7)
void oled_write_byte(int col, int page, int val)
{
    int t1;
	
	oled_command(0x21); //COL
	oled_command(col); 
	oled_command(col); 
	
	oled_command(0x22); //PAGE
	oled_command(page); 
	oled_command(page); 
		
    twi_start();
	twi_write(0x78);
	twi_write(0x40); //Data
    for(t1 = 0; t1 < 7; t1++)
    {
        twi_write(val);
    }
	twi_stop();
}

//Clear screen
void oled_cls(void)
{

    int x, y;
	for(x = 0; x < 128; x++)
	{
	    for(y = 0; y < 8; y++)
		{
		    oled_write_byte(x, y, 0);
		}
    }	

}

//Print one character in normal size to OLED
void oled_putchar1(int col, int row, char ch1, int inv)
{ 
    int p, t1;
    char ch2;
	int c = col;
	    
    p = (5 * ch1) - 160;
    for(t1 = 0; t1 < 5; t1++)
    { 
	    if(!inv)
		{
	        ch2 = fontchar[p + t1];
		}
		else
		{
	        ch2 = ~fontchar[p + t1];
		}
		
        oled_write_byte(c++, row, ch2);
    }
	
	if(!inv)
	{
	    oled_write_byte(c, row, 0x00);
	}
	else
	{
	    oled_write_byte(c, row, 0xFF);
	}
    
}

//2^x
int xp2(int xp)
{
    int t1, r = 1;
    for(t1 = 0; t1 < xp; t1++)
    {
        r <<= 1;
    }
    return r;

}


//Print character in double size
void oled_putchar2(int col, int row, char ch1, int inv)
{ 
    int p, t1, t2, x;
	int b, b1, b2; 
    char colval;
	   
    p = (5 * ch1) - 160;
    	
	for(t2 = 0; t2 < 6; t2++)
    { 
	    //Get vertical byte data of char
		if(!inv)
		{
	        colval = fontchar[p];
			if(t2 == 5)
			{
			    colval = 0;
			}
		}
		else
		{
	        colval = ~fontchar[p];
			if(t2 == 5)
			{
			    colval = 255;
			}
		}
	  		
		b = 0;
		x = 1;
        
		for(t1 = 0; t1 < 7; t1++)
        {
            if(colval & x)
            {
	            b += xp2(t1 * 2);
	            b += xp2(t1 * 2 + 1);
            }
            x <<= 1;
	    }
    
        b1 = b & 0xFF; //Lower byte
        b2 = (b & 0xFF00) >> 8; //Upper byte
		
		//Print data to screen
		oled_write_byte(col + t2 * 2, row, b1);
		oled_write_byte(col + t2 * 2, row + 1, b2);
		oled_write_byte(col + t2 * 2 + 1, row, b1);
		oled_write_byte(col + t2 * 2 + 1, row + 1, b2);
		p++;
	}	
	
}

//Print string in given size
//lsize=0 => normal height, lsize=1 => double height
void oled_putstring(int col, int row, char *s, char lsize, int inv)
{
    int c = col;
	
	while(*s)
	{
	    if(!lsize)
		{
	        oled_putchar1(c, row, *s++, inv);
		}
        else
        {
            oled_putchar2(c, row, *s++, inv);
		}	
		c += (lsize + 1) * 6;
	}
}

//Print an integer/long to OLED
void oled_putnumber(int col, int row, long num, int dec, int lsize, int inv)
{
    char *s = malloc(16);
	if(s != NULL)
	{
	    int2asc(num, dec, s, 16);
	    oled_putstring(col, row, s, lsize, inv);
	    free(s);
	}	
}

void oled_clear_section(int x1, int x2, int row)
{
    int t1;
	for(t1 = x1; t1 < x2; t1++)
	{
	    oled_write_byte(t1, row, 0);	
	}

}

/////////////////////////////////
//
// STRING FUNCTIONS
//
////////////////////////////////
//INT 2 ASC
int int2asc(long num, int dec, char *buf, int buflen)
{
    int i, c, xp = 0, neg = 0;
    long n, dd = 1E09;

    if(!num)
	{
	    *buf++ = '0';
		*buf = 0;
		return 1;
	}	
		
    if(num < 0)
    {
     	neg = 1;
	    n = num * -1;
    }
    else
    {
	    n = num;
    }

    //Fill buffer with \0
    for(i = 0; i < 12; i++)
    {
	    *(buf + i) = 0;
    }

    c = 9; //Max. number of displayable digits
    while(dd)
    {
	    i = n / dd;
	    n = n - i * dd;
	
	    *(buf + 9 - c + xp) = i + 48;
	    dd /= 10;
	    if(c == dec && dec)
	    {
	        *(buf + 9 - c + ++xp) = '.';
	    }
	    c--;
    }

    //Search for 1st char different from '0'
    i = 0;
    while(*(buf + i) == 48)
    {
	    *(buf + i++) = 32;
    }

    //Add minus-sign if neccessary
    if(neg)
    {
	    *(buf + --i) = '-';
    }

    //Eleminate leading spaces
    c = 0;
    while(*(buf + i))
    {
	    *(buf + c++) = *(buf + i++);
    }
    *(buf + c) = 0;
	
	return c;
}

//STRLEN
int strlen(char *s)
{
   int t1 = 0;

   while(*(s + t1++));

   return (t1 - 1);
}

/****************************/
//
//  DATA DISPLAY FUNCTIONS
//
/****************************/

//Current frequency (double letter height)
void show_frequency(long f)
{
    oled_putnumber(15, 3, (long) f / 10, 2, 1, 0);
}

void show_frequency_cls(void)
{
    oled_clear_section(0, 127, 3);
    oled_clear_section(0, 127, 4);
}

//Current tuning step (Page 0)
void show_tstep(int n_step, int invert)
{
	int xpos = 0, xlen = 5;
    char *s_tstep[] = {" 10Hz", " 50Hz", "100Hz", "250Hz", "500Hz", " 1kHz", " 5kHz"};
		
	//Clear section of LCD
	oled_clear_section(xpos * 6, xpos + xlen * 6, 0);
	
	//Write string to position
	oled_putstring(0, 0, s_tstep[n_step], 0, invert);
}

void show_sideband(int sb, int invert)
{
	int xpos = 6, xlen = 3;
	char *sidebandstr[MAXMODES + 1] = {"USB", "LSB"};
		
	//Clear section of LCD
	oled_clear_section(xpos * 6 , xpos + xlen * 6, 0);
	
	//Write string to position
	oled_putstring(xpos * 6, 0, sidebandstr[sb], 0, invert);
}

void show_store(int done, int invert, unsigned long f)
{
	int xpos = 10;	
	if(done)
	{
		oled_putnumber(62, 5, (long) f / 10, 2, 0, 0);
		oled_putstring(xpos * 6, 0, "STO", 0, invert);
		return;
	}	
	oled_putstring(xpos * 6, 0, "STO", 0, invert);
}

void show_voltage(int v1)
{
    char *buf;
	int t1, p;
	
	oled_clear_section(0, 30, 1);
	
	buf = malloc(10);
	//Init buffer string
	for(t1 = 0; t1 < 10; t1++)
	{
	    *(buf + t1) = 0;
	}
    p = int2asc(v1, 1, buf, 6) * 6;
    oled_putstring(0, 1, buf, 0, 0);
	oled_putchar1(p, 1, 'V', 0);
	free(buf);
}

void show_txrx(int status)
{
	int xpos = 19;
	
	//Write string to position
	if(status)
	{
	    oled_putstring(xpos * 6, 1, "TX", 0, 1);
	}
	else    
	{
	    oled_putstring(xpos * 6, 1, "RX", 0, 0);
	}
	
}	
//S-Meter bargraph (Page 6)
void show_meter(int sv0)
{
    int t1, sv;
	
	sv = sv0;
	
    if(sv > 120)
	{
	    sv = 120;
	}
		
	//Clear bar graph partly, when new s-val smaller than previous
    if(sv < sv_old)
    {
	     //Clear s-meter
	     for(t1 = sv; t1 < 128; t1 += 3)//
	     {
	          oled_write_byte(t1, 6, 0);
	     }	
         sv_old = sv;
         return;
    }
   
    //Draw bar graph
	for(t1 = 0; t1 < sv; t1 += 3)//
	{
	    oled_write_byte(t1, 6, 0x1E);
	}	
    
    sv_old = sv;
}

void draw_meter_scale(int meter_type)
{
    if(!meter_type)
    {
        oled_putstring(0, 7, "S1 S3 S5 S7 S9 +  ", 0, 0);
    }
    else
    {
        oled_putstring(0, 7, "0  1W   2W   3W   ", 0, 0);
    }
}

void show_split(int stat)
{
	int xpos = 18, xlen = 5;
			
	//Clear section of LCD
	oled_clear_section(xpos * 6 , xpos + xlen * 6, 0);
	
	//Write string to position
	oled_putstring(xpos * 6, 0, "SPL", 0, stat);
	
}

void show_split2(int stat)
{
	int xpos = 6, xlen = 5;
			
	//Clear section of LCD
	oled_clear_section(xpos * 6 , xpos + xlen * 6, 1);
	
	//Write string to position
	if(stat)
	{
	    oled_putstring(xpos * 6, 1, "SPLIT", 0, 1);
	}
	else
	{
		oled_putstring(xpos * 6, 1, "     ", 0, 0);    
	}	
	
}

void show_rcl(int stat)
{
	int xpos = 14, xlen = 3;
			
	//Clear section of LCD
	oled_clear_section(xpos * 6 , xpos + xlen * 6, 0);
	
	//Write string to position
	oled_putstring(xpos * 6, 0, "RCL", 0, stat);
	
}

void show_scan(int stat)
{

	int xpos = 13, xlen = 3;
			
	//Clear section of LCD
	oled_clear_section(xpos * 6 , xpos + xlen * 6, 1);
	
	//Write string to position
	oled_putstring(xpos * 6, 1, "SCAN", 0, stat);
	
}

////////////////////////////////////////////////////
//               INTERRUPT HANDLERS
////////////////////////////////////////////////////
//Rotary encoder
ISR(PCINT2_vect)
{ 
    
    int gray = (PIND & 0x60) >> 5;           // Read PD5 and PD6
	
    int state = (gray >> 1) ^ gray;         // Convert from Gray code to binary

    if (state != laststate)                //Compare states
    {        
        tuningknob += ((laststate - state) & 3) - 2; // Results in -1 or +1
        laststate = state;
    } 
	PCIFR |=  (1 << PCIF0); // Clear pin change interrupt flag.
}

//////////////////////
//
//   A   D   C   
//
/////////////////////
//Read ADC value
int get_adc(int adc_channel)
{
	
	int adc_val = 0;
	
	ADMUX = (1<<REFS0) + adc_channel;     // Kanal adcmode aktivieren
    _delay_ms(3);
	
    ADCSRA |= (1<<ADSC);
    //while(ADCSRA & (1<<ADSC));
	_delay_ms(3);
	
	adc_val = ADCL;
    adc_val += ADCH * 256;   
	
	return adc_val;
	
}	

//Read keys via ADC0
int get_keys(void)
{

    int key_value[] = {151, 241};  //PCB
    //151 left single push button #1, 241 = right button from rotator switch #2, 
    	
    int t1;
    int adcval = get_adc(0);
        
    //TEST display of ADC value 
    //oled_putstring(0, 5, "----", 0, 0);    
    //oled_putnumber(0, 5, adcval, -1, 0, 0);    
    
    if(adcval < 110 && adcval > 106) //Both buttons pressed simultanously PCB
    {
		return 99;
	}
		
    for(t1 = 0; t1 < 2; t1++)
    {
        if(adcval > key_value[t1] - 10 && adcval < key_value[t1] + 10)
        {
             return t1 + 1;
        }
    }
    return 0;
}

//////////////////////////////
//
//    EEPROM-Functions
//
//////////////////////////////

//Load and store frequency from EEPROM based on VFO
unsigned long load_frequency(void)
{
    unsigned long rf;
    unsigned char hmsb, lmsb, hlsb, llsb;
    int start_adr = 0;
		
    cli();
    hmsb = eeprom_read_byte((uint8_t*)start_adr);
    hlsb = eeprom_read_byte((uint8_t*)start_adr + 1);
    lmsb = eeprom_read_byte((uint8_t*)start_adr + 2);
    llsb = eeprom_read_byte((uint8_t*)start_adr + 3);
	sei();
	
    rf = (unsigned long) 16777216 * hmsb + (unsigned long) 65536 * hlsb + (unsigned int) 256 * lmsb + llsb;
		
	return rf;
	
}


void store_frequency(unsigned long f)
{
    unsigned long hiword, loword;
    unsigned char hmsb, lmsb, hlsb, llsb;
	
    int start_adr = 0;
    
	cli();
    hiword = f >> 16;
    loword = f - (hiword << 16);
    hmsb = hiword >> 8;
    hlsb = hiword - (hmsb << 8);
    lmsb = loword >> 8;
    llsb = loword - (lmsb << 8);

    while(!eeprom_is_ready());
    eeprom_write_byte((uint8_t*)start_adr, hmsb);

    while(!eeprom_is_ready());
    eeprom_write_byte((uint8_t*)start_adr + 1, hlsb);

    while(!eeprom_is_ready());
    eeprom_write_byte((uint8_t*)start_adr + 2, lmsb);

    while(!eeprom_is_ready());
    eeprom_write_byte((uint8_t*)start_adr + 3, llsb);
    
    sei();	
	
}

//////////////////////////////
//
//    M   E   N   U
//
//////////////////////////////
unsigned int menu(void)
{
	int key = 0;
    int store = 0;
    int rcl = 0;	
    int split2;	
    int scan2 = 0;
	while(get_keys());
	show_tstep(cur_tstep, 1);
	
	//TUNING STEP
    while(!key)
	{
		if(tuningknob > 2)  
		{    
		    if(cur_tstep < MAXTSTEPS)
		    {
			     cur_tstep++;
			}
			else     
			{
				 cur_tstep = 0;
			}	 
			tuningknob = 0;
			show_tstep(cur_tstep, 1);
		}
		
		if(tuningknob < -2 )
		{
		    if(cur_tstep > 0)
		    {
			     cur_tstep--;
			}
			else     
			{
				 cur_tstep = MAXTSTEPS;
			}	 
			tuningknob = 0;
			show_tstep(cur_tstep, 1);
		}
		key = get_keys();
	}	
	show_tstep(cur_tstep, 0);
	
	if(key == 2)
	{
		return 1;
	}
	while(get_keys());
	//////////////////////////
			
	//SIDEBAND SET	
	key = 0;	
	show_sideband(sideband, 1);
	while(!key)
	{
		if(tuningknob > 2 || tuningknob < -2)  
		{    
		    if(!sideband)
		    {
			     sideband = 1;
			}
			else     
			{
				 sideband = 0;
			}	 
			tuningknob = 0;
			show_sideband(sideband, 1);
			si5351_set_freq(SYNTH_MS_0, f_bfo[sideband]);
		}
		key = get_keys();
	}	
	show_sideband(sideband, 0);
	
	if(key == 2)
	{
		return 2;
	}	
	while(get_keys());
	////////////////////////////
		
	//STORE CURRENT QRG
	key = 0;
	store = 1;	
	show_store(0, store, 0); 
	while(!key)
	{
		if(tuningknob > 2 || tuningknob < -2)  
		{    
		    if(!store)
		    {
			     store = 1;
			}
			else     
			{
				 store = 0;
			}	 
			tuningknob = 0;
			show_store(0, store, 0);
		}
		key = get_keys();
	}	
	
	if(key == 2)
	{
	    return 4;
	}	
	
	show_store(0, 0, 0);	
	while(get_keys());
    ////////////////////////
    
	//Recall stored freuency
	key = 0;
	rcl = 1;
	show_rcl(rcl); 
	while(!key)
	{
		if(tuningknob > 2 || tuningknob < -2)  
		{    
		    if(!rcl)
		    {
			     rcl = 1;
			}
			else     
			{
				 rcl = 0;
			}	 
			tuningknob = 0;
			show_rcl(rcl); 
		}
		key = get_keys();
	}	
	
	show_rcl(0); 
	
	if(key == 1)
	{
		rcl = 0;
	}
			
	if(key == 2 && rcl)
	{
		return 6;
	}	
	while(get_keys());
    //////////////////////
		
	//SPLITMODE on/off
	key = 0;
	split2 = 1;
	show_split(split2); 
	while(!key)
	{
		if(tuningknob > 2 || tuningknob < -2)  
		{    
		    if(!split2)
		    {
			     split2 = 1;
			}
			else     
			{
				 split2 = 0;
			}	 
			tuningknob = 0;
			show_split(split2);
		}
		key = get_keys();
	}	
	
	show_split(0);  
	
	if(key == 2)
	{
	    if(split2)
	    {
			split = 1;
			show_split2(split);  
		    return 5;
	    }	
	    else
	    {
			split = 0;
			show_split2(split);  
	        return 0;
	    }	
    }	
    
    while(get_keys());
	
	
	//SCAN on/off
	key = 0;
	scan2 = 1;
	show_scan(scan2); 
	while(!key)
	{
		if(tuningknob > 2 || tuningknob < -2)  
		{    
		    if(!scan2)
		    {
			     scan2 = 1;
			}
			else     
			{
				 scan2 = 0;
			}	 
			tuningknob = 0;
			show_scan(scan2);
		}
		key = get_keys();
	}	
		
		
	if(key == 2)
	{
	    if(scan2)
	    {
			return 7;
	    }	
	    else
	    {
			return 0;
	    }	
    }	
    
    while(get_keys());
		
	return 0;
		
}	

int set_lo_frequencies(void)
{
	int key = 0;
		
    //LO FREQ USB
	key = 0;	
	show_frequency(f_bfo[0]);
	oled_putstring(1, 5, "fBFO USB", 0, 0);
	
	while(!key)
	{
		if(tuningknob > 2)  
		{    
		    f_bfo[0] += n_tstep[cur_tstep];
		    tuningknob = 0;
			show_frequency(f_bfo[0]);
		}	
		
		if(tuningknob < -2)  
		{    
		    f_bfo[0] -= n_tstep[cur_tstep];
			tuningknob = 0;
			show_frequency(f_bfo[0]);
		}	
		
		if(PIND & 0x02) //TX, cause PIND1 is 1
		{
			set_oscillators(1, 14200000, 0);
		    //si5351_set_freq(SYNTH_MS_0, 14200000 + f_bfo[0]);		   
			//si5351_set_freq(SYNTH_MS_1, f_bfo[0]);
		}	
		else
		{
			set_oscillators(0, 14200000, 0);
		    //si5351_set_freq(SYNTH_MS_0, f_bfo[0]);
		    //si5351_set_freq(SYNTH_MS_1, 14200000 + f_bfo[0]);   
		}    
	    key = get_keys();    
	}	
	
	while(get_keys());
		
	//LO FREQ LSB
	key = 0;
	si5351_set_freq(SYNTH_MS_0, f_bfo[1]);	
	show_frequency(f_bfo[1]);
	oled_putstring(1, 5, "fBFO LSB", 0, 0);
	
	while(!key)
	{
		if(tuningknob > 2)  
		{    
		    f_bfo[1] += n_tstep[cur_tstep];
		    tuningknob = 0;
			show_frequency(f_bfo[1]);
		}	
		
		if(tuningknob < -2)  
		{    
		    f_bfo[1] -= n_tstep[cur_tstep];
			tuningknob = 0;
			show_frequency(f_bfo[1]);
		}	
		
		if(PIND & 0x02) //TX, cause PIND1 is 1
		{
			set_oscillators(1, 14200000, 1);
		    //si5351_set_freq(SYNTH_MS_0, 14200000 + f_bfo[1]);		   
			//si5351_set_freq(SYNTH_MS_1, f_bfo[1]);
		}	
		else
		{
			 set_oscillators(0, 14200000, 1);
		     //si5351_set_freq(SYNTH_MS_0, f_bfo[1]);
		     //si5351_set_freq(SYNTH_MS_1, 14200000 + f_bfo[1]);   
		}    
	    key = get_keys();
	}	
			
	while(get_keys());
    oled_putstring(1, 5, "        ", 0, 0);
    return 0;
}

int main(void)
{
	//Keys
	int key = 0, key_old = 0;
	int loopcnt3 = 0;
	
	//Volts measurement
    int adc_v;
    int adc_v_old = 0;
    int v_cnt = 1000;
    double v1;		
        
    //Meter
    int loopcnt1 = 0;
    int sval = 0;
            
    //OPerating frequencies    
    unsigned long freq1 = 14200000;
    unsigned long freq2 = 14200000;
	unsigned long f_scan = 0;
	
	//TX/RX indicator
	int txrx = 0;
	
	//Menu value
	int men = 0;
		
	//INPUT
    PORTC = 0x31;//PC0: Pull-up for key switches with various resistors against GND 
	             //I²C-Bus lines: PC4=SDA, PC5=SCL
	
	PORTD = 0x60;//INPUT: Pullup resistors for PD0 (TX/RX), PD5 and PD6 rotary encoder
					  
	//Watchdog off
	WDTCSR = 0;
	WDTCSR = 0;
    _delay_ms(200);

	//Interrupt definitions for rotary encoder  
	PCMSK2 |= ((1<<PCINT21) | (1<<PCINT22));  //enable encoder pins as interrupt source
	PCICR |= (1<<PCIE2);                      // enable pin change interupts 
	
	//ADC config and ADC init
    ADCSRA = (1<<ADPS0) | (1<<ADPS1) | (1<<ADEN); //Prescaler 64 and ADC on
	get_adc(0); //One dummy conversion
	
	
	freq1 = load_frequency();
	if(freq1 < 14000000 || freq1 > 14350000)
	{
	    freq1 = 14200000;
	}
    freq2 = freq1;
    
	//TWI
	twi_init();
	
	//OLED
	oled_init();
	oled_cls();	

	//Si5351	
	_delay_ms(100);
	si5351_start();
	
    //Frequencies in NORMAL position
    set_oscillators(0, freq1, 0);
    
	//si5351_set_freq(SYNTH_MS_0, f_bfo[sideband]);
	//si5351_set_freq(SYNTH_MS_1, freq1 + f_bfo[sideband]);
		
	show_frequency(freq1);
	show_tstep(cur_tstep, 0);
	show_sideband(sideband, 0);
	draw_meter_scale(0);
    show_store(1, 0, freq1);
    show_store(0, 0, 0);
    show_txrx(0);
    show_split(0);
    show_rcl(0);
    show_scan(0);
    sei();        
    
    for(;;)
    {
        //TUNING		
		if(tuningknob > 2 && !txrx)  
		{    
		    freq1 += n_tstep[cur_tstep];
		    set_oscillators(txrx, freq1, sideband);
			//si5351_set_freq(SYNTH_MS_1, freq1 + f_bfo[sideband]);
			tuningknob = 0;
			show_frequency(freq1);
		}
		
		if(tuningknob < -2 && !txrx)
		{
		    freq1 -= n_tstep[cur_tstep];
		    set_oscillators(txrx, freq1, sideband);
			//si5351_set_freq(SYNTH_MS_1, freq1 + f_bfo[sideband]);
			tuningknob = 0;
			show_frequency(freq1);
		}
				
		//MENU
		if(key_old != key)
		{
			//oled_putstring(0, 20, "    ", 0, 0);
		    //oled_putnumber(0, 20, key, -1, 0, 0);
		    key_old = key;
		    while(get_keys());
		    switch(key)
		    {
				case 1: men = menu();
				        switch(men)
				        {
				            case 4: store_frequency(freq1); //Store QRG
					                show_store(1, 0, freq1);
					                show_store(0, 0, 0);
	                                show_frequency(freq1);				
                                    break;
                                    
                            case 5: if(load_frequency() >= 14000000 && load_frequency() <= 14350000) //Get split freqw
	                                {
	                                    freq2 = load_frequency();
					                }      
					                else
					                {
	                                    freq2 = freq1;
					                }      
					                break;
					                
					        case 6: if(load_frequency() >= 14000000 && load_frequency() <= 14350000) //RCL
					                {
										freq1 = load_frequency(); //Load stored QRG
										set_oscillators(txrx, freq1, sideband);
							            //si5351_set_freq(SYNTH_MS_1, freq1 + f_bfo[sideband]);
	                                    show_frequency(freq1);
	                                }    
	                                break;
	                                
	                        case 7: if(load_frequency() >= 14000000 && load_frequency() <= 14350000)
	                                {      
	                                    show_scan(1);
	                                    f_scan = scan_frequency(load_frequency(), freq1, n_tstep[cur_tstep], sideband);     
	                                    if(get_keys() == 2 && f_scan >= 14000000 && f_scan <= 14350000)
	                                    {
									        freq1 = f_scan;
									    }	
									    show_scan(0);
									    set_oscillators(txrx, freq1, sideband);	
									}
	                                break;
	                                
	                        case 99: set_lo_frequencies();
	                                 set_oscillators(txrx, freq1, sideband);
	                                 show_frequency_cls();
	                                 show_frequency(freq1);
	                                
	                    }            
	        }             
	        show_frequency(freq1);
		}
		
		//VOLTS
		//Voltage divider factor (7.5+2.56k)/2.56 = 3.941176471
		if(v_cnt++ > 5000)
		{
		    v1 = (double) get_adc(2) * 5 / 1024 * 3.94 * 10;
		    adc_v = (int) v1;
   		    if(adc_v != adc_v_old)
		    {
    	        show_voltage(adc_v);
	     		adc_v_old = adc_v;
		    }	
		    v_cnt = 0;
	    }
        
		//After n loops check S-Val resp. PWR value
		if(loopcnt1++ > 20)
		{
			//sval = 390 - get_adc(1); //ADC voltage on ADC1 SVAL
			//oled_putnumber(0, 5, 390 - get_adc(1), -1, 0, 0);
		 	//mval += get_adc(1); //ADC voltage on ADC1 PWR
		 	if(!txrx)
		 	{
				sval = 390 - get_adc(1); //ADC voltage on ADC3 SVAL
		 	    show_meter(sval >> 2); //S-Meter
		 	}
		 	else
		 	{
				sval = get_adc(3); //ADC voltage on ADC3 SVAL
				show_meter(sval); //S-Meter
				
			}    
 		 	loopcnt1 = 0;
		}
		
        //Check if key pressed
        if(loopcnt3++ > 50)
        {
		    key = get_keys();
		    loopcnt3 = 0;
		}    
		
		//TX/RX switching
		if(PIND & 0x02) //TX, cause PIND1 is 1
		{
			if(!txrx)
			{
			    draw_meter_scale(1);	 
			    show_meter(0);
			    show_txrx(1);
			    txrx = 1;
			    
			    //Frequencies in REVERSE position
			    if(split)
			    {
				    show_frequency(freq2);
				    set_oscillators(txrx, freq2, sideband);
					//si5351_set_freq(SYNTH_MS_0, freq2 + f_bfo[sideband]);
				}	 
				else
				{
					set_oscillators(txrx, freq1, sideband);
				    //si5351_set_freq(SYNTH_MS_0, freq1 + f_bfo[sideband]);	
				}
				
				//si5351_set_freq(SYNTH_MS_1, f_bfo[sideband]);
	            
	
			}     
		}	
		
		if(!(PIND & 0x02)) //RX, cause PIND1 is 0
		{
			if(txrx)
			{
				draw_meter_scale(0);
				show_meter(0);
			    show_txrx(0);
			    txrx = 0;
				//Frequencies in NORMAL position
				set_oscillators(txrx, freq1, sideband);
			    //si5351_set_freq(SYNTH_MS_0, f_bfo[sideband]);
  	            //si5351_set_freq(SYNTH_MS_1, freq1 + f_bfo[sideband]);
  	            if(split)
			    {
				    show_frequency(freq1);
				}    
				
			}     
		}
	}
	
	return 0;
}

Leave a Reply

Your email address will not be published. Required fields are marked *