/*****************************************************************/ /* 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; }