///////////////////////////////////////////////////////// // Authors: G3ZIL Gwyn Griffiths gxgriffiths@virginmedia.com // with grateful thanks to OE1CGS Christoph Schwärzler for the Si5351A functions for LO and WSPR TX ///////////////////////////////////////////////////////// // // Code for a multi-frequency WSPR transmitter/receiver using the Si5351a module kit http://www.hanssummers.com/synth // Si5351A control using OE1CGS sketch // This version copes with full frequency range of the Si5351A and uses the TWI Wire Library // Please also refer to SiLabs AN619 which describes all the registers to use // // Uses the QRPLabs Arduino shield and the Si5351A synth board with an Uno clone // Display uses 16x2 LCD screen // Temperature of the Si5351A chip using a TMP36 temperature sensor, superglued to the Si5351A // Direct conversion receiver Main Board PCB G3ZIL/W3PM design, without filter components // QRP Labs (5) relay-switched filter board with G3ZIL/W3PM single xtal filters in same form factor as QRP Labs LPFs // Initial set for 20, 30, 40 and 60 metres receive. // Single frequency for WSPR Transmit, need to manually plug in selected band's LPF // Real Time Clock using a DS3231 board, which has a TXCO. Its temperature sensor has new value every 64 seconds. // // August 2015 - March 2016 // pin summary for reference // d0 Reserved: USB SERIAL IN // d1 Reserved: USB SERIAL OUT // d2 Menu - Interrupt enabled // d3 Antenna change-over relay Normally HIGH = OFF, antenna to receiver. LOW = ON = Tx. // d4 LCD d7 // d5 Reserved: CLK0 from Si5351A daughter board. // d6 LCD d6 // d7 LCD d5 // d8 LCD d4 // d9 Relay 1 // d10 LCD enable // d11 LCD RS // d12 Encoder A // d13 Encoder B // A0 Temperature sensor // A1 Relay 4 // A2 Relay 3 // A3 Relay 5 // A4 Reserved: TWI - SDA - reserved [Used to talk to the Si5351A and the DS3231] // A5 Reserved: TWI - SCL - reserved [Used to talk to the Si5351A and the DS3231] - GG using edit button and getting away with it // include the LCD, TWI Wire and integer types library code #include #include #include int readEncoder(); // initialize with the numbers of the LCD interface pins // defined as LiquidCrystal(rs, enable, d4, d5, d6, d7) LiquidCrystal lcd(11,10,8,7,6,4); // now the encoder int encoderPinA = 12; int encoderPinB = 13; int encoderPinALast = LOW; int encoderRead = LOW; long lastTime=0; int encoder0Pos = 0; // now the TMP36 temperature sensor int tmp36Pin = A0; // now for the edit/menu buttons int editPin = A5; // using the analogue pin - this is a conflict with TWI but I get away with it. int menuPin = 2; int menuPinStatus = 1; // normally high, switch pulls low int editPinStatus = 1; // normally high, switch pulls low int menu_int_status =0; // gets set to one when an interrupt happens, clears when dealt with // now the relay control pins int Relay5 = A3; // Default position for 14MHz WSPR Xtal filter int Relay4 = A1; // Default position for 10MHz WSPR Xtal filter int Relay3 = A2; // Default position for 7MHz WSPR Xtal filter int Relay1 = 9; // Default position for 5MHz WSPR Xtal filter int TxRxRelay = 3; // Antenna change over relay HIGH=OFF=Rx LOW=ON=Tx // set up default cycle time for transmit - set at 2, that is transmit at 0, 4, 8 etc mins with receive between int N_cyc=2; int secstoGo =0; #define F_XTAL 27004486; //This is best initial measurement, can be updated #define c 1048574; // "c" part of Feedback-Multiplier from XTAL to PLL #define DS3231_I2C_ADDRESS 0x68 // defines the TWI (I2C) address for the DS3231 Real Time Clock uint32_t WSPR_60m_Dial=5287200; // WSPR dial frequency for 60 metres uint32_t WSPR_40m_Dial=7038600; // WSPR dial frequency for 40 metres uint32_t WSPR_30m_Dial=10138700; // WSPR dial frequency for 30 metres uint32_t WSPR_20m_Dial=14095600; // WSPR dial frequency for 20 metres int clk = 1; // which CLK output to set for Rx LO. The hardware uses CLK1 for the Rx LO. CK2 is available to an external BNC int band = 0; // CLK0 is used for Tx int menu_item = 0; // for the transmit capability, as in OE1CGS sketch unsigned long lasttime; // Time variable for next action byte channel; // channel value [0-3] unsigned long TX_MSNB_P2; // Si5351 register MSNB_P2 PLLB for Tx unsigned long TX_P2; // Variable values for MSNB_P2 which defines the frequencies for the data // G3ZIL IO90 0.5W generated on Raspberry Pi wspr using: wspr0 Tx 0 0.0015 0 G3ZIL IO90 27 11 (where the 11 is the option to list the codes const byte message[] ={ 3, 1, 0, 2, 0, 2, 2, 2, 1, 0, 2, 2, 3, 3, 1, 2, 2, 0, 3, 2, 2, 1, 0, 3, 1, 1, 3, 0, 0, 2,\ 2, 0, 0, 2, 1, 2, 0, 1, 0, 1, 0, 0, 2, 0, 0, 0, 3, 0, 1, 1, 0, 0, 3, 1, 0, 1, 0, 0, 2, 3,\ 3, 2, 1, 2, 2, 0, 2, 1, 3, 2, 1, 0, 3, 0, 3, 2, 3, 2, 0, 3, 2, 2, 1, 0, 3, 1, 2, 2, 2, 1,\ 1, 0, 1, 0, 3, 0, 2, 2, 1, 0, 0, 2, 0, 2, 3, 0, 0, 1, 0, 2, 3, 1, 1, 0, 3, 3, 2, 0, 3, 3,\ 2, 1, 0, 2, 2, 3, 1, 3, 2, 0, 0, 0, 0, 1, 0, 3, 2, 0, 1, 1, 0, 2, 0, 0, 2, 0, 2, 1, 1, 2,\ 3, 0, 1, 3, 2, 0, 0, 1, 1, 0, 2, 2}; const int chan_mult[] ={384, 640, 896, 1280}; void setup() { Serial.begin(9600); //open a serial port - for debugging, commented out normally Wire.begin(); // Initialize I2C-communication as master // SDA on pin ADC04 // SCL on pin ADC05 Set_RX_LO_Frequency (7038600); // Set TX-Frequency default at start is for WSPR 40m 7.0386MHz SetPower (1); RX_LO_ON(); pinMode (encoderPinA,INPUT); pinMode (encoderPinB,INPUT); pinMode (menuPin, INPUT_PULLUP); pinMode (editPin, INPUT_PULLUP); attachInterrupt (0, menu_ISR,FALLING);// Pins 2 and 3 on an Uno are the interrupts 0 and 1 respectively // menu_ISR is the interrupt service routine, triggered on falling edge pinMode (Relay5,OUTPUT); // these are for the band-changing relays pinMode (Relay4,OUTPUT); pinMode (Relay3,OUTPUT); pinMode (Relay1,OUTPUT); digitalWrite(Relay5, HIGH); // Relays are active low, so all high to start with on the xtal filter bank digitalWrite(Relay4, HIGH); digitalWrite(Relay3, HIGH); digitalWrite(Relay1, HIGH); pinMode (TxRxRelay, OUTPUT); digitalWrite(TxRxRelay, HIGH); // High=OFF=RX for the antenna change over // set up the number of columns and rows on the LCD and initital message on first line lcd.begin(16, 2); lcd.setCursor(0, 0); lcd.print("G3ZIL WSPR RxTx5"); delay (1000); menu_int_status =0; // set the initial time here: Put in the parameters, uncomment the function call, upload, run, then comment out again // DS3231 seconds, minutes, hours, day, date, month, year // setDS3231time(0,0,19,5,25,2,16); } void loop() { lcd.setCursor(0, 1); lcd.print(">Press Menu"); while (menu_int_status == 0) // loop here whle no interrupt to service { lcdwriteTemperature(); // reads TMP36 temperature sensor, e.g, attached to the Si5351A crystal and writes to the 16x2 LCD screen LCDisplayTime(); // display the hhmm time on the LCD second line delay (200); } delay (200); // just to avoid bounces menu_int_status=0; // clear the menu interrupt status flag while (digitalRead(editPin) == HIGH) { menu_item=readEncoder() % 3; // here we read the encoder with a function that returns a value 0, 1, 2 at present lcd.setCursor(0, 1); // set to second line of display, where > and ? indicate input mode switch (menu_item) // this switch case loops when Edit button is not pressed, so display echoes menu item to select { case 0: lcd.print(">Set Rx Band "); break; case 1: lcd.print(">Turn On Tx "); break; case 2: lcd.print(">Turn off Tx "); break; } } delay (500); switch (menu_item) // this switch case loops when Edit button is not pressed, so display echoes menu item to select { case 0: while (digitalRead(editPin) == HIGH) { band=readEncoder() % 4; // here we read the encoder with a function that returns a value 0, 1, 2 or 3 at present // mapping to 20m, 30m, 40m, and 60m. Apply the modulo function (%), so the selection repeats every 4 lcd.setCursor(0, 1); // set to second line of display, where > and ? indicate input mode switch (band) // this switch case loops when Edit button is not pressed, so display echoes band to select { case 0: lcd.print(">Band=20m? "); break; case 1: lcd.print(">Band=30m? "); break; case 2: lcd.print(">Band=40m? "); break; case 3: lcd.print(">Band=60m? "); break; } } // Serial.print ("Edit pressed "); lcd.setCursor(0, 1); // just to be sure switch (band) // this switch case is executed when Edit button is pressed. { case 0: lcd.print(" Band=20m "); // Display loses > and ? indicating operation mode digitalWrite(Relay5, LOW); // Relay 5 on for 20 metres, make sure others are off digitalWrite(Relay4, HIGH); digitalWrite(Relay3, HIGH); digitalWrite(Relay1, HIGH); Set_RX_LO_Frequency (WSPR_20m_Dial); // Set RX-Frequency WSPR 20m RX_LO_ON(); break; case 1: lcd.print(" Band=30m "); digitalWrite(Relay4, LOW); // Relay 4 on for 30 metres, make sure others are off digitalWrite(Relay5, HIGH); digitalWrite(Relay3, HIGH); digitalWrite(Relay1, HIGH); Set_RX_LO_Frequency (WSPR_30m_Dial); // Set RX-Frequency WSPR 30m RX_LO_ON(); break; case 2: lcd.print(" Band=40m "); digitalWrite(Relay3, LOW); // Relay 3 on for 40 metres, make sure others are off digitalWrite(Relay5, HIGH); digitalWrite(Relay4, HIGH); digitalWrite(Relay1, HIGH); Set_RX_LO_Frequency (WSPR_40m_Dial); // Set RX-Frequency WSPR 40m RX_LO_ON(); break; case 3: lcd.print(" Band=60m "); Serial.print (" 60m selected "); digitalWrite(Relay1, LOW); // Relay 1 on for 60 metres, make sure others are off digitalWrite(Relay5, HIGH); digitalWrite(Relay4, HIGH); digitalWrite(Relay3, HIGH); Set_RX_LO_Frequency (WSPR_60m_Dial); // Set RX-Frequency WSPR 60m RX_LO_ON(); break; } // Serial.print (" Band setting done "); break; case 1: // This is the Tx turn on - but ... will wait here in this loop, so not good, but simple for now while (digitalRead(editPin) == HIGH) { N_cyc=readEncoder() % 6; // here we read the encoder with a function that returns a value 0, 1, 2, 3, 4, or 5 at present // after adding 1 this is the multiplier for 2 minute intervals for transmission lcd.setCursor(0, 1); switch (N_cyc) { case 0: lcd.print(">Cycle 2min"); break; case 1: lcd.print(">Cycle 4min"); break; case 2: lcd.print(">Cycle 6min"); break; case 3: lcd.print(">Cycle 8min"); break; case 4: lcd.print(">Cycle 10min"); break; case 5: lcd.print(">Cycle 12min"); break; } } delay (500); while (digitalRead(editPin) == HIGH) { band=readEncoder() % 4; // here we read the encoder with a function that returns a value 0, 1, 2 or 3 at present // mapping to 20m, 30m, 40m, and 60m. Apply the modulo function (%), so the selection repeats every 4 lcd.setCursor(0, 1); // set to second line of display, where > and ? indicate input mode switch (band) // this switch case loops when Edit button is not pressed, so display echoes band to select { case 0: lcd.print(">Band=20m? "); break; case 1: lcd.print(">Band=30m? "); break; case 2: lcd.print(">Band=40m? "); break; case 3: lcd.print(">Band=60m? "); break; } } lcd.setCursor(0, 1); // just to be sure switch (band) // this switch case is executed when Edit button is pressed. { case 0: lcd.print(" Band=20m "); // Display loses > and ? indicating operation mode TX_MSNB_P2 = Set_TX_Frequency (14097038); // Set TX-Frequency [Hz] for channel "0" 20m at base +38Hz break; case 1: lcd.print(" Band=30m "); TX_MSNB_P2 = Set_TX_Frequency (10140138); // Set TX-Frequency [Hz] for channel "0" 30m at base +38Hz break; case 2: lcd.print(" Band=40m "); TX_MSNB_P2 = Set_TX_Frequency (7040038); // Set TX-Frequency [Hz] for channel "0" 40m at base +38Hz break; case 3: lcd.print(" Band=60m "); TX_MSNB_P2 = Set_TX_Frequency (5288638); // Set TX-Frequency [Hz] for channel "0" 60m at base +38Hz break; } delay (2000); while (menu_int_status==0) // stay in this transmit loop until menu pressed, then completes a wait and transmit cycle { lcd.setCursor(0, 1); lcd.print("TX wt Rx on "); // while (millis() % (119050*(N_cyc+1)) > 1000) // Wait for the first second in every (Ncyc+1)*2 minutes Commented out as for Arduino free running clock while (getDS3231seconds() % (120*(N_cyc+1)) > 1) // Wait for first second in every (Ncyc+1)*2 minutes. Function getDS3231seconds returns seconds into each hour { int secstoGo=120*(N_cyc+1)-(getDS3231seconds() % (120*(N_cyc+1))); // recall % is modulo, this does a countdown to next transmission on selected even minute interval String secsString=u2s(secstoGo,3); // u2s is a useful function from Morris Dovey on the Arduino forum. Converts unsigned integer of in this case 3 digits, to a right justified string lcd.setCursor(12, 1); lcd.print(secsString); lcdwriteTemperature(); //reads TMP36 temperature sensor, e.g, attached to the Si5351A crystal and writes to the 16x2 LCD screen } lcd.setCursor(0, 1); digitalWrite(TxRxRelay, LOW); // TxRx antenna change over relay to active LOW = Tx lcd.print("TX active Rx off"); TX_ENB(); // Switch Park Mode off and CLK0 on RX_LO_OFF(); // Turn CLK 1 for Rx LO off for (int i=0;i 900){ // If output divider out of range (>900) use additional Output divider R = R * 2; outdivider = outdivider / 2; } if (outdivider % 2) outdivider--; // finds the even divider which delivers the intended Frequency fvco = outdivider * R * frequency; // Calculate the PLL-Frequency (given the even divider) switch (R){ // Convert the Output Divider to the bit-setting required in register 44 case 1: R = 0; break; // Bits [6:4] = 000 case 2: R = 16; break; // Bits [6:4] = 001 case 4: R = 32; break; // Bits [6:4] = 010 case 8: R = 48; break; // Bits [6:4] = 011 case 16: R = 64; break; // Bits [6:4] = 100 case 32: R = 80; break; // Bits [6:4] = 101 case 64: R = 96; break; // Bits [6:4] = 110 case 128: R = 112; break; // Bits [6:4] = 111 } a = fvco / F_XTAL; // Multiplier to get from Quartz-Oscillator Freq. to PLL-Freq. f = fvco - a * F_XTAL; // Multiplier = a+b/c f = f * c; // this is just "int" and "float" mathematics f = f / F_XTAL; b = f; MS0_P1 = 128 * outdivider - 512; // Calculation of Output Divider registers MS0_P1 to MS0_P3 // MS0_P2 = 0 and MS0_P3 = 1; these values are hardcoded, see below f = 128 * b / c; // Calculation of Feedback Multisynth registers MSNA_P1 to MSNA_P3 MSNA_P1 = 128 * a + f - 512; MSNA_P2 = f; MSNA_P2 = 128 * b - MSNA_P2 * c; MSNA_P3 = c; Si5351a_Write_Reg (17, 128); // Disable output during the following register settings Si5351a_Write_Reg (26, (MSNA_P3 & 65280) >> 8); // Bits [15:8] of MSNA_P3 in register 26 Si5351a_Write_Reg (27, MSNA_P3 & 255); // Bits [7:0] of MSNA_P3 in register 27 Si5351a_Write_Reg (28, (MSNA_P1 & 196608) >> 16); // Bits [17:16] of MSNA_P1 in bits [1:0] of register 28 Si5351a_Write_Reg (29, (MSNA_P1 & 65280) >> 8); // Bits [15:8] of MSNA_P1 in register 29 Si5351a_Write_Reg (30, MSNA_P1 & 255); // Bits [7:0] of MSNA_P1 in register 30 Si5351a_Write_Reg (31, ((MSNA_P3 & 983040) >> 12) | ((MSNA_P2 & 983040) >> 16)); // Parts of MSNA_P3 und MSNA_P1 Si5351a_Write_Reg (32, (MSNA_P2 & 65280) >> 8); // Bits [15:8] of MSNA_P2 in register 32 Si5351a_Write_Reg (33, MSNA_P2 & 255); // Bits [7:0] of MSNA_P2 in register 33 Si5351a_Write_Reg (50, 0); // Bits [15:8] of MS1_P3 (always 0) in register 50 Si5351a_Write_Reg (51, 1); // Bits [7:0] of MS1_P3 (always 1) in register 51 Si5351a_Write_Reg (52, ((MS0_P1 & 196608) >> 16) | R); // Bits [17:16] of MS1_P1 in bits [1:0] and R in [7:4] Si5351a_Write_Reg (53, (MS0_P1 & 65280) >> 8); // Bits [15:8] of MS1_P1 in register 53 Si5351a_Write_Reg (54, MS0_P1 & 255); // Bits [7:0] of MS1_P1 in register 54 Si5351a_Write_Reg (55, 0); // Bits [19:16] of MS1_P2 and MS0_P3 are always 0 Si5351a_Write_Reg (56, 0); // Bits [15:8] of MS1_P2 are always 0 Si5351a_Write_Reg (57, 0); // Bits [7:0] of MS1_P2 are always 0 if (outdivider == 4){ Si5351a_Write_Reg (52, 12 | R); // Special settings for R = 4 (see datasheet) Si5351a_Write_Reg (53, 0); // Bits [15:8] of MS1_P1 must be 0 Si5351a_Write_Reg (54, 0); // Bits [7:0] of MS1_P1 must be 0 } Si5351a_Write_Reg (177, 32); // This resets PLL A } void SetPower (byte power){ // Sets the output power level if (power == 0 || power > 4){power = 0;} // valid power values are 0 (25%), 1 (50%), 2 (75%) or 3 (100%) switch (power){ case 1: Si5351a_Write_Reg (17, 76); // CLK1 drive strength = 2mA; power level ~ -8dB break; case 2: Si5351a_Write_Reg (17, 77); // CLK1 drive strength = 4mA; power level ~ -3dB break; case 3: Si5351a_Write_Reg (17, 78); // CLK1 drive strength = 6mA; power level ~ -1dB break; case 4: Si5351a_Write_Reg (17, 79); // CLK1 drive strength = 8mA; power level := 0dB break; } } //-------------------------------------------------------------------- // Now for the Si5351a TRANSMIT set-up functions Using Clock 0, Multisynth0 and PLLB //-------------------------------------------------------------------- // void TX (unsigned long P2) // Changes TX frequency according to channel { Si5351a_Write_Reg (40, (P2 & 65280) >> 8); // Bits [15:8] of MSNB_P2 in register 40 Si5351a_Write_Reg (41, P2 & 255); // Bits [7:0] of MSNB_P2 in register 41 } void TX_ENB () { // Enables output on CLK0 Si5351a_Write_Reg (16, 111); // Enable output CLK0, Integer Mode, PLLB, MS0 as source } void TX_OFF () { // Disables output on CLK0 Si5351a_Write_Reg (16, 128); } unsigned long Set_TX_Frequency (unsigned long TX_frequency) // Frequency in Hz; must be within [7810 Hz to 200 Mhz] { unsigned long TX_fvco; // VCO frequency (600-900 MHz) of PLL unsigned long TX_outdivider; // Output divider in range [4,6,8-900], even numbers preferred byte TX_R = 1; // Additional Output Divider in range [1,2,4,...128] byte TX_a; // "a" part of Feedback-Multiplier from XTAL to PLL in range [15,90] unsigned long TX_b; // "b" part of Feedback-Multiplier from XTAL to PLL float TX_f; // floating variable, needed in calculation unsigned long MS0_P1; // Si5351a Output Divider register MS0_P1, P2 and P3 are hardcoded below unsigned long MSNB_P1; // Si5351a Feedback Multisynth register MSNB_P1 unsigned long MSNB_P2; // Si5351a Feedback Multisynth register MSNB_P2 unsigned long MSNB_P3; // Si5351a Feedback Multisynth register MSNB_P3 TX_outdivider = 900000000 / TX_frequency; // With 900 MHz beeing the maximum internal PLL-Frequency while (TX_outdivider > 900){ // If output divider out of range (>900) use additional Output divider TX_R = TX_R * 2; TX_outdivider = TX_outdivider / 2; } if (TX_outdivider % 2) TX_outdivider--; // finds the even divider which delivers the intended Frequency TX_fvco = TX_outdivider * TX_R * TX_frequency; // Calculate the PLL-Frequency (given the even divider) switch (TX_R){ // Convert the Output Divider to the bit-setting required in register 44 case 1: TX_R = 0; break; // Bits [6:4] = 000 case 2: TX_R = 16; break; // Bits [6:4] = 001 case 4: TX_R = 32; break; // Bits [6:4] = 010 case 8: TX_R = 48; break; // Bits [6:4] = 011 case 16: TX_R = 64; break; // Bits [6:4] = 100 case 32: TX_R = 80; break; // Bits [6:4] = 101 case 64: TX_R = 96; break; // Bits [6:4] = 110 case 128: TX_R = 112; break; // Bits [6:4] = 111 } TX_a = TX_fvco / F_XTAL; // Multiplier to get from Quartz-Oscillator Freq. to PLL-Freq. TX_f = TX_fvco - TX_a * F_XTAL; // Multiplier = a+b/c TX_f = TX_f * c; // this is just "int" and "float" mathematics TX_f = TX_f / F_XTAL; TX_b = TX_f; MS0_P1 = 128 * TX_outdivider - 512; // Calculation of Output Divider registers MS0_P1 to MS0_P3 // MS0_P2 = 0 and MS0_P3 = 1; these values are hardcoded, see below TX_f = 128 * TX_b / c; // Calculation of Feedback Multisynth registers MSNB_P1 to MSNB_P3 MSNB_P1 = 128 * TX_a + TX_f - 512; MSNB_P2 = TX_f; MSNB_P2 = 128 * TX_b - MSNB_P2 * c; MSNB_P3 = c; Si5351a_Write_Reg (16, 128); // Disable output during the following register settings Si5351a_Write_Reg (34, (MSNB_P3 & 65280) >> 8); // Bits [15:8] of MSNB_P3 in register 34 Si5351a_Write_Reg (35, MSNB_P3 & 255); // Bits [7:0] of MSNB_P3 in register 35 Si5351a_Write_Reg (36, (MSNB_P1 & 196608) >> 10); // Bits [17:16] of MSNB_P1 in bits [7:6] of register 36 so only 10 bits to right shift Si5351a_Write_Reg (37, (MSNB_P1 & 65280) >> 8); // Bits [15:8] of MSNB_P1 in register 37 Si5351a_Write_Reg (38, MSNB_P1 & 255); // Bits [7:0] of MSNB_P1 in register 38 Si5351a_Write_Reg (39, ((MSNB_P3 & 983040) >> 12) | ((MSNB_P2 & 983040) >> 16)); // Parts of MSNB_P3 and MSNB_P1 Si5351a_Write_Reg (40, (MSNB_P2 & 65280) >> 8); // Bits [15:8] of MSNB_P2 in register 40 Si5351a_Write_Reg (41, MSNB_P2 & 255); // Bits [7:0] of MSNB_P2 in register 41 Si5351a_Write_Reg (42, 0); // Bits [15:8] of MS0_P3 (always 0) in register 42 Si5351a_Write_Reg (43, 1); // Bits [7:0] of MS0_P3 (always 1) in register 43 Si5351a_Write_Reg (44, ((MS0_P1 & 196608) >> 16) | TX_R); // Bits [17:16] of MS0_P1 in bits [1:0] and R in [7:4] Si5351a_Write_Reg (45, (MS0_P1 & 65280) >> 8); // Bits [15:8] of MS0_P1 in register 45 Si5351a_Write_Reg (46, MS0_P1 & 255); // Bits [7:0] of MS0_P1 in register 46 Si5351a_Write_Reg (47, 0); // Bits [19:16] of MS0_P2 and MS0_P3 are always 0 Si5351a_Write_Reg (48, 0); // Bits [15:8] of MS0_P2 are always 0 Si5351a_Write_Reg (49, 0); // Bits [7:0] of MS0_P2 are always 0 if (TX_outdivider == 4){ Si5351a_Write_Reg (44, 12 | TX_R); // Special settings for R = 4 (see datasheet) Si5351a_Write_Reg (45, 0); // Bits [15:8] of MS0_P1 must be 0 Si5351a_Write_Reg (46, 0); // Bits [7:0] of MS0_P1 must be 0 } Si5351a_Write_Reg (177, 128); // This resets PLL B return MSNB_P2; } //-------------------------------------------------------------------- // Now for the Si5351a multipurpose function //-------------------------------------------------------------------- // void Si5351a_Write_Reg (byte regist, byte value){ // Writes "byte" into "regist" of Si5351a via I2C Wire.beginTransmission(96); // Starts transmission as master to slave 96, which is the // I2C address of the Si5351a (see Si5351a datasheet) Wire.write(regist); // Writes a byte containing the number of the register Wire.write(value); // Writes a byte containing the value to be written in the register Wire.endTransmission(); // Sends the data and ends the transmission } //_________________________________________________________________________________________ // LCD functions void lcdwriteTemperature() // temperature measurement and display { int sensorVal = analogRead (tmp36Pin); float temperature = (((sensorVal/1024.0) * 5.0)-0.5)*100.0; // convert to degC from 10 bit lcd.setCursor(0,0); // set pointer to left of the first line lcd.print("G3ZIL WSPR T"); lcd.setCursor(12, 0); String tempString=String(temperature); // have to convert to a string to display lcd.print(tempString); } // _________________________________________________________________________________________ // Optical encoder read function int readEncoder() // Read the optical encoder { encoderRead = digitalRead(encoderPinA); if ((encoderPinALast == LOW) && (encoderRead == HIGH)) { if (digitalRead(encoderPinB) == LOW) { encoder0Pos--; } else { encoder0Pos++; } encoderPinALast = encoderRead; return abs(encoder0Pos/10); // the divide by 10 means the encoder has to be turned through a greater angle, helps reduce 'twitches' } encoderPinALast = encoderRead; return abs(encoder0Pos/10); } // ------------------------------------------------------------------------------------------- // DS3231 Real Time Clock functions byte decToBcd(byte val) // Convert decimal to binary coded decimal numbers { return( (val/10*16) + (val%10) ); } byte bcdToDec(byte val) // Convert binary coded decimal to normal decimal numbers { return( (val/16*10) + (val%16) ); } void setDS3231time(byte second, byte minute, byte hour, byte dayOfWeek, byte dayOfMonth, byte month, byte year) { // sets time and date data within DS3231 Wire.beginTransmission(DS3231_I2C_ADDRESS); Wire.write(0); // set next input to start at the seconds register, i.e. reg 0, then autoincrements Wire.write(decToBcd(second)); // set seconds Wire.write(decToBcd(minute)); // set minutes Wire.write(decToBcd(hour)); // set hours Wire.write(decToBcd(dayOfWeek)); // set day of week (1=Sunday, 7=Saturday) Wire.write(decToBcd(dayOfMonth)); // set date (1 to 31) Wire.write(decToBcd(month)); // set month Wire.write(decToBcd(year)); // set year (0 to 99) Wire.endTransmission(); } void readDS3231time(byte *second, byte *minute, byte *hour, byte *dayOfWeek, byte *dayOfMonth, byte *month, byte *year) { // sets time and date data within DS3231 Wire.beginTransmission(DS3231_I2C_ADDRESS); Wire.write(0); // set DS3231 register pointer to 00h Wire.endTransmission(); Wire.requestFrom(DS3231_I2C_ADDRESS, 7); // request seven bytes of data from DS3231 starting from register 00h *second = bcdToDec(Wire.read() & 0x7f); *minute = bcdToDec(Wire.read()); *hour = bcdToDec(Wire.read() & 0x3f); // Don't need bit 6, which is 12/24hr flag, and bit7=0 *dayOfWeek = bcdToDec(Wire.read()); *dayOfMonth = bcdToDec(Wire.read()); *month = bcdToDec(Wire.read()); *year = bcdToDec(Wire.read()); } void readDS3231temp(byte *MSB, byte *LSB) // GG function to read the DS3231 temperature { Wire.beginTransmission(DS3231_I2C_ADDRESS); Wire.write(0x11); // set DS3231 register pointer to 11h sign and MSB of the temperature sensor Wire.endTransmission(); Wire.requestFrom(DS3231_I2C_ADDRESS, 2); // request two bytes of data from DS3231 starting from register 11h *MSB=Wire.read(); // Top seven bits are data, 8th bit is the sign 1=negative *LSB=Wire.read() >> 6; // just bits 8 and 7 are valid [10 bit converter], so move down 6 } void LCDisplayTime() { byte second, minute, hour, dayOfWeek, dayOfMonth, month, year; readDS3231time(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year); // retrieve data from DS3231 lcd.setCursor(12, 1); // set the cursor position int time=minute+(hour*100); String timeString=u2s(time,4); lcd.print(timeString); lcd.setCursor(12, 1); // set the cursor position if (hour<10) { lcd.print("0"); } } int getDS3231seconds() { byte second, minute, hour, dayOfWeek, dayOfMonth, month, year; readDS3231time(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year); // retrieve data from DS3231 int secondsintoHour=60*minute+second; return secondsintoHour; } // ------------------------------------------------------------------------------------------- // Convert unsigned integer to d-digit decimal string in local buffer // A useful function from Morris Dovey on the Arduino forum. Converts unsigned integer of 'd' digits to a right justified string // b[6] allows for 5 characters in the int number and 1 character for the null end-of-string delimiter. // see http://forum.arduino.cc/index.php?topic=95175.0 char *u2s(unsigned int x,unsigned int d) { static char b[6]; char *p; unsigned digits = 0; unsigned int t = x; do ++digits; while (t /= 10); *(p = b + d) = '\0'; do *--p = x % 10 + '0'; while (x /= 10); // x modulo 10 returns a digit, this is added to the character for 0 to give the character for the digit while (p != b) *--p = '0'; return b; } // ------------------------------------------------------------------------------------------- // Menu Interrupt service routine void menu_ISR() { menu_int_status =1; lcd.setCursor(0, 1); lcd.print("Interrupt! "); } // // -------------------------------------------------------------------------------------------