Adrian Chadd 2021-07-05 21:11:11 -04:00 zatwierdzone przez GitHub
commit 3aae0451ee
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
11 zmienionych plików z 648 dodań i 349 usunięć

Wyświetl plik

@ -1 +1,11 @@
#ubitx4
This is adrian's fork of the ubitx v4 firmware.
It currently is tailored to run on a stock ubitx4 with bugfixes
brought in from other branches and derivatives.
Adrian Chadd / KK6VQK / adrian.chadd@gmail.com

Wyświetl plik

@ -1,4 +1,4 @@
/**
/**
* This source file is under General Public License version 3.
*
* This verision uses a built-in Si5351 library
@ -33,6 +33,14 @@
#include <Wire.h>
#include <EEPROM.h>
#include "ubitx4_eeprom_defs.h"
#include "ubitx4_pin_config.h"
#include "ubitx4_defaults.h"
#include "ubitx4_txfilt.h"
#include "ubitx_ui.h"
/**
The main chip which generates upto three oscillators of various frequencies in the
Raduino is the Si5351a. To learn more about Si5351a you can download the datasheet
@ -44,57 +52,9 @@
code). Here are some defines and declarations used by Jerry's routines:
*/
/**
* We need to carefully pick assignment of pin for various purposes.
* There are two sets of completely programmable pins on the Raduino.
* First, on the top of the board, in line with the LCD connector is an 8-pin connector
* that is largely meant for analog inputs and front-panel control. It has a regulated 5v output,
* ground and six pins. Each of these six pins can be individually programmed
* either as an analog input, a digital input or a digital output.
* The pins are assigned as follows (left to right, display facing you):
* Pin 1 (Violet), A7, SPARE
* Pin 2 (Blue), A6, KEYER (DATA)
* Pin 3 (Green), +5v
* Pin 4 (Yellow), Gnd
* Pin 5 (Orange), A3, PTT
* Pin 6 (Red), A2, F BUTTON
* Pin 7 (Brown), A1, ENC B
* Pin 8 (Black), A0, ENC A
*Note: A5, A4 are wired to the Si5351 as I2C interface
* *
* Though, this can be assigned anyway, for this application of the Arduino, we will make the following
* assignment
* A2 will connect to the PTT line, which is the usually a part of the mic connector
* A3 is connected to a push button that can momentarily ground this line. This will be used for RIT/Bandswitching, etc.
* A6 is to implement a keyer, it is reserved and not yet implemented
* A7 is connected to a center pin of good quality 100K or 10K linear potentiometer with the two other ends connected to
* ground and +5v lines available on the connector. This implments the tuning mechanism
*/
#define ENC_A (A0)
#define ENC_B (A1)
#define FBUTTON (A2)
#define PTT (A3)
#define ANALOG_KEYER (A6)
#define ANALOG_SPARE (A7)
/**
* The Raduino board is the size of a standard 16x2 LCD panel. It has three connectors:
*
* First, is an 8 pin connector that provides +5v, GND and six analog input pins that can also be
* configured to be used as digital input or output pins. These are referred to as A0,A1,A2,
* A3,A6 and A7 pins. The A4 and A5 pins are missing from this connector as they are used to
* talk to the Si5351 over I2C protocol.
*
* Second is a 16 pin LCD connector. This connector is meant specifically for the standard 16x2
* LCD display in 4 bit mode. The 4 bit mode requires 4 data lines and two control lines to work:
* Lines used are : RESET, ENABLE, D4, D5, D6, D7
* We include the library and declare the configuration of the LCD panel too
*/
#include <LiquidCrystal.h>
LiquidCrystal lcd(8,9,10,11,12,13);
LiquidCrystal lcd(LCD_PIN_RESET, LCD_PIN_ENABLE,
LCD_PIN_D4, LCD_PIN_D5, LCD_PIN_D6, LCD_PIN_D7);
/**
* The Arduino, unlike C/C++ on a regular computer with gigabytes of RAM, has very little memory.
@ -111,77 +71,6 @@ char c[30], b[30];
char printBuff[2][31]; //mirrors what is showing on the two lines of the display
int count = 0; //to generally count ticks, loops, etc
/**
* The second set of 16 pins on the Raduino's bottom connector are have the three clock outputs and the digital lines to control the rig.
* This assignment is as follows :
* Pin 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
* GND +5V CLK0 GND GND CLK1 GND GND CLK2 GND D2 D3 D4 D5 D6 D7
* These too are flexible with what you may do with them, for the Raduino, we use them to :
* - TX_RX line : Switches between Transmit and Receive after sensing the PTT or the morse keyer
* - CW_KEY line : turns on the carrier for CW
*/
#define TX_RX (7)
#define CW_TONE (6)
#define TX_LPF_A (5)
#define TX_LPF_B (4)
#define TX_LPF_C (3)
#define CW_KEY (2)
/**
* These are the indices where these user changable settinngs are stored in the EEPROM
*/
#define MASTER_CAL 0
#define LSB_CAL 4
#define USB_CAL 8
#define SIDE_TONE 12
//these are ids of the vfos as well as their offset into the eeprom storage, don't change these 'magic' values
#define VFO_A 16
#define VFO_B 20
#define CW_SIDETONE 24
#define CW_SPEED 28
//These are defines for the new features back-ported from KD8CEC's software
//these start from beyond 256 as Ian, KD8CEC has kept the first 256 bytes free for the base version
#define VFO_A_MODE 256 // 2: LSB, 3: USB
#define VFO_B_MODE 257
//values that are stroed for the VFO modes
#define VFO_MODE_LSB 2
#define VFO_MODE_USB 3
// handkey, iambic a, iambic b : 0,1,2f
#define CW_KEY_TYPE 358
/**
* The uBITX is an upconnversion transceiver. The first IF is at 45 MHz.
* The first IF frequency is not exactly at 45 Mhz but about 5 khz lower,
* this shift is due to the loading on the 45 Mhz crystal filter by the matching
* L-network used on it's either sides.
* The first oscillator works between 48 Mhz and 75 MHz. The signal is subtracted
* from the first oscillator to arriive at 45 Mhz IF. Thus, it is inverted : LSB becomes USB
* and USB becomes LSB.
* The second IF of 12 Mhz has a ladder crystal filter. If a second oscillator is used at
* 57 Mhz, the signal is subtracted FROM the oscillator, inverting a second time, and arrives
* at the 12 Mhz ladder filter thus doouble inversion, keeps the sidebands as they originally were.
* If the second oscillator is at 33 Mhz, the oscilaltor is subtracated from the signal,
* thus keeping the signal's sidebands inverted. The USB will become LSB.
* We use this technique to switch sidebands. This is to avoid placing the lsbCarrier close to
* 12 MHz where its fifth harmonic beats with the arduino's 16 Mhz oscillator's fourth harmonic
*/
// the second oscillator should ideally be at 57 MHz, however, the crystal filter's center frequency
// is shifted down a little due to the loading from the impedance matching L-networks on either sides
#define SECOND_OSC_USB (56995000l)
#define SECOND_OSC_LSB (32995000l)
//these are the two default USB and LSB frequencies. The best frequencies depend upon your individual taste and filter shape
#define INIT_USB_FREQ (11996500l)
// limits the tuning and working range of the ubitx between 3 MHz and 30 MHz
#define LOWEST_FREQ (100000l)
#define HIGHEST_FREQ (30000000l)
//we directly generate the CW by programmin the Si5351 to the cw tx frequency, hence, both are different modes
//these are the parameter passed to startTx
#define TX_SSB 0
@ -193,7 +82,7 @@ int8_t meter_reading = 0; // a -1 on meter makes it invisible
unsigned long vfoA=7150000L, vfoB=14200000L, sideTone=800, usbCarrier;
char isUsbVfoA=0, isUsbVfoB=1;
unsigned long frequency, ritRxFrequency, ritTxFrequency; //frequency is the current frequency on the dial
unsigned long firstIF = 45000000L;
unsigned long firstIF = DEFAULT_FIRSTIF;
//these are variables that control the keyer behaviour
int cwSpeed = 100; //this is actuall the dot period in milliseconds
@ -238,46 +127,6 @@ void active_delay(int delay_by){
}
}
/**
* Select the properly tx harmonic filters
* The four harmonic filters use only three relays
* the four LPFs cover 30-21 Mhz, 18 - 14 Mhz, 7-10 MHz and 3.5 to 5 Mhz
* Briefly, it works like this,
* - When KT1 is OFF, the 'off' position routes the PA output through the 30 MHz LPF
* - When KT1 is ON, it routes the PA output to KT2. Which is why you will see that
* the KT1 is on for the three other cases.
* - When the KT1 is ON and KT2 is off, the off position of KT2 routes the PA output
* to 18 MHz LPF (That also works for 14 Mhz)
* - When KT1 is On, KT2 is On, it routes the PA output to KT3
* - KT3, when switched on selects the 7-10 Mhz filter
* - KT3 when switched off selects the 3.5-5 Mhz filter
* See the circuit to understand this
*/
void setTXFilters(unsigned long freq){
if (freq > 21000000L){ // the default filter is with 35 MHz cut-off
digitalWrite(TX_LPF_A, 0);
digitalWrite(TX_LPF_B, 0);
digitalWrite(TX_LPF_C, 0);
}
else if (freq >= 14000000L){ //thrown the KT1 relay on, the 30 MHz LPF is bypassed and the 14-18 MHz LPF is allowd to go through
digitalWrite(TX_LPF_A, 1);
digitalWrite(TX_LPF_B, 0);
digitalWrite(TX_LPF_C, 0);
}
else if (freq > 7000000L){
digitalWrite(TX_LPF_A, 1);
digitalWrite(TX_LPF_B, 1);
digitalWrite(TX_LPF_C, 0);
}
else {
digitalWrite(TX_LPF_A, 1);
digitalWrite(TX_LPF_B, 1);
digitalWrite(TX_LPF_C, 1);
}
}
/**
* This is the most frequently called function that configures the
* radio to a particular frequeny, sideband and sets up the transmit filters
@ -317,8 +166,19 @@ void setFrequency(unsigned long f){
void startTx(byte txMode){
unsigned long tx_freq = 0;
// Turn off the main mixer oscillator and wait for RX->TX burst before
// TX is flipped on. Then we will re-enable the clocks.
si5351bx_setfreq(2, 0);
// Wait so things settle.
delay(TX_DELAY_OSC_OFF);
// Flip to TX.
digitalWrite(TX_RX, 1);
// Wait for relays, etc to settle.
delay(TX_DELAY_ENABLE);
inTx = 1;
if (ritOn){
@ -357,12 +217,19 @@ void startTx(byte txMode){
si5351bx_setfreq(2, frequency - sideTone);
}
updateDisplay();
clearMeterDisplay();
}
void stopTx(){
inTx = 0;
// Turn off the main vfo before disabling TX. setFrequency() will fix
// it.
si5351bx_setfreq(2, 0); // disable the VFO oscillator.
delay(TX_DELAY_OSC_OFF);
digitalWrite(TX_RX, 0); //turn off the tx
delay(TX_DELAY_ENABLE); // wait for relays to settle
si5351bx_setfreq(0, usbCarrier); //set back the cardrier oscillator anyway, cw tx switches it off
if (ritOn)
@ -459,23 +326,49 @@ void checkButton(){
void doTuning(){
int s;
unsigned long prev_freq;
int freq_delta = 0;
s = enc_read();
if (s != 0){
prev_freq = frequency;
/*
* Ensure that we don't tune below LOWEST_FREQ/HIGHEST_FREQ.
* We need to be sure that frequency doesn't underflow!
*/
if (s > 4)
frequency += 10000l;
freq_delta = 10000;
else if (s > 2)
frequency += 500;
freq_delta = 500;
else if (s > 0)
frequency += 50l;
freq_delta = 50;
else if (s > -2)
frequency -= 50l;
freq_delta = -50;
else if (s > -4)
frequency -= 500l;
freq_delta = -500;
else
frequency -= 10000l;
freq_delta = -10000;
/* Be super careful we don't underflow! */
if (freq_delta < 0) {
unsigned long offset;
offset = frequency - LOWEST_FREQ;
if (offset < (-freq_delta)) {
frequency = LOWEST_FREQ;
freq_delta = 0;
}
}
/* Not so likely to overflow here; a straight comparison is fine */
if (freq_delta > 0) {
if (frequency + freq_delta > HIGHEST_FREQ) {
frequency = HIGHEST_FREQ;
freq_delta = 0;
}
}
frequency += freq_delta;
if (prev_freq < 10000000l && frequency > 10000000l)
isUSB = true;
@ -483,8 +376,14 @@ void doTuning(){
if (prev_freq > 10000000l && frequency < 10000000l)
isUSB = false;
setFrequency(frequency);
updateDisplay();
/*
* Don't hammer on setting frequency / updating display if
* we are at the edges already and we're being clamped above.
*/
// if (prev_freq != frequency) {
setFrequency(frequency);
updateDisplay();
// }
}
}
@ -523,10 +422,10 @@ void initSettings(){
EEPROM.get(VFO_B, vfoB);
EEPROM.get(CW_SIDETONE, sideTone);
EEPROM.get(CW_SPEED, cwSpeed);
EEPROM.get(CAL_FIRST_IF_FREQ, firstIF);
if (usbCarrier > 12000000l || usbCarrier < 11990000l)
usbCarrier = 11997000l;
usbCarrier = INIT_USB_FREQ;
if (vfoA > 35000000l || 3500000l > vfoA)
vfoA = 7150000l;
if (vfoB > 35000000l || 3500000l > vfoB)
@ -535,6 +434,8 @@ void initSettings(){
sideTone = 800;
if (cwSpeed < 10 || 1000 < cwSpeed)
cwSpeed = 100;
if (firstIF < 44500000 || firstIF > 45500000)
firstIF = DEFAULT_FIRSTIF;
/*
* The VFO modes are read in as either 2 (USB) or 3(LSB), 0, the default
@ -629,19 +530,24 @@ void initPorts(){
void setup()
{
Serial.begin(38400);
Serial.flush();
Serial.flush();
lcd.begin(16, 2);
lcd.clear();
initMeter();
//we print this line so this shows up even if the raduino
//crashes later in the code
printLine2("uBITX v4.3");
printLineF2(F("uBITX v4.3b"));
//active_delay(500);
// initMeter(); //not used in this build
initSettings();
initPorts();
initOscillators();
si5351bx_set_drive(0, 1); // 4ma
si5351bx_set_drive(1, 1); // 4ma
si5351bx_set_drive(2, 1); // 4ma
frequency = vfoA;
setFrequency(vfoA);
updateDisplay();
@ -656,21 +562,23 @@ void setup()
*/
byte flasher = 0;
void loop(){
cwKeyer();
void loop(){
cwKeyer();
if (!txCAT)
checkPTT();
checkButton();
//tune only when not tranmsitting
//tune / display s-meter only when not tranmsitting
if (!inTx){
if (ritOn)
doRIT();
else
doTuning();
// TODO: only update this every few milliseconds? Not constantly?
updateMeterDisplay();
}
//we check CAT after the encoder as it might put the radio into TX
checkCAT();
}

46
ubitx4_defaults.h 100644
Wyświetl plik

@ -0,0 +1,46 @@
/**
* This source file is under General Public License version 3.
*/
#ifndef __UBITX4_EEPROM_DEFAULTS_H__
#define __UBITX4_EEPROM_DEFAULTS_H__
// Delay when switching between TX and RX.
// This prevents spurious bursts when switching RX->TX.
#define TX_DELAY_OSC_OFF 15 // wait 15ms after turning off si5351 for it to be off off
#define TX_DELAY_ENABLE 30 // wait 30ms after enabling PTT to let things settle.
/**
* The uBITX is an upconnversion transceiver. The first IF is at 45 MHz.
* The first IF frequency is not exactly at 45 Mhz but about 5 khz lower,
* this shift is due to the loading on the 45 Mhz crystal filter by the matching
* L-network used on it's either sides.
* The first oscillator works between 48 Mhz and 75 MHz. The signal is subtracted
* from the first oscillator to arriive at 45 Mhz IF. Thus, it is inverted : LSB becomes USB
* and USB becomes LSB.
* The second IF of 12 Mhz has a ladder crystal filter. If a second oscillator is used at
* 57 Mhz, the signal is subtracted FROM the oscillator, inverting a second time, and arrives
* at the 12 Mhz ladder filter thus doouble inversion, keeps the sidebands as they originally were.
* If the second oscillator is at 33 Mhz, the oscilaltor is subtracated from the signal,
* thus keeping the signal's sidebands inverted. The USB will become LSB.
* We use this technique to switch sidebands. This is to avoid placing the lsbCarrier close to
* 12 MHz where its fifth harmonic beats with the arduino's 16 Mhz oscillator's fourth harmonic
*/
// the second oscillator should ideally be at 57 MHz, however, the crystal filter's center frequency
// is shifted down a little due to the loading from the impedance matching L-networks on either sides
#define SECOND_OSC_USB (56995000l)
#define SECOND_OSC_LSB (32995000l)
//these are the two default USB and LSB frequencies. The best frequencies depend upon your individual taste and filter shape
#define INIT_USB_FREQ (11996500l)
// Default first IF frequency (can be tuned via EEPROM)
#define DEFAULT_FIRSTIF 45000000L
// limits the tuning and working range of the ubitx between 3 MHz and 30 MHz
#define LOWEST_FREQ (100000l)
#define HIGHEST_FREQ (30000000l)
#define FAST_TUNE_STEP (200000L)
#endif /* __UBITX4_EEPROM_DEFAULTS_H__ */

Wyświetl plik

@ -0,0 +1,36 @@
/**
* This source file is under General Public License version 3.
*/
#ifndef __UBITX4_EEPROM_DEFS_H__
#define __UBITX4_EEPROM_DEFS_H__
/**
* These are the indices where these user changable settinngs are stored in the EEPROM
*/
#define MASTER_CAL 0
#define LSB_CAL 4
#define USB_CAL 8
#define SIDE_TONE 12
//these are ids of the vfos as well as their offset into the eeprom storage, don't change these 'magic' values
#define VFO_A 16
#define VFO_B 20
#define CW_SIDETONE 24
#define CW_SPEED 28
// Adrian's custom values
#define CAL_FIRST_IF_FREQ 252 // 252-255 - first IF freq calibrated value
//These are defines for the new features back-ported from KD8CEC's software
//these start from beyond 256 as Ian, KD8CEC has kept the first 256 bytes free for the base version
#define VFO_A_MODE 256 // 2: LSB, 3: USB
#define VFO_B_MODE 257
//values that are stored for the VFO modes
#define VFO_MODE_LSB 2
#define VFO_MODE_USB 3
// handkey, iambic a, iambic b : 0,1,2f
#define CW_KEY_TYPE 358
#endif /* __UBITX4_EEPROM_DEFS_H__ */

Wyświetl plik

@ -0,0 +1,74 @@
/**
* This source file is under General Public License version 3.
*/
#ifndef __UBITX4_PIN_CONFIG_H__
#define __UBITX4_PIN_CONFIG_H__
/**
* We need to carefully pick assignment of pin for various purposes.
* There are two sets of completely programmable pins on the Raduino.
* First, on the top of the board, in line with the LCD connector is an 8-pin connector
* that is largely meant for analog inputs and front-panel control. It has a regulated 5v output,
* ground and six pins. Each of these six pins can be individually programmed
* either as an analog input, a digital input or a digital output.
* The pins are assigned as follows (left to right, display facing you):
* Pin 1 (Violet), A7, S-METER / SPARE
* Pin 2 (Blue), A6, KEYER (DATA)
* Pin 3 (Green), +5v
* Pin 4 (Yellow), Gnd
* Pin 5 (Orange), A3, PTT
* Pin 6 (Red), A2, F BUTTON
* Pin 7 (Brown), A1, ENC B
* Pin 8 (Black), A0, ENC A
*Note: A5, A4 are wired to the Si5351 as I2C interface
* *
* - A0 and A1 are used for the rotary encoder
* - A2 is used for the menu push button
* - A3 is PTT to the microphone
* - A6 is hooked up to the CW key jack via resistors, to implement
* either a straight key or paddle support
* - A7 is spare, but is currently used to drive an S-meter menu
*/
#define ENC_A (A0)
#define ENC_B (A1)
#define FBUTTON (A2)
#define PTT (A3)
#define ANALOG_KEYER (A6)
#define ANALOG_SPARE (A7)
/*
* The 16x2 LCD panel is connected to digital pins as:
* RESET : 8
* ENABLE : 9
* D4 : 10
* D5 : 11
* D6 : 12
* D7 : 13
*/
#define LCD_PIN_RESET 8
#define LCD_PIN_ENABLE 9
#define LCD_PIN_D4 10
#define LCD_PIN_D5 11
#define LCD_PIN_D6 12
#define LCD_PIN_D7 13
/**
* The second set of 16 pins on the Raduino's bottom connector are have the
* three clock outputs and the digital lines to control the rig.
* This assignment is as follows :
* Pin 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
* GND +5V CLK0 GND GND CLK1 GND GND CLK2 GND D2 D3 D4 D5 D6 D7
* These too are flexible with what you may do with them, for the Raduino, we use them to :
* - TX_RX line : Switches between Transmit and Receive after sensing the PTT or the morse keyer
* - CW_KEY line : turns on the carrier for CW
*/
#define TX_RX (7)
#define CW_TONE (6)
#define TX_LPF_A (5)
#define TX_LPF_B (4)
#define TX_LPF_C (3)
#define CW_KEY (2)
#endif /* __UBITX4_PIN_CONFIG_H__ */

7
ubitx4_txfilt.h 100644
Wyświetl plik

@ -0,0 +1,7 @@
#ifndef __UBITX4_TXFILT_H__
#define __UBITX4_TXFILT_H__
extern void setTXFilters(unsigned long freq);
#endif

55
ubitx4_txfilt.ino 100644
Wyświetl plik

@ -0,0 +1,55 @@
/**
* This source file is under General Public License version 3.
*/
#include "ubitx4_eeprom_defs.h"
#include "ubitx4_pin_config.h"
/**
* Select the properly tx harmonic filters
*
* This is valid for the ubitx v4 board; different boards have different TX filter
* configurations and thus will need a different filter mapping routine here!
*
* The four harmonic filters use only three relays
* the four LPFs cover 30-21 Mhz, 18 - 14 Mhz, 7-10 MHz and 3.5 to 5 Mhz
* Briefly, it works like this,
* - When KT1 is OFF, the 'off' position routes the PA output through the 30 MHz LPF
* - When KT1 is ON, it routes the PA output to KT2. Which is why you will see that
* the KT1 is on for the three other cases.
* - When the KT1 is ON and KT2 is off, the off position of KT2 routes the PA output
* to 18 MHz LPF (That also works for 14 Mhz)
* - When KT1 is On, KT2 is On, it routes the PA output to KT3
* - KT3, when switched on selects the 7-10 Mhz filter
* - KT3 when switched off selects the 3.5-5 Mhz filter
* See the circuit to understand this
*/
void
setTXFilters(unsigned long freq)
{
if (freq > 21000000L){ // the default filter is with 35 MHz cut-off
digitalWrite(TX_LPF_A, 0);
digitalWrite(TX_LPF_B, 0);
digitalWrite(TX_LPF_C, 0);
}
else if (freq >= 14000000L){
/*
* throw the KT1 relay on, the 30 MHz LPF is bypassed and
* the 14-18 MHz LPF is allowedd to go through
*/
digitalWrite(TX_LPF_A, 1);
digitalWrite(TX_LPF_B, 0);
digitalWrite(TX_LPF_C, 0);
}
else if (freq > 7000000L){
digitalWrite(TX_LPF_A, 1);
digitalWrite(TX_LPF_B, 1);
digitalWrite(TX_LPF_C, 0);
}
else {
digitalWrite(TX_LPF_A, 1);
digitalWrite(TX_LPF_B, 1);
digitalWrite(TX_LPF_C, 1);
}
}

Wyświetl plik

@ -11,10 +11,11 @@
* - If the menu item is NOT clicked on, then the menu's prompt is to be displayed
*/
#include "ubitx4_defaults.h"
/** A generic control to read variable values
*/
int getValueByKnob(int minimum, int maximum, int step_size, int initial, char* prefix, char *postfix)
int getValueByKnob(int minimum, int maximum, int step_size, int initial, const char* prefix, const char *postfix)
{
int knob = 0;
int knob_value;
@ -55,20 +56,17 @@ int getValueByKnob(int minimum, int maximum, int step_size, int initial, char*
//# Menu: 1
int menuBand(int btn){
void menuBand(int btn){
int knob = 0;
int band;
unsigned long offset;
// band = frequency/1000000l;
// offset = frequency % 1000000l;
if (!btn){
printLine2("Band Select \x7E");
printLineF2(F("Band Select \x7E"));
return;
}
printLine2("Band Select:");
printLineF2(F("Band Select:"));
//wait for the button menu select button to be lifted)
while (btnDown())
active_delay(50);
@ -80,19 +78,27 @@ int menuBand(int btn){
knob = enc_read();
if (knob != 0){
/*
if (band > 3 && knob < 0)
band--;
if (band < 30 && knob > 0)
band++;
if (band > 10)
isUSB = true;
else
isUSB = false;
setFrequency(((unsigned long)band * 1000000l) + offset); */
if (knob < 0 && frequency > 3000000l)
setFrequency(frequency - 200000l);
if (knob > 0 && frequency < 30000000l)
setFrequency(frequency + 200000l);
* Ensure that we cap the band select between LOWEST_FREQ
* and HIGHEST_FREQ.
*/
if ((knob < 0) && (frequency > LOWEST_FREQ)) {
/* Ensure we don't tune below LOWEST_FREQ */
if (frequency > (FAST_TUNE_STEP * 2)) {
setFrequency(frequency - FAST_TUNE_STEP);
} else {
setFrequency(LOWEST_FREQ);
}
}
if ((knob > 0) && (frequency < HIGHEST_FREQ)) {
/* Ensure we don't tune above HIGHEST_FREQ */
if ((frequency + FAST_TUNE_STEP) > HIGHEST_FREQ) {
setFrequency(HIGHEST_FREQ);
} else {
setFrequency(frequency + FAST_TUNE_STEP);
}
}
/* Adjust mode to be USB/LSB across 10MHz */
if (frequency > 10000000l)
isUSB = true;
else
@ -107,7 +113,7 @@ int menuBand(int btn){
active_delay(50);
active_delay(50);
printLine2("");
printLineF2(F(""));
updateDisplay();
menuOn = 0;
}
@ -116,24 +122,24 @@ int menuBand(int btn){
void menuRitToggle(int btn){
if (!btn){
if (ritOn == 1)
printLine2("RIT On \x7E Off");
printLineF2(F("RIT On \x7E Off"));
else
printLine2("RIT Off \x7E On");
printLineF2(F("RIT Off \x7E On"));
}
else {
if (ritOn == 0){
//enable RIT so the current frequency is used at transmit
ritEnable(frequency);
printLine2("RIT is On");
printLineF2(F("RIT is On"));
}
else{
ritDisable();
printLine2("RIT is Off");
printLineF2(F("RIT is Off"));
}
menuOn = 0;
active_delay(500);
printLine2("");
printLineF2(F(""));
updateDisplay();
}
}
@ -144,9 +150,9 @@ void menuVfoToggle(int btn){
if (!btn){
if (vfoActive == VFO_A)
printLine2("VFO A \x7E B");
printLineF2(F("VFO A \x7E B"));
else
printLine2("VFO B \x7E A");
printLineF2(F("VFO B \x7E A"));
}
else {
if (vfoActive == VFO_B){
@ -159,7 +165,7 @@ void menuVfoToggle(int btn){
EEPROM.put(VFO_B_MODE, VFO_MODE_LSB);
vfoActive = VFO_A;
// printLine2("Selected VFO A ");
// printLineF2(F("Selected VFO A "));
frequency = vfoA;
isUSB = isUsbVfoA;
}
@ -173,7 +179,7 @@ void menuVfoToggle(int btn){
EEPROM.put(VFO_A_MODE, VFO_MODE_LSB);
vfoActive = VFO_B;
// printLine2("Selected VFO B ");
// printLineF2(F("Selected VFO B "));
frequency = vfoB;
isUSB = isUsbVfoB;
}
@ -181,7 +187,7 @@ void menuVfoToggle(int btn){
ritDisable();
setFrequency(frequency);
updateDisplay();
printLine2("");
printLineF2(F(""));
//exit the menu
menuOn = 0;
}
@ -191,31 +197,35 @@ void menuVfoToggle(int btn){
void menuSidebandToggle(int btn){
if (!btn){
if (isUSB == true)
printLine2("USB \x7E LSB");
printLineF2(F("USB \x7E LSB"));
else
printLine2("LSB \x7E USB");
printLineF2(F("LSB \x7E USB"));
}
else {
if (isUSB == true){
isUSB = false;
printLine2("LSB Selected");
printLineF2(F("LSB Selected"));
active_delay(500);
printLine2("");
printLineF2(F(""));
}
else {
isUSB = true;
printLine2("USB Selected");
printLineF2(F("USB Selected"));
active_delay(500);
printLine2("");
printLineF2(F(""));
}
//Added by KD8CEC
if (vfoActive == VFO_B){
isUsbVfoB = isUSB;
}
else {
isUsbVfoB = isUSB;
isUsbVfoA = isUSB;
}
updateDisplay();
// Update the programmed frequency immediately so the band flips.
setFrequency(frequency);
menuOn = 0;
}
}
@ -225,29 +235,29 @@ void menuSidebandToggle(int btn){
void menuSplitToggle(int btn){
if (!btn){
if (splitOn == 0)
printLine2("Split Off \x7E On");
printLineF2(F("Split Off \x7E On"));
else
printLine2("Split On \x7E Off");
printLineF2(F("Split On \x7E Off"));
}
else {
if (splitOn == 1){
splitOn = 0;
printLine2("Split ON");
printLineF2(F("Split ON"));
}
else {
splitOn = 1;
if (ritOn == 1)
ritOn = 0;
printLine2("Split Off");
printLineF2(F("Split Off"));
}
active_delay(500);
printLine2("");
printLineF2(F(""));
updateDisplay();
menuOn = 0;
}
}
int menuCWSpeed(int btn){
void menuCWSpeed(int btn){
int knob = 0;
int wpm;
@ -295,12 +305,12 @@ int menuCWSpeed(int btn){
*/
wpm = getValueByKnob(1, 100, 1, wpm, "CW: ", " WPM>");
printLine2("CW Speed set!");
printLineF2(F("CW Speed set!"));
cwSpeed = 1200/wpm;
EEPROM.put(CW_SPEED, cwSpeed);
active_delay(500);
printLine2("");
printLineF2(F(""));
updateDisplay();
menuOn = 0;
}
@ -308,12 +318,12 @@ int menuCWSpeed(int btn){
void menuExit(int btn){
if (!btn){
printLine2("Exit Menu \x7E");
printLineF2(F("Exit Menu \x7E"));
}
else{
printLine2("Exiting...");
printLineF2(F("Exiting..."));
active_delay(500);
printLine2("");
printLineF2(F(""));
updateDisplay();
menuOn = 0;
}
@ -326,29 +336,29 @@ void menuExit(int btn){
int menuSetup(int btn){
if (!btn){
if (!modeCalibrate)
printLine2("Settings \x7E");
printLineF2(F("Settings \x7E"));
else
printLine2("Settings \x7E Off");
printLineF2(F("Settings \x7E Off"));
}else {
if (!modeCalibrate){
modeCalibrate = true;
printLine2("Settings On");
printLineF2(F("Settings On"));
}
else {
modeCalibrate = false;
printLine2("Settings Off");
printLineF2(F("Settings Off"));
}
while(btnDown())
active_delay(100);
active_delay(500);
printLine2("");
printLineF2(F(""));
return 10;
}
return 0;
}
//this is used by the si5351 routines in the ubitx_5351 file
//this is used by the si5351 routines in the ubitx_5351 file
extern int32_t calibration;
extern uint32_t si5351bx_vcoa;
@ -410,7 +420,7 @@ int calibrateClock(){
keyDown = 0;
stopTx();
printLine2("Calibration set!");
printLineF2(F("Calibration set!"));
EEPROM.put(MASTER_CAL, calibration);
initOscillators();
setFrequency(frequency);
@ -426,12 +436,12 @@ int menuSetupCalibration(int btn){
int32_t prev_calibration;
if (!btn){
printLine2("Setup:Calibrate\x7E");
printLineF2(F("Setup:Calibrate\x7E"));
return 0;
}
printLine1("Press PTT & tune");
printLine2("to exactly 10 MHz");
printLineF1(F("Press PTT & tune"));
printLineF2(F("to exactly 10 MHz"));
active_delay(2000);
calibrateClock();
}
@ -451,21 +461,30 @@ void printCarrierFreq(unsigned long freq){
printLine2(c);
}
void
printFreq(unsigned long freq)
{
memset(c, 0, sizeof(c));
memset(b, 0, sizeof(b));
ultoa(freq, b, DEC);
printLine2(b);
}
void menuSetupCarrier(int btn){
int knob = 0;
unsigned long prevCarrier;
if (!btn){
printLine2("Setup:BFO \x7E");
printLineF2(F("Setup:BFO \x7E"));
return;
}
prevCarrier = usbCarrier;
printLine1("Tune to best Signal");
printLine2("Press to confirm. ");
printLineF1(F("Tune to best Signal"));
printLineF2(F("Press to confirm. "));
active_delay(1000);
usbCarrier = 11995000l;
usbCarrier = INIT_USB_FREQ;
si5351bx_setfreq(0, usbCarrier);
printCarrierFreq(usbCarrier);
@ -486,14 +505,62 @@ void menuSetupCarrier(int btn){
active_delay(100);
}
printLine2("Carrier set! ");
printLineF2(F("Carrier set! "));
EEPROM.put(USB_CAL, usbCarrier);
active_delay(1000);
si5351bx_setfreq(0, usbCarrier);
setFrequency(frequency);
updateDisplay();
printLine2("");
printLineF2(F(""));
menuOn = 0;
}
// Calibrate the IF frequency
void menuSetupFreqIF(int btn){
int knob = 0;
unsigned long prevIF;
if (!btn){
printLineF2(F("Setup:IF \x7E"));
return;
}
prevIF = firstIF;
printLineF1(F("Tune to loudest signal"));
printLineF2(F("Press to confirm. "));
active_delay(1000);
setFrequency(frequency);
printFreq(firstIF);
//disable all clock 1 and clock 2
while (!btnDown()){
knob = enc_read();
if (knob > 0)
firstIF += 50;
else if (knob < 0)
firstIF -= 50;
checkPTT();
if (knob == 0)
continue; //don't update the frequency or the display
setFrequency(frequency);
printFreq(firstIF);
active_delay(100);
}
printLineF2(F("IF set! "));
EEPROM.put(CAL_FIRST_IF_FREQ, firstIF);
active_delay(1000);
setFrequency(frequency);
updateDisplay();
printLineF2(F(""));
menuOn = 0;
}
@ -502,13 +569,13 @@ void menuSetupCwTone(int btn){
int prev_sideTone;
if (!btn){
printLine2("Setup:CW Tone \x7E");
printLineF2(F("Setup:CW Tone \x7E"));
return;
}
prev_sideTone = sideTone;
printLine1("Tune CW tone");
printLine2("PTT to confirm. ");
printLineF1(F("Tune CW tone"));
printLineF2(F("PTT to confirm. "));
active_delay(1000);
tone(CW_TONE, sideTone);
@ -534,16 +601,16 @@ void menuSetupCwTone(int btn){
noTone(CW_TONE);
//save the setting
if (digitalRead(PTT) == LOW){
printLine2("Sidetone set! ");
printLineF2(F("Sidetone set! "));
EEPROM.put(CW_SIDETONE, sideTone);
active_delay(2000);
}
else
sideTone = prev_sideTone;
printLine2("");
updateDisplay();
menuOn = 0;
printLineF2(F(""));
updateDisplay();
menuOn = 0;
}
void menuSetupCwDelay(int btn){
@ -551,7 +618,7 @@ void menuSetupCwDelay(int btn){
int prev_cw_delay;
if (!btn){
printLine2("Setup:CW Delay \x7E");
printLineF2(F("Setup:CW Delay \x7E"));
return;
}
@ -559,8 +626,8 @@ void menuSetupCwDelay(int btn){
prev_cw_delay = cwDelayTime;
cwDelayTime = getValueByKnob(10, 1000, 50, cwDelayTime, "CW Delay>", " msec");
printLine1("CW Delay Set!");
printLine2("");
printLineF1(F("CW Delay Set!"));
printLineF2(F(""));
active_delay(500);
updateDisplay();
menuOn = 0;
@ -571,11 +638,11 @@ void menuSetupKeyer(int btn){
if (!btn){
if (!Iambic_Key)
printLine2("Setup:CW(Hand)\x7E");
printLineF2(F("Setup:CW(Hand)\x7E"));
else if (keyerControl & IAMBICB)
printLine2("Setup:CW(IambA)\x7E");
printLineF2(F("Setup:CW(IambA)\x7E"));
else
printLine2("Setup:CW(IambB)\x7E");
printLineF2(F("Setup:CW(IambB)\x7E"));
return;
}
@ -600,11 +667,11 @@ void menuSetupKeyer(int btn){
tmp_key = 0;
if (tmp_key == 0)
printLine1("Hand Key?");
printLineF1(F("Hand Key?"));
else if (tmp_key == 1)
printLine1("Iambic A?");
printLineF1(F("Iambic A?"));
else if (tmp_key == 2)
printLine1("Iambic B?");
printLineF1(F("Iambic B?"));
}
active_delay(500);
@ -621,32 +688,67 @@ void menuSetupKeyer(int btn){
EEPROM.put(CW_KEY_TYPE, tmp_key);
printLine1("Keyer Set!");
printLineF1(F("Keyer Set!"));
active_delay(600);
printLine1("");
printLineF1(F(""));
//Added KD8CEC
printLine2("");
printLineF2(F(""));
updateDisplay();
menuOn = 0;
menuOn = 0;
}
void menuReadADC(int btn){
int adc;
char a = 0, al; // Go from A0 -> A7
int knob;
if (!btn){
printLine2("6:Setup>Read ADC>");
printLineF2(F("6:Setup>Read ADC>"));
return;
}
delay(500);
while (!btnDown()){
adc = analogRead(ANALOG_KEYER);
// Assemble the display string, showing the A line and value
c[0] = 0;
strcat(c, "A");
itoa(a, b, 10);
strcat(c, b);
strcat(c, " = ");
// Map A line to value, yeah, I wish it were easier?
switch (a) {
case 0: al = A0; break;
case 1: al = A1; break;
case 2: al = A2; break;
case 3: al = A3; break;
case 4: al = A4; break;
case 5: al = A5; break;
case 6: al = A6; break;
case 7: al = A7; break;
}
adc = analogRead(al);
itoa(adc, b, 10);
printLine1(b);
strcat(c, b);
printLine1(c);
// Read the encoder, see if we need to change the ADC
knob = enc_read();
if ((knob > 4) && (a < 7)) {
a++;
delay(100);
}
if ((knob < -4) && (a > 0)) {
a--;
delay(100);
}
}
printLine1("");
printLineF1(F(""));
// Debounce the menu select
delay(500);
updateDisplay();
}
@ -665,7 +767,7 @@ void doMenu(){
btnState = btnDown();
if (i > 0){
if (modeCalibrate && select + i < 150)
if (modeCalibrate && select + i < 160)
select += i;
if (!modeCalibrate && select + i < 80)
select += i;
@ -701,6 +803,8 @@ void doMenu(){
menuReadADC(btnState);
else if (select < 140 && modeCalibrate)
menuSetupKeyer(btnState);
else if (select < 150 && modeCalibrate)
menuSetupFreqIF(btnState);
else
menuExit(btnState);
}
@ -712,4 +816,3 @@ void doMenu(){
checkCAT();
}

Wyświetl plik

@ -60,6 +60,12 @@ void i2cWriten(uint8_t reg, uint8_t *vals, uint8_t vcnt) { // write array
Wire.endTransmission();
}
// Call to set the drive strength, takes effect next frequency
// set.
void si5351bx_set_drive(uint8_t clknum, uint8_t drv)
{
si5351bx_drive[clknum] = drv & 0x3;
}
void si5351bx_init() { // Call once at power-up, start PLLA
uint8_t reg; uint32_t msxp1;

23
ubitx_ui.h 100644
Wyświetl plik

@ -0,0 +1,23 @@
#ifndef __UBITX_UI_H__
#define __UBITX_UI_H__
extern int btnDown(void);
extern void initMeter(void);
extern void drawMeter(uint8_t needle);
extern void printLine(char linenmbr, const char *c);
extern void printLineF(char linenmbr, const __FlashStringHelper *c);
#define printLine1(c) printLine(1, (c))
#define printLine2(c) printLine(0, (c))
#define printLineF1(c) printLineF(1, (c))
#define printLineF2(c) printLineF(0, (c))
extern void updateDisplay(void);
extern void updateMeterDisplay(void);
extern void clearMeterDisplay(void);
extern byte enc_state (void);
extern int enc_read(void);
#endif

Wyświetl plik

@ -6,8 +6,14 @@
* quickly cleared up.
*/
#include "ubitx4_eeprom_defs.h"
#include "ubitx4_pin_config.h"
#include "ubitx_ui.h"
//returns true if the button is pressed
int btnDown(){
int btnDown(void)
{
if (digitalRead(FBUTTON) == HIGH)
return 0;
else
@ -24,65 +30,59 @@ int btnDown(){
*/
char meter[17];
char meter[2];
const byte PROGMEM s_meter_bitmap[] = {
B00000,B00000,B00000,B00000,B00000,B00100,B00100,B11011,
B10000,B10000,B10000,B10000,B10100,B10100,B10100,B11011,
B01000,B01000,B01000,B01000,B01100,B01100,B01100,B11011,
B00100,B00100,B00100,B00100,B00100,B00100,B00100,B11011,
B00010,B00010,B00010,B00010,B00110,B00110,B00110,B11011,
B00001,B00001,B00001,B00001,B00101,B00101,B00101,B11011,
B10000,B11000,B11100,B11110,B11100,B11000,B10000,B00000,
B00001,B00011,B00111,B01111,B00111,B00011,B00001,B00000
const PROGMEM uint8_t s_meter_bitmap[64] = {
B00000, B00000, B00000, B00000, B00000, B00000, B00000, B11111, //shortest bar
B00000, B00000, B00000, B00000, B00000, B00000, B11111, B11111,
B00000, B00000, B00000, B00000, B00000, B11111, B11111, B11111,
B00000, B00000, B00000, B00000, B11111, B11111, B11111, B11111,
B00000, B00000, B00000, B11111, B11111, B11111, B11111, B11111,
B00000, B00000, B11111, B11111, B11111, B11111, B11111, B11111,
B00000, B11111, B11111, B11111, B11111, B11111, B11111, B11111,
B11111, B11111, B11111, B11111, B11111, B11111, B11111, B11111, // tallest bar
};
// Initialise the custom character out of program memory
static void
initLcdChar(char lcd_char, char s_meter_bitmap_offset)
{
uint8_t tmp_bytes[8];
char i;
for (i = 0; i < 8; i++) {
tmp_bytes[i] = pgm_read_byte(&s_meter_bitmap[s_meter_bitmap_offset + i]);
}
lcd.createChar(lcd_char, tmp_bytes);
}
// initializes the custom characters
// we start from char 1 as char 0 terminates the string!
void initMeter(){
lcd.createChar(1, s_meter_bitmap);
lcd.createChar(2, s_meter_bitmap + 8);
lcd.createChar(3, s_meter_bitmap + 16);
lcd.createChar(4, s_meter_bitmap + 24);
lcd.createChar(5, s_meter_bitmap + 32);
lcd.createChar(6, s_meter_bitmap + 40);
lcd.createChar(0, s_meter_bitmap + 48);
lcd.createChar(7, s_meter_bitmap + 56);
void initMeter(void)
{
lcd.setCursor(0, 0); // Ensure we've reset to display RAM
initLcdChar(0, 0); // First call - points to chargen RAM
initLcdChar(1, 8);
initLcdChar(2, 16);
initLcdChar(3, 24);
initLcdChar(4, 32);
initLcdChar(5, 40);
initLcdChar(6, 48);
initLcdChar(7, 56);
lcd.setCursor(0, 0); // Finish up - point to display RAM again
}
/**
* The meter is drawn with special characters.
* character 1 is used to simple draw the blocks of the scale of the meter
* characters 2 to 6 are used to draw the needle in positions 1 to within the block
* This displays a meter from 0 to 100, -1 displays nothing
* The meter is drawn with special characters from 0..255
*/
void drawMeter(int8_t needle){
int16_t best, i, s;
if (needle < 0)
return;
s = (needle * 4)/10;
for (i = 0; i < 8; i++){
if (s >= 5)
meter[i] = 1;
else if (s >= 0)
meter[i] = 2 + s;
else
meter[i] = 1;
s = s - 5;
}
if (needle >= 40)
meter[i-1] = 6;
meter[i] = 0;
void drawMeter(uint8_t needle)
{
meter[0] = needle / 32;
meter[1] = needle / 32;
}
// The generic routine to display one line on the LCD
void printLine(char linenmbr, char *c) {
void printLine(char linenmbr, const char *c) {
if (strcmp(c, printBuff[linenmbr])) { // only refresh the display when there was a change
lcd.setCursor(0, linenmbr); // place the cursor at the beginning of the selected line
lcd.print(c);
@ -94,18 +94,26 @@ void printLine(char linenmbr, char *c) {
}
}
void
printLineF(char linenmbr, const __FlashStringHelper *c)
{
int i;
char tmp[17]; // XXX TODO: figure out how to do this inline without the buffer
PGM_P p = reinterpret_cast<PGM_P>(c);
unsigned char ch;
// short cut to print to the first line
void printLine1(char *c){
printLine(1,c);
}
// short cut to print to the first line
void printLine2(char *c){
printLine(0,c);
for (i = 0; i < 17; i++) {
ch = pgm_read_byte(p++);
tmp[i] = ch;
if (ch == 0)
break;
}
printLine(linenmbr, tmp);
}
// this builds up the top line of the display with frequency and mode
void updateDisplay() {
void updateDisplay(void)
{
// tks Jack Purdum W8TEE
// replaced fsprint commmands by str commands for code size reduction
@ -134,19 +142,25 @@ void updateDisplay() {
else
strcat(c, "B:");
}
//one mhz digit if less than 10 M, two digits if more
if (frequency < 10000000l){
/*
* + zero mhz digit/one less dot if less than 1MHz
* + one mhz digit if less than 10MHz
* + two digits if more
*/
if (frequency < 1000000L) {
c[6] = ' '; c[7] = ' '; c[8] = ' ';
strncat(c, &b[0], 3);
strcat(c, ".");
strncat(c, &b[3], 3);
} else if (frequency < 10000000l){
c[6] = ' ';
c[7] = b[0];
strcat(c, ".");
strncat(c, &b[1], 3);
strcat(c, ".");
strncat(c, &b[4], 3);
}
else {
} else {
strncat(c, b, 2);
strcat(c, ".");
strncat(c, &b[2], 3);
@ -157,22 +171,41 @@ void updateDisplay() {
if (inTx)
strcat(c, " TX");
printLine(1, c);
}
/*
//now, the second line
memset(c, 0, sizeof(c));
memset(b, 0, sizeof(b));
* Update the meter display!
*/
void
updateMeterDisplay(void)
{
int meter_reading = 0;
if (inTx)
strcat(c, "TX ");
else if (ritOn)
strcpy(c, "RIT");
// Second line, meter on A7 (ANALOG_SPARE)
meter_reading = analogRead(ANALOG_SPARE);
strcpy(c, " \xff");
drawMeter(meter_reading);
strcat(c, meter);
strcat(c, "\xff");
printLine2(c);*/
// TODO - there's no mapping table here yet! Just linear it!
if (meter_reading > 255) {
// Ensure we definitely don't overflow an int here
meter_reading = 255;
}
drawMeter(meter_reading); // this takes uint8_t
lcd.setCursor(14, 0);
lcd.write(meter[0]);
lcd.setCursor(15, 0);
lcd.write(meter[1]);
}
/*
* Clear the meter - used during transmit
*/
void
clearMeterDisplay(void)
{
lcd.setCursor(14, 0);
lcd.write(' ');
lcd.setCursor(15, 0);
lcd.write(' ');
}
int enc_prev_state = 3;
@ -234,5 +267,3 @@ int enc_read(void) {
}
return(result);
}