Téléverser les fichiers vers "src"

This commit is contained in:
Xavier 2026-10-04 19:33:21 +02:00
parent 7cc911f734
commit ddd41921b4
5 changed files with 1437 additions and 0 deletions

574
src/cibi.cpp Normal file
View File

@ -0,0 +1,574 @@
/*
* Copyright (c) 2019, Vincent Hervieux vincent.hervieux@gmail.com
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the author Vincent Hervieux, nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "cibi.h"
#include "utils.h"
#include "vfo.h"
const int8_t Cibi::cibi_channels[45] PROGMEM = {
1, 2, 3, 3, 4, 5, 6, 7, 7, 8, 9,
10, 11, 11, 12, 13, 14, 15, 15, 16, 17, 18, 19, 19,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
40};
const bool Cibi::cibi_channels_bis[45] PROGMEM = {
/* 1- 9*/ false, false, false, true /*3*/, false, false, false, false, true /*7*/, false, false,
/*10-19*/ false, false, true /*11*/, false, false, false, false, true /*15*/, false, false, false, false, true /*19*/,
/*20-29*/ false, false, false, false, false, false, false, false, false, false,
/*30-39*/ false, false, false, false, false, false, false, false, false, false,
/* 40 */ false};
Cibi::Cibi(Config &_config, Input &_input, U8G2 &_display, DDS &_dds) : config_(_config),
input_(_input),
dds_(_dds),
current_cibi_channel_({.channel = -1,
.band = CIBI_BAND_UNKNOWN,
.bis = false}),
current_freq_(0),
freq_change_time_(0),
current_clarifier_(0),
freq_step_(0),
current_cursor_pos_(CURSOR_POS_FREQ_DIG5), // <-- should be a parameter?
current_modulation_(-1),
current_smeter_(0),
current_tx_(false),
scanning_(false)
{
view_ = new CibiDisplay(_display);
updateFreqStep(current_cursor_pos_);
}
Cibi::Cibi(const Cibi &_cibi) : config_(_cibi.config_),
input_(_cibi.input_),
dds_(_cibi.dds_),
current_cibi_channel_({.channel = _cibi.current_cibi_channel_.channel,
.band = _cibi.current_cibi_channel_.band,
.bis = _cibi.current_cibi_channel_.bis}),
current_freq_(_cibi.current_freq_),
freq_change_time_(_cibi.freq_change_time_),
current_clarifier_(_cibi.current_clarifier_),
freq_step_(_cibi.freq_step_),
current_cursor_pos_(_cibi.current_cursor_pos_),
current_modulation_(_cibi.current_modulation_),
current_smeter_(_cibi.current_smeter_),
current_tx_(_cibi.current_tx_),
scanning_(_cibi.scanning_),
view_(_cibi.view_)
{
}
Cibi::~Cibi()
{
delete view_;
}
int Cibi::getCibiChannel(uint32_t _frequency, Cibi::cibi_channel_t *_channel)
{
/* Cibi channels */
/* Pay attention that TX over 28Mhz is reserved for HAM use */
const static uint16_t FREQ_CIBI_LOW_DAKHZ PROGMEM = 2561;
const static uint16_t FREQ_CIBI_HIGH_DAKHZ PROGMEM = 2875;
/* A cibi band is 45 channels:
- 40 channels
- 5 'bis' channels: 3,7,11,15,19 */
const uint16_t CIBI_BAND_DAKHZ PROGMEM = 45;
/* Round to kHz to compute channels
* This avoids to get no display of channels
* in case a fine tuning was made to clarify the voice
*/
uint16_t freq_dakhz = _frequency / 10000;
_channel->channel = -1;
_channel->band = CIBI_BAND_UNKNOWN;
_channel->bis = false;
/* Check if we are not out of Cibi band */
if (freq_dakhz < FREQ_CIBI_LOW_DAKHZ || freq_dakhz > FREQ_CIBI_HIGH_DAKHZ)
return -1;
/* move down band to 0 for computation */
freq_dakhz -= FREQ_CIBI_LOW_DAKHZ;
_channel->band = freq_dakhz / CIBI_BAND_DAKHZ;
/* reuse variable freq_dakhz as index in cibi_channel array, */
freq_dakhz = freq_dakhz % CIBI_BAND_DAKHZ;
_channel->channel = pgm_read_byte_near(cibi_channels + freq_dakhz);
_channel->bis = pgm_read_byte_near(cibi_channels_bis + freq_dakhz);
return 0;
}
void Cibi::init()
{
if (0 == current_freq_)
{
/* startup: retrieve value from config and compute our context */
current_freq_ = config_.getCibiFreq();
current_modulation_ = input_.getModulation();
current_tx_ = input_.getTx();
/* update DDS according to current_freq_ and current_modulation_*/
updateDDS(current_freq_, current_modulation_, current_tx_);
}
/* init display with newly created context */
view_->init();
view_->setFreq(current_freq_, WHITE);
bool cibiBand = (0 == getCibiChannel(current_freq_, &current_cibi_channel_));
if(cibiBand)
{
view_->setCibiChannel(current_cibi_channel_.channel, WHITE);
view_->setCibiBand(current_cibi_channel_.band, WHITE);
view_->setCibiChannelBis(current_cibi_channel_.bis, WHITE);
}
else
{
/* HAM band */
view_->setBand(current_freq_, WHITE);
}
view_->setCursorPos(current_cursor_pos_, WHITE);
view_->setModulation(current_modulation_, WHITE);
#ifdef SMETER_DISPLAY
view_->setSMeter(0);
#endif
}
#if 1
void Cibi::updateDisplay(uint32_t _frequency, int _position, int _modulation, bool _tx, int _smeter)
{
cibi_channel_t cibi_channel({.channel = -1,
.band = CIBI_BAND_UNKNOWN,
.bis = false});
uint32_t clarified_freq = add_int32_to_uint32(current_freq_, current_clarifier_);
if (_frequency != clarified_freq ||
current_cibi_channel_.channel != cibi_channel.channel ||
_position != current_cursor_pos_ ||
_modulation != current_modulation_ ||
_tx != current_tx_)
{
view_->clear();
/* TODO: could be optimized to update only the impacted digits */
view_->setTx(_tx);
view_->setFreq(_frequency, (_tx? BLACK : WHITE));
/* if withing the 10-11m band, check for Cibi channels */
/* Outside this range, set HAM bands */
bool cibiBand = (0 == getCibiChannel(_frequency, &cibi_channel));
if(cibiBand)
{
#if 0 // def DEBUG
Serial.println(cibi_channel.channel);
#endif // DEBUG
view_->setCibiChannel(cibi_channel.channel, WHITE);
current_cibi_channel_.channel = cibi_channel.channel;
view_->setCibiBand(cibi_channel.band, WHITE);
current_cibi_channel_.band = cibi_channel.band;
view_->setCibiChannelBis(cibi_channel.bis, WHITE);
current_cibi_channel_.bis = cibi_channel.bis;
}
else
{
view_->setBand(_frequency, WHITE);
current_cibi_channel_.channel = -1;
current_cibi_channel_.band = CIBI_BAND_UNKNOWN;
}
view_->setCursorPos(_position, WHITE);
view_->setModulation(_modulation, WHITE);
#ifdef SMETER_DISPLAY
view_->setSMeter(_smeter);
#else
view_->display();
#endif
clarified_freq = _frequency;
current_cibi_channel_.channel = cibi_channel.channel;
current_cursor_pos_ = _position;
current_modulation_ = _modulation;
}
}
#endif
#if 0
void Cibi::updateDisplay(uint32_t _frequency, int _position, int _modulation, bool _tx, int _smeter)
{
bool update = false;
cibi_channel_t cibi_channel({.channel = -1,
.band = CIBI_BAND_UNKNOWN,
.bis = false});
uint32_t clarified_freq = add_int32_to_uint32(current_freq_, current_clarifier_);
if (_frequency != clarified_freq)
{
/* TODO: could be optimized to update only the impacted digits */
view_->setFreq(clarified_freq, BLACK);
view_->setFreq(_frequency, WHITE);
update = true;
/* if withing the 10-11m band, check for Cibi channels */
/* Outside this range, set HAM bands */
bool cibiBand = (0 == getCibiChannel(_frequency, &cibi_channel));
if(cibiBand)
{
#if 0 // def DEBUG
Serial.println(cibi_channel.channel);
#endif // DEBUG
if (current_cibi_channel_.channel != cibi_channel.channel)
{
if(-1 == current_cibi_channel_.channel)
{
view_->setBand(clarified_freq, BLACK);
}
else
{
view_->setCibiChannel(current_cibi_channel_.channel, BLACK);
}
view_->setCibiChannel(cibi_channel.channel, WHITE);
current_cibi_channel_.channel = cibi_channel.channel;
}
if (current_cibi_channel_.band != cibi_channel.band &&
CIBI_BAND_UNKNOWN != cibi_channel.band)
{
view_->setCibiBand(current_cibi_channel_.band, BLACK);
view_->setCibiBand(cibi_channel.band, WHITE);
current_cibi_channel_.band = cibi_channel.band;
}
if (current_cibi_channel_.bis != cibi_channel.bis)
{
view_->setCibiChannelBis(current_cibi_channel_.bis, BLACK);
view_->setCibiChannelBis(cibi_channel.bis, WHITE);
current_cibi_channel_.bis = cibi_channel.bis;
}
}
else
{
/* HAM band */
if(-1 != current_cibi_channel_.channel)
{
view_->setCibiChannel(current_cibi_channel_.channel, BLACK);
view_->setCibiBand(current_cibi_channel_.band, BLACK);
view_->setCibiChannelBis(current_cibi_channel_.bis, BLACK);
}
else
{
view_->setBand(clarified_freq, BLACK);
}
view_->setBand(_frequency, WHITE);
current_cibi_channel_.channel = -1;
current_cibi_channel_.band = CIBI_BAND_UNKNOWN;
}
}
if (_position != current_cursor_pos_)
{
view_->setCursorPos(current_cursor_pos_, BLACK);
view_->setCursorPos(_position, WHITE);
update = true;
}
if (_modulation != current_modulation_)
{
view_->setModulation(current_modulation_, BLACK);
view_->setModulation(_modulation, WHITE);
update = true;
}
if (update)
{
view_->display();
}
}
#endif
void Cibi::updateDDS(uint32_t _frequency, int _modulation, bool _tx)
{
bool enable_fi = _tx;
uint32_t vfo_freq = _frequency;
/* order matters, modulation needs to be updated prior to frequency */
switch (_modulation)
{
case MOD_AM:
case MOD_FM:
/* store the frequency currently used */
//dds_.setOutputFreq(DDS::OUTPUT_FI, config_.getFiAm());
break;
case MOD_CW:
/* store the frequency currently used */
dds_.setOutputFreq(DDS::OUTPUT_FI, config_.getFiAm());
enable_fi = true;
break;
case MOD_USB:
/* store the frequency currently used */
dds_.setOutputFreq(DDS::OUTPUT_FI, config_.getFiUsb());
enable_fi = true;
break;
case MOD_LSB:
/* store the frequency currently used */
dds_.setOutputFreq(DDS::OUTPUT_FI, config_.getFiLsb());
enable_fi = true;
break;
default:
// don't change the clock for unknown modulation type.
break;
}
/* RX/TX frequency is the VF0 + the FI
* So if we want to RX/TX on 27.555MHz in AM
* Set VFO to 27.555MHz - 10.695MHz = 16.860MHz (infradyne)
*/
vfo_freq -= dds_.getOutputFreq(DDS::OUTPUT_FI);
#if 0
/* RX/TX frequency is the VF0 - the FI
* So if we want to RX/TX on 27.555MHz in AM
* Set VFO to 27.555MHz + 10.695MHz = 38.250MHz (supradyne)
*/
vfo_freq += dds_.getOutputFreq(DDS::OUTPUT_FI);
#endif
dds_.setOutputFreq(DDS::OUTPUT_VFO, vfo_freq);
dds_.enableOutput(DDS::OUTPUT_VFO, true);
/* enable FI oscillator only while transmitting or in CW/SSB mode */
dds_.enableOutput(DDS::OUTPUT_FI, enable_fi);
}
void Cibi::updateFreqStep(int cursor_pos)
{
if (cursor_pos > CURSOR_POS_MAX ||
cursor_pos == CURSOR_POS_NONE)
{
freq_step_ = 0;
}
else
{
freq_step_ = quick_pow10(cursor_pos - 1);
}
}
void Cibi::loop(bool _update_display)
{
uint32_t new_frequency = current_freq_;
int new_cursor_pos = current_cursor_pos_;
int new_clarifier = current_clarifier_;
int16_t increment = input_.getEncoderIncrement();
bool click = input_.buttonClicked();
int new_smeter = input_.getSMeter();
// smeter adjustment
if(0 != config_.getSMeterAdj())
{
if(config_.getSMeterAdj() > 0)
{
/* if positive, multiply factor */
new_smeter = new_smeter * config_.getSMeterAdj();
}
else if (config_.getSMeterAdj() < 0)
{
/* if negative, division factor */
new_smeter = new_smeter / abs(config_.getSMeterAdj());
}
}
#ifdef DEBUG
//Serial.print(F("Sig "));Serial.println(smeter);
#endif // DEBUG
if (_update_display)
{
/* initial display */
init();
/* waiting for next call to loop() */
return;
}
if(input_.getIncrementMode() == Input::INCREMENT_MANUAL)
{
/* Increment mode: manual
======================
Click the encoder to select a digit
Change the selected digit value by encoder rotation
*/
if (click)
{
new_cursor_pos++;
if (new_cursor_pos > CURSOR_POS_MAX)
{
new_cursor_pos = CURSOR_POS_NONE;
}
}
updateFreqStep(new_cursor_pos);
}
else
{
/* Increment mode: step
====================
Encoder rotation change frequency value by step
Click to scan
*/
new_cursor_pos = CURSOR_POS_NONE;
freq_step_ = config_.getFreqStepIncrement();
if(click && !scanning_) {
scanning_ = true;
increment = 1;
}
else if ((click || input_.getTx()) && scanning_)
{
scanning_ = false;
increment = 0;
}
else if (increment && scanning_)
{
scanning_ = false;
}
if(scanning_) {
/* TODO: does scan threshold needs be in the config?
* Playing with RF gain does allow to play around this level
*/
if(new_smeter > 10) {
increment = -1; // seems that measure takes one loop, so return to previous freq
scanning_ = false;
} else {
increment = 1;
}
}
}
if (increment)
{
if (increment < 0 && (freq_step_ * abs(increment)) > new_frequency)
{
/* avoid negative values */
new_frequency = 0;
}
else
{
new_frequency += ((uint32_t)increment * freq_step_);
}
/* Limits with rotation */
if (new_frequency > config_.getCibiMaxFreq())
new_frequency = config_.getCibiMinFreq();
else if (new_frequency < config_.getCibiMinFreq())
new_frequency = config_.getCibiMaxFreq();
}
/* save new frequency to EEPROM after 30 seconds of usage */
if (new_frequency != current_freq_)
{
freq_change_time_ = millis();
input_.setBuiltInLED(HIGH);
}
else if(freq_change_time_ != 0)
{
if(millis() - freq_change_time_ > 30000)
{
/* about 30 seconds on the same frequency, save it */
config_.setCibiFreq(current_freq_);
freq_change_time_ = 0;
input_.setBuiltInLED(LOW);
}
}
if (new_frequency != current_freq_ ||
(input_.getClarifier() + config_.getClarifierAdj()) != current_clarifier_ ||
input_.getModulation() != current_modulation_ ||
new_cursor_pos != current_cursor_pos_ ||
input_.getTx() != current_tx_
#ifdef SMETER_DISPLAY
|| new_smeter != current_smeter_
#endif
)
{
/* Update display and context with newer values */
if(!input_.getTx())
{
/* Apply clarifier here if we are receiving */
new_clarifier = input_.getClarifier() + config_.getClarifierAdj();
}
else
{
new_clarifier = 0;
}
updateDisplay(add_int32_to_uint32(new_frequency, new_clarifier), new_cursor_pos, input_.getModulation(), input_.getTx(), new_smeter);
updateDDS(add_int32_to_uint32(new_frequency, new_clarifier), input_.getModulation(), input_.getTx());
current_freq_ = new_frequency;
current_clarifier_ = new_clarifier;
current_modulation_ = input_.getModulation();
current_cursor_pos_ = new_cursor_pos;
current_tx_ = input_.getTx();
current_smeter_ = new_smeter;
}
}
const uint32_t& Cibi::getCurrentFreq()
{
return current_freq_;
}
#if 0
int Cibi::getBand(uint32_t _frequency)
{
/*
* Display band names according to
* https://en.wikipedia.org/wiki/Amateur_radio_frequency_allocations
*/
const static uint32_t BAND_2200_LOW PROGMEM = 135700;
const static uint32_t BAND_2200_HI PROGMEM = 135800;
const static uint32_t BAND_1750_LOW PROGMEM = 160000;
const static uint32_t BAND_1750_HI PROGMEM = 190000;
const static uint32_t BAND_630_LOW PROGMEM = 472000;
const static uint32_t BAND_630_HI PROGMEM = 479000;
const static uint32_t BAND_160_LOW PROGMEM = 1800000;
const static uint32_t BAND_160_HI PROGMEM = 2000000;
const static uint32_t BAND_80_LOW PROGMEM = 3500000;
const static uint32_t BAND_80_HI PROGMEM = 4000000;
const static uint32_t BAND_60_LOW PROGMEM = 5332000;
const static uint32_t BAND_60_HI PROGMEM = 5450000;
const static uint32_t BAND_40_LOW PROGMEM = 7000000;
const static uint32_t BAND_40_HI PROGMEM = 7300000;
const static uint32_t BAND_30_LOW PROGMEM = 10100000;
const static uint32_t BAND_30_HI PROGMEM = 10150000;
const static uint32_t BAND_20_LOW PROGMEM = 14000000;
const static uint32_t BAND_20_HI PROGMEM = 14350000;
const static uint32_t BAND_17_LOW PROGMEM = 18068000;
const static uint32_t BAND_17_HI PROGMEM = 18168000;
const static uint32_t BAND_15_LOW PROGMEM = 21000000;
const static uint32_t BAND_15_HI PROGMEM = 21450000;
const static uint32_t BAND_12_LOW PROGMEM = 24890000;
const static uint32_t BAND_12_HI PROGMEM = 24990000;
const static uint32_t BAND_11_LOW PROGMEM = 25000000;
const static uint32_t BAND_11_HI PROGMEM = 28000000;
const static uint32_t BAND_10_LOW PROGMEM = 28000000;
const static uint32_t BAND_10_HI PROGMEM = 29700000;
const static uint32_t BAND_6_LOW PROGMEM = 50000000;
const static uint32_t BAND_6_HI PROGMEM = 54000000;
const static uint32_t BAND_2_LOW PROGMEM = 144000000;
const static uint32_t BAND_2_HI PROGMEM = 148000000;
if (_frequency > BAND_10_LOW && _frequency <= BAND_10_HI) return BAND_10;
else if (_frequency > BAND_11_LOW && _frequency <= BAND_11_HI) return BAND_11;
else if (_frequency > BAND_12_LOW && _frequency <= BAND_12_HI) return BAND_12;
else if (_frequency > BAND_15_LOW && _frequency <= BAND_15_HI) return BAND_15;
else if (_frequency > BAND_17_LOW && _frequency <= BAND_17_HI) return BAND_17;
else if (_frequency > BAND_20_LOW && _frequency <= BAND_20_HI) return BAND_20;
else if (_frequency > BAND_30_LOW && _frequency <= BAND_30_HI) return BAND_30;
else if (_frequency > BAND_2200_LOW && _frequency <= BAND_2200_HI) return BAND_2200;
else if (_frequency > BAND_1750_LOW && _frequency <= BAND_1750_HI) return BAND_1750;
else if (_frequency > BAND_630_LOW && _frequency <= BAND_630_HI) return BAND_630;
else if (_frequency > BAND_160_LOW && _frequency <= BAND_160_HI) return BAND_160;
else if (_frequency > BAND_80_LOW && _frequency <= BAND_80_HI) return BAND_80;
else if (_frequency > BAND_60_LOW && _frequency <= BAND_60_HI) return BAND_60;
else if (_frequency > BAND_40_LOW && _frequency <= BAND_40_HI) return BAND_40;
else if (_frequency > BAND_6_LOW && _frequency <= BAND_6_HI) return BAND_6;
else if (_frequency > BAND_2_LOW && _frequency <= BAND_2_HI) return BAND_2;
else return BAND_UNKNOWN;
}
#endif /* 0 */

303
src/cibidisplay.cpp Normal file
View File

@ -0,0 +1,303 @@
/*
* Copyright (c) 2019, Vincent Hervieux vincent.hervieux@gmail.com
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the author Vincent Hervieux, nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/***
*** Display: using an OLED SSD1306
***/
#include "cibidisplay.h"
#include "vfo.h"
#include <Arduino.h>
#include <avr/pgmspace.h>
CibiDisplay::CibiDisplay(U8G2 &_display):
display_(_display)
{
}
CibiDisplay::CibiDisplay(const CibiDisplay &_cibidisplay):
display_(_cibidisplay.display_)
{
}
CibiDisplay::~CibiDisplay()
{
display_.clearDisplay();
}
void CibiDisplay::init()
{
const static int16_t SMETER_TOP_X PROGMEM = 5;
const static int16_t SMETER_TOP_Y PROGMEM = 8;
const static int16_t FREQ_DOT_TEXT_POS_X PROGMEM = 32;
const static int16_t MHZ_TEXT_POS_X PROGMEM = 120;
const static int16_t H_LINE_WIDTH PROGMEM = 118;
const static int16_t H_LINE_POS_X PROGMEM = 0;
#ifdef SMETER_DISPLAY
/* S meter frame */
display_.setFont(u8g2_font_6x10_tr);
display_.setCursor(SMETER_TOP_X,SMETER_TOP_Y);
display_.setDrawColor(WHITE);
display_.print(F("SIG.1.3.5.7.9..+30dB"));
#else
/* S meter frame */
display_.setFont(u8g2_font_6x10_tr);
display_.setCursor(SMETER_TOP_X,SMETER_TOP_Y+4);
display_.setDrawColor(WHITE);
display_.print(SMETER_REPLACEMENT_TEXT);
#endif
/* Frequency dot */
display_.drawDisc(FREQ_DOT_TEXT_POS_X, 35, 2, WHITE);
/* Mhz sign */
display_.setCursor(MHZ_TEXT_POS_X,24);
display_.print(F("M"));
display_.setCursor(MHZ_TEXT_POS_X,33);
display_.print(F("H"));
display_.setCursor(MHZ_TEXT_POS_X,40);
display_.print(F("z"));
/* 2 lines separation between informations */
display_.drawHLine(H_LINE_POS_X, 16, H_LINE_WIDTH);
display_.drawHLine(H_LINE_POS_X, 40, H_LINE_WIDTH);
}
void CibiDisplay::clear()
{
display_.clearBuffer();
}
void CibiDisplay::display()
{
init();
display_.sendBuffer();
}
void CibiDisplay::setFreq(uint32_t _freq, uint16_t _color)
{
const static uint16_t MAX_FREQ_STR_LENGTH PROGMEM = 8; /* 30000000*/
char str[MAX_FREQ_STR_LENGTH + 1] = {0};
char big_str[4] = {0};
snprintf(str, MAX_FREQ_STR_LENGTH + 1, "%08ld", _freq);
strncpy (big_str, str, 2 * sizeof(char));
display_.setFont(u8g2_font_crox5hb_tr);
display_.setCursor(FREQ_DECIMAL_TEXT_POS_X,FREQ_TEXT_POS_Y);
display_.setDrawColor(_color);
display_.print(big_str);
display_.setCursor(FREQ_FRACTIONNAL_TEXT_POS_X,FREQ_TEXT_POS_Y);
display_.print(str + 2 * sizeof(char));
}
void CibiDisplay::setCursorPos(int _position, uint16_t _color)
{
const static int16_t CURSOR_TEXT_POS_Y PROGMEM = 37;
const static int16_t CURSOR_DECIMAL_TEXT_POS_X PROGMEM = 17;
const static int16_t CURSOR_FRACTIONAL_TEXT_POS_X PROGMEM = FREQ_DECIMAL_TEXT_POS_X + 35;
const static int16_t CURSOR_WIDTH PROGMEM = 10;
const static int16_t CURSOR_HEIGHT PROGMEM = 2;
const static int16_t CURSOR_SHIFT PROGMEM = 14;
int16_t x;
if(CURSOR_POS_NONE == _position) return;
if(CURSOR_POS_FREQ_DIG7 == _position)
{
x = CURSOR_DECIMAL_TEXT_POS_X;
}
else
{
x = CURSOR_FRACTIONAL_TEXT_POS_X +
(CURSOR_POS_FREQ_DIG6 - _position) * CURSOR_SHIFT;
}
display_.setDrawColor(_color);
display_.drawBox(x, CURSOR_TEXT_POS_Y, CURSOR_WIDTH, CURSOR_HEIGHT);
}
void CibiDisplay::setModulation(int _modulation, uint16_t _color)
{
const static int16_t MOD_TEXT_POS_X PROGMEM = 70;
const static int16_t MOD_TEXT_POS_Y PROGMEM = 64;
display_.setFont(u8g2_font_crox5hb_tr);
display_.setCursor(MOD_TEXT_POS_X,MOD_TEXT_POS_Y);
display_.setDrawColor(_color);
switch (_modulation)
{
case MOD_CW:
display_.print(F(" CW"));
break;
case MOD_AM:
display_.print(F(" AM"));
break;
case MOD_FM:
display_.print(F(" FM"));
break;
case MOD_USB:
display_.print(F("USB"));
break;
case MOD_LSB:
default:
display_.print(F("LSB"));
break;
}
}
void CibiDisplay::setCibiChannel(int _channel, uint16_t _color)
{
const static int16_t CIBI_CHANNEL_TEXT_POS_X PROGMEM = 16;
const static int16_t CIBI_CHANNEL_TEXT_POS_Y PROGMEM = 64;
char str[3] = {0};
if(-1 != _channel)
{
display_.setFont(u8g2_font_crox5hb_tr);
display_.setDrawColor(_color);
if(_channel < 10)
display_.setCursor(CIBI_CHANNEL_TEXT_POS_X + 14, CIBI_CHANNEL_TEXT_POS_Y);
else
display_.setCursor(CIBI_CHANNEL_TEXT_POS_X, CIBI_CHANNEL_TEXT_POS_Y);
snprintf(str, sizeof(str), "%d", _channel);
display_.print(str);
}
}
void CibiDisplay::setCibiChannelBis(bool _bis, uint16_t _color)
{
const static int16_t CIBI_CHANNEL_BIS_TEXT_POS_X PROGMEM = 45;
const static int16_t CIBI_CHANNEL_BIS_POS_Y PROGMEM = 50;
if(_bis)
{
display_.setFont(u8g2_font_6x10_tr);
display_.setDrawColor(_color);
display_.setCursor(CIBI_CHANNEL_BIS_TEXT_POS_X, CIBI_CHANNEL_BIS_POS_Y);
display_.print(F("bis"));
}
}
void CibiDisplay::setCibiBand(int _band, uint16_t _color)
{
const static int CIBI_BAND_NORMAL PROGMEM = 3;
if(CIBI_BAND_NORMAL == _band) return;
int deviation = CIBI_BAND_NORMAL - _band;
display_.setDrawColor(_color);
for(int i = 0; i < abs(deviation); i++)
{
display_.drawHLine(7, 58 - i*6, 5);
}
if(deviation < 0)
{
/* + signs */
for(int i = 0; i < abs(deviation); i++)
{
display_.drawVLine(9, 56 - i*6, 5);
}
}
}
void CibiDisplay::setBand(uint32_t _freq, uint16_t _color)
{
const static int16_t BAND_TEXT_POS_X PROGMEM = FREQ_DECIMAL_TEXT_POS_X;
const static int16_t BAND_TEXT_POS_Y PROGMEM = 64;
display_.setFont(u8g2_font_crox5hb_tr);
display_.setCursor(BAND_TEXT_POS_X,BAND_TEXT_POS_Y);
display_.setDrawColor(_color);
if(135700 <=_freq && _freq <= 137800)
{
/* For this band do not print 'm' to earn on string's lenght */
display_.print(F("2222"));
}
else if(472000 <=_freq && _freq <= 479000)
{
display_.print(F("630m"));
}
else if(1800000 <=_freq && _freq <= 2000000)
{
display_.print(F("160m"));
}
else if(3500000 <= _freq && _freq <= 4000000)
{
display_.print(F("80m"));
}
else if(5351500 <= _freq && _freq <= 5366500)
{
display_.print(F("60m"));
}
else if(7000000 <= _freq && _freq <= 7300000)
{
display_.print(F("40m"));
}
else if(10100000 <= _freq && _freq <= 10150000)
{
display_.print(F("30m"));
}
else if(14000000 <= _freq && _freq <= 14350000)
{
display_.print(F("20m"));
}
else if(18068000 <= _freq && _freq <= 18168000)
{
display_.print(F("17m"));
}
else if(21000000 <= _freq && _freq <= 21450000)
{
display_.print(F("15m"));
}
else if(24890000 <= _freq && _freq <= 24990000)
{
display_.print(F("12m"));
}
else if(28000000 <= _freq && _freq <= 29700000)
{
display_.print(F("10m"));
}
else
{
display_.print(F("OOB"));
}
}
#ifdef SMETER_DISPLAY
void CibiDisplay::setSMeter(int _signal)
{
// cursor
display_.setDrawColor(BLACK);
display_.drawBox(12, 11, 116, 4);
display_.setDrawColor(WHITE);
for(int index = 0; index < _signal && index < 21; index++)
{
display_.drawBox(12 + index * 5, 11, 4, 4);
}
display();
}
#endif
void CibiDisplay::setTx(bool _tx)
{
// invert black and white of the displayed frequency in TX
const static int16_t H_LINE_WIDTH PROGMEM = 118;
const static int16_t H_LINE_POS_X PROGMEM = 0;
const static int16_t FREQ_DOT_TEXT_POS_X PROGMEM = 32;
display_.setDrawColor((_tx? WHITE : BLACK));
display_.drawBox(H_LINE_POS_X, 17, H_LINE_WIDTH, 23);
}

307
src/config.cpp Normal file
View File

@ -0,0 +1,307 @@
/*
* Copyright (c) 2019, Vincent Hervieux vincent.hervieux@gmail.com
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the author Vincent Hervieux, nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "utils.h"
#include "vfo.h"
#include <avr/eeprom.h>
const static uint32_t EEPROM_HEADER PROGMEM = 0xDEADBEEF;
const static uint32_t DEFAULT_CIBI_FREQ PROGMEM = 27555000;
const static uint32_t DEFAULT_CIBI_MIN_FREQ PROGMEM = 25000000;
const static uint32_t DEFAULT_CIBI_MAX_FREQ PROGMEM = 30000000;
const static uint32_t DEFAULT_FREQ_STEP_INC PROGMEM = 5000;
/* On superstar 3900 and derivated:
- reference oscillator is at 10240KHz
- FI is either 10695KHz(CW,AM,FM), 10692.5KHz(USB), 10697.5KHz(LSB) */
const static uint32_t DEFAULT_FI_FREQ_AM PROGMEM = 10695000;
const static uint32_t DEFAULT_FI_FREQ_USB PROGMEM = 10692500;
const static uint32_t DEFAULT_FI_FREQ_LSB PROGMEM = 10697500;
const static int32_t DEFAULT_VFO_ADJ PROGMEM = 0;
const static int32_t DEFAULT_CLARIFIER_ADJ PROGMEM = 0;
const static int32_t DEFAULT_SMETER_ADJ PROGMEM = 0;
Config::Config(Input &_input, U8G2 &_display, DDS &_dds):
context_({
.eeprom_header = EEPROM_HEADER,
.cibi_frequency = DEFAULT_CIBI_FREQ,
.cibi_min_freq = DEFAULT_CIBI_MIN_FREQ,
.cibi_max_freq = DEFAULT_CIBI_MAX_FREQ,
.freq_step_inc = DEFAULT_FREQ_STEP_INC,
.fi_am = DEFAULT_FI_FREQ_AM,
.fi_usb = DEFAULT_FI_FREQ_USB,
.fi_lsb = DEFAULT_FI_FREQ_LSB,
.vfo_adj = DEFAULT_VFO_ADJ,
.clarifier_adj = DEFAULT_CLARIFIER_ADJ,
.smeter_adj = DEFAULT_SMETER_ADJ
}),
input_(_input),
dds_(_dds),
current_cursor_pos_(CURSOR_POS_NONE),
current_menu_index_(CONFIG_CIBI_FREQ),
current_value_(&context_.cibi_frequency)
{
view_ = new ConfigDisplay(_display);
}
Config::Config(const Config& _config) :
context_({
.eeprom_header = _config.context_.eeprom_header,
.cibi_frequency = _config.context_.cibi_frequency,
.cibi_min_freq = _config.context_.cibi_min_freq,
.cibi_max_freq = _config.context_.cibi_max_freq,
.freq_step_inc = _config.context_.freq_step_inc,
.fi_am = _config.context_.fi_am,
.fi_usb = _config.context_.fi_usb,
.fi_lsb = _config.context_.fi_lsb,
.vfo_adj = _config.context_.vfo_adj,
.clarifier_adj = _config.context_.clarifier_adj,
.smeter_adj = _config.context_.smeter_adj,
}),
input_(_config.input_),
dds_(_config.dds_),
view_(_config.view_),
current_cursor_pos_(_config.current_cursor_pos_),
current_menu_index_(_config.current_menu_index_),
current_value_(_config.current_value_)
{
}
Config::~Config()
{
delete view_;
}
void Config::loop(bool _update_display)
{
bool update = _update_display;
uint32_t freq_step = 0;
int increment = 0;
if(input_.buttonClicked())
{
update = true;
current_cursor_pos_++;
if(current_cursor_pos_ >= CURSOR_POS_MAX)
{
current_cursor_pos_ = CURSOR_POS_NONE;
}
}
freq_step = quick_pow10(current_cursor_pos_-1);
increment = (int)input_.getEncoderIncrement();
if(increment)
{
update = true;
if(CURSOR_POS_NONE == current_cursor_pos_)
{
/* change between menu items */
current_menu_index_ += increment;
if (current_menu_index_ >= CONFIG_MAX)
{
current_menu_index_ = 0;
}
else if (current_menu_index_ < 0)
{
current_menu_index_ = CONFIG_MAX - 1;
}
/* change displayed item value */
switch (current_menu_index_)
{
case CONFIG_CIBI_FREQ:
current_value_ = &context_.cibi_frequency;
break;
case CONFIG_CIBI_MIN_FREQ:
current_value_ = &context_.cibi_min_freq;
break;
case CONFIG_CIBI_MAX_FREQ:
current_value_ = &context_.cibi_max_freq;
break;
case CONFIG_FREQ_STEP:
current_value_ = &context_.freq_step_inc;
break;
case CONFIG_FI_AM:
current_value_ = &context_.fi_am; /* FI AM/FM/CW */
break;
case CONFIG_FI_USB:
current_value_ = &context_.fi_usb; /* FI USB */
break;
case CONFIG_FI_LSB:
current_value_ = &context_.fi_lsb; /* FI LSB */
break;
case CONFIG_VFO_ADJ:
current_value_ = (uint32_t*)&context_.vfo_adj;
break;
case CONFIG_CLARIFIER_ADJ:
current_value_ = (uint32_t*)&context_.clarifier_adj;
break;
case CONFIG_SMETER_ADJ:
current_value_ = (uint32_t*)&context_.smeter_adj;
break;
default:
return;
}
}
else
{
if((current_menu_index_ != CONFIG_VFO_ADJ) &&
(current_menu_index_ != CONFIG_CLARIFIER_ADJ) &&
(current_menu_index_ != CONFIG_SMETER_ADJ) &&
(increment < 0) &&
(freq_step * abs(increment)) > (*current_value_))
{
/* avoid negative values */
(*current_value_) = 0;
}
else
{
(*current_value_) += ((uint32_t)increment * freq_step);
}
}
}
if(update)
{
view_->clear();
if(CURSOR_POS_NONE == current_cursor_pos_)
{
view_->highlightTitle(WHITE);
view_->setTitle(current_menu_index_, BLACK);
}
else
{
view_->setTitle(current_menu_index_, WHITE);
}
if (current_menu_index_ != CONFIG_VFO_ADJ &&
current_menu_index_ != CONFIG_CLARIFIER_ADJ &&
current_menu_index_ != CONFIG_SMETER_ADJ)
{
view_->setValueUint((*current_value_), WHITE);
}
else
{
view_->setValueInt((*(int32_t*)current_value_), WHITE);
}
if(current_menu_index_ == CONFIG_VFO_ADJ)
{
/* set si5351 correction here */
dds_.setOutputCorrection((*(int32_t*)current_value_));
}
view_->setCursorPos(current_cursor_pos_, WHITE);
view_->display();
}
}
int Config::open()
{
eeprom_read_block((void*)&context_, (void*)0, sizeof(context_t));
if(context_.eeprom_header != EEPROM_HEADER)
{
/* EEPROM first time use */
context_.eeprom_header = EEPROM_HEADER;
context_.cibi_frequency = DEFAULT_CIBI_FREQ;
context_.cibi_min_freq = DEFAULT_CIBI_MIN_FREQ;
context_.cibi_max_freq = DEFAULT_CIBI_MAX_FREQ;
context_.freq_step_inc = DEFAULT_FREQ_STEP_INC;
context_.fi_am = DEFAULT_FI_FREQ_AM;
context_.fi_usb = DEFAULT_FI_FREQ_USB;
context_.fi_lsb = DEFAULT_FI_FREQ_LSB;
context_.vfo_adj = DEFAULT_VFO_ADJ;
context_.smeter_adj = DEFAULT_SMETER_ADJ;
}
return 0;
}
int Config::save()
{
eeprom_update_block((const void*)&context_, (void*)0, sizeof(context_t));
return 0;
}
const uint32_t& Config::getCibiFreq()
{
return context_.cibi_frequency;
}
const uint32_t& Config::getCibiMinFreq()
{
return context_.cibi_min_freq;
}
const uint32_t& Config::getCibiMaxFreq()
{
return context_.cibi_max_freq;
}
const uint32_t& Config::getFreqStepIncrement()
{
return context_.freq_step_inc;
}
const uint32_t& Config::getFiAm()
{
return context_.fi_am;
}
const uint32_t& Config::getFiUsb()
{
return context_.fi_usb;
}
const uint32_t& Config::getFiLsb()
{
return context_.fi_lsb;
}
const int32_t& Config::getVFOAdj()
{
return context_.vfo_adj;
}
const int32_t& Config::getClarifierAdj()
{
return context_.clarifier_adj;
}
const int32_t& Config::getSMeterAdj()
{
return context_.smeter_adj;
}
void Config::setCibiFreq(const uint32_t& _freq)
{
if(_freq != context_.cibi_frequency)
{
context_.cibi_frequency = _freq;
save();
}
}

153
src/configdisplay.cpp Normal file
View File

@ -0,0 +1,153 @@
/*
* Copyright (c) 2019, Vincent Hervieux vincent.hervieux@gmail.com
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the author Vincent Hervieux, nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "configdisplay.h"
#include "vfo.h"
#include <Arduino.h>
#include <avr/pgmspace.h>
ConfigDisplay::ConfigDisplay(U8G2 &_display):
display_(_display)
{
}
ConfigDisplay::~ConfigDisplay()
{
display_.clearDisplay();
}
void ConfigDisplay::init()
{
display_.drawHLine(0, 15, SCREEN_WIDTH);
display_.drawHLine(0, 31, SCREEN_WIDTH);
}
void ConfigDisplay::clear()
{
display_.clearBuffer();
}
void ConfigDisplay::display()
{
display_.updateDisplay();
}
void ConfigDisplay::clearDisplay()
{
display_.clearDisplay();
}
void ConfigDisplay::setTitle(int _title_index, uint16_t _color)
{
const static int16_t TITLE_POS_X PROGMEM = 4;
const static int16_t TITLE_POS_Y PROGMEM = 27;
const static int16_t INDEX_POS_X PROGMEM = 35;
const static int16_t INDEX_POS_Y PROGMEM = 11;
const static int16_t MAX_STR_LENGTH PROGMEM = 20;
const static char title_cibi_default_freq[] PROGMEM = "CIBI DEFAULT FREQ.";
const static char title_cibi_min_freq[] PROGMEM = " CIBI FREQ. MIN.";
const static char title_cibi_max_freq[] PROGMEM = " CIBI FREQ. MAX.";
const static char title_freq_step_inc[] PROGMEM = "FREQ. STEP INCREM.";
const static char title_fi_am_fm[] PROGMEM = " FI AM/FM";
const static char title_fi_usb[] PROGMEM = " FI USB";
const static char title_fi_lsb[] PROGMEM = " FI LSB";
const static char title_vfo_adj[] PROGMEM = " VFO FREQ. ADJ";
const static char title_clarifier_adj[] PROGMEM = "CLARIFIER FREQ. ADJ";
const static char title_smeter_adj[] PROGMEM = " S-METER ADJ";
const static char* const titles[] PROGMEM = {
title_cibi_default_freq,
title_cibi_min_freq,
title_cibi_max_freq,
title_freq_step_inc,
title_fi_am_fm,
title_fi_usb,
title_fi_lsb,
title_vfo_adj,
title_clarifier_adj,
title_smeter_adj
};
char str[MAX_STR_LENGTH] = {0};
int menu_index = _title_index + 1;
int menu_items = sizeof(titles) / sizeof(char*);
//display_.setFont(u8g2_font_6x10_tr);
display_.setFont(u8g2_font_6x12_tr);
display_.setDrawColor(_color);
snprintf(str, MAX_STR_LENGTH + 1, "CONFIG %d/%d :", menu_index, menu_items);
display_.setCursor(INDEX_POS_X, INDEX_POS_Y);
display_.print(str);
display_.setCursor(TITLE_POS_X,TITLE_POS_Y);
strcpy_P(str, (char*)pgm_read_word(&(titles[_title_index])));
display_.print(str);
display_.drawHLine(0, 15, SCREEN_WIDTH);
display_.drawHLine(0, 31, SCREEN_WIDTH);
}
void ConfigDisplay::highlightTitle(uint16_t _color)
{
display_.setDrawColor(_color);
display_.drawBox(0, 0, SCREEN_WIDTH, 31);
}
void ConfigDisplay::setValueUint(uint32_t _value, uint16_t _color)
{
char str[MAX_STR_LENGTH] = {0};
snprintf(str, MAX_STR_LENGTH + 1, "%08lu", _value);
display_.setFont(u8g2_font_7x14B_tf);
display_.setCursor(VALUE_POS_X,VALUE_POS_Y);
display_.setDrawColor(_color);
display_.print(str);
}
void ConfigDisplay::setValueInt(int32_t _value, uint16_t _color)
{
const static uint16_t SIGN_SHIFT PROGMEM = 9;
char str[MAX_STR_LENGTH] = {0};
display_.setDrawColor(_color);
if(_value < 0) {
display_.drawHLine(VALUE_POS_X - SIGN_SHIFT, VALUE_POS_Y - 8, 5);
}
snprintf(str, MAX_STR_LENGTH + 1, "%08ld", abs(_value));
display_.setFont(u8g2_font_7x14B_tr);
display_.setCursor(VALUE_POS_X,VALUE_POS_Y);
display_.print(str);
}
void ConfigDisplay::setCursorPos(int _position, uint16_t _color)
{
const static int16_t CURSOR_POS_X PROGMEM = 25;
const static int16_t CURSOR_POS_Y PROGMEM = 58;
const static int16_t CURSOR_BIG_WIDTH PROGMEM = 8;
const static int16_t CURSOR_SHIFT PROGMEM = 9;
int16_t x;
display_.setDrawColor(_color);
if(_position >= CURSOR_POS_FREQ_DIG1 && _position <= CURSOR_POS_FREQ_DIG7)
{
x = CURSOR_POS_X + (8 - _position) * CURSOR_SHIFT;
display_.drawBox(x, CURSOR_POS_Y, CURSOR_BIG_WIDTH, 2);
}
}

100
src/dds.cpp Normal file
View File

@ -0,0 +1,100 @@
/*
* Copyright (c) 2019, Vincent Hervieux vincent.hervieux@gmail.com
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the author Vincent Hervieux, nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/***
*** DDS: Direct Digital Synthesis
*** The three si5351 clock outputs are square wave
***/
#include "dds.h"
#include "vfo.h"
DDS::DDS():
initialized_(false)
{
/* initialize stored values */
for(int i = 0; i < OUTPUT_MAX; i++)
current_freq_[i] = 0;
}
DDS::~DDS()
{
clockgen_.off();
}
void DDS::init()
{
if(!initialized_)
{
/* This lib has no way to know if I2C communication is effective */
clockgen_.init(XTALFREQ);
/* Set drive current to its maximum */
clockgen_.setPower(OUTPUT_FI, SIOUT_8mA);
enabled_[OUTPUT_FI] = true;
/* 2 mA : FM is weak */
/* 4 mA : FM is distorded */
/* 6 mA : FM is distorded */
/* 8 mA : FM is really distorded - adjust VR5*/
clockgen_.setPower(OUTPUT_VFO, SIOUT_8mA);
enabled_[OUTPUT_VFO] = true;
enabled_[OUTPUT_UNUSED] = false;
initialized_ = true;
}
}
const uint32_t& DDS::getOutputFreq(output_t _output)
{
return current_freq_[_output];
}
void DDS::setOutputFreq(output_t _output, uint32_t _frequency)
{
clockgen_.setFreq(_output, _frequency);
/* store the frequency currently used */
current_freq_[_output] = _frequency;
}
void DDS::setOutputCorrection(int32_t _correction)
{
/* set quartz correction */
clockgen_.correction(_correction);
clockgen_.setFreq(OUTPUT_VFO, current_freq_[OUTPUT_VFO]);
clockgen_.setFreq(OUTPUT_FI, current_freq_[OUTPUT_FI]);
}
void DDS::enableOutput(output_t _output, bool _enable)
{
if(_enable && !enabled_[_output])
{
clockgen_.enable(_output);
enabled_[_output] = true;
}
else if(!_enable && enabled_[_output])
{
clockgen_.disable(_output);
enabled_[_output] = false;
}
}