SSB QRP Transceiver

Schaltung eines QPR-Transceivers für das 20-Meter-Band (14 MHz). Copyright 2012 Peter Rachow (DK7IH)

Schaltplan (Schematic) QRP SSB Transceiver 20 Meter 14 MHz by DK7IH (Peter Rachow)
Schaltplan (Schematic) QRP SSB Transceiver 20 Meter 14 MHz by DK7IH (Peter Rachow)

C-Programm für den ATMega8 in der Frequenzaufbereitung:

/*****************************************************************/
 /* DDS mit ATMega 32 und AD 9835 */
 /* ************************************************************ */
 /* Mikrocontroller: ATMEL AVR ATmega32, 8 MHz */
 /* */
 /* Compiler: GCC (GNU AVR C-Compiler) */
 /* Autor: Peter Rachow */
 /* Letzte Aenderung: 05.12.2014 */
 /*****************************************************************/
/* PORTX |= 1; // Bit 0 setzen
 PORTX &= ~(1); // Bit 0 löschen
 */
/* PORTBELEGUNG */
 // LCD
 // RS = PB6
 // E = PB7
 // D4...D7 = PD4..PD7
 //
 // SPI
 // FSYNC = PB0
 // SDATA: = PB1
 // SCLK: = PB2
 //
 // VFO SET = PC2
 // TUNE UP= PC3
 // TUNE DOWN= PC4
 // Tuning Step = PC5
 /**********************/
#define MAXSHIFT 6
 #define MAXVFO 3
#include 
 #include 
 #include 
 #include
#include <avr/io.h>
 #include <avr/interrupt.h>
 #include <avr/sleep.h>
 #include <avr/eeprom.h>
 #include <util/delay.h>
#define SCLOCK 8 //Systemtakt in MHz
#define AD9835INIT 1 //AD9835 wird vollständig mit Freqeunz initalisiert
 #define AD9835UPDATE 0 //AD9835 wird nur "upgedated"
void wait_ms(int);
//Timer 1
 unsigned long runsecs = 0;
int main(void);
/*******************/
 // SPI
 /*******************/
 //Belegung
 //FSYNC: PB0 (1)
 //SCLK: PB1 (2)
 //SDATA: PB2 (4)
void spi_start(void);
 void spi_send_bit(int);
 void spi_send_byte(unsigned int);
 void spi_send_word(unsigned int);
 void spi_stop(void);
 void set_frequency(unsigned long, unsigned long, int);
/***************/
 /* LCD-Display */
 /***************/
 //Daten: PD4..PD7
 //E: PC0
 //RS: PC1
#define LCD_INST 0x00
 #define LCD_DATA 0x01
void lcd_write(char, unsigned char, int);
 void set_rs(char);
 void set_e(char);
 void lcd_init(void);
 void lcd_cls(void);
 void lcd_linecls(int, int);
 void lcd_putchar(int, int, unsigned char);
 void lcd_putstring(int, int, char*);
 int lcd_putnumber(int, int, long, int, int, char, char);
 void lcd_display_test(void);
//****************
 // ADC
 //****************
 #define ADWAITSTATE 3
 int get_adc(int);
//EEPROM
 void savefreq(int, unsigned long);
 unsigned long loadfreq(int vfo_num);
void savefreq(int vfo_num, unsigned long num)
 {
 unsigned int hiword, loword;
 unsigned char hmsb, lmsb, hlsb, llsb;
 int start_adr = vfo_num * 4;
hiword = num / 65536;
 loword = num - hiword * 65536;
hmsb = hiword / 256;
 hlsb = hiword - hmsb * 256;
 lmsb = loword / 256;
 llsb = loword - lmsb * 256;
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);
}
unsigned long loadfreq(int vfo_num)
 {
 unsigned long num2;
 unsigned char hmsb, lmsb, hlsb, llsb;
 int start_adr = vfo_num * 4;
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);
num2 = (unsigned long) 16777216 * hmsb + 65536 * hlsb + (unsigned int) 256 * lmsb + llsb;
if(num2 >= 1390000 && num2 <= 1438000)
 {
 return num2;
 }
 else
 {
 return 1415000;
 }
}
//************
 // SPI
 //************
 void spi_start(void)
 {
//FSYNC lo
 PORTB &= ~(1); // Bit PB0 löschen
}
void spi_stop(void)
 {
 //FSYNC hi
 PORTB |= 1; // Bit PB0 setzen
}
void spi_send_bit(int sbit)
 {
 //Bit setzen oder löschen
 if(sbit)
 {
 PORTB |= 2; //SDATA Bit PB1 setzen
 }
 else
 {
 PORTB &= ~(2); //SDATA Bit PB1 löschen
 }
//SCLK hi
 PORTB |= 4; //Bit PB2 setzen
//SCLK lo
 PORTB &= ~(4); //Bit PB2 löschen
}
void spi_send_byte(unsigned int sbyte)
 {
 int t1, x = 128;
for(t1 = 0; t1 < 8; t1++)
 {
 spi_send_bit(sbyte & x);
 x /= 2;
 }
PORTB |= 2; //PB1 SDATA hi
}
void spi_send_word(unsigned int sbyte)
 {
 unsigned int t1, x = 32768;
for(t1 = 0; t1 < 16; t1++)
 {
 spi_send_bit(sbyte & x);
 x /= 2;
 }
PORTB |= 2; //PB1 SDATA hi
 }
/* Wartezeit in Millisekunden */
 void wait_ms(int ms)
 {
 int t1, t2;
for(t1 = 0; t1 < ms; t1++)
 for(t2 = 0; t2 < 137 * SCLOCK; t2++) asm volatile ("nop" ::); } /**************************************/ /* Funktionen und Prozeduren fuer LCD */ /**************************************/ //Anschlussbelegeung am uC: //LCD-Data: PD4..PD7 //E: PC0 //RS: PC1 /* Ein Byte (Befehl bzw. Zeichen) zum Display senden */ void lcd_write(char lcdmode, unsigned char value, int waitcycles) { set_e(0); if(!lcdmode) set_rs(0); /* RS=0 => Befehl */
 else
 set_rs(1); /* RS=1 => Zeichen */
_delay_ms(waitcycles * 2);
set_e(1);
 PORTD = (value & 0xF0) / 16; /* Hi nibble */
 set_e(0);
set_e(1);
 PORTD = (value & 0x0F); /* Lo byte */
 set_e(0);
}
/* RS setzen */
 void set_rs(char status) /* PORT PB6 */
 {
 if(status)
 {
 PORTB |= 64;
 }
 else
 {
 PORTB &= ~(64);
 }
 }
/* E setzen */
 void set_e(char status) /* PORT PB7*/
 {
 if(status)
 {
 PORTB |= 128;
 }
 else
 {
 PORTB &= ~(128);
 }
 }
/* Ein Zeichen (Char) zum Display senden, dieses in */
 /* Zeile row und Spalte col positionieren */
 void lcd_putchar(int row, int col, unsigned char ch)
 {
 lcd_write(LCD_INST, col + 128 + row * 0x40, 1);
 lcd_write(LCD_DATA, ch, 1);
 }
/* Eine Zeichenkette direkt in das LCD schreiben */
 /* Parameter: Startposition, Zeile und Pointer */
 void lcd_putstring(int row, int col, char *s)
 {
 unsigned char t1;
for(t1 = col; *(s); t1++)
 {
 lcd_putchar(row, t1, *(s++));
 }
 }
/* Display loeschen */
 void lcd_cls(void)
 {
 lcd_write(LCD_INST, 1, 5);
 }
/* Display loeschen (eine Zeile) */
 void lcd_linecls(int displine, int chars)
 {
 unsigned char t1;
for(t1 = 0; t1 <= chars; t1++)
 lcd_putchar(displine, t1, 32);
 }
/* LCD-Display initialisieren */
 void lcd_init(void)
 {
 /* Grundeinstellungen: 2 Zeilen, 5x7 Matrix, 4 Bit */
 lcd_write(LCD_INST, 40, 5);
 lcd_write(LCD_INST, 40, 5);
 lcd_write(LCD_INST, 40, 5);
//MAtrix 5*7
 lcd_write(LCD_INST, 8, 5);
/* Display on, Cursor off, Blink off */
 lcd_write(LCD_INST, 12, 5);
/* Entrymode !cursoincrease + !displayshifted */
 lcd_write(LCD_INST, 4, 5);
//4-Bit-Mode
 lcd_write(LCD_INST, 2, 5);
lcd_cls();
 }
/* Eine n-stellige Zahl direkt in das LCD schreiben */
 /* Parameter: Startposition und Zeile; Zahl, */
 /* darzustellende Ziffern, Position des Dezimalpunktes, (l)links- oder (r)echtsbuendig */
 int lcd_putnumber(int row, int col, long num, int digits, int dec, char orientation, char showplussign)
 {
 char cl = col, minusflag = 0;
 unsigned char cdigit[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, digitcnt = 0;
 long t1, t2, n = num, r, x = 1;
if(num < 0)
 {
 minusflag = 1;
 n *= -1;
 }
/* Stellenzahl automatisch bestimmen */
 if(digits == -1)
 {
 for(t1 = 1; t1 < 10 && (n / x); t1++) x *= 10; digits = t1 - 1; } if(!digits) digits = 1; for(t1 = digits - 1; t1 >= 0; t1--)
 {
 x = 1;
 for(t2 = 0; t2 < t1; t2++)
 x *= 10;
 r = n / x;
 cdigit[digitcnt++] = r + 48;
if(t1 == dec)
 cdigit[digitcnt++] = 46;
 n -= r * x;
 }
digitcnt--;
 t1 = 0;
/* Ausgabe */
 switch(orientation)
 {
 case 'l':
 cl = col;
 if(minusflag)
 {
 lcd_putchar(row, cl++, '-');
 digitcnt++;
 }
 else
 {
 if(showplussign)
 {
 lcd_putchar(row, cl++, '+');
 digitcnt++;
 }
 }
while(cl <= col + digitcnt) /* Linksbuendig */ lcd_putchar(row, cl++, cdigit[t1++]); break; case 'r': t1 = digitcnt; /* Rechtsbuendig */ for(cl = col; t1 >= 0; cl--)
 lcd_putchar(row, cl, cdigit[t1--]);
 if(minusflag)
 lcd_putchar(row, --cl, '-');
 }
if(dec == -1)
 return digits;
 else
 return digits + 1;
 }
void set_frequency(unsigned long freq, unsigned long ifrequency, int ad9835fullinit)
 {
double fxtal = 5000000; //fQuarz in MHz
 double fword1;
 unsigned long hiword, loword;
 unsigned char hmsb, lmsb, hlsb, llsb;
fword1 = (freq - ifrequency) / fxtal * 0xFFFFFFFF;
//Aufspalten der 32 Bit in 2 * 16 Bit
 hiword = (unsigned long) fword1 / 65536;
 loword = (unsigned long) fword1 - hiword * 65536;
//Aufspalten der 1. 16 Bit in 2 * 8 Bit
 hmsb = hiword / 256;
 lmsb = hiword - hmsb * 256;
//Aufspalten der 2. 16 Bit in 2 * 8 Bit
 hlsb = loword / 256;
 llsb = loword - hlsb * 256;
if(ad9835fullinit)
 {
 //Initialisierung, AD9835 in Sleepmode setzen
 spi_start();
 spi_send_word(0xF800);
 spi_stop();
 }
//Senden der 4 * 16 Bit
 spi_start();
 spi_send_word(0x33 * 0x100 + hmsb);
 spi_stop();
spi_start();
 spi_send_word(0x22 * 0x100 + lmsb);
 spi_stop();
spi_start();
 spi_send_word(0x31 * 0x100 + hlsb);
 spi_stop();
spi_start();
 spi_send_word(0x20 * 0x100 + llsb);
 spi_stop();
//Sequenzende
 spi_start();
 if(ad9835fullinit)
 {
 //AD9835 aus Sleepmode wecken
 spi_send_word(0xC000);
 }
 else
 {
 //AD9835 Daten übertragen ohne volle Initialisierung
 spi_send_word(0x8000);
 }
 spi_stop();
//Frequenz anzeigen
 lcd_putnumber(0, 0, freq, -1, 2, 'l', 0);
 }
ISR(TIMER1_OVF_vect) // Timer1 Überlauf
 {
 runsecs++;
 TCNT1 = 57724;
 }
//***************************************************
 // ADC
 //***************************************************
int get_adc(int adcmode)
 {
 int adc_val = 0;
ADMUX = (ADMUX &~(0x1F)) | (adcmode & 0x1F); // Kanal adcmode aktivieren PA0=TUNE
 wait_ms(ADWAITSTATE);
ADCSRA |= (1<<ADSC);
 wait_ms(ADWAITSTATE);
adc_val = ADCL;
 adc_val += ADCH * 256;
while(ADCSRA & (1<<ADSC));
 return adc_val;
 }
int main()
 {
 unsigned long freq1; //Sendefrequenz in 10 Hz
 unsigned long freq_tmp;
 unsigned long vfo[MAXVFO + 1] = {1415000, 1410000, 1420000, 1430000}; //VFO A, B, C, D
 char *vfo_str[MAXVFO + 1] = {"VFOA", "VFOB", "VFOC", "VFOD"};
 unsigned int cur_freq_shift = 0; //Frequenzschritt beim Abstimmen
 unsigned int freq_shift[MAXSHIFT + 1] = {1, 5, 10, 25, 50, 100, 500};
 char *freq_shift_str[MAXSHIFT + 1] = {" 10", " 50", "100", "250", "500", " 1k", " 5k"};
int vfo_cnt = 0, vfo_cnt_old = 0, ok;
 unsigned long interfreq = 900000; //Zwischenfrequenz in 10Hz
unsigned int adc_val; //, adc_val_old = 0;
 //unsigned int adc_middle, adc_sum = 0;
 //unsigned int adc_interval = 1;
 unsigned int runsecsold = 0;
 unsigned int runsecsold2 = 0;
 char displayed = 0;
 unsigned long voltage;
int t1;
/* Ports einrichten */
 /* OUTPUT */
 DDRB = 0xC7; //SPI (PB0..PB2) und und LCD RS und E an PB6 und PB7
 DDRD = 0x0F; //LCD (Daten) an PD0...PD3
 PORTC = 0x3E; //Pull-Up-Widerstand an PC1..PC5 aktivieren
 // PC4 = Schalter für Frequenzschritt beim Abstimmen)
 // PC5 = TX detect
 // PC6 = VFO-Umschaltung
//Display
 lcd_init();
 wait_ms(50);
 lcd_putstring(0, 0, "SSB TRX");
 lcd_putstring(1, 0, "14 MHz");
 wait_ms(500);
lcd_cls();
 lcd_putstring(0, 0, "*DK7IH*");
 lcd_putstring(1, 0, "11/01/15");
 wait_ms(500);
 lcd_cls();
//Watchdog abschalten
 WDTCR = 0;
 WDTCR = 0;
//ADC initialisieren
 ADMUX = (1<<REFS0); // Referenz = AVCC
 ADCSRA = (1<<ADPS2) | (1<<ADPS1) | (1<<ADEN); //Frequenzvorteiler 64 u. //ADC einschalten
 ADCSRA |= (1<<ADSC); //Eine Wandlung vornehmen
 while (ADCSRA & (1<<ADSC)); //Eine Wandlung abwarten
 adc_val = ADCL;
 adc_val += ADCH * 256; //Wert auslesen
 adc_val = 0;
//Timer 1
 TCCR1A = 0; // normal mode, keine PWM Ausgänge
 TCCR1B = (1<<CS12) + (1<<CS10) ; // start Timer mit Systemtakt, Prescaler = /1024
 //Auslösung des Overflow alle Sekunde sec.
 TIMSK = (1<<TOIE1); // overflow aktivieren.
 TCNT1 = 57724; //Startwert für Sekundentakt
freq1 = vfo[vfo_cnt];
 set_frequency(freq1, interfreq, AD9835UPDATE);
//4 VFO Frequenzen aus EEPROM laden, wenn vorhanden
 for(t1 = 0; t1 <= MAXVFO; t1++) { freq_tmp = loadfreq(t1); if(freq_tmp > 0)
 {
 vfo[t1] = freq_tmp;
 }
 }
 freq1 = vfo[0];
set_frequency(freq1, interfreq, AD9835INIT);
 lcd_putstring(1, 0, vfo_str[vfo_cnt]);
lcd_putstring(1, 5, " ");
 lcd_putstring(1, 5, freq_shift_str[cur_freq_shift]);
runsecsold2 = runsecs;
sei();
for(;;)
 {
//Anzeige des ADC-WERTES
 /*if(adc_val != adc_val_old)
 {
 lcd_putstring(1, 0, " ");
 lcd_putnumber(1, 0, adc_val, -1, -1, 'l', 0);
 adc_val_old = adc_val;
 }*/
if(!(PINC & (1<<PINC2))) //VFO-Select
 {
 //Aktuelle Frequenz speichern
 freq_tmp = freq1;
 vfo_cnt_old = vfo_cnt; //Aktuellen VFO speichern
 lcd_putstring(1, 0, " ");
while(!(PINC & (1<<PINC2))); //Warten bis Taste losgelassen runsecsold = runsecs; while(runsecsold + 2 >= runsecs)
 {
 //Aktuellen VFO anzeigen
 lcd_putstring(1, 0, vfo_str[vfo_cnt]);
 //Frequenz anzeigen
 lcd_putnumber(0, 0, vfo[vfo_cnt], -1, 2, 'l', 0);
if(!(PINC & (1<<PINC2))) //Taste gedrückt
 {
 runsecsold = runsecs; //Zeit zurücksetzen
 if(vfo_cnt < MAXVFO)
 {
 vfo_cnt++;
 }
 else
 {
 vfo_cnt = 0;
 }
 //Frequenz setzen
 set_frequency(vfo[vfo_cnt], interfreq, AD9835UPDATE);
 while(!(PINC & (1<<PINC2))); //Warten bis Taste losgelassen } } //Zeit abgelaufen //VFO x mit aktueller Frequenz belegen? lcd_cls(); lcd_putnumber(0, 0, freq_tmp, -1, 2, 'l', 0); lcd_putstring(1, 0, "Save"); lcd_putchar(1, 5, vfo_cnt + 65); lcd_putstring(1, 6, "?"); runsecsold = runsecs; ok = 0; while(runsecsold + 2 >= runsecs) //2 sec. Zeit zum Bestätigen geben
 {
 if(!(PINC & (1<<PINC2))) //Bestätigt
 {
 ok = 1;
 runsecsold = 0;
 }
 else
 {
 lcd_putnumber(1, 7, runsecsold + 2 - runsecs, -1, -1, 'l', 0);
 }
 //Schnellausstieg mit blauer Taste
 if(!(PINC & (1<<PINC4)))
 {
 runsecsold = 0;
 }
 }
lcd_cls();
if(ok)
 {
 //VFO neu setzen UND Frequenz einstellen
 vfo[vfo_cnt] = freq_tmp;
 lcd_putnumber(0, 0, freq_tmp, -1, 2, 'l', 0);
 lcd_putstring(1, 0, "Saved");
 lcd_putchar(1, 6, vfo_cnt + 65);
 lcd_putchar(1, 7, '.');
 savefreq(vfo_cnt, freq_tmp);
 wait_ms(500);
 }
 else //Nur VFO wechseln mit bereits gegebener Frequenz
 {
 freq1 = vfo[vfo_cnt];
 }
 lcd_cls();
set_frequency(freq1, interfreq, AD9835UPDATE);
 lcd_putstring(1, 0, vfo_str[vfo_cnt]);
//Schrittweite anzeigen
 lcd_putstring(1, 5, " ");
 lcd_putstring(1, 5, freq_shift_str[cur_freq_shift]);
 }
if(!(PINC & (1<<PINC3))) //SCAN UP
 {
 freq1 += freq_shift[cur_freq_shift];
 set_frequency(freq1, interfreq, AD9835UPDATE);
 }
if(!(PINC & (1<<PINC4))) //SCAN DOWN
 {
 freq1 -= freq_shift[cur_freq_shift];
 set_frequency(freq1, interfreq, AD9835UPDATE);
 }
//Schrittweite setzen und ggf. anzeigen
 //Set Frequency-Shift for tuning
 if(!(PINC & (1<<PINC5))) //Frequency-Shift when tuning
 {
 runsecsold = runsecs;
 if(cur_freq_shift < MAXSHIFT)
 {
 cur_freq_shift++;
 }
 else
 {
 cur_freq_shift = 0;
 }
 while(!(PINC & (1<<PINC5))); lcd_putstring(1, 5, " "); lcd_putstring(1, 5, freq_shift_str[cur_freq_shift]); } //VFO anzeigen if(runsecs > runsecsold2 + 1 && displayed != 2)
 {
 if(displayed != 2)
 {
 lcd_putstring(1, 0, " ");
 lcd_putstring(1, 0, vfo_str[vfo_cnt]);
 displayed = 2;
 runsecsold2 = runsecs;
 }
 }
//Anzeige Betriebsspannung
 if(runsecs > runsecsold2 + 3 && displayed != 1)
 {
 //ADC abfragen (Akkuspannung)
 adc_val = get_adc(0);
 voltage = (unsigned long) adc_val * 150 / 1024;
 lcd_putstring(1, 0, " ");
 lcd_putnumber(1, 0, voltage, -1, 1, 'l', 0);
 lcd_putstring(1, 4, "V");
 runsecsold2 = runsecs;
 displayed = 1;
 }
}
return 0;
 }

Leave a Reply

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