This page collects the materials related to the article published in the February 2014 issue of CQ Magazine (http://www NULL.cq-amateur-radio NULL.com/cq_highlights/2014_cq/2014_01_cq/PDF_NO_1_2014_02_CQ_TOC NULL.pdf) (page 36).
You will find here :
- **NEW** The corrected schematic with missing analog buttons connection to P1.5 (A5)
- The source code for the last release of DDS WSPR beacon firmware (see below)
- The source code for the last release of DDS QRSS beacon firmware (see below)
- The source code for the last release of Simple DDS VFO firmware (see below)
- The link to the required libraries (AD9850, 5110 LCD and Real-Time clock) to compile and upload the code thanks to Energia IDE (/ftp/libraries_Energia_xv4y_20130313 NULL.zip)
- The link to the WSPR beacon kit user’s manual (/ftp/WSPR_QRSS_DDS_Beacon_User_Manual_20130321 NULL.pdf) that explain how to interact with the firmware
- The link to the Energia IDE (http://energia NULL.nu/download/) that is used to compile the code and upload it to the Texas Instruments MSP430 LaunchPad Board
Corrected schematic
The schematic published in CQ Magazine had a slight error with the connection from the analog buttons (between R1 and R2) going to the A5 analog input of the MSP430G2553 (P1.5 port).
(http://xv4y NULL.radioclub NULL.asia/wp-content/uploads/2013/11/1_WSPR_Schema_corrected NULL.png)
DDS WSPR beacon firmware
/* WSPR DDS Generator for MSP430G2553 * Code for Energia 009 * By Yannick DEVOS - XV4Y - January 2014 http://xv4y.radioclub.asia/ Copyright 2012-2013-2014 Yannick DEVOS under GPL 3.0 license Any commercial use or inclusion in a kit is subject to author approval * Based on code by DH3JO and G4JNT for WSPR sequence encoding * Optimization by XV4Y to reduce memory usage ==== * PTT_key output allows to turn on/off the TX PA while sending * Agile Frequency generation using AD9850/9851 DDS * Output to Nokia 5110 compatible LCD ==== Revision history : v1.00 2012-12-31 First release v1.01 2013-01-05 Added reading from analog multiplexed buttons v1.02 2013-01-29 A few last minute bugs corrected and NoLCD reincorporated in the code v1.10 2013-03-09 Added random shift enable/disable parameter Added WSPR-15 support (with better symbol spacing accuracy) V1.11 2013-03-21 Multiple QTH (Callsign and Locator) allowed and startup selectable V1.12 2013-06-09 Cycling throug sets of predefined frequencies ==== This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You can download a copy of the GNU General Public License at <http://www.gnu.org/licenses/> */ // Here modify to your taste or needs #define PTT_key P2_2 //#define StartBtn P1_3 #define ANALOG_BUTTONS A5 #define LCD_OUT_INFO // Comment if no 5110 display is connected const char call[2][7] = { "XV4Y ", "XV4Y "} ; // Callsigns must be 6 characters, number is mandatory in 3rd position, pad with spaces if necessary const char locator[2][5] = { "OK20", "OK20"} ; #define PWR_LEVEL 9 // Power in dBm (value 6 is 20 dBm) #define DEF_TX_RATE 4 // TX rate for a random start optimizing frequency sharing (value 4 is 1/5) #define DEFAULT_FREQ 4 // Value 8 in Frequencies array is 14097100 #define DEFAULT_SHIFT 146 // WSPR Symbol frequency separation in 1/100 of Hertz #define DEFAULT_QTH 0 // The callsign / locator par you choose by default #define DEF_CYCLING -1 // Which band set we cycle through frequencies by default : -1 is cycling disabled, 0 is first band set, 1 second band set... #define MAX_CYCLE 8 // Number of bands we will cycle // List of frequencies we will cycle through, for real frequencis see table in constant belows // First set is user defined (40, 20, 15, 10m) // Second set is low band (160, 80, 40, 30m) // Third set is high bands (20, 17, 15, 10m) // Fourth set is WARC band (60, 30, 17, 12m) const byte band_cycling[4][MAX_CYCLE] = {{6, 6, 8, 8, 10, 10, 12, 12}, {3, 3, 4, 4, 6, 6, 7, 7}, {8, 8, 9, 9, 10, 10, 12, 12}, {6, 6, 7, 7, 9, 9, 11, 11}}; // ** // Here under don't touch anything unless you know what you do // ** #include <legacymsp430.h> #include <sRTC.h> // Library for RTC clock with MSP430 #include <AD9850.h> // Library for AD9850 control by MSP430 #ifdef LCD_OUT_INFO #include <LCD_5110.h> #endif // Power levels (in dBm) you can select const byte powers[16] = {0,3,7,10,13,17,20,23,27,30,33,37,40,43,47,50}; // Frequencies (1 Hz precision) you can select const unsigned long int frequencies[] = { 137500, 471700, 501500, 1838500, 3594100, 5288700, 7040100, 10140200, 14097100, 18106100, 21096100, 24926100, 28126100, 50294500, 70092500}; const unsigned tx_rates[7] = {1,2,3,4,5,7,10}; const char SyncVec[162] = { 1,1,0,0,0,0,0,0,1,0,0,0,1,1,1,0,0,0,1,0,0,1,0,1,1,1,1,0,0,0,0,0,0,0,1,0,0,1,0,1,0,0,0,0,0,0,1,0, 1,1,0,0,1,1,0,1,0,0,0,1,1,0,1,0,0,0,0,1,1,0,1,0,1,0,1,0,1,0,0,1,0,0,1,0,1,1,0,0,0,1,1,0,1,0,1,0, 0,0,1,0,0,0,0,0,1,0,0,1,0,0,1,1,1,0,1,1,0,0,1,1,0,1,0,0,0,1,1,1,0,0,0,0,0,1,0,1,0,0,1,1,0,0,0,0, 0,0,0,1,1,0,1,0,1,1,0,0,0,1,1,0,0,0 }; const byte Analog_Noise = 20; // Margins for reading analog buttons boolean force_send = 1; // The first time the beacon is turned on it will send a sequence byte bb, begin_sec, wspr_shift=DEFAULT_SHIFT; char freq_select=DEFAULT_FREQ, rate_select=DEF_TX_RATE, qth=DEFAULT_QTH, cycle_select=0, band_set=DEF_CYCLING; int begin_chunk, random_shift=50; char chaine_txt[6] = {' ', ' ', ' ', ' ', ' ', 0x00}; byte sym[162]; // symbol table 162 RealTimeClock myClock; //AD9850 myDDS (P1_0, P1_1, P1_2, P1_4); // Call the AD9850 Library, AD9850 pins for CLOCK, LOAD, DATA and RESET AD9850 myDDS (P1_1, P1_2, P1_0, P1_4); // Call the AD9850 Library, AD9851 pins for CLOCK, LOAD, DATA and RESET #ifdef LCD_OUT_INFO LCD_5110 myScreen(P2_3, // Chip Select * P1_6, // Serial Clock * P2_5, // Serial Data * P2_4, // Data/Command * NULL, // Reset * P1_7, // Backlight NULL); // Push Button 0 #endif //****************************************************************** // Defining pins mode // Encoding the WSPR sequence void setup() { pinMode(PTT_key, OUTPUT); myClock.begin(); myDDS.begin(); #ifdef LCD_OUT_INFO pinMode(ANALOG_BUTTONS, INPUT); myScreen.begin(); myScreen.setBacklight(1); myScreen.setFont(1); myScreen.text(0, 0, "WSPR"); myScreen.text(0, 2, "Beacon"); myScreen.setFont(0); myScreen.text(0, 4, "v1.12 - XV4Y"); myScreen.text(0, 5, "Init..."); randomSeed(analogRead(ANALOG_BUTTONS)); // Set a random shift of +/- 50Hz around the central WSPR frequency random_shift = random(100)-50; setup_menu(); // We call the setup menu myDDS.reset(); #else pinMode(StartBtn, INPUT_PULLUP); pinMode(RED_LED, OUTPUT); pinMode(GREEN_LED, OUTPUT); digitalWrite(RED_LED, LOW); digitalWrite(GREEN_LED, LOW); myDDS.reset(); #endif myDDS.SetFrequency( frequencies[freq_select], 0, true ); // Set frequency with DDS power down digitalWrite( PTT_key, LOW ); prepare_sequence(); // We compute the the WSPR sequence begin_chunk = myClock.RTC_chunk; begin_sec = myClock.RTC_sec; #if defined(LCD_OUT_INFO) myScreen.clear(); myScreen.setBacklight(1); myScreen.setFont(1); myScreen.text(0, 0, "TIME"); myScreen.setFont(0); myScreen.text(0, 2, "Press button"); myScreen.text(0, 3, "to set time."); myScreen.text(0, 4, "Sending test"); while(read_buttons()!=5) { // We also send a test sequence to help calibrate the transmitter if ((myClock.RTC_chunk-(begin_chunk+0))%240==0) send_test(begin_sec); }; myScreen.setBacklight(0); #else while(digitalRead(StartBtn)) { // This is for blinking LED fast if ((myClock.RTC_chunk-(begin_chunk+15))%60==0) digitalWrite( RED_LED, HIGH ); if ((myClock.RTC_chunk-(begin_chunk+30))%60==0) digitalWrite( GREEN_LED, HIGH ); if ((myClock.RTC_chunk-(begin_chunk+45))%60==0) digitalWrite( RED_LED, LOW ); if ((myClock.RTC_chunk-(begin_chunk+60))%60==0) digitalWrite( GREEN_LED, LOW ); // We also send a test sequence to help calibrate the transmitter if ((myClock.RTC_chunk-(begin_chunk+0))%240==0) send_test(begin_sec); }; #endif myClock.Set_Time(0,0,0); }; //****************************************************************** // Here starts the actual sequence sending void loop() { if (band_set >= 0) freq_select = band_cycling[band_set][cycle_select]; digitalWrite( PTT_key, LOW ); myDDS.SetFrequency( (frequencies[freq_select]+random_shift), 0, true ); // Set frequency with DDS power down begin_chunk = myClock.RTC_chunk; #if defined(LCD_OUT_INFO) myScreen.clear(); myScreen.setBacklight(0); myScreen.setFont(1); if (wspr_shift == 18) { myScreen.text(0, 0, "WSPR-15"); } else if (wspr_shift == 146) { myScreen.text(0, 0, "WSPR"); }; myScreen.setFont(0); myScreen.text(0, 2, call[qth]); myScreen.text(8, 2, locator[qth]); myScreen.text(0, 4, "Waiting..."); if (wspr_shift == 146) { // Wait start for WSPR 2 while (!(myClock.RTC_sec==1 && (myClock.RTC_min%2)==0)) { // We start each first second of even minutes if (read_buttons()==5){ force_send = 1; // Pressing the button while in wait mode force the sequence to start next time myScreen.text(0, 4, "Send next slot"); myScreen.setBacklight(1); }; if (myClock.RTC_chunk==1 || myClock.RTC_chunk==128) { display_time(); }; }; } else if (wspr_shift == 18) { // Wait start for WSPR 15 while (!(myClock.RTC_sec==1 && (myClock.RTC_min%15)==0)) { // We start each first second of even minutes if (read_buttons()==5){ force_send = 1; // Pressing the button while in wait mode force the sequence to start next time myScreen.text(0, 4, "Send next slot"); myScreen.setBacklight(1); }; if (myClock.RTC_chunk==1 || myClock.RTC_chunk==128) { display_time(); }; }; }; if (random(tx_rates[rate_select]+1)==tx_rates[rate_select] || force_send) { // In order to minimize band occupation, randomly start the transmission depending on tx_rate // Now we start the transmit procedure force_send = 0; // Set PTT high, turn on back light and set DDS digitalWrite( PTT_key, HIGH ); myScreen.setBacklight(1); myDDS.SetFrequency( (frequencies[freq_select]+random_shift), 0, false ); // Set frequency with DDS and turn on DDS // Display the frequency display_freq (frequencies[freq_select]+random_shift, 4); // Display Power display_number (powers[PWR_LEVEL], 0, 3); myScreen.text(2, 3, "dBm"); myScreen.text(0, 5, "Now sending "); send_sequence(); myScreen.text(0, 4, "End of sequence"); } else { // If not, we wait around 1 sec so we don't go through this too early... digitalWrite( PTT_key, LOW ); myDDS.SetFrequency( (frequencies[freq_select]+random_shift), 0, true ); // Set frequency with DDS power down begin_chunk = myClock.RTC_chunk; while ( ((256+myClock.RTC_chunk-begin_chunk)%256)<220 ) { delay(0); }; }; #else digitalWrite( RED_LED, LOW ); digitalWrite( GREEN_LED, LOW ); begin_chunk = myClock.RTC_chunk; if (wspr_shift == 146) { // Wait start for WSPR 2 while (!(myClock.RTC_sec==1 && (myClock.RTC_min%2)==0)) { // We start each first second of even minutes if ((myClock.RTC_chunk-(begin_chunk+10))%20==0) digitalWrite( GREEN_LED, HIGH ); if ((myClock.RTC_chunk-(begin_chunk+20))%20==0) digitalWrite( GREEN_LED, LOW ); if (!digitalRead(StartBtn)) force_send = 1; // Pressing the button while in wait mode force the sequence to start next time }; } else if (wspr_shift == 18) { // Wait start for WSPR 15 while (!(myClock.RTC_sec==1 && (myClock.RTC_min%15)==0)) { // We start each first second of even minutes if ((myClock.RTC_chunk-(begin_chunk+20))%40==0) digitalWrite( GREEN_LED, HIGH ); if ((myClock.RTC_chunk-(begin_chunk+40))%40==0) digitalWrite( GREEN_LED, LOW ); if (!digitalRead(StartBtn)) force_send = 1; // Pressing the button while in wait mode force the sequence to start next time }; }; if (random(tx_rates[rate_select]+1)==tx_rates[rate_select] || force_send) { // In order to minimize band occupation, randomly start the transmission depending on tx_rate force_send = 0; digitalWrite( PTT_key, HIGH ); myDDS.SetFrequency( (frequencies[freq_select]+random_shift), 0, false ); // Set frequency with DDS and turn on DDS send_sequence(); } else { // If not, we wait around 1 sec so we don't go through this too early... digitalWrite( PTT_key, LOW ); myDDS.SetFrequency( (frequencies[freq_select]+random_shift), 0, true ); // Set frequency with DDS power down begin_chunk = myClock.RTC_chunk; while ( ((256+myClock.RTC_chunk-begin_chunk)%256)<220 ) { delay(0); }; }; #endif if (band_set >= 0) cycle_select++; // We increase the index through the band cycling table if (cycle_select == MAX_CYCLE) cycle_select = 0; }; //****************************************************************** // normalize characters 0..9 A..Z Space in order 0..36 char chr_normf(char bc ) { char cc=36; if (bc >= '0' && bc <= '9') cc=bc-'0'; if (bc >= 'A' && bc <= 'Z') cc=bc-'A'+10; if (bc == ' ' ) cc=36; return(cc); } //****************************************************************** // Check parity byte parity(unsigned long li) { byte po = 0; while(li != 0) { po++; li&= (li-1); } return (po & 1); } //****************************************************************** // Compute and initialize the WSPR symbols sequence void prepare_sequence() { unsigned long sh1=0; unsigned long t1; boolean symt[164]; // symbol table temp => A remplacer par table de boolean byte c[11]={0,0,0,0,0,0,0,0,0,0,0}; // encoded message int bc=0, cnt=0, cc; int ii, ij, b2, bis, ip=0; // Encode callsign t1=chr_normf(call[qth][0]); t1=t1*36+chr_normf(call[qth][1]); t1=t1*10+chr_normf(call[qth][2]); t1=t1*27+chr_normf(call[qth][3])-10; t1=t1*27+chr_normf(call[qth][4])-10; t1=t1*27+chr_normf(call[qth][5])-10; // merge coded callsign into message array c[] c[0]= t1 >> 20; c[1]= t1 >> 12; c[2]= t1 >> 4; c[3]= t1 << 4; // Encode locator t1=179-10*(chr_normf(locator[qth][0])-10)-chr_normf(locator[qth][2]); t1=t1*180+10*(chr_normf(locator[qth][1])-10)+chr_normf(locator[qth][3]); t1=t1*128+powers[PWR_LEVEL]+64; // Encore power // merge coded locator and power into message array c[] c[3]= c[3] + ( 0x0f & t1 >> 18); c[4]= t1 >> 10; c[5]= t1 >> 2; c[6]= t1 << 6; // Intilialize tables to 0 for (int i=0; i<163; i++) { sym[i] = 0; symt[i] = 0; } // convolutional encoding of message array c[] into a 162 bit stream cc=c[0]; for (int i=0; i < 81;i++) { if (i % 8 == 0 ) { cc=c[bc]; bc++; } if (cc & 0x80) sh1=sh1 | 1; symt[cnt++]=parity(sh1 & 0xF2D05351); symt[cnt++]=parity(sh1 & 0xE4613C47); cc=cc << 1; sh1=sh1 << 1; } // interleave reorder the 162 data bits and and merge table with the sync vector for (ii=0;ii<=255;ii++) { bis=1; ij=0; for (b2=0;b2 < 8 ;b2++) { if (ii & bis) ij= ij | (0x80 >> b2); bis=bis << 1; } if (ij < 162 ) { sym[ij]= (SyncVec[ij] +2*symt[ip]); ip++; } } }; //****************************************************************** // Function to send the test sequence void send_test(char begin_sec) { if ((myClock.RTC_sec-(begin_sec+0))%30==0) { digitalWrite( PTT_key, HIGH ); myDDS.SetFrequency( (frequencies[freq_select]+random_shift), 0, false ); #ifdef LCD_OUT_INFO myScreen.text(0, 5, "TX"); myScreen.text(4, 5, "00"); #endif } if ((myClock.RTC_sec-(begin_sec+6))%30==0) { digitalWrite( PTT_key, HIGH ); myDDS.SetFrequency( (frequencies[freq_select]+random_shift), (wspr_shift*1), false ); #ifdef LCD_OUT_INFO myScreen.text(0, 5, "TX"); myScreen.text(4, 5, "01"); #endif } if ((myClock.RTC_sec-(begin_sec+12))%30==0) { digitalWrite( PTT_key, HIGH ); myDDS.SetFrequency( (frequencies[freq_select]+random_shift), (wspr_shift*2), false ); #ifdef LCD_OUT_INFO myScreen.text(0, 5, "TX"); myScreen.text(4, 5, "10"); #endif } if ((myClock.RTC_sec-(begin_sec+18))%30==0) { digitalWrite( PTT_key, HIGH ); myDDS.SetFrequency( (frequencies[freq_select]+random_shift), (wspr_shift*3), false ); #ifdef LCD_OUT_INFO myScreen.text(0, 5, "TX"); myScreen.text(4, 5, "11"); #endif } if ((myClock.RTC_sec-(begin_sec+24))%30==0) { digitalWrite( PTT_key, LOW ); myDDS.SetFrequency( (frequencies[freq_select]+random_shift), 0, 1 ); #ifdef LCD_OUT_INFO myScreen.text(0, 5, "--"); myScreen.text(4, 5, "00"); #endif } }; //****************************************************************** // Function to send the sequence void send_sequence() { for (bb = 0; bb < 162; bb++) { begin_chunk = myClock.RTC_chunk; for (byte i=0; i<6; i++) { chaine_txt[i] = ' '; }; myDDS.SetFrequency( (frequencies[freq_select]+random_shift), (wspr_shift*sym[bb]), false ); // Change frequency with symbols #ifdef LCD_OUT_INFO display_number (sym[bb], 12, 5); #else digitalWrite( GREEN_LED, (sym[bb] >> 1) ); digitalWrite( RED_LED, (sym[bb] & 1) ); #endif for (byte i=0; i<8; i++) { // In case of WSPR-15 we have to wait 8 times longer while ( ((256+myClock.RTC_chunk-begin_chunk)%256)<175 ) { #ifdef LCD_OUT_INFO if (myClock.RTC_chunk==1 || myClock.RTC_chunk==128) display_time(); // Display time only twice a second #else delay(0); #endif }; begin_chunk = myClock.RTC_chunk; if (wspr_shift == 146) break; // In case of WSPR-2 we can go out earlier }; }; }; #if defined(LCD_OUT_INFO) //****************************************************************** // Setup menu at startup in 2 parts void setup_menu() { // Here we wait 5 seconds more and check if the user wants to change settings begin_sec = myClock.RTC_sec; while ( ((60 + myClock.RTC_sec - begin_sec)%60) < 5 ) { if ( read_buttons()==5 ) { break; }; delay(10); } // If button pressed we enter setting if(read_buttons()==5) { while (read_buttons()==5) delay(0); // Debounce setup_menu_1(); while (read_buttons()==5) delay(0); // Debounce setup_menu_2(); while (read_buttons()==5) delay(0); // Debounce setup_menu_3(); while (read_buttons()==5) delay(0); // Debounce setup_menu_3b(); while (read_buttons()==5) delay(0); // Debounce if (band_set == -1) { setup_menu_4(); while (read_buttons()==5) delay(0); // Debounce }; setup_menu_5(); while (read_buttons()==5) delay(0); // Debounce } } void setup_menu_1() { // First we set the frequency myScreen.clear(); myScreen.setBacklight(1); myScreen.setFont(1); myScreen.text(0, 0, "SETUP 1"); myScreen.setFont(0); myScreen.text(0, 2, "Press up/dwn"); myScreen.text(0, 3, "to set Band"); myScreen.text(0, 4, "center Freq"); while (1) { display_freq (frequencies[freq_select], 5); if(read_buttons()==1) { // Up we increase while (read_buttons()==1) delay(0); // Debounce freq_select++; if (freq_select > 14) freq_select = 0; } else if (read_buttons()==3) { // Down we decrease while (read_buttons()==3) delay(0); // Debounce freq_select--; if (freq_select < 0) freq_select = 14; } else if (read_buttons()==5) { // OK we go out while (read_buttons()==5) delay(0); // Debounce break; } } } void setup_menu_2() { // Then we set the tx_rate myScreen.clear(); myScreen.setBacklight(1); myScreen.setFont(1); myScreen.text(0, 0, "SETUP 2"); myScreen.setFont(0); myScreen.text(0, 2, "Press up/dwn"); myScreen.text(0, 3, "set TX/idle"); myScreen.text(0, 4, "ratio"); while (1) { myScreen.text(0, 5, "1/"); display_number (tx_rates[rate_select], 2, 5); if(read_buttons()==1) { // Up we increase while (read_buttons()==1) delay(0); // Debounce rate_select++; if (rate_select > 6) rate_select = 0; } else if (read_buttons()==3) { // Down we decrease while (read_buttons()==3) delay(0); // Debounce rate_select--; if (rate_select < 0) rate_select = 6; } else if (read_buttons()==5) { // OK we go out while (read_buttons()==5) delay(0); // Debounce break; } } } void setup_menu_3() { // Then we set the WSPR mode myScreen.clear(); myScreen.setBacklight(1); myScreen.setFont(1); myScreen.text(0, 0, "SETUP 3"); myScreen.setFont(0); myScreen.text(0, 2, "Press up/dwn"); myScreen.text(0, 3, "to toggle"); myScreen.text(0, 4, "WSPR Mode"); while (1) { if (wspr_shift == 146) { myScreen.text(0, 5, "WSPR-2 "); } else if (wspr_shift == 18) { myScreen.text(0, 5, "WSPR-15 "); }; if(read_buttons()==1) { // Up toggle modes while (read_buttons()==1) delay(0); // Debounce if (wspr_shift == 18) { wspr_shift = 146; random_shift = random(100)-50; } else if (wspr_shift == 146) { wspr_shift = 18; random_shift = 80; }; } else if (read_buttons()==3) { // Down toggle modes while (read_buttons()==3) delay(0); // Debounce if (wspr_shift == 18) { wspr_shift = 146; random_shift = random(100)-50; } else if (wspr_shift == 146) { wspr_shift = 18; random_shift = 80; }; } else if (read_buttons()==5) { // OK we go out while (read_buttons()==5) delay(0); // Debounce break; } } } void setup_menu_3b() { // Then we decide if we cycle through frequencies myScreen.clear(); myScreen.setBacklight(1); myScreen.setFont(1); myScreen.text(0, 0, "SETUP 3B"); myScreen.setFont(0); myScreen.text(0, 2, "Press up/dwn"); myScreen.text(0, 3, "to set band"); myScreen.text(0, 4, "cycling mode"); while (1) { if (band_set == -1) { myScreen.text(0, 5, "Disabled "); } else if (band_set == 0) { myScreen.text(0, 5, "User "); } else if (band_set == 1) { myScreen.text(0, 5, "Low bands "); } else if (band_set == 2) { myScreen.text(0, 5, "High bands "); } else if (band_set == 3) { myScreen.text(0, 5, "WARC bands "); }; if(read_buttons()==1) { // Up toggle modes while (read_buttons()==1) delay(0); // Debounce band_set++; if (band_set > 3) band_set = 3; } else if (read_buttons()==3) { // Down toggle modes while (read_buttons()==3) delay(0); // Debounce band_set--; if (band_set < -1) band_set = -1; } else if (read_buttons()==5) { // OK we go out while (read_buttons()==5) delay(0); // Debounce break; } } } void setup_menu_4() { // Then we adjust frequency manually myScreen.clear(); myScreen.setBacklight(1); myScreen.setFont(1); myScreen.text(0, 0, "SETUP 4"); myScreen.setFont(0); myScreen.text(0, 2, "Press up/dwn"); myScreen.text(0, 3, "adjust TX Freq"); myScreen.text(0, 4, "by 5Hz step"); while (1) { display_freq (frequencies[freq_select]+random_shift, 5); if(read_buttons()==1) { // Up we increase delay(200); // Debounce random_shift+=5; if (random_shift > 150) random_shift = 150; } else if (read_buttons()==3) { // Down we decrease delay(200); // Debounce random_shift-=5; if (random_shift < -150) random_shift = -150; } else if (read_buttons()==5) { // OK we go out while (read_buttons()==5) delay(0); // Debounce break; } } } void setup_menu_5() { // Then we choose the QTH myScreen.clear(); myScreen.setBacklight(1); myScreen.setFont(1); myScreen.text(0, 0, "SETUP 5"); myScreen.setFont(0); myScreen.text(0, 2, "Press up/dwn"); myScreen.text(0, 3, "to choose QTH"); while (1) { myScreen.text(0, 5, call[qth]); myScreen.text(8, 5, locator[qth]); if(read_buttons()==1) { // Up we increase while (read_buttons()==1) delay(0); // Debounce qth++; if (qth > 1) qth = 0; } else if (read_buttons()==3) { // Down we decrease while (read_buttons()==3) delay(0); // Debounce qth--; if (qth < 0) qth = 1; } else if (read_buttons()==5) { // OK we go out while (read_buttons()==5) delay(0); // Debounce break; } } } //****************************************************************** // Display the frequency void display_freq (unsigned long freq, char ligne) { myScreen.text(10, ligne, " Hz "); chaine_txt[5] = 0x00; chaine_txt[4] = 0x30 + (((freq)/1)%10); chaine_txt[3] = 0x30 + (((freq)/10)%10); chaine_txt[2] = 0x30 + (((freq)/100)%10); chaine_txt[1] = '.'; chaine_txt[0] = 0x30 + (((freq)/1000)%10); myScreen.text(5, ligne, chaine_txt); chaine_txt[5] = 0x00; chaine_txt[4] = 0x30 + (((freq)/10000)%10); chaine_txt[3] = 0x30 + (((freq)/100000)%10); chaine_txt[2] = '.'; chaine_txt[1] = 0x30 + (((freq)/1000000)%10); chaine_txt[0] = 0x30 + (((freq)/10000000)%10); myScreen.text(0, ligne, chaine_txt); } //****************************************************************** // Display the time void display_time () { chaine_txt[5] = 0x00; chaine_txt[4] = 0x30 + (myClock.RTC_sec%10); chaine_txt[3] = 0x30 + ((myClock.RTC_sec/10)%10); chaine_txt[2] = ':'; chaine_txt[1] = 0x30 + (myClock.RTC_min%10); chaine_txt[0] = 0x30 + ((myClock.RTC_min/10)%10); myScreen.text(7, 3, chaine_txt); } //****************************************************************** // Display a 2 digits number void display_number (byte number, char column, char ligne) { chaine_txt[2] = 0x00; chaine_txt[1] = 0x30 + (number%10); chaine_txt[0] = 0x30 + ((number/10)%10); myScreen.text(column, ligne, chaine_txt); } //****************************************************************** // Return a button value depending on the analog reading byte read_buttons () { int value = analogRead(ANALOG_BUTTONS); if ( value<(1+Analog_Noise) ) { return 1; } else if ( value<(78+Analog_Noise) ) { return 2; } else if ( value<(146+Analog_Noise) ) { return 3; } else if ( value<(205+Analog_Noise) ) { return 4; } else if ( value<(255+Analog_Noise) ) { return 5; } else { return 0; } } #endif //****************************************************************** // Interruption for the RTC clock interrupt(TIMER1_A0_VECTOR) Tic_Tac(void) { myClock++; // Update chunks };
DDS QRSS beacon firmware
/* QRSS beacon with 10 minutes frame for MSP430G2553 driving an AD9850 DDS * Code for Energia 009 * By Yannick DEVOS - XV4Y - January 2014 http://xv4y.radioclub.asia/ Copyright 2012-2013-2014 Yannick DEVOS under GPL 3.0 license Any commercial use or inclusion in a kit is subject to author approval ==== * PTT_key output allows to turn on/off the TX PA while sending * Agile Frequency generation using AD9850/9851 DDS * Output to Nokia 5110 compatible LCD ==== Revision history : v1.00 2013-06-13 First release based on QRSS framed v1.02 ==== This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You can download a copy of the GNU General Public License at <http://www.gnu.org/licenses/> */ // ** // Here under don't touch anything unless you know what you do // ** // Alphabet binary coding by G0UPL // 0 = dot, 1 = dash const int _A = 0b11111001; const int _B = 0b11101000; const int _C = 0b11101010; const int _D = 0b11110100; const int _E = 0b11111100; const int _F = 0b11100010; const int _G = 0b11110110; const int _H = 0b11100000; const int _I = 0b11111000; const int _J = 0b11100111; const int _K = 0b11110101; const int _L = 0b11100100; const int _M = 0b11111011; const int _N = 0b11111010; const int _O = 0b11110111; const int _P = 0b11100110; const int _Q = 0b11101101; const int _R = 0b11110010; const int _S = 0b11110000; const int _T = 0b11111101; const int _U = 0b11110001; const int _V = 0b11100001; const int _W = 0b11110011; const int _X = 0b11101001; const int _Y = 0b11101011; const int _Z = 0b11101100; const int _SPC = 0b11101111; const int _0 = 0b11011111; const int _1 = 0b11001111; const int _2 = 0b11000111; const int _3 = 0b11000011; const int _4 = 0b11000001; const int _5 = 0b11000000; const int _6 = 0b11010000; const int _7 = 0b11011000; const int _8 = 0b11011100; const int _9 = 0b11011110; const int _BRK = 0b11010010; const int _WAIT = 0b10000000; // ** // PARAMETERS : Here modify to your taste or needs // ** const int msg[] = {5, _X, _V, _4, _Y, _WAIT}; // Format, is lenght in decimal and then the character constants including the _WAIT for the end of the sequence int vitesse = 6; // Number of seconds per dot (QRSS6 = 6) #define PTT_key P2_2 //#define StartBtn P1_3 #define ANALOG_BUTTONS A5 #define LCD_OUT_INFO // Comment if no 5110 display is connected #define DEFAULT_FREQ 6 // Value 8 in Frequencies array is 14097100 #define DEFAULT_SHIFT 500 // QRSS FSK frequency separation in 1/100 of Hertz #define DEF_CYCLING -1 // Which band set we cycle through frequencies by default : -1 is cycling disabled, 0 is first band set, 1 second band set... #define MAX_CYCLE 8 // Number of bands we will cycle // List of frequencies we will cycle through, for real frequencis see table in constant belows // First set is user defined (40, 20, 15, 10m) // Second set is low band (160, 80, 40, 30m) // Third set is high bands (20, 17, 15, 10m) // Fourth set is WARC band (60, 30, 17, 12m) const byte band_cycling[4][MAX_CYCLE] = {{6, 6, 8, 8, 10, 10, 12, 12}, {3, 3, 4, 4, 6, 6, 7, 7}, {8, 8, 9, 9, 10, 10, 12, 12}, {6, 6, 7, 7, 9, 9, 11, 11}}; #include <legacymsp430.h> #include <sRTC.h> // Library for RTC clock with MSP430 #include <AD9850.h> // Library for AD9850 control by MSP430 #ifdef LCD_OUT_INFO #include <LCD_5110.h> #endif RealTimeClock myClock; //AD9850 myDDS (P1_0, P1_1, P1_2, P1_4); // Call the AD9850 Library, AD9850 pins for CLOCK, LOAD, DATA and RESET AD9850 myDDS (P1_1, P1_2, P1_0, P1_4); // Call the AD9850 Library, AD9851 pins for CLOCK, LOAD, DATA and RESET #ifdef LCD_OUT_INFO LCD_5110 myScreen(P2_3, // Chip Select * P1_6, // Serial Clock * P2_5, // Serial Data * P2_4, // Data/Command * NULL, // Reset * P1_7, // Backlight NULL); // Push Button 0 #endif // ** // Here under don't touch anything unless you know what you do // ** // Frequencies (1 Hz precision) you can select const unsigned long int frequencies[] = { 137600, 476175, 501500, 1843200, 3500890, 5288700, 7000800, 10140000, 14000800, 18108900, 21000800, 24928900, 28000800, 50294400, 70092500}; const byte Analog_Noise = 20; // Margins for reading analog buttons boolean force_send = 1; // The first time the beacon is turned on it will send a sequence byte bb, begin_sec; char freq_select=DEFAULT_FREQ, cycle_select=0, band_set=DEF_CYCLING; int begin_chunk, random_shift=0, qrss_shift=DEFAULT_SHIFT; // Déclaration et initilisation des variables byte msgIndex = 1; byte inc_bit = 8; byte character = _SPC; boolean start = false; byte key = 0; byte etat = 0; char chaine_txt[6] = {' ', ' ', ' ', ' ', ' ', 0x00}; //****************************************************************** // Defining pins mode // Encoding the WSPR sequence void setup() { pinMode(PTT_key, OUTPUT); myClock.begin(); myDDS.begin(); #ifdef LCD_OUT_INFO pinMode(ANALOG_BUTTONS, INPUT); myScreen.begin(); myScreen.setBacklight(1); myScreen.setFont(1); myScreen.text(0, 0, "QRSS"); myScreen.text(0, 2, "Beacon"); myScreen.setFont(0); myScreen.text(0, 4, "v1.00 - XV4Y"); myScreen.text(0, 5, "Init..."); setup_menu(); // We call the setup menu myDDS.reset(); #else pinMode(StartBtn, INPUT_PULLUP); pinMode(RED_LED, OUTPUT); pinMode(GREEN_LED, OUTPUT); digitalWrite(RED_LED, LOW); digitalWrite(GREEN_LED, LOW); myDDS.reset(); #endif myDDS.SetFrequency( frequencies[freq_select]+random_shift, 0, true ); // Set frequency with DDS power down digitalWrite( PTT_key, LOW ); begin_chunk = myClock.RTC_chunk; begin_sec = myClock.RTC_sec; #if defined(LCD_OUT_INFO) myScreen.clear(); myScreen.setBacklight(1); myScreen.setFont(1); myScreen.text(0, 0, "TIME"); myScreen.setFont(0); myScreen.text(0, 2, "Press button"); myScreen.text(0, 3, "to set time."); myScreen.text(0, 4, "Sending test"); while(read_buttons()!=5) { // We also send a test sequence to help calibrate the transmitter if ((myClock.RTC_chunk-(begin_chunk+0))%240==0) send_test(begin_sec); }; myScreen.setBacklight(0); #else while(digitalRead(StartBtn)) { // This is for blinking LED fast if ((myClock.RTC_chunk-(begin_chunk+15))%60==0) digitalWrite( RED_LED, HIGH ); if ((myClock.RTC_chunk-(begin_chunk+30))%60==0) digitalWrite( GREEN_LED, HIGH ); if ((myClock.RTC_chunk-(begin_chunk+45))%60==0) digitalWrite( RED_LED, LOW ); if ((myClock.RTC_chunk-(begin_chunk+60))%60==0) digitalWrite( GREEN_LED, LOW ); // We also send a test sequence to help calibrate the transmitter if ((myClock.RTC_chunk-(begin_chunk+0))%240==0) send_test(begin_sec); }; #endif myClock.Set_Time(0,0,0); }; //****************************************************************** // Here starts the actual sequence sending void loop() { if (band_set >= 0) freq_select = band_cycling[band_set][cycle_select]; digitalWrite( PTT_key, LOW ); myDDS.SetFrequency( (frequencies[freq_select])+random_shift, 0, true ); // Set frequency with DDS power down begin_chunk = myClock.RTC_chunk; #if defined(LCD_OUT_INFO) myScreen.clear(); myScreen.setBacklight(0); myScreen.setFont(1); myScreen.text(0, 0, "QRSS "); display_number (vitesse, 8, 0); myScreen.setFont(0); myScreen.text(0, 2, ""); myScreen.text(8, 2, ""); myScreen.text(0, 4, "Waiting..."); digitalWrite( PTT_key, LOW ); begin_chunk = myClock.RTC_chunk; while (!(myClock.RTC_sec==1 && (myClock.RTC_min%2)==0)) { // We start each first second of even minutes if (read_buttons()==5){ force_send = 1; // Pressing the button while in wait mode force the sequence to start next time myScreen.text(0, 4, "Send next slot"); myScreen.setBacklight(1); }; if (myClock.RTC_chunk==1 || myClock.RTC_chunk==128) { display_time(); }; }; // Now we start the transmit procedure force_send = 0; // Set PTT high, turn on back light and set DDS digitalWrite( PTT_key, HIGH ); myScreen.setBacklight(1); myDDS.SetFrequency( (frequencies[freq_select])+random_shift, 0, false ); // Set frequency with DDS and turn on DDS // Display the frequency display_freq (frequencies[freq_select]+random_shift, 4); myScreen.text(0, 5, "Sending "); send_sequence(); #else digitalWrite( RED_LED, LOW ); digitalWrite( GREEN_LED, LOW ); while (!(myClock.RTC_sec==1 && (myClock.RTC_min%2)==0)) { // We start each first second of even minutes if ((myClock.RTC_chunk-(begin_chunk+10))%20==0) digitalWrite( GREEN_LED, HIGH ); if ((myClock.RTC_chunk-(begin_chunk+20))%20==0) digitalWrite( GREEN_LED, LOW ); }; force_send = 0; digitalWrite( PTT_key, HIGH ); myDDS.SetFrequency( (frequencies[freq_select])+random_shift, 0, false ); // Set frequency with DDS and turn on DDS send_sequence(); #endif if (band_set >= 0) cycle_select++; // We increase the index through the band cycling table if (cycle_select == MAX_CYCLE) cycle_select = 0; }; //****************************************************************** // Function to send the test sequence void send_test(char begin_sec) { if ((myClock.RTC_sec-(begin_sec+0))%30==0) { digitalWrite( PTT_key, HIGH ); myDDS.SetFrequency( (frequencies[freq_select])+random_shift, 0, false ); #ifdef LCD_OUT_INFO myScreen.text(0, 5, "TX"); myScreen.text(4, 5, "00"); #endif } if ((myClock.RTC_sec-(begin_sec+6))%30==0) { digitalWrite( PTT_key, HIGH ); myDDS.SetFrequency( (frequencies[freq_select])+random_shift, (qrss_shift/3), false ); #ifdef LCD_OUT_INFO myScreen.text(0, 5, "TX"); myScreen.text(4, 5, "01"); #endif } if ((myClock.RTC_sec-(begin_sec+12))%30==0) { digitalWrite( PTT_key, HIGH ); myDDS.SetFrequency( (frequencies[freq_select])+random_shift, ((qrss_shift/3)*2), false ); #ifdef LCD_OUT_INFO myScreen.text(0, 5, "TX"); myScreen.text(4, 5, "10"); #endif } if ((myClock.RTC_sec-(begin_sec+18))%30==0) { digitalWrite( PTT_key, HIGH ); myDDS.SetFrequency( (frequencies[freq_select])+random_shift, (qrss_shift), false ); #ifdef LCD_OUT_INFO myScreen.text(0, 5, "TX"); myScreen.text(4, 5, "11"); #endif } if ((myClock.RTC_sec-(begin_sec+24))%30==0) { digitalWrite( PTT_key, LOW ); myDDS.SetFrequency( (frequencies[freq_select])+random_shift, 0, 1 ); #ifdef LCD_OUT_INFO myScreen.text(0, 5, "--"); myScreen.text(4, 5, "00"); #endif } }; //****************************************************************** // Function to send the sequence void send_sequence() { while (character != _WAIT) { myDDS.SetFrequency( (frequencies[freq_select])+random_shift, 0, false ); msgIndex = 1; while (msgIndex < msg[0]+1) { // We read each character in the array character = msg[msgIndex]; inc_bit = 8; // Special characters if (character == _SPC) { // For inter-words spacing, we need 5 dots, since we already have 1+2, we need 2 more begin_sec = myClock.RTC_sec; #ifdef LCD_OUT_INFO myScreen.text(0, 4, "Break"); #endif while ( ((60+myClock.RTC_sec-begin_sec)%60)<(2*vitesse) ) { delay(0); }; inc_bit = 0; }; if (character == _WAIT) { // For WAIT or end of sequence, we just skip sending #ifdef LCD_OUT_INFO myScreen.text(0, 4, "End of sequence"); #endif inc_bit = 0; }; while (inc_bit) { // We read each bit of the character etat = bitRead(character,inc_bit-1); if (start) { if (etat) { key=3; // If the bit is 1 then it is a dash #ifdef LCD_OUT_INFO myScreen.text(8, 5, "Dash "); #endif } else { key=1; // If the bit is 0 then it is a dot #ifdef LCD_OUT_INFO myScreen.text(8, 5, "Dot "); #endif } while (key) { myDDS.SetFrequency( (frequencies[freq_select]+random_shift), qrss_shift, false ); #ifdef LCD_OUT_INFO #else digitalWrite( RED_LED, HIGH ); digitalWrite( GREEN_LED, HIGH ); #endif // We wait 1 second x VITESSE parameter begin_sec = myClock.RTC_sec; while ( ((60+myClock.RTC_sec-begin_sec)%60)<(1*vitesse) ) { #ifdef LCD_OUT_INFO if (myClock.RTC_chunk==1 || myClock.RTC_chunk==128) display_time(); // Display time only twice a second #else delay(0); #endif }; key--; } myDDS.SetFrequency( (frequencies[freq_select])+random_shift, 0, false ); #ifdef LCD_OUT_INFO myScreen.text(8, 5, " "); #else digitalWrite( RED_LED, LOW ); digitalWrite( GREEN_LED, LOW ); #endif // We wait 1 second x VITESSE parameter begin_sec = myClock.RTC_sec; while ( ((60+myClock.RTC_sec-begin_sec)%60)<(1*vitesse) ) { #ifdef LCD_OUT_INFO if (myClock.RTC_chunk==1 || myClock.RTC_chunk==128) display_time(); // Display time only twice a second #else delay(0); #endif }; } if (!etat && !start) start=true; inc_bit--; } // We add one space between each characteur (lenght is 3 dots, including the one in the here above loop) begin_sec = myClock.RTC_sec; #ifdef LCD_OUT_INFO myScreen.text(8, 5, "Space"); #endif while ( ((60+myClock.RTC_sec-begin_sec)%60)<(2*vitesse) ) { #ifdef LCD_OUT_INFO if (myClock.RTC_chunk==1 || myClock.RTC_chunk==128) display_time(); // Display time only twice a second #else delay(0); #endif }; start = false; msgIndex++; }; }; character = _SPC; }; #if defined(LCD_OUT_INFO) //****************************************************************** // Setup menu at startup in 2 parts void setup_menu() { // Here we wait 5 seconds more and check if the user wants to change settings begin_sec = myClock.RTC_sec; while ( ((60 + myClock.RTC_sec - begin_sec)%60) < 5 ) { if ( read_buttons()==5 ) { break; }; delay(10); } // If button pressed we enter setting if(read_buttons()==5) { while (read_buttons()==5) delay(0); // Debounce setup_menu_1(); while (read_buttons()==5) delay(0); // Debounce setup_menu_2(); while (read_buttons()==5) delay(0); // Debounce setup_menu_3(); while (read_buttons()==5) delay(0); // Debounce if (band_set == -1) { setup_menu_4(); while (read_buttons()==5) delay(0); // Debounce }; } } void setup_menu_1() { // First we set the frequency myScreen.clear(); myScreen.setBacklight(1); myScreen.setFont(1); myScreen.text(0, 0, "SETUP 1"); myScreen.setFont(0); myScreen.text(0, 2, "Press up/dwn"); myScreen.text(0, 3, "to set Band"); myScreen.text(0, 4, "center Freq"); while (1) { display_freq (frequencies[freq_select], 5); if(read_buttons()==1) { // Up we increase while (read_buttons()==1) delay(0); // Debounce freq_select++; if (freq_select > 14) freq_select = 0; } else if (read_buttons()==3) { // Down we decrease while (read_buttons()==3) delay(0); // Debounce freq_select--; if (freq_select < 0) freq_select = 14; } else if (read_buttons()==5) { // OK we go out while (read_buttons()==5) delay(0); // Debounce break; } } } void setup_menu_2() { // Then we set the shift widht mode myScreen.clear(); myScreen.setBacklight(1); myScreen.setFont(1); myScreen.text(0, 0, "SETUP 2"); myScreen.setFont(0); myScreen.text(0, 2, "Press up/dwn"); myScreen.text(0, 3, "to set QRSS"); myScreen.text(0, 4, "shift width"); while (1) { display_number (byte(qrss_shift/100), 0, 5); myScreen.text(2, 5, " Hz "); if(read_buttons()==1) { // Up we increase delay(200); // Debounce qrss_shift+=100; if (qrss_shift > 2000) qrss_shift = 2000; } else if (read_buttons()==3) { // Down we decrease delay(200); // Debounce qrss_shift-=100; if (qrss_shift < 100) qrss_shift = 100; } else if (read_buttons()==5) { // OK we go out while (read_buttons()==5) delay(0); // Debounce break; } } } void setup_menu_3() { // Then we decide if we cycle through frequencies myScreen.clear(); myScreen.setBacklight(1); myScreen.setFont(1); myScreen.text(0, 0, "SETUP 3"); myScreen.setFont(0); myScreen.text(0, 2, "Press up/dwn"); myScreen.text(0, 3, "to set band"); myScreen.text(0, 4, "cycling mode"); while (1) { if (band_set == -1) { myScreen.text(0, 5, "Disabled "); } else if (band_set == 0) { myScreen.text(0, 5, "User "); } else if (band_set == 1) { myScreen.text(0, 5, "Low bands "); } else if (band_set == 2) { myScreen.text(0, 5, "High bands "); } else if (band_set == 3) { myScreen.text(0, 5, "WARC bands "); }; if(read_buttons()==1) { // Up toggle modes while (read_buttons()==1) delay(0); // Debounce band_set++; if (band_set > 3) band_set = 3; } else if (read_buttons()==3) { // Down toggle modes while (read_buttons()==3) delay(0); // Debounce band_set--; if (band_set < -1) band_set = -1; } else if (read_buttons()==5) { // OK we go out while (read_buttons()==5) delay(0); // Debounce break; } } } void setup_menu_4() { // Then we adjust frequency manually myScreen.clear(); myScreen.setBacklight(1); myScreen.setFont(1); myScreen.text(0, 0, "SETUP 4"); myScreen.setFont(0); myScreen.text(0, 2, "Press up/dwn"); myScreen.text(0, 3, "adjust TX Freq"); myScreen.text(0, 4, "by 5Hz step"); while (1) { display_freq (frequencies[freq_select]+random_shift, 5); if(read_buttons()==1) { // Up we increase delay(200); // Debounce random_shift+=5; if (random_shift > 150) random_shift = 150; } else if (read_buttons()==3) { // Down we decrease delay(200); // Debounce random_shift-=5; if (random_shift < -150) random_shift = -150; } else if (read_buttons()==5) { // OK we go out while (read_buttons()==5) delay(0); // Debounce break; } } } //****************************************************************** // Display the frequency void display_freq (unsigned long freq, char ligne) { myScreen.text(10, ligne, " Hz "); chaine_txt[5] = 0x00; chaine_txt[4] = 0x30 + (((freq)/1)%10); chaine_txt[3] = 0x30 + (((freq)/10)%10); chaine_txt[2] = 0x30 + (((freq)/100)%10); chaine_txt[1] = '.'; chaine_txt[0] = 0x30 + (((freq)/1000)%10); myScreen.text(5, ligne, chaine_txt); chaine_txt[5] = 0x00; chaine_txt[4] = 0x30 + (((freq)/10000)%10); chaine_txt[3] = 0x30 + (((freq)/100000)%10); chaine_txt[2] = '.'; chaine_txt[1] = 0x30 + (((freq)/1000000)%10); chaine_txt[0] = 0x30 + (((freq)/10000000)%10); myScreen.text(0, ligne, chaine_txt); } //****************************************************************** // Display the time void display_time () { chaine_txt[5] = 0x00; chaine_txt[4] = 0x30 + (myClock.RTC_sec%10); chaine_txt[3] = 0x30 + ((myClock.RTC_sec/10)%10); chaine_txt[2] = ':'; chaine_txt[1] = 0x30 + (myClock.RTC_min%10); chaine_txt[0] = 0x30 + ((myClock.RTC_min/10)%10); myScreen.text(7, 3, chaine_txt); } //****************************************************************** // Display a 2 digits number void display_number (byte number, char column, char ligne) { chaine_txt[2] = 0x00; chaine_txt[1] = 0x30 + (number%10); chaine_txt[0] = 0x30 + ((number/10)%10); myScreen.text(column, ligne, chaine_txt); } //****************************************************************** // Return a button value depending on the analog reading byte read_buttons () { int value = analogRead(ANALOG_BUTTONS); if ( value<(1+Analog_Noise) ) { return 1; } else if ( value<(78+Analog_Noise) ) { return 2; } else if ( value<(146+Analog_Noise) ) { return 3; } else if ( value<(205+Analog_Noise) ) { return 4; } else if ( value<(255+Analog_Noise) ) { return 5; } else { return 0; } } #endif //****************************************************************** // Interruption for the RTC clock interrupt(TIMER1_A0_VECTOR) Tic_Tac(void) { myClock.Inc_chunk(); // Update chunks };
DDS Simple VFO firmware
/* Simple VFO with DDS for MSP430G2553 * Code for Energia 009 * By Yannick DEVOS - XV4Y - January 2014 http://xv4y.radioclub.asia/ Copyright 2012-2013-2014 Yannick DEVOS under GPL 3.0 license Any commercial use or inclusion in a kit is subject to author approval ==== * Agile Frequency generation using AD9850/9851 DDS * Output to Nokia 5110 compatible LCD * Check if P2_2 has changed state and switch VFO (like when PTT is pressed to operate split) Usage for short press of buttons : - Up / Down increase or decrease the frequency following the choosen digit - Left / Right increase or decrease the digit - OK validate the frequency and send it to the DDS Long press of buttons : - Left Set the current VFO to the next band bottom frequency - Right VFO A = VFO B - OK Switch between VFO A and VFO B ==== Revision history : v1.00 2013-03-18 First release ==== This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You can download a copy of the GNU General Public License at <http://www.gnu.org/licenses/> */ // Here modify to your taste or needs #define PTT_key P2_2 #define ANALOG_BUTTONS A5 #define AUTO_VALID // Uncomment if you want the DDS frequency to be udapted automatically after each value change #define DEFAULT_FREQ 8 // Value 8 in Frequencies array is 14000000 // Here under don't touch anything unless you know what you do #include <legacymsp430.h> #include <AD9850.h> // Library for AD9850 control by MSP430 #include <LCD_5110.h> // Frequencies (1 Hz precision) you can select const unsigned long int frequencies[] = { 137000, 471000, 501000, 1830000, 3500000, 5200000, 7000000, 10100000, 14000000, 18068000, 21000000, 24890000, 28000000, 50000000, 70000000}; const byte Analog_Noise = 5; // Margins for reading analog buttons boolean vfo=0, saved_PTT; // VFO A or B and PTT input state char multiplier, aff_multiplier, freq_select=DEFAULT_FREQ; unsigned long int frequency_A=frequencies[DEFAULT_FREQ], frequency_B=frequencies[DEFAULT_FREQ], debounce; char chaine_txt[6] = {' ', ' ', ' ', ' ', ' ', 0x00}; AD9850 myDDS (P1_0, P1_1, P1_2, P1_4); // Call the AD9850 Library, AD9850 pins for CLOCK, LOAD, DATA and RESET //AD9850 myDDS (P1_1, P1_2, P1_0, P1_4); // Call the AD9850 Library, AD9851 pins for CLOCK, LOAD, DATA and RESET LCD_5110 myScreen(P2_3, // Chip Select * P1_6, // Serial Clock * P2_5, // Serial Data * P2_4, // Data/Command * NULL, // Reset * P1_7, // Backlight NULL); // Push Button 0 //****************************************************************** // Defining pins mode and initializing hardware void setup() { pinMode(PTT_key, INPUT_PULLUP); myDDS.begin(); pinMode(ANALOG_BUTTONS, INPUT); myScreen.begin(); myScreen.setBacklight(1); myScreen.setFont(1); myScreen.text(0, 0, "sVFO"); myScreen.text(0, 2, "AD9850"); myScreen.setFont(0); myScreen.text(0, 4, "v1.00 - XV4Y"); myScreen.text(0, 5, "Init..."); delay(1000); myDDS.reset(); myDDS.SetFrequency( frequency_A, 0, false ); digitalWrite( PTT_key, LOW ); }; //****************************************************************** // Here starts the actual sequence sending void loop() { myScreen.clear(); myScreen.setBacklight(1); myScreen.setFont(1); if (vfo == 0) { myScreen.text(0, 0, "VFO A"); myDDS.SetFrequency( frequency_A, 0, false ); } else { myScreen.text(0, 0, "VFO B"); myDDS.SetFrequency( frequency_B, 0, false ); }; myScreen.setFont(0); display_freq (frequency_A, 3); display_freq (frequency_B, 5); while (read_buttons()==5 || read_buttons()==4 || read_buttons()==2) delay(10); // Debounce except for UP/DWN while (1) { // Update the frequencies display if (multiplier > 5) { aff_multiplier = multiplier + 2; } else if (multiplier > 2) { aff_multiplier = multiplier + 1; } else { aff_multiplier = multiplier; }; myScreen.text(0, 4, " "); if (vfo == 0) { myScreen.text(9-aff_multiplier, 4, "^"); } else { myScreen.text(9-aff_multiplier, 4, "v"); } display_freq (frequency_A, 3); display_freq (frequency_B, 5); // Read the analog buttons input if(read_buttons()==1) { // Up we increase frequency delay(200); // Debounce if (vfo == 0) { frequency_A = frequency_A + powf(10,(float)multiplier); } else { frequency_B = frequency_B + powf(10,(float)multiplier); }; #if defined AUTO_VALID break; #endif } else if (read_buttons()==3) { // Down we decrease frequency delay(200); // Debounce if (vfo == 0) { frequency_A = frequency_A - powf(10,(float)multiplier); } else { frequency_B = frequency_B - powf(10,(float)multiplier); }; #if defined AUTO_VALID break; #endif } else if (read_buttons()==2) { // Left we increase multiplier debounce = millis(); while (read_buttons()==2) { //Debounce if ((millis()-debounce)>1000) { // Long press we do "Band UP" freq_select++; if (freq_select > 14) freq_select = 0; if (vfo == 1) frequency_B = frequencies[freq_select]; else frequency_A=frequencies[freq_select]; multiplier--; break; }; }; multiplier++; if (multiplier > 7) multiplier = 7; } else if (read_buttons()==4) { // Right we decrease multiplier debounce = millis(); while (read_buttons()==4) { //Debounce if ((millis()-debounce)>1000) { // Long press we do VFO A=B if (vfo == 1) frequency_A = frequency_B; else frequency_B=frequency_A; multiplier++; break; }; }; multiplier--; if (multiplier < 0) multiplier = 0; } else if (read_buttons()==5) { // OK we go out debounce = millis(); while (read_buttons()==5) { //Debounce if ((millis()-debounce)>1000) { // Long press we switch VFO A/B if (vfo == 1) vfo=0; else vfo=1; break; }; }; break; // Short press we just leave the loop so the frequency is transmitted to the AD9850 } // Check if we are transmitting split (momentaneous VFO A->B switch) if (saved_PTT != digitalRead(PTT_key)) { saved_PTT = digitalRead(PTT_key); if (vfo == 1) vfo=0; else vfo=1; break; }; } }; //****************************************************************** // Display the frequency void display_freq (unsigned long freq, char ligne) { myScreen.text(10, ligne, " Hz "); chaine_txt[5] = 0x00; chaine_txt[4] = 0x30 + (((freq)/1)%10); chaine_txt[3] = 0x30 + (((freq)/10)%10); chaine_txt[2] = 0x30 + (((freq)/100)%10); chaine_txt[1] = '.'; chaine_txt[0] = 0x30 + (((freq)/1000)%10); myScreen.text(5, ligne, chaine_txt); chaine_txt[5] = 0x00; chaine_txt[4] = 0x30 + (((freq)/10000)%10); chaine_txt[3] = 0x30 + (((freq)/100000)%10); chaine_txt[2] = '.'; chaine_txt[1] = 0x30 + (((freq)/1000000)%10); chaine_txt[0] = 0x30 + (((freq)/10000000)%10); myScreen.text(0, ligne, chaine_txt); } //****************************************************************** // Display a 2 digits number void display_number (byte number, char column, char ligne) { chaine_txt[2] = 0x00; chaine_txt[1] = 0x30 + (number%10); chaine_txt[0] = 0x30 + ((number/10)%10); myScreen.text(column, ligne, chaine_txt); } //****************************************************************** // Return a button value depending on the analog reading byte read_buttons () { int value = analogRead(ANALOG_BUTTONS); if ( value<(1+Analog_Noise) ) { return 1; } else if ( value<(78+Analog_Noise) ) { return 2; } else if ( value<(146+Analog_Noise) ) { return 3; } else if ( value<(205+Analog_Noise) ) { return 4; } else if ( value<(255+Analog_Noise) ) { return 5; } else { return 0; } }
Bonjour,
I am very interested in your developmets for Energia, exspecially for the new Tiva C.
The WSPR beacon would be nice to get work on Tiva Lauchpad.
I cannot find any downloads on your page.
73 Jouko OH5RM
Hi Jouko,
I have no more time to work on Energia unfortunately. However I think the code should be portable from MSP430 to Tiva without too much work.
Right now, you can download the source code in the online shop. You have to register.
Once CQ Magazine has published my article (february issue I think), I will update this page with all the related material.
I am just waiting for them…
73,
Yan – XV4Y.
Hi Yan,
Thanks for the info. Yes I will try to convert the code to Tiva. But downloading it is a problem.
I did register myself but at checkout it keeps asking abt paying method (free) and let me not complete.
73 Jouko OH5RM