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