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

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