mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2026-09-02 12:59:03 +00:00
Compare commits
32 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 86203f090f | |||
| 19fed78b7f | |||
| c8774e82d9 | |||
| 62ee23a314 | |||
| 5f95cec5f4 | |||
| 66ba6442b1 | |||
| 7e20cea6d7 | |||
| 65217856c0 | |||
| 5117538b49 | |||
| 362c208078 | |||
| 4f9e8e9642 | |||
| de53626a46 | |||
| 2cf6849985 | |||
| 1cde3ab189 | |||
| bdb70a3231 | |||
| 49e63e2728 | |||
| 1beca57e56 | |||
| 27632825a3 | |||
| e02e58b366 | |||
| 37912df085 | |||
| a20a08c630 | |||
| a8885282cd | |||
| 2d9090862b | |||
| 6e5bb7911c | |||
| 46f98ddc8b | |||
| 2902365b80 | |||
| c0f368c978 | |||
| 75c2b69f19 | |||
| 3291c7e2a0 | |||
| 64ca9f6a68 | |||
| 297063873f | |||
| 1f7b800c2a |
@@ -6,7 +6,7 @@ This is a fork of the [Havoc](https://github.com/furrtek/portapack-havoc/) firmw
|
||||
|
||||
[<img src="https://raw.githubusercontent.com/wiki/eried/portapack-mayhem/img/hw_overview_h2_front.png" height="400">](https://github.com/eried/portapack-mayhem/wiki/Hardware-overview) [<img src="https://raw.githubusercontent.com/wiki/eried/portapack-mayhem/img/hw_overview_h2_inside.png" height="400">](https://github.com/eried/portapack-mayhem/wiki/Hardware-overview#portapack-internals)
|
||||
|
||||
*[PortaPack H2+HackRF+battery](https://s.click.aliexpress.com/e/_9zbXMk) (clone) with a custom [3d printed case](https://github.com/eried/portapack-mayhem/wiki/H2-Enclosure)*
|
||||
*[PortaPack H2+HackRF+battery](https://s.click.aliexpress.com/e/_DmU7GQX) (clone) with a custom [3d printed case](https://github.com/eried/portapack-mayhem/wiki/H2-Enclosure)*
|
||||
|
||||
# Quick overview
|
||||
|
||||
@@ -18,31 +18,23 @@ If you are new to *HackRF+PortaPack+Mayhem*, there is an awesome introductory vi
|
||||
|
||||
This repository expands upon the previous work by many people and aims to constantly add new features, bugfixes and generate documentation to make further development easier. [Collaboration](https://github.com/eried/portapack-mayhem/wiki/How-to-collaborate) is always welcomed and appreciated.
|
||||
|
||||
## Does it work on H1/H2 PortaPack?
|
||||
## What to buy?
|
||||
|
||||
Yes, both devices are the [same](https://github.com/eried/portapack-mayhem/wiki/First-steps). The one I am using to test all changes is this [PortaPack H2+HackRF+battery](https://s.click.aliexpress.com/e/_9zbXMk), which is a kit that includes everything you need. Sadly, the people making the H2 never made the updated schematics available, which is not ideal (and goes against the terms of the license). Most members of the community are using a clone of the [PortaPack H1+HackRF+metal case](https://s.click.aliexpress.com/e/_dS6liw4), which does not include any battery functionality, but it is a cheaper alternative.
|
||||
:heavy_check_mark: A recommended one is this [PortaPack H2](https://s.click.aliexpress.com/e/_DmU7GQX) pack, that includes everything you need. Sadly, the people making the H2 never made the updated schematics available (against the terms of the license).
|
||||
|
||||
To support the people behind the hardware, please buy a genuine [HackRF](https://greatscottgadgets.com/hackrf/) and [PortaPack](https://store.sharebrained.com/products/portapack-for-hackrf-one-kit).
|
||||
:heavy_check_mark: Another popular option is the clone of the [PortaPack H1](https://s.click.aliexpress.com/e/_Dkbqs2X).
|
||||
|
||||
## Where is the latest firmware?
|
||||
:warning: Be cautious , *ask* the seller about compatibility with the latest releases. Look out for the description of the item, if they provide the firmware files for an older version or they have custom setup instructions, this means it might be **NOT compatible**, for example:
|
||||
|
||||
The **latest (nightly) release** can be found [here](https://github.com/eried/portapack-mayhem/releases/).
|
||||

|
||||
|
||||
The current **stable release** is on the [](https://github.com/eried/portapack-mayhem/releases/latest) page. Follow the instructions you can find in the release description.
|
||||
:warning: If it looks **too different**, this might mean that they are using their own recipe, check the [different models](https://github.com/eried/portapack-mayhem/wiki/PortaPack-Versions) in our wiki. For example all the H3 and clones of that version use their own version of the firmware. They do not contribute the changes back and eventually you will be left with a device that nobody maintains:
|
||||
|
||||
## Is this the newest firmware for my PortaPack?
|
||||
Most probably: **YES**. *If you find new features somewhere else, please [suggest](https://github.com/eried/portapack-mayhem/issues/new/choose) them*.
|
||||

|
||||
|
||||
## Which one is actually the newest?
|
||||
There is a lot of confusion of which is the latest version because no old version used any actual "version number". Additionally, since the files were distributed on facebook groups, github issue links and similar temporal sources, then there was no central location for them.
|
||||
## Where is the latest version?
|
||||
|
||||
This fork (**Mayhem**) uses *major.minor.release* [semantic versioning](https://en.wikipedia.org/wiki/Software_versioning), so you can always compare your current version with the latest from [Releases](https://github.com/eried/portapack-mayhem/releases/latest).
|
||||
|
||||
## What about Havoc/GridRF/jamesshao8/jboone's?
|
||||
* jboone's PortaPack: the [vanilla](https://en.wikipedia.org/wiki/Vanilla_software) experience
|
||||
* Havoc: It was the most popular fork of jboone's PortaPack, currrently, it is not being maintained nor updated
|
||||
* jamesshao8: He keeps his own version of the fork, while not attached as a fork to anything. Latest functions do not follow the license and are not being published with source code, so keep this in mind
|
||||
* GridRF: They sell PortaPack clones with their own firmware based on a old version, which has no sourcecode available
|
||||
The current **stable release** is on the [](https://github.com/eried/portapack-mayhem/releases/latest) page. Follow the instructions you can find in the release description. The **latest (nightly) release** can be found [here](https://github.com/eried/portapack-mayhem/releases/).
|
||||
|
||||
## How can I collaborate
|
||||
You can write [documentation](https://github.com/eried/portapack-mayhem/wiki), fix bugs and [answer issues](https://github.com/eried/portapack-mayhem/issues) or add new functionality. Please check the following [guide](https://github.com/eried/portapack-mayhem/wiki/How-to-collaborate) with details.
|
||||
@@ -51,11 +43,15 @@ Consider that the hardware and firmware has been created and maintain by a [lot]
|
||||
|
||||
[](https://github.com/eried/portapack-mayhem/graphs/contributors)
|
||||
|
||||
To support the people behind the hardware, please buy a genuine [HackRF](https://greatscottgadgets.com/hackrf/) and [PortaPack](https://store.sharebrained.com/products/portapack-for-hackrf-one-kit).
|
||||
|
||||
As a last option, if you want to send money directly to me for getting more boards, antennas and such:
|
||||
|
||||
[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=CBPQA4HRRPJQ6&source=url)
|
||||
|
||||
## What if I really want to pay for something?
|
||||
## What if I really want something specific?
|
||||
If what you need can be relevant in general, you can [request a feature](https://github.com/eried/portapack-mayhem/issues/new?labels=enhancement&template=feature_request.md).
|
||||
|
||||
You can create a bounty and invite people to your own bounty. This will incentivize coders to work on a new feature, solving a bug or even writting documentation. Start a bounty by [creating](https://github.com/eried/portapack-mayhem/issues/new/choose) or [choosing](https://github.com/eried/portapack-mayhem/issues/) an existing issue. Then, go to [Bountysource](https://www.bountysource.com/) and post a bounty using the link to that specific [issue](https://www.bountysource.com/teams/portapack-mayhem/issues).
|
||||
|
||||
Promote your bounty over our Discord by clicking the chat badge on [top](#portapack-mayhem).
|
||||
@@ -64,6 +60,3 @@ Promote your bounty over our Discord by clicking the chat badge on [top](#portap
|
||||
First, check the [documentation](https://github.com/eried/portapack-mayhem/wiki). If you find a bug or you think the problem is related to the current repository, please open an [issue](https://github.com/eried/portapack-mayhem/issues/new/choose).
|
||||
|
||||
You can reach the [official community](https://www.facebook.com/groups/177623356165819) in Facebook, and our Discord by clicking the chat badge on [top](#portapack-mayhem).
|
||||
|
||||
## What if I find incongruencies, or grammatical errors in the text?
|
||||
If is on the [Wiki](https://github.com/eried/portapack-mayhem/wiki), you can modify it directly. If is on files of the repository, you can send corrections as [pull requests](https://github.com/eried/portapack-mayhem/wiki/How-to-collaborate#coding-new-stuff-or-fixing-bugs). As a last resource, open an [issue](https://github.com/eried/portapack-mayhem/issues/new/choose).
|
||||
|
||||
@@ -103,6 +103,7 @@ set(CSRC
|
||||
${PLATFORMSRC}
|
||||
${BOARDSRC}
|
||||
${FATFSSRC}
|
||||
firmware_info.c
|
||||
)
|
||||
|
||||
# C++ sources that can be compiled in ARM or THUMB mode depending on the global
|
||||
@@ -193,6 +194,7 @@ set(CPPSRC
|
||||
hw/debounce.cpp
|
||||
hw/encoder.cpp
|
||||
hw/max2837.cpp
|
||||
hw/max2839.cpp
|
||||
hw/max5864.cpp
|
||||
hw/rffc507x.cpp
|
||||
hw/rffc507x_spi.cpp
|
||||
|
||||
@@ -51,6 +51,13 @@ int app_settings::load(std::string application, AppSettings* settings) {
|
||||
settings->tx_amp=std::app_settings::read_long_long(file_content, "tx_amp=");
|
||||
settings->tx_frequency=std::app_settings::read_long_long(file_content, "tx_frequency=");
|
||||
settings->tx_gain=std::app_settings::read_long_long(file_content, "tx_gain=");
|
||||
settings->step=std::app_settings::read_long_long(file_content, "step=");
|
||||
settings->modulation=std::app_settings::read_long_long(file_content, "modulation=");
|
||||
settings->am_config_index=std::app_settings::read_long_long(file_content, "am_config_index=");
|
||||
settings->nbfm_config_index=std::app_settings::read_long_long(file_content, "nbfm_config_index=");
|
||||
settings->wfm_config_index=std::app_settings::read_long_long(file_content, "wfm_config_index=");
|
||||
settings->squelch=std::app_settings::read_long_long(file_content, "squelch=");
|
||||
|
||||
rc = SETTINGS_OK;
|
||||
}
|
||||
else rc = SETTINGS_UNABLE_TO_LOAD;
|
||||
@@ -61,7 +68,7 @@ int app_settings::load(std::string application, AppSettings* settings) {
|
||||
|
||||
int app_settings::save(std::string application, AppSettings* settings) {
|
||||
|
||||
if (portapack::persistent_memory::save_app_settings()) {
|
||||
if (portapack::persistent_memory::save_app_settings()) {
|
||||
file_path = folder+"/"+application+".ini";
|
||||
make_new_directory(folder);
|
||||
|
||||
@@ -79,6 +86,12 @@ int app_settings::save(std::string application, AppSettings* settings) {
|
||||
// Save other settings from struct
|
||||
settings_file.write_line("rx_frequency="+to_string_dec_uint(settings->rx_frequency));
|
||||
settings_file.write_line("tx_frequency="+to_string_dec_uint(settings->tx_frequency));
|
||||
settings_file.write_line("step="+to_string_dec_uint(settings->step));
|
||||
settings_file.write_line("modulation="+to_string_dec_uint(settings->modulation));
|
||||
settings_file.write_line("am_config_index="+to_string_dec_uint(settings->am_config_index));
|
||||
settings_file.write_line("nbfm_config_index="+to_string_dec_uint(settings->nbfm_config_index));
|
||||
settings_file.write_line("wfm_config_index="+to_string_dec_uint(settings->wfm_config_index));
|
||||
settings_file.write_line("squelch="+to_string_dec_uint(settings->squelch));
|
||||
|
||||
rc = SETTINGS_OK;
|
||||
}
|
||||
|
||||
@@ -59,6 +59,11 @@ public:
|
||||
uint32_t tx_frequency;
|
||||
uint8_t tx_gain;
|
||||
uint8_t vga;
|
||||
uint32_t step;
|
||||
uint8_t am_config_index;
|
||||
uint8_t nbfm_config_index;
|
||||
uint8_t wfm_config_index;
|
||||
uint8_t squelch;
|
||||
};
|
||||
|
||||
int load(std::string application, AppSettings* settings);
|
||||
|
||||
@@ -136,6 +136,7 @@ AnalogAudioView::AnalogAudioView(
|
||||
field_vga.set_value(app_settings.vga);
|
||||
receiver_model.set_rf_amp(app_settings.rx_amp);
|
||||
field_frequency.set_value(app_settings.rx_frequency);
|
||||
receiver_model.set_configuration_without_init(static_cast<ReceiverModel::Mode>(app_settings.modulation), app_settings.step, app_settings.am_config_index, app_settings.nbfm_config_index, app_settings.wfm_config_index, app_settings.squelch);
|
||||
}
|
||||
else field_frequency.set_value(receiver_model.tuning_frequency());
|
||||
|
||||
@@ -193,36 +194,45 @@ AnalogAudioView::AnalogAudioView(
|
||||
audio::output::start();
|
||||
|
||||
update_modulation(static_cast<ReceiverModel::Mode>(modulation));
|
||||
on_modulation_changed(static_cast<ReceiverModel::Mode>(modulation));
|
||||
on_modulation_changed(static_cast<ReceiverModel::Mode>(modulation));
|
||||
}
|
||||
|
||||
size_t AnalogAudioView::get_spec_bw_index() {
|
||||
return spec_bw_index;
|
||||
return spec_bw_index;
|
||||
}
|
||||
|
||||
void AnalogAudioView::set_spec_bw(size_t index, uint32_t bw) {
|
||||
spec_bw_index = index;
|
||||
spec_bw = bw;
|
||||
spec_bw_index = index;
|
||||
spec_bw = bw;
|
||||
|
||||
baseband::set_spectrum(bw, spec_trigger);
|
||||
receiver_model.set_sampling_rate(bw);
|
||||
receiver_model.set_baseband_bandwidth(bw/2);
|
||||
baseband::set_spectrum(bw, spec_trigger);
|
||||
receiver_model.set_sampling_rate(bw);
|
||||
receiver_model.set_baseband_bandwidth(bw/2);
|
||||
}
|
||||
|
||||
uint16_t AnalogAudioView::get_spec_trigger() {
|
||||
return spec_trigger;
|
||||
return spec_trigger;
|
||||
}
|
||||
|
||||
void AnalogAudioView::set_spec_trigger(uint16_t trigger) {
|
||||
spec_trigger = trigger;
|
||||
spec_trigger = trigger;
|
||||
|
||||
baseband::set_spectrum(spec_bw, spec_trigger);
|
||||
baseband::set_spectrum(spec_bw, spec_trigger);
|
||||
}
|
||||
|
||||
AnalogAudioView::~AnalogAudioView() {
|
||||
|
||||
// save app settings
|
||||
app_settings.rx_frequency = field_frequency.value();
|
||||
app_settings.lna = receiver_model.lna();
|
||||
app_settings.vga = receiver_model.vga();
|
||||
app_settings.rx_amp = receiver_model.rf_amp();
|
||||
app_settings.step = receiver_model.frequency_step();
|
||||
app_settings.modulation = (uint8_t)receiver_model.modulation();
|
||||
app_settings.am_config_index = receiver_model.am_configuration();
|
||||
app_settings.nbfm_config_index = receiver_model.nbfm_configuration();
|
||||
app_settings.wfm_config_index = receiver_model.wfm_configuration();
|
||||
app_settings.squelch = receiver_model.squelch_level();
|
||||
settings.save("rx_audio", &app_settings);
|
||||
|
||||
// TODO: Manipulating audio codec here, and in ui_receiver.cpp. Good to do
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace ui
|
||||
console.writeln("Brumi-2021,texasyojimbo");
|
||||
console.writeln("heurist1,intoxsick,ckuethe");
|
||||
console.writeln("notpike,jLynx,zigad");
|
||||
console.writeln("MichalLeonBorsuk");
|
||||
console.writeln("MichalLeonBorsuk,jimilinuxguy");
|
||||
console.writeln("");
|
||||
break;
|
||||
|
||||
|
||||
@@ -162,13 +162,16 @@ ADSBSpeedView::ADSBSpeedView(
|
||||
|
||||
add_children({
|
||||
&labels_speed,
|
||||
&labels_vert_rate,
|
||||
&compass,
|
||||
&field_angle,
|
||||
&field_speed
|
||||
&field_speed,
|
||||
&field_vert_rate
|
||||
});
|
||||
|
||||
field_angle.set_value(0);
|
||||
field_speed.set_value(400);
|
||||
field_vert_rate.set_value(0);
|
||||
|
||||
field_angle.on_change = [this](int32_t v) {
|
||||
compass.set_value(v);
|
||||
@@ -181,7 +184,7 @@ void ADSBSpeedView::collect_frames(const uint32_t ICAO_address, std::vector<ADSB
|
||||
ADSBFrame temp_frame;
|
||||
|
||||
encode_frame_velo(temp_frame, ICAO_address, field_speed.value(),
|
||||
field_angle.value(), 0); // TODO: v_rate
|
||||
field_angle.value(), field_vert_rate.value()); // Added v_rate , ft/min , (+) climb , (-) descend .
|
||||
|
||||
frame_list.emplace_back(temp_frame);
|
||||
}
|
||||
|
||||
@@ -96,6 +96,10 @@ private:
|
||||
{ { 1 * 8, 6 * 16 }, "Speed: kn Bearing: *", Color::light_grey() }
|
||||
};
|
||||
|
||||
Labels labels_vert_rate {
|
||||
{ { 1 * 8, 8 * 16 }, "Vert. rate: ft/min, (+/-)", Color::light_grey() }
|
||||
};
|
||||
|
||||
Compass compass {
|
||||
{ 21 * 8, 2 * 16 }
|
||||
};
|
||||
@@ -107,6 +111,10 @@ private:
|
||||
NumberField field_speed {
|
||||
{ 8 * 8, 6 * 16 }, 3, { 0, 999 }, 5, ' '
|
||||
};
|
||||
|
||||
NumberField field_vert_rate {
|
||||
{ 11 * 8, 8 * 16 }, 5, { -4096, 4096 }, 64, ' ' // Let's limit to +/-5k aprox , Ex. max safe descent vertical rate aprox -1000 ft/min on an instrument approach. , std step is 64
|
||||
};
|
||||
};
|
||||
|
||||
class ADSBSquawkView : public OptionTabView {
|
||||
|
||||
@@ -322,17 +322,19 @@ void DebugControlsView::focus() {
|
||||
/* DebugPeripheralsMenuView **********************************************/
|
||||
|
||||
DebugPeripheralsMenuView::DebugPeripheralsMenuView(NavigationView& nav) {
|
||||
const char * max283x = hackrf_r9 ? "MAX2839" : "MAX2837";
|
||||
const char * si5351x = hackrf_r9 ? "Si5351A" : "Si5351C";
|
||||
add_items({
|
||||
{ "RFFC5072", ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav](){ nav.push<RegistersView>(
|
||||
"RFFC5072", RegistersWidgetConfig { 31, 16 },
|
||||
[](const size_t register_number) { return radio::debug::first_if::register_read(register_number); }
|
||||
); } },
|
||||
{ "MAX2837", ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav](){ nav.push<RegistersView>(
|
||||
"MAX2837", RegistersWidgetConfig { 32, 10 },
|
||||
{ max283x, ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav, max283x](){ nav.push<RegistersView>(
|
||||
max283x, RegistersWidgetConfig { 32, 10 },
|
||||
[](const size_t register_number) { return radio::debug::second_if::register_read(register_number); }
|
||||
); } },
|
||||
{ "Si5351C", ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav](){ nav.push<RegistersView>(
|
||||
"Si5351C", RegistersWidgetConfig { 96, 8 },
|
||||
{ si5351x, ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav, si5351x](){ nav.push<RegistersView>(
|
||||
si5351x, RegistersWidgetConfig { 96, 8 },
|
||||
[](const size_t register_number) { return portapack::clock_generator.read_register(register_number); }
|
||||
); } },
|
||||
{ audio::debug::codec_name(), ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav](){ nav.push<RegistersView>(
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
#include "ui_navigation.hpp"
|
||||
|
||||
#include "rffc507x.hpp"
|
||||
#include "max2837.hpp"
|
||||
#include "portapack.hpp"
|
||||
|
||||
#include <functional>
|
||||
|
||||
@@ -156,6 +156,8 @@ void EncodersConfigView::generate_frame() {
|
||||
for (auto c : encoder_def->word_format) {
|
||||
if (c == 'S')
|
||||
frame_fragments += encoder_def->sync;
|
||||
else if (!c)
|
||||
break;
|
||||
else
|
||||
frame_fragments += encoder_def->bit_format[symfield_word.get_sym(i++)];
|
||||
}
|
||||
@@ -252,7 +254,8 @@ void EncodersView::start_tx(const bool scan) {
|
||||
scan_width = view_scan.field_length.value();
|
||||
samples_per_bit =
|
||||
((view_scan.bit_length_10.value() * 10 + view_scan.bit_length.value()) * OOK_SAMPLERATE) / 1000000UL;
|
||||
const uint32_t seq_len = ((1 << (scan_width - 1)) * 2) * ((uint64_t) samples_per_bit) / 2048UL;
|
||||
constexpr auto sym_len = 4;
|
||||
const uint32_t seq_len = ((1 << (scan_width - 1)) * 2) * ((uint64_t) samples_per_bit) * sym_len / 2048UL;
|
||||
progressbar.set_max(seq_len);
|
||||
repeat_min = seq_len;
|
||||
} else {
|
||||
|
||||
@@ -22,315 +22,340 @@
|
||||
|
||||
#include "ui_looking_glass_app.hpp"
|
||||
|
||||
|
||||
using namespace portapack;
|
||||
|
||||
namespace ui
|
||||
{
|
||||
|
||||
void GlassView::focus() {
|
||||
field_marker.focus();
|
||||
}
|
||||
|
||||
GlassView::~GlassView() {
|
||||
receiver_model.set_sampling_rate(3072000); // Just a hack to avoid hanging other apps
|
||||
receiver_model.disable();
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
void GlassView::on_lna_changed(int32_t v_db) {
|
||||
receiver_model.set_lna(v_db);
|
||||
}
|
||||
|
||||
void GlassView::on_vga_changed(int32_t v_db) {
|
||||
receiver_model.set_vga(v_db);
|
||||
}
|
||||
|
||||
void GlassView::add_spectrum_pixel(Color color)
|
||||
{
|
||||
spectrum_row[pixel_index++] = color;
|
||||
if (pixel_index == 240) //got an entire waterfall line
|
||||
{
|
||||
const auto draw_y = display.scroll(1); //Scroll 1 pixel down
|
||||
display.draw_pixels( {{0, draw_y}, {240, 1}}, spectrum_row); //new line at top
|
||||
pixel_index = 0; //Start New cascade line
|
||||
}
|
||||
}
|
||||
|
||||
//Apparently, the spectrum object returns an array of 256 bins
|
||||
//Each having the radio signal power for it's corresponding frequency slot
|
||||
void GlassView::on_channel_spectrum(const ChannelSpectrum &spectrum)
|
||||
{
|
||||
baseband::spectrum_streaming_stop();
|
||||
|
||||
// Convert bins of this spectrum slice into a representative max_power and when enough, into pixels
|
||||
// Spectrum.db has 256 bins. Center 12 bins are ignored (DC spike is blanked) Leftmost and rightmost 2 bins are ignored
|
||||
// All things said and done, we actually need 240 of those bins:
|
||||
for(uint8_t bin = 0; bin < 240; bin++)
|
||||
void GlassView::focus()
|
||||
{
|
||||
if (bin < 120) {
|
||||
if (spectrum.db[134 + bin] > max_power)
|
||||
max_power = spectrum.db[134 + bin];
|
||||
}
|
||||
else
|
||||
field_marker.focus();
|
||||
}
|
||||
|
||||
GlassView::~GlassView()
|
||||
{
|
||||
receiver_model.set_sampling_rate(3072000); // Just a hack to avoid hanging other apps
|
||||
receiver_model.disable();
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
void GlassView::on_lna_changed(int32_t v_db)
|
||||
{
|
||||
receiver_model.set_lna(v_db);
|
||||
}
|
||||
|
||||
void GlassView::on_vga_changed(int32_t v_db)
|
||||
{
|
||||
receiver_model.set_vga(v_db);
|
||||
}
|
||||
|
||||
void GlassView::add_spectrum_pixel(Color color)
|
||||
{
|
||||
spectrum_row[pixel_index++] = color;
|
||||
if (pixel_index == 240) // got an entire waterfall line
|
||||
{
|
||||
if (spectrum.db[bin - 118] > max_power)
|
||||
max_power = spectrum.db[bin - 118];
|
||||
display.draw_pixels({{0, display.scroll(1)}, {240, 1}}, spectrum_row); // new line at top, one less var, speedier
|
||||
pixel_index = 0; // Start New cascade line
|
||||
}
|
||||
}
|
||||
|
||||
bins_Hz_size += each_bin_size; //add this bin Hz count into the "pixel fulfilled bag of Hz"
|
||||
// Apparently, the spectrum object returns an array of 256 bins
|
||||
// Each having the radio signal power for it's corresponding frequency slot
|
||||
void GlassView::on_channel_spectrum(const ChannelSpectrum &spectrum)
|
||||
{
|
||||
baseband::spectrum_streaming_stop();
|
||||
|
||||
if (bins_Hz_size >= marker_pixel_step) //new pixel fullfilled
|
||||
// Convert bins of this spectrum slice into a representative max_power and when enough, into pixels
|
||||
// Spectrum.db has 256 bins. Center 12 bins are ignored (DC spike is blanked) Leftmost and rightmost 2 bins are ignored
|
||||
// All things said and done, we actually need 240 of those bins:
|
||||
for (uint8_t bin = 0; bin < 240; bin++)
|
||||
{
|
||||
if (min_color_power < max_power)
|
||||
add_spectrum_pixel(spectrum_rgb3_lut[max_power]); //Pixel will represent max_power
|
||||
else
|
||||
add_spectrum_pixel(0); //Filtered out, show black
|
||||
|
||||
max_power = 0;
|
||||
|
||||
if (!pixel_index) //Received indication that a waterfall line has been completed
|
||||
if (bin < 120)
|
||||
{
|
||||
bins_Hz_size = 0; //Since this is an entire pixel line, we don't carry "Pixels into next bin"
|
||||
break;
|
||||
} else {
|
||||
bins_Hz_size -= marker_pixel_step; //reset bins size, but carrying the eventual excess Hz into next pixel
|
||||
if (spectrum.db[134 + bin] > max_power) // 134
|
||||
max_power = spectrum.db[134 + bin];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (spectrum.db[bin - 118] > max_power) // 118
|
||||
max_power = spectrum.db[bin - 118];
|
||||
}
|
||||
|
||||
bins_Hz_size += each_bin_size; // add this bin Hz count into the "pixel fulfilled bag of Hz"
|
||||
|
||||
if (bins_Hz_size >= marker_pixel_step) // new pixel fullfilled
|
||||
{
|
||||
if (min_color_power < max_power)
|
||||
add_spectrum_pixel(spectrum_rgb3_lut[max_power]); // Pixel will represent max_power
|
||||
else
|
||||
add_spectrum_pixel(0); // Filtered out, show black
|
||||
|
||||
max_power = 0;
|
||||
|
||||
if (!pixel_index) // Received indication that a waterfall line has been completed
|
||||
{
|
||||
bins_Hz_size = 0; // Since this is an entire pixel line, we don't carry "Pixels into next bin"
|
||||
f_center = f_center_ini; // Start a new sweep
|
||||
radio::set_tuning_frequency(f_center); // tune rx for this new slice directly, faster than using persistent memory saving
|
||||
chThdSleepMilliseconds(10);
|
||||
baseband::spectrum_streaming_start(); // Do the RX
|
||||
return;
|
||||
}
|
||||
bins_Hz_size -= marker_pixel_step; // reset bins size, but carrying the eventual excess Hz into next pixel
|
||||
}
|
||||
}
|
||||
|
||||
f_center += LOOKING_GLASS_SLICE_WIDTH; // Move into the next bandwidth slice NOTE: spectrum.sampling_rate = LOOKING_GLASS_SLICE_WIDTH
|
||||
radio::set_tuning_frequency(f_center); // tune rx for this new slice directly, faster than using persistent memory saving
|
||||
chThdSleepMilliseconds(5);
|
||||
|
||||
// receiver_model.set_tuning_frequency(f_center); //tune rx for this slice
|
||||
baseband::spectrum_streaming_start(); // Do the RX
|
||||
}
|
||||
|
||||
if (pixel_index)
|
||||
f_center += LOOKING_GLASS_SLICE_WIDTH; //Move into the next bandwidth slice NOTE: spectrum.sampling_rate = LOOKING_GLASS_SLICE_WIDTH
|
||||
else
|
||||
f_center = f_center_ini; //Start a new sweep
|
||||
|
||||
receiver_model.set_tuning_frequency(f_center); //tune rx for this slice
|
||||
baseband::spectrum_streaming_start(); //Do the RX
|
||||
}
|
||||
|
||||
void GlassView::on_hide()
|
||||
{
|
||||
baseband::spectrum_streaming_stop();
|
||||
display.scroll_disable();
|
||||
}
|
||||
|
||||
void GlassView::on_show()
|
||||
{
|
||||
display.scroll_set_area( 109, 319); //Restart scroll on the correct coordinates
|
||||
baseband::spectrum_streaming_start();
|
||||
}
|
||||
|
||||
void GlassView::on_range_changed()
|
||||
{
|
||||
f_min = field_frequency_min.value();
|
||||
f_max = field_frequency_max.value();
|
||||
search_span = f_max - f_min;
|
||||
|
||||
field_marker.set_range(f_min, f_max); //Move the marker between range
|
||||
field_marker.set_value(f_min + (search_span / 2)); //Put MARKER AT MIDDLE RANGE
|
||||
text_range.set(to_string_dec_uint(search_span));
|
||||
|
||||
f_min = (f_min)*MHZ_DIV; //Transpose into full frequency realm
|
||||
f_max = (f_max)*MHZ_DIV;
|
||||
search_span = search_span * MHZ_DIV;
|
||||
|
||||
marker_pixel_step = search_span / 240; //Each pixel value in Hz
|
||||
text_marker_pm.set(to_string_dec_uint((marker_pixel_step / X2_MHZ_DIV) + 1)); // Give idea of +/- marker precision
|
||||
|
||||
int32_t marker_step = marker_pixel_step / MHZ_DIV;
|
||||
if (!marker_step)
|
||||
field_marker.set_step(1); //in case selected range is less than 240 (pixels)
|
||||
else
|
||||
field_marker.set_step(marker_step); //step needs to be a pixel wide.
|
||||
|
||||
f_center_ini = f_min + (LOOKING_GLASS_SLICE_WIDTH / 2); //Initial center frequency for sweep
|
||||
f_center_ini += LOOKING_GLASS_SLICE_WIDTH; //euquiq: Why do I need to move the center ???!!! (shift needed for marker accuracy)
|
||||
|
||||
PlotMarker(field_marker.value()); //Refresh marker on screen
|
||||
|
||||
f_center = f_center_ini; //Reset sweep into first slice
|
||||
pixel_index = 0; //reset pixel counter
|
||||
max_power = 0;
|
||||
bins_Hz_size = 0; //reset amount of Hz filled up by pixels
|
||||
|
||||
baseband::set_spectrum(LOOKING_GLASS_SLICE_WIDTH, field_trigger.value());
|
||||
receiver_model.set_tuning_frequency(f_center_ini); //tune rx for this slice
|
||||
}
|
||||
|
||||
void GlassView::PlotMarker(rf::Frequency fpos)
|
||||
{
|
||||
int64_t pos = fpos * MHZ_DIV;
|
||||
pos -= f_min;
|
||||
pos = pos / marker_pixel_step; //Real pixel
|
||||
|
||||
portapack::display.fill_rectangle({0, 100, 240, 8}, Color::black()); //Clear old marker and whole marker rectangle btw
|
||||
portapack::display.fill_rectangle({(int16_t)pos - 2, 100, 5, 3}, Color::red()); //Red marker middle
|
||||
portapack::display.fill_rectangle({(int16_t)pos - 1, 103, 3, 3}, Color::red()); //Red marker middle
|
||||
portapack::display.fill_rectangle({(int16_t)pos, 106, 1, 2}, Color::red()); //Red marker middle
|
||||
}
|
||||
|
||||
GlassView::GlassView(
|
||||
NavigationView &nav) : nav_(nav)
|
||||
{
|
||||
baseband::run_image(portapack::spi_flash::image_tag_wideband_spectrum);
|
||||
|
||||
add_children({&labels,
|
||||
&field_frequency_min,
|
||||
&field_frequency_max,
|
||||
&field_lna,
|
||||
&field_vga,
|
||||
&text_range,
|
||||
&filter_config,
|
||||
&field_rf_amp,
|
||||
&range_presets,
|
||||
&field_marker,
|
||||
&text_marker_pm,
|
||||
&field_trigger
|
||||
});
|
||||
|
||||
load_Presets(); //Load available presets from TXT files (or default)
|
||||
|
||||
field_frequency_min.set_value(presets_db[0].min); //Defaults to first preset
|
||||
field_frequency_min.on_change = [this](int32_t v) {
|
||||
if (v >= field_frequency_max.value())
|
||||
field_frequency_max.set_value(v + 240);
|
||||
this->on_range_changed();
|
||||
};
|
||||
|
||||
field_frequency_max.set_value(presets_db[0].max); //Defaults to first preset
|
||||
field_frequency_max.on_change = [this](int32_t v) {
|
||||
if (v <= field_frequency_min.value())
|
||||
field_frequency_min.set_value(v - 240);
|
||||
this->on_range_changed();
|
||||
};
|
||||
|
||||
field_lna.set_value(receiver_model.lna());
|
||||
field_lna.on_change = [this](int32_t v) {
|
||||
this->on_lna_changed(v);
|
||||
};
|
||||
|
||||
field_vga.set_value(receiver_model.vga());
|
||||
field_vga.on_change = [this](int32_t v_db) {
|
||||
this->on_vga_changed(v_db);
|
||||
};
|
||||
|
||||
filter_config.set_selected_index(0);
|
||||
filter_config.on_change = [this](size_t n, OptionsField::value_t v) {
|
||||
(void)n;
|
||||
min_color_power = v;
|
||||
};
|
||||
|
||||
range_presets.on_change = [this](size_t n, OptionsField::value_t v) {
|
||||
(void)n;
|
||||
field_frequency_min.set_value(presets_db[v].min,false);
|
||||
field_frequency_max.set_value(presets_db[v].max,false);
|
||||
this->on_range_changed();
|
||||
};
|
||||
|
||||
field_marker.on_change = [this](int32_t v) {
|
||||
PlotMarker(v); //Refresh marker on screen
|
||||
};
|
||||
|
||||
field_marker.on_select = [this](NumberField&) {
|
||||
f_center = field_marker.value();
|
||||
f_center = f_center * MHZ_DIV;
|
||||
receiver_model.set_tuning_frequency(f_center); //Center tune rx in marker freq.
|
||||
receiver_model.set_frequency_step(MHZ_DIV); // Preset a 1 MHz frequency step into RX -> AUDIO
|
||||
nav_.pop();
|
||||
nav_.push<AnalogAudioView>(); //Jump into audio view
|
||||
};
|
||||
|
||||
field_trigger.set_value(32); //Defaults to 32, as normal triggering resolution
|
||||
field_trigger.on_change = [this](int32_t v) {
|
||||
baseband::set_spectrum(LOOKING_GLASS_SLICE_WIDTH, v);
|
||||
};
|
||||
|
||||
display.scroll_set_area( 109, 319);
|
||||
baseband::set_spectrum(LOOKING_GLASS_SLICE_WIDTH, field_trigger.value()); //trigger:
|
||||
// Discord User jteich: WidebandSpectrum::on_message to set the trigger value. In WidebandSpectrum::execute ,
|
||||
// it keeps adding the output of the fft to the buffer until "trigger" number of calls are made,
|
||||
//at which time it pushes the buffer up with channel_spectrum.feed
|
||||
|
||||
on_range_changed();
|
||||
|
||||
receiver_model.set_modulation(ReceiverModel::Mode::SpectrumAnalysis);
|
||||
receiver_model.set_sampling_rate(LOOKING_GLASS_SLICE_WIDTH); //20mhz
|
||||
receiver_model.set_baseband_bandwidth(LOOKING_GLASS_SLICE_WIDTH); // possible values: 1.75/2.5/3.5/5/5.5/6/7/8/9/10/12/14/15/20/24/28MHz
|
||||
receiver_model.set_squelch_level(0);
|
||||
receiver_model.enable();
|
||||
}
|
||||
|
||||
void GlassView::load_Presets() {
|
||||
File presets_file; //LOAD /WHIPCALC/ANTENNAS.TXT from microSD
|
||||
auto result = presets_file.open("LOOKINGGLASS/PRESETS.TXT");
|
||||
presets_db.clear(); //Start with fresh db
|
||||
if (result.is_valid()) {
|
||||
presets_Default(); //There is no txt, store a default range
|
||||
} else {
|
||||
|
||||
std::string line; //There is a txt file
|
||||
char one_char[1]; //Read it char by char
|
||||
for (size_t pointer=0; pointer < presets_file.size();pointer++) {
|
||||
presets_file.seek(pointer);
|
||||
presets_file.read(one_char, 1);
|
||||
if ((int)one_char[0] > 31) { //ascii space upwards
|
||||
line += one_char[0]; //Add it to the textline
|
||||
}
|
||||
else if (one_char[0] == '\n') { //New Line
|
||||
txtline_process(line); //make sense of this textline
|
||||
line.clear(); //Ready for next textline
|
||||
}
|
||||
}
|
||||
if (line.length() > 0) txtline_process(line); //Last line had no newline at end ?
|
||||
if (!presets_db.size()) presets_Default(); //no antenna on txt, use default
|
||||
void GlassView::on_hide()
|
||||
{
|
||||
baseband::spectrum_streaming_stop();
|
||||
display.scroll_disable();
|
||||
}
|
||||
|
||||
populate_Presets();
|
||||
}
|
||||
void GlassView::on_show()
|
||||
{
|
||||
display.scroll_set_area(109, 319); // Restart scroll on the correct coordinates
|
||||
baseband::spectrum_streaming_start();
|
||||
}
|
||||
|
||||
void GlassView::txtline_process(std::string& line)
|
||||
{
|
||||
if (line.find("#") != std::string::npos) return; //Line is just a comment
|
||||
void GlassView::on_range_changed()
|
||||
{
|
||||
f_min = field_frequency_min.value();
|
||||
f_max = field_frequency_max.value();
|
||||
search_span = f_max - f_min;
|
||||
|
||||
size_t comma = line.find(","); //Get first comma position
|
||||
if (comma == std::string::npos) return; //No comma at all
|
||||
field_marker.set_range(f_min, f_max); // Move the marker between range
|
||||
field_marker.set_value(f_min + (search_span / 2)); // Put MARKER AT MIDDLE RANGE
|
||||
text_range.set(to_string_dec_uint(search_span));
|
||||
|
||||
size_t previous = 0;
|
||||
preset_entry new_preset;
|
||||
f_min = (f_min)*MHZ_DIV; // Transpose into full frequency realm
|
||||
f_max = (f_max)*MHZ_DIV;
|
||||
search_span = search_span * MHZ_DIV;
|
||||
|
||||
new_preset.min = std::stoi(line.substr(0,comma));
|
||||
if (!new_preset.min) return; //No frequency!
|
||||
|
||||
previous = comma + 1;
|
||||
comma = line.find(",",previous); //Search for next delimiter
|
||||
if (comma == std::string::npos) return; //No comma at all
|
||||
marker_pixel_step = search_span / 240; // Each pixel value in Hz
|
||||
text_marker_pm.set(to_string_dec_uint((marker_pixel_step / X2_MHZ_DIV) + 1)); // Give idea of +/- marker precision
|
||||
|
||||
new_preset.max = std::stoi(line.substr(previous,comma - previous));
|
||||
if (!new_preset.max) return; //No frequency!
|
||||
int32_t marker_step = marker_pixel_step / MHZ_DIV;
|
||||
if (!marker_step)
|
||||
field_marker.set_step(1); // in case selected range is less than 240 (pixels)
|
||||
else
|
||||
field_marker.set_step(marker_step); // step needs to be a pixel wide.
|
||||
|
||||
new_preset.label = line.substr(comma + 1);
|
||||
if (new_preset.label.size() == 0) return; //No label ?
|
||||
|
||||
presets_db.push_back(new_preset); //Add this preset.
|
||||
}
|
||||
f_center_ini = f_min + (LOOKING_GLASS_SLICE_WIDTH / 2); // Initial center frequency for sweep
|
||||
|
||||
void GlassView::populate_Presets()
|
||||
{
|
||||
using option_t = std::pair<std::string, int32_t>;
|
||||
using options_t = std::vector<option_t>;
|
||||
options_t entries;
|
||||
PlotMarker(field_marker.value()); // Refresh marker on screen
|
||||
|
||||
for (preset_entry preset : presets_db)
|
||||
{ //go thru all available presets
|
||||
entries.emplace_back(preset.label,entries.size());
|
||||
f_center = f_center_ini; // Reset sweep into first slice
|
||||
pixel_index = 0; // reset pixel counter
|
||||
max_power = 0;
|
||||
bins_Hz_size = 0; // reset amount of Hz filled up by pixels
|
||||
|
||||
baseband::set_spectrum(LOOKING_GLASS_SLICE_WIDTH, field_trigger.value());
|
||||
receiver_model.set_tuning_frequency(f_center_ini); // tune rx for this slice
|
||||
}
|
||||
|
||||
void GlassView::PlotMarker(rf::Frequency pos)
|
||||
{
|
||||
pos = pos * MHZ_DIV;
|
||||
pos -= f_min;
|
||||
pos = pos / marker_pixel_step; // Real pixel
|
||||
|
||||
portapack::display.fill_rectangle({0, 100, 240, 8}, Color::black()); // Clear old marker and whole marker rectangle btw
|
||||
portapack::display.fill_rectangle({(int)pos - 2, 100, 5, 3}, Color::red()); // Red marker top
|
||||
portapack::display.fill_rectangle({(int)pos - 1, 103, 3, 3}, Color::red()); // Red marker middle
|
||||
portapack::display.fill_rectangle({(int)pos, 106, 1, 2}, Color::red()); // Red marker bottom
|
||||
}
|
||||
|
||||
GlassView::GlassView(
|
||||
NavigationView &nav) : nav_(nav)
|
||||
{
|
||||
baseband::run_image(portapack::spi_flash::image_tag_wideband_spectrum);
|
||||
|
||||
add_children({&labels,
|
||||
&field_frequency_min,
|
||||
&field_frequency_max,
|
||||
&field_lna,
|
||||
&field_vga,
|
||||
&text_range,
|
||||
&filter_config,
|
||||
&field_rf_amp,
|
||||
&range_presets,
|
||||
&field_marker,
|
||||
&text_marker_pm,
|
||||
&field_trigger});
|
||||
|
||||
load_Presets(); // Load available presets from TXT files (or default)
|
||||
|
||||
field_frequency_min.set_value(presets_db[0].min); // Defaults to first preset
|
||||
field_frequency_min.on_change = [this](int32_t v)
|
||||
{
|
||||
if (v >= field_frequency_max.value())
|
||||
field_frequency_max.set_value(v + 240);
|
||||
this->on_range_changed();
|
||||
};
|
||||
|
||||
field_frequency_max.set_value(presets_db[0].max); // Defaults to first preset
|
||||
field_frequency_max.on_change = [this](int32_t v)
|
||||
{
|
||||
if (v <= field_frequency_min.value())
|
||||
field_frequency_min.set_value(v - 240);
|
||||
this->on_range_changed();
|
||||
};
|
||||
|
||||
field_lna.set_value(receiver_model.lna());
|
||||
field_lna.on_change = [this](int32_t v)
|
||||
{
|
||||
this->on_lna_changed(v);
|
||||
};
|
||||
|
||||
field_vga.set_value(receiver_model.vga());
|
||||
field_vga.on_change = [this](int32_t v_db)
|
||||
{
|
||||
this->on_vga_changed(v_db);
|
||||
};
|
||||
|
||||
filter_config.set_selected_index(0);
|
||||
filter_config.on_change = [this](size_t n, OptionsField::value_t v)
|
||||
{
|
||||
(void)n;
|
||||
min_color_power = v;
|
||||
};
|
||||
|
||||
range_presets.on_change = [this](size_t n, OptionsField::value_t v)
|
||||
{
|
||||
(void)n;
|
||||
field_frequency_min.set_value(presets_db[v].min, false);
|
||||
field_frequency_max.set_value(presets_db[v].max, false);
|
||||
this->on_range_changed();
|
||||
};
|
||||
|
||||
field_marker.on_change = [this](int32_t v)
|
||||
{
|
||||
PlotMarker(v); // Refresh marker on screen
|
||||
};
|
||||
|
||||
field_marker.on_select = [this](NumberField &)
|
||||
{
|
||||
f_center = field_marker.value();
|
||||
f_center = f_center * MHZ_DIV;
|
||||
receiver_model.set_tuning_frequency(f_center); // Center tune rx in marker freq.
|
||||
receiver_model.set_frequency_step(MHZ_DIV); // Preset a 1 MHz frequency step into RX -> AUDIO
|
||||
nav_.pop();
|
||||
nav_.push<AnalogAudioView>(); // Jump into audio view
|
||||
};
|
||||
|
||||
field_trigger.set_value(32); // Defaults to 32, as normal triggering resolution
|
||||
field_trigger.on_change = [this](int32_t v)
|
||||
{
|
||||
baseband::set_spectrum(LOOKING_GLASS_SLICE_WIDTH, v);
|
||||
};
|
||||
|
||||
display.scroll_set_area(109, 319);
|
||||
baseband::set_spectrum(LOOKING_GLASS_SLICE_WIDTH, field_trigger.value()); // trigger:
|
||||
// Discord User jteich: WidebandSpectrum::on_message to set the trigger value. In WidebandSpectrum::execute ,
|
||||
// it keeps adding the output of the fft to the buffer until "trigger" number of calls are made,
|
||||
// at which time it pushes the buffer up with channel_spectrum.feed
|
||||
|
||||
on_range_changed();
|
||||
|
||||
receiver_model.set_modulation(ReceiverModel::Mode::SpectrumAnalysis);
|
||||
receiver_model.set_sampling_rate(LOOKING_GLASS_SLICE_WIDTH); // 20mhz
|
||||
receiver_model.set_baseband_bandwidth(LOOKING_GLASS_SLICE_WIDTH); // possible values: 1.75/2.5/3.5/5/5.5/6/7/8/9/10/12/14/15/20/24/28MHz
|
||||
receiver_model.set_squelch_level(0);
|
||||
receiver_model.enable();
|
||||
}
|
||||
|
||||
void GlassView::load_Presets()
|
||||
{
|
||||
File presets_file; // LOAD /WHIPCALC/ANTENNAS.TXT from microSD
|
||||
auto result = presets_file.open("LOOKINGGLASS/PRESETS.TXT");
|
||||
presets_db.clear(); // Start with fresh db
|
||||
if (result.is_valid())
|
||||
{
|
||||
presets_Default(); // There is no txt, store a default range
|
||||
}
|
||||
range_presets.set_options(entries);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string line; // There is a txt file
|
||||
char one_char[1]; // Read it char by char
|
||||
for (size_t pointer = 0; pointer < presets_file.size(); pointer++)
|
||||
{
|
||||
presets_file.seek(pointer);
|
||||
presets_file.read(one_char, 1);
|
||||
if ((int)one_char[0] > 31)
|
||||
{ // ascii space upwards
|
||||
line += one_char[0]; // Add it to the textline
|
||||
}
|
||||
else if (one_char[0] == '\n')
|
||||
{ // New Line
|
||||
txtline_process(line); // make sense of this textline
|
||||
line.clear(); // Ready for next textline
|
||||
}
|
||||
}
|
||||
if (line.length() > 0)
|
||||
txtline_process(line); // Last line had no newline at end ?
|
||||
if (!presets_db.size())
|
||||
presets_Default(); // no antenna on txt, use default
|
||||
}
|
||||
populate_Presets();
|
||||
}
|
||||
|
||||
void GlassView::presets_Default()
|
||||
{
|
||||
void GlassView::txtline_process(std::string &line)
|
||||
{
|
||||
if (line.find("#") != std::string::npos)
|
||||
return; // Line is just a comment
|
||||
|
||||
size_t comma = line.find(","); // Get first comma position
|
||||
if (comma == std::string::npos)
|
||||
return; // No comma at all
|
||||
|
||||
size_t previous = 0;
|
||||
preset_entry new_preset;
|
||||
|
||||
new_preset.min = std::stoi(line.substr(0, comma));
|
||||
if (!new_preset.min)
|
||||
return; // No frequency!
|
||||
|
||||
previous = comma + 1;
|
||||
comma = line.find(",", previous); // Search for next delimiter
|
||||
if (comma == std::string::npos)
|
||||
return; // No comma at all
|
||||
|
||||
new_preset.max = std::stoi(line.substr(previous, comma - previous));
|
||||
if (!new_preset.max)
|
||||
return; // No frequency!
|
||||
|
||||
new_preset.label = line.substr(comma + 1);
|
||||
if (new_preset.label.size() == 0)
|
||||
return; // No label ?
|
||||
|
||||
presets_db.push_back(new_preset); // Add this preset.
|
||||
}
|
||||
|
||||
void GlassView::populate_Presets()
|
||||
{
|
||||
using option_t = std::pair<std::string, int32_t>;
|
||||
using options_t = std::vector<option_t>;
|
||||
options_t entries;
|
||||
|
||||
for (preset_entry preset : presets_db)
|
||||
{ // go thru all available presets
|
||||
entries.emplace_back(preset.label, entries.size());
|
||||
}
|
||||
range_presets.set_options(entries);
|
||||
}
|
||||
|
||||
void GlassView::presets_Default()
|
||||
{
|
||||
presets_db.clear();
|
||||
presets_db.push_back({2320, 2560, "DEFAULT WIFI 2.4GHz"});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -57,13 +57,13 @@ static msg_t ookthread_fn(void * arg) {
|
||||
v = (symbol < 2) ? 1 : 0; // TX on for dot or dash, off for pause
|
||||
delay = morse_symbols[symbol];
|
||||
|
||||
gpio_tx.write(v);
|
||||
gpio_og_tx.write(v);
|
||||
arg_c->on_tx_progress(i, false);
|
||||
|
||||
chThdSleepMilliseconds(delay * arg_c->time_unit_ms);
|
||||
}
|
||||
|
||||
gpio_tx.write(0); // Ensure TX is off
|
||||
gpio_og_tx.write(0); // Ensure TX is off
|
||||
arg_c->on_tx_progress(0, true);
|
||||
chThdExit(0);
|
||||
|
||||
|
||||
@@ -31,13 +31,23 @@ ScannerThread::ScannerThread(
|
||||
std::vector<rf::Frequency> frequency_list
|
||||
) : frequency_list_ { std::move(frequency_list) }
|
||||
{
|
||||
thread = chThdCreateFromHeap(NULL, 1024, NORMALPRIO + 10, ScannerThread::static_fn, this);
|
||||
create_thread();
|
||||
}
|
||||
|
||||
ScannerThread::ScannerThread(const jammer::jammer_range_t& frequency_range, size_t def_step) : frequency_range_(frequency_range), def_step_(def_step)
|
||||
{
|
||||
create_thread();
|
||||
}
|
||||
|
||||
ScannerThread::~ScannerThread() {
|
||||
stop();
|
||||
}
|
||||
|
||||
void ScannerThread::create_thread()
|
||||
{
|
||||
thread = chThdCreateFromHeap(NULL, 1024, NORMALPRIO + 10, ScannerThread::static_fn, this);
|
||||
}
|
||||
|
||||
void ScannerThread::stop() {
|
||||
if( thread ) {
|
||||
chThdTerminate(thread);
|
||||
@@ -102,12 +112,13 @@ void ScannerThread::run() {
|
||||
else
|
||||
restart_scan=false; //Effectively skipping first retuning, giving system time
|
||||
}
|
||||
message.freq = frequency_list_[frequency_index];
|
||||
message.range = frequency_index; //Inform freq (for coloring purposes also!)
|
||||
EventDispatcher::send_message(message);
|
||||
}
|
||||
else { //NOT scanning
|
||||
if (_freq_del != 0) { //There is a frequency to delete
|
||||
for (uint16_t i = 0; i < frequency_list_.size(); i++) { //Search for the freq to delete
|
||||
for (uint32_t i = 0; i < frequency_list_.size(); i++) { //Search for the freq to delete
|
||||
if (frequency_list_[i] == _freq_del)
|
||||
{ //found: Erase it
|
||||
frequency_list_.erase(frequency_list_.begin() + i);
|
||||
@@ -126,16 +137,58 @@ void ScannerThread::run() {
|
||||
}
|
||||
chThdSleepMilliseconds(50); //Needed to (eventually) stabilize the receiver into new freq
|
||||
}
|
||||
}else if(def_step_ > 0) // manual mode
|
||||
{
|
||||
RetuneMessage message { };
|
||||
int64_t frequency_index = 0;
|
||||
int64_t size = (frequency_range_.max - frequency_range_.min) / def_step_;
|
||||
bool restart_scan = false; //Flag whenever scanning is restarting after a pause
|
||||
while( !chThdShouldTerminate() ) {
|
||||
if (_scanning) { //Scanning
|
||||
if (_freq_lock == 0) { //normal scanning (not performing freq_lock)
|
||||
if (!restart_scan) { //looping at full speed
|
||||
if (_fwd) { //forward
|
||||
frequency_index++;
|
||||
if (frequency_index >= size)
|
||||
frequency_index = 0;
|
||||
|
||||
} else { //reverse
|
||||
if (frequency_index < 1)
|
||||
frequency_index = size;
|
||||
frequency_index--;
|
||||
}
|
||||
receiver_model.set_tuning_frequency(frequency_range_.min + frequency_index * def_step_); // Retune
|
||||
}
|
||||
else
|
||||
restart_scan=false; //Effectively skipping first retuning, giving system time
|
||||
}
|
||||
message.freq = frequency_range_.min + frequency_index * def_step_;
|
||||
message.range = 0; //Inform freq (for coloring purposes also!)
|
||||
EventDispatcher::send_message(message);
|
||||
}
|
||||
else { //NOT scanning
|
||||
restart_scan=true; //Flag the need for skipping a cycle when restarting scan
|
||||
}
|
||||
chThdSleepMilliseconds(50); //Needed to (eventually) stabilize the receiver into new freq
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScannerView::handle_retune(uint32_t i) {
|
||||
void ScannerView::handle_retune(int64_t freq, uint32_t freq_idx) {
|
||||
|
||||
current_index = freq_idx; //since it is an ongoing scan, this is a new index
|
||||
current_frequency = freq;
|
||||
|
||||
switch (scan_thread->is_freq_lock())
|
||||
{
|
||||
case 0: //NO FREQ LOCK, ONGOING STANDARD SCANNING
|
||||
text_cycle.set( to_string_dec_uint(i + 1,3) );
|
||||
current_index = i; //since it is an ongoing scan, this is a new index
|
||||
if (description_list[current_index].size() > 0) desc_cycle.set( description_list[current_index] ); //Show new description
|
||||
if (frequency_list.size() > 0) {
|
||||
text_cycle.set( to_string_dec_uint(freq_idx + 1,3) );
|
||||
}
|
||||
|
||||
if (freq_idx < description_list.size() && description_list[freq_idx].size() > 0) {
|
||||
desc_cycle.set( description_list[freq_idx] ); //Show new description
|
||||
}
|
||||
break;
|
||||
case 1: //STARTING LOCK FREQ
|
||||
big_display.set_style(&style_yellow);
|
||||
@@ -146,7 +199,7 @@ void ScannerView::handle_retune(uint32_t i) {
|
||||
default: //freq lock is checking the signal, do not update display
|
||||
return;
|
||||
}
|
||||
big_display.set(frequency_list[current_index]); //UPDATE the big Freq after 0, 1 or MAX_FREQ_LOCK (at least, for color synching)
|
||||
big_display.set(freq); //UPDATE the big Freq after 0, 1 or MAX_FREQ_LOCK (at least, for color synching)
|
||||
}
|
||||
|
||||
void ScannerView::focus() {
|
||||
@@ -154,6 +207,8 @@ void ScannerView::focus() {
|
||||
}
|
||||
|
||||
ScannerView::~ScannerView() {
|
||||
// make sure to stop the thread before shutting down the receiver
|
||||
scan_thread.reset();
|
||||
audio::output::stop();
|
||||
receiver_model.disable();
|
||||
baseband::shutdown();
|
||||
@@ -297,23 +352,15 @@ ScannerView::ScannerView(
|
||||
description_list.clear();
|
||||
def_step = step_mode.selected_index_value(); //Use def_step from manual selector
|
||||
|
||||
description_list.push_back(
|
||||
"M" + to_string_short_freq(frequency_range.min) + ">"
|
||||
+ to_string_short_freq(frequency_range.max) + "S"
|
||||
+ to_string_short_freq(def_step).erase(0,1) //euquiq: lame kludge to reduce spacing in step freq
|
||||
);
|
||||
|
||||
rf::Frequency frequency = frequency_range.min;
|
||||
while (frequency_list.size() < FREQMAN_MAX_PER_FILE && frequency <= frequency_range.max) { //add manual range
|
||||
frequency_list.push_back(frequency);
|
||||
description_list.push_back(""); //If empty, will keep showing the last description
|
||||
frequency+=def_step;
|
||||
}
|
||||
show_max();
|
||||
desc_cycle.set("");
|
||||
text_max.set("");
|
||||
text_cycle.set("");
|
||||
|
||||
if ( userpause ) //If user-paused, resume
|
||||
user_resume();
|
||||
big_display.set_style(&style_grey); //Back to grey color
|
||||
start_scan_thread(); //RESTART SCANNER THREAD
|
||||
start_scan_thread(frequency_range, def_step); //RESTART SCANNER THREAD
|
||||
}
|
||||
};
|
||||
|
||||
@@ -333,35 +380,56 @@ ScannerView::ScannerView(
|
||||
big_display.set_style(&style_grey); //Back to grey color
|
||||
};
|
||||
|
||||
button_add.on_select = [this](Button&) { //frequency_list[current_index]
|
||||
|
||||
button_add.on_select = [this](Button&) { // current_frequency
|
||||
|
||||
File scanner_file;
|
||||
std::string freq_file_path = "FREQMAN/" + loaded_file_name + ".TXT";
|
||||
const std::string freq_file_path = "FREQMAN/" + loaded_file_name + ".TXT";
|
||||
auto result = scanner_file.open(freq_file_path); //First search if freq is already in txt
|
||||
|
||||
if (!result.is_valid()) {
|
||||
std::string frequency_to_add = "f="
|
||||
+ to_string_dec_uint(frequency_list[current_index] / 1000)
|
||||
+ to_string_dec_uint(frequency_list[current_index] % 1000UL, 3, '0');
|
||||
char one_char[1]; //Read it char by char
|
||||
std::string line; //and put read line in here
|
||||
const std::string frequency_to_add = "f="
|
||||
+ to_string_dec_uint(current_frequency / 1000)
|
||||
+ to_string_dec_uint(current_frequency % 1000UL, 3, '0');
|
||||
|
||||
bool found=false;
|
||||
for (size_t pointer=0; pointer < scanner_file.size();pointer++) {
|
||||
constexpr size_t buffer_size = 1024;
|
||||
char buffer[buffer_size];
|
||||
|
||||
for (size_t pointer = 0, freq_str_idx = 0; pointer < scanner_file.size(); pointer += buffer_size) {
|
||||
size_t adjusted_buffer_size;
|
||||
if (pointer + buffer_size >= scanner_file.size()) {
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
adjusted_buffer_size = scanner_file.size() - pointer;
|
||||
}
|
||||
else {
|
||||
adjusted_buffer_size = buffer_size;
|
||||
}
|
||||
|
||||
scanner_file.seek(pointer);
|
||||
scanner_file.read(one_char, 1);
|
||||
if ((int)one_char[0] > 31) { //ascii space upwards
|
||||
line += one_char[0]; //Add it to the textline
|
||||
}
|
||||
else if (one_char[0] == '\n') { //New Line
|
||||
if (line.compare(0, frequency_to_add.size(),frequency_to_add) == 0) {
|
||||
found=true;
|
||||
break;
|
||||
scanner_file.read(buffer, adjusted_buffer_size);
|
||||
|
||||
for (size_t i = 0; i < adjusted_buffer_size; ++i) {
|
||||
if (buffer[i] == frequency_to_add.data()[freq_str_idx]) {
|
||||
++freq_str_idx;
|
||||
if (freq_str_idx >= frequency_to_add.size()) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
freq_str_idx = 0;
|
||||
}
|
||||
line.clear(); //Ready for next textline
|
||||
}
|
||||
}
|
||||
|
||||
if (found) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found) {
|
||||
nav_.display_modal("Error", "Frequency already exists");
|
||||
big_display.set(frequency_list[current_index]); //After showing an error
|
||||
big_display.set(current_frequency); //After showing an error
|
||||
}
|
||||
else {
|
||||
scanner_file.append(freq_file_path); //Second: append if it is not there
|
||||
@@ -370,7 +438,7 @@ ScannerView::ScannerView(
|
||||
} else
|
||||
{
|
||||
nav_.display_modal("Error", "Cannot open " + loaded_file_name + ".TXT\nfor appending freq.");
|
||||
big_display.set(frequency_list[current_index]); //After showing an error
|
||||
big_display.set(current_frequency); //After showing an error
|
||||
}
|
||||
};
|
||||
|
||||
@@ -516,9 +584,9 @@ size_t ScannerView::change_mode(uint8_t new_mod) { //Before this, do a scan_thre
|
||||
|
||||
switch (new_mod) {
|
||||
case NFM: //bw 16k (2) default
|
||||
bw.emplace_back("8k5", 0);
|
||||
bw.emplace_back("11k", 0);
|
||||
bw.emplace_back("16k", 0);
|
||||
bw.emplace_back("8k5 ", 0);
|
||||
bw.emplace_back("11k ", 0);
|
||||
bw.emplace_back("16k ", 0);
|
||||
field_bw.set_options(bw);
|
||||
|
||||
baseband::run_image(portapack::spi_flash::image_tag_nfm_audio);
|
||||
@@ -545,7 +613,7 @@ size_t ScannerView::change_mode(uint8_t new_mod) { //Before this, do a scan_thre
|
||||
receiver_model.set_sampling_rate(3072000); receiver_model.set_baseband_bandwidth(1750000);
|
||||
break;
|
||||
case WFM:
|
||||
bw.emplace_back("200k", 0);
|
||||
bw.emplace_back("200k ", 0);
|
||||
field_bw.set_options(bw);
|
||||
|
||||
baseband::run_image(portapack::spi_flash::image_tag_wfm_audio);
|
||||
@@ -566,4 +634,10 @@ void ScannerView::start_scan_thread() {
|
||||
scan_thread = std::make_unique<ScannerThread>(frequency_list);
|
||||
}
|
||||
|
||||
void ScannerView::start_scan_thread(const jammer::jammer_range_t& frequency_range, size_t def_step) {
|
||||
receiver_model.enable();
|
||||
receiver_model.set_squelch_level(0);
|
||||
scan_thread = std::make_unique<ScannerThread>(frequency_range, def_step);
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
@@ -46,6 +46,7 @@ size_t const mod_step[3] = {9000, 100000, 12500 };
|
||||
class ScannerThread {
|
||||
public:
|
||||
ScannerThread(std::vector<rf::Frequency> frequency_list);
|
||||
ScannerThread(const jammer::jammer_range_t& frequency_range, size_t def_step);
|
||||
~ScannerThread();
|
||||
|
||||
void set_scanning(const bool v);
|
||||
@@ -67,6 +68,8 @@ public:
|
||||
|
||||
private:
|
||||
std::vector<rf::Frequency> frequency_list_ { };
|
||||
jammer::jammer_range_t frequency_range_ {false, 0, 0};
|
||||
size_t def_step_ { 0 };
|
||||
Thread* thread { nullptr };
|
||||
|
||||
bool _scanning { true };
|
||||
@@ -75,6 +78,7 @@ private:
|
||||
uint32_t _freq_del { 0 };
|
||||
static msg_t static_fn(void* arg);
|
||||
void run();
|
||||
void create_thread();
|
||||
};
|
||||
|
||||
class ScannerView : public View {
|
||||
@@ -120,6 +124,7 @@ private:
|
||||
NavigationView& nav_;
|
||||
|
||||
void start_scan_thread();
|
||||
void start_scan_thread(const jammer::jammer_range_t& frequency_range, size_t def_step);
|
||||
size_t change_mode(uint8_t mod_type);
|
||||
void show_max();
|
||||
void scan_pause();
|
||||
@@ -129,7 +134,7 @@ private:
|
||||
|
||||
void on_statistics_update(const ChannelStatistics& statistics);
|
||||
void on_headphone_volume_changed(int32_t v);
|
||||
void handle_retune(uint32_t i);
|
||||
void handle_retune(int64_t freq, uint32_t freq_idx);
|
||||
|
||||
jammer::jammer_range_t frequency_range { false, 0, 0 }; //perfect for manual scan task too...
|
||||
int32_t squelch { 0 };
|
||||
@@ -139,6 +144,7 @@ private:
|
||||
freqman_db database { };
|
||||
std::string loaded_file_name;
|
||||
uint32_t current_index { 0 };
|
||||
rf::Frequency current_frequency { 0 };
|
||||
bool userpause { false };
|
||||
|
||||
Labels labels {
|
||||
@@ -295,7 +301,7 @@ private:
|
||||
Message::ID::Retune,
|
||||
[this](const Message* const p) {
|
||||
const auto message = *reinterpret_cast<const RetuneMessage*>(p);
|
||||
this->handle_retune(message.range);
|
||||
this->handle_retune(message.freq, message.range);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -307,7 +307,7 @@ void run_image(const portapack::spi_flash::image_tag_t image_tag) {
|
||||
|
||||
creg::m4txevent::clear();
|
||||
|
||||
m4_init(image_tag, portapack::memory::map::m4_code);
|
||||
m4_init(image_tag, portapack::memory::map::m4_code, false);
|
||||
baseband_image_running = true;
|
||||
|
||||
creg::m4txevent::enable();
|
||||
|
||||
@@ -41,12 +41,18 @@ constexpr si5351::Inputs si5351_inputs {
|
||||
static_assert(si5351_inputs.f_xtal == si5351_xtal_f, "XTAL output frequency wrong");
|
||||
static_assert(si5351_inputs.f_clkin_out() == si5351_clkin_f, "CLKIN output frequency wrong");
|
||||
|
||||
constexpr si5351::PLLInputSource::Type si5351_pll_input_sources {
|
||||
constexpr si5351::PLLInputSource::Type si5351c_pll_input_sources {
|
||||
si5351::PLLInputSource::PLLA_Source_XTAL
|
||||
| si5351::PLLInputSource::PLLB_Source_CLKIN
|
||||
| si5351::PLLInputSource::CLKIN_Div1
|
||||
};
|
||||
|
||||
constexpr si5351::PLLInputSource::Type si5351a_pll_input_sources {
|
||||
si5351::PLLInputSource::PLLA_Source_XTAL
|
||||
| si5351::PLLInputSource::PLLB_Source_XTAL
|
||||
| si5351::PLLInputSource::CLKIN_Div1
|
||||
};
|
||||
|
||||
constexpr si5351::PLL si5351_pll_xtal_25m {
|
||||
.f_in = si5351_inputs.f_xtal,
|
||||
.a = 32,
|
||||
@@ -61,7 +67,8 @@ constexpr si5351::PLL si5351_pll_clkin_10m {
|
||||
.b = 0,
|
||||
.c = 1,
|
||||
};
|
||||
constexpr auto si5351_pll_b_clkin_reg = si5351_pll_clkin_10m.reg(1);
|
||||
constexpr auto si5351c_pll_b_clkin_reg = si5351_pll_clkin_10m.reg(1);
|
||||
constexpr auto si5351a_pll_a_clkin_reg = si5351_pll_clkin_10m.reg(0);
|
||||
|
||||
static_assert(si5351_pll_xtal_25m.f_vco() == si5351_vco_f, "PLL XTAL frequency wrong");
|
||||
static_assert(si5351_pll_xtal_25m.p1() == 3584, "PLL XTAL P1 wrong");
|
||||
@@ -98,7 +105,7 @@ constexpr si5351::MultisynthFractional si5351_ms_0_8m {
|
||||
.c = 1,
|
||||
.r_div = 1,
|
||||
};
|
||||
constexpr auto si5351_ms_0_8m_reg = si5351_ms_0_8m.reg(clock_generator_output_codec);
|
||||
constexpr auto si5351c_ms_0_8m_reg = si5351_ms_0_8m.reg(clock_generator_output_og_codec);
|
||||
|
||||
constexpr si5351::MultisynthFractional si5351_ms_group {
|
||||
.f_src = si5351_vco_f,
|
||||
@@ -107,8 +114,17 @@ constexpr si5351::MultisynthFractional si5351_ms_group {
|
||||
.c = 1,
|
||||
.r_div = 0,
|
||||
};
|
||||
constexpr auto si5351_ms_1_group_reg = si5351_ms_group.reg(clock_generator_output_cpld);
|
||||
constexpr auto si5351_ms_2_group_reg = si5351_ms_group.reg(clock_generator_output_sgpio);
|
||||
constexpr auto si5351c_ms_1_group_reg = si5351_ms_group.reg(clock_generator_output_og_cpld);
|
||||
constexpr auto si5351c_ms_2_group_reg = si5351_ms_group.reg(clock_generator_output_og_sgpio);
|
||||
|
||||
constexpr si5351::MultisynthFractional si5351_ms_16m {
|
||||
.f_src = si5351_vco_f,
|
||||
.a = 50,
|
||||
.b = 0,
|
||||
.c = 1,
|
||||
.r_div = 0,
|
||||
};
|
||||
constexpr auto si5351a_ms_1_sgpio_16m_reg = si5351_ms_16m.reg(clock_generator_output_r9_sgpio);
|
||||
|
||||
constexpr si5351::MultisynthFractional si5351_ms_10m {
|
||||
.f_src = si5351_vco_f,
|
||||
@@ -117,7 +133,8 @@ constexpr si5351::MultisynthFractional si5351_ms_10m {
|
||||
.c = 1,
|
||||
.r_div = 0,
|
||||
};
|
||||
constexpr auto si5351_ms_3_10m_reg = si5351_ms_10m.reg(3);
|
||||
constexpr auto si5351c_ms_3_10m_reg = si5351_ms_10m.reg(3);
|
||||
constexpr auto si5351a_ms_2_mcu_10m_reg = si5351_ms_10m.reg(clock_generator_output_r9_mcu_clkin);
|
||||
|
||||
constexpr si5351::MultisynthFractional si5351_ms_40m {
|
||||
.f_src = si5351_vco_f,
|
||||
@@ -128,10 +145,11 @@ constexpr si5351::MultisynthFractional si5351_ms_40m {
|
||||
};
|
||||
|
||||
constexpr auto si5351_ms_rffc5072 = si5351_ms_40m;
|
||||
constexpr auto si5351_ms_max2837 = si5351_ms_40m;
|
||||
constexpr auto si5351_ms_max283x = si5351_ms_40m;
|
||||
|
||||
constexpr auto si5351_ms_4_reg = si5351_ms_rffc5072.reg(clock_generator_output_first_if);
|
||||
constexpr auto si5351_ms_5_reg = si5351_ms_max2837.reg(clock_generator_output_second_if);
|
||||
constexpr auto si5351c_ms_4_reg = si5351_ms_rffc5072.reg(clock_generator_output_og_first_if);
|
||||
constexpr auto si5351c_ms_5_reg = si5351_ms_max283x.reg(clock_generator_output_og_second_if);
|
||||
constexpr auto si5351a_ms_0_if_40m_reg = si5351_ms_40m.reg(clock_generator_output_r9_if);
|
||||
|
||||
static_assert(si5351_ms_10m.f_out() == 10000000, "MS 10MHz f_out wrong");
|
||||
static_assert(si5351_ms_10m.p1() == 9728, "MS 10MHz p1 wrong");
|
||||
@@ -139,14 +157,8 @@ static_assert(si5351_ms_10m.p2() == 0, "MS 10MHz p2 wrong");
|
||||
static_assert(si5351_ms_10m.p3() == 1, "MS 10MHz p3 wrong");
|
||||
|
||||
static_assert(si5351_ms_rffc5072.f_out() == rffc5072_reference_f, "RFFC5072 reference f_out wrong");
|
||||
// static_assert(si5351_ms_50m.p1() == 2048, "MS 50MHz P1 wrong");
|
||||
// static_assert(si5351_ms_50m.p2() == 0, "MS 50MHz P2 wrong");
|
||||
// static_assert(si5351_ms_50m.p3() == 1, "MS 50MHz P3 wrong");
|
||||
|
||||
static_assert(si5351_ms_max2837.f_out() == max2837_reference_f, "MAX2837 reference f_out wrong");
|
||||
// static_assert(si5351_ms_50m.p1() == 2048, "MS 40MHz P1 wrong");
|
||||
// static_assert(si5351_ms_50m.p2() == 0, "MS 40MHz P2 wrong");
|
||||
// static_assert(si5351_ms_50m.p3() == 1, "MS 40MHz P3 wrong");
|
||||
static_assert(si5351_ms_max283x.f_out() == max283x_reference_f, "MAX283x reference f_out wrong");
|
||||
|
||||
constexpr si5351::MultisynthInteger si5351_ms_int_off {
|
||||
.f_src = si5351_vco_f,
|
||||
@@ -154,40 +166,60 @@ constexpr si5351::MultisynthInteger si5351_ms_int_off {
|
||||
.r_div = 0,
|
||||
};
|
||||
|
||||
constexpr si5351::MultisynthInteger si5351_ms_int_mcu_clkin {
|
||||
constexpr si5351::MultisynthInteger si5351_ms_int_40m {
|
||||
.f_src = si5351_vco_f,
|
||||
.a = 20,
|
||||
.r_div = 0,
|
||||
};
|
||||
|
||||
constexpr auto si5351_ms6_7_off_mcu_clkin_reg = si5351::ms6_7_reg(si5351_ms_int_off, si5351_ms_int_mcu_clkin);
|
||||
constexpr si5351::MultisynthInteger si5351_ms_int_10m {
|
||||
.f_src = si5351_vco_f,
|
||||
.a = 80,
|
||||
.r_div = 0,
|
||||
};
|
||||
|
||||
constexpr auto si5351c_ms_int_mcu_clkin = si5351_ms_int_40m;
|
||||
constexpr auto si5351a_ms_int_mcu_clkin = si5351_ms_int_10m;
|
||||
|
||||
constexpr auto si5351c_ms6_7_off_mcu_clkin_reg = si5351::ms6_7_reg(si5351_ms_int_off, si5351c_ms_int_mcu_clkin);
|
||||
constexpr auto si5351a_ms6_7_off_reg = si5351::ms6_7_reg(si5351_ms_int_off, si5351_ms_int_off);
|
||||
|
||||
static_assert(si5351_ms_int_off.f_out() == 3137254, "MS int off f_out wrong");
|
||||
static_assert(si5351_ms_int_off.p1() == 255, "MS int off P1 wrong");
|
||||
|
||||
static_assert(si5351_ms_int_mcu_clkin.f_out() == mcu_clkin_f, "MS int MCU CLKIN f_out wrong");
|
||||
// static_assert(si5351_ms_int_mcu_clkin.p1() == 20, "MS int MCU CLKIN P1 wrong");
|
||||
static_assert(si5351c_ms_int_mcu_clkin.f_out() == mcu_clkin_og_f, "MS int MCU CLKIN OG f_out wrong");
|
||||
static_assert(si5351a_ms_int_mcu_clkin.f_out() == mcu_clkin_r9_f, "MS int MCU CLKIN r9 f_out wrong");
|
||||
|
||||
using namespace si5351;
|
||||
|
||||
static constexpr ClockControl::MultiSynthSource get_reference_clock_generator_pll(const ClockManager::ReferenceSource reference_source) {
|
||||
static constexpr ClockControl::MultiSynthSource get_si5351c_reference_clock_generator_pll(const ClockManager::ReferenceSource reference_source) {
|
||||
return (reference_source == ClockManager::ReferenceSource::Xtal)
|
||||
? ClockControl::MultiSynthSource::PLLA
|
||||
: ClockControl::MultiSynthSource::PLLB
|
||||
;
|
||||
}
|
||||
|
||||
constexpr ClockControls si5351_clock_control_common { {
|
||||
{ ClockControl::ClockCurrentDrive::_8mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, get_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Fractional, ClockControl::ClockPowerDown::Power_Off },
|
||||
{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Group, ClockControl::ClockInvert::Invert, get_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
|
||||
{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Group, ClockControl::ClockInvert::Normal, get_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
|
||||
{ ClockControl::ClockCurrentDrive::_8mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, get_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
|
||||
{ ClockControl::ClockCurrentDrive::_6mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Invert, get_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
|
||||
{ ClockControl::ClockCurrentDrive::_4mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, get_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
|
||||
{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, get_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Fractional, ClockControl::ClockPowerDown::Power_Off },
|
||||
{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, get_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
|
||||
constexpr ClockControls si5351c_clock_control_common { {
|
||||
{ ClockControl::ClockCurrentDrive::_8mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, get_si5351c_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Fractional, ClockControl::ClockPowerDown::Power_Off },
|
||||
{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Group, ClockControl::ClockInvert::Invert, get_si5351c_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
|
||||
{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Group, ClockControl::ClockInvert::Normal, get_si5351c_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
|
||||
{ ClockControl::ClockCurrentDrive::_8mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, get_si5351c_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
|
||||
{ ClockControl::ClockCurrentDrive::_6mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Invert, get_si5351c_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
|
||||
{ ClockControl::ClockCurrentDrive::_4mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, get_si5351c_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
|
||||
{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, get_si5351c_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Fractional, ClockControl::ClockPowerDown::Power_Off },
|
||||
{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, get_si5351c_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
|
||||
} };
|
||||
|
||||
constexpr ClockControls si5351a_clock_control_common { {
|
||||
{ ClockControl::ClockCurrentDrive::_6mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
|
||||
{ ClockControl::ClockCurrentDrive::_4mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Fractional, ClockControl::ClockPowerDown::Power_Off },
|
||||
{ ClockControl::ClockCurrentDrive::_8mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
|
||||
{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
|
||||
{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
|
||||
{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
|
||||
{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
|
||||
{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
|
||||
} };
|
||||
ClockManager::Reference ClockManager::get_reference() const {
|
||||
return reference;
|
||||
}
|
||||
@@ -212,11 +244,25 @@ void ClockManager::init_clock_generator() {
|
||||
clock_generator.reset();
|
||||
clock_generator.set_crystal_internal_load_capacitance(CrystalInternalLoadCapacitance::XTAL_CL_8pF);
|
||||
clock_generator.enable_fanout();
|
||||
clock_generator.set_pll_input_sources(si5351_pll_input_sources);
|
||||
clock_generator.set_pll_input_sources(hackrf_r9
|
||||
? si5351a_pll_input_sources
|
||||
: si5351c_pll_input_sources);
|
||||
|
||||
auto si5351_clock_control_common = hackrf_r9
|
||||
? si5351a_clock_control_common
|
||||
: si5351c_clock_control_common;
|
||||
|
||||
auto clock_generator_output_mcu_clkin = hackrf_r9
|
||||
? clock_generator_output_r9_mcu_clkin
|
||||
: clock_generator_output_og_mcu_clkin;
|
||||
|
||||
clock_generator.set_clock_control(
|
||||
clock_generator_output_mcu_clkin,
|
||||
si5351_clock_control_common[clock_generator_output_mcu_clkin].clk_src(ClockControl::ClockSource::CLKIN).clk_pdn(ClockControl::ClockPowerDown::Power_On)
|
||||
si5351_clock_control_common[clock_generator_output_mcu_clkin]
|
||||
.clk_src(hackrf_r9
|
||||
? ClockControl::ClockSource::Xtal
|
||||
: ClockControl::ClockSource::CLKIN)
|
||||
.clk_pdn(ClockControl::ClockPowerDown::Power_On)
|
||||
);
|
||||
clock_generator.enable_output(clock_generator_output_mcu_clkin);
|
||||
|
||||
@@ -224,7 +270,10 @@ void ClockManager::init_clock_generator() {
|
||||
|
||||
clock_generator.disable_output(clock_generator_output_mcu_clkin);
|
||||
|
||||
const auto ref_pll = get_reference_clock_generator_pll(reference.source);
|
||||
const auto ref_pll = hackrf_r9
|
||||
? ClockControl::MultiSynthSource::PLLA
|
||||
: get_si5351c_reference_clock_generator_pll(reference.source);
|
||||
|
||||
const ClockControls si5351_clock_control = ClockControls { {
|
||||
si5351_clock_control_common[0].ms_src(ref_pll),
|
||||
si5351_clock_control_common[1].ms_src(ref_pll),
|
||||
@@ -237,21 +286,35 @@ void ClockManager::init_clock_generator() {
|
||||
} };
|
||||
clock_generator.set_clock_control(si5351_clock_control);
|
||||
|
||||
clock_generator.write(si5351_pll_a_xtal_reg);
|
||||
clock_generator.write(si5351_pll_b_clkin_reg);
|
||||
clock_generator.write(si5351_ms_0_8m_reg);
|
||||
clock_generator.write(si5351_ms_1_group_reg);
|
||||
clock_generator.write(si5351_ms_2_group_reg);
|
||||
clock_generator.write(si5351_ms_3_10m_reg);
|
||||
clock_generator.write(si5351_ms_4_reg);
|
||||
clock_generator.write(si5351_ms_5_reg);
|
||||
clock_generator.write(si5351_ms6_7_off_mcu_clkin_reg);
|
||||
if (hackrf_r9) {
|
||||
const PLLReg pll_reg = (reference.source == ReferenceSource::Xtal)
|
||||
? si5351_pll_a_xtal_reg
|
||||
: si5351a_pll_a_clkin_reg;
|
||||
clock_generator.write(pll_reg);
|
||||
clock_generator.write(si5351a_ms_0_if_40m_reg);
|
||||
clock_generator.write(si5351a_ms_1_sgpio_16m_reg);
|
||||
clock_generator.write(si5351a_ms_2_mcu_10m_reg);
|
||||
clock_generator.write(si5351a_ms6_7_off_reg);
|
||||
} else {
|
||||
clock_generator.write(si5351_pll_a_xtal_reg);
|
||||
clock_generator.write(si5351c_pll_b_clkin_reg);
|
||||
clock_generator.write(si5351c_ms_0_8m_reg);
|
||||
clock_generator.write(si5351c_ms_1_group_reg);
|
||||
clock_generator.write(si5351c_ms_2_group_reg);
|
||||
clock_generator.write(si5351c_ms_3_10m_reg);
|
||||
clock_generator.write(si5351c_ms_4_reg);
|
||||
clock_generator.write(si5351c_ms_5_reg);
|
||||
clock_generator.write(si5351c_ms6_7_off_mcu_clkin_reg);
|
||||
}
|
||||
|
||||
clock_generator.reset_plls();
|
||||
|
||||
// Wait for both PLLs to lock.
|
||||
// TODO: Disable the unused PLL?
|
||||
const uint8_t device_status_mask = (ref_pll == ClockControl::MultiSynthSource::PLLB) ? 0x40 : 0x20;
|
||||
// Wait for PLL(s) to lock.
|
||||
uint8_t device_status_mask = hackrf_r9
|
||||
? 0x20
|
||||
: (ref_pll == ClockControl::MultiSynthSource::PLLB)
|
||||
? 0x40
|
||||
: 0x20;
|
||||
while((clock_generator.device_status() & device_status_mask) != 0);
|
||||
|
||||
clock_generator.set_clock_control(
|
||||
@@ -268,12 +331,18 @@ uint32_t ClockManager::measure_gp_clkin_frequency() {
|
||||
return get_frequency_monitor_measurement_in_hertz();
|
||||
}
|
||||
|
||||
bool ClockManager::loss_of_signal() {
|
||||
return hackrf_r9
|
||||
? clock_generator.plla_loss_of_signal()
|
||||
: clock_generator.clkin_loss_of_signal();
|
||||
}
|
||||
|
||||
ClockManager::ReferenceSource ClockManager::detect_reference_source() {
|
||||
if( clock_generator.clkin_loss_of_signal() ) {
|
||||
if(loss_of_signal()) {
|
||||
// No external reference. Turn on PortaPack reference (if present).
|
||||
portapack_tcxo_enable();
|
||||
|
||||
if( clock_generator.clkin_loss_of_signal() ) {
|
||||
if(loss_of_signal()) {
|
||||
// No PortaPack reference was detected. Choose the HackRF crystal as the reference.
|
||||
return ReferenceSource::Xtal;
|
||||
} else {
|
||||
@@ -285,6 +354,11 @@ ClockManager::ReferenceSource ClockManager::detect_reference_source() {
|
||||
}
|
||||
|
||||
ClockManager::Reference ClockManager::choose_reference() {
|
||||
if (hackrf_r9) {
|
||||
gpio_r9_clkin_en.write(1);
|
||||
volatile uint32_t delay = 240000 + 24000;
|
||||
while(delay--);
|
||||
}
|
||||
const auto detected_reference = detect_reference_source();
|
||||
|
||||
if( (detected_reference == ReferenceSource::External) ||
|
||||
@@ -296,8 +370,12 @@ ClockManager::Reference ClockManager::choose_reference() {
|
||||
}
|
||||
}
|
||||
|
||||
if (hackrf_r9) {
|
||||
gpio_r9_clkin_en.write(0);
|
||||
}
|
||||
|
||||
portapack_tcxo_disable();
|
||||
return { ReferenceSource::Xtal, 10000000 };
|
||||
return { ReferenceSource::Xtal, 25000000 };
|
||||
}
|
||||
|
||||
void ClockManager::shutdown() {
|
||||
@@ -305,18 +383,26 @@ void ClockManager::shutdown() {
|
||||
}
|
||||
|
||||
void ClockManager::enable_codec_clocks() {
|
||||
clock_generator.enable_clock(clock_generator_output_codec);
|
||||
clock_generator.enable_clock(clock_generator_output_cpld);
|
||||
clock_generator.enable_clock(clock_generator_output_sgpio);
|
||||
if (hackrf_r9) {
|
||||
clock_generator.enable_clock(clock_generator_output_r9_sgpio);
|
||||
} else {
|
||||
clock_generator.enable_clock(clock_generator_output_og_codec);
|
||||
clock_generator.enable_clock(clock_generator_output_og_cpld);
|
||||
clock_generator.enable_clock(clock_generator_output_og_sgpio);
|
||||
}
|
||||
/* Turn on all outputs at the same time. This probably doesn't ensure
|
||||
* their phase relationships. For example, clocks that output frequencies
|
||||
* in a 2:1 relationship may start with the slower clock high or low?
|
||||
*/
|
||||
clock_generator.enable_output_mask(
|
||||
(1U << clock_generator_output_codec)
|
||||
| (1U << clock_generator_output_cpld)
|
||||
| (1U << clock_generator_output_sgpio)
|
||||
);
|
||||
if (hackrf_r9) {
|
||||
clock_generator.enable_output_mask(1U << clock_generator_output_r9_sgpio);
|
||||
} else {
|
||||
clock_generator.enable_output_mask(
|
||||
(1U << clock_generator_output_og_codec)
|
||||
| (1U << clock_generator_output_og_cpld)
|
||||
| (1U << clock_generator_output_og_sgpio)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void ClockManager::disable_codec_clocks() {
|
||||
@@ -324,34 +410,45 @@ void ClockManager::disable_codec_clocks() {
|
||||
* be enabled for the output to come to rest at the state specified by
|
||||
* CLKx_DISABLE_STATE.
|
||||
*/
|
||||
clock_generator.disable_output_mask(
|
||||
(1U << clock_generator_output_codec)
|
||||
| (1U << clock_generator_output_cpld)
|
||||
| (1U << clock_generator_output_sgpio)
|
||||
);
|
||||
clock_generator.disable_clock(clock_generator_output_codec);
|
||||
clock_generator.disable_clock(clock_generator_output_cpld);
|
||||
clock_generator.disable_clock(clock_generator_output_sgpio);
|
||||
if (hackrf_r9) {
|
||||
clock_generator.disable_output_mask(1U << clock_generator_output_r9_sgpio);
|
||||
clock_generator.disable_clock(clock_generator_output_r9_sgpio);
|
||||
} else {
|
||||
clock_generator.disable_output_mask(
|
||||
(1U << clock_generator_output_og_codec)
|
||||
| (1U << clock_generator_output_og_cpld)
|
||||
| (1U << clock_generator_output_og_sgpio)
|
||||
);
|
||||
clock_generator.disable_clock(clock_generator_output_og_codec);
|
||||
clock_generator.disable_clock(clock_generator_output_og_cpld);
|
||||
clock_generator.disable_clock(clock_generator_output_og_sgpio);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ClockManager::enable_first_if_clock() {
|
||||
clock_generator.enable_clock(clock_generator_output_first_if);
|
||||
clock_generator.enable_output_mask(1U << clock_generator_output_first_if);
|
||||
void ClockManager::enable_if_clocks() {
|
||||
if (hackrf_r9) {
|
||||
clock_generator.enable_clock(clock_generator_output_r9_if);
|
||||
clock_generator.enable_output_mask(1U << clock_generator_output_r9_if);
|
||||
} else {
|
||||
clock_generator.enable_clock(clock_generator_output_og_first_if);
|
||||
clock_generator.enable_output_mask(1U << clock_generator_output_og_first_if);
|
||||
clock_generator.enable_clock(clock_generator_output_og_second_if);
|
||||
clock_generator.enable_output_mask(1U << clock_generator_output_og_second_if);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void ClockManager::disable_first_if_clock() {
|
||||
clock_generator.disable_output_mask(1U << clock_generator_output_first_if);
|
||||
clock_generator.disable_clock(clock_generator_output_first_if);
|
||||
}
|
||||
|
||||
void ClockManager::enable_second_if_clock() {
|
||||
clock_generator.enable_clock(clock_generator_output_second_if);
|
||||
clock_generator.enable_output_mask(1U << clock_generator_output_second_if);
|
||||
}
|
||||
|
||||
void ClockManager::disable_second_if_clock() {
|
||||
clock_generator.disable_output_mask(1U << clock_generator_output_second_if);
|
||||
clock_generator.disable_clock(clock_generator_output_second_if);
|
||||
void ClockManager::disable_if_clocks() {
|
||||
if (hackrf_r9) {
|
||||
clock_generator.disable_output_mask(1U << clock_generator_output_r9_if);
|
||||
clock_generator.disable_clock(clock_generator_output_r9_if);
|
||||
} else {
|
||||
clock_generator.disable_output_mask(1U << clock_generator_output_og_first_if);
|
||||
clock_generator.disable_clock(clock_generator_output_og_first_if);
|
||||
clock_generator.disable_output_mask(1U << clock_generator_output_og_second_if);
|
||||
clock_generator.disable_clock(clock_generator_output_og_second_if);
|
||||
}
|
||||
}
|
||||
|
||||
void ClockManager::set_sampling_frequency(const uint32_t frequency) {
|
||||
@@ -360,14 +457,21 @@ void ClockManager::set_sampling_frequency(const uint32_t frequency) {
|
||||
* necessary to change the MS0 synth frequency, and ensure the output
|
||||
* is divided by two.
|
||||
*/
|
||||
clock_generator.set_ms_frequency(clock_generator_output_codec, frequency * 2, si5351_vco_f, 1);
|
||||
if (hackrf_r9) {
|
||||
clock_generator.set_ms_frequency(clock_generator_output_r9_sgpio, frequency * 2, si5351_vco_f, 0);
|
||||
} else {
|
||||
clock_generator.set_ms_frequency(clock_generator_output_og_codec, frequency * 2, si5351_vco_f, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void ClockManager::set_reference_ppb(const int32_t ppb) {
|
||||
/* NOTE: This adjustment only affects PLLA, which is derived from the 25MHz crystal.
|
||||
* It is assumed an external clock coming in to PLLB is sufficiently accurate as to not need adjustment.
|
||||
/* NOTE: This adjustment only affects PLLA when it is derived from the 25MHz crystal.
|
||||
* It is assumed an external clock coming in to CLKIN/PLLB is sufficiently accurate as to not need adjustment.
|
||||
* TODO: Revisit the above policy. It may be good to allow adjustment of the external reference too.
|
||||
*/
|
||||
if (hackrf_r9 && reference.source != ReferenceSource::Xtal) {
|
||||
return;
|
||||
}
|
||||
constexpr uint32_t pll_multiplier = si5351_pll_xtal_25m.a;
|
||||
constexpr uint32_t denominator = 1000000 / pll_multiplier;
|
||||
const uint32_t new_a = (ppb >= 0) ? pll_multiplier : (pll_multiplier - 1);
|
||||
@@ -425,11 +529,13 @@ void ClockManager::start_audio_pll() {
|
||||
|
||||
/* For 40MHz clock source, 48kHz audio rate, 256Fs MCLK:
|
||||
* Fout=12.288MHz, Fcco=491.52MHz
|
||||
* PSEL=20, NSEL=125, MSEL=768
|
||||
* OG: PSEL=20, NSEL=125, MSEL=768
|
||||
* PDEC=31, NDEC=45, MDEC=30542
|
||||
* r9: PSEL=20, NSEL=125, MSEL=3072
|
||||
* PDEC=31, NDEC=45, MDEC=8308
|
||||
*/
|
||||
cgu::pll0audio::mdiv({
|
||||
.mdec = 30542,
|
||||
.mdec = hackrf_r9 ? 8308UL : 30542UL,
|
||||
});
|
||||
cgu::pll0audio::np_div({
|
||||
.pdec = 31,
|
||||
@@ -467,18 +573,25 @@ void ClockManager::stop_audio_pll() {
|
||||
|
||||
void ClockManager::enable_clock_output(bool enable) {
|
||||
if(enable) {
|
||||
clock_generator.enable_output(clock_generator_output_clkout);
|
||||
clock_generator.enable_output(clock_generator_output_og_clkout);
|
||||
if(portapack::persistent_memory::clkout_freq() < 1000) {
|
||||
clock_generator.set_ms_frequency(clock_generator_output_clkout, portapack::persistent_memory::clkout_freq() * 128000, si5351_vco_f, 7);
|
||||
clock_generator.set_ms_frequency(clock_generator_output_og_clkout, portapack::persistent_memory::clkout_freq() * 128000, si5351_vco_f, 7);
|
||||
} else {
|
||||
clock_generator.set_ms_frequency(clock_generator_output_clkout, portapack::persistent_memory::clkout_freq() * 1000, si5351_vco_f, 0);
|
||||
clock_generator.set_ms_frequency(clock_generator_output_og_clkout, portapack::persistent_memory::clkout_freq() * 1000, si5351_vco_f, 0);
|
||||
}
|
||||
} else {
|
||||
clock_generator.disable_output(clock_generator_output_clkout);
|
||||
clock_generator.disable_output(clock_generator_output_og_clkout);
|
||||
}
|
||||
|
||||
auto si5351_clock_control_common = hackrf_r9
|
||||
? si5351a_clock_control_common
|
||||
: si5351c_clock_control_common;
|
||||
const auto ref_pll = hackrf_r9
|
||||
? ClockControl::MultiSynthSource::PLLA
|
||||
: get_si5351c_reference_clock_generator_pll(reference.source);
|
||||
|
||||
if(enable)
|
||||
clock_generator.set_clock_control(clock_generator_output_clkout, si5351_clock_control_common[clock_generator_output_clkout].ms_src(get_reference_clock_generator_pll(reference.source)).clk_pdn(ClockControl::ClockPowerDown::Power_On));
|
||||
clock_generator.set_clock_control(clock_generator_output_og_clkout, si5351_clock_control_common[clock_generator_output_og_clkout].ms_src(ref_pll).clk_pdn(ClockControl::ClockPowerDown::Power_On));
|
||||
else
|
||||
clock_generator.set_clock_control(clock_generator_output_clkout, ClockControl::power_off());
|
||||
clock_generator.set_clock_control(clock_generator_output_og_clkout, ClockControl::power_off());
|
||||
}
|
||||
|
||||
@@ -65,11 +65,8 @@ public:
|
||||
void enable_codec_clocks();
|
||||
void disable_codec_clocks();
|
||||
|
||||
void enable_first_if_clock();
|
||||
void disable_first_if_clock();
|
||||
|
||||
void enable_second_if_clock();
|
||||
void disable_second_if_clock();
|
||||
void enable_if_clocks();
|
||||
void disable_if_clocks();
|
||||
|
||||
void set_sampling_frequency(const uint32_t frequency);
|
||||
|
||||
@@ -99,6 +96,7 @@ private:
|
||||
|
||||
ReferenceSource detect_reference_source();
|
||||
Reference choose_reference();
|
||||
bool loss_of_signal();
|
||||
};
|
||||
|
||||
#endif/*__CLOCK_MANAGER_H__*/
|
||||
|
||||
@@ -38,7 +38,7 @@ using namespace lpc43xx;
|
||||
* I suppose I could force M4MEMMAP to an invalid memory reason which would
|
||||
* cause an exception and effectively halt the M4. But that feels gross.
|
||||
*/
|
||||
void m4_init(const portapack::spi_flash::image_tag_t image_tag, const portapack::memory::region_t to) {
|
||||
void m4_init(const portapack::spi_flash::image_tag_t image_tag, const portapack::memory::region_t to, const bool full_reset) {
|
||||
const portapack::spi_flash::chunk_t* chunk = reinterpret_cast<const portapack::spi_flash::chunk_t*>(portapack::spi_flash::images.base());
|
||||
while(chunk->tag) {
|
||||
if( chunk->tag == image_tag ) {
|
||||
@@ -50,8 +50,11 @@ void m4_init(const portapack::spi_flash::image_tag_t image_tag, const portapack:
|
||||
*/
|
||||
LPC_CREG->M4MEMMAP = to.base();
|
||||
|
||||
/* Reset M4 core */
|
||||
LPC_RGU->RESET_CTRL[0] = (1 << 13);
|
||||
/* Reset M4 core and optionally all peripherals */
|
||||
LPC_RGU->RESET_CTRL[0] = (full_reset) ?
|
||||
(1 << 1) // PERIPH_RST
|
||||
: (1 << 13) // M4_RST
|
||||
;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
#include "memory_map.hpp"
|
||||
#include "spi_image.hpp"
|
||||
|
||||
void m4_init(const portapack::spi_flash::image_tag_t image_tag, const portapack::memory::region_t to);
|
||||
void m4_init(const portapack::spi_flash::image_tag_t image_tag, const portapack::memory::region_t to, const bool full_reset);
|
||||
void m4_request_shutdown();
|
||||
|
||||
void m0_halt();
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
#include "../../hackrf/firmware/common/firmware_info.h"
|
||||
#include "../../hackrf/firmware/common/platform_detect.h"
|
||||
|
||||
#define SUPPORTED_PLATFORM (PLATFORM_HACKRF1_OG | PLATFORM_HACKRF1_R9)
|
||||
#define DFU_MODE_VALUE 0
|
||||
|
||||
__attribute__((section(".firmware_info"))) const struct firmware_info_t firmware_info = {
|
||||
.magic = "HACKRFFW",
|
||||
.struct_version = 1,
|
||||
.dfu_mode = DFU_MODE_VALUE,
|
||||
.supported_platform = SUPPORTED_PLATFORM,
|
||||
.version_string = VERSION_STRING,
|
||||
};
|
||||
@@ -32,8 +32,12 @@ using namespace hackrf::one;
|
||||
|
||||
namespace max2837 {
|
||||
|
||||
using namespace max283x;
|
||||
|
||||
namespace lna {
|
||||
|
||||
using namespace max283x::lna;
|
||||
|
||||
constexpr std::array<uint8_t, 8> lookup_8db_steps {
|
||||
0b111, 0b011, 0b110, 0b010,
|
||||
0b100, 0b000, 0b000, 0b000
|
||||
@@ -48,6 +52,8 @@ static uint_fast8_t gain_ordinal(const int8_t db) {
|
||||
|
||||
namespace vga {
|
||||
|
||||
using namespace max283x::vga;
|
||||
|
||||
static uint_fast8_t gain_ordinal(const int8_t db) {
|
||||
const auto db_sat = gain_db_range.clip(db);
|
||||
return ((db_sat >> 1) & 0b11111) ^ 0b11111;
|
||||
@@ -57,6 +63,8 @@ static uint_fast8_t gain_ordinal(const int8_t db) {
|
||||
|
||||
namespace tx {
|
||||
|
||||
using namespace max283x::tx;
|
||||
|
||||
static uint_fast8_t gain_ordinal(const int8_t db) {
|
||||
const auto db_sat = gain_db_range.clip(db);
|
||||
uint8_t value = db_sat & 0x0f;
|
||||
@@ -69,6 +77,8 @@ static uint_fast8_t gain_ordinal(const int8_t db) {
|
||||
|
||||
namespace filter {
|
||||
|
||||
using namespace max283x::filter;
|
||||
|
||||
static uint_fast8_t bandwidth_ordinal(const uint32_t bandwidth) {
|
||||
/* Determine filter setting that will provide bandwidth greater than or
|
||||
* equal to requested bandwidth.
|
||||
@@ -84,13 +94,13 @@ static uint_fast8_t bandwidth_ordinal(const uint32_t bandwidth) {
|
||||
constexpr float seconds_for_temperature_sense_adc_conversion = 30.0e-6;
|
||||
constexpr halrtcnt_t ticks_for_temperature_sense_adc_conversion = (base_m4_clk_f * seconds_for_temperature_sense_adc_conversion + 1);
|
||||
|
||||
constexpr uint32_t reference_frequency = max2837_reference_f;
|
||||
constexpr uint32_t reference_frequency = max283x_reference_f;
|
||||
constexpr uint32_t pll_factor = 1.0 / (4.0 / 3.0 / reference_frequency) + 0.5;
|
||||
|
||||
void MAX2837::init() {
|
||||
set_mode(Mode::Shutdown);
|
||||
|
||||
gpio_max2837_enable.output();
|
||||
gpio_max283x_enable.output();
|
||||
gpio_max2837_rxenable.output();
|
||||
gpio_max2837_txenable.output();
|
||||
|
||||
@@ -140,10 +150,30 @@ void MAX2837::init() {
|
||||
set_mode(Mode::Standby);
|
||||
}
|
||||
|
||||
enum class Mask {
|
||||
Enable = 0b001,
|
||||
RxEnable = 0b010,
|
||||
TxEnable = 0b100,
|
||||
Shutdown = 0b000,
|
||||
Standby = Enable,
|
||||
Receive = Enable | RxEnable,
|
||||
Transmit = Enable | TxEnable,
|
||||
};
|
||||
|
||||
Mask mode_mask(const Mode mode) {
|
||||
switch (mode) {
|
||||
case Mode::Standby: return Mask::Standby;
|
||||
case Mode::Receive: return Mask::Receive;
|
||||
case Mode::Transmit: return Mask::Transmit;
|
||||
default: return Mask::Shutdown;
|
||||
}
|
||||
}
|
||||
|
||||
void MAX2837::set_mode(const Mode mode) {
|
||||
gpio_max2837_enable.write(toUType(mode) & toUType(Mode::Mask_Enable));
|
||||
gpio_max2837_rxenable.write(toUType(mode) & toUType(Mode::Mask_RxEnable));
|
||||
gpio_max2837_txenable.write(toUType(mode) & toUType(Mode::Mask_TxEnable));
|
||||
Mask mask = mode_mask(mode);
|
||||
gpio_max283x_enable.write(toUType(mask) & toUType(Mask::Enable));
|
||||
gpio_max2837_rxenable.write(toUType(mask) & toUType(Mask::RxEnable));
|
||||
gpio_max2837_txenable.write(toUType(mask) & toUType(Mask::TxEnable));
|
||||
}
|
||||
|
||||
void MAX2837::flush() {
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#ifndef __MAX2837_H__
|
||||
#define __MAX2837_H__
|
||||
|
||||
#include "max283x.hpp"
|
||||
#include "gpio.hpp"
|
||||
#include "spi_arbiter.hpp"
|
||||
|
||||
@@ -29,99 +30,11 @@
|
||||
#include <array>
|
||||
|
||||
#include "dirty_registers.hpp"
|
||||
#include "rf_path.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
namespace max2837 {
|
||||
|
||||
enum class Mode {
|
||||
Mask_Enable = 0b001,
|
||||
Mask_RxEnable = 0b010,
|
||||
Mask_TxEnable = 0b100,
|
||||
|
||||
Shutdown = 0b000,
|
||||
Standby = Mask_Enable,
|
||||
Receive = Mask_Enable | Mask_RxEnable,
|
||||
Transmit = Mask_Enable | Mask_TxEnable,
|
||||
};
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
namespace lo {
|
||||
|
||||
constexpr std::array<rf::FrequencyRange, 4> band { {
|
||||
{ 2300000000, 2400000000 },
|
||||
{ 2400000000, 2500000000 },
|
||||
{ 2500000000, 2600000000 },
|
||||
{ 2600000000, 2700000000 },
|
||||
} };
|
||||
|
||||
} /* namespace lo */
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
namespace lna {
|
||||
|
||||
constexpr range_t<int8_t> gain_db_range { 0, 40 };
|
||||
constexpr int8_t gain_db_step = 8;
|
||||
|
||||
constexpr std::array<rf::FrequencyRange, 2> band { {
|
||||
{ 2300000000, 2500000000 },
|
||||
{ 2500000000, 2700000000 },
|
||||
} };
|
||||
|
||||
} /* namespace lna */
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
namespace vga {
|
||||
|
||||
constexpr range_t<int8_t> gain_db_range { 0, 62 };
|
||||
constexpr int8_t gain_db_step = 2;
|
||||
|
||||
} /* namespace vga */
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
namespace tx {
|
||||
|
||||
constexpr range_t<int8_t> gain_db_range { 0, 47 };
|
||||
constexpr int8_t gain_db_step = 1;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
namespace filter {
|
||||
|
||||
constexpr std::array<uint32_t, 16> bandwidths {
|
||||
/* Assumption: these values are in ascending order */
|
||||
1750000,
|
||||
2500000, /* Some documentation says 2.25MHz */
|
||||
3500000,
|
||||
5000000,
|
||||
5500000,
|
||||
6000000,
|
||||
7000000,
|
||||
8000000,
|
||||
9000000,
|
||||
10000000,
|
||||
12000000,
|
||||
14000000,
|
||||
15000000,
|
||||
20000000,
|
||||
24000000,
|
||||
28000000,
|
||||
};
|
||||
|
||||
constexpr auto bandwidth_minimum = bandwidths[0];
|
||||
constexpr auto bandwidth_maximum = bandwidths[bandwidths.size() - 1];
|
||||
|
||||
} /* namespace filter */
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
using reg_t = uint16_t;
|
||||
using address_t = uint8_t;
|
||||
using namespace max283x;
|
||||
|
||||
constexpr size_t reg_count = 32;
|
||||
|
||||
@@ -826,7 +739,7 @@ constexpr RegisterMap initial_register_values { Register_Type {
|
||||
},
|
||||
} };
|
||||
|
||||
class MAX2837 {
|
||||
class MAX2837 : public MAX283x {
|
||||
public:
|
||||
constexpr MAX2837(
|
||||
spi::arbiter::Target& target
|
||||
@@ -834,13 +747,13 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
void init();
|
||||
void set_mode(const Mode mode);
|
||||
void init() override;
|
||||
void set_mode(const Mode mode) override;
|
||||
|
||||
void set_tx_vga_gain(const int_fast8_t db);
|
||||
void set_lna_gain(const int_fast8_t db);
|
||||
void set_vga_gain(const int_fast8_t db);
|
||||
void set_lpf_rf_bandwidth(const uint32_t bandwidth_minimum);
|
||||
void set_tx_vga_gain(const int_fast8_t db) override;
|
||||
void set_lna_gain(const int_fast8_t db) override;
|
||||
void set_vga_gain(const int_fast8_t db) override;
|
||||
void set_lpf_rf_bandwidth(const uint32_t bandwidth_minimum) override;
|
||||
#if 0
|
||||
void rx_cal() {
|
||||
_map.r.spi_en.EN_SPI = 1;
|
||||
@@ -882,16 +795,16 @@ public:
|
||||
}
|
||||
#endif
|
||||
|
||||
bool set_frequency(const rf::Frequency lo_frequency);
|
||||
bool set_frequency(const rf::Frequency lo_frequency) override;
|
||||
|
||||
void set_rx_lo_iq_calibration(const size_t v);
|
||||
void set_rx_lo_iq_calibration(const size_t v) override;
|
||||
void set_rx_bias_trim(const size_t v);
|
||||
void set_vco_bias(const size_t v);
|
||||
void set_rx_buff_vcm(const size_t v);
|
||||
void set_rx_buff_vcm(const size_t v) override;
|
||||
|
||||
reg_t temp_sense();
|
||||
reg_t temp_sense() override;
|
||||
|
||||
reg_t read(const address_t reg_num);
|
||||
reg_t read(const address_t reg_num) override;
|
||||
|
||||
private:
|
||||
spi::arbiter::Target& _target;
|
||||
|
||||
@@ -0,0 +1,354 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2023 Great Scott Gadgets
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "max2839.hpp"
|
||||
|
||||
#include "hackrf_hal.hpp"
|
||||
#include "hackrf_gpio.hpp"
|
||||
using namespace hackrf::one;
|
||||
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace max2839 {
|
||||
|
||||
namespace lna {
|
||||
|
||||
using namespace max283x::lna;
|
||||
|
||||
constexpr std::array<uint8_t, 8> lookup_8db_steps {
|
||||
0b11, 0b11, 0b10, 0b10,
|
||||
0b01, 0b00, 0b00, 0b00
|
||||
};
|
||||
|
||||
static uint_fast8_t gain_ordinal(const int8_t db) {
|
||||
const auto db_sat = gain_db_range.clip(db);
|
||||
return lna::lookup_8db_steps[(db_sat >> 3) & 7];
|
||||
}
|
||||
|
||||
} /* namespace lna */
|
||||
|
||||
namespace vga {
|
||||
|
||||
using namespace max283x::vga;
|
||||
|
||||
constexpr range_t<int8_t> gain_db_range_internal { 0, 63 };
|
||||
|
||||
static uint_fast8_t gain_ordinal(const int8_t db) {
|
||||
const auto db_sat = gain_db_range_internal.clip(db);
|
||||
return (db_sat & 0b111111) ^ 0b111111;
|
||||
}
|
||||
|
||||
} /* namespace vga */
|
||||
|
||||
namespace tx {
|
||||
|
||||
using namespace max283x::tx;
|
||||
|
||||
static uint_fast8_t gain_ordinal(const int8_t db) {
|
||||
const auto db_sat = gain_db_range.clip(db);
|
||||
return 47 - db_sat;
|
||||
}
|
||||
|
||||
} /* namespace tx */
|
||||
|
||||
namespace filter {
|
||||
|
||||
using namespace max283x::filter;
|
||||
|
||||
static uint_fast8_t bandwidth_ordinal(const uint32_t bandwidth) {
|
||||
/* Determine filter setting that will provide bandwidth greater than or
|
||||
* equal to requested bandwidth.
|
||||
*/
|
||||
return std::lower_bound(bandwidths.cbegin(), bandwidths.cend(), bandwidth) - bandwidths.cbegin();
|
||||
}
|
||||
|
||||
} /* namespace filter */
|
||||
|
||||
/* Empirical testing indicates about 25us is necessary to get a valid
|
||||
* temperature sense conversion from the ADC.
|
||||
*/
|
||||
constexpr float seconds_for_temperature_sense_adc_conversion = 30.0e-6;
|
||||
constexpr halrtcnt_t ticks_for_temperature_sense_adc_conversion = (base_m4_clk_f * seconds_for_temperature_sense_adc_conversion + 1);
|
||||
|
||||
constexpr uint32_t reference_frequency = max283x_reference_f;
|
||||
constexpr uint32_t pll_factor = 1.0 / (4.0 / 3.0 / reference_frequency) + 0.5;
|
||||
|
||||
static int_fast8_t requested_rx_lna_gain = 0;
|
||||
static int_fast8_t requested_rx_vga_gain = 0;
|
||||
|
||||
void MAX2839::init() {
|
||||
set_mode(Mode::Shutdown);
|
||||
|
||||
gpio_max283x_enable.output();
|
||||
gpio_max2839_rxtx.output();
|
||||
|
||||
_map.r.rxrf_1.MIMOmode = 1; /* enable RXINB */
|
||||
|
||||
_map.r.pa_drv.TXVGA_GAIN_SPI_EN = 1;
|
||||
_map.r.tx_gain.TXVGA_GAIN_SPI = 0x00;
|
||||
|
||||
_map.r.hpfsm_3.HPC_STOP = 1; /* 1kHz */
|
||||
|
||||
_map.r.rxrf_2.LNAgain_SPI_EN = 1; /* control LNA gain from SPI */
|
||||
_map.r.lpf_vga_1.L = 0b000;
|
||||
_map.r.lpf_vga_2.L = 0b000;
|
||||
|
||||
_map.r.rx_top_1.VGAgain_SPI_EN = 1; /* control VGA gain from SPI */
|
||||
_map.r.lpf_vga_1.VGA = 0b000000;
|
||||
_map.r.lpf_vga_2.VGA = 0b010101;
|
||||
|
||||
_map.r.lpf_vga_2.BUFF_VCM = 0b11; /* maximum RX output common-mode voltage */
|
||||
|
||||
_map.r.lpf_vga_1.ModeCtrl = 0b01; /* Rx LPF */
|
||||
_map.r.lpf.FT = 0b0000; /* 1.75 MHz LPF */
|
||||
|
||||
_map.r.spi_en.EN_SPI = 1; /* enable chip functions when ENABLE pin set */
|
||||
|
||||
_map.r.lo_gen.LOGEN_2GM = 0;
|
||||
|
||||
_map.r.rssi_vga.RSSI_MODE = 1; /* RSSI independent of RXHP */
|
||||
|
||||
/*
|
||||
* There are two LNA band settings, but we only use one of them.
|
||||
* Switching to the other one doesn't make the overall spectrum any
|
||||
* flatter but adds a surprise step in the middle.
|
||||
*/
|
||||
_map.r.rxrf_1.LNAband = 0; /* 2.3 - 2.5GHz */
|
||||
|
||||
_dirty.set();
|
||||
flush();
|
||||
|
||||
set_mode(Mode::Standby);
|
||||
}
|
||||
|
||||
enum class Mask {
|
||||
Enable = 0b01,
|
||||
RxTx = 0b10,
|
||||
Shutdown = 0b00,
|
||||
Standby = RxTx,
|
||||
Receive = Enable | RxTx,
|
||||
Transmit = Enable,
|
||||
};
|
||||
|
||||
Mask mode_mask(const Mode mode) {
|
||||
switch (mode) {
|
||||
case Mode::Standby: return Mask::Standby;
|
||||
case Mode::Receive: return Mask::Receive;
|
||||
case Mode::Transmit: return Mask::Transmit;
|
||||
default: return Mask::Shutdown;
|
||||
}
|
||||
}
|
||||
|
||||
void MAX2839::set_mode(const Mode mode) {
|
||||
Mask mask = mode_mask(mode);
|
||||
gpio_max283x_enable.write(toUType(mask) & toUType(Mask::Enable));
|
||||
gpio_max2839_rxtx.write(toUType(mask) & toUType(Mask::RxTx));
|
||||
}
|
||||
|
||||
void MAX2839::flush() {
|
||||
if( _dirty ) {
|
||||
for(size_t n=0; n<reg_count; n++) {
|
||||
if( _dirty[n] ) {
|
||||
write(n, _map.w[n]);
|
||||
}
|
||||
}
|
||||
_dirty.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void MAX2839::flush_one(const Register reg) {
|
||||
const auto reg_num = toUType(reg);
|
||||
write(reg_num, _map.w[reg_num]);
|
||||
_dirty.clear(reg_num);
|
||||
}
|
||||
|
||||
void MAX2839::write(const address_t reg_num, const reg_t value) {
|
||||
uint16_t t = (0U << 15) | (reg_num << 10) | (value & 0x3ffU);
|
||||
_target.transfer(&t, 1);
|
||||
}
|
||||
|
||||
reg_t MAX2839::read(const address_t reg_num) {
|
||||
uint16_t t = (1U << 15) | (reg_num << 10);
|
||||
_target.transfer(&t, 1U);
|
||||
return t & 0x3ffU;
|
||||
}
|
||||
|
||||
void MAX2839::write(const Register reg, const reg_t value) {
|
||||
write(toUType(reg), value);
|
||||
}
|
||||
|
||||
reg_t MAX2839::read(const Register reg) {
|
||||
return read(toUType(reg));
|
||||
}
|
||||
|
||||
void MAX2839::set_tx_vga_gain(const int_fast8_t db) {
|
||||
_map.r.tx_gain.TXVGA_GAIN_SPI = tx::gain_ordinal(db);
|
||||
_dirty[Register::TX_GAIN] = 1;
|
||||
flush();
|
||||
}
|
||||
|
||||
/*
|
||||
* MAX2839 gain rain ranges differ slightly from MAX2837's but are close
|
||||
* enough that it makes sense to emulate MAX2837 gain ranges for a consistent
|
||||
* user experience.
|
||||
*/
|
||||
void MAX2839::configure_rx_gain() {
|
||||
/* Apply MAX2837 restrictions to requested gain settings. */
|
||||
int_fast8_t lna_gain = lna::gain_db_range.clip(requested_rx_lna_gain);
|
||||
lna_gain &= 0x38;
|
||||
int_fast8_t vga_gain = vga::gain_db_range.clip(requested_rx_vga_gain);
|
||||
vga_gain &= 0x3e;
|
||||
|
||||
/*
|
||||
* MAX2839 has lower full-scale RX output voltage than MAX2837, so we
|
||||
* adjust the VGA (baseband) gain to compensate.
|
||||
*/
|
||||
vga_gain += 3;
|
||||
|
||||
/*
|
||||
* If that adjustment puts VGA gain out of range, use LNA gain to
|
||||
* compensate. MAX2839 VGA gain can be any number from 0 through 63.
|
||||
*/
|
||||
if (vga_gain > 63) {
|
||||
if (lna_gain <= 32) {
|
||||
vga_gain -= 8;
|
||||
lna_gain += 8;
|
||||
} else {
|
||||
vga_gain = 63;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* MAX2839 lacks max-24 dB (16 dB) and max-40 dB (0 dB) LNA gain
|
||||
* settings, so we use VGA gain to compensate.
|
||||
*/
|
||||
if (lna_gain == 0) {
|
||||
lna_gain = 8;
|
||||
vga_gain = (vga_gain >= 8) ? vga_gain - 8 : 0;
|
||||
}
|
||||
if (lna_gain == 16) {
|
||||
if (vga_gain > 32) {
|
||||
vga_gain -= 8;
|
||||
lna_gain += 8;
|
||||
} else {
|
||||
vga_gain += 8;
|
||||
lna_gain -= 8;
|
||||
}
|
||||
}
|
||||
|
||||
_map.r.lpf_vga_2.L = lna::gain_ordinal(lna_gain);
|
||||
_dirty[Register::RXRF_2] = 1;
|
||||
_map.r.lpf_vga_2.VGA = vga::gain_ordinal(vga_gain);
|
||||
_dirty[Register::LPF_VGA_2] = 1;
|
||||
flush();
|
||||
}
|
||||
|
||||
void MAX2839::set_lna_gain(const int_fast8_t db) {
|
||||
requested_rx_lna_gain = db;
|
||||
configure_rx_gain();
|
||||
}
|
||||
|
||||
void MAX2839::set_vga_gain(const int_fast8_t db) {
|
||||
requested_rx_vga_gain = db;
|
||||
configure_rx_gain();
|
||||
}
|
||||
|
||||
void MAX2839::set_lpf_rf_bandwidth(const uint32_t bandwidth_minimum) {
|
||||
_map.r.lpf.FT = filter::bandwidth_ordinal(bandwidth_minimum);
|
||||
_dirty[Register::LPF] = 1;
|
||||
flush();
|
||||
}
|
||||
|
||||
bool MAX2839::set_frequency(const rf::Frequency lo_frequency) {
|
||||
/* TODO: This is a sad implementation. Refactor. */
|
||||
if( lo::band[0].contains(lo_frequency) ) {
|
||||
_map.r.syn_int_div.LOGEN_BSW = 0b00; /* 2300 - 2399.99MHz */
|
||||
} else if( lo::band[1].contains(lo_frequency) ) {
|
||||
_map.r.syn_int_div.LOGEN_BSW = 0b01; /* 2400 - 2499.99MHz */
|
||||
} else if( lo::band[2].contains(lo_frequency) ) {
|
||||
_map.r.syn_int_div.LOGEN_BSW = 0b10; /* 2500 - 2599.99MHz */
|
||||
} else if( lo::band[3].contains(lo_frequency) ) {
|
||||
_map.r.syn_int_div.LOGEN_BSW = 0b11; /* 2600 - 2700Hz */
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
_dirty[Register::SYN_INT_DIV] = 1;
|
||||
|
||||
const uint64_t div_q20 = (lo_frequency * (1 << 20)) / pll_factor;
|
||||
|
||||
_map.r.syn_int_div.SYN_INTDIV = div_q20 >> 20;
|
||||
_dirty[Register::SYN_INT_DIV] = 1;
|
||||
_map.r.syn_fr_div_2.SYN_FRDIV_19_10 = (div_q20 >> 10) & 0x3ff;
|
||||
_dirty[Register::SYN_FR_DIV_2] = 1;
|
||||
/* flush to commit high FRDIV first, as low FRDIV commits the change */
|
||||
flush();
|
||||
|
||||
_map.r.syn_fr_div_1.SYN_FRDIV_9_0 = (div_q20 & 0x3ff);
|
||||
_dirty[Register::SYN_FR_DIV_1] = 1;
|
||||
flush();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MAX2839::set_rx_lo_iq_calibration(const size_t v) {
|
||||
_map.r.rxrf_2.RX_IQERR_SPI_EN = 1;
|
||||
_dirty[Register::RXRF_2] = 1;
|
||||
_map.r.rxrf_1.iqerr_trim = v;
|
||||
_dirty[Register::RXRF_1] = 1;
|
||||
flush();
|
||||
}
|
||||
|
||||
void MAX2839::set_rx_buff_vcm(const size_t v) {
|
||||
_map.r.lpf_vga_2.BUFF_VCM = v;
|
||||
_dirty[Register::LPF_VGA_2] = 1;
|
||||
flush();
|
||||
}
|
||||
|
||||
reg_t MAX2839::temp_sense() {
|
||||
if( !_map.r.rx_top_2.ts_en ) {
|
||||
_map.r.rx_top_2.ts_en = 1;
|
||||
flush_one(Register::RX_TOP_2);
|
||||
|
||||
chThdSleepMilliseconds(1);
|
||||
}
|
||||
|
||||
_map.r.rx_top_2.ts_adc_trigger = 1;
|
||||
flush_one(Register::RX_TOP_2);
|
||||
|
||||
halPolledDelay(ticks_for_temperature_sense_adc_conversion);
|
||||
|
||||
/*
|
||||
* Things look very similar to MAX2837, so this probably works, but the
|
||||
* MAX2839 data sheet does not describe the TEMP_SENSE register contents.
|
||||
*/
|
||||
const auto value = read(Register::TEMP_SENSE);
|
||||
|
||||
_map.r.rx_top_2.ts_adc_trigger = 0;
|
||||
flush_one(Register::RX_TOP_2);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,688 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2023 Great Scott Gadgets
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __MAX2839_H__
|
||||
#define __MAX2839_H__
|
||||
|
||||
#include "max283x.hpp"
|
||||
#include "gpio.hpp"
|
||||
#include "spi_arbiter.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
|
||||
#include "dirty_registers.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
namespace max2839 {
|
||||
|
||||
using namespace max283x;
|
||||
|
||||
constexpr size_t reg_count = 32;
|
||||
|
||||
enum class Register : address_t {
|
||||
RXENABLE = 0,
|
||||
RXRF_1 = 1,
|
||||
RXRF_2 = 2,
|
||||
RXRF_LPF = 3,
|
||||
LPF = 4,
|
||||
LPF_VGA_1 = 5,
|
||||
LPF_VGA_2 = 6,
|
||||
RSSI_VGA = 7,
|
||||
RX_TOP_1 = 8,
|
||||
RX_TOP_2 = 9,
|
||||
TX_TOP_1 = 10,
|
||||
TEMP_SENSE = 11,
|
||||
HPFSM_1 = 12,
|
||||
HPFSM_2 = 13,
|
||||
HPFSM_3 = 14,
|
||||
HPFSM_4 = 15,
|
||||
SPI_EN = 16,
|
||||
SYN_FR_DIV_1 = 17,
|
||||
SYN_FR_DIV_2 = 18,
|
||||
SYN_INT_DIV = 19,
|
||||
SYN_CFG_1 = 20,
|
||||
SYN_CFG_2 = 21,
|
||||
VAS_CFG = 22,
|
||||
LO_MISC = 23,
|
||||
XTAL_CFG = 24,
|
||||
VCO_CFG = 25,
|
||||
LO_GEN = 26,
|
||||
PA_DRV = 27,
|
||||
PA_DAC = 28,
|
||||
TX_GAIN = 29,
|
||||
TX_LO_IQ = 30,
|
||||
TX_DC_CORR = 31,
|
||||
};
|
||||
|
||||
struct RXENABLE_Type {
|
||||
reg_t RESERVED0 : 10;
|
||||
reg_t RESERVED1 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(RXENABLE_Type) == sizeof(reg_t), "RXENABLE_Type wrong size");
|
||||
|
||||
struct RXRF_1_Type {
|
||||
reg_t LNAband : 1;
|
||||
reg_t RESERVED0 : 1;
|
||||
reg_t MIMOmode : 1;
|
||||
reg_t iqerr_trim : 5;
|
||||
reg_t RESERVED1 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(RXRF_1_Type) == sizeof(reg_t), "RXRF_1_Type wrong size");
|
||||
|
||||
struct RXRF_2_Type {
|
||||
reg_t LNAgain_SPI_EN : 1;
|
||||
reg_t RESERVED0 : 1;
|
||||
reg_t RX_IQERR_SPI_EN : 1;
|
||||
reg_t RESERVED1 : 7;
|
||||
reg_t RESERVED2 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(RXRF_2_Type) == sizeof(reg_t), "RXRF_2_Type wrong size");
|
||||
|
||||
struct RXRF_LPF_Type {
|
||||
reg_t RESERVED0 : 10;
|
||||
reg_t RESERVED1 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(RXRF_LPF_Type) == sizeof(reg_t), "RXRF_LPF_Type wrong size");
|
||||
|
||||
struct LPF_Type {
|
||||
reg_t RESERVED0 : 2;
|
||||
reg_t dF : 2;
|
||||
reg_t RESERVED1 : 2;
|
||||
reg_t FT : 4;
|
||||
reg_t RESERVED2 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(LPF_Type) == sizeof(reg_t), "LPF_Type wrong size");
|
||||
|
||||
struct LPF_VGA_1_Type {
|
||||
reg_t L : 2;
|
||||
reg_t VGA : 6;
|
||||
reg_t ModeCtrl : 2;
|
||||
reg_t RESERVED0 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(LPF_VGA_1_Type) == sizeof(reg_t), "LPF_VGA_1_Type wrong size");
|
||||
|
||||
struct LPF_VGA_2_Type {
|
||||
reg_t L : 2;
|
||||
reg_t VGA : 6;
|
||||
reg_t BUFF_VCM : 2;
|
||||
reg_t RESERVED0 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(LPF_VGA_2_Type) == sizeof(reg_t), "LPF_VGA_2_Type wrong size");
|
||||
|
||||
struct RSSI_VGA_Type {
|
||||
reg_t RESERVED0 : 1;
|
||||
reg_t RSSI_MUX : 1;
|
||||
reg_t RSSI_MODE : 1;
|
||||
reg_t RESERVED1 : 4;
|
||||
reg_t RXBB_OUT_SEL : 1;
|
||||
reg_t RESERVED2 : 1;
|
||||
reg_t RSSI_INPUT : 1;
|
||||
reg_t RESERVED3 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(RSSI_VGA_Type) == sizeof(reg_t), "RSSI_VGA_Type wrong size");
|
||||
|
||||
struct RX_TOP_1_Type {
|
||||
reg_t RESERVED0 : 1;
|
||||
reg_t VGAgain_SPI_EN : 1;
|
||||
reg_t LPF_MODE_SEL : 1;
|
||||
reg_t RESERVED1 : 7;
|
||||
reg_t RESERVED2 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(RX_TOP_1_Type) == sizeof(reg_t), "RX_TOP_1_Type wrong size");
|
||||
|
||||
struct RX_TOP_2_Type {
|
||||
reg_t ts_adc_trigger : 1;
|
||||
reg_t ts_en : 1;
|
||||
reg_t RESERVED0 : 1;
|
||||
reg_t DOUT_DRVH : 1;
|
||||
reg_t DOUT_CSB_SEL : 1;
|
||||
reg_t DOUT_SEL : 3;
|
||||
reg_t RESERVED1 : 2;
|
||||
reg_t RESERVED2 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(RX_TOP_2_Type) == sizeof(reg_t), "RX_TOP_2_Type wrong size");
|
||||
|
||||
struct TX_TOP_1_Type {
|
||||
reg_t TXCAL_GAIN : 2;
|
||||
reg_t TXCAL_V2I_FILT : 3;
|
||||
reg_t RESERVED1 : 5;
|
||||
reg_t RESERVED2 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(TX_TOP_1_Type) == sizeof(reg_t), "TX_TOP_1_Type wrong size");
|
||||
|
||||
struct TEMP_SENSE_Type {
|
||||
reg_t RESERVED0 : 10;
|
||||
reg_t RESERVED1 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(TEMP_SENSE_Type) == sizeof(reg_t), "TEMP_SENSE_Type wrong size");
|
||||
|
||||
struct HPFSM_1_Type {
|
||||
reg_t HPC_10M : 2;
|
||||
reg_t HPC_10M_GAIN : 2;
|
||||
reg_t HPC_600k : 3;
|
||||
reg_t HPC_600k_GAIN : 3;
|
||||
reg_t RESERVED0 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(HPFSM_1_Type) == sizeof(reg_t), "HPFSM_1_Type wrong size");
|
||||
|
||||
struct HPFSM_2_Type {
|
||||
reg_t HPC_100k : 2;
|
||||
reg_t HPC_100k_GAIN : 2;
|
||||
reg_t HPC_30k : 2;
|
||||
reg_t HPC_30k_GAIN : 2;
|
||||
reg_t HPC_1k : 2;
|
||||
reg_t RESERVED0 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(HPFSM_2_Type) == sizeof(reg_t), "HPFSM_2_Type wrong size");
|
||||
|
||||
struct HPFSM_3_Type {
|
||||
reg_t HPC_1k_B7B6 : 2;
|
||||
reg_t HPC_DELAY : 2;
|
||||
reg_t HPC_STOP : 2;
|
||||
reg_t HPC_STOP_M2 : 2;
|
||||
reg_t HPC_RXGAIN_EN : 1;
|
||||
reg_t TXGATE_EN : 1;
|
||||
reg_t RESERVED0 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(HPFSM_3_Type) == sizeof(reg_t), "HPFSM_3_Type wrong size");
|
||||
|
||||
struct HPFSM_4_Type {
|
||||
reg_t HPC_DIVH : 1;
|
||||
reg_t RESERVED0 : 5;
|
||||
reg_t HPC_SEQ_BYP : 1;
|
||||
reg_t RESERVED1 : 2;
|
||||
reg_t HPC_MODE : 1;
|
||||
reg_t RESERVED2 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(HPFSM_4_Type) == sizeof(reg_t), "HPFSM_4_Type wrong size");
|
||||
|
||||
struct SPI_EN_Type {
|
||||
reg_t EN_SPI : 1;
|
||||
reg_t CAL_SPI : 1;
|
||||
reg_t RESERVED0 : 4;
|
||||
reg_t PADAC_SPI_EN : 1;
|
||||
reg_t PADAC_TX_EN : 1;
|
||||
reg_t RESERVED1 : 2;
|
||||
reg_t RESERVED2 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(SPI_EN_Type) == sizeof(reg_t), "SPI_EN_Type wrong size");
|
||||
|
||||
struct SYN_FR_DIV_1_Type {
|
||||
reg_t SYN_FRDIV_9_0 : 10;
|
||||
reg_t RESERVED0 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(SYN_FR_DIV_1_Type) == sizeof(reg_t), "SYN_FR_DIV_1_Type wrong size");
|
||||
|
||||
struct SYN_FR_DIV_2_Type {
|
||||
reg_t SYN_FRDIV_19_10 : 10;
|
||||
reg_t RESERVED0 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(SYN_FR_DIV_2_Type) == sizeof(reg_t), "SYN_FR_DIV_2_Type wrong size");
|
||||
|
||||
struct SYN_INT_DIV_Type {
|
||||
reg_t SYN_INTDIV : 8;
|
||||
reg_t LOGEN_BSW : 2;
|
||||
reg_t RESERVED0 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(SYN_INT_DIV_Type) == sizeof(reg_t), "SYN_INT_DIV_Type wrong size");
|
||||
|
||||
struct SYN_CFG_1_Type {
|
||||
reg_t RESERVED0 : 1;
|
||||
reg_t SYN_REF_DIV_RATIO : 2;
|
||||
reg_t RESERVED1 : 2;
|
||||
reg_t SYN_CLOCKOUT_DRIVE : 1;
|
||||
reg_t RESERVED2 : 4;
|
||||
reg_t RESERVED3 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(SYN_CFG_1_Type) == sizeof(reg_t), "SYN_CFG_1_Type wrong size");
|
||||
|
||||
struct SYN_CFG_2_Type {
|
||||
reg_t RESERVED0 : 10;
|
||||
reg_t RESERVED1 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(SYN_CFG_2_Type) == sizeof(reg_t), "SYN_CFG_2_Type wrong size");
|
||||
|
||||
struct VAS_CFG_Type {
|
||||
reg_t VAS_MODE : 1;
|
||||
reg_t VAS_RELOCK_SEL : 1;
|
||||
reg_t VAS_DIV : 3;
|
||||
reg_t VAS_DLY : 2;
|
||||
reg_t VAS_TRIG_EN : 1;
|
||||
reg_t RESERVED0 : 2;
|
||||
reg_t RESERVED1 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(VAS_CFG_Type) == sizeof(reg_t), "VAS_CFG_Type wrong size");
|
||||
|
||||
struct LO_MISC_Type {
|
||||
reg_t VAS_SPI : 5;
|
||||
reg_t XTAL_BIAS_SEL : 2;
|
||||
reg_t RESERVED0 : 3;
|
||||
reg_t RESERVED1 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(LO_MISC_Type) == sizeof(reg_t), "LO_MISC_Type wrong size");
|
||||
|
||||
struct XTAL_CFG_Type {
|
||||
reg_t XTAL_FTUNE : 7;
|
||||
reg_t RESERVED0 : 1;
|
||||
reg_t XTAL_CLKOUT_DIV : 1;
|
||||
reg_t XTAL_CORE_EN : 1;
|
||||
reg_t RESERVED1 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(XTAL_CFG_Type) == sizeof(reg_t), "XTAL_CFG_Type wrong size");
|
||||
|
||||
struct VCO_CFG_Type {
|
||||
reg_t RESERVED0 : 10;
|
||||
reg_t RESERVED1 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(VCO_CFG_Type) == sizeof(reg_t), "VCO_CFG_Type wrong size");
|
||||
|
||||
struct LO_GEN_Type {
|
||||
reg_t RESERVED0 : 3;
|
||||
reg_t LOGEN_2GM : 1;
|
||||
reg_t RESERVED1 : 2;
|
||||
reg_t VAS_TST : 4;
|
||||
reg_t RESERVED2 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(LO_GEN_Type) == sizeof(reg_t), "LO_GEN_Type wrong size");
|
||||
|
||||
struct PA_DRV_Type {
|
||||
reg_t TXLO_IQ_SPI : 6;
|
||||
reg_t TXLO_IQ_SPI_EN : 1;
|
||||
reg_t TXVGA_GAIN_SPI_EN : 1;
|
||||
reg_t TX_DCCORR_SPI_EN : 1;
|
||||
reg_t RESERVED0 : 1;
|
||||
reg_t RESERVED1 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(PA_DRV_Type) == sizeof(reg_t), "PA_DRV_Type wrong size");
|
||||
|
||||
struct PA_DAC_Type {
|
||||
reg_t PADAC_BIAS : 6;
|
||||
reg_t PADAC_DLY : 4;
|
||||
reg_t RESERVED0 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(PA_DAC_Type) == sizeof(reg_t), "PA_DAC_Type wrong size");
|
||||
|
||||
struct TX_GAIN_Type {
|
||||
reg_t TXVGA_GAIN_SPI : 6;
|
||||
reg_t RESERVED0 : 4;
|
||||
reg_t RESERVED1 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(TX_GAIN_Type) == sizeof(reg_t), "TX_GAIN_Type wrong size");
|
||||
|
||||
struct TX_LO_IQ_Type {
|
||||
reg_t TX_DCCORR_I : 6;
|
||||
reg_t RESERVED0 : 2;
|
||||
reg_t PADAC_IV : 1;
|
||||
reg_t PADAC_VMODE : 1;
|
||||
reg_t RESERVED1 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(TX_LO_IQ_Type) == sizeof(reg_t), "TX_LO_IQ_Type wrong size");
|
||||
|
||||
struct TX_DC_CORR_Type {
|
||||
reg_t TX_DCCORR_Q : 6;
|
||||
reg_t RESERVED0 : 3;
|
||||
reg_t PADAC_DIVH : 1;
|
||||
reg_t RESERVED1 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(TX_DC_CORR_Type) == sizeof(reg_t), "TX_DC_CORR_Type wrong size");
|
||||
|
||||
struct Register_Type {
|
||||
RXENABLE_Type rxenable; /* 0 */
|
||||
RXRF_1_Type rxrf_1;
|
||||
RXRF_2_Type rxrf_2;
|
||||
RXRF_LPF_Type rxrf_lpf_1;
|
||||
LPF_Type lpf; /* 4 */
|
||||
LPF_VGA_1_Type lpf_vga_1;
|
||||
LPF_VGA_2_Type lpf_vga_2;
|
||||
RSSI_VGA_Type rssi_vga;
|
||||
RX_TOP_1_Type rx_top_1; /* 8 */
|
||||
RX_TOP_2_Type rx_top_2;
|
||||
TX_TOP_1_Type tx_top_1;
|
||||
TEMP_SENSE_Type temp_sense;
|
||||
HPFSM_1_Type hpfsm_1; /* 12 */
|
||||
HPFSM_2_Type hpfsm_2;
|
||||
HPFSM_3_Type hpfsm_3;
|
||||
HPFSM_4_Type hpfsm_4;
|
||||
SPI_EN_Type spi_en; /* 16 */
|
||||
SYN_FR_DIV_1_Type syn_fr_div_1;
|
||||
SYN_FR_DIV_2_Type syn_fr_div_2;
|
||||
SYN_INT_DIV_Type syn_int_div;
|
||||
SYN_CFG_1_Type syn_cfg_1; /* 20 */
|
||||
SYN_CFG_2_Type syn_cfg_2;
|
||||
VAS_CFG_Type vas_cfg;
|
||||
LO_MISC_Type lo_misc;
|
||||
XTAL_CFG_Type xtal_cfg; /* 24 */
|
||||
VCO_CFG_Type vco_cfg;
|
||||
LO_GEN_Type lo_gen;
|
||||
PA_DRV_Type pa_drv;
|
||||
PA_DAC_Type pa_dac; /* 28 */
|
||||
TX_GAIN_Type tx_gain;
|
||||
TX_LO_IQ_Type tx_lo_iq;
|
||||
TX_DC_CORR_Type tx_dc_corr;
|
||||
};
|
||||
|
||||
static_assert(sizeof(Register_Type) == reg_count * sizeof(reg_t), "Register_Type wrong size");
|
||||
|
||||
struct RegisterMap {
|
||||
constexpr RegisterMap(
|
||||
Register_Type values
|
||||
) : r(values)
|
||||
{
|
||||
}
|
||||
|
||||
union {
|
||||
Register_Type r;
|
||||
std::array<reg_t, reg_count> w;
|
||||
};
|
||||
};
|
||||
|
||||
static_assert(sizeof(RegisterMap) == reg_count * sizeof(reg_t), "RegisterMap type wrong size");
|
||||
|
||||
constexpr RegisterMap initial_register_values { Register_Type {
|
||||
/* settings recommended by MAX2839 data sheet */
|
||||
.rxenable = { /* 0 */
|
||||
.RESERVED0 = 0,
|
||||
.RESERVED1 = 0,
|
||||
},
|
||||
.rxrf_1 = { /* 1 */
|
||||
.LNAband = 0,
|
||||
.RESERVED0 = 0,
|
||||
.MIMOmode = 1,
|
||||
.iqerr_trim = 0b000001,
|
||||
.RESERVED1 = 0,
|
||||
},
|
||||
.rxrf_2 = { /* 2 */
|
||||
.LNAgain_SPI_EN = 0,
|
||||
.RESERVED0 = 0,
|
||||
.RX_IQERR_SPI_EN = 0,
|
||||
.RESERVED1 = 0b0010000,
|
||||
.RESERVED2 = 0,
|
||||
},
|
||||
.rxrf_lpf_1 = { /* 3 */
|
||||
.RESERVED0 = 0b0110111001,
|
||||
.RESERVED1 = 0,
|
||||
},
|
||||
.lpf = { /* 4 */
|
||||
.RESERVED0 = 0b10,
|
||||
.dF = 0b01,
|
||||
.RESERVED1 = 0b10,
|
||||
.FT = 0b1111,
|
||||
.RESERVED2 = 0,
|
||||
},
|
||||
.lpf_vga_1 = { /* 5 */
|
||||
.L = 0b00,
|
||||
.VGA = 0b000000,
|
||||
.ModeCtrl = 0b01,
|
||||
.RESERVED0 = 0,
|
||||
},
|
||||
.lpf_vga_2 = { /* 6 */
|
||||
.L = 0b00,
|
||||
.VGA = 0b000000,
|
||||
.BUFF_VCM = 0b00,
|
||||
.RESERVED0 = 0,
|
||||
},
|
||||
.rssi_vga = { /* 7 */
|
||||
.RESERVED0 = 0,
|
||||
.RSSI_MUX = 0,
|
||||
.RSSI_MODE = 0,
|
||||
.RESERVED1 = 0b0001,
|
||||
.RXBB_OUT_SEL = 0,
|
||||
.RESERVED2 = 0,
|
||||
.RSSI_INPUT = 1,
|
||||
.RESERVED3 = 0,
|
||||
},
|
||||
.rx_top_1 = { /* 8 */
|
||||
.RESERVED0 = 0,
|
||||
.VGAgain_SPI_EN = 0,
|
||||
.LPF_MODE_SEL = 0,
|
||||
.RESERVED1 = 0b1000100,
|
||||
.RESERVED2 = 0,
|
||||
},
|
||||
.rx_top_2 = { /* 9 */
|
||||
.ts_adc_trigger = 0,
|
||||
.ts_en = 0,
|
||||
.RESERVED0 = 0,
|
||||
.DOUT_DRVH = 1,
|
||||
.DOUT_CSB_SEL = 1,
|
||||
.DOUT_SEL = 0b000,
|
||||
.RESERVED1 = 0b00,
|
||||
.RESERVED2 = 0,
|
||||
},
|
||||
.tx_top_1 = { /* 10 */
|
||||
.TXCAL_GAIN = 0b00,
|
||||
.TXCAL_V2I_FILT = 0b011,
|
||||
.RESERVED1 = 0b00000,
|
||||
.RESERVED2 = 0,
|
||||
},
|
||||
.temp_sense = { /* 11 */
|
||||
.RESERVED0 = 0b0000000100,
|
||||
.RESERVED1 = 0,
|
||||
},
|
||||
.hpfsm_1 = { /* 12 */
|
||||
.HPC_10M = 0b11,
|
||||
.HPC_10M_GAIN = 0b11,
|
||||
.HPC_600k = 0b100,
|
||||
.HPC_600k_GAIN = 0b100,
|
||||
.RESERVED0 = 0,
|
||||
},
|
||||
.hpfsm_2 = { /* 13 */
|
||||
.HPC_100k = 0b00,
|
||||
.HPC_100k_GAIN = 0b00,
|
||||
.HPC_30k = 0b01,
|
||||
.HPC_30k_GAIN = 0b01,
|
||||
.HPC_1k = 0b01,
|
||||
.RESERVED0 = 0,
|
||||
},
|
||||
.hpfsm_3 = { /* 14 */
|
||||
.HPC_1k_B7B6 = 0b01,
|
||||
.HPC_DELAY = 0b01,
|
||||
.HPC_STOP = 0b00,
|
||||
.HPC_STOP_M2 = 0b11,
|
||||
.HPC_RXGAIN_EN = 1,
|
||||
.TXGATE_EN = 1,
|
||||
.RESERVED0 = 0,
|
||||
},
|
||||
.hpfsm_4 = { /* 15 */
|
||||
.HPC_DIVH = 1,
|
||||
.RESERVED0 = 0b00000,
|
||||
.HPC_SEQ_BYP = 0,
|
||||
.RESERVED1 = 0b00,
|
||||
.HPC_MODE = 1,
|
||||
.RESERVED2 = 0,
|
||||
},
|
||||
.spi_en = { /* 16 */
|
||||
.EN_SPI = 0,
|
||||
.CAL_SPI = 0,
|
||||
.RESERVED0 = 0b0111,
|
||||
.PADAC_SPI_EN = 0,
|
||||
.PADAC_TX_EN = 0,
|
||||
.RESERVED1 = 0b00,
|
||||
.RESERVED2 = 0,
|
||||
},
|
||||
.syn_fr_div_1 = { /* 17 */
|
||||
.SYN_FRDIV_9_0 = 0b0101010101,
|
||||
.RESERVED0 = 0,
|
||||
},
|
||||
.syn_fr_div_2 = { /* 18 */
|
||||
.SYN_FRDIV_19_10 = 0b0101010101,
|
||||
.RESERVED0 = 0,
|
||||
},
|
||||
.syn_int_div = { /* 19 */
|
||||
.SYN_INTDIV = 0b01010011,
|
||||
.LOGEN_BSW = 0b01,
|
||||
.RESERVED0 = 0,
|
||||
},
|
||||
.syn_cfg_1 = { /* 20 */
|
||||
.RESERVED0 = 1,
|
||||
.SYN_REF_DIV_RATIO = 0b00,
|
||||
.RESERVED1 = 0b01,
|
||||
.SYN_CLOCKOUT_DRIVE = 0,
|
||||
.RESERVED2 = 0b1001,
|
||||
.RESERVED3 = 0,
|
||||
},
|
||||
.syn_cfg_2 = { /* 21 */
|
||||
.RESERVED0 = 0b0000101101,
|
||||
.RESERVED1 = 0,
|
||||
},
|
||||
.vas_cfg = { /* 22 */
|
||||
.VAS_MODE = 1,
|
||||
.VAS_RELOCK_SEL = 0,
|
||||
.VAS_DIV = 0b010,
|
||||
.VAS_DLY = 0b01,
|
||||
.VAS_TRIG_EN = 1,
|
||||
.RESERVED0 = 0b01,
|
||||
.RESERVED1 = 0,
|
||||
},
|
||||
.lo_misc = { /* 23 */
|
||||
.VAS_SPI = 0b01111,
|
||||
.XTAL_BIAS_SEL = 0b10,
|
||||
.RESERVED0 = 0b100,
|
||||
.RESERVED1 = 0,
|
||||
},
|
||||
.xtal_cfg = { /* 24 */
|
||||
.XTAL_FTUNE = 0b0000000,
|
||||
.RESERVED0 = 1,
|
||||
.XTAL_CLKOUT_DIV = 1,
|
||||
.XTAL_CORE_EN = 0,
|
||||
.RESERVED1 = 0,
|
||||
},
|
||||
.vco_cfg = { /* 25 */
|
||||
.RESERVED0 = 0b0000000000,
|
||||
.RESERVED1 = 0,
|
||||
},
|
||||
.lo_gen = { /* 26 */
|
||||
.RESERVED0 = 0b000,
|
||||
.LOGEN_2GM = 0,
|
||||
.RESERVED1 = 0b00,
|
||||
.VAS_TST = 0b1111,
|
||||
.RESERVED2 = 0,
|
||||
},
|
||||
.pa_drv = { /* 27 */
|
||||
.TXLO_IQ_SPI = 0b000000,
|
||||
.TXLO_IQ_SPI_EN = 0,
|
||||
.TXVGA_GAIN_SPI_EN = 0,
|
||||
.TX_DCCORR_SPI_EN = 0,
|
||||
.RESERVED0 = 1,
|
||||
.RESERVED1 = 0,
|
||||
},
|
||||
.pa_dac = { /* 28 */
|
||||
.PADAC_BIAS = 0b000000,
|
||||
.PADAC_DLY = 0b0011,
|
||||
.RESERVED0 = 0,
|
||||
},
|
||||
.tx_gain = { /* 29 */
|
||||
.TXVGA_GAIN_SPI = 0b111111,
|
||||
.RESERVED0 = 0b0000,
|
||||
.RESERVED1 = 0,
|
||||
},
|
||||
.tx_lo_iq = { /* 30 */
|
||||
.TX_DCCORR_I = 0b000000,
|
||||
.RESERVED0 = 0b00,
|
||||
.PADAC_IV = 1,
|
||||
.PADAC_VMODE = 1,
|
||||
.RESERVED1 = 0,
|
||||
},
|
||||
.tx_dc_corr = { /* 31 */
|
||||
.TX_DCCORR_Q = 0b000000,
|
||||
.RESERVED0 = 0b101,
|
||||
.PADAC_DIVH = 1,
|
||||
.RESERVED1 = 0,
|
||||
},
|
||||
} };
|
||||
|
||||
class MAX2839 : public MAX283x {
|
||||
public:
|
||||
constexpr MAX2839(
|
||||
spi::arbiter::Target& target
|
||||
) : _target(target)
|
||||
{
|
||||
}
|
||||
|
||||
void init() override;
|
||||
void set_mode(const Mode mode) override;
|
||||
|
||||
void set_tx_vga_gain(const int_fast8_t db) override;
|
||||
void set_lna_gain(const int_fast8_t db) override;
|
||||
void set_vga_gain(const int_fast8_t db) override;
|
||||
void set_lpf_rf_bandwidth(const uint32_t bandwidth_minimum) override;
|
||||
bool set_frequency(const rf::Frequency lo_frequency) override;
|
||||
void set_rx_lo_iq_calibration(const size_t v) override;
|
||||
void set_rx_buff_vcm(const size_t v) override;
|
||||
|
||||
reg_t temp_sense() override;
|
||||
|
||||
reg_t read(const address_t reg_num) override;
|
||||
|
||||
private:
|
||||
spi::arbiter::Target& _target;
|
||||
|
||||
RegisterMap _map { initial_register_values };
|
||||
DirtyRegisters<Register, reg_count> _dirty { };
|
||||
|
||||
void flush_one(const Register reg);
|
||||
|
||||
void write(const address_t reg_num, const reg_t value);
|
||||
|
||||
void write(const Register reg, const reg_t value);
|
||||
reg_t read(const Register reg);
|
||||
|
||||
void flush();
|
||||
|
||||
void configure_rx_gain();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif/*__MAX2839_H__*/
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Great Scott Gadgets
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __MAX283X_H__
|
||||
#define __MAX283X_H__
|
||||
|
||||
#include "rf_path.hpp"
|
||||
|
||||
namespace max283x {
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
namespace lo {
|
||||
|
||||
constexpr std::array<rf::FrequencyRange, 4> band { {
|
||||
{ 2300000000, 2400000000 },
|
||||
{ 2400000000, 2500000000 },
|
||||
{ 2500000000, 2600000000 },
|
||||
{ 2600000000, 2700000000 },
|
||||
} };
|
||||
|
||||
} /* namespace lo */
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
namespace lna {
|
||||
|
||||
constexpr range_t<int8_t> gain_db_range { 0, 40 };
|
||||
constexpr int8_t gain_db_step = 8;
|
||||
|
||||
constexpr std::array<rf::FrequencyRange, 2> band { {
|
||||
{ 2300000000, 2500000000 },
|
||||
{ 2500000000, 2700000000 },
|
||||
} };
|
||||
|
||||
} /* namespace lna */
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
namespace vga {
|
||||
|
||||
constexpr range_t<int8_t> gain_db_range { 0, 62 };
|
||||
constexpr int8_t gain_db_step = 2;
|
||||
|
||||
} /* namespace vga */
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
namespace tx {
|
||||
|
||||
constexpr range_t<int8_t> gain_db_range { 0, 47 };
|
||||
constexpr int8_t gain_db_step = 1;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
namespace filter {
|
||||
|
||||
constexpr std::array<uint32_t, 16> bandwidths {
|
||||
/* Assumption: these values are in ascending order */
|
||||
1750000,
|
||||
2500000, /* Some documentation says 2.25MHz */
|
||||
3500000,
|
||||
5000000,
|
||||
5500000,
|
||||
6000000,
|
||||
7000000,
|
||||
8000000,
|
||||
9000000,
|
||||
10000000,
|
||||
12000000,
|
||||
14000000,
|
||||
15000000,
|
||||
20000000,
|
||||
24000000,
|
||||
28000000,
|
||||
};
|
||||
|
||||
constexpr auto bandwidth_minimum = bandwidths[0];
|
||||
constexpr auto bandwidth_maximum = bandwidths[bandwidths.size() - 1];
|
||||
|
||||
} /* namespace filter */
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
enum Mode {
|
||||
Shutdown,
|
||||
Standby,
|
||||
Receive,
|
||||
Transmit,
|
||||
};
|
||||
|
||||
using reg_t = uint16_t;
|
||||
using address_t = uint8_t;
|
||||
|
||||
class MAX283x {
|
||||
public:
|
||||
virtual ~MAX283x() = default;
|
||||
|
||||
virtual void init();
|
||||
virtual void set_mode(const Mode mode);
|
||||
|
||||
virtual void set_tx_vga_gain(const int_fast8_t db);
|
||||
virtual void set_lna_gain(const int_fast8_t db);
|
||||
virtual void set_vga_gain(const int_fast8_t db);
|
||||
virtual void set_lpf_rf_bandwidth(const uint32_t bandwidth_minimum);
|
||||
|
||||
virtual bool set_frequency(const rf::Frequency lo_frequency);
|
||||
|
||||
virtual void set_rx_lo_iq_calibration(const size_t v);
|
||||
virtual void set_rx_buff_vcm(const size_t v);
|
||||
|
||||
virtual reg_t temp_sense();
|
||||
|
||||
virtual reg_t read(const address_t reg_num);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif/*__MAX283X_H__*/
|
||||
@@ -372,6 +372,10 @@ public:
|
||||
while(device_status() & 0x80);
|
||||
}
|
||||
|
||||
bool plla_loss_of_signal() {
|
||||
return (device_status() >> 5) & 1;
|
||||
}
|
||||
|
||||
bool clkin_loss_of_signal() {
|
||||
return (device_status() >> 4) & 1;
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ int main(void) {
|
||||
portapack::shutdown();
|
||||
}
|
||||
|
||||
m4_init(portapack::spi_flash::image_tag_hackrf, portapack::memory::map::m4_code_hackrf);
|
||||
m4_init(portapack::spi_flash::image_tag_hackrf, portapack::memory::map::m4_code_hackrf, true);
|
||||
m0_halt();
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -381,8 +381,9 @@ static void shutdown_base() {
|
||||
*
|
||||
* PLL0USB = powered down
|
||||
* PLL0AUDIO = GP_CLKIN, Fcco=491.52 MHz, Fout=12.288 MHz
|
||||
* PLL1 = GP_CLKIN * 10 = 200 MHz
|
||||
*
|
||||
* PLL1 =
|
||||
* OG: GP_CLKIN * 10 = 200 MHz
|
||||
* r9: GP_CLKIN * 20 = 200 MHz
|
||||
* IDIVA = IRC / 1 = 12 MHz
|
||||
* IDIVB = PLL1 / 2 = 100 MHz
|
||||
* IDIVC = PLL1 / 1 = 200 MHz
|
||||
@@ -432,9 +433,14 @@ bool init() {
|
||||
*/
|
||||
|
||||
/* Step into the 90-110MHz M4 clock range */
|
||||
/* Fclkin = 40M
|
||||
* /N=2 = 20M = PFDin
|
||||
* Fcco = PFDin * (M=10) = 200M
|
||||
/* OG:
|
||||
* Fclkin = 40M
|
||||
* /N=2 = 20M = PFDin
|
||||
* Fcco = PFDin * (M=10) = 200M
|
||||
* r9:
|
||||
* Fclkin = 10M
|
||||
* /N=1 = 10M = PFDin
|
||||
* Fcco = PFDin * (M=20) = 200M
|
||||
* Fclk = Fcco / (2*(P=1)) = 100M
|
||||
*/
|
||||
cgu::pll1::ctrl({
|
||||
@@ -444,8 +450,8 @@ bool init() {
|
||||
.direct = 0,
|
||||
.psel = 0,
|
||||
.autoblock = 1,
|
||||
.nsel = 1,
|
||||
.msel = 9,
|
||||
.nsel = hackrf_r9 ? 0UL : 1UL,
|
||||
.msel = hackrf_r9 ? 19UL : 9UL,
|
||||
.clk_sel = cgu::CLK_SEL::GP_CLKIN,
|
||||
});
|
||||
|
||||
@@ -474,8 +480,7 @@ bool init() {
|
||||
chThdSleepMilliseconds(10);
|
||||
|
||||
clock_manager.set_reference_ppb(persistent_memory::correction_ppb());
|
||||
clock_manager.enable_first_if_clock();
|
||||
clock_manager.enable_second_if_clock();
|
||||
clock_manager.enable_if_clocks();
|
||||
clock_manager.enable_codec_clocks();
|
||||
radio::init();
|
||||
|
||||
|
||||
@@ -28,12 +28,12 @@
|
||||
#define __ENCODERS_H__
|
||||
|
||||
namespace encoders {
|
||||
|
||||
|
||||
#define ENC_TYPES_COUNT 14
|
||||
#define OOK_SAMPLERATE 2280000U
|
||||
|
||||
|
||||
#define ENCODER_UM3750 8
|
||||
|
||||
|
||||
size_t make_bitstream(std::string& fragments);
|
||||
void bitstream_append(size_t& bitstream_length, uint32_t bit_count, uint32_t bits);
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace encoders {
|
||||
150000, 2,
|
||||
0
|
||||
},
|
||||
|
||||
|
||||
// PT2260-R4
|
||||
{
|
||||
"2260-R4",
|
||||
@@ -77,7 +77,7 @@ namespace encoders {
|
||||
150000, 2,
|
||||
0
|
||||
},
|
||||
|
||||
|
||||
// PT2262
|
||||
{
|
||||
"2262 ",
|
||||
@@ -89,7 +89,7 @@ namespace encoders {
|
||||
20000, 4,
|
||||
0
|
||||
},
|
||||
|
||||
|
||||
// 16-bit ?
|
||||
{
|
||||
"16-bit ",
|
||||
@@ -101,7 +101,7 @@ namespace encoders {
|
||||
25000, 50,
|
||||
0 // ?
|
||||
},
|
||||
|
||||
|
||||
// RT1527
|
||||
{
|
||||
"1527 ",
|
||||
@@ -111,9 +111,9 @@ namespace encoders {
|
||||
24, "SAAAAAAAAAAAAAAAAAAAADDDD",
|
||||
"10000000000000000000000000000000",
|
||||
100000, 4,
|
||||
10 // ?
|
||||
0
|
||||
},
|
||||
|
||||
|
||||
// HK526E
|
||||
{
|
||||
"526E ",
|
||||
@@ -125,7 +125,7 @@ namespace encoders {
|
||||
20000, 4,
|
||||
10 // ?
|
||||
},
|
||||
|
||||
|
||||
// HT12E
|
||||
{
|
||||
"12E ",
|
||||
@@ -137,7 +137,7 @@ namespace encoders {
|
||||
3000, 4,
|
||||
10 // ?
|
||||
},
|
||||
|
||||
|
||||
// VD5026 13 bits ?
|
||||
{
|
||||
"5026 ",
|
||||
@@ -149,7 +149,7 @@ namespace encoders {
|
||||
100000, 4,
|
||||
10 // ?
|
||||
},
|
||||
|
||||
|
||||
// UM3750
|
||||
{
|
||||
"UM3750 ",
|
||||
@@ -161,7 +161,7 @@ namespace encoders {
|
||||
100000, 4,
|
||||
(3 * 12) - 6 // Compensates for pause delay bug in proc_ook
|
||||
},
|
||||
|
||||
|
||||
// UM3758
|
||||
{
|
||||
"UM3758 ",
|
||||
@@ -173,7 +173,7 @@ namespace encoders {
|
||||
160000, 4,
|
||||
10 // ?
|
||||
},
|
||||
|
||||
|
||||
// BA5104
|
||||
{
|
||||
"BA5104 ",
|
||||
@@ -185,7 +185,7 @@ namespace encoders {
|
||||
455000, 4,
|
||||
10 // ?
|
||||
},
|
||||
|
||||
|
||||
// MC145026
|
||||
{
|
||||
"145026 ",
|
||||
@@ -197,7 +197,7 @@ namespace encoders {
|
||||
455000, 2,
|
||||
2
|
||||
},
|
||||
|
||||
|
||||
// HT6*** TODO: Add individual variations
|
||||
{
|
||||
"HT6*** ",
|
||||
@@ -209,7 +209,7 @@ namespace encoders {
|
||||
80000, 3,
|
||||
10 // ?
|
||||
},
|
||||
|
||||
|
||||
// TC9148
|
||||
{
|
||||
"TC9148 ",
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
#include "rffc507x.hpp"
|
||||
#include "max2837.hpp"
|
||||
#include "max2839.hpp"
|
||||
#include "max5864.hpp"
|
||||
#include "baseband_cpld.hpp"
|
||||
|
||||
@@ -52,12 +53,12 @@ static constexpr uint32_t ssp_scr(
|
||||
return static_cast<uint8_t>(pclk_f / cpsr / spi_f - 1);
|
||||
}
|
||||
|
||||
static constexpr SPIConfig ssp_config_max2837 = {
|
||||
static constexpr SPIConfig ssp_config_max283x = {
|
||||
.end_cb = NULL,
|
||||
.ssport = gpio_max2837_select.port(),
|
||||
.sspad = gpio_max2837_select.pad(),
|
||||
.ssport = gpio_max283x_select.port(),
|
||||
.sspad = gpio_max283x_select.pad(),
|
||||
.cr0 =
|
||||
CR0_CLOCKRATE(ssp_scr(ssp1_pclk_f, ssp1_cpsr, max2837_spi_f))
|
||||
CR0_CLOCKRATE(ssp_scr(ssp1_pclk_f, ssp1_cpsr, max283x_spi_f))
|
||||
| CR0_FRFSPI
|
||||
| CR0_DSS16BIT
|
||||
,
|
||||
@@ -78,9 +79,9 @@ static constexpr SPIConfig ssp_config_max5864 = {
|
||||
|
||||
static spi::arbiter::Arbiter ssp1_arbiter(portapack::ssp1);
|
||||
|
||||
static spi::arbiter::Target ssp1_target_max2837 {
|
||||
static spi::arbiter::Target ssp1_target_max283x {
|
||||
ssp1_arbiter,
|
||||
ssp_config_max2837
|
||||
ssp_config_max283x
|
||||
};
|
||||
|
||||
static spi::arbiter::Target ssp1_target_max5864 {
|
||||
@@ -90,16 +91,27 @@ static spi::arbiter::Target ssp1_target_max5864 {
|
||||
|
||||
static rf::path::Path rf_path;
|
||||
rffc507x::RFFC507x first_if;
|
||||
max2837::MAX2837 second_if { ssp1_target_max2837 };
|
||||
max283x::MAX283x *second_if;
|
||||
max2837::MAX2837 second_if_max2837 { ssp1_target_max283x };
|
||||
max2839::MAX2839 second_if_max2839 { ssp1_target_max283x };
|
||||
static max5864::MAX5864 baseband_codec { ssp1_target_max5864 };
|
||||
static baseband::CPLD baseband_cpld;
|
||||
|
||||
static rf::Direction direction { rf::Direction::Receive };
|
||||
static bool baseband_invert = false;
|
||||
static bool mixer_invert = false;
|
||||
|
||||
void init() {
|
||||
if (hackrf_r9) {
|
||||
gpio_r9_not_ant_pwr.write(1);
|
||||
gpio_r9_not_ant_pwr.output();
|
||||
}
|
||||
rf_path.init();
|
||||
first_if.init();
|
||||
second_if.init();
|
||||
second_if = hackrf_r9
|
||||
? (max283x::MAX283x *) &second_if_max2839
|
||||
: (max283x::MAX283x *) &second_if_max2837;
|
||||
second_if->init();
|
||||
baseband_codec.init();
|
||||
baseband_cpld.init();
|
||||
}
|
||||
@@ -121,7 +133,30 @@ void set_direction(const rf::Direction new_direction) {
|
||||
|
||||
direction = new_direction;
|
||||
|
||||
second_if.set_mode((direction == rf::Direction::Transmit) ? max2837::Mode::Transmit : max2837::Mode::Receive);
|
||||
if (hackrf_r9) {
|
||||
/*
|
||||
* HackRF One r9 inverts analog baseband only for RX. Previous hardware
|
||||
* revisions inverted analog baseband for neither direction because of
|
||||
* compensation in the CPLD. If we ever simplify the CPLD to handle RX
|
||||
* and TX the same way, we will need to update this baseband_invert
|
||||
* logic.
|
||||
*/
|
||||
baseband_invert = (direction == rf::Direction::Receive);
|
||||
} else {
|
||||
/*
|
||||
* Analog baseband is inverted in RX but not TX. The RX inversion is
|
||||
* corrected by the CPLD, but future hardware or CPLD changes may
|
||||
* change this for either or both directions. For a given hardware+CPLD
|
||||
* platform, baseband inversion is set here for RX and/or TX. Spectrum
|
||||
* inversion resulting from the mixer is tracked separately according
|
||||
* to the tuning configuration. We ask the CPLD to apply a correction
|
||||
* for the total inversion.
|
||||
*/
|
||||
baseband_invert = false;
|
||||
}
|
||||
baseband_cpld.set_invert(mixer_invert ^ baseband_invert);
|
||||
|
||||
second_if->set_mode((direction == rf::Direction::Transmit) ? max283x::Mode::Transmit : max283x::Mode::Receive);
|
||||
rf_path.set_direction(direction);
|
||||
|
||||
baseband_codec.set_mode((direction == rf::Direction::Transmit) ? max5864::Mode::Transmit : max5864::Mode::Receive);
|
||||
@@ -142,10 +177,11 @@ bool set_tuning_frequency(const rf::Frequency frequency) {
|
||||
first_if.enable();
|
||||
}
|
||||
|
||||
const auto result_second_if = second_if.set_frequency(tuning_config.second_lo_frequency);
|
||||
const auto result_second_if = second_if->set_frequency(tuning_config.second_lo_frequency);
|
||||
|
||||
rf_path.set_band(tuning_config.rf_path_band);
|
||||
baseband_cpld.set_invert(tuning_config.baseband_invert);
|
||||
mixer_invert = tuning_config.mixer_invert;
|
||||
baseband_cpld.set_invert(mixer_invert ^ baseband_invert);
|
||||
|
||||
return result_second_if;
|
||||
} else {
|
||||
@@ -165,19 +201,19 @@ void set_rf_amp(const bool rf_amp) {
|
||||
}
|
||||
|
||||
void set_lna_gain(const int_fast8_t db) {
|
||||
second_if.set_lna_gain(db);
|
||||
second_if->set_lna_gain(db);
|
||||
}
|
||||
|
||||
void set_vga_gain(const int_fast8_t db) {
|
||||
second_if.set_vga_gain(db);
|
||||
second_if->set_vga_gain(db);
|
||||
}
|
||||
|
||||
void set_tx_gain(const int_fast8_t db) {
|
||||
second_if.set_tx_vga_gain(db);
|
||||
second_if->set_tx_vga_gain(db);
|
||||
}
|
||||
|
||||
void set_baseband_filter_bandwidth(const uint32_t bandwidth_minimum) {
|
||||
second_if.set_lpf_rf_bandwidth(bandwidth_minimum);
|
||||
second_if->set_lpf_rf_bandwidth(bandwidth_minimum);
|
||||
}
|
||||
|
||||
void set_baseband_rate(const uint32_t rate) {
|
||||
@@ -186,13 +222,17 @@ void set_baseband_rate(const uint32_t rate) {
|
||||
|
||||
void set_antenna_bias(const bool on) {
|
||||
/* Pull MOSFET gate low to turn on antenna bias. */
|
||||
first_if.set_gpo1(on ? 0 : 1);
|
||||
if (hackrf_r9) {
|
||||
gpio_r9_not_ant_pwr.write(on ? 0 : 1);
|
||||
} else {
|
||||
first_if.set_gpo1(on ? 0 : 1);
|
||||
}
|
||||
}
|
||||
|
||||
void disable() {
|
||||
set_antenna_bias(false);
|
||||
baseband_codec.set_mode(max5864::Mode::Shutdown);
|
||||
second_if.set_mode(max2837::Mode::Standby);
|
||||
second_if->set_mode(max2837::Mode::Standby);
|
||||
first_if.disable();
|
||||
set_rf_amp(false);
|
||||
|
||||
@@ -227,11 +267,11 @@ uint32_t register_read(const size_t register_number) {
|
||||
namespace second_if {
|
||||
|
||||
uint32_t register_read(const size_t register_number) {
|
||||
return radio::second_if.read(register_number);
|
||||
return radio::second_if->read(register_number);
|
||||
}
|
||||
|
||||
uint8_t temp_sense() {
|
||||
return radio::second_if.temp_sense() & 0x1f;
|
||||
return radio::second_if->temp_sense() & 0x1f;
|
||||
}
|
||||
|
||||
} /* namespace second_if */
|
||||
|
||||
@@ -313,3 +313,12 @@ size_t ReceiverModel::wfm_configuration() const {
|
||||
void ReceiverModel::update_wfm_configuration() {
|
||||
wfm_configs[wfm_config_index].apply();
|
||||
}
|
||||
|
||||
void ReceiverModel::set_configuration_without_init(const Mode new_mode, const rf::Frequency new_frequency_step, const size_t new_am_config_index, const size_t new_nbfm_config_index, const size_t new_wfm_config_index, uint8_t new_squelch_level) {
|
||||
mode_ = new_mode;
|
||||
frequency_step_ = new_frequency_step;
|
||||
am_config_index = new_am_config_index;
|
||||
nbfm_config_index = new_nbfm_config_index;
|
||||
wfm_config_index = new_wfm_config_index;
|
||||
squelch_level_ = new_squelch_level;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
#include "message.hpp"
|
||||
#include "rf_path.hpp"
|
||||
#include "max2837.hpp"
|
||||
#include "max283x.hpp"
|
||||
#include "volume.hpp"
|
||||
|
||||
class ReceiverModel {
|
||||
@@ -87,12 +87,14 @@ public:
|
||||
size_t wfm_configuration() const;
|
||||
void set_wfm_configuration(const size_t n);
|
||||
|
||||
void set_configuration_without_init(const Mode new_mode, const rf::Frequency new_frequency_step, const size_t new_am_config_index, const size_t new_nbfm_config_index, const size_t new_wfm_config_index, uint8_t new_squelch_level);
|
||||
|
||||
private:
|
||||
rf::Frequency frequency_step_ { 25000 };
|
||||
bool enabled_ { false };
|
||||
bool rf_amp_ { false };
|
||||
int32_t lna_gain_db_ { 32 };
|
||||
uint32_t baseband_bandwidth_ { max2837::filter::bandwidth_minimum };
|
||||
uint32_t baseband_bandwidth_ { max283x::filter::bandwidth_minimum };
|
||||
int32_t vga_gain_db_ { 32 };
|
||||
int32_t tx_gain_db_ { 47 };
|
||||
Mode mode_ { Mode::NarrowbandFMAudio };
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
*/
|
||||
|
||||
#include "rf_path.hpp"
|
||||
#include "platform.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <initializer_list>
|
||||
@@ -34,14 +35,12 @@ namespace path {
|
||||
|
||||
namespace {
|
||||
|
||||
using GPIOs = std::array<GPIO, 13>;
|
||||
using GPIOs = std::array<GPIO, 11>;
|
||||
|
||||
/* TODO: ARM GCC 4.8 2014q3 doesn't like this array inside struct Config.
|
||||
* No idea why.
|
||||
*/
|
||||
constexpr GPIOs gpios {
|
||||
gpio_tx,
|
||||
gpio_rx,
|
||||
gpio_mix_bypass,
|
||||
gpio_not_mix_bypass,
|
||||
gpio_tx_mix_bp,
|
||||
@@ -122,15 +121,39 @@ struct Config {
|
||||
}
|
||||
|
||||
static void gpio_init() {
|
||||
for(auto gpio : gpios) {
|
||||
if (hackrf_r9) {
|
||||
gpio_r9_rx.output();
|
||||
} else {
|
||||
gpio_og_tx.output();
|
||||
gpio_og_rx.output();
|
||||
}
|
||||
for (auto gpio : gpios) {
|
||||
gpio.output();
|
||||
}
|
||||
}
|
||||
|
||||
void apply() const {
|
||||
/* NOTE: Assumes order in gpios[] and Config bitfield match. */
|
||||
for(size_t n=0; n<gpios.size(); n++) {
|
||||
gpios[n].write((*this)[n]);
|
||||
/* NOTE: Assumes order in gpios[] and Config bitfield match,
|
||||
* after the 'tx' and 'rx' fields which are handled specially. */
|
||||
for (size_t n = 0; n < gpios.size() + 2; n++) {
|
||||
bool value = (*this)[n];
|
||||
switch (n) {
|
||||
case 0:
|
||||
if (!hackrf_r9) {
|
||||
gpio_og_tx.write(value);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if (hackrf_r9) {
|
||||
gpio_r9_rx.write(value);
|
||||
} else {
|
||||
gpio_og_rx.write(value);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
gpios[n - 2].write(value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -39,8 +39,8 @@ enum class Direction {
|
||||
|
||||
namespace path {
|
||||
|
||||
constexpr FrequencyRange band_low { 0, 2150000000 };
|
||||
constexpr FrequencyRange band_high { 2750000000, 7250000000 };
|
||||
constexpr FrequencyRange band_low { 0, 2170000000 };
|
||||
constexpr FrequencyRange band_high { 2740000000, 7250000000 };
|
||||
constexpr FrequencyRange band_mid { band_low.maximum, band_high.minimum };
|
||||
|
||||
enum class Band {
|
||||
|
||||
@@ -41,7 +41,7 @@ constexpr rf::Frequency high_band_second_lo_regions_2_and_3(const rf::Frequency
|
||||
|
||||
constexpr rf::Frequency high_band_second_lo_frequency(const rf::Frequency target_frequency) {
|
||||
return (target_frequency < 3600000000)
|
||||
? (2150000000 + (((target_frequency - 2750000000) * 60) / 85))
|
||||
? (2170000000 + (((target_frequency - 2740000000) * 57) / 86))
|
||||
: high_band_second_lo_regions_2_and_3(target_frequency)
|
||||
;
|
||||
}
|
||||
@@ -49,8 +49,8 @@ constexpr rf::Frequency high_band_second_lo_frequency(const rf::Frequency target
|
||||
Config low_band(const rf::Frequency target_frequency) {
|
||||
const rf::Frequency first_lo_frequency = target_frequency + low_band_second_lo_frequency(target_frequency);
|
||||
const rf::Frequency second_lo_frequency = first_lo_frequency - target_frequency;
|
||||
const bool baseband_invert = true;
|
||||
return { first_lo_frequency, second_lo_frequency, rf::path::Band::Low, baseband_invert };
|
||||
const bool mixer_invert = true;
|
||||
return { first_lo_frequency, second_lo_frequency, rf::path::Band::Low, mixer_invert };
|
||||
}
|
||||
|
||||
Config mid_band(const rf::Frequency target_frequency) {
|
||||
@@ -60,8 +60,8 @@ Config mid_band(const rf::Frequency target_frequency) {
|
||||
Config high_band(const rf::Frequency target_frequency) {
|
||||
const rf::Frequency first_lo_frequency = target_frequency - high_band_second_lo_frequency(target_frequency);
|
||||
const rf::Frequency second_lo_frequency = target_frequency - first_lo_frequency;
|
||||
const bool baseband_invert = false;
|
||||
return { first_lo_frequency, second_lo_frequency, rf::path::Band::High, baseband_invert };
|
||||
const bool mixer_invert = false;
|
||||
return { first_lo_frequency, second_lo_frequency, rf::path::Band::High, mixer_invert };
|
||||
}
|
||||
|
||||
} /* namespace */
|
||||
|
||||
@@ -33,7 +33,7 @@ struct Config {
|
||||
) : first_lo_frequency(0),
|
||||
second_lo_frequency(0),
|
||||
rf_path_band(rf::path::Band::Mid),
|
||||
baseband_invert(false)
|
||||
mixer_invert(false)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -41,11 +41,11 @@ struct Config {
|
||||
rf::Frequency first_lo_frequency,
|
||||
rf::Frequency second_lo_frequency,
|
||||
rf::path::Band rf_path_band,
|
||||
bool baseband_invert
|
||||
bool mixer_invert
|
||||
) : first_lo_frequency(first_lo_frequency),
|
||||
second_lo_frequency(second_lo_frequency),
|
||||
rf_path_band(rf_path_band),
|
||||
baseband_invert(baseband_invert)
|
||||
mixer_invert(mixer_invert)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ struct Config {
|
||||
const rf::Frequency first_lo_frequency;
|
||||
const rf::Frequency second_lo_frequency;
|
||||
const rf::path::Band rf_path_band;
|
||||
const bool baseband_invert;
|
||||
const bool mixer_invert;
|
||||
};
|
||||
|
||||
Config create(const rf::Frequency target_frequency);
|
||||
|
||||
@@ -28,7 +28,7 @@ using namespace portapack;
|
||||
|
||||
#include "string_format.hpp"
|
||||
|
||||
#include "max2837.hpp"
|
||||
#include "max283x.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
@@ -334,8 +334,8 @@ LNAGainField::LNAGainField(
|
||||
Point parent_pos
|
||||
) : NumberField {
|
||||
parent_pos, 2,
|
||||
{ max2837::lna::gain_db_range.minimum, max2837::lna::gain_db_range.maximum },
|
||||
max2837::lna::gain_db_step,
|
||||
{ max283x::lna::gain_db_range.minimum, max283x::lna::gain_db_range.maximum },
|
||||
max283x::lna::gain_db_step,
|
||||
' ',
|
||||
}
|
||||
{
|
||||
@@ -359,8 +359,8 @@ VGAGainField::VGAGainField(
|
||||
Point parent_pos
|
||||
) : NumberField {
|
||||
parent_pos, 2,
|
||||
{ max2837::vga::gain_db_range.minimum, max2837::vga::gain_db_range.maximum },
|
||||
max2837::vga::gain_db_step,
|
||||
{ max283x::vga::gain_db_range.minimum, max283x::vga::gain_db_range.maximum },
|
||||
max283x::vga::gain_db_step,
|
||||
' ',
|
||||
}
|
||||
{
|
||||
|
||||
@@ -44,57 +44,118 @@ inline void OOKProcessor::write_sample(const buffer_c8_t& buffer, uint8_t bit_va
|
||||
buffer.p[i] = {re, im};
|
||||
}
|
||||
|
||||
inline void OOKProcessor::duval_algo(const buffer_c8_t& buffer) {
|
||||
bool OOKProcessor::scan_init(unsigned int order) {
|
||||
if (order > MAX_DE_BRUIJN_ORDER)
|
||||
return false;
|
||||
|
||||
scan_done = false;
|
||||
scan_progress = 0;
|
||||
|
||||
k = 0;
|
||||
idx = 1;
|
||||
|
||||
duval_symbols = 2; // 2 for binary, 3 for ternary encoders
|
||||
duval_length = 0;
|
||||
duval_bit = 0;
|
||||
duval_sample_bit = 0;
|
||||
duval_symbol = 0;
|
||||
|
||||
memset(v, 0, sizeof(v));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OOKProcessor::scan_encode(const buffer_c8_t& buffer, size_t& buf_ptr) {
|
||||
// encode data: 0 = 1000, 1 = 1110
|
||||
// @TODO: make this user-configurable
|
||||
const uint8_t sym[] = { 0b0001, 0b0111 };
|
||||
constexpr auto symbol_length = 4;
|
||||
|
||||
// iterate over every symbol in the sequence and convert it to bits with required bitrate
|
||||
for (; duval_bit < duval_length; duval_bit++) {
|
||||
auto val = v_tmp[duval_bit];
|
||||
for (; duval_symbol < symbol_length; duval_symbol++) {
|
||||
auto s = sym[val] & (1 << duval_symbol);
|
||||
for (; duval_sample_bit < samples_per_bit; duval_sample_bit++) {
|
||||
if (buf_ptr >= buffer.count) {
|
||||
// buffer is full - continue next time
|
||||
txprogress_message.done = false;
|
||||
txprogress_message.progress = scan_progress++;
|
||||
shared_memory.application_queue.push(txprogress_message);
|
||||
return false;
|
||||
}
|
||||
write_sample(buffer, s, buf_ptr++);
|
||||
}
|
||||
duval_sample_bit = 0;
|
||||
}
|
||||
duval_symbol = 0;
|
||||
}
|
||||
duval_bit = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline size_t OOKProcessor::duval_algo_step() {
|
||||
size_t buf_ptr = 0;
|
||||
const unsigned int w = de_bruijn_length;
|
||||
|
||||
// Duval's algorithm for generating de Bruijn sequence
|
||||
while (idx) {
|
||||
if (w % idx == 0) {
|
||||
for (; k < idx; k++) {
|
||||
size_t available_size = buffer.count - buf_ptr;
|
||||
size_t len = (samples_per_bit > available_size) ? available_size : samples_per_bit;
|
||||
|
||||
for (; bit_ptr < len; bit_ptr++) {
|
||||
write_sample(buffer, v[k], buf_ptr);
|
||||
buf_ptr++;
|
||||
}
|
||||
|
||||
if (buf_ptr == buffer.count) {
|
||||
txprogress_message.done = false;
|
||||
txprogress_message.progress = scan_progress++;
|
||||
shared_memory.application_queue.push(txprogress_message);
|
||||
return;
|
||||
}
|
||||
|
||||
bit_ptr = 0;
|
||||
}
|
||||
|
||||
for (unsigned int k = 0; k < idx; k++)
|
||||
v_tmp[buf_ptr++] = v[k];
|
||||
k = 0;
|
||||
}
|
||||
|
||||
for (unsigned int j = 0; j < w - idx; j++)
|
||||
v[idx + j] = v[j];
|
||||
|
||||
for (idx = w; idx > 0 && v[idx - 1]; idx--) ;
|
||||
for (idx = w; (idx > 0) && (v[idx - 1] >= duval_symbols - 1); idx--) ;
|
||||
|
||||
if (idx)
|
||||
v[idx - 1] = 1;
|
||||
}
|
||||
v[idx - 1]++;
|
||||
|
||||
// clear the buffer in case we have any bytes left
|
||||
if (buf_ptr < buffer.count) {
|
||||
for (size_t i = buf_ptr; i < buffer.count; i++) {
|
||||
buffer.p[i] = {0, 0};
|
||||
if (buf_ptr) {
|
||||
// we fill at most de_bruijn_length number of elements
|
||||
return buf_ptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (!scan_done) {
|
||||
txprogress_message.done = true;
|
||||
shared_memory.application_queue.push(txprogress_message);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
scan_done = 1;
|
||||
void OOKProcessor::scan_process(const buffer_c8_t& buffer) {
|
||||
size_t buf_ptr = 0;
|
||||
|
||||
// transmit any leftover bits from previous step
|
||||
if (!scan_encode(buffer, buf_ptr))
|
||||
return;
|
||||
|
||||
while (1) {
|
||||
// calculate next chunk of deBruijn sequence
|
||||
duval_length = duval_algo_step();
|
||||
|
||||
if (duval_length == 0) {
|
||||
// last chunk - done
|
||||
if (!scan_done) {
|
||||
txprogress_message.done = true;
|
||||
shared_memory.application_queue.push(txprogress_message);
|
||||
}
|
||||
scan_done = 1;
|
||||
|
||||
// clear the remaining buffer in case we have any bytes left
|
||||
for (size_t i = buf_ptr; i < buffer.count; i++)
|
||||
buffer.p[i] = { 0, 0 };
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
duval_bit = 0;
|
||||
duval_sample_bit = 0;
|
||||
duval_symbol = 0;
|
||||
|
||||
// encode the sequence into required format
|
||||
if (!scan_encode(buffer, buf_ptr))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void OOKProcessor::execute(const buffer_c8_t& buffer) {
|
||||
@@ -103,7 +164,7 @@ void OOKProcessor::execute(const buffer_c8_t& buffer) {
|
||||
if (!configured) return;
|
||||
|
||||
if (de_bruijn_length) {
|
||||
duval_algo(buffer);
|
||||
scan_process(buffer);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -175,22 +236,8 @@ void OOKProcessor::on_message(const Message* const p) {
|
||||
}
|
||||
|
||||
if (de_bruijn_length) {
|
||||
if (de_bruijn_length > sizeof(v)) {
|
||||
if (!scan_init(de_bruijn_length))
|
||||
return;
|
||||
}
|
||||
|
||||
if (samples_per_bit > 2048) {
|
||||
// can't handle more than dma::transfer_samples
|
||||
return;
|
||||
}
|
||||
|
||||
k = 0;
|
||||
bit_ptr = 0;
|
||||
idx = 1;
|
||||
scan_done = false;
|
||||
scan_progress = 0;
|
||||
|
||||
memset(v, 0, sizeof(v));
|
||||
} else {
|
||||
samples_per_bit /= 10;
|
||||
}
|
||||
|
||||
@@ -46,9 +46,9 @@ private:
|
||||
uint32_t pause_counter { 0 };
|
||||
uint8_t repeat_counter { 0 };
|
||||
uint8_t s { 0 };
|
||||
uint16_t bit_pos { 0 };
|
||||
uint8_t cur_bit { 0 };
|
||||
uint32_t sample_count { 0 };
|
||||
uint16_t bit_pos { 0 };
|
||||
uint8_t cur_bit { 0 };
|
||||
uint32_t sample_count { 0 };
|
||||
uint32_t tone_phase { 0 }, phase { 0 }, sphase { 0 };
|
||||
int32_t tone_sample { 0 }, sig { 0 }, frq { 0 };
|
||||
|
||||
@@ -56,14 +56,22 @@ private:
|
||||
|
||||
static constexpr auto MAX_DE_BRUIJN_ORDER = 24;
|
||||
uint8_t v[MAX_DE_BRUIJN_ORDER];
|
||||
uint8_t v_tmp[MAX_DE_BRUIJN_ORDER];
|
||||
unsigned int idx { 0 };
|
||||
unsigned int k { 0 };
|
||||
size_t bit_ptr{ 0 };
|
||||
unsigned int duval_symbols { 0 };
|
||||
unsigned int duval_length { 0 };
|
||||
unsigned int duval_bit { 0 };
|
||||
unsigned int duval_sample_bit { 0 };
|
||||
unsigned int duval_symbol { 0 };
|
||||
size_t scan_progress{ 0 };
|
||||
uint8_t scan_done { true };
|
||||
|
||||
size_t duval_algo_step();
|
||||
void scan_process(const buffer_c8_t& buffer);
|
||||
bool scan_init(unsigned int order);
|
||||
bool scan_encode(const buffer_c8_t& buffer, size_t& buf_ptr);
|
||||
void write_sample(const buffer_c8_t& buffer, uint8_t bit_value, size_t i);
|
||||
void duval_algo(const buffer_c8_t& buffer);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -105,22 +105,25 @@ void SpectrumCollector::post_message(const buffer_c16_t& data) {
|
||||
|
||||
template<typename T>
|
||||
static typename T::value_type spectrum_window_none(const T& s, const size_t i) {
|
||||
static_assert(power_of_two(ARRAY_ELEMENTS(s)), "Array number of elements must be power of 2"); // c/m compile error GCC10 , OK for all GCC versions.
|
||||
constexpr size_t length = sizeof(s)/sizeof(s[0]);
|
||||
static_assert(power_of_two(length), "Array length must be power of 2");
|
||||
return s[i];
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
static typename T::value_type spectrum_window_hamming_3(const T& s, const size_t i) {
|
||||
static_assert(power_of_two(ARRAY_ELEMENTS(s)), "Array number of elements must be power of 2"); // c/m compile error GCC10 , OK for all GCC versions.
|
||||
const size_t mask = s.size() - 1; // c/m compile error GCC10 , constexpr->const
|
||||
constexpr size_t length = sizeof(s)/sizeof(s[0]);
|
||||
static_assert((length), "Array length must be power of 2");
|
||||
constexpr size_t mask = length - 1;
|
||||
// Three point Hamming window.
|
||||
return s[i] * 0.54f + (s[(i-1) & mask] + s[(i+1) & mask]) * -0.23f;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
static typename T::value_type spectrum_window_blackman_3(const T& s, const size_t i) {
|
||||
static_assert(power_of_two(ARRAY_ELEMENTS(s)), "Array number of elements must be power of 2"); // c/m compile error GCC10 , OK for all GCC versions.
|
||||
const size_t mask = s.size() - 1; // c/m compile error GCC10 , constexpr->const
|
||||
constexpr size_t length = sizeof(s)/sizeof(s[0]);
|
||||
static_assert(power_of_two(length), "Array length must be power of 2");
|
||||
constexpr size_t mask = length - 1;
|
||||
// Three term Blackman window.
|
||||
constexpr float alpha = 0.42f;
|
||||
constexpr float beta = 0.5f * 0.5f;
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
|
||||
#include <array>
|
||||
|
||||
bool hackrf_r9;
|
||||
|
||||
#if HAL_USE_PAL || defined(__DOXYGEN__)
|
||||
/**
|
||||
* @brief PAL setup.
|
||||
@@ -56,9 +58,9 @@ const PALConfig pal_default_config = {
|
||||
| (0 << 12) // P1_17: SGPIO11, HOST_DIRECTION
|
||||
| (1 << 11) // P1_4: SSP1_MOSI
|
||||
| (1 << 10) // P1_3: SSP1_MISO
|
||||
| (0 << 9) // P1_2: 10K PD, BOOT1
|
||||
| (1 << 8) // P1_1: 10K PU, BOOT0
|
||||
| (1 << 7) // P2_7: 10K PU, ISP
|
||||
| (0 << 9) // P1_2: Varies by revision, float until detection
|
||||
| (0 << 8) // P1_1: Varies by revision, float until detection
|
||||
| (0 << 7) // P2_7: Varies by revision, float until detection
|
||||
| (0 << 6) // P3_6: SPIFI_MISO
|
||||
| (1 << 5) // P6_6: SGPIO5, HOST_DATA5
|
||||
| (1 << 4) // P1_0: SGPIO7, HOST_DATA7
|
||||
@@ -74,9 +76,9 @@ const PALConfig pal_default_config = {
|
||||
| (0 << 12) // P1_17: SGPIO11, HOST_DIRECTION
|
||||
| (0 << 11) // P1_4: SSP1_MOSI
|
||||
| (0 << 10) // P1_3: SSP1_MISO
|
||||
| (0 << 9) // P1_2: 10K PD, BOOT1
|
||||
| (0 << 8) // P1_1: 10K PU, BOOT0
|
||||
| (0 << 7) // P2_7: 10K PU, ISP
|
||||
| (0 << 9) // P1_2: Varies by revision, float until detection
|
||||
| (0 << 8) // P1_1: Varies by revision, float until detection
|
||||
| (0 << 7) // P2_7: Varies by revision, float until detection
|
||||
| (0 << 6) // P3_6: SPIFI_MISO
|
||||
| (0 << 5) // P6_6: SGPIO5, HOST_DATA5
|
||||
| (0 << 4) // P1_0: SGPIO7, HOST_DATA7
|
||||
@@ -130,12 +132,12 @@ const PALConfig pal_default_config = {
|
||||
| (1 << 12) // P5_3: RX_MIX_BP
|
||||
| (0 << 11) // P5_2: TX_MIX_BP
|
||||
| (0 << 10) // P5_1: LP
|
||||
| (1 << 9) // P5_0: !VAA_ENABLE
|
||||
| (0 << 9) // P5_0: Varies by revision, float until detection
|
||||
| (0 << 8) // P6_12: LED3 (TX)
|
||||
| (1 << 7) // P5_7: CS_AD
|
||||
| (0 << 6) // P4_6: XCVR_EN, 10K PD
|
||||
| (0 << 5) // P4_5: RXENABLE
|
||||
| (0 << 4) // P4_4: TXENABLE
|
||||
| (0 << 4) // P4_4: Varies by revision, float until detection
|
||||
| (1 << 3) // P4_3: SGPIO9, HOST_CAPTURE
|
||||
| (0 << 2) // P4_2: LED2 (RX)
|
||||
| (0 << 1) // P4_1: LED1 (USB)
|
||||
@@ -148,12 +150,12 @@ const PALConfig pal_default_config = {
|
||||
| (1 << 12) // P5_3: RX_MIX_BP
|
||||
| (1 << 11) // P5_2: TX_MIX_BP
|
||||
| (1 << 10) // P5_1: LP
|
||||
| (1 << 9) // P5_0: !VAA_ENABLE
|
||||
| (0 << 9) // P5_0: Varies by revision, float until detection
|
||||
| (1 << 8) // P6_12: LED3 (TX)
|
||||
| (1 << 7) // P5_7: CS_AD
|
||||
| (1 << 6) // P4_6: XCVR_EN, 10K PD
|
||||
| (1 << 5) // P4_5: RXENABLE
|
||||
| (1 << 4) // P4_4: TXENABLE
|
||||
| (0 << 4) // P4_4: Varies by revision, float until detection
|
||||
| (0 << 3) // P4_3: SGPIO9, HOST_CAPTURE
|
||||
| (1 << 2) // P4_2: LED2 (RX)
|
||||
| (1 << 1) // P4_1: LED1 (USB)
|
||||
@@ -170,7 +172,7 @@ const PALConfig pal_default_config = {
|
||||
| (1 << 9) // P7_1: PortaPack GPIO3_9(IO)
|
||||
| (1 << 8) // P7_0: PortaPack GPIO3_8(IO)
|
||||
| (1 << 7) // P6_11: VREGMODE
|
||||
| (0 << 6) // P6_10: EN1V8, 10K PD
|
||||
| (0 << 6) // P6_10: Varies by revision, float until detection
|
||||
| (1 << 5) // P6_9: !TX_AMP_PWR, 10K PU
|
||||
| (1 << 4) // P6_5: HackRF CPLD.TMS(I) (output only when needed, pull-up internal to CPLD when 1V8 present)
|
||||
| (1 << 3) // P6_4: MIXER_SDATA
|
||||
@@ -188,7 +190,7 @@ const PALConfig pal_default_config = {
|
||||
| (0 << 9) // P7_1: PortaPack GPIO3_9(IO)
|
||||
| (0 << 8) // P7_0: PortaPack GPIO3_8(IO)
|
||||
| (1 << 7) // P6_11: VREGMODE
|
||||
| (1 << 6) // P6_10: EN1V8, 10K PD
|
||||
| (0 << 6) // P6_10: Varies by revision, float until detection
|
||||
| (1 << 5) // P6_9: !TX_AMP_PWR, 10K PU
|
||||
| (0 << 4) // P6_5: HackRF CPLD.TMS(I) (output only when needed, pull-up internal to CPLD when 1V8 present)
|
||||
| (0 << 3) // P6_4: MIXER_SDATA
|
||||
@@ -207,17 +209,17 @@ const PALConfig pal_default_config = {
|
||||
.data
|
||||
= (1 << 18) // P9:5: HackRF CPLD.TDO(O) (input with pull up)
|
||||
| (1 << 16) // P6_8: MIX_BYPASS
|
||||
| (0 << 15) // P6_7: TX
|
||||
| (0 << 15) // P6_7: Varies by revision, float until detection
|
||||
| (1 << 14) // P4_10: SGPIO15, CPLD (unused)
|
||||
| (1 << 13) // P4_9: SGPIO14, CPLD (unused)
|
||||
| (0 << 12) // P4_8: SGPIO13, HOST_SYNC_EN
|
||||
| (0 << 12) // P4_8: Varies by revision, float until detection
|
||||
| (1 << 11) // P3_8: SPIFI_CS
|
||||
| (1 << 10) // P3_7: SPIFI_MOSI
|
||||
| (1 << 9) // P3_2: I2S0_RX_SDA
|
||||
| (1 << 8) // P3_1: I2S0_RX_WS
|
||||
| (0 << 7) // P2_8: BOOT2
|
||||
| (0 << 6) // P2_6: MIXER_SCLK
|
||||
| (1 << 5) // P2_5: RX
|
||||
| (0 << 5) // P2_5: Varies by revision, float until detection
|
||||
| (1 << 4) // P2_4: PortaPack LCD_RDX
|
||||
| (0 << 3) // P2_3: PortaPack LCD_TE
|
||||
| (1 << 2) // P2_2: SGPIO6, HOST_DATA6
|
||||
@@ -227,17 +229,17 @@ const PALConfig pal_default_config = {
|
||||
.dir
|
||||
= (0 << 18) // P9_5: HackRF CPLD.TDO(O) (input with pull up)
|
||||
| (1 << 16) // P6_8: MIX_BYPASS
|
||||
| (1 << 15) // P6_7: TX
|
||||
| (0 << 15) // P6_7: Varies by revision, float until detection
|
||||
| (0 << 14) // P4_10: SGPIO15, CPLD (unused)
|
||||
| (0 << 13) // P4_9: SGPIO14, CPLD (unused)
|
||||
| (0 << 12) // P4_8: SGPIO13, HOST_SYNC_EN
|
||||
| (0 << 12) // P4_8: Varies by revision, float until detection
|
||||
| (0 << 11) // P3_8: SPIFI_CS
|
||||
| (0 << 10) // P3_7: SPIFI_MOSI
|
||||
| (0 << 9) // P3_2: I2S0_RX_SDA
|
||||
| (0 << 8) // P3_1: I2S0_RX_WS
|
||||
| (0 << 7) // P2_8: BOOT2
|
||||
| (0 << 6) // P2_6: MIXER_SCLK
|
||||
| (1 << 5) // P2_5: RX
|
||||
| (0 << 5) // P2_5: Varies by revision, float until detection
|
||||
| (0 << 4) // P2_4: PortaPack LCD_RDX
|
||||
| (0 << 3) // P2_3: PortaPack LCD_TE
|
||||
| (0 << 2) // P2_2: SGPIO6, HOST_DATA6
|
||||
@@ -265,8 +267,6 @@ const PALConfig pal_default_config = {
|
||||
{ 6, 12, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* LED3: LED3.A(I) */
|
||||
|
||||
/* Power control */
|
||||
{ 5, 0, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* !VAA_ENABLE: 10K PU, Q3.G(I), power to VAA */
|
||||
{ 6, 10, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* EN1V8/P70: 10K PD, TPS62410.EN2(I), 1V8LED.A(I) */
|
||||
{ 6, 11, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* VREGMODE/P69: TPS62410.MODE/DATA(I) */
|
||||
|
||||
/* HackRF: I2C0 */
|
||||
@@ -297,13 +297,11 @@ const PALConfig pal_default_config = {
|
||||
{ 1, 7, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* !MIX_BYPASS/P35: U1.VCTL1(I), U11.VCTL2(I), U9.V2(I) */
|
||||
{ 1, 19, scu_config_normal_drive_t { .mode=1, .epd=0, .epun=0, .ehs=0, .ezi=0, .zif=0 } }, /* SSP1_SCK/P39: MAX2837.SCLK(I), MAX5864.SCLK(I) */
|
||||
{ 1, 20, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* CS_XCVR/P53: MAX2837.CS(I) */
|
||||
{ 2, 5, scu_config_normal_drive_t { .mode=4, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* RX/P43: U7.VCTL1(I), U10.VCTL1(I), U2.VCTL1(I) */
|
||||
{ 2, 6, scu_config_normal_drive_t { .mode=4, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* MIXER_SCLK/P31: 33pF, RFFC5072.SCLK(I) */
|
||||
{ 2, 10, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* AMP_BYPASS/P50: U14.V2(I), U12.V2(I) */
|
||||
{ 2, 11, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* RX_AMP/P49: U12.V1(I), U14.V3(I) */
|
||||
{ 2, 12, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* !RX_AMP_PWR/P52: 10K PU, Q1.G(I), power to U13 (RX amp) */
|
||||
{ 4, 0, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* HP/P44: U6.VCTL1(I), U5.VCTL2(I) */
|
||||
{ 4, 4, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* TXENABLE/P55: MAX2837.TXENABLE(I) */
|
||||
{ 4, 5, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* RXENABLE/P56: MAX2837.RXENABLE(I) */
|
||||
{ 4, 6, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* XCVR_EN: 10K PD, MAX2837.ENABLE(I) */
|
||||
{ 5, 1, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* LP/P45: U6.VCTL2(I), U5.VCTL1(I) */
|
||||
@@ -314,7 +312,6 @@ const PALConfig pal_default_config = {
|
||||
{ 5, 6, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* TX_AMP/P48: U12.V3(I), U14.V1(I) */
|
||||
{ 5, 7, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* CS_AD/P54: MAX5864.CS(I) */
|
||||
{ 6, 4, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=0, .ehs=0, .ezi=1, .zif=0 } }, /* MIXER_SDATA/P27: 33pF, RFFC5072.SDATA(IO) */
|
||||
{ 6, 7, scu_config_normal_drive_t { .mode=4, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* TX/P42: U7.VCTL2(I), U10.VCTL2(I), U2.VCTL2(I) */
|
||||
{ 6, 8, scu_config_normal_drive_t { .mode=4, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* MIX_BYPASS/P34: U1.VCTL2(I), U11.VCTL1(I) */
|
||||
{ 6, 9, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* !TX_AMP_PWR/P51: 10K PU, Q2.G(I), power to U25 (TX amp) */
|
||||
|
||||
@@ -332,7 +329,6 @@ const PALConfig pal_default_config = {
|
||||
{ 1, 14, scu_config_normal_drive_t { .mode=6, .epd=0, .epun=0, .ehs=1, .ezi=0, .zif=0 } }, /* SGPIO10/P78/BANK2F3M8: CPLD.76/HOST_DISABLE(I) */
|
||||
{ 1, 17, scu_config_normal_drive_t { .mode=6, .epd=1, .epun=1, .ehs=1, .ezi=0, .zif=0 } }, /* SGPIO11/P79/BANK2F3M11: CPLD.71/HOST_DIRECTION(I) */
|
||||
{ 1, 18, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* SGPIO12/BANK2F3M12: CPLD.70/HOST_INVERT(I) */
|
||||
{ 4, 8, scu_config_normal_drive_t { .mode=4, .epd=1, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* SGPIO13/BANK2F3M2: CPLD.90/HOST_SYNC_EN(I) */
|
||||
{ 4, 9, scu_config_normal_drive_t { .mode=4, .epd=0, .epun=0, .ehs=0, .ezi=0, .zif=0 } }, /* SGPIO14/BANK2F3M4: CPLD.81/CPLD_P81 */
|
||||
{ 4, 10, scu_config_normal_drive_t { .mode=4, .epd=0, .epun=0, .ehs=0, .ezi=0, .zif=0 } }, /* SGPIO15/BANK2F3M6: CPLD.78/CPLD_P78 */
|
||||
|
||||
@@ -346,14 +342,165 @@ const PALConfig pal_default_config = {
|
||||
{ 1, 5, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=0, .ehs=0, .ezi=1, .zif=0 } }, /* SD_POW: PortaPack CPLD.TDO(O) */
|
||||
{ 1, 8, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=0, .ehs=0, .ezi=0, .zif=0 } }, /* SD_VOLT0: PortaPack CPLD.TMS(I) */
|
||||
|
||||
/* Miscellaneous */
|
||||
{ 6, 0, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=0, .ehs=0, .ezi=0, .zif=0 } }, /* I2S0_RX_MCLK: Unused */
|
||||
{ 15, 4, scu_config_normal_drive_t { .mode=7, .epd=0, .epun=0, .ehs=0, .ezi=0, .zif=0 } }, /* I2S0_RX_SCK: Unused */
|
||||
|
||||
/* Usage of these pins varies by revision, float until detection */
|
||||
{ 1, 1, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } },
|
||||
{ 1, 2, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } },
|
||||
{ 2, 5, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } },
|
||||
{ 2, 7, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } },
|
||||
{ 4, 4, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } },
|
||||
{ 4, 8, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } },
|
||||
{ 5, 0, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } },
|
||||
{ 6, 7, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } },
|
||||
{ 6, 10, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } },
|
||||
}
|
||||
};
|
||||
|
||||
/* Additional GPIO configuration for HackRF OG */
|
||||
static const std::array<gpio_setup_t, 6> gpio_setup_og { {
|
||||
{ // GPIO0
|
||||
.data
|
||||
= (0 << 9) // P1_2: 10K PD, BOOT1
|
||||
| (1 << 8) // P1_1: 10K PU, BOOT0
|
||||
| (1 << 7) // P2_7: 10K PU, ISP
|
||||
,
|
||||
.dir
|
||||
= (0 << 9) // P1_2: 10K PD, BOOT1
|
||||
| (0 << 8) // P1_1: 10K PU, BOOT0
|
||||
| (0 << 7) // P2_7: 10K PU, ISP
|
||||
},
|
||||
{ // GPIO1
|
||||
.data = 0,
|
||||
.dir = 0
|
||||
},
|
||||
{ // GPIO2
|
||||
.data
|
||||
= (1 << 9) // P5_0: !VAA_ENABLE
|
||||
| (0 << 4) // P4_4: TXENABLE
|
||||
,
|
||||
.dir
|
||||
= (1 << 9) // P5_0: !VAA_ENABLE
|
||||
| (1 << 4) // P4_4: TXENABLE
|
||||
},
|
||||
{ // GPIO3
|
||||
.data
|
||||
= (0 << 6) // P6_10: EN1V8, 10K PD
|
||||
,
|
||||
.dir
|
||||
= (1 << 6) // P6_10: EN1V8, 10K PD
|
||||
},
|
||||
{ // GPIO4
|
||||
.data = 0,
|
||||
.dir = 0
|
||||
},
|
||||
{ // GPIO5
|
||||
.data
|
||||
= (0 << 15) // P6_7: TX
|
||||
| (0 << 12) // P4_8: SGPIO13, HOST_SYNC_EN
|
||||
| (1 << 5) // P2_5: RX
|
||||
,
|
||||
.dir
|
||||
= (1 << 15) // P6_7: TX
|
||||
| (0 << 12) // P4_8: SGPIO13, HOST_SYNC_EN
|
||||
| (1 << 5) // P2_5: RX
|
||||
},
|
||||
} };
|
||||
|
||||
/* Additional GPIO configuration for HackRF r9 */
|
||||
static const std::array<gpio_setup_t, 6> gpio_setup_r9 { {
|
||||
{ // GPIO0
|
||||
.data
|
||||
= (0 << 9) // P1_2: 10K PD, BOOT1, CLKOUT_EN
|
||||
| (1 << 8) // P1_1: 10K PU, BOOT0, MCU_CLK_EN
|
||||
| (1 << 7) // P2_7: 10K PU, ISP, RX
|
||||
,
|
||||
.dir
|
||||
= (0 << 9) // P1_2: 10K PD, BOOT1, CLKOUT_EN
|
||||
| (0 << 8) // P1_1: 10K PU, BOOT0, MCU_CLK_EN
|
||||
| (0 << 7) // P2_7: 10K PU, ISP, RX
|
||||
},
|
||||
{ // GPIO1
|
||||
.data = 0,
|
||||
.dir = 0
|
||||
},
|
||||
{ // GPIO2
|
||||
.data
|
||||
= (1 << 9) // P5_0: EN1V8, 10K PD
|
||||
| (1 << 4) // P4_4: !ANT_BIAS
|
||||
,
|
||||
.dir
|
||||
= (1 << 9) // P5_0: EN1V8, 10K PD
|
||||
| (1 << 4) // P4_4: !ANT_BIAS
|
||||
},
|
||||
{ // GPIO3
|
||||
.data
|
||||
= (1 << 6) // P6_10: !VAA_ENABLE
|
||||
,
|
||||
.dir
|
||||
= (1 << 6) // P6_10: !VAA_ENABLE
|
||||
},
|
||||
{ // GPIO4
|
||||
.data = 0,
|
||||
.dir = 0
|
||||
},
|
||||
{ // GPIO5
|
||||
.data
|
||||
= (0 << 15) // P6_7: CLKIN_EN
|
||||
| (0 << 12) // P4_8: CLKIN_DETECT
|
||||
| (0 << 5) // P2_5: HOST_SYNC_EN
|
||||
,
|
||||
.dir
|
||||
= (1 << 15) // P6_7: CLKIN_EN
|
||||
| (0 << 12) // P4_8: CLKIN_DETECT
|
||||
| (0 << 5) // P2_5: HOST_SYNC_EN
|
||||
},
|
||||
} };
|
||||
|
||||
/* Additional SCU configuration for HackRF OG */
|
||||
static const std::array<scu_setup_t, 9> pins_setup_og { {
|
||||
/* Power control */
|
||||
{ 5, 0, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* !VAA_ENABLE: 10K PU, Q3.G(I), power to VAA */
|
||||
{ 6, 10, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* EN1V8/P70: 10K PD, TPS62410.EN2(I), 1V8LED.A(I) */
|
||||
|
||||
/* Radio section control */
|
||||
{ 2, 5, scu_config_normal_drive_t { .mode=4, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* RX/P43: U7.VCTL1(I), U10.VCTL1(I), U2.VCTL1(I) */
|
||||
{ 4, 4, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* TXENABLE/P55: MAX2837.TXENABLE(I) */
|
||||
{ 6, 7, scu_config_normal_drive_t { .mode=4, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* TX/P42: U7.VCTL2(I), U10.VCTL2(I), U2.VCTL2(I) */
|
||||
|
||||
/* SGPIO for sample transfer interface to HackRF CPLD. */
|
||||
{ 4, 8, scu_config_normal_drive_t { .mode=4, .epd=1, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* SGPIO13/BANK2F3M2: CPLD.90/HOST_SYNC_EN(I) */
|
||||
|
||||
/* Miscellaneous */
|
||||
{ 1, 1, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* P1_1/P74: 10K PU, BOOT0 */
|
||||
{ 1, 2, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* P1_2/P73: 10K PD, BOOT1 */
|
||||
{ 2, 7, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* ISP: 10K PU, Unused */
|
||||
{ 6, 0, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=0, .ehs=0, .ezi=0, .zif=0 } }, /* I2S0_RX_MCLK: Unused */
|
||||
{ 15, 4, scu_config_normal_drive_t { .mode=7, .epd=0, .epun=0, .ehs=0, .ezi=0, .zif=0 } }, /* I2S0_RX_SCK: Unused */
|
||||
}
|
||||
};
|
||||
} };
|
||||
|
||||
/* Additional SCU configuration for HackRF r9 */
|
||||
static const std::array<scu_setup_t, 9> pins_setup_r9 { {
|
||||
/* Power control */
|
||||
{ 6, 10, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* !VAA_ENABLE: 10K PU, Q3.G(I), power to VAA */
|
||||
{ 5, 0, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* EN1V8: 10K PD, TPS62410.EN2(I), 1V8LED.A(I) */
|
||||
|
||||
/* Radio section control */
|
||||
{ 2, 7, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* RX/ISP/P96: U7.VCTL(I), U10.VCTL(I), U2.VCTL(I) */
|
||||
{ 4, 4, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* !ANT_BIAS: 10K PU, Q4.G(I) */
|
||||
|
||||
/* SGPIO for sample transfer interface to HackRF CPLD. */
|
||||
{ 2, 5, scu_config_normal_drive_t { .mode=4, .epd=1, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* BANK2F3M2: CPLD.90/HOST_SYNC_EN(I) */
|
||||
|
||||
/* Clock control */
|
||||
{ 1, 1, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* MCU_CLK_EN/BOOT0: 10K PU, U28.1A(I) */
|
||||
{ 1, 2, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* CLKOUT_EN/BOOT1: 10K PD, U28.2A(I) */
|
||||
{ 6, 7, scu_config_normal_drive_t { .mode=4, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* CLKIN_EN: U16.SEL(I), U26.1A(I) */
|
||||
|
||||
/* Miscellaneous */
|
||||
{ 4, 8, scu_config_normal_drive_t { .mode=1, .epd=1, .epun=1, .ehs=0, .ezi=0, .zif=0 } }, /* CLKIN_DETECT: U26.2Y(O) */
|
||||
} };
|
||||
|
||||
#endif
|
||||
|
||||
static const std::array<scu_setup_t, 26> pins_setup_portapack { {
|
||||
@@ -398,6 +545,14 @@ static const std::array<scu_setup_t, 6> pins_setup_spifi { {
|
||||
{ 3, 8, scu_config_normal_drive_t { .mode=3, .epd=0, .epun=1, .ehs=1, .ezi=1, .zif=1 } }, /* SPIFI_CS/P68: W25Q80BV.CS(I) */
|
||||
} };
|
||||
|
||||
template<size_t N>
|
||||
void setup_gpios(const std::array<gpio_setup_t, N>& pins_setup) {
|
||||
for (size_t i = 0; i < N; i++) {
|
||||
LPC_GPIO->PIN[i] |= pins_setup[i].data;
|
||||
LPC_GPIO->DIR[i] |= pins_setup[i].dir;
|
||||
}
|
||||
}
|
||||
|
||||
static void setup_pin(const scu_setup_t& pin_setup) {
|
||||
LPC_SCU->SFSP[pin_setup.port][pin_setup.pin] = pin_setup.config;
|
||||
}
|
||||
@@ -409,6 +564,19 @@ void setup_pins(const std::array<scu_setup_t, N>& pins_setup) {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* HackRF One r9 has a pull-up on GPIO3_6 (P6_10) and a pull-down on GPIO2_9 (P5_0).
|
||||
* HackRF One OG has a pull-down on GPIO3_6 (P6_10) and a pull-up on GPIO2_9 (P5_0).
|
||||
*/
|
||||
static const scu_setup_t pin_setup_detect { 5, 0, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=1, .zif=0 } };
|
||||
|
||||
/* Check resistor on GPIO2_9 (P5_0) to detect HackRF hardware revision. */
|
||||
extern "C" bool detect_hackrf_r9() {
|
||||
setup_pin(pin_setup_detect);
|
||||
LPC_GPIO->DIR[2] &= ~(1 << 9);
|
||||
return LPC_GPIO->W2[9] == 0;
|
||||
}
|
||||
|
||||
static void configure_spifi(void) {
|
||||
setup_pins(pins_setup_spifi);
|
||||
|
||||
@@ -449,7 +617,8 @@ static const motocon_pwm_resources_t motocon_pwm_resources = {
|
||||
};
|
||||
|
||||
static const scu_setup_t pin_setup_vaa_enablex_pwm = { 5, 0, scu_config_normal_drive_t { .mode=1, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } };
|
||||
static const scu_setup_t pin_setup_vaa_enablex_gpio = { 5, 0, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } };
|
||||
static const scu_setup_t pin_setup_vaa_enablex_gpio_og = { 5, 0, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } };
|
||||
static const scu_setup_t pin_setup_vaa_enablex_gpio_r9 = { 6, 10, scu_config_normal_drive_t { .mode=0, .epd=0, .epun=1, .ehs=0, .ezi=0, .zif=0 } };
|
||||
|
||||
/* VAA powers:
|
||||
* MAX5864 analog section.
|
||||
@@ -467,52 +636,69 @@ void vaa_power_on(void) {
|
||||
*
|
||||
* Controlling timing while running from SPIFI flash is tricky, hence use of a PWM peripheral...
|
||||
*/
|
||||
if (hackrf_r9) {
|
||||
/*
|
||||
* There is enough VCC->VAA leakage prior to VAA activation from IO pins on
|
||||
* HackRF One r9 that slowing down activation like this isn't necessary, but
|
||||
* we do it just in case a different start-up sequence in the future results
|
||||
* in less leakage.
|
||||
*/
|
||||
setup_pin(pin_setup_vaa_enablex_gpio_r9); // P6_10 GPIO3[ 6]: !VAA_ENABLE, 10K PU
|
||||
for (uint32_t i = 0; i < 1000; i++) {
|
||||
LPC_GPIO->W3[6] = 1;
|
||||
LPC_GPIO->W3[6] = 0;
|
||||
}
|
||||
} else {
|
||||
/* Configure and enable MOTOCONPWM peripheral clocks.
|
||||
* Assume IDIVC is running the post-bootloader configuration, outputting 96MHz derived from PLL1.
|
||||
*/
|
||||
base_clock_enable(&motocon_pwm_resources.base);
|
||||
branch_clock_enable(&motocon_pwm_resources.branch);
|
||||
peripheral_reset(&motocon_pwm_resources.reset);
|
||||
|
||||
/* Configure and enable MOTOCONPWM peripheral clocks.
|
||||
* Assume IDIVC is running the post-bootloader configuration, outputting 96MHz derived from PLL1.
|
||||
*/
|
||||
base_clock_enable(&motocon_pwm_resources.base);
|
||||
branch_clock_enable(&motocon_pwm_resources.branch);
|
||||
peripheral_reset(&motocon_pwm_resources.reset);
|
||||
|
||||
/* Combination of pulse duration and duty cycle was arrived at empirically, to keep supply glitching
|
||||
* to +/- 0.15V.
|
||||
*/
|
||||
const uint32_t cycle_period = 256;
|
||||
uint32_t enable_period = 2;
|
||||
LPC_MCPWM->TC2 = 0;
|
||||
LPC_MCPWM->MAT2 = cycle_period - enable_period;
|
||||
LPC_MCPWM->LIM2 = cycle_period;
|
||||
|
||||
/* Switch !VAA_ENABLE pin from GPIO to MOTOCONPWM peripheral output, now that the peripheral is configured. */
|
||||
setup_pin(pin_setup_vaa_enablex_pwm); // P5_0 /GPIO2[ 9]/MCOB2: !VAA_ENABLE, 10K PU
|
||||
|
||||
/* Start the PWM operation. */
|
||||
LPC_MCPWM->CON_SET = (1 << 16);
|
||||
|
||||
/* Wait until VAA rises to approximately 90% of final voltage. */
|
||||
/* Timing assumes we're running immediately after the bootloader: 96 MHz from IRC+PLL1
|
||||
*/
|
||||
while(enable_period < cycle_period) {
|
||||
{ volatile uint32_t delay = 2000; while(delay--); }
|
||||
enable_period <<= 1;
|
||||
/* Combination of pulse duration and duty cycle was arrived at empirically, to keep supply glitching
|
||||
* to +/- 0.15V.
|
||||
*/
|
||||
const uint32_t cycle_period = 256;
|
||||
uint32_t enable_period = 2;
|
||||
LPC_MCPWM->TC2 = 0;
|
||||
LPC_MCPWM->MAT2 = cycle_period - enable_period;
|
||||
LPC_MCPWM->LIM2 = cycle_period;
|
||||
|
||||
/* Switch !VAA_ENABLE pin from GPIO to MOTOCONPWM peripheral output, now that the peripheral is configured. */
|
||||
setup_pin(pin_setup_vaa_enablex_pwm); // P5_0 /GPIO2[ 9]/MCOB2: !VAA_ENABLE, 10K PU
|
||||
|
||||
/* Start the PWM operation. */
|
||||
LPC_MCPWM->CON_SET = (1 << 16);
|
||||
|
||||
/* Wait until VAA rises to approximately 90% of final voltage. */
|
||||
/* Timing assumes we're running immediately after the bootloader: 96 MHz from IRC+PLL1
|
||||
*/
|
||||
while(enable_period < cycle_period) {
|
||||
{ volatile uint32_t delay = 2000; while(delay--); }
|
||||
enable_period <<= 1;
|
||||
LPC_MCPWM->MAT2 = cycle_period - enable_period;
|
||||
}
|
||||
|
||||
/* Hold !VAA_ENABLE active using a GPIO, so we can reclaim and shut down the MOTOCONPWM peripheral. */
|
||||
LPC_GPIO->CLR[2] = (1 << 9); // !VAA_ENABLE
|
||||
LPC_GPIO->DIR[2] |= (1 << 9);
|
||||
setup_pin(pin_setup_vaa_enablex_gpio_og); // P5_0 /GPIO2[ 9]/MCOB2: !VAA_ENABLE, 10K PU
|
||||
|
||||
peripheral_reset(&motocon_pwm_resources.reset);
|
||||
branch_clock_disable(&motocon_pwm_resources.branch);
|
||||
base_clock_disable(&motocon_pwm_resources.base);
|
||||
}
|
||||
|
||||
/* Hold !VAA_ENABLE active using a GPIO, so we can reclaim and shut down the MOTOCONPWM peripheral. */
|
||||
LPC_GPIO->CLR[2] = (1 << 9); // !VAA_ENABLE
|
||||
LPC_GPIO->DIR[2] |= (1 << 9);
|
||||
setup_pin(pin_setup_vaa_enablex_gpio); // P5_0 /GPIO2[ 9]/MCOB2: !VAA_ENABLE, 10K PU
|
||||
|
||||
peripheral_reset(&motocon_pwm_resources.reset);
|
||||
branch_clock_disable(&motocon_pwm_resources.branch);
|
||||
base_clock_disable(&motocon_pwm_resources.base);
|
||||
}
|
||||
|
||||
void vaa_power_off(void) {
|
||||
// TODO: There's a lot of other stuff that must be done to prevent
|
||||
// leakage from +3V3 into VAA.
|
||||
LPC_GPIO->W2[9] = 1;
|
||||
if (hackrf_r9) {
|
||||
LPC_GPIO->W3[6] = 1;
|
||||
} else {
|
||||
LPC_GPIO->W2[9] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -638,12 +824,30 @@ extern "C" void __late_init(void) {
|
||||
* @todo Add your board-specific code, if any.
|
||||
*/
|
||||
extern "C" void boardInit(void) {
|
||||
/* Detect HackRF variant */
|
||||
hackrf_r9 = detect_hackrf_r9();
|
||||
/* Configure variant-dependent pins. */
|
||||
if (hackrf_r9) {
|
||||
setup_gpios(gpio_setup_r9);
|
||||
setup_pins(pins_setup_r9);
|
||||
} else {
|
||||
setup_gpios(gpio_setup_og);
|
||||
setup_pins(pins_setup_og);
|
||||
}
|
||||
vaa_power_on();
|
||||
LPC_GPIO->W3[6] = 1;
|
||||
if (hackrf_r9) {
|
||||
LPC_GPIO->W2[9] = 1;
|
||||
} else {
|
||||
LPC_GPIO->W3[6] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void _default_exit(void) {
|
||||
LPC_GPIO->W3[6] = 0;
|
||||
if (hackrf_r9) {
|
||||
LPC_GPIO->W2[9] = 0;
|
||||
} else {
|
||||
LPC_GPIO->W3[6] = 0;
|
||||
}
|
||||
vaa_power_off();
|
||||
|
||||
chSysDisable();
|
||||
|
||||
@@ -41,6 +41,10 @@ SECTIONS
|
||||
startup : ALIGN(16) SUBALIGN(16)
|
||||
{
|
||||
KEEP(*(vectors))
|
||||
. = ALIGN(0x400);
|
||||
ASSERT(. == 0x400, "Error: attempting to place firmware information section at incorrect location");
|
||||
KEEP(*(.firmware_info));
|
||||
|
||||
} > flash
|
||||
|
||||
constructors : ALIGN(4) SUBALIGN(4)
|
||||
|
||||
+17
-10
@@ -307,26 +307,33 @@ adsb_pos decode_frame_pos(ADSBFrame& frame_even, ADSBFrame& frame_odd) {
|
||||
void encode_frame_velo(ADSBFrame& frame, const uint32_t ICAO_address, const uint32_t speed,
|
||||
const float angle, const int32_t v_rate) {
|
||||
|
||||
int32_t velo_ew, velo_ns, v_rate_coded;
|
||||
int32_t velo_ew, velo_ns;
|
||||
uint32_t velo_ew_abs, velo_ns_abs, v_rate_coded_abs;
|
||||
|
||||
// To get NS and EW speeds from speed and bearing, a polar to cartesian conversion is enough
|
||||
velo_ew = static_cast<int32_t>(sin_f32(DEG_TO_RAD(angle) + (pi / 2)) * speed);
|
||||
velo_ns = static_cast<int32_t>(sin_f32(DEG_TO_RAD(angle)) * speed);
|
||||
velo_ew = static_cast<int32_t>(sin_f32(DEG_TO_RAD(angle) ) * speed); // East direction, is the projection from West -> East is directly sin(angle=Compas Bearing) , (90º is the max +1, EAST) max velo_EW
|
||||
velo_ns = static_cast<int32_t>(sin_f32( (pi/2 - DEG_TO_RAD(angle) ) ) * speed); // North direction,is the projection of North = cos(angle=Compas Bearing), cos(angle)= sen(90-angle) (0º is the max +1 NORTH) max velo_NS
|
||||
|
||||
v_rate_coded = (v_rate / 64) + 1;
|
||||
|
||||
velo_ew_abs = abs(velo_ew) + 1;
|
||||
velo_ns_abs = abs(velo_ns) + 1;
|
||||
v_rate_coded_abs = abs(v_rate_coded);
|
||||
v_rate_coded_abs = (abs(v_rate) / 64) + 1; //encoding vertical rate source. (Decoding, VR ft/min = (Decimal v_rate_value - 1)* 64)
|
||||
|
||||
velo_ew_abs = abs(velo_ew) + 1; // encoding Velo speed EW , when sign Direction is 0 (+): West->East, (-) 1: East->West
|
||||
velo_ns_abs = abs(velo_ns) + 1; // encoding Velo speed NS , when sign Direction is 0 (+): South->North , (-) 1: North->South
|
||||
|
||||
make_frame_adsb(frame, ICAO_address);
|
||||
|
||||
frame.push_byte((TC_AIRBORNE_VELO << 3) | 1); // Subtype: 1 (subsonic)
|
||||
// Airborne velocities are all transmitted with Type Code 19 ( TC=19, using 5 bits ,TC=19 [Binary: 10011]), the following 3 bits are Subt-type Code ,SC= 1,2,3,4
|
||||
// SC Subtypes code 1 and 2 are used to report ground speeds of aircraft. (SC 3,4 to used to report true airspeed. SC 2,4 are for supersonic aircraft (not used in commercial airline).
|
||||
frame.push_byte((TC_AIRBORNE_VELO << 3) | 1); // 1st byte , top 5 bits Type Code TC=19, and lower 3 bits (38-40 bits), SC=001 Subtype Code SC: 1 (subsonic) ,
|
||||
|
||||
// Message A, (ME bits from 14-35) , 22 bits = Sign ew(1 bit) + V_ew (10 bits) + Sign_ns (1 bit) + V_ns (10 bits)
|
||||
// Vertical rate source bit VrSrc (ME bit 36) indicates source of the altitude measurements. GNSS altitude(0) / , barometric altitude(1).
|
||||
// Vertical rate source direction,(ME bit 37) movement can be read from Svr bit , with 0 and 1 referring to climb and descent, respectively (ft/min)
|
||||
// The encoded vertical rate value VR can be computed using message (ME bits 38 to 46). If the 9-bit block contains all zeros, the vertical rate information is not available.
|
||||
// + Sign VrSrc (vert rate src) (1 bit)+ VrSrc (9 bits).
|
||||
frame.push_byte(((velo_ew < 0 ? 1 : 0) << 2) | (velo_ew_abs >> 8));
|
||||
frame.push_byte(velo_ew_abs);
|
||||
frame.push_byte(((velo_ns < 0 ? 1 : 0) << 7) | (velo_ns_abs >> 3));
|
||||
frame.push_byte((velo_ns_abs << 5) | ((v_rate_coded < 0 ? 1 : 0) << 3) | (v_rate_coded_abs >> 6)); // VrSrc = 0
|
||||
frame.push_byte((velo_ns_abs << 5) | ((v_rate < 0 ? 1 : 0) << 3) | (v_rate_coded_abs >> 6)); // VrSrc = 0
|
||||
frame.push_byte(v_rate_coded_abs << 2);
|
||||
frame.push_byte(0);
|
||||
|
||||
|
||||
@@ -28,11 +28,11 @@ namespace baseband {
|
||||
|
||||
void CPLD::init() {
|
||||
set_invert(false);
|
||||
gpio_baseband_invert.output();
|
||||
gpio_q_invert.output();
|
||||
}
|
||||
|
||||
void CPLD::set_invert(const bool invert) {
|
||||
gpio_baseband_invert.write(invert);
|
||||
gpio_q_invert.write(invert);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -197,25 +197,11 @@ struct Pin {
|
||||
|
||||
constexpr Pin(
|
||||
const uint8_t port,
|
||||
const uint8_t pad,
|
||||
const PinConfig initial_config
|
||||
const uint8_t pad
|
||||
) : _pin_port { port },
|
||||
_pin_pad { pad },
|
||||
_initial_config { initial_config }
|
||||
_pin_pad { pad }
|
||||
{
|
||||
}
|
||||
/*
|
||||
constexpr Pin(
|
||||
const Pin& pin
|
||||
) : _pin_port { pin._pin_port },
|
||||
_pin_pad { pin._pin_pad },
|
||||
_initial_config { pin._initial_config }
|
||||
{
|
||||
}
|
||||
*/
|
||||
void init() const {
|
||||
LPC_SCU->SFSP[_pin_port][_pin_pad] = _initial_config;
|
||||
}
|
||||
|
||||
void mode(const uint_fast16_t mode) const {
|
||||
LPC_SCU->SFSP[_pin_port][_pin_pad] =
|
||||
@@ -228,7 +214,6 @@ struct Pin {
|
||||
|
||||
uint8_t _pin_port;
|
||||
uint8_t _pin_pad;
|
||||
uint16_t _initial_config;
|
||||
};
|
||||
|
||||
struct GPIO {
|
||||
|
||||
@@ -38,17 +38,20 @@ constexpr GPIO gpio_led_usb = gpio[GPIO2_1];
|
||||
constexpr GPIO gpio_led_rx = gpio[GPIO2_2];
|
||||
constexpr GPIO gpio_led_tx = gpio[GPIO2_8];
|
||||
|
||||
constexpr GPIO gpio_1v8_enable = gpio[GPIO3_6];
|
||||
constexpr GPIO gpio_og_1v8_enable = gpio[GPIO3_6];
|
||||
constexpr GPIO gpio_r9_1v8_enable = gpio[GPIO2_9];
|
||||
constexpr GPIO gpio_vregmode = gpio[GPIO3_7];
|
||||
constexpr GPIO gpio_vaa_disable = gpio[GPIO2_9];
|
||||
constexpr GPIO gpio_og_vaa_disable = gpio[GPIO2_9];
|
||||
constexpr GPIO gpio_r9_vaa_disable = gpio[GPIO3_6];
|
||||
|
||||
constexpr GPIO gpio_rx_mix_bp = gpio[GPIO2_12];
|
||||
constexpr GPIO gpio_tx_mix_bp = gpio[GPIO2_11];
|
||||
constexpr GPIO gpio_mix_bypass = gpio[GPIO5_16];
|
||||
constexpr GPIO gpio_not_mix_bypass = gpio[GPIO1_0];
|
||||
|
||||
constexpr GPIO gpio_rx = gpio[GPIO5_5];
|
||||
constexpr GPIO gpio_tx = gpio[GPIO5_15];
|
||||
constexpr GPIO gpio_og_rx = gpio[GPIO5_5];
|
||||
constexpr GPIO gpio_og_tx = gpio[GPIO5_15];
|
||||
constexpr GPIO gpio_r9_rx = gpio[GPIO0_7];
|
||||
|
||||
constexpr GPIO gpio_lp = gpio[GPIO2_10];
|
||||
constexpr GPIO gpio_hp = gpio[GPIO2_0];
|
||||
@@ -64,20 +67,26 @@ constexpr GPIO gpio_rffc5072_select = gpio[GPIO2_13];
|
||||
constexpr GPIO gpio_rffc5072_clock = gpio[GPIO5_6];
|
||||
constexpr GPIO gpio_rffc5072_data = gpio[GPIO3_3];
|
||||
|
||||
constexpr GPIO gpio_max2837_select = gpio[GPIO0_15];
|
||||
constexpr GPIO gpio_max2837_enable = gpio[GPIO2_6];
|
||||
constexpr GPIO gpio_max283x_select = gpio[GPIO0_15];
|
||||
constexpr GPIO gpio_max283x_enable = gpio[GPIO2_6];
|
||||
constexpr GPIO gpio_max2837_rxenable = gpio[GPIO2_5];
|
||||
constexpr GPIO gpio_max2837_txenable = gpio[GPIO2_4];
|
||||
constexpr GPIO gpio_max2839_rxtx = gpio[GPIO2_5];
|
||||
|
||||
constexpr GPIO gpio_max5864_select = gpio[GPIO2_7];
|
||||
|
||||
constexpr GPIO gpio_baseband_invert = gpio[GPIO0_13];
|
||||
constexpr GPIO gpio_q_invert = gpio[GPIO0_13];
|
||||
|
||||
constexpr GPIO gpio_cpld_tdo = gpio[GPIO5_18];
|
||||
constexpr GPIO gpio_cpld_tck = gpio[GPIO3_0];
|
||||
constexpr GPIO gpio_cpld_tms = gpio[GPIO3_4];
|
||||
constexpr GPIO gpio_cpld_tdi = gpio[GPIO3_1];
|
||||
|
||||
constexpr GPIO gpio_r9_clkin_en = gpio[GPIO5_15];
|
||||
constexpr GPIO gpio_r9_clkout_en = gpio[GPIO0_9];
|
||||
constexpr GPIO gpio_r9_mcu_clk_en = gpio[GPIO0_8];
|
||||
constexpr GPIO gpio_r9_not_ant_pwr = gpio[GPIO2_4];
|
||||
|
||||
/* LEDs */
|
||||
|
||||
constexpr LED led_usb { gpio_led_usb };
|
||||
|
||||
@@ -47,23 +47,28 @@ constexpr ClockFrequency base_apb3_clk_f = base_m4_clk_f;
|
||||
constexpr ClockFrequency ssp1_pclk_f = base_m4_clk_f;
|
||||
|
||||
constexpr ClockFrequency max5864_spi_f = 20000000U;
|
||||
constexpr ClockFrequency max2837_spi_f = 20000000U;
|
||||
constexpr ClockFrequency max283x_spi_f = 20000000U;
|
||||
|
||||
constexpr ClockFrequency rffc5072_reference_f = 40000000U;
|
||||
constexpr ClockFrequency max2837_reference_f = 40000000U;
|
||||
constexpr ClockFrequency mcu_clkin_f = 40000000U;
|
||||
constexpr ClockFrequency max283x_reference_f = 40000000U;
|
||||
constexpr ClockFrequency mcu_clkin_og_f = 40000000U;
|
||||
constexpr ClockFrequency mcu_clkin_r9_f = 10000000U;
|
||||
|
||||
constexpr uint8_t si5351_i2c_address = 0x60;
|
||||
|
||||
/* Clock Generator */
|
||||
|
||||
constexpr size_t clock_generator_output_codec = 0;
|
||||
constexpr size_t clock_generator_output_cpld = 1;
|
||||
constexpr size_t clock_generator_output_sgpio = 2;
|
||||
constexpr size_t clock_generator_output_clkout = 3;
|
||||
constexpr size_t clock_generator_output_first_if = 4;
|
||||
constexpr size_t clock_generator_output_second_if = 5;
|
||||
constexpr size_t clock_generator_output_mcu_clkin = 7;
|
||||
constexpr size_t clock_generator_output_og_codec = 0;
|
||||
constexpr size_t clock_generator_output_og_cpld = 1;
|
||||
constexpr size_t clock_generator_output_og_sgpio = 2;
|
||||
constexpr size_t clock_generator_output_og_clkout = 3;
|
||||
constexpr size_t clock_generator_output_og_first_if = 4;
|
||||
constexpr size_t clock_generator_output_og_second_if = 5;
|
||||
constexpr size_t clock_generator_output_og_mcu_clkin = 7;
|
||||
|
||||
constexpr size_t clock_generator_output_r9_if = 0;
|
||||
constexpr size_t clock_generator_output_r9_sgpio = 1;
|
||||
constexpr size_t clock_generator_output_r9_mcu_clkin = 2;
|
||||
|
||||
/* ADC0 */
|
||||
|
||||
|
||||
+86
-86
@@ -40,92 +40,92 @@ enum Pins {
|
||||
CLK0, CLK2,
|
||||
};
|
||||
|
||||
constexpr Pin pins[] = {
|
||||
[P0_0] = { 0, 0, PinConfig::sgpio_inout_fast(3) }, /* SGPIO0/P75/BANK2F3M3: CPLD.89/HOST_DATA0(IO) */
|
||||
[P0_1] = { 0, 1, PinConfig::sgpio_inout_fast(3) }, /* SGPIO1/BANK2F3M5: CPLD.79/HOST_DATA1(IO) */
|
||||
[P1_0] = { 1, 0, PinConfig::sgpio_inout_fast(6) }, /* SGPIO7/P76/BANK2F3M7: CPLD.77/HOST_DATA7(IO) */
|
||||
[P1_1] = { 1, 1, { .mode=0, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* P1_1/P74: 10K PU, BOOT0 */
|
||||
[P1_2] = { 1, 2, { .mode=0, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* P1_2/P73: 10K PD, BOOT1 */
|
||||
[P1_3] = { 1, 3, { .mode=5, .pd=0, .pu=0, .fast=0, .input=1, .ifilt=1 } }, /* SSP1_MISO/P41: MAX2837.DOUT(O) */
|
||||
[P1_4] = { 1, 4, { .mode=5, .pd=1, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* SSP1_MOSI/P40: MAX2837.DIN(I), MAX5864.DIN(I) */
|
||||
[P1_5] = { 1, 5, { .mode=0, .pd=0, .pu=1, .fast=0, .input=1, .ifilt=1 } }, /* SD_POW: PortaPack CPLD.TDO(O) */
|
||||
[P1_6] = { 1, 6, { .mode=7, .pd=0, .pu=1, .fast=0, .input=1, .ifilt=0 } }, /* SD_CMD: PortaPack SD.CMD(IO) */
|
||||
[P1_7] = { 1, 7, { .mode=0, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* !MIX_BYPASS/P35: U1.VCTL1(I), U11.VCTL2(I), U9.V2(I) */
|
||||
[P1_8] = { 1, 8, { .mode=0, .pd=0, .pu=1, .fast=0, .input=0, .ifilt=1 } }, /* SD_VOLT0: PortaPack CPLD.TMS(I) */
|
||||
[P1_9] = { 1, 9, { .mode=7, .pd=0, .pu=1, .fast=0, .input=1, .ifilt=0 } }, /* SD_DAT0: PortaPack SD.DAT0(IO) */
|
||||
[P1_10] = { 1, 10, { .mode=7, .pd=0, .pu=1, .fast=0, .input=1, .ifilt=0 } }, /* SD_DAT1: PortaPack SD.DAT1(IO) */
|
||||
[P1_11] = { 1, 11, { .mode=7, .pd=0, .pu=1, .fast=0, .input=1, .ifilt=0 } }, /* SD_DAT2: PortaPack SD.DAT2(IO) */
|
||||
[P1_12] = { 1, 12, { .mode=7, .pd=0, .pu=1, .fast=0, .input=1, .ifilt=0 } }, /* SD_DAT3: PortaPack SD.DAT3(IO) */
|
||||
[P1_13] = { 1, 13, { .mode=7, .pd=0, .pu=1, .fast=0, .input=1, .ifilt=1 } }, /* SD_CD: PortaPack SD.CD(O) */
|
||||
[P1_14] = { 1, 14, PinConfig::sgpio_out_fast_with_pullup(6) }, /* SGPIO10/P78/BANK2F3M8: CPLD.76/HOST_DISABLE(I) */
|
||||
[P1_15] = { 1, 15, PinConfig::sgpio_inout_fast(2) }, /* SGPIO2/BANK2F3M9: CPLD.74/HOST_DATA2(IO) */
|
||||
[P1_16] = { 1, 16, PinConfig::sgpio_inout_fast(2) }, /* SGPIO3/BANK2F3M10: CPLD.72/HOST_DATA3(IO) */
|
||||
[P1_17] = { 1, 17, PinConfig::sgpio_out_fast_with_pullup(6) }, /* SGPIO11/P79/BANK2F3M11: CPLD.71/HOST_DIRECTION(I) */
|
||||
[P1_18] = { 1, 18, PinConfig::gpio_out_with_pulldown(0) }, /* SGPIO12/BANK2F3M12: CPLD.70/HOST_INVERT(I) */
|
||||
[P1_19] = { 1, 19, { .mode=1, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* SSP1_SCK/P39: MAX2837.SCLK(I), MAX5864.SCLK(I) */
|
||||
[P1_20] = { 1, 20, { .mode=0, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* CS_XCVR/P53: MAX2837.CS(I) */
|
||||
[P2_0] = { 2, 0, { .mode=4, .pd=0, .pu=0, .fast=0, .input=1, .ifilt=1 } }, /* U0_TXD: PortaPack P2_0/IO_STBX */
|
||||
[P2_1] = { 2, 1, { .mode=4, .pd=0, .pu=0, .fast=0, .input=1, .ifilt=1 } }, /* U0_RXD: PortaPack P2_1/ADDR */
|
||||
[P2_2] = { 2, 2, PinConfig::sgpio_inout_fast(0) }, /* SGPIO6/BANK2F3M16: CPLD.61/HOST_DATA6(IO) */
|
||||
[P2_3] = { 2, 3, { .mode=4, .pd=0, .pu=0, .fast=0, .input=1, .ifilt=1 } }, /* I2C1_SDA: PortaPack P2_3/LCD_TE */
|
||||
[P2_4] = { 2, 4, { .mode=4, .pd=0, .pu=0, .fast=0, .input=1, .ifilt=1 } }, /* I2C1_SCL: PortaPack P2_4/LCD_RDX */
|
||||
[P2_5] = { 2, 5, { .mode=4, .pd=0, .pu=1, .fast=0, .input=0, .ifilt=1 } }, /* RX/P43: U7.VCTL1(I), U10.VCTL1(I), U2.VCTL1(I) */
|
||||
[P2_6] = { 2, 6, { .mode=4, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* MIXER_SCLK/P31: 33pF, RFFC5072.SCLK(I) */
|
||||
[P2_7] = { 2, 7, { .mode=0, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* ISP: 10K PU, Unused */
|
||||
[P2_8] = { 2, 8, { .mode=4, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* P2_8: 10K PD, BOOT2, DFU switch, PortaPack P2_8/<unused> */
|
||||
[P2_9] = { 2, 9, { .mode=0, .pd=0, .pu=0, .fast=0, .input=1, .ifilt=1 } }, /* P2_9: 10K PD, BOOT3, PortaPack P2_9/LCD_WRX */
|
||||
[P2_10] = { 2, 10, { .mode=0, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* AMP_BYPASS/P50: U14.V2(I), U12.V2(I) */
|
||||
[P2_11] = { 2, 11, { .mode=0, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* RX_AMP/P49: U12.V1(I), U14.V3(I) */
|
||||
[P2_12] = { 2, 12, { .mode=0, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* !RX_AMP_PWR/P52: 10K PU, Q1.G(I), power to U13 (RX amp) */
|
||||
[P2_13] = { 2, 13, { .mode=0, .pd=0, .pu=0, .fast=0, .input=1, .ifilt=1 } }, /* P2_13: PortaPack P2_13/DIR */
|
||||
[P3_0] = { 3, 0, { .mode=2, .pd=0, .pu=1, .fast=0, .input=1, .ifilt=1 } }, /* I2S0_TX_SCK: PortaPack I2S0_TX_SCK(I) */
|
||||
[P3_1] = { 3, 1, { .mode=0, .pd=0, .pu=1, .fast=0, .input=1, .ifilt=1 } }, /* I2S0_RX_WS: PortaPack I2S0_TX_WS(I). Input enabled to fold back into RX. */
|
||||
[P3_2] = { 3, 2, { .mode=0, .pd=0, .pu=1, .fast=0, .input=0, .ifilt=1 } }, /* I2S0_RX_SDA: PortaPack I2S0_TX_SDA(I) */
|
||||
[P4_0] = { 4, 0, { .mode=0, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* HP/P44: U6.VCTL1(I), U5.VCTL2(I) */
|
||||
[P4_1] = { 4, 1, PinConfig::gpio_led(0) }, /* LED1: LED1.A(I) */
|
||||
[P4_2] = { 4, 2, PinConfig::gpio_led(0) }, /* LED2: LED2.A(I) */
|
||||
[P4_3] = { 4, 3, PinConfig::sgpio_in_fast(7) }, /* SGPIO9/P77/BANK2F3M1: CPLD.91/HOST_CAPTURE(O) */
|
||||
[P4_4] = { 4, 4, { .mode=0, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* TXENABLE/P55: MAX2837.TXENABLE(I) */
|
||||
[P4_5] = { 4, 5, { .mode=0, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* RXENABLE/P56: MAX2837.RXENABLE(I) */
|
||||
[P4_6] = { 4, 6, { .mode=0, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* XCVR_EN: 10K PD, MAX2837.ENABLE(I) */
|
||||
[P4_7] = { 4, 7, { .mode=1, .pd=0, .pu=0, .fast=0, .input=1, .ifilt=0 } }, /* GP_CLKIN/P72/MCU_CLK: SI5351C.CLK7(O) */
|
||||
[P4_8] = { 4, 8, PinConfig::floating_input_with_pull(0, 4) }, /* SGPIO13/BANK2F3M2: CPLD.90/HOST_SYNC_EN(I) */
|
||||
[P4_9] = { 4, 9, PinConfig::floating_input(4) }, /* SGPIO14/BANK2F3M4: CPLD.81(I) */
|
||||
[P4_10] = { 4, 10, PinConfig::floating_input(4) }, /* SGPIO15/BANK2F3M6: CPLD.78(I) */
|
||||
[P5_0] = { 5, 0, { .mode=0, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* !VAA_ENABLE: 10K PU, Q3.G(I), power to VAA */
|
||||
[P5_1] = { 5, 1, { .mode=0, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* LP/P45: U6.VCTL2(I), U5.VCTL1(I) */
|
||||
[P5_2] = { 5, 2, { .mode=0, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* TX_MIX_BP/P46: U9.V1(I) */
|
||||
[P5_3] = { 5, 3, { .mode=0, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* RX_MIX_BP/P47: U9.V3(I) */
|
||||
[P5_4] = { 5, 4, { .mode=0, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* MIXER_ENX/P32: 10K PU, 33pF, RFFC5072.ENX(I) */
|
||||
[P5_5] = { 5, 5, { .mode=0, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* MIXER_RESETX/P33: 10K PU, 33pF, RFFC5072.RESETX(I) */
|
||||
[P5_6] = { 5, 6, { .mode=0, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* TX_AMP/P48: U12.V3(I), U14.V1(I) */
|
||||
[P5_7] = { 5, 7, { .mode=0, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* CS_AD/P54: MAX5864.CS(I) */
|
||||
[P6_0] = { 6, 0, { .mode=0, .pd=0, .pu=1, .fast=0, .input=0, .ifilt=1 } }, /* I2S0_RX_MCLK: Unused */
|
||||
[P6_1] = { 6, 1, { .mode=0, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* CPLD_TCK: CPLD.TCK(I), PortaPack CPLD.TCK(I) */
|
||||
[P6_2] = { 6, 2, { .mode=0, .pd=0, .pu=0, .fast=0, .input=1, .ifilt=1 } }, /* CPLD_TDI: CPLD.TDI(I), PortaPack I2S0_RX_SDA(O), PortaPack CPLD.TDI(I) */
|
||||
[P6_3] = { 6, 3, PinConfig::sgpio_inout_fast(2) }, /* SGPIO4/BANK2F3M14: CPLD.67/HOST_DATA4(IO) */
|
||||
[P6_4] = { 6, 4, { .mode=0, .pd=0, .pu=0, .fast=0, .input=1, .ifilt=1 } }, /* MIXER_SDATA/P27: 33pF, RFFC5072.SDATA(IO) */
|
||||
[P6_5] = { 6, 5, { .mode=0, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* CPLD_TMS: CPLD.TMS(I) */
|
||||
[P6_6] = { 6, 6, PinConfig::sgpio_inout_fast(2) }, /* SGPIO5/BANK2F3M15: CPLD.64/HOST_DATA5(IO) */
|
||||
[P6_7] = { 6, 7, { .mode=4, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* TX/P42: U7.VCTL2(I), U10.VCTL2(I), U2.VCTL2(I) */
|
||||
[P6_8] = { 6, 8, { .mode=4, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* MIX_BYPASS/P34: U1.VCTL2(I), U11.VCTL1(I) */
|
||||
[P6_9] = { 6, 9, { .mode=0, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* !TX_AMP_PWR/P51: 10K PU, Q2.G(I), power to U25 (TX amp) */
|
||||
[P6_10] = { 6, 10, { .mode=0, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* EN1V8/P70: 10K PD, TPS62410.EN2(I), 1V8LED.A(I) */
|
||||
[P6_11] = { 6, 11, { .mode=0, .pd=0, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* VREGMODE/P69: TPS62410.MODE/DATA(I) */
|
||||
[P6_12] = { 6, 12, PinConfig::gpio_led(0) }, /* LED3: LED3.A(I) */
|
||||
[P7_0] = { 7, 0, PinConfig::gpio_inout_with_pulldown(0) }, /* GPIO3_8: PortaPack GPIO3_8(IO) */
|
||||
[P7_1] = { 7, 1, PinConfig::gpio_inout_with_pulldown(0) }, /* GPIO3_9: PortaPack GPIO3_9(IO) */
|
||||
[P7_2] = { 7, 2, PinConfig::gpio_inout_with_pulldown(0) }, /* GPIO3_10: PortaPack GPIO3_10(IO) */
|
||||
[P7_3] = { 7, 3, PinConfig::gpio_inout_with_pulldown(0) }, /* GPIO3_11: PortaPack GPIO3_11(IO) */
|
||||
[P7_4] = { 7, 4, PinConfig::gpio_inout_with_pulldown(0) }, /* GPIO3_12: PortaPack GPIO3_12(IO) */
|
||||
[P7_5] = { 7, 5, PinConfig::gpio_inout_with_pulldown(0) }, /* GPIO3_13: PortaPack GPIO3_13(IO) */
|
||||
[P7_6] = { 7, 6, PinConfig::gpio_inout_with_pulldown(0) }, /* GPIO3_14: PortaPack GPIO3_14(IO) */
|
||||
[P7_7] = { 7, 7, PinConfig::gpio_inout_with_pulldown(0) }, /* GPIO3_15: PortaPack GPIO3_15(IO) */
|
||||
[P9_5] = { 9, 5, { .mode=4, .pd=0, .pu=0, .fast=0, .input=1, .ifilt=1 } }, /* CPLD_TDO: CPLD.TDO(O) */
|
||||
[P9_6] = { 9, 6, PinConfig::sgpio_in_fast(6) }, /* SGPIO8/SGPIO_CLK/P60: SI5351C.CLK2(O) */
|
||||
[PF_4] = { 15, 4, { .mode=7, .pd=0, .pu=1, .fast=0, .input=0, .ifilt=1 } }, /* I2S0_RX_SCK: Unused */
|
||||
[CLK0] = { 24, 0, { .mode=4, .pd=1, .pu=0, .fast=0, .input=1, .ifilt=0 } }, /* SD_CLK: PortaPack SD.CLK, enable input buffer for timing feedback? */
|
||||
[CLK2] = { 24, 2, { .mode=6, .pd=1, .pu=0, .fast=0, .input=0, .ifilt=1 } }, /* I2S0_TX_CLK: PortaPack I2S0_TX_MCLK */
|
||||
constexpr Pin pins[] {
|
||||
[P0_0] = { 0, 0},
|
||||
[P0_1] = { 0, 1},
|
||||
[P1_0] = { 1, 0},
|
||||
[P1_1] = { 1, 1},
|
||||
[P1_2] = { 1, 2},
|
||||
[P1_3] = { 1, 3},
|
||||
[P1_4] = { 1, 4},
|
||||
[P1_5] = { 1, 5},
|
||||
[P1_6] = { 1, 6},
|
||||
[P1_7] = { 1, 7},
|
||||
[P1_8] = { 1, 8},
|
||||
[P1_9] = { 1, 9},
|
||||
[P1_10] = { 1, 10},
|
||||
[P1_11] = { 1, 11},
|
||||
[P1_12] = { 1, 12},
|
||||
[P1_13] = { 1, 13},
|
||||
[P1_14] = { 1, 14},
|
||||
[P1_15] = { 1, 15},
|
||||
[P1_16] = { 1, 16},
|
||||
[P1_17] = { 1, 17},
|
||||
[P1_18] = { 1, 18},
|
||||
[P1_19] = { 1, 19},
|
||||
[P1_20] = { 1, 20},
|
||||
[P2_0] = { 2, 0},
|
||||
[P2_1] = { 2, 1},
|
||||
[P2_2] = { 2, 2},
|
||||
[P2_3] = { 2, 3},
|
||||
[P2_4] = { 2, 4},
|
||||
[P2_5] = { 2, 5},
|
||||
[P2_6] = { 2, 6},
|
||||
[P2_7] = { 2, 7},
|
||||
[P2_8] = { 2, 8},
|
||||
[P2_9] = { 2, 9},
|
||||
[P2_10] = { 2, 10},
|
||||
[P2_11] = { 2, 11},
|
||||
[P2_12] = { 2, 12},
|
||||
[P2_13] = { 2, 13},
|
||||
[P3_0] = { 3, 0},
|
||||
[P3_1] = { 3, 1},
|
||||
[P3_2] = { 3, 2},
|
||||
[P4_0] = { 4, 0},
|
||||
[P4_1] = { 4, 1},
|
||||
[P4_2] = { 4, 2},
|
||||
[P4_3] = { 4, 3},
|
||||
[P4_4] = { 4, 4},
|
||||
[P4_5] = { 4, 5},
|
||||
[P4_6] = { 4, 6},
|
||||
[P4_7] = { 4, 7},
|
||||
[P4_8] = { 4, 8},
|
||||
[P4_9] = { 4, 9},
|
||||
[P4_10] = { 4, 10},
|
||||
[P5_0] = { 5, 0},
|
||||
[P5_1] = { 5, 1},
|
||||
[P5_2] = { 5, 2},
|
||||
[P5_3] = { 5, 3},
|
||||
[P5_4] = { 5, 4},
|
||||
[P5_5] = { 5, 5},
|
||||
[P5_6] = { 5, 6},
|
||||
[P5_7] = { 5, 7},
|
||||
[P6_0] = { 6, 0},
|
||||
[P6_1] = { 6, 1},
|
||||
[P6_2] = { 6, 2},
|
||||
[P6_3] = { 6, 3},
|
||||
[P6_4] = { 6, 4},
|
||||
[P6_5] = { 6, 5},
|
||||
[P6_6] = { 6, 6},
|
||||
[P6_7] = { 6, 7},
|
||||
[P6_8] = { 6, 8},
|
||||
[P6_9] = { 6, 9},
|
||||
[P6_10] = { 6, 10},
|
||||
[P6_11] = { 6, 11},
|
||||
[P6_12] = { 6, 12},
|
||||
[P7_0] = { 7, 0},
|
||||
[P7_1] = { 7, 1},
|
||||
[P7_2] = { 7, 2},
|
||||
[P7_3] = { 7, 3},
|
||||
[P7_4] = { 7, 4},
|
||||
[P7_5] = { 7, 5},
|
||||
[P7_6] = { 7, 6},
|
||||
[P7_7] = { 7, 7},
|
||||
[P9_5] = { 9, 5},
|
||||
[P9_6] = { 9, 6},
|
||||
[PF_4] = { 15, 4},
|
||||
[CLK0] = { 24, 0},
|
||||
[CLK2] = { 24, 2},
|
||||
};
|
||||
|
||||
enum GPIOs {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
extern bool hackrf_r9;
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <cstddef>
|
||||
#include <array>
|
||||
|
||||
#include "platform.hpp"
|
||||
#include "gpio.hpp"
|
||||
#include "ui.hpp"
|
||||
|
||||
|
||||
@@ -35,7 +35,10 @@ w = numpy.arange(length, dtype=numpy.float64) * (2 * numpy.pi / length)
|
||||
v = numpy.sin(w)
|
||||
print(v)
|
||||
*/
|
||||
constexpr uint16_t sine_table_f32_period = 256;
|
||||
constexpr uint16_t sine_table_f32_period = 256;
|
||||
// periode is 256 . means sine_table_f32[0]= sine_table_f32[0+256], sine_table_f32[1]=sine_table_f32[1+256] (those two added manualy)
|
||||
// Then table has 258 values ,256:[0,..255] + [256] and [257], those two are used when we interpolate[255] with [255+1], and [256] with [256+1]
|
||||
// [256] index is needed in the function sin_f32() when we are inputing very small radian values , example , sin_f32((-1e-14) in radians)
|
||||
|
||||
static constexpr std::array<float, sine_table_f32_period + 2> sine_table_f32{
|
||||
0.00000000e+00, 2.45412285e-02, 4.90676743e-02,
|
||||
@@ -123,7 +126,7 @@ static constexpr std::array<float, sine_table_f32_period + 2> sine_table_f32{
|
||||
-2.42980180e-01, -2.19101240e-01, -1.95090322e-01,
|
||||
-1.70961889e-01, -1.46730474e-01, -1.22410675e-01,
|
||||
-9.80171403e-02, -7.35645636e-02, -4.90676743e-02,
|
||||
-2.45412285e-02, 0.00000000e+00, 0.00000000e+00
|
||||
-2.45412285e-02, 0.00000000e+00, 2.45412285e-02
|
||||
};
|
||||
|
||||
inline float sin_f32(const float w) {
|
||||
|
||||
+1
-1
Submodule hackrf updated: eff4a20022...b42a185503
@@ -0,0 +1,10 @@
|
||||
f=433920000,d=433 EU CN
|
||||
f=315000000,d=315 US CN
|
||||
f=418000000,d=418
|
||||
f=303000000,d=303
|
||||
f=310000000,d=310
|
||||
f=330000000,d=330
|
||||
f=350000000,d=350
|
||||
f=370000000,d=370
|
||||
f=390000000,d=390
|
||||
f=430500000,d=430 CN
|
||||
Reference in New Issue
Block a user