Compare commits

...

8 Commits

Author SHA1 Message Date
OpenSourceSDRLab 6e95288c1f Open‑Source Hardware Files for H4M Pro (#3298)
* Add H4M Pro PCB R1

* Add H4M Pro Gerber Files

* Add H4M Pro Schematic Files

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: gullradriel <3157857+gullradriel@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-08-25 07:16:33 +12:00
未来方舟 cad70d6e39 gpio fix; author: HTotoo (#3302) 2026-08-23 12:20:14 +02:00
Pezsma 84e7f517ba #3288 (#3297)
* Refactor clock delays and improve JTAG runtest timing logic
* Refactor time calculations to use CH_FREQUENCY for consistency across modules
* Improve delay mechanism for reference oscillator startup in portapack_tcxo_enable
* comment
* increasing the time
* copilot
2026-08-21 19:56:54 +02:00
未来方舟 0840756c0e remove fake brightness feature (#3296) 2026-08-20 20:46:29 +02:00
Matej Sochan 6dadefe86f Bug/big frequency (#3247)
* fix(ui): BigFrequency ignores set_dirty() when frequency is unchanged

paint() gated its entire body behind a frequency-changed check (_previous_frequency cache), so calling set_dirty() without changing the value (e.g. forcing a repaint after a style/theme change) silently did nothing. Moved the change check into set() instead, so paint() always redraws when called and set_dirty() behaves as expected.

* accidental deletion
2026-08-19 05:57:38 +02:00
Copilot ea40e3a46d Remove dead _previous_frequency state from BigFrequency widget (#3294)
* Initial plan

* Remove unused _previous_frequency member from BigFrequency and remove paint() guard

Co-authored-by: gullradriel <3157857+gullradriel@users.noreply.github.com>

* Move frequency dirty-check to set() to avoid redundant repaints

Co-authored-by: gullradriel <3157857+gullradriel@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: gullradriel <3157857+gullradriel@users.noreply.github.com>
2026-08-19 10:15:39 +08:00
rollorentner 86584feb16 Add Superrollo (GW60/HCS361) TX and RX (#3290)
keeloqtx: Superrollo 67-bit transmit mode with rolling counter.
subghzd: GW60 receive decoder (reports as KeeLoq; manufacturer key read from KEELOQKEYS/MFCODES). The manufacturer key is not included; add a Superrollo entry to MFCODES to enable TX/RX.
2026-08-16 20:10:04 +02:00
Bernd Herzog 47c94dbf26 SD Over USB for the Hackrf Pro (#3291)
* enabled sd over usb compilation
* updated hackrf pro usb stack
* fixed hackrf one code path
* fixed hackrf pro code path
* improved performance
* fixed sd card clock
* refactoring
* formatted code
2026-08-16 20:08:19 +02:00
67 changed files with 61263 additions and 1573 deletions
+1 -1
View File
@@ -59,7 +59,7 @@ void DfuMenu::paint(Painter& painter) {
text_info_line_7.set(to_string_dec_uint(shared_memory.m4_stack_usage, 6));
text_info_line_8.set(to_string_dec_uint(shared_memory.m4_performance_counter, 6));
text_info_line_9.set(to_string_dec_uint(shared_memory.m4_buffer_missed, 6));
text_info_line_10.set(to_string_dec_uint(chTimeNow() / 1000, 6));
text_info_line_10.set(to_string_dec_uint(chTimeNow() / CH_FREQUENCY, 6));
constexpr auto margin = 5;
+10 -20
View File
@@ -362,7 +362,6 @@ SetUIView::SetUIView(NavigationView& nav) {
&toggle_bias_tee,
&toggle_clock,
&toggle_mute,
&toggle_fake_brightness,
&toggle_sd_card,
&button_save,
&button_cancel});
@@ -399,7 +398,6 @@ SetUIView::SetUIView(NavigationView& nav) {
toggle_clock.set_value(!pmem::ui_hide_clock());
toggle_speaker.set_value(!pmem::ui_hide_speaker());
toggle_mute.set_value(!pmem::ui_hide_mute());
toggle_fake_brightness.set_value(!pmem::ui_hide_fake_brightness());
toggle_battery_icon.set_value(!pmem::ui_hide_battery_icon());
toggle_battery_text.set_value(!pmem::ui_hide_numeric_battery());
toggle_sd_card.set_value(!pmem::ui_hide_sd_card());
@@ -427,7 +425,6 @@ SetUIView::SetUIView(NavigationView& nav) {
pmem::set_ui_hide_clock(!toggle_clock.value());
pmem::set_ui_hide_speaker(!toggle_speaker.value());
pmem::set_ui_hide_mute(!toggle_mute.value());
pmem::set_ui_hide_fake_brightness(!toggle_fake_brightness.value());
pmem::set_ui_hide_battery_icon(!toggle_battery_icon.value());
pmem::set_ui_hide_numeric_battery(!toggle_battery_text.value());
pmem::set_ui_hide_sd_card(!toggle_sd_card.value());
@@ -921,22 +918,16 @@ void SetConfigModeView::focus() {
SetDisplayView::SetDisplayView(NavigationView& nav) {
add_children({&labels,
&field_fake_brightness,
&button_save,
&button_cancel,
&checkbox_brightness_switch});
&button_cancel});
if (portapack::device_type == portapack::DeviceType::DEV_PORTAPACK) {
add_child(&checkbox_ips_screen_switch);
}
field_fake_brightness.set_by_value(pmem::fake_brightness_level());
checkbox_brightness_switch.set_value(pmem::apply_fake_brightness());
checkbox_ips_screen_switch.set_value(pmem::config_lcd_normally_black());
button_save.on_select = [&nav, this](Button&) {
pmem::set_apply_fake_brightness(checkbox_brightness_switch.value());
pmem::set_fake_brightness_level(field_fake_brightness.selected_index_value());
if (checkbox_ips_screen_switch.value() != pmem::config_lcd_normally_black()) {
pmem::set_lcd_normally_black(checkbox_ips_screen_switch.value());
}
@@ -944,14 +935,6 @@ SetDisplayView::SetDisplayView(NavigationView& nav) {
nav.pop();
};
// only enable invert OR fake brightness
checkbox_ips_screen_switch.on_select = [this](Checkbox&, bool v) {
if (v) checkbox_brightness_switch.set_value(false);
};
checkbox_brightness_switch.on_select = [this](Checkbox&, bool v) {
if (v) checkbox_ips_screen_switch.set_value(false);
};
button_cancel.on_select = [&nav, this](Button&) {
nav.pop();
};
@@ -1029,10 +1012,17 @@ void SetTouchscreenThresholdView::focus() {
void SetTouchscreenThresholdView::on_frame_sync() {
if (!in_auto_detect) return;
uint32_t time_now = chTimeNow();
int32_t time_diff = time_now - time_start_auto_detect;
text_wait_timer.set("ETA " + to_string_dec_uint((10 - time_diff / 1000) <= 0 ? 0 : 10 - time_diff / 1000) + "s");
if (time_diff >= 10001 && !auto_detect_succeed_consumed) { // 10s
// Calculate elapsed seconds using CH_FREQUENCY
uint32_t elapsed_seconds = time_diff / CH_FREQUENCY;
int32_t remaining_seconds = 10 - (int32_t)elapsed_seconds;
if (remaining_seconds < 0) remaining_seconds = 0;
text_wait_timer.set("ETA " + to_string_dec_uint(remaining_seconds) + "s");
if (elapsed_seconds >= 10 && !auto_detect_succeed_consumed) { // 10s
in_auto_detect = false;
text_wait_timer.hidden(true);
text_hint.set("OK, press save and reboot");
+1 -23
View File
@@ -404,12 +404,8 @@ class SetUIView : public View {
{19 * 8, 12 * 16 + 2, 16, 16},
&bitmap_icon_batt_text};
ImageToggle toggle_fake_brightness{
{21 * 8, 12 * 16 + 2, 16, 16},
&bitmap_icon_brightness};
ImageToggle toggle_sd_card{
{23 * 8, 12 * 16 + 2, 16, 16},
{21 * 8, 12 * 16 + 2, 16, 16},
&bitmap_sd_card_ok};
Button button_save{
@@ -811,7 +807,6 @@ class SetConfigModeView : public View {
"Cancel",
};
};
using portapack::persistent_memory::fake_brightness_level_options;
class SetDisplayView : public View {
public:
@@ -823,26 +818,9 @@ class SetDisplayView : public View {
private:
Labels labels{
{{1 * 8, 1 * 16}, "Limits screen brightness", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 2 * 16}, "(has a small performance", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 3 * 16}, "impact when enabled).", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 8 * 16}, "Brightness:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 10 * 16}, "REBOOT TO APPLY SCREEN TYPE", Theme::getInstance()->fg_light->foreground},
};
OptionsField field_fake_brightness{
{20 * 8, 8 * 16},
6,
{{"12.5%", fake_brightness_level_options::BRIGHTNESS_12p5},
{"25%", fake_brightness_level_options::BRIGHTNESS_25},
{"50%", fake_brightness_level_options::BRIGHTNESS_50}}};
Checkbox checkbox_brightness_switch{
{1 * 8, 5 * 16},
16,
"Enable brightness adjust"};
Checkbox checkbox_ips_screen_switch{
{1 * 8, 12 * 16},
23,
+4 -2
View File
@@ -74,7 +74,7 @@ void SubGhzDRecentEntryDetailView::update_data() {
console.writeln("Action: 0x" + to_string_hex(func_code));
}
if (entry_.sensorType == FPS_KEELOQ) {
if (entry_.sensorType == FPS_KEELOQ || entry_.sensorType == FPS_SUPERROLLO) {
console.writeln("Fix: " + to_string_hex(fix));
console.writeln("Encrypted: " + to_string_hex(encrypted));
console.writeln("Manufacturer: " + mf_name);
@@ -243,6 +243,8 @@ const char* SubGhzDView::getSensorTypeName(FPROTO_SUBGHZD_SENSOR type) {
return "Ido 11x";
case FPS_INTERTECHNOV3:
return "InterTehcno v3";
case FPS_SUPERROLLO:
return "Superrollo";
case FPS_KEELOQ:
return "KeeLoq";
case FPS_KINGGATESSTYLO4K:
@@ -671,7 +673,7 @@ void SubGhzDRecentEntryDetailView::parseProtocol() {
return;
}
if (entry_.sensorType == FPS_KEELOQ) {
if (entry_.sensorType == FPS_KEELOQ || entry_.sensorType == FPS_SUPERROLLO) {
uint64_t data_rev = FProtoGeneral::subghz_protocol_blocks_reverse_key(entry_.data, 64);
btn = data_rev >> 60;
+4 -8
View File
@@ -32,10 +32,6 @@
#include "core_control.hpp"
/* Set true to enable additional checks to ensure
* M4 and M0 are synchronized before passing messages. */
static constexpr bool enforce_core_sync = true;
/* Set true to enable check for baseband messages getting stuck.
* This implies the baseband thread is not dequeuing and has probably stalled.
* NB: This check adds a small amout of overhead to the message sending code
@@ -474,7 +470,7 @@ bool is_image_running() {
return baseband_image_running;
}
void run_image(const spi_flash::image_tag_t image_tag) {
void run_image(const spi_flash::image_tag_t image_tag, bool enforce_core_sync) {
if (baseband_image_running) {
chDbgPanic("BBRunning");
}
@@ -487,7 +483,7 @@ void run_image(const spi_flash::image_tag_t image_tag) {
creg::m4txevent::enable();
if constexpr (enforce_core_sync) {
if (enforce_core_sync) {
// Wait up to 3 seconds for baseband to start handling events.
auto count = 3'000u;
while (!shared_memory.baseband_ready && --count)
@@ -498,7 +494,7 @@ void run_image(const spi_flash::image_tag_t image_tag) {
}
}
void run_prepared_image(const uint32_t m4_code) {
void run_prepared_image(const uint32_t m4_code, bool enforce_core_sync) {
if (baseband_image_running) {
chDbgPanic("BBRunning");
}
@@ -511,7 +507,7 @@ void run_prepared_image(const uint32_t m4_code) {
creg::m4txevent::enable();
if constexpr (enforce_core_sync) {
if (enforce_core_sync) {
// Wait up to 3 seconds for baseband to start handling events.
auto count = 3'000u;
while (!shared_memory.baseband_ready && --count)
+2 -2
View File
@@ -131,8 +131,8 @@ void request_beep_stop();
void request_audio_beep(uint32_t freq, uint32_t sample_rate, uint32_t duration_ms);
bool is_image_running();
void run_image(const portapack::spi_flash::image_tag_t image_tag);
void run_prepared_image(const uint32_t m4_code);
void run_image(const portapack::spi_flash::image_tag_t image_tag, bool enforce_core_sync = true);
void run_prepared_image(const uint32_t m4_code, bool enforce_core_sync = true);
void shutdown();
void spectrum_streaming_start();
+13 -13
View File
@@ -428,7 +428,7 @@ void ClockManager::portapack_tcxo_enable() {
/* Delay >10ms at 96MHz clock speed for reference oscillator to start. */
/* Delay an additional 1ms (arbitrary) for the clock generator to detect a signal. */
volatile uint32_t delay = 240000 + 24000;
volatile uint32_t delay = 2400000 + 24000;
while (delay--);
}
@@ -668,37 +668,37 @@ ClockManager::ReferenceSource ClockManager::detect_reference_source() {
}
ClockManager::Reference ClockManager::choose_reference() {
#ifdef PRALINE
const auto detected_reference = detect_reference_source();
if ((detected_reference == ReferenceSource::External) ||
(detected_reference == ReferenceSource::PortaPack)) {
const auto frequency = measure_gp_clkin_frequency();
if ((frequency >= 9850000) && (frequency <= 10150000)) {
return {detected_reference, 10000000};
}
}
#else
#ifndef PRALINE
if (hackrf_r9) {
gpio_control::r9_clkin_en.setActive();
volatile uint32_t delay = 240000 + 24000;
// Allow extra time for slower TCXOs on clone boards to stabilize before measurement
volatile uint32_t delay = 240000 + 240000;
while (delay--);
}
#endif
// Determine reference source (respects user config and Si5351 loss-of-signal)
const auto detected_reference = detect_reference_source();
// If an external or PortaPack source is detected, verify its actual frequency
if ((detected_reference == ReferenceSource::External) ||
(detected_reference == ReferenceSource::PortaPack)) {
const auto frequency = measure_gp_clkin_frequency();
// Check if the measured frequency is within the valid 10 MHz range
if ((frequency >= 9850000) && (frequency <= 10150000)) {
return {detected_reference, 10000000};
}
}
#ifndef PRALINE
if (hackrf_r9) {
// Disable r9 clock input if the 10 MHz validation failed
gpio_control::r9_clkin_en.setInactive();
}
#endif
// Fallback: Disable PortaPack TCXO and default to the HackRF 25 MHz crystal
portapack_tcxo_disable();
return {ReferenceSource::Xtal, 25000000};
}
@@ -89,7 +89,6 @@ bool DebugDumpView::debug_dump_func() {
pmem_dump_file.write_line("encoder_dial_direction: " + to_string_dec_uint(encoder_dial_direction())); // 0 = normal, 1 = reverse
pmem_dump_file.write_line("config_mode_storage: 0x" + to_string_hex(config_mode_storage_direct(), 8));
pmem_dump_file.write_line("dst_config: 0x" + to_string_hex((uint32_t)config_dst().v, 8));
pmem_dump_file.write_line("fake_brightness_level: " + to_string_dec_uint(fake_brightness_level()));
pmem_dump_file.write_line("menu_color: 0x" + to_string_hex(menu_color().v, 4));
pmem_dump_file.write_line("touchscreen_threshold: " + to_string_dec_uint(touchscreen_threshold()));
@@ -105,7 +104,6 @@ bool DebugDumpView::debug_dump_func() {
pmem_dump_file.write_line("ui_config hide_clock: " + to_string_dec_uint(hide_clock()));
pmem_dump_file.write_line("ui_config clock_with_date: " + to_string_dec_uint(clock_with_date()));
pmem_dump_file.write_line("ui_config clkout_enabled: " + to_string_dec_uint(clkout_enabled()));
pmem_dump_file.write_line("ui_config apply_fake_brightness: " + to_string_dec_uint(apply_fake_brightness()));
pmem_dump_file.write_line("ui_config stealth_mode: " + to_string_dec_uint(stealth_mode()));
pmem_dump_file.write_line("ui_config config_login: " + to_string_dec_uint(config_login()));
pmem_dump_file.write_line("ui_config config_splash: " + to_string_dec_uint(config_splash()));
@@ -120,7 +118,6 @@ bool DebugDumpView::debug_dump_func() {
pmem_dump_file.write_line("ui_config2 hide_clock: " + to_string_dec_uint(ui_hide_clock()));
pmem_dump_file.write_line("ui_config2 hide_sd_card: " + to_string_dec_uint(ui_hide_sd_card()));
pmem_dump_file.write_line("ui_config2 hide_mute: " + to_string_dec_uint(ui_hide_mute()));
pmem_dump_file.write_line("ui_config2 hide_fake_brightness: " + to_string_dec_uint(ui_hide_fake_brightness()));
pmem_dump_file.write_line("ui_config2 hide_battery_icon: " + to_string_dec_uint(ui_hide_battery_icon()));
pmem_dump_file.write_line("ui_config2 hide_numeric_battery: " + to_string_dec_uint(ui_hide_numeric_battery()));
pmem_dump_file.write_line("ui_config2 theme_id: " + to_string_dec_uint(ui_theme_id()));
+5 -9
View File
@@ -407,6 +407,10 @@ set(EXTCPPSRC
external/aprs_tx/main.cpp
external/aprs_tx/ui_aprs_tx.cpp
#sd over usb
external/sdusb/main.cpp
external/sdusb/ui_sd_over_usb.cpp
)
set(EXTAPPLIST
@@ -505,13 +509,5 @@ set(EXTAPPLIST
ais_rx
aprs_rx
aprs_tx
sdusb
)
# sdusb has type conflicts with PRALINE (HackRF Pro) - add only for non-PRALINE builds
if(NOT BOARD STREQUAL "PRALINE")
list(APPEND EXTCPPSRC
external/sdusb/main.cpp
external/sdusb/ui_sd_over_usb.cpp
)
list(APPEND EXTAPPLIST sdusb)
endif()
+38 -2
View File
@@ -34,7 +34,7 @@ void KeeloqTXView::update_hop() {
hop = data.btn << 28 | (apri_serial & 0xFFF) << 16 | data.counter;
} else if (
data.mf_name == "DTM_Neo" || data.mf_name == "FAAC_RC,XT" || data.mf_name == "Mutanco_Mutancode" || data.mf_name == "Came_Space" || data.mf_name == "Genius_Bravo" || data.mf_name == "GSN" || data.mf_name == "Rosh" || data.mf_name == "Rossi" || data.mf_name == "Peccinin" || data.mf_name == "Steelmate" || data.mf_name == "Cardin_S449") {
data.mf_name == "DTM_Neo" || data.mf_name == "FAAC_RC,XT" || data.mf_name == "Mutanco_Mutancode" || data.mf_name == "Came_Space" || data.mf_name == "Genius_Bravo" || data.mf_name == "GSN" || data.mf_name == "Rosh" || data.mf_name == "Rossi" || data.mf_name == "Peccinin" || data.mf_name == "Steelmate" || data.mf_name == "Cardin_S449" || data.mf_name == "Superrollo") {
hop = data.btn << 28 | (data.serial & 0xFFF) << 16 | data.counter;
} else if (
data.mf_name == "NICE_Smilo" || data.mf_name == "NICE_MHOUSE" || data.mf_name == "JCM_Tech") {
@@ -75,6 +75,12 @@ void KeeloqTXView::update_payload() {
}
}
if (data.mf_name == "Superrollo") {
text_payload.set(to_string_hex((uint64_t)(uint32_t)encrypt));
encode_data_gw60((uint32_t)encrypt);
return;
}
payload = (uint64_t)fix << 32 | encrypt;
uint64_t preview_payload = FProtoGeneral::subghz_protocol_blocks_reverse_key(payload, 64);
@@ -83,6 +89,34 @@ void KeeloqTXView::update_payload() {
encode_data();
}
// Superrollo GW60 (HCS361) 67-bit OOK frame at Te=450us.
void KeeloqTXView::encode_data_gw60(uint32_t hop_enc) {
int bits[67];
for (uint32_t i = 0; i < 32; i++) bits[i] = (hop_enc >> i) & 1;
for (uint32_t i = 0; i < 28; i++) bits[32 + i] = (data.serial >> i) & 1;
for (uint32_t i = 0; i < 4; i++) bits[60 + i] = (data.btn >> i) & 1;
bits[64] = 1;
int crc0 = 0, crc1 = 0;
for (uint32_t i = 0; i < 65; i++) {
int new_crc1 = crc0 ^ bits[i];
int new_crc0 = new_crc1 ^ crc1;
crc0 = new_crc0 & 1;
crc1 = new_crc1 & 1;
}
bits[65] = crc0;
bits[66] = crc1;
std::string s{};
for (int i = 0; i < 9; i++) s += "100";
s += "11111111110000000000";
for (int i = 0; i < 67; i++) s += keeloq_fragments[bits[i]];
for (int i = 0; i < 18; i++) s += "0";
encoded_data = s;
pause_duration = 0;
}
void KeeloqTXView::encode_data() {
std::string fragments{};
@@ -206,9 +240,11 @@ void KeeloqTXView::start_tx() {
transmitter_model.enable();
const double te_us = (data.mf_name == "Superrollo") ? 450.0 : 400.0;
baseband::set_ook_data(
bitstream_length,
OOK_SAMPLERATE * (400.0 / 1000000.0),
OOK_SAMPLERATE * (te_us / 1000000.0),
repeat,
pause_duration);
}
@@ -106,6 +106,7 @@ class KeeloqTXView : public View {
std::string encoded_data{};
void encode_data();
void encode_data_gw60(uint32_t hop_enc);
uint32_t repeat = 4;
uint32_t pause_duration = 0;
+1 -1
View File
@@ -46,7 +46,7 @@ SdOverUsbView::SdOverUsbView(NavigationView& nav)
sdcStop(&SDCD1);
portapack::shutdown(true, false);
baseband::run_prepared_image(portapack::memory::map::m4_code.base());
baseband::run_prepared_image(portapack::memory::map::m4_code.base(), false);
m0_halt();
/* will not return*/
};
+9 -17
View File
@@ -359,15 +359,6 @@ SystemStatusView::SystemStatusView(
this->on_bias_tee();
};
button_fake_brightness.on_select = [this](ImageButton&) {
set_dirty();
pmem::toggle_fake_brightness_level();
refresh();
if (nullptr != parent()) {
parent()->set_dirty(); // The parent of NavigationView shal be the SystemView
}
};
button_camera.on_select = [this](ImageButton&) {
this->on_camera();
};
@@ -460,7 +451,6 @@ void SystemStatusView::refresh() {
// Display "Disable speaker" icon only if AK4951 Codec which has separate speaker/headphone control
if (audio::speaker_disable_supported() && !pmem::ui_hide_speaker()) status_icons.add(&toggle_speaker);
if (!pmem::ui_hide_fake_brightness()) status_icons.add(&button_fake_brightness);
if (battery::BatteryManagement::isDetected()) {
batt_was_inited = true;
if (!pmem::ui_hide_battery_icon()) {
@@ -493,9 +483,6 @@ void SystemStatusView::refresh() {
button_converter.set_bitmap(pmem::config_updown_converter() ? &bitmap_icon_downconvert : &bitmap_icon_upconvert);
button_converter.set_foreground(pmem::config_converter() ? Theme::getInstance()->fg_red->foreground : Theme::getInstance()->fg_light->foreground);
// Fake Brightness
button_fake_brightness.set_foreground(pmem::apply_fake_brightness() ? *Theme::getInstance()->status_active : Theme::getInstance()->fg_light->foreground);
set_dirty();
}
@@ -1143,13 +1130,18 @@ void SystemView::toggle_overlay() {
}
void SystemView::paint_overlay() {
static bool last_paint_state = false;
// Static variable to store the timestamp of the last update
static systime_t last_update_time = 0;
if (overlay_active) {
// paint background only every other second
if ((((chTimeNow() >> 10) & 0x01) == 0x01) == last_paint_state)
// Update exactly once per second (CH_FREQUENCY equals 1 second of ticks)
// This replaces the old hardcoded bit-shift logic for better portability
if ((chTimeNow() - last_update_time) < CH_FREQUENCY)
return;
last_paint_state = !last_paint_state;
// One second has passed, save the new timestamp
last_update_time = chTimeNow();
if (overlay_active == 1 && overlay)
overlay->set_dirty();
else if (overlay_active == 2 && overlay2)
-6
View File
@@ -307,12 +307,6 @@ class SystemStatusView : public View {
Theme::getInstance()->fg_light->foreground,
Theme::getInstance()->bg_dark->background};
ImageButton button_fake_brightness{
{0, 0, 2 * 8, 1 * 16},
&bitmap_icon_brightness,
*Theme::getInstance()->status_active,
Theme::getInstance()->bg_dark->background};
SDCardStatusView sd_card_status_view{
{0, UI_POS_Y(0), 2 * 8, 1 * 16}};
+1 -1
View File
@@ -1180,7 +1180,7 @@ static void cmd_sysinfo(BaseSequentialStream* chp, int argc, char* argv[]) {
"M4 stack: " + to_string_dec_uint(shared_memory.m4_stack_usage) + "\r\n" +
"M0 cpu%: " + to_string_dec_uint(shared_memory.m4_performance_counter) + "\r\n" +
"M4 miss: " + to_string_dec_uint(shared_memory.m4_buffer_missed) + "\r\n" +
"uptime: " + to_string_dec_uint(chTimeNow() / 1000) + "\r\n";
"uptime: " + to_string_dec_uint(chTimeNow() / CH_FREQUENCY) + "\r\n";
fillOBuffer(&((SerialUSBDriver*)chp)->oqueue, (const uint8_t*)info.c_str(), info.length());
return;
+21 -11
View File
@@ -748,13 +748,6 @@ set(MODE_CPPSRC
)
DeclareTargets(PRTT rtty_tx)
### SD over USB
set(MODE_INCDIR
${HACKRF_PATH}/firmware
${HACKRF_PATH}/firmware/common
${HACKRF_PATH}/firmware/libopencm3/include
)
### Morse TX
@@ -794,6 +787,15 @@ set(MODE_CPPSRC
)
DeclareTargets(PTET tetra_rx)
### SD over USB
set(MODE_INCDIR
${HACKRF_PATH}/firmware
${HACKRF_PATH}/firmware/common
${HACKRF_PATH}/firmware/libopencm3/include
${HACKRF_PATH}/firmware/hackrf_usb
sd_over_usb/
)
set(MODE_CPPSRC
sd_over_usb/proc_sd_over_usb.cpp
@@ -802,7 +804,9 @@ set(MODE_CPPSRC
sd_over_usb/diskio.c
sd_over_usb/sd_over_usb.c
sd_over_usb/usb_descriptor.c
sd_over_usb/hackrf_core.c
sd_over_usb/usb_api_transceiver.c
sd_over_usb/cpu_clock.c
sd_over_usb/pins.c
${HACKRF_PATH}/firmware/common/adc.c
${HACKRF_PATH}/firmware/common/selftest.c
@@ -829,6 +833,15 @@ set(MODE_CPPSRC
${HACKRF_PATH}/firmware/common/rffc5071_spi.c
${HACKRF_PATH}/firmware/common/rffc5071.c
${HACKRF_PATH}/firmware/common/gpdma.c
${HACKRF_PATH}/firmware/common/clock_gen.c
${HACKRF_PATH}/firmware/common/cpld_jtag.c
${HACKRF_PATH}/firmware/common/leds.c
${HACKRF_PATH}/firmware/common/power.c
${HACKRF_PATH}/firmware/common/clock_io.c
${HACKRF_PATH}/firmware/common/rom_iap.c
${HACKRF_PATH}/firmware/common/w25q80bv.c
${HACKRF_PATH}/firmware/common/w25q80bv_target.c
${HACKRF_PATH}/firmware/libopencm3/lib/cm3/nvic.c
${HACKRF_PATH}/firmware/libopencm3/lib/cm3/sync.c
@@ -836,10 +849,7 @@ set(MODE_CPPSRC
${HACKRF_PATH}/firmware/libopencm3/lib/lpc43xx/timer.c
${HACKRF_PATH}/firmware/libopencm3/lib/lpc43xx/i2c.c
)
# sd_over_usb has type conflicts with PRALINE (HackRF Pro) - disable for now
if(NOT BOARD STREQUAL "PRALINE")
DeclareTargets(PUSB sd_over_usb)
endif()
### HackRF "factory" firmware
+6 -3
View File
@@ -125,15 +125,18 @@ void update_performance_counters() {
if (performance_counter_active == 0x00)
return;
static bool last_paint_state = false;
if ((((chTimeNow() >> 10) & 0x01) == 0x01) == last_paint_state)
static systime_t last_update_time = 0;
// The MS2ST(1000) guarantees that this is exactly 1 second, regardless of the system clock setting.
if ((chTimeNow() - last_update_time) < MS2ST(1000))
return;
// Idle thread state is sometimes unuseable
if (chThdGetTicks(chSysGetIdleThread()) > 0x10000000)
return;
last_paint_state = !last_paint_state;
// Update the last update time
last_update_time = chTimeNow();
if (performance_counter_active == 0x01) {
auto utilisation = get_cpu_utilisation_in_percent();
+124
View File
@@ -0,0 +1,124 @@
#ifndef __FPROTO_SUPERROLLO_H__
#define __FPROTO_SUPERROLLO_H__
#include "subghzdbase.hpp"
// Superrollo GW60 (HCS361) 67-bit OOK decoder. Reports FPS_SUPERROLLO; SubGhzD
// treats FPS_SUPERROLLO like KeeLoq for decrypt/display (see ui_subghzd.cpp), so
// the "Superrollo" keystore key decrypts it while it stays identifiable in logs.
typedef enum : uint8_t {
SuperrolloStepReset = 0,
SuperrolloStepPreambleLow,
SuperrolloStepSyncLow,
SuperrolloStepSaveDuration,
SuperrolloStepCheckDuration,
} SuperrolloDecoderStep;
class FProtoSubGhzDSuperrollo : public FProtoSubGhzDBase {
public:
FProtoSubGhzDSuperrollo() {
sensorType = FPS_SUPERROLLO;
te_short = 450;
te_long = 900;
te_delta = 200;
min_count_bit_for_found = 64;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case SuperrolloStepReset:
if (level) {
if (DURATION_DIFF(duration, te_short) < te_delta) {
header_count++;
parser_step = SuperrolloStepPreambleLow;
} else if (
(header_count >= 4) &&
(DURATION_DIFF(duration, te_short * 10) < te_delta * 10)) {
parser_step = SuperrolloStepSyncLow;
} else {
header_count = 0;
}
} else {
header_count = 0;
}
break;
case SuperrolloStepPreambleLow:
if ((!level) && (DURATION_DIFF(duration, te_long) < te_delta)) {
parser_step = SuperrolloStepReset;
} else {
parser_step = SuperrolloStepReset;
header_count = 0;
}
break;
case SuperrolloStepSyncLow:
if ((!level) && (DURATION_DIFF(duration, te_short * 10) < te_delta * 10)) {
parser_step = SuperrolloStepSaveDuration;
decode_data = 0;
decode_count_bit = 0;
} else {
parser_step = SuperrolloStepReset;
header_count = 0;
}
break;
case SuperrolloStepSaveDuration:
if (level) {
te_last = duration;
parser_step = SuperrolloStepCheckDuration;
} else if (duration >= te_short * 12) {
endFrame();
}
break;
case SuperrolloStepCheckDuration:
if (!level) {
if ((DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_long) < te_delta)) {
if (decode_count_bit < min_count_bit_for_found)
subghz_protocol_blocks_add_bit(1);
else
decode_count_bit++;
parser_step = SuperrolloStepSaveDuration;
} else if (
(DURATION_DIFF(te_last, te_long) < te_delta) &&
(DURATION_DIFF(duration, te_short) < te_delta)) {
if (decode_count_bit < min_count_bit_for_found)
subghz_protocol_blocks_add_bit(0);
else
decode_count_bit++;
parser_step = SuperrolloStepSaveDuration;
} else if (duration >= te_short * 12) {
endFrame();
} else {
parser_step = SuperrolloStepReset;
header_count = 0;
}
} else {
parser_step = SuperrolloStepReset;
header_count = 0;
}
break;
}
}
uint16_t header_count = 0;
private:
void endFrame() {
if ((decode_count_bit >= min_count_bit_for_found) &&
(decode_count_bit <= min_count_bit_for_found + 3)) {
data_count_bit = min_count_bit_for_found;
if (callback) callback(this);
}
parser_step = SuperrolloStepReset;
decode_data = 0;
decode_count_bit = 0;
header_count = 0;
}
};
#endif
@@ -29,6 +29,7 @@ So include here the .hpp, and add a new element to the protos vector in the cons
#include "s-ido.hpp"
#include "s-intertechnov3.hpp"
#include "s-keeloq.hpp"
#include "s-superrollo.hpp"
#include "s-kinggates_stylo_4k.hpp"
#include "s-linear.hpp"
#include "s-linear_delta3.hpp"
@@ -85,6 +86,7 @@ class SubGhzDProtos : public FProtoListGeneral {
protos[FPS_IDO] = new FProtoSubGhzDIdo();
protos[FPS_INTERTECHNOV3] = new FProtoSubGhzDIntertechnoV3();
protos[FPS_KEELOQ] = new FProtoSubGhzDKeeLoq();
protos[FPS_SUPERROLLO] = new FProtoSubGhzDSuperrollo();
protos[FPS_KINGGATESSTYLO4K] = new FProtoSubGhzDKinggatesStylo4K();
protos[FPS_LINEAR] = new FProtoSubGhzDLinear();
protos[FPS_LINEARDELTA3] = new FProtoSubGhzDLinearDelta3();
@@ -60,6 +60,7 @@ enum FPROTO_SUBGHZD_SENSOR : uint8_t {
FPS_MARANTEC24,
FPS_HOLTEKHT6P20B,
FPS_RESTAURANT_PAGER,
FPS_SUPERROLLO,
FPS_COUNT
};
+263
View File
@@ -0,0 +1,263 @@
/*
* Copyright 2026 Great Scott Gadgets <info@greatscottgadgets.com>
*
* This file is part of HackRF.
*
* 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 "cpu_clock.h"
#include <stdint.h>
#include <libopencm3/lpc43xx/cgu.h>
#if defined(IS_JAWBREAKER) || defined(IS_HACKRF_ONE) || defined(IS_PRALINE)
#include <libopencm3/lpc43xx/ccu.h>
#endif
#include "delay.h"
#include "i2c_bus.h"
#include "i2c_lpc.h"
#include "si5351c.h"
/* We start with the CPU clock at 96MHz */
unsigned int cpu_clock_mhz = 96;
/*
Configure PLL1 (Main MCU Clock) to max speed (204MHz).
Note: PLL1 clock is used by M4/M0 core, Peripheral, APB1.
This function shall be called after cpu_clock_init().
*/
static void cpu_clock_pll1_max_speed(void) {
uint32_t reg_val;
/* This function implements the sequence recommended in:
* UM10503 Rev 2.4 (Aug 2018), section 13.2.1.1, page 167. */
/* 1. Select the IRC as BASE_M4_CLK source. */
reg_val = CGU_BASE_M4_CLK;
reg_val &= ~CGU_BASE_M4_CLK_CLK_SEL_MASK;
reg_val |= CGU_BASE_M4_CLK_CLK_SEL(CGU_SRC_IRC) | CGU_BASE_M4_CLK_AUTOBLOCK(1);
CGU_BASE_M4_CLK = reg_val;
/* CPU is now at 12MHz */
cpu_clock_mhz = 12;
/* 2. Enable the crystal oscillator. */
CGU_XTAL_OSC_CTRL &= ~CGU_XTAL_OSC_CTRL_ENABLE_MASK;
/* 3. Wait 250us. */
delay_us(250);
/* 4. Set the AUTOBLOCK bit. */
CGU_PLL1_CTRL |= CGU_PLL1_CTRL_AUTOBLOCK(1);
/* 5. Reconfigure PLL1 to produce the final output frequency, with the
* crystal oscillator as clock source. */
reg_val = CGU_PLL1_CTRL;
// clang-format off
reg_val &= ~( CGU_PLL1_CTRL_CLK_SEL_MASK |
CGU_PLL1_CTRL_PD_MASK |
CGU_PLL1_CTRL_FBSEL_MASK |
CGU_PLL1_CTRL_BYPASS_MASK |
CGU_PLL1_CTRL_DIRECT_MASK |
CGU_PLL1_CTRL_PSEL_MASK |
CGU_PLL1_CTRL_MSEL_MASK |
CGU_PLL1_CTRL_NSEL_MASK );
/* Set PLL1 up to 12MHz * 17 = 204MHz.
* Direct mode: FCLKOUT = FCCO = M*(FCLKIN/N) */
reg_val |= CGU_PLL1_CTRL_CLK_SEL(CGU_SRC_XTAL) |
CGU_PLL1_CTRL_PSEL(0) |
CGU_PLL1_CTRL_NSEL(0) |
CGU_PLL1_CTRL_MSEL(16) |
CGU_PLL1_CTRL_FBSEL(0) |
CGU_PLL1_CTRL_DIRECT(1);
// clang-format on
CGU_PLL1_CTRL = reg_val;
/* 6. Wait for PLL1 to lock. */
while (!(CGU_PLL1_STAT & CGU_PLL1_STAT_LOCK_MASK)) {
}
/* 7. Set the PLL1 P-divider to divide by 2 (DIRECT=0, PSEL=0). */
CGU_PLL1_CTRL &= ~CGU_PLL1_CTRL_DIRECT_MASK;
/* 8. Select PLL1 as BASE_M4_CLK source. */
reg_val = CGU_BASE_M4_CLK;
reg_val &= ~CGU_BASE_M4_CLK_CLK_SEL_MASK;
reg_val |= CGU_BASE_M4_CLK_CLK_SEL(CGU_SRC_PLL1);
CGU_BASE_M4_CLK = reg_val;
/* CPU is now at 102MHz */
cpu_clock_mhz = 102;
/* 9. Wait 50us. */
delay_us(50);
/* 10. Set the PLL1 P-divider to direct output mode (DIRECT=1). */
CGU_PLL1_CTRL |= CGU_PLL1_CTRL_DIRECT_MASK;
/* CPU is now at 204MHz */
cpu_clock_mhz = 204;
}
/* clock startup for LPC4320 configure PLL1 to max speed (204MHz).
Note: PLL1 clock is used by M4/M0 core, Peripheral, APB1. */
void cpu_clock_init(void) {
/* use IRC as clock source for APB1 (including I2C0) */
CGU_BASE_APB1_CLK = CGU_BASE_APB1_CLK_CLK_SEL(CGU_SRC_IRC);
/* use IRC as clock source for APB3 */
CGU_BASE_APB3_CLK = CGU_BASE_APB3_CLK_CLK_SEL(CGU_SRC_IRC);
// FIXME disable I2C
/* Kick I2C0 down to 400kHz when we switch over to APB1 clock = 204MHz */
i2c_bus_start(si5351c.bus, &i2c_config_fast_clock);
/*
* 12MHz clock is entering LPC XTAL1/OSC input now.
* On HackRF One and Jawbreaker, there is a 12 MHz crystal at the LPC.
* Set up PLL1 to run from XTAL1 input.
*/
// FIXME a lot of the details here should be in a CGU driver
/* set xtal oscillator to low frequency mode */
CGU_XTAL_OSC_CTRL &= ~CGU_XTAL_OSC_CTRL_HF_MASK;
cpu_clock_pll1_max_speed();
/* use XTAL_OSC as clock source for APB1 */
CGU_BASE_APB1_CLK =
CGU_BASE_APB1_CLK_AUTOBLOCK(1) | CGU_BASE_APB1_CLK_CLK_SEL(CGU_SRC_XTAL);
/* use XTAL_OSC as clock source for APB3 */
CGU_BASE_APB3_CLK =
CGU_BASE_APB3_CLK_AUTOBLOCK(1) | CGU_BASE_APB3_CLK_CLK_SEL(CGU_SRC_XTAL);
/* use XTAL_OSC as clock source for PLL0USB */
CGU_PLL0USB_CTRL = CGU_PLL0USB_CTRL_PD(1) | CGU_PLL0USB_CTRL_AUTOBLOCK(1) |
CGU_PLL0USB_CTRL_CLK_SEL(CGU_SRC_XTAL);
while (CGU_PLL0USB_STAT & CGU_PLL0USB_STAT_LOCK_MASK) {
}
/* configure PLL0USB to produce 480 MHz clock from 12 MHz XTAL_OSC */
/* Values from User Manual v1.4 Table 94, for 12MHz oscillator. */
CGU_PLL0USB_MDIV = 0x06167FFA;
CGU_PLL0USB_NP_DIV = 0x00302062;
CGU_PLL0USB_CTRL |=
(CGU_PLL0USB_CTRL_PD(1) | CGU_PLL0USB_CTRL_DIRECTI(1) |
CGU_PLL0USB_CTRL_DIRECTO(1) | CGU_PLL0USB_CTRL_CLKEN(1));
/* power on PLL0USB and wait until stable */
CGU_PLL0USB_CTRL &= ~CGU_PLL0USB_CTRL_PD_MASK;
while (!(CGU_PLL0USB_STAT & CGU_PLL0USB_STAT_LOCK_MASK)) {
}
/* use PLL0USB as clock source for USB0 */
CGU_BASE_USB0_CLK = CGU_BASE_USB0_CLK_AUTOBLOCK(1) |
CGU_BASE_USB0_CLK_CLK_SEL(CGU_SRC_PLL0USB);
/* Switch peripheral clock over to use PLL1 (204MHz) */
CGU_BASE_PERIPH_CLK = CGU_BASE_PERIPH_CLK_AUTOBLOCK(1) |
CGU_BASE_PERIPH_CLK_CLK_SEL(CGU_SRC_PLL1);
/* Switch APB1 clock over to use PLL1 (204MHz) */
CGU_BASE_APB1_CLK =
CGU_BASE_APB1_CLK_AUTOBLOCK(1) | CGU_BASE_APB1_CLK_CLK_SEL(CGU_SRC_PLL1);
/* Switch APB3 clock over to use PLL1 (204MHz) */
CGU_BASE_APB3_CLK =
CGU_BASE_APB3_CLK_AUTOBLOCK(1) | CGU_BASE_APB3_CLK_CLK_SEL(CGU_SRC_PLL1);
CGU_BASE_SSP0_CLK =
CGU_BASE_SSP0_CLK_AUTOBLOCK(1) | CGU_BASE_SSP0_CLK_CLK_SEL(CGU_SRC_PLL1);
CGU_BASE_SSP1_CLK =
CGU_BASE_SSP1_CLK_AUTOBLOCK(1) | CGU_BASE_SSP1_CLK_CLK_SEL(CGU_SRC_PLL1);
#ifdef IS_NOT_RAD1O
if (IS_NOT_RAD1O) {
/* Disable unused clocks */
/* Start with PLLs */
CGU_PLL0AUDIO_CTRL = CGU_PLL0AUDIO_CTRL_PD(1);
/* Dividers */
CGU_IDIVA_CTRL = CGU_IDIVA_CTRL_PD(1);
CGU_IDIVB_CTRL = CGU_IDIVB_CTRL_PD(1);
CGU_IDIVC_CTRL = CGU_IDIVC_CTRL_PD(1);
CGU_IDIVD_CTRL = CGU_IDIVD_CTRL_PD(1);
CGU_IDIVE_CTRL = CGU_IDIVE_CTRL_PD(1);
/* Base clocks */
CGU_BASE_SPIFI_CLK =
CGU_BASE_SPIFI_CLK_PD(1); /* SPIFI is only used at boot */
CGU_BASE_USB1_CLK =
CGU_BASE_USB1_CLK_PD(1); /* USB1 is not exposed on HackRF */
CGU_BASE_PHY_RX_CLK = CGU_BASE_PHY_RX_CLK_PD(1);
CGU_BASE_PHY_TX_CLK = CGU_BASE_PHY_TX_CLK_PD(1);
CGU_BASE_LCD_CLK = CGU_BASE_LCD_CLK_PD(1);
CGU_BASE_VADC_CLK = CGU_BASE_VADC_CLK_PD(1);
CGU_BASE_SDIO_CLK = CGU_BASE_SDIO_CLK_PD(0) | CGU_BASE_SDIO_CLK_AUTOBLOCK(1) | CGU_BASE_SDIO_CLK_CLK_SEL(0x09);
CGU_BASE_UART0_CLK = CGU_BASE_UART0_CLK_PD(1);
CGU_BASE_UART1_CLK = CGU_BASE_UART1_CLK_PD(1);
CGU_BASE_UART2_CLK = CGU_BASE_UART2_CLK_PD(1);
CGU_BASE_UART3_CLK = CGU_BASE_UART3_CLK_PD(1);
CGU_BASE_OUT_CLK = CGU_BASE_OUT_CLK_PD(1);
CGU_BASE_AUDIO_CLK = CGU_BASE_AUDIO_CLK_PD(1);
CGU_BASE_CGU_OUT0_CLK = CGU_BASE_CGU_OUT0_CLK_PD(1);
CGU_BASE_CGU_OUT1_CLK = CGU_BASE_CGU_OUT1_CLK_PD(1);
/* Disable unused peripheral clocks */
CCU1_CLK_APB1_CAN1_CFG = 0;
CCU1_CLK_APB1_I2S_CFG = 0;
CCU1_CLK_APB1_MOTOCONPWM_CFG = 0;
// CCU1_CLK_APB3_ADC0_CFG = 0;
CCU1_CLK_APB3_ADC1_CFG = 0;
CCU1_CLK_APB3_CAN0_CFG = 0;
CCU1_CLK_APB3_DAC_CFG = 0;
// CCU1_CLK_M4_DMA_CFG = 0;
CCU1_CLK_M4_EMC_CFG = 0;
CCU1_CLK_M4_EMCDIV_CFG = 0;
CCU1_CLK_M4_ETHERNET_CFG = 0;
CCU1_CLK_M4_LCD_CFG = 0;
CCU1_CLK_M4_QEI_CFG = 0;
CCU1_CLK_M4_RITIMER_CFG = 0;
// CCU1_CLK_M4_SCT_CFG = 0;
// CCU1_CLK_M4_SDIO_CFG = 1;
CCU1_CLK_M4_SPIFI_CFG = 0;
CCU1_CLK_M4_TIMER0_CFG = 0;
// CCU1_CLK_M4_TIMER1_CFG = 0;
// CCU1_CLK_M4_TIMER2_CFG = 0;
CCU1_CLK_M4_TIMER3_CFG = 0;
CCU1_CLK_M4_UART1_CFG = 0;
CCU1_CLK_M4_USART0_CFG = 0;
CCU1_CLK_M4_USART2_CFG = 0;
CCU1_CLK_M4_USART3_CFG = 0;
CCU1_CLK_M4_USB1_CFG = 0;
CCU1_CLK_M4_VADC_CFG = 0;
// CCU1_CLK_SPIFI_CFG = 0;
// CCU1_CLK_USB1_CFG = 0;
// CCU1_CLK_VADC_CFG = 0;
// CCU2_CLK_APB0_UART1_CFG = 0;
// CCU2_CLK_APB0_USART0_CFG = 0;
// CCU2_CLK_APB2_USART2_CFG = 0;
// CCU2_CLK_APB2_USART3_CFG = 0;
// CCU2_CLK_APLL_CFG = 0;
// CCU2_CLK_SDIO_CFG = 0;
}
#endif
}
@@ -0,0 +1 @@
#pragma once
File diff suppressed because it is too large Load Diff
+254
View File
@@ -0,0 +1,254 @@
/*
* Copyright 2012-2026 Great Scott Gadgets <info@greatscottgadgets.com>
* Copyright 2012 Jared Boone <jared@sharebrained.com>
* Copyright 2013 Benjamin Vernoux <titanmkd@gmail.com>
*
* This file is part of HackRF.
*
* 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 "pins.h"
#include <libopencm3/lpc43xx/scu.h>
#include "gpio.h"
#include "leds.h"
#include "platform_detect.h"
#include "platform_gpio.h"
#include "platform_scu.h"
#include "power.h"
#ifdef IS_PRALINE
#include "clock_io.h"
#endif
void pins_shutdown(void) {
/* Configure all GPIO as Input (safe state) */
// gpio_init();
/* Detect Platform */
const platform_gpio_t* gpio = platform_gpio();
const platform_scu_t* scu = platform_scu();
/* TDI and TMS pull-ups are required in all JTAG-compliant devices.
*
* The HackRF CPLD is always present, so let the CPLD pull up its TDI and TMS.
*
* The PortaPack may not be present, so pull up the PortaPack TMS pin from the
* microcontroller.
*
* TCK is recommended to be held low, so use microcontroller pull-down.
*
* TDO is undriven except when in Shift-IR or Shift-DR phases.
* Use the microcontroller to pull down to keep from floating.
*
* LPC43xx pull-up and pull-down resistors are approximately 53K.
*/
#ifdef IS_EXPANSION_COMPATIBLE
if (IS_EXPANSION_COMPATIBLE) {
scu_pinmux(scu->PINMUX_PP_TMS, SCU_GPIO_PUP | SCU_CONF_FUNCTION0);
scu_pinmux(scu->PINMUX_PP_TDO, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
}
#endif
scu_pinmux(scu->PINMUX_CPLD_TCK, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
#ifdef IS_NOT_PRALINE
if (IS_NOT_PRALINE) {
scu_pinmux(scu->PINMUX_CPLD_TMS, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0);
scu_pinmux(scu->PINMUX_CPLD_TDI, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0);
scu_pinmux(scu->PINMUX_CPLD_TDO, SCU_GPIO_PDN | SCU_CONF_FUNCTION4);
}
#endif
/* Configure SCU Pin Mux as GPIO */
scu_pinmux(scu->PINMUX_LED1, SCU_GPIO_NOPULL);
scu_pinmux(scu->PINMUX_LED2, SCU_GPIO_NOPULL);
scu_pinmux(scu->PINMUX_LED3, SCU_GPIO_NOPULL);
#ifdef IS_RAD1O
if (IS_RAD1O) {
scu_pinmux(scu->PINMUX_LED4, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION4);
}
#endif
#ifdef IS_PRALINE
if (IS_PRALINE) {
scu_pinmux(scu->PINMUX_LED4, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0);
}
#endif
/* Configure USB indicators */
#ifdef IS_JAWBREAKER
if (IS_JAWBREAKER) {
scu_pinmux(scu->PINMUX_USB_LED0, SCU_CONF_FUNCTION3);
scu_pinmux(scu->PINMUX_USB_LED1, SCU_CONF_FUNCTION3);
}
#endif
#ifdef IS_PRALINE
if (IS_PRALINE) {
// disable_1v2_power();
// disable_3v3aux_power();
// gpio_output(gpio->gpio_1v2_enable);
// gpio_output(gpio->gpio_3v3aux_enable_n);
// scu_pinmux(scu->PINMUX_EN1V2, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
// scu_pinmux(scu->PINMUX_EN3V3_AUX_N, SCU_GPIO_FAST | SCU_CONF_FUNCTION4);
}
#endif
#ifdef IS_NOT_PRALINE
if (IS_NOT_PRALINE) {
disable_1v8_power();
#ifdef IS_H1_R9
if (IS_H1_R9) {
gpio_output(gpio->h1r9_1v8_enable);
scu_pinmux(scu->H1R9_EN1V8, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
}
#endif
#ifdef IS_NOT_H1_R9
if (IS_NOT_H1_R9) {
gpio_output(gpio->gpio_1v8_enable);
scu_pinmux(scu->PINMUX_EN1V8, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
}
#endif
}
#endif
#ifdef IS_H1_OR_PRALINE
if (IS_H1_OR_PRALINE) {
/* Safe state: start with VAA turned off: */
disable_rf_power();
/* Configure RF power supply (VAA) switch control signal as output */
#ifdef IS_H1_R9
if (IS_H1_R9) {
gpio_output(gpio->h1r9_vaa_disable);
}
#endif
#ifdef IS_NOT_H1_R9
if (IS_NOT_H1_R9) {
gpio_output(gpio->vaa_disable);
}
#endif
}
#endif
#ifdef IS_RAD1O
if (IS_RAD1O) {
/* Safe state: start with VAA turned off: */
disable_rf_power();
/* Configure RF power supply (VAA) switch control signal as output */
gpio_output(gpio->vaa_enable);
/* Disable unused clock outputs. They generate noise. */
scu_pinmux(CLK0, SCU_CLK_IN | SCU_CONF_FUNCTION7);
scu_pinmux(CLK2, SCU_CLK_IN | SCU_CONF_FUNCTION7);
scu_pinmux(scu->PINMUX_GPIO3_10, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
scu_pinmux(scu->PINMUX_GPIO3_11, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
}
#endif
#ifdef IS_PRALINE
if (IS_PRALINE) {
scu_pinmux(scu->P2_CTRL0, scu->P2_CTRL0_PINCFG);
scu_pinmux(scu->P2_CTRL1, scu->P2_CTRL1_PINCFG);
scu_pinmux(scu->P1_CTRL0, scu->P1_CTRL0_PINCFG);
scu_pinmux(scu->P1_CTRL1, scu->P1_CTRL1_PINCFG);
scu_pinmux(scu->P1_CTRL2, scu->P1_CTRL2_PINCFG);
scu_pinmux(scu->CLKIN_CTRL, scu->CLKIN_CTRL_PINCFG);
scu_pinmux(scu->AA_EN, scu->AA_EN_PINCFG);
scu_pinmux(scu->TRIGGER_IN, scu->TRIGGER_IN_PINCFG);
scu_pinmux(scu->TRIGGER_OUT, scu->TRIGGER_OUT_PINCFG);
scu_pinmux(scu->PPS_OUT, scu->PPS_OUT_PINCFG);
scu_pinmux(scu->SCT_CLK, scu->SCT_CLK_PINCFG);
scu_pinmux(scu->PINMUX_FPGA_CRESET, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
scu_pinmux(scu->PINMUX_FPGA_CDONE, SCU_GPIO_PDN | SCU_CONF_FUNCTION4);
scu_pinmux(scu->PINMUX_FPGA_SPI_CS, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
scu_pinmux(scu->SSP1_CIPO, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
scu_pinmux(scu->SSP1_COPI, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
scu_pinmux(scu->SSP1_SCK, SCU_GPIO_PDN | SCU_CONF_FUNCTION2);
scu_pinmux(scu->XCVR_ENABLE, SCU_GPIO_PDN | SCU_CONF_FUNCTION4);
scu_pinmux(scu->XCVR_RXENABLE, SCU_GPIO_PDN | SCU_CONF_FUNCTION4);
scu_pinmux(scu->XCVR_CS, SCU_GPIO_PDN | SCU_CONF_FUNCTION4);
scu_pinmux(scu->XCVR_RXHP, SCU_GPIO_PDN | SCU_CONF_FUNCTION4);
scu_pinmux(scu->XCVR_LD, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
scu_pinmux(scu->MIXER_LD, SCU_GPIO_PDN | SCU_CONF_FUNCTION4);
scu_pinmux(scu->MIXER_SCLK, SCU_GPIO_PDN | SCU_CONF_FUNCTION4);
scu_pinmux(scu->MIXER_SDATA, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
scu_pinmux(scu->MIXER_ENX, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
scu_pinmux(scu->MIXER_RESETX, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
scu_pinmux(scu->MIXER_ENBL, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
scu_pinmux(scu->AD_CS, SCU_GPIO_PDN | SCU_CONF_FUNCTION4);
p2_ctrl_set(P2_SIGNAL_CLK3);
p1_ctrl_set(P1_SIGNAL_CLKIN);
clkin_ctrl_set(CLKIN_SIGNAL_P1);
gpio_output(gpio->p2_ctrl0);
gpio_output(gpio->p2_ctrl1);
gpio_output(gpio->p1_ctrl0);
gpio_output(gpio->p1_ctrl1);
gpio_output(gpio->p1_ctrl2);
gpio_output(gpio->clkin_ctrl);
gpio_output(gpio->pps_out);
gpio_input(gpio->trigger_in);
gpio_input(gpio->trigger_out);
gpio_clear(gpio->fpga_cfg_spi_cs);
gpio_output(gpio->fpga_cfg_spi_cs);
gpio_clear(gpio->fpga_cfg_creset);
gpio_output(gpio->fpga_cfg_creset);
gpio_input(gpio->fpga_cfg_cdone);
gpio_input(gpio->max5864_select);
}
#endif
/* enable input on SCL and SDA pins */
SCU_SFSI2C0 = SCU_I2C0_NOMINAL;
}
/* Run after pins_shutdown() and prior to enabling power supplies. */
void pins_setup(void) {
/* Detect Platform */
const platform_gpio_t* gpio = platform_gpio();
const platform_scu_t* scu = platform_scu();
/* Configure LEDs */
led_off(0);
led_off(1);
led_off(2);
#ifdef IS_FOUR_LEDS
if (IS_FOUR_LEDS) {
led_off(3);
}
#endif
gpio_output(gpio->led[0]);
gpio_output(gpio->led[1]);
gpio_output(gpio->led[2]);
#ifdef IS_FOUR_LEDS
if (IS_FOUR_LEDS) {
gpio_output(gpio->led[3]);
}
#endif
/* Configure external clock in */
scu_pinmux(scu->PINMUX_GP_CLKIN, SCU_CLK_IN | SCU_CONF_FUNCTION1);
}
@@ -44,11 +44,11 @@ CH_IRQ_HANDLER(Vector60) {
}
int main() {
start_usb();
sdcStart(&SDCD1, nullptr);
if (sdcConnect(&SDCD1) == CH_FAILED) chDbgPanic("no sd card #1");
start_usb();
while (true) {
usb_transfer();
}
+145 -8
View File
@@ -23,14 +23,35 @@
#include "scsi.h"
#include "diskio.h"
#include "gpio_lpc.h"
#include "delay.h"
#include <libopencm3/lpc43xx/scu.h>
#include <libopencm3/lpc43xx/rgu.h>
#include <libopencm3/lpc43xx/wwdt.h>
#include "string.h"
/* Maximum number of 512-byte blocks transferred per SD command / USB bulk
* transfer. The data region of usb_bulk_buffer is 16 KiB (0x0000..0x3FFF),
* so this must be <= 32. It is split into two equal halves for double
* buffering. */
#define MAX_BLOCKS_PER_TRANSFER 32
#define HALF_BLOCKS (MAX_BLOCKS_PER_TRANSFER / 2)
volatile bool usb_bulk_block_done = false;
void delay(uint32_t duration);
/* Per-buffer-half completion flags for the asynchronous (double-buffered)
* bulk transfers. Index 0 = usb_bulk_buffer[0], index 1 = second half. */
volatile bool usb_bulk_block_done_async[2] = {false, false};
static uint32_t usb_bulk_buffer_index(const void* const data) {
return (data == &usb_bulk_buffer[HALF_BLOCKS * 512]) ? 1 : 0;
}
void usb_bulk_block_cb_async(void* user_data, unsigned int bytes_transferred) {
const uint32_t idx = (uint32_t)(uintptr_t)user_data;
usb_bulk_block_done_async[idx] = true;
(void)bytes_transferred;
}
void usb_bulk_block_cb(void* user_data, unsigned int bytes_transferred) {
usb_bulk_block_done = true;
@@ -65,6 +86,49 @@ void usb_receive_bulk(void* const data, const uint32_t maximum_length) {
while (!usb_bulk_block_done);
}
/* Schedule a bulk IN transfer without waiting for it to complete. The caller
* must later call usb_send_bulk_wait_finish() with the same buffer before
* reusing it. */
void usb_send_bulk_start(void* const data, const uint32_t maximum_length) {
const uint32_t idx = usb_bulk_buffer_index(data);
usb_bulk_block_done_async[idx] = false;
usb_transfer_schedule_block(
&usb_endpoint_bulk_in,
data,
maximum_length,
usb_bulk_block_cb_async,
(void*)(uintptr_t)idx);
}
/* Wait for a bulk IN transfer scheduled by usb_send_bulk_start() to finish. */
void usb_send_bulk_wait_finish(void* const data) {
const uint32_t idx = usb_bulk_buffer_index(data);
while (!usb_bulk_block_done_async[idx]);
}
/* Schedule a bulk OUT transfer without waiting for it to complete. The caller
* must later call usb_receive_bulk_finish() with the same buffer before
* reading from it. */
void usb_receive_bulk_start(void* const data, const uint32_t maximum_length) {
const uint32_t idx = usb_bulk_buffer_index(data);
usb_bulk_block_done_async[idx] = false;
usb_transfer_schedule_block(
&usb_endpoint_bulk_out,
data,
maximum_length,
usb_bulk_block_cb_async,
(void*)(uintptr_t)idx);
}
/* Wait for a bulk OUT transfer scheduled by usb_receive_bulk_start() to
* finish. */
void usb_receive_bulk_finish(void* const data) {
const uint32_t idx = usb_bulk_buffer_index(data);
while (!usb_bulk_block_done_async[idx]);
}
void usb_send_csw(msd_cbw_t* msd_cbw_data, uint8_t status) {
msd_csw_t csw = {
.signature = MSD_CSW_SIGNATURE,
@@ -213,22 +277,95 @@ static data_request_t decode_data_request(const uint8_t* cmd) {
uint8_t data_read10(msd_cbw_t* msd_cbw_data) {
data_request_t req = decode_data_request(msd_cbw_data->cmd_data);
for (size_t block_index = 0; block_index < req.blk_cnt; block_index++) {
read_block(req.first_lba + block_index, &usb_bulk_buffer[0], 1 /* n blocks */);
usb_send_bulk(&usb_bulk_buffer[0], 512);
uint32_t lba = req.first_lba;
uint32_t remaining = req.blk_cnt;
uint8_t* buf[2] = {&usb_bulk_buffer[0], &usb_bulk_buffer[HALF_BLOCKS * 512]};
uint32_t buf_idx = 0;
uint8_t* in_flight = NULL;
if (remaining == 0)
return 0;
/* Read the first chunk and start sending it. */
uint32_t n = (remaining > HALF_BLOCKS) ? HALF_BLOCKS : remaining;
if (read_block(lba, buf[0], n))
return 1;
usb_send_bulk_start(buf[0], n * 512);
in_flight = buf[0];
lba += n;
remaining -= n;
/* While USB sends the previous chunk, read the next one into the other
* half of the buffer. */
while (remaining > 0) {
buf_idx ^= 1;
n = (remaining > HALF_BLOCKS) ? HALF_BLOCKS : remaining;
if (read_block(lba, buf[buf_idx], n)) {
usb_send_bulk_wait_finish(in_flight);
return 1;
}
usb_send_bulk_wait_finish(in_flight);
usb_send_bulk_start(buf[buf_idx], n * 512);
in_flight = buf[buf_idx];
lba += n;
remaining -= n;
}
usb_send_bulk_wait_finish(in_flight);
return 0;
}
uint8_t data_write10(msd_cbw_t* msd_cbw_data) {
data_request_t req = decode_data_request(msd_cbw_data->cmd_data);
for (size_t block_index = 0; block_index < req.blk_cnt; block_index++) {
usb_receive_bulk(&usb_bulk_buffer[0], 512);
write_block(req.first_lba + block_index, &usb_bulk_buffer[0], 1 /* n blocks */);
uint32_t lba = req.first_lba;
uint32_t remaining = req.blk_cnt;
uint8_t* buf[2] = {&usb_bulk_buffer[0], &usb_bulk_buffer[HALF_BLOCKS * 512]};
uint32_t buf_idx = 0;
uint8_t* pending_buf = NULL;
uint32_t pending_lba = 0;
uint32_t pending_n = 0;
if (remaining == 0)
return 0;
/* Start receiving the first chunk. */
uint32_t n = (remaining > HALF_BLOCKS) ? HALF_BLOCKS : remaining;
usb_receive_bulk_start(buf[0], n * 512);
pending_buf = buf[0];
pending_lba = lba;
pending_n = n;
lba += n;
remaining -= n;
/* While USB receives the next chunk, write the previous one to the SD
* card. */
while (remaining > 0) {
buf_idx ^= 1;
n = (remaining > HALF_BLOCKS) ? HALF_BLOCKS : remaining;
usb_receive_bulk_start(buf[buf_idx], n * 512);
usb_receive_bulk_finish(pending_buf);
if (write_block(pending_lba, pending_buf, pending_n)) {
usb_receive_bulk_finish(buf[buf_idx]);
return 1;
}
pending_buf = buf[buf_idx];
pending_lba = lba;
pending_n = n;
lba += n;
remaining -= n;
}
usb_receive_bulk_finish(pending_buf);
if (write_block(pending_lba, pending_buf, pending_n))
return 1;
return 0;
}
@@ -293,7 +430,7 @@ void scsi_command(msd_cbw_t* msd_cbw_data) {
gpio_output(&dfu);
gpio_clear(&dfu);
delay(50 * 40800);
delay_ms(50);
RESET_CTRL0 = (1 << 0);
break;
+79 -7
View File
@@ -23,6 +23,8 @@
#include "sd_over_usb.h"
#include "scsi.h"
#include "usb_descriptor.h"
#include <rom_iap.h>
#include "delay.h"
#include <string.h>
@@ -36,8 +38,14 @@
extern usb_configuration_t* usb_configurations[];
static const usb_device_t usb_device_sd_over_usb = {
.descriptor = usb_descriptor_device,
.descriptor_strings = usb_descriptor_strings,
#ifdef IS_NOT_PRALINE
.descriptor = usb_descriptor_device_hackrf,
.descriptor_strings = usb_descriptor_strings_hackrf_one,
#endif
#ifdef IS_PRALINE
.descriptor = usb_descriptor_device_hackrf,
.descriptor_strings = usb_descriptor_strings_praline,
#endif
.qualifier_descriptor = usb_descriptor_device_qualifier,
.configurations = &usb_configurations,
.configuration = 0,
@@ -91,16 +99,83 @@ void usb_configuration_changed(usb_device_t* const device) {
usb_endpoint_init(&usb_endpoint_bulk_out, false);
}
void usb_set_descriptor_by_serial_number(void) {
iap_cmd_res_t iap_cmd_res;
/* Read IAP Serial Number Identification */
iap_cmd_res.cmd_param.command_code = IAP_CMD_READ_SERIAL_NO;
iap_cmd_call(&iap_cmd_res);
if (iap_cmd_res.status_res.status_ret == CMD_SUCCESS) {
usb_descriptor_string_serial_number[0] =
USB_DESCRIPTOR_STRING_SERIAL_BUF_LEN;
usb_descriptor_string_serial_number[1] = USB_DESCRIPTOR_TYPE_STRING;
/* 32 characters of serial number, convert to UTF-16LE */
for (size_t i = 0; i < USB_DESCRIPTOR_STRING_SERIAL_LEN; i++) {
const uint_fast8_t nibble =
(iap_cmd_res.status_res.iap_result[i >> 3] >>
(28 - (i & 7) * 4)) &
0xf;
const char c =
(nibble > 9) ? ('a' + nibble - 10) : ('0' + nibble);
usb_descriptor_string_serial_number[2 + i * 2] = c;
usb_descriptor_string_serial_number[3 + i * 2] = 0x00;
}
} else {
usb_descriptor_string_serial_number[0] = 2;
usb_descriptor_string_serial_number[1] = USB_DESCRIPTOR_TYPE_STRING;
}
}
void start_usb(void) {
// Detect hardware platform before we do anything else.
detect_hardware_platform();
pin_setup();
board_id_t board_id = detected_platform();
pins_shutdown();
sgpio_pin_shutdown(&sgpio_config);
rf_path_pin_shutdown();
if (board_id != BOARD_ID_RAD1O) {
clock_gen_shutdown();
}
delay_ms(10);
pins_setup();
cpld_jtag_pin_setup();
cpu_clock_init();
memcpy(&usb_device, &usb_device_sd_over_usb, sizeof(usb_device_sd_over_usb));
#ifndef DFU_MODE
usb_set_descriptor_by_serial_number();
#endif
usb_set_configuration_changed_cb(usb_configuration_changed);
usb_peripheral_reset();
#ifdef IS_HACKRF_ONE
if (IS_HACKRF_ONE) {
memcpy(&usb_device,
&usb_device_sd_over_usb,
sizeof(usb_device_sd_over_usb));
}
#endif
#ifdef IS_JAWBREAKER
if (IS_JAWBREAKER) {
memcpy(&usb_device,
&usb_device_jawbreaker,
sizeof(usb_device_jawbreaker));
}
#endif
#ifdef IS_RAD1O
if (IS_RAD1O) {
memcpy(&usb_device, &usb_device_rad1o, sizeof(usb_device_rad1o));
}
#endif
#ifdef IS_PRALINE
if (IS_PRALINE) {
memcpy(&usb_device, &usb_device_sd_over_usb, sizeof(usb_device_sd_over_usb));
}
#endif
usb_device_init(0, &usb_device);
usb_queue_init(&usb_endpoint_control_out_queue);
@@ -109,9 +184,6 @@ void start_usb(void) {
usb_queue_init(&usb_endpoint_bulk_in_queue);
usb_endpoint_init(&usb_endpoint_control_out, false);
/* Match the new usb_endpoint_init() contract introduced upstream by
* db73ecbf, control IN needs ZLP for transfers whose length is a
* multiple of the EP0 max packet size, otherwise the host hangs. */
usb_endpoint_init(&usb_endpoint_control_in, true);
nvic_set_priority(NVIC_USB0_IRQ, 255);
@@ -0,0 +1,766 @@
/*
* Copyright 2012-2022 Great Scott Gadgets <info@greatscottgadgets.com>
* Copyright 2012 Jared Boone
* Copyright 2013 Benjamin Vernoux
* Copyright 2024 Bernd Herzog
*
* This file is part of HackRF.
*
* 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 "usb_api_transceiver.h"
#include <stdbool.h>
#include <stddef.h>
#include <libopencm3/cm3/nvic.h>
#include <libopencm3/lpc43xx/gpdma.h>
#include <libopencm3/lpc43xx/usb.h>
#include <clock_gen.h>
#include <fixed_point.h>
#include <gpdma.h>
#include <hackrf_ui.h>
#include <leds.h>
#include <m0_state.h>
#include <operacake_sctimer.h>
#include <platform_detect.h>
#include <radio.h>
#include <sgpio.h>
#include <streaming.h>
#include <transceiver_mode.h>
#include "common/usb.h"
#include <usb_queue.h>
#include <usb_request.h>
#include <usb_type.h>
#include "usb_buffer.h"
#include "usb_endpoint.h"
#define USB_TRANSFER_SIZE 0x4000
#define DMA_TRANSFER_SIZE 0x2000
#define BUF_HALF_MASK (USB_SAMP_BUFFER_SIZE >> 1)
// Unless we know the host knows our buffer size, we'll avoid leaving TX
// until we've transmitted all bytes sent by the host. This flag is cleared
// when the host requests our buffer size.
bool auto_tx_flush = true;
volatile uint32_t dma_started, dma_pending, usb_started, usb_completed;
typedef struct {
uint32_t freq_mhz;
uint32_t freq_hz;
} set_freq_params_t;
set_freq_params_t set_freq_params;
struct set_freq_explicit_params {
uint64_t if_freq_hz; /* intermediate frequency */
uint64_t lo_freq_hz; /* front-end local oscillator frequency */
uint8_t path; /* image rejection filter path */
};
struct set_freq_explicit_params explicit_params;
typedef struct {
uint32_t freq_hz;
uint32_t divider;
} set_sample_r_params_t;
set_sample_r_params_t set_sample_r_params;
void transceiver_dma_setup(void);
usb_request_status_t usb_vendor_request_set_baseband_filter_bandwidth(
usb_endpoint_t* const endpoint,
const usb_transfer_stage_t stage) {
if (stage == USB_TRANSFER_STAGE_SETUP) {
const uint32_t bandwidth =
(endpoint->setup.index << 16) | endpoint->setup.value;
radio_reg_write(
&radio,
RADIO_BANK_REQUESTED,
RADIO_BB_BANDWIDTH_TX,
bandwidth);
radio_reg_write(
&radio,
RADIO_BANK_REQUESTED,
RADIO_BB_BANDWIDTH_RX,
bandwidth);
usb_transfer_schedule_ack(endpoint->in);
}
return USB_REQUEST_STATUS_OK;
}
usb_request_status_t usb_vendor_request_set_freq(
usb_endpoint_t* const endpoint,
const usb_transfer_stage_t stage) {
if (stage == USB_TRANSFER_STAGE_SETUP) {
usb_transfer_schedule_block(
endpoint->out,
&set_freq_params,
sizeof(set_freq_params_t),
NULL,
NULL);
} else if (stage == USB_TRANSFER_STAGE_DATA) {
const uint64_t freq =
set_freq_params.freq_mhz * 1000000ULL + set_freq_params.freq_hz;
radio_reg_write(
&radio,
RADIO_BANK_REQUESTED,
RADIO_FREQUENCY_RF,
freq * FP_ONE_HZ);
radio_reg_write(
&radio,
RADIO_BANK_REQUESTED,
RADIO_FREQUENCY_IF,
RADIO_UNSET);
radio_reg_write(
&radio,
RADIO_BANK_REQUESTED,
RADIO_FREQUENCY_LO,
RADIO_UNSET);
radio_reg_write(
&radio,
RADIO_BANK_REQUESTED,
RADIO_IMAGE_REJECT,
RADIO_UNSET);
usb_transfer_schedule_ack(endpoint->in);
}
return USB_REQUEST_STATUS_OK;
}
usb_request_status_t usb_vendor_request_set_freq_explicit(
usb_endpoint_t* const endpoint,
const usb_transfer_stage_t stage) {
if (stage == USB_TRANSFER_STAGE_SETUP) {
usb_transfer_schedule_block(
endpoint->out,
&explicit_params,
sizeof(struct set_freq_explicit_params),
NULL,
NULL);
} else if (stage == USB_TRANSFER_STAGE_DATA) {
radio_reg_write(
&radio,
RADIO_BANK_REQUESTED,
RADIO_FREQUENCY_IF,
explicit_params.if_freq_hz * FP_ONE_HZ);
radio_reg_write(
&radio,
RADIO_BANK_REQUESTED,
RADIO_FREQUENCY_LO,
explicit_params.lo_freq_hz * FP_ONE_HZ);
radio_reg_write(
&radio,
RADIO_BANK_REQUESTED,
RADIO_IMAGE_REJECT,
explicit_params.path);
usb_transfer_schedule_ack(endpoint->in);
}
return USB_REQUEST_STATUS_OK;
}
/*
* Convert fractional sample rate to units of 1/(2**36) Hz.
*/
static inline fp_28_36_t round_sample_rate(uint64_t num, uint32_t denom) {
uint64_t q1, r1, q2, r2, q3;
if (denom == 0) {
denom = 1;
}
q1 = num / denom;
r1 = num % denom;
q2 = (r1 << 32) / denom;
r2 = (r1 << 32) % denom;
q3 = ((r2 << 4) + (denom >> 1)) / denom;
return (q1 << 36) + (q2 << 4) + q3;
}
usb_request_status_t usb_vendor_request_set_sample_rate_frac(
usb_endpoint_t* const endpoint,
const usb_transfer_stage_t stage) {
if (stage == USB_TRANSFER_STAGE_SETUP) {
usb_transfer_schedule_block(
endpoint->out,
&set_sample_r_params,
sizeof(set_sample_r_params_t),
NULL,
NULL);
} else if (stage == USB_TRANSFER_STAGE_DATA) {
uint32_t numerator = set_sample_r_params.freq_hz;
uint32_t denominator = set_sample_r_params.divider;
uint64_t value = round_sample_rate(numerator, denominator);
radio_reg_write(&radio, RADIO_BANK_REQUESTED, RADIO_SAMPLE_RATE, value);
usb_transfer_schedule_ack(endpoint->in);
}
return USB_REQUEST_STATUS_OK;
}
usb_request_status_t usb_vendor_request_set_amp_enable(
usb_endpoint_t* const endpoint,
const usb_transfer_stage_t stage) {
if (stage == USB_TRANSFER_STAGE_SETUP) {
radio_reg_write(
&radio,
RADIO_BANK_REQUESTED,
RADIO_GAIN_TX_RF,
endpoint->setup.value);
radio_reg_write(
&radio,
RADIO_BANK_REQUESTED,
RADIO_GAIN_RX_RF,
endpoint->setup.value);
usb_transfer_schedule_ack(endpoint->in);
}
return USB_REQUEST_STATUS_OK;
}
usb_request_status_t usb_vendor_request_set_lna_gain(
usb_endpoint_t* const endpoint,
const usb_transfer_stage_t stage) {
if (stage == USB_TRANSFER_STAGE_SETUP) {
uint8_t gain = endpoint->setup.index;
radio_reg_write(&radio, RADIO_BANK_REQUESTED, RADIO_GAIN_RX_IF, gain);
endpoint->buffer[0] = RADIO_OK;
usb_transfer_schedule_block(
endpoint->in,
&endpoint->buffer,
1,
NULL,
NULL);
usb_transfer_schedule_ack(endpoint->out);
}
return USB_REQUEST_STATUS_OK;
}
usb_request_status_t usb_vendor_request_set_vga_gain(
usb_endpoint_t* const endpoint,
const usb_transfer_stage_t stage) {
if (stage == USB_TRANSFER_STAGE_SETUP) {
uint8_t gain = endpoint->setup.index;
radio_reg_write(&radio, RADIO_BANK_REQUESTED, RADIO_GAIN_RX_BB, gain);
endpoint->buffer[0] = RADIO_OK;
usb_transfer_schedule_block(
endpoint->in,
&endpoint->buffer,
1,
NULL,
NULL);
usb_transfer_schedule_ack(endpoint->out);
}
return USB_REQUEST_STATUS_OK;
}
usb_request_status_t usb_vendor_request_set_txvga_gain(
usb_endpoint_t* const endpoint,
const usb_transfer_stage_t stage) {
if (stage == USB_TRANSFER_STAGE_SETUP) {
uint8_t gain = endpoint->setup.index;
radio_reg_write(&radio, RADIO_BANK_REQUESTED, RADIO_GAIN_TX_IF, gain);
endpoint->buffer[0] = RADIO_OK;
usb_transfer_schedule_block(
endpoint->in,
&endpoint->buffer,
1,
NULL,
NULL);
usb_transfer_schedule_ack(endpoint->out);
}
return USB_REQUEST_STATUS_OK;
}
usb_request_status_t usb_vendor_request_set_antenna_enable(
usb_endpoint_t* const endpoint,
const usb_transfer_stage_t stage) {
switch (detected_platform()) {
case BOARD_ID_HACKRF1_OG:
case BOARD_ID_HACKRF1_R9:
case BOARD_ID_PRALINE:
// supported
break;
default:
return USB_REQUEST_STATUS_STALL;
}
if (stage == USB_TRANSFER_STAGE_SETUP) {
radio_reg_write(
&radio,
RADIO_BANK_REQUESTED,
RADIO_BIAS_TEE,
endpoint->setup.value);
usb_transfer_schedule_ack(endpoint->in);
}
return USB_REQUEST_STATUS_OK;
}
static volatile uint32_t _tx_underrun_limit;
static volatile uint32_t _rx_overrun_limit;
volatile transceiver_request_t transceiver_request = {
.mode = TRANSCEIVER_MODE_OFF,
.seq = 0,
};
void transceiver_usb_setup_complete(usb_endpoint_t* const endpoint) {
if (transceiver_request.mode == TRANSCEIVER_MODE_TX &&
endpoint->setup.request == 1 && auto_tx_flush) {
// This is a request to leave TX mode. Do so but NAK for now.
request_transceiver_mode(endpoint->setup.value);
} else {
usb_setup_complete(endpoint);
}
}
// Must be called from an atomic context (normally USB ISR)
void request_transceiver_mode(transceiver_mode_t mode) {
usb_endpoint_flush(&usb_endpoint_bulk_in);
usb_endpoint_flush(&usb_endpoint_bulk_out);
transceiver_request.mode = mode;
transceiver_request.seq++;
}
void transceiver_shutdown(void) {
baseband_streaming_disable(&sgpio_config);
operacake_sctimer_reset_state();
usb_endpoint_flush(&usb_endpoint_bulk_in);
usb_endpoint_flush(&usb_endpoint_bulk_out);
led_off(LED2);
led_off(LED3);
radio_switch_opmode(&radio, TRANSCEIVER_MODE_OFF);
m0_set_mode(M0_MODE_IDLE);
}
void transceiver_startup(const transceiver_mode_t mode) {
dma_started = 0;
dma_pending = 0;
usb_started = 0;
usb_completed = 0;
transceiver_dma_setup();
radio_switch_opmode(&radio, mode);
switch (mode) {
case TRANSCEIVER_MODE_RX_SWEEP:
case TRANSCEIVER_MODE_RX:
led_off(LED3);
led_on(LED2);
m0_set_mode(M0_MODE_RX);
m0_state.shortfall_limit = _rx_overrun_limit;
break;
case TRANSCEIVER_MODE_TX:
led_off(LED2);
led_on(LED3);
m0_set_mode(M0_MODE_TX_START);
m0_state.shortfall_limit = _tx_underrun_limit;
break;
default:
break;
}
activate_best_clock_source();
}
usb_request_status_t usb_vendor_request_set_transceiver_mode(
usb_endpoint_t* const endpoint,
const usb_transfer_stage_t stage) {
if (stage == USB_TRANSFER_STAGE_SETUP) {
switch (endpoint->setup.value) {
case TRANSCEIVER_MODE_OFF:
case TRANSCEIVER_MODE_RX:
case TRANSCEIVER_MODE_TX:
case TRANSCEIVER_MODE_RX_SWEEP:
case TRANSCEIVER_MODE_CPLD_UPDATE:
request_transceiver_mode(endpoint->setup.value);
usb_transfer_schedule_ack(endpoint->in);
return USB_REQUEST_STATUS_OK;
default:
return USB_REQUEST_STATUS_STALL;
}
} else {
return USB_REQUEST_STATUS_OK;
}
}
usb_request_status_t usb_vendor_request_set_hw_sync_mode(
usb_endpoint_t* const endpoint,
const usb_transfer_stage_t stage) {
if (stage == USB_TRANSFER_STAGE_SETUP) {
radio_reg_write(
&radio,
RADIO_BANK_REQUESTED,
RADIO_TRIGGER,
endpoint->setup.value);
usb_transfer_schedule_ack(endpoint->in);
}
return USB_REQUEST_STATUS_OK;
}
usb_request_status_t usb_vendor_request_set_tx_underrun_limit(
usb_endpoint_t* const endpoint,
const usb_transfer_stage_t stage) {
if (stage == USB_TRANSFER_STAGE_SETUP) {
uint32_t value = (endpoint->setup.index << 16) + endpoint->setup.value;
_tx_underrun_limit = value;
usb_transfer_schedule_ack(endpoint->in);
}
return USB_REQUEST_STATUS_OK;
}
usb_request_status_t usb_vendor_request_set_rx_overrun_limit(
usb_endpoint_t* const endpoint,
const usb_transfer_stage_t stage) {
if (stage == USB_TRANSFER_STAGE_SETUP) {
uint32_t value = (endpoint->setup.index << 16) + endpoint->setup.value;
_rx_overrun_limit = value;
usb_transfer_schedule_ack(endpoint->in);
}
return USB_REQUEST_STATUS_OK;
}
usb_request_status_t usb_vendor_request_get_buffer_size(
usb_endpoint_t* const endpoint,
const usb_transfer_stage_t stage) {
if (stage == USB_TRANSFER_STAGE_SETUP) {
uint32_t value = USB_SAMP_BUFFER_SIZE + USB_BULK_BUFFER_SIZE;
endpoint->buffer[0] = value & 0xff;
endpoint->buffer[1] = (value & 0xff00) >> 8;
endpoint->buffer[2] = (value & 0xff0000) >> 16;
endpoint->buffer[3] = (value & 0xff000000) >> 24;
usb_transfer_schedule_block(
endpoint->in,
&endpoint->buffer,
4,
NULL,
NULL);
usb_transfer_schedule_ack(endpoint->out);
// We now know the host is aware of our buffer size, so it
// can make its own decisions about flushing the buffer.
auto_tx_flush = false;
return USB_REQUEST_STATUS_OK;
}
return USB_REQUEST_STATUS_OK;
}
/* clang-format off */
// Which GPDMA channel to use.
const uint32_t DMA_CHANNEL = 1;
// GPDMA CCONFIG register setting.
const uint32_t DMA_CONFIG =
GPDMA_CCONFIG_FLOWCNTRL(0) // memory-to-memory
| GPDMA_CCONFIG_IE(0) // no error interrupt
| GPDMA_CCONFIG_ITC(1) // terminal count interrupt
| GPDMA_CCONFIG_L(0) // do not lock
| GPDMA_CCONFIG_H(0); // do not halt
// GPDMA CCONTROL register setting (excluding TRANSFERSIZE field).
const uint32_t DMA_CONTROL =
GPDMA_CCONTROL_SBSIZE(7) // 256-transfer src bursts
| GPDMA_CCONTROL_DBSIZE(7) // 256-transfer dst bursts
| GPDMA_CCONTROL_SWIDTH(2) // 32-bit src transfers
| GPDMA_CCONTROL_DWIDTH(2) // 32-bit dst transfers
| GPDMA_CCONTROL_S(0) // AHB Master 0
| GPDMA_CCONTROL_D(1) // AHB Master 1
| GPDMA_CCONTROL_SI(1) // increment source
| GPDMA_CCONTROL_DI(1) // increment destination
| GPDMA_CCONTROL_PROT1(0) // user mode
| GPDMA_CCONTROL_PROT2(0) // not bufferable
| GPDMA_CCONTROL_PROT3(0) // not cacheable
| GPDMA_CCONTROL_I(1); // interrupt enabled
/* clang-format on */
// Called before any sequence of DMA transfers.
void transceiver_dma_setup(void) {
gpdma_controller_enable();
GPDMA_CCONFIG(DMA_CHANNEL) = DMA_CONFIG;
GPDMA_CCONTROL(DMA_CHANNEL) = DMA_CONTROL;
GPDMA_CLLI(DMA_CHANNEL) = 0;
GPDMA_INTTCCLEAR = (1 << DMA_CHANNEL);
nvic_enable_irq(NVIC_DMA_IRQ);
}
// Called to start each DMA transfer.
void transceiver_start_dma(void* src, void* dest, size_t size) {
uint32_t num_transfers = size >> 2;
GPDMA_CCONTROL(DMA_CHANNEL) = DMA_CONTROL | num_transfers;
GPDMA_CSRCADDR(DMA_CHANNEL) = (uint32_t)src;
GPDMA_CDESTADDR(DMA_CHANNEL) = (uint32_t)dest;
dma_pending = size;
gpdma_channel_enable(DMA_CHANNEL);
}
// Called when a DMA transfer completes.
void dma_isr(void) {
gpdma_channel_disable(DMA_CHANNEL);
GPDMA_INTTCCLEAR = (1 << DMA_CHANNEL);
m0_state.m4_count += dma_pending;
dma_pending = 0;
}
void transceiver_bulk_transfer_complete(void* user_data, unsigned int bytes_transferred) {
(void)user_data;
usb_completed += bytes_transferred;
}
typedef enum {
DIRECTION_RX,
DIRECTION_TX,
} direction_t;
void start_dma_if_possible(direction_t direction, size_t size) {
if (dma_pending) {
return;
}
uint32_t sampling_completed = m0_state.m0_count;
uint32_t dma_completed = m0_state.m4_count;
uint32_t samp_offset = dma_started & USB_SAMP_BUFFER_MASK;
uint32_t bulk_offset = dma_started & USB_BULK_BUFFER_MASK;
uint32_t data_available, space_in_use, space_available, samp_buf_margin;
uint8_t *dest, *src;
if (direction == DIRECTION_RX) {
data_available = sampling_completed - dma_started;
space_in_use = usb_completed - dma_completed;
space_available = USB_BULK_BUFFER_SIZE - space_in_use;
samp_buf_margin = USB_SAMP_BUFFER_SIZE - data_available;
src = &usb_samp_buffer[samp_offset];
dest = &usb_bulk_buffer[bulk_offset];
} else {
data_available = usb_completed - dma_started;
space_in_use = dma_completed - sampling_completed;
space_available = USB_SAMP_BUFFER_SIZE - space_in_use;
samp_buf_margin = space_in_use;
src = &usb_bulk_buffer[bulk_offset];
dest = &usb_samp_buffer[samp_offset];
}
if (data_available < size || size > space_available) {
return;
}
uint32_t m0_buf_half = sampling_completed & BUF_HALF_MASK;
uint32_t dma_buf_half = dma_started & BUF_HALF_MASK;
bool same_buf_half = m0_buf_half == dma_buf_half;
if (same_buf_half && samp_buf_margin >= (USB_SAMP_BUFFER_SIZE / 2)) {
return;
}
transceiver_start_dma(src, dest, size);
dma_started += size;
}
void start_usb_if_possible(direction_t direction) {
uint32_t bulk_offset = usb_started & USB_BULK_BUFFER_MASK;
uint32_t dma_completed = m0_state.m4_count;
uint32_t bytes_available;
usb_endpoint_t* usb_endpoint;
if (direction == DIRECTION_RX) {
bytes_available = dma_completed - usb_started;
usb_endpoint = &usb_endpoint_bulk_in;
} else {
uint32_t space_used = usb_started - dma_completed;
bytes_available = USB_BULK_BUFFER_SIZE - space_used;
usb_endpoint = &usb_endpoint_bulk_out;
}
if (bytes_available < USB_TRANSFER_SIZE) {
return;
}
usb_transfer_schedule_block(
usb_endpoint,
&usb_bulk_buffer[bulk_offset],
USB_TRANSFER_SIZE,
transceiver_bulk_transfer_complete,
NULL);
usb_started += USB_TRANSFER_SIZE;
}
int8_t saturation_buffer = 0;
uint64_t saturation_buffer_time = 0;
volatile uint64_t systick_counter = 0;
void sys_tick_handler(void) {
systick_counter++;
}
void rx_mode(uint32_t seq) {
transceiver_startup(TRANSCEIVER_MODE_RX);
baseband_streaming_enable(&sgpio_config);
while (transceiver_request.seq == seq) {
start_dma_if_possible(DIRECTION_RX, DMA_TRANSFER_SIZE);
start_usb_if_possible(DIRECTION_RX);
int8_t sample_value = *(
int8_t*)&usb_samp_buffer[m0_state.m0_count & USB_SAMP_BUFFER_MASK];
if (sample_value > saturation_buffer)
saturation_buffer = sample_value;
if (-sample_value > saturation_buffer)
saturation_buffer = -sample_value;
if (saturation_buffer_time + 4 < systick_counter) {
saturation_buffer_time = systick_counter;
hackrf_ui()->set_saturation(saturation_buffer);
saturation_buffer = 0;
}
radio_update(&radio);
}
transceiver_shutdown();
}
void tx_mode(uint32_t seq) {
transceiver_startup(TRANSCEIVER_MODE_TX);
// First, make transfers directly into the sample buffer to fill it.
for (int i = 0; i < (USB_SAMP_BUFFER_SIZE / USB_TRANSFER_SIZE); i++) {
// Set up transfer.
usb_transfer_schedule_block(
&usb_endpoint_bulk_out,
&usb_samp_buffer[usb_started],
USB_TRANSFER_SIZE,
transceiver_bulk_transfer_complete,
NULL);
usb_started += USB_TRANSFER_SIZE;
// Wait for the transfer to complete.
while (usb_completed < usb_started) {
// Handle the host switching modes before filling the buffer.
if (transceiver_request.seq != seq) {
transceiver_shutdown();
return;
}
radio_update(&radio);
}
}
// Sample buffer is now full. Update DMA counters accordingly.
dma_started = USB_SAMP_BUFFER_SIZE;
m0_state.m4_count = USB_SAMP_BUFFER_SIZE;
// Start transmitting samples.
baseband_streaming_enable(&sgpio_config);
// Continue feeding samples to the sample buffer.
while (transceiver_request.seq == seq) {
start_dma_if_possible(DIRECTION_TX, DMA_TRANSFER_SIZE);
start_usb_if_possible(DIRECTION_TX);
radio_update(&radio);
}
// Host has now requested to stop TX. If we're not auto-flushing, we
// should now stop TX immediately.
if (!auto_tx_flush) {
transceiver_shutdown();
return;
}
// Otherwise, we should now ensure all bytes sent by the host are
// transmitted before we leave TX. First, we should make sure all data
// currently in the USB bulk buffer reaches the sample buffer.
if ((usb_started - usb_completed) > 0) {
// We were part way through a 16KB firmware-side transfer when
// the transceiver mode change request to stop TX was received.
//
// We want to include the contents of that partial transfer in
// the data we move to the sample buffer.
//
// The transfer was already stopped by usb_endpoint_flush(),
// which was called from request_transceiver_mode().
//
// We will not have had a callback, and the transfer descriptor
// (dTD) will not have been updated, since the transfer did not
// complete.
//
// However, as long as we haven't started a new transfer, we
// can retrieve the partial byte count from the transfer
// overlay in the endpoint queue head (dQH) (UM10503 25.9.1).
usb_queue_head_t* const qh =
usb_queue_head(usb_endpoint_bulk_out.address);
unsigned int bytes_remaining =
(qh->total_bytes & USB_TD_DTD_TOKEN_TOTAL_BYTES_MASK) >>
USB_TD_DTD_TOKEN_TOTAL_BYTES_SHIFT;
unsigned int bytes_transferred = USB_TRANSFER_SIZE - bytes_remaining;
usb_completed += bytes_transferred;
}
// Feed the remaining data from the bulk buffer to the sample buffer.
// At this point, we also need to handle the case where there is less data
// to be transferred to the sample buffer than a full-sized DMA transfer.
// Any remainder of less than 4 bytes will be ignored; this is the chunk
// size of our DMA transfers.
while ((usb_completed - m0_state.m4_count) >= 4) {
uint32_t data_available = usb_completed - dma_started;
if (data_available > DMA_TRANSFER_SIZE) {
start_dma_if_possible(DIRECTION_TX, DMA_TRANSFER_SIZE);
} else {
start_dma_if_possible(DIRECTION_TX, data_available);
}
radio_update(&radio);
}
// Wait for the data in the sample buffer to be transmitted.
// Any remainder of less than 32 bytes will be ignored; this is
// the chunk size used by the M0 core to transfer samples to SGPIO.
while ((m0_state.m4_count - m0_state.m0_count) >= 32) {
radio_update(&radio);
}
// All data received from the host has now been transmitted.
// Now we can ACK the control request that took us out of TX mode.
usb_transfer_schedule_ack(usb_endpoint_control_in.in);
transceiver_shutdown();
}
void off_mode(uint32_t seq) {
while (transceiver_request.seq == seq) {
radio_update(&radio);
}
}
+233 -153
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2022 Great Scott Gadgets <info@greatscottgadgets.com>
* Copyright 2012-2026 Great Scott Gadgets <info@greatscottgadgets.com>
* Copyright 2012 Jared Boone
*
* This file is part of HackRF.
@@ -20,22 +20,13 @@
* Boston, MA 02110-1301, USA.
*/
#include <stdint.h>
#include "usb_type.h"
#include "usb_descriptor.h"
#define USB_VENDOR_ID (0x0781) /* SanDisk Corp. */
#include <stdint.h>
#ifdef HACKRF_ONE
#define USB_PRODUCT_ID (0xa7a8) /* SD card reader */
#elif JAWBREAKER
#define USB_PRODUCT_ID (0x604B)
#elif RAD1O
#define USB_PRODUCT_ID (0xCC15)
#else
#define USB_PRODUCT_ID (0xFFFF)
#endif
#include <usb_type.h>
#define USB_VENDOR_ID (0x0781) /* SanDisk Corp. */
#define USB_API_VERSION (0x0127) /* hardware revision */
@@ -50,7 +41,8 @@
#define USB_STRING_LANGID (0x0409)
uint8_t usb_descriptor_device[] = {
#if defined(IS_HACKRF_ONE) || defined(IS_PRALINE)
uint8_t usb_descriptor_device_hackrf[] = {
18, // bLength
USB_DESCRIPTOR_TYPE_DEVICE, // bDescriptorType
USB_WORD(0x0200), // bcdUSB
@@ -59,13 +51,50 @@ uint8_t usb_descriptor_device[] = {
0x00, // bDeviceProtocol
USB_MAX_PACKET0, // bMaxPacketSize0
USB_WORD(USB_VENDOR_ID), // idVendor
USB_WORD(USB_PRODUCT_ID), // idProduct
USB_WORD(0xa7a8), // idProduct /* SD card reader */
USB_WORD(USB_API_VERSION), // bcdDevice
0x01, // iManufacturer
0x02, // iProduct
0x04, // iSerialNumber
0x01 // bNumConfigurations
};
#endif
#ifdef IS_JAWBREAKER
uint8_t usb_descriptor_device_jawbreaker[] = {
18, // bLength
USB_DESCRIPTOR_TYPE_DEVICE, // bDescriptorType
USB_WORD(0x0200), // bcdUSB
0x00, // bDeviceClass
0x00, // bDeviceSubClass
0x00, // bDeviceProtocol
USB_MAX_PACKET0, // bMaxPacketSize0
USB_WORD(USB_VENDOR_ID), // idVendor
USB_WORD(0x604B), // idProduct
USB_WORD(USB_API_VERSION), // bcdDevice
0x01, // iManufacturer
0x02, // iProduct
0x04, // iSerialNumber
0x01 // bNumConfigurations
};
#endif
#ifdef IS_RAD1O
uint8_t usb_descriptor_device_rad1o[] = {
18, // bLength
USB_DESCRIPTOR_TYPE_DEVICE, // bDescriptorType
USB_WORD(0x0200), // bcdUSB
0x00, // bDeviceClass
0x00, // bDeviceSubClass
0x00, // bDeviceProtocol
USB_MAX_PACKET0, // bMaxPacketSize0
USB_WORD(USB_VENDOR_ID), // idVendor
USB_WORD(0xCC15), // idProduct
USB_WORD(USB_API_VERSION), // bcdDevice
0x01, // iManufacturer
0x02, // iProduct
0x04, // iSerialNumber
0x01 // bNumConfigurations
};
#endif
uint8_t usb_descriptor_device_qualifier[] = {
10, // bLength
@@ -161,162 +190,213 @@ uint8_t usb_descriptor_string_languages[] = {
// clang-format off
uint8_t usb_descriptor_string_manufacturer[] = {
40, // bLength
USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType
'G', 0x00,
'r', 0x00,
'e', 0x00,
'a', 0x00,
't', 0x00,
' ', 0x00,
'S', 0x00,
'c', 0x00,
'o', 0x00,
't', 0x00,
't', 0x00,
' ', 0x00,
'G', 0x00,
'a', 0x00,
'd', 0x00,
'g', 0x00,
'e', 0x00,
't', 0x00,
's', 0x00,
40, // bLength
USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType
'G', 0x00,
'r', 0x00,
'e', 0x00,
'a', 0x00,
't', 0x00,
' ', 0x00,
'S', 0x00,
'c', 0x00,
'o', 0x00,
't', 0x00,
't', 0x00,
' ', 0x00,
'G', 0x00,
'a', 0x00,
'd', 0x00,
'g', 0x00,
'e', 0x00,
't', 0x00,
's', 0x00,
};
uint8_t usb_descriptor_string_product[] = {
#ifdef HACKRF_ONE
34, // bLength
USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType
'P', 0x00,
'o', 0x00,
'r', 0x00,
't', 0x00,
'a', 0x00,
'P', 0x00,
'a', 0x00,
'c', 0x00,
'k', 0x00,
' ', 0x00,
#ifdef IS_HACKRF_ONE
uint8_t usb_descriptor_string_product_hackrf_one[] = {
34, // bLength
USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType
'P', 0x00,
'o', 0x00,
'r', 0x00,
't', 0x00,
'a', 0x00,
'P', 0x00,
'a', 0x00,
'c', 0x00,
'k', 0x00,
' ', 0x00,
'M', 0x00,
'a', 0x00,
'y', 0x00,
'h', 0x00,
'e', 0x00,
'm', 0x00,
#elif JAWBREAKER
36, // bLength
USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType
'H', 0x00,
'a', 0x00,
'c', 0x00,
'k', 0x00,
'R', 0x00,
'F', 0x00,
' ', 0x00,
'J', 0x00,
'a', 0x00,
'w', 0x00,
'b', 0x00,
'r', 0x00,
'e', 0x00,
'a', 0x00,
'k', 0x00,
'e', 0x00,
'r', 0x00,
#elif RAD1O
12, // bLength
USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType
'r', 0x00,
'a', 0x00,
'd', 0x00,
'1', 0x00,
'o', 0x00,
#else
14, // bLength
USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType
'H', 0x00,
'a', 0x00,
'c', 0x00,
'k', 0x00,
'R', 0x00,
'F', 0x00,
'M', 0x00,
'a', 0x00,
'y', 0x00,
'h', 0x00,
'e', 0x00,
'm', 0x00,
};
#endif
#ifdef IS_PRALINE
uint8_t usb_descriptor_string_product_praline[] = {
34, // bLength
USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType
'P', 0x00,
'o', 0x00,
'r', 0x00,
't', 0x00,
'a', 0x00,
'P', 0x00,
'a', 0x00,
'c', 0x00,
'k', 0x00,
' ', 0x00,
'M', 0x00,
'a', 0x00,
'y', 0x00,
'h', 0x00,
'e', 0x00,
'm', 0x00,
};
#endif
#ifdef IS_JAWBREAKER
uint8_t usb_descriptor_string_product_jawbreaker[] = {
36, // bLength
USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType
'H', 0x00,
'a', 0x00,
'c', 0x00,
'k', 0x00,
'R', 0x00,
'F', 0x00,
' ', 0x00,
'J', 0x00,
'a', 0x00,
'w', 0x00,
'b', 0x00,
'r', 0x00,
'e', 0x00,
'a', 0x00,
'k', 0x00,
'e', 0x00,
'r', 0x00,
};
#endif
#ifdef IS_RAD1O
uint8_t usb_descriptor_string_product_rad1o[] = {
12, // bLength
USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType
'r', 0x00,
'a', 0x00,
'd', 0x00,
'1', 0x00,
'o', 0x00,
};
#endif
uint8_t usb_descriptor_string_config_description[] = {
24, // bLength
USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType
'T', 0x00,
'r', 0x00,
'a', 0x00,
'n', 0x00,
's', 0x00,
'c', 0x00,
'e', 0x00,
'i', 0x00,
'v', 0x00,
'e', 0x00,
'r', 0x00,
24, // bLength
USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType
'T', 0x00,
'r', 0x00,
'a', 0x00,
'n', 0x00,
's', 0x00,
'c', 0x00,
'e', 0x00,
'i', 0x00,
'v', 0x00,
'e', 0x00,
'r', 0x00,
};
#ifdef DFU_MODE
uint8_t usb_descriptor_string_serial_number[] = {
30, // bLength
USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType
'R', 0x00,
'u', 0x00,
'n', 0x00,
'n', 0x00,
'i', 0x00,
'n', 0x00,
'g', 0x00,
'F', 0x00,
'r', 0x00,
'o', 0x00,
'm', 0x00,
'R', 0x00,
'A', 0x00,
'M', 0x00,
30, // bLength
USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType
'R', 0x00,
'u', 0x00,
'n', 0x00,
'n', 0x00,
'i', 0x00,
'n', 0x00,
'g', 0x00,
'F', 0x00,
'r', 0x00,
'o', 0x00,
'm', 0x00,
'R', 0x00,
'A', 0x00,
'M', 0x00,
};
#else
uint8_t usb_descriptor_string_serial_number[USB_DESCRIPTOR_STRING_SERIAL_BUF_LEN];
#endif
uint8_t* usb_descriptor_strings[] = {
usb_descriptor_string_languages,
usb_descriptor_string_manufacturer,
usb_descriptor_string_product,
usb_descriptor_string_config_description,
usb_descriptor_string_serial_number,
0, // TERMINATOR
#ifdef IS_HACKRF_ONE
uint8_t* usb_descriptor_strings_hackrf_one[] = {
usb_descriptor_string_languages,
usb_descriptor_string_manufacturer,
usb_descriptor_string_product_hackrf_one,
usb_descriptor_string_config_description,
usb_descriptor_string_serial_number,
0, // TERMINATOR
};
#endif
#ifdef IS_PRALINE
uint8_t* usb_descriptor_strings_praline[] = {
usb_descriptor_string_languages,
usb_descriptor_string_manufacturer,
usb_descriptor_string_product_praline,
usb_descriptor_string_config_description,
usb_descriptor_string_serial_number,
0, // TERMINATOR
};
#endif
#ifdef IS_JAWBREAKER
uint8_t* usb_descriptor_strings_jawbreaker[] = {
usb_descriptor_string_languages,
usb_descriptor_string_manufacturer,
usb_descriptor_string_product_jawbreaker,
usb_descriptor_string_config_description,
usb_descriptor_string_serial_number,
0, // TERMINATOR
};
#endif
#ifdef IS_RAD1O
uint8_t* usb_descriptor_strings_rad1o[] = {
usb_descriptor_string_languages,
usb_descriptor_string_manufacturer,
usb_descriptor_string_product_rad1o,
usb_descriptor_string_config_description,
usb_descriptor_string_serial_number,
0, // TERMINATOR
};
#endif
uint8_t wcid_string_descriptor[] = {
18, // bLength
USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType
'M', 0x00,
'S', 0x00,
'F', 0x00,
'T', 0x00,
'1', 0x00,
'0', 0x00,
'0', 0x00,
USB_WCID_VENDOR_REQ, // vendor request code for further descriptor
0x00
18, // bLength
USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType
'M', 0x00,
'S', 0x00,
'F', 0x00,
'T', 0x00,
'1', 0x00,
'0', 0x00,
'0', 0x00,
USB_WCID_VENDOR_REQ, // vendor request code for further descriptor
0x00
};
uint8_t wcid_feature_descriptor[] = {
0x28, 0x00, 0x00, 0x00, // bLength
USB_WORD(0x0100), // WCID version
USB_WORD(0x0004), // WICD descriptor index
0x01, // bNumSections
0x00,0x00,0x00,0x00,0x00,0x00,0x00, // Reserved
0x00, // bInterfaceNumber
0x01, // Reserved
'W', 'I', 'N', 'U', 'S', 'B', 0x00,0x00, // Compatible ID, padded with zeros
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // Sub-compatible ID
0x00,0x00,0x00,0x00,0x00,0x00 // Reserved
};
0x28, 0x00, 0x00, 0x00, // bLength
USB_WORD(0x0100), // WCID version
USB_WORD(0x0004), // WICD descriptor index
0x01, // bNumSections
0x00,0x00,0x00,0x00,0x00,0x00,0x00, // Reserved
0x00, // bInterfaceNumber
0x01, // Reserved
'W', 'I', 'N', 'U', 'S', 'B', 0x00,0x00, // Compatible ID, padded with zeros
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // Sub-compatible ID
0x00,0x00,0x00,0x00,0x00,0x00 // Reserved
};
+27 -3
View File
@@ -20,22 +20,46 @@
* Boston, MA 02110-1301, USA.
*/
#pragma once
#include <stdint.h>
extern uint8_t usb_descriptor_device[];
#if defined(IS_HACKRF_ONE) || defined(IS_PRALINE)
extern uint8_t usb_descriptor_device_hackrf[];
#endif
#ifdef IS_JAWBREAKER
extern uint8_t usb_descriptor_device_jawbreaker[];
#endif
#ifdef IS_RAD1O
extern uint8_t usb_descriptor_device_rad1o[];
#endif
extern uint8_t usb_descriptor_device_qualifier[];
extern uint8_t usb_descriptor_configuration_full_speed[];
extern uint8_t usb_descriptor_configuration_high_speed[];
extern uint8_t usb_descriptor_string_languages[];
extern uint8_t usb_descriptor_string_manufacturer[];
extern uint8_t usb_descriptor_string_product[];
#ifdef IS_HACKRF_ONE
extern uint8_t usb_descriptor_string_product_hackrf_one[];
#endif
#ifdef IS_PRALINE
extern uint8_t usb_descriptor_string_product_praline[];
#endif
#ifdef IS_JAWBREAKER
extern uint8_t usb_descriptor_string_product_jawbreaker[];
#endif
#ifdef IS_RAD1O
extern uint8_t usb_descriptor_string_product_rad1o[];
#endif
#define USB_DESCRIPTOR_STRING_SERIAL_LEN 32
#define USB_DESCRIPTOR_STRING_SERIAL_BUF_LEN \
(USB_DESCRIPTOR_STRING_SERIAL_LEN * 2 + 2) /* UTF-16LE */
extern uint8_t usb_descriptor_string_serial_number[];
extern uint8_t* usb_descriptor_strings[];
extern uint8_t* usb_descriptor_strings_hackrf_one[];
extern uint8_t* usb_descriptor_strings_jawbreaker[];
extern uint8_t* usb_descriptor_strings_rad1o[];
extern uint8_t* usb_descriptor_strings_praline[];
#define USB_WCID_VENDOR_REQ 0x19
extern uint8_t wcid_string_descriptor[];
+12 -12
View File
@@ -251,36 +251,36 @@ struct GPIO {
// Physical Level Operations
void set() const {
__attribute__((always_inline)) void set() const {
palSetPad(_gpio_port, _gpio_pad);
}
void clear() const {
__attribute__((always_inline)) void clear() const {
palClearPad(_gpio_port, _gpio_pad);
}
void toggle() const {
__attribute__((always_inline)) void toggle() const {
palTogglePad(_gpio_port, _gpio_pad);
}
void output() const {
__attribute__((always_inline)) void output() const {
palSetPadMode(_gpio_port, _gpio_pad, PAL_MODE_OUTPUT_PUSHPULL);
}
void input() const {
__attribute__((always_inline)) void input() const {
palSetPadMode(_gpio_port, _gpio_pad, PAL_MODE_INPUT);
}
void write(const bool value) const {
__attribute__((always_inline)) void write(const bool value) const {
palWritePad(_gpio_port, _gpio_pad, value);
}
bool read() const {
__attribute__((always_inline)) bool read() const {
return palReadPad(_gpio_port, _gpio_pad);
}
// Turns the feature ON based on its polarity
void setActive() const {
__attribute__((always_inline)) void setActive() const {
if (_polarity == Polarity::ActiveHigh) {
set();
} else {
@@ -289,7 +289,7 @@ struct GPIO {
}
// Turns the feature OFF based on its polarity
void setInactive() const {
__attribute__((always_inline)) void setInactive() const {
if (_polarity == Polarity::ActiveHigh) {
clear();
} else {
@@ -298,7 +298,7 @@ struct GPIO {
}
// Sets the logical state of the feature
void setState(const bool active) const {
__attribute__((always_inline)) void setState(const bool active) const {
if (active) {
setActive();
} else {
@@ -307,12 +307,12 @@ struct GPIO {
}
// Returns true if the feature is logically active/enabled
bool isEnabled() const {
__attribute__((always_inline)) bool isEnabled() const {
const bool physical_state = read();
return (_polarity == Polarity::ActiveHigh) ? physical_state : !physical_state;
}
bool operator!=(const GPIO& other) const {
__attribute__((always_inline)) bool operator!=(const GPIO& other) const {
return (port() != other.port()) || (pad() != other.pad());
}
+27 -10
View File
@@ -318,29 +318,33 @@ void I2CDevManager::create_thread() {
msg_t I2CDevManager::timer_fn(void* arg) {
(void)arg;
uint16_t curr_timer = 0; // seconds since thread start
while (1) {
systime_t start_time = chTimeNow();
bool changed = false;
// check if i2c scan needed
// Check if i2c scan is needed
if (force_scan || (scan_interval != 0 && curr_timer % scan_interval == 0)) {
changed = changed | scan();
force_scan = false;
}
// Update connected devices based on their own intervals
for (size_t i = 0; i < devlist.size(); i++) {
if (devlist[i].addr != 0 && devlist[i].dev && devlist[i].dev->query_interval != 0) {
if ((curr_timer % devlist[i].dev->query_interval) == 0) { // only if it is device's interval
devlist[i].dev->update(); // updates it's data, and broadcasts it. if there is any error it will handle in it, and later we can remove it
if ((curr_timer % devlist[i].dev->query_interval) == 0) {
devlist[i].dev->update();
}
}
}
// remove all unneeded items
// Remove all unneeded items (dead devices or devices throwing too many errors)
chMtxLock(&mutex_list);
size_t cnt = devlist.size();
devlist.erase(std::remove_if(devlist.begin(), devlist.end(), [](const I2DevListElement& x) {
if (x.addr == 0) return true;
if (x.dev && x.dev->need_del == true) return true; // self destruct on too many errors
return false; // won't remove the unidentified ones, so we can list them, and not trying all the time with them
if (x.dev && x.dev->need_del == true) return true;
return false;
}),
devlist.end());
chMtxUnlock();
@@ -350,11 +354,24 @@ msg_t I2CDevManager::timer_fn(void* arg) {
I2CDevListChangedMessage msg{};
EventDispatcher::send_message(msg);
}
systime_t end_time = chTimeNow();
systime_t delta = (end_time > start_time) ? end_time - start_time : 100; // wont calculate overflow, just guess.
if (delta > 950) delta = 950; // ensure minimum 50 milli sleep
chThdSleepMilliseconds(1000 - delta); // 1sec timer
systime_t end_time = chTimeNow();
// 1. Calculate elapsed ticks safely handling overflow
uint32_t delta_ticks = end_time - start_time;
// 2. Keep EVERYTHING in ticks (No MS conversion!) to prevent truncation drift
if (delta_ticks < CH_FREQUENCY) {
// Calculate exactly how many ticks are missing to complete 1 full second
uint32_t sleep_ticks = CH_FREQUENCY - delta_ticks;
// Sleep using native ticks instead of milliseconds
chThdSleep(sleep_ticks);
} else {
// Safety fallback: if processing took longer than 1 second, sleep 50ms to yield CPU
chThdSleepMilliseconds(50);
}
++curr_timer;
}
return 0;
+3 -1
View File
@@ -59,8 +59,10 @@ class JTAG {
void runtest_ms(const size_t count) {
auto starttime = chTimeNow();
// We convert the count in milliseconds to system ticks:
auto duration_ticks = MS2ST(count) + 1;
while ((chTimeNow() - starttime) < (count + 1))
while ((chTimeNow() - starttime) < duration_ticks)
target.clock(0, 0);
}
-1
View File
@@ -294,7 +294,6 @@ void lcd_start_ram_write(
lcd_caset(p.x(), p.x() + s.width() - 1);
lcd_paset(p.y(), p.y() + s.height() - 1);
lcd_ramwr_start();
io.update_cached_values();
}
void lcd_start_ram_read(
-18
View File
@@ -111,24 +111,6 @@ void IO::reference_oscillator(const bool enable) {
io_write(1, io_reg);
}
bool IO::get_dark_cover() {
return portapack::persistent_memory::apply_fake_brightness();
}
bool IO::get_is_normally_black() {
return portapack::persistent_memory::config_lcd_normally_black();
}
uint8_t IO::get_brightness() {
return portapack::persistent_memory::fake_brightness_level();
}
void IO::update_cached_values() {
lcd_normally_black = get_is_normally_black();
dark_cover_enabled = get_dark_cover();
brightness = get_brightness();
}
uint32_t IO::io_update(const TouchPinsConfig write_value) {
/* Very touchy code to save context of PortaPack data bus while the
* resistive touch pin drive is changed. Order of operations is
-39
View File
@@ -37,21 +37,6 @@
// #include "portapack_persistent_memory.hpp"
// Darkened pixel bit mask for each possible shift value.
static const uint16_t darken_mask[4] = {
0b1111111111111111, // RrrrrGgggggBbbbb
0b0111101111101111, // 0Rrrr0Ggggg0Bbbb
0b0011100111100111, // 00Rrr00Gggg00Bbb
0b0001100011100011 // 000Rr000Ggg000Bb
};
// To darken, dividing each color level R/G/B by 2^shift.
#define DARKENED_PIXEL(pixel, shift) ((pixel >> shift) & darken_mask[shift])
// To un-darken, multiply each color level by 2^shift (might still be darker that before since some bits may have been lost above).
// This function will only be called when the pixel has previously been darkened, so no masking is needed.
#define UNDARKENED_PIXEL(pixel, shift) (pixel << shift)
namespace portapack {
enum DeviceType {
@@ -197,9 +182,6 @@ class IO {
}
void lcd_write_pixel(ui::Color pixel) {
if (dark_cover_enabled) {
pixel.v = DARKENED_PIXEL(pixel.v, brightness);
}
lcd_write_data(pixel.v);
}
@@ -208,18 +190,12 @@ class IO {
}
void lcd_write_pixels(ui::Color pixel, size_t n) {
if (dark_cover_enabled) {
pixel.v = DARKENED_PIXEL(pixel.v, brightness);
}
while (n--) {
lcd_write_data(pixel.v);
}
}
void lcd_write_pixels_unrolled8(ui::Color pixel, size_t n) {
if (dark_cover_enabled) {
pixel.v = DARKENED_PIXEL(pixel.v, brightness);
}
auto v = pixel.v;
n >>= 3;
while (n--) {
@@ -254,13 +230,6 @@ class IO {
return switches_raw;
}
bool lcd_normally_black = false;
bool dark_cover_enabled = false;
uint8_t brightness = 0;
bool get_is_normally_black();
bool get_dark_cover();
uint8_t get_brightness();
void update_cached_values();
uint32_t io_update(const TouchPinsConfig write_value);
@@ -432,14 +401,6 @@ class IO {
halPolledDelay(71); // 90ns
const auto value_low = data_read();
uint32_t original_value = (value_high << 8) | value_low;
if (lcd_normally_black) return original_value;
if (dark_cover_enabled) {
// this is read data, so if the fake brightness is enabled AKA get_dark_cover() == true,
// then shift to back side AKA UNDARKENED_PIXEL, to prevent read shifted darkern info
original_value = UNDARKENED_PIXEL(original_value, brightness);
}
return original_value;
}
const auto value_high = data_read();
@@ -109,7 +109,7 @@ struct ui_config_t {
bool hide_clock : 1;
bool clock_show_date : 1;
bool clkout_enabled : 1;
bool apply_fake_brightness : 1; // Fake brightness level, which eventually could be something along the lines of apply_pwm_brightness
bool UNUSED_8 : 1; // Deprecated: was apply_fake_brightness
bool stealth_mode : 1;
bool config_login : 1;
bool config_splash : 1;
@@ -130,7 +130,7 @@ struct ui_config2_t {
bool hide_sd_card : 1;
bool hide_mute : 1;
bool hide_fake_brightness : 1;
bool UNUSED_9 : 1; // Deprecated: was hide_fake_brightness
bool hide_numeric_battery : 1;
bool hide_battery_icon : 1;
bool override_batt_calc : 1;
@@ -225,8 +225,7 @@ struct data_t {
// Rotary encoder dial sensitivity (encoder.cpp/hpp)
uint16_t encoder_dial_sensitivity : 4;
// fake brightness level (not switch, switch is in another place)
uint16_t fake_brightness_level : 4;
uint16_t UNUSED_5 : 4; // Deprecated: was fake_brightness_level
// Encoder rotation rate multiplier for larger increments when rotated rapidly
uint16_t encoder_rate_multiplier : 4;
@@ -299,7 +298,7 @@ struct data_t {
frequency_tx_correction(0),
encoder_dial_sensitivity(DIAL_SENSITIVITY_NORMAL),
fake_brightness_level(BRIGHTNESS_50),
UNUSED_5(0),
encoder_rate_multiplier(1),
UNUSED(0),
@@ -476,7 +475,6 @@ void init() {
set_config_mode_storage_direct(config_mode_backup);
// Firmware upgrade handling - adjust newly defined fields where 0 is an invalid default
if (fake_brightness_level() == 0) set_fake_brightness_level(BRIGHTNESS_50);
if (menu_color().v == 0) set_menu_color(Color::grey());
}
@@ -671,10 +669,6 @@ bool stealth_mode() {
return data->ui_config.stealth_mode;
}
bool apply_fake_brightness() {
return data->ui_config.apply_fake_brightness;
}
bool config_login() {
return data->ui_config.config_login;
}
@@ -794,10 +788,6 @@ void set_config_backlight_timer(const backlight_config_t& new_value) {
data->ui_config.enable_backlight_timeout = static_cast<uint8_t>(new_value.timeout_enabled());
}
void set_apply_fake_brightness(const bool v) {
data->ui_config.apply_fake_brightness = v;
}
uint32_t pocsag_last_address() {
return data->pocsag_last_address;
}
@@ -987,9 +977,6 @@ bool ui_hide_clock() {
bool ui_hide_sd_card() {
return data->ui_config2.hide_sd_card;
}
bool ui_hide_fake_brightness() {
return data->ui_config2.hide_fake_brightness;
}
bool ui_hide_numeric_battery() {
return data->ui_config2.hide_numeric_battery;
}
@@ -1044,9 +1031,6 @@ void set_ui_hide_clock(bool v) {
void set_ui_hide_sd_card(bool v) {
data->ui_config2.hide_sd_card = v;
}
void set_ui_hide_fake_brightness(bool v) {
data->ui_config2.hide_fake_brightness = v;
}
void set_ui_hide_numeric_battery(bool v) {
data->ui_config2.hide_numeric_battery = v;
}
@@ -1183,25 +1167,6 @@ void set_config_dst(dst_config_t v) {
rtc_time::dst_init();
}
// Fake brightness level (switch is in another place)
uint8_t fake_brightness_level() {
return data->fake_brightness_level;
}
void set_fake_brightness_level(uint8_t v) {
data->fake_brightness_level = v;
}
// Cycle through 4 brightness options: disabled -> enabled/50% -> enabled/25% -> enabled/12.5% -> disabled
void toggle_fake_brightness_level() {
bool fbe = apply_fake_brightness();
if ((!fbe) || (data->fake_brightness_level >= BRIGHTNESS_12p5)) {
set_apply_fake_brightness(!fbe);
data->fake_brightness_level = BRIGHTNESS_50;
} else {
data->fake_brightness_level++;
}
}
// Menu Color Scheme
Color menu_color() {
return data->menu_color;
@@ -139,12 +139,6 @@ typedef union {
} dst_config_t;
static_assert(sizeof(dst_config_t) == sizeof(uint32_t));
enum fake_brightness_level_options {
BRIGHTNESS_50 = 1,
BRIGHTNESS_25 = 2,
BRIGHTNESS_12p5 = 3, // 12p5 is 12.5
};
namespace cache {
/* Set values in cache to sensible defaults. */
@@ -289,15 +283,6 @@ uint16_t clkout_freq();
dst_config_t config_dst();
void set_config_dst(dst_config_t v);
/* Fake brightness */
// switch (if do color change):
bool apply_fake_brightness();
void set_apply_fake_brightness(const bool v);
// level (color change level):
uint8_t fake_brightness_level();
void set_fake_brightness_level(uint8_t v);
void toggle_fake_brightness_level();
/* Touchscreen threshold */
uint16_t touchscreen_threshold();
void set_touchscreen_threshold(uint16_t v);
@@ -351,7 +336,6 @@ bool ui_hide_camera();
bool ui_hide_sleep();
bool ui_hide_bias_tee();
bool ui_hide_clock();
bool ui_hide_fake_brightness();
bool ui_hide_numeric_battery();
bool ui_hide_battery_icon();
bool ui_hide_sd_card();
@@ -370,7 +354,6 @@ void set_ui_hide_camera(bool v);
void set_ui_hide_sleep(bool v);
void set_ui_hide_bias_tee(bool v);
void set_ui_hide_clock(bool v);
void set_ui_hide_fake_brightness(bool v);
void set_ui_hide_numeric_battery(bool v);
void set_ui_hide_battery_icon(bool v);
void set_ui_hide_sd_card(bool v);
+49 -55
View File
@@ -564,16 +564,14 @@ void LiveDateTime::set_seconds_enabled(bool new_value) {
/* BigFrequency **********************************************************/
BigFrequency::BigFrequency(
Rect parent_rect,
rf::Frequency frequency)
: Widget{parent_rect},
_frequency{frequency} {
}
BigFrequency::BigFrequency(Rect parent_rect, rf::Frequency frequency)
: Widget{parent_rect}, _frequency{frequency} {}
void BigFrequency::set(const rf::Frequency frequency) {
_frequency = frequency;
set_dirty();
if (_frequency != frequency) {
_frequency = frequency;
set_dirty();
}
}
void BigFrequency::paint(Painter& painter) {
@@ -583,63 +581,59 @@ void BigFrequency::paint(Painter& painter) {
Point digit_pos;
ui::Color segment_color;
if (_frequency != _previous_frequency) {
_previous_frequency = _frequency;
rf::Frequency frequency{_frequency};
const auto rect = screen_rect(); // why not use screen_rect() directly for width, ...? it may be too small, but ...
rf::Frequency frequency{_frequency};
const auto rect = screen_rect(); // why not use screen_rect() directly for width, ...? it may be too small, but ...
// Erase
painter.fill_rectangle(
{{0, rect.location().y()}, {screen_width, 52}},
Theme::getInstance()->bg_darkest->background);
// Erase
painter.fill_rectangle(
{{0, rect.location().y()}, {screen_width, 52}},
Theme::getInstance()->bg_darkest->background);
// Prepare digits
if (!frequency) {
digits.fill(10); // ----.---
digit_pos = {(screen_width - ((7 * digit_width) + 8)) / 2, rect.location().y()};
} else {
frequency /= 1000; // GMMM.KKK(uuu)
// Prepare digits
if (!frequency) {
digits.fill(10); // ----.---
digit_pos = {(screen_width - ((7 * digit_width) + 8)) / 2, rect.location().y()};
} else {
frequency /= 1000; // GMMM.KKK(uuu)
for (i = 0; i < 7; i++) {
digits[6 - i] = frequency % 10;
frequency /= 10;
}
// Remove leading zeros
for (i = 0; i < 3; i++) {
if (!digits[i])
digits[i] = 16; // "Don't draw" code
else
break;
}
digit_pos = {(Coord)(screen_width - ((7 * digit_width) + 8) - (i * digit_width)) / 2, rect.location().y()};
for (i = 0; i < 7; i++) {
digits[6 - i] = frequency % 10;
frequency /= 10;
}
segment_color = style().foreground;
// Remove leading zeros
for (i = 0; i < 3; i++) {
if (!digits[i])
digits[i] = 16; // "Don't draw" code
else
break;
}
// Draw
for (i = 0; i < 7; i++) {
digit = digits[i];
digit_pos = {(Coord)(screen_width - ((7 * digit_width) + 8) - (i * digit_width)) / 2, rect.location().y()};
}
if (digit < 16) {
digit_def = segment_font[(uint8_t)digit];
segment_color = style().foreground;
for (size_t s = 0; s < 7; s++) {
if (digit_def & 1)
painter.fill_rectangle({digit_pos + segments[s].location(), segments[s].size()}, segment_color);
digit_def >>= 1;
}
// Draw
for (i = 0; i < 7; i++) {
digit = digits[i];
if (digit < 16) {
digit_def = segment_font[(uint8_t)digit];
for (size_t s = 0; s < 7; s++) {
if (digit_def & 1)
painter.fill_rectangle({digit_pos + segments[s].location(), segments[s].size()}, segment_color);
digit_def >>= 1;
}
}
if (i == 3) {
// Dot
painter.fill_rectangle({digit_pos + Point(34, 48), {4, 4}}, segment_color);
digit_pos += {(digit_width + 8), 0};
} else {
digit_pos += {digit_width, 0};
}
if (i == 3) {
// Dot
painter.fill_rectangle({digit_pos + Point(34, 48), {4, 4}}, segment_color);
digit_pos += {(digit_width + 8), 0};
} else {
digit_pos += {digit_width, 0};
}
}
}
-1
View File
@@ -297,7 +297,6 @@ class BigFrequency : public Widget {
private:
rf::Frequency _frequency;
rf::Frequency _previous_frequency{~0LL};
static constexpr Dim digit_width = 32;
@@ -0,0 +1 @@
The schematic files of H4M Pro are the same as H4M. The only difference is the use of an 18650 battery holder. If you have any questions about them, please feel free to contact me. Our email address is support@opensourcesdrlab.com
@@ -0,0 +1,16 @@
M48
METRIC,LZ,000.000
;FILE_FORMAT=3:3
;TYPE=NON_PLATED
;Layer: NPTH_Through
;EasyEDA v6.5.46, 2026-07-24 11:31:18
;10
;Gerber Generator version 0.2
;Holesize 1 = 2.400 mm
T01C2.400
%
G05
G90
T01
X-077153Y008000
M30
@@ -0,0 +1,497 @@
M48
METRIC,LZ,000.000
;FILE_FORMAT=3:3
;TYPE=PLATED
;Layer: PTH_Through
;EasyEDA v6.5.46, 2026-07-24 11:31:18
;10
;Gerber Generator version 0.2
;Holesize 1 = 0.310 mm
T01C0.310
;Holesize 2 = 0.400 mm
T02C0.400
;Holesize 3 = 0.500 mm
T03C0.500
;Holesize 4 = 0.800 mm
T04C0.800
;Holesize 5 = 0.801 mm
T05C0.801
;Holesize 6 = 0.901 mm
T06C0.901
;Holesize 7 = 0.915 mm
T07C0.915
;Holesize 8 = 1.000 mm
T08C1.000
;Holesize 9 = 1.001 mm
T09C1.001
;Holesize 10 = 1.201 mm
T10C1.201
;Holesize 11 = 1.450 mm
T11C1.450
;Holesize 12 = 1.501 mm
T12C1.501
;Holesize 13 = 1.600 mm
T13C1.600
;Holesize 14 = 3.200 mm
T14C3.200
;Holesize 15 = 3.300 mm
T15C3.300
;Holesize 16 = 4.000 mm
T16C4.000
%
G05
G90
T01
X-088930Y002230
X-088920Y001501
T02
X-065866Y-026064
X-065866Y-024804
X-064616Y-026064
X-064616Y-024814
X-103345Y-058046
X-101770Y-058694
X-104895Y-058681
X-105606Y-060307
X-101148Y-060256
X-104895Y-061780
X-103345Y-062440
X-101783Y-061793
X008643Y-057983
X010218Y-058630
X007094Y-058618
X006383Y-060243
X010840Y-060192
X007094Y-061716
X008643Y-062377
X010205Y-061729
X008605Y009124
X010180Y008476
X007056Y008489
X006345Y006864
X010802Y006914
X007056Y005390
X008605Y004730
X010167Y005378
X-103485Y008896
X-101910Y008248
X-105034Y008261
X-105746Y006635
X-101288Y006686
X-105034Y005162
X-103485Y004501
X-101923Y005149
X-052607Y-060457
X-053627Y-060447
X-088017Y-013997
X-089057Y-014837
X-069077Y-011847
X-068037Y-011857
X-066897Y-011867
X-065667Y-011887
X-064587Y-011877
X-078267Y-047197
X-071547Y-036317
X-020277Y-018847
X-020277Y004343
X-040397Y-025987
X-040575Y-032758
X-029717Y-041107
X-020287Y-034967
X-021067Y-026607
X-028927Y-018647
X-030457Y-018647
X-065667Y-021107
X-057456Y-024088
X-060737Y-024377
X-059917Y-008637
X-059927Y-023877
X-104564Y-055198
X-106453Y-018834
X-100174Y-013237
X-099374Y-013237
X-099374Y-012368
X-100174Y-012368
X-103054Y-010817
X-103044Y-010018
X011536Y-051637
X011536Y-050037
X011536Y-048617
X-012464Y-041857
X-010964Y-041857
X-009464Y-041857
X-007964Y-041857
X-006464Y-041857
X-050564Y009283
X-052064Y009283
X-049064Y009283
X-059564Y009283
X-047564Y009283
X-053564Y009283
X-055064Y009283
X-046064Y009283
X-081967Y010163
X-080467Y010163
X011536Y-047217
X011536Y-046017
X011516Y-044857
X011536Y-043527
X011366Y-055957
X011356Y-057457
X005536Y-062948
X004036Y-062937
X002536Y-062927
X001036Y-062937
X-000464Y-062937
X-001964Y-062948
X-003464Y-062957
X-004964Y-062948
X-006464Y-062948
X-007964Y-062957
X-009464Y-062957
X-010964Y-062957
X-012464Y-062957
X-013964Y-062957
X-015464Y-062957
X-016964Y-062967
X-103064Y-056187
X-101564Y-057307
X-100364Y-058458
X-106444Y-055358
X-106464Y-053857
X-106464Y-052358
X-106464Y-050857
X-106464Y-049358
X-106464Y-047857
X-104664Y001342
X-106164Y001342
X-027764Y009298
X-030577Y009273
X-032564Y009288
X-035564Y009298
X-022364Y-019708
X-004964Y-041857
X-001964Y-041857
X-000464Y-041857
X001036Y-041857
X011536Y-041857
X010036Y-041857
X008536Y-041857
X004036Y-041857
X002536Y-041857
X-102264Y001342
X-100564Y001342
X-098864Y001342
X-106164Y002843
X-104664Y002843
X-102464Y003243
X-100664Y004243
X-099664Y005843
X-099464Y007442
X-106464Y-031358
X-106464Y-032857
X-106464Y-034358
X-106464Y-035857
X-106464Y-037358
X-106464Y-038857
X-106464Y-040358
X-106464Y-041857
X-106464Y-043358
X-106464Y-044857
X-106464Y-046358
X-106364Y-056657
X-106364Y-057957
X-099164Y-059557
X-098264Y-060557
X-097364Y-061457
X-096464Y-062358
X-100964Y-063358
X-099464Y-063358
X-097964Y-063358
X-096464Y-063358
X-094964Y-063358
X-093464Y-063358
X-091964Y-063358
X-090464Y-063358
X-088964Y-063358
X-087464Y-063358
X-085964Y-063358
X-084464Y-063358
X-082964Y-063358
X-081464Y-063358
X-079964Y-063358
X-078464Y-063358
X-076964Y-063358
X-075464Y-063358
X-073964Y-063358
X-072464Y-063358
X-070964Y-063358
X-069464Y-063358
X-067964Y-063358
X-066464Y-063358
X-064964Y-063358
X-063464Y-063358
X-061964Y-063358
X-060464Y-063358
X-058964Y-063358
X-057464Y-063358
X-055964Y-063358
X-054464Y-063358
X-052964Y-063358
X-051464Y-063358
X-049964Y-063358
X-048464Y-063358
X-046964Y-063358
X-045464Y-063358
X-043964Y-063358
X-042464Y-063358
X-040964Y-063358
X-039464Y-063358
X-037964Y-063358
X-036464Y-063358
X-034957Y-063358
X-033457Y-063358
X-031957Y-063358
X-030457Y-063358
X-028957Y-063358
X-027417Y-063359
X-025847Y-063347
X-024464Y-062957
X-022964Y-062957
X-021464Y-062957
X-019964Y-062957
X-018464Y-062957
X-099467Y010163
X-097967Y010163
X-096467Y010163
X-094967Y010163
X-089467Y010163
X-087967Y010163
X-086467Y010163
X-083467Y010163
X-084967Y010163
X-078967Y010163
X-077387Y010143
X-075927Y010133
X-074687Y010153
X-072967Y010163
X-071467Y010163
X-069967Y010163
X-068467Y010163
X-066967Y010163
X-065467Y010163
X-063967Y010163
X-062467Y010163
X-058064Y009283
X-056564Y009283
X-044564Y009283
X-043064Y009288
X-041564Y009288
X-040064Y009288
X-038564Y009288
X-037064Y009288
X-025247Y009323
X-022664Y009288
X-021164Y009288
X-019364Y009288
X-017864Y009288
X-016364Y009288
X-014864Y009288
X-013364Y009288
X-011864Y009288
X-010364Y009288
X-008864Y009288
X-007364Y009288
X-005864Y009288
X-004364Y009288
X-002864Y009288
X-001364Y009288
X000136Y009288
X001636Y009288
X003136Y009288
X004636Y009288
X-025866Y-018414
X-025786Y-017264
X-026606Y-016524
X-027546Y-015804
X-024636Y-018204
X-106464Y-029857
X-106464Y-028358
X-106464Y-026358
X-106464Y-024857
X-106363Y-013064
X-106443Y-018144
X-106553Y-013824
X-099553Y-007464
X-104963Y-016144
X-038810Y-022149
X-094086Y-032454
X-040996Y-022914
X-104146Y-032414
X-041796Y-023694
X-104186Y-035004
X-042626Y-024474
X-094056Y-035014
X-043422Y-025258
X-094056Y-037584
X-042626Y-025944
X-104186Y-037524
X-041946Y-026694
X-043396Y-027454
X-104176Y-040064
X-044216Y-028244
X-093676Y-040754
X-044956Y-029034
X-044186Y-029774
X-043506Y-030644
X-045006Y-031364
X-090186Y-058954
X-092766Y-053544
X-091236Y-051324
X-044166Y-032034
X-072396Y-059084
X-043496Y-032834
X-071326Y-051254
X-042726Y-033604
X-067296Y-048904
X-094086Y-029934
X-094086Y-027394
X-094056Y-024894
X-094086Y-022334
X-045776Y-034984
X-037596Y-021174
X-045768Y-037527
X-035266Y-018514
X-036386Y-019584
X-036936Y-018674
X-033856Y-015154
X-087186Y-018554
X-033236Y-014554
X-079606Y-013464
X-032576Y-013954
X-031966Y-013294
X-099466Y-018304
X-031376Y-012654
X-056086Y-036104
X-005239Y-047337
X-005236Y-048444
X-050366Y-042604
X-005215Y-049535
X-007606Y-052754
X-005196Y-053934
X-005189Y-055027
X-045756Y-040054
X-005166Y-056124
X-051616Y-040034
X-053656Y-035944
X-056726Y-037524
X-056006Y-042604
X-050846Y-021284
X-074966Y-049034
X-069826Y-045014
X-026706Y-044984
X-068656Y-045694
X-025966Y-045694
X-072126Y-024584
X-072116Y-026684
X-075176Y-025914
X-073566Y-031704
X-091667Y-005777
X-091667Y-003097
X-088187Y003323
X-087527Y003303
X-088947Y000733
X-088957Y000103
X-072057Y-006757
X-074327Y-006797
X-074327Y-005817
X-074327Y-004707
X-074327Y-003647
X-072067Y-005837
X-072067Y-004707
X-072077Y-003627
X-072047Y-002657
X-074327Y-002667
X-102637Y-012197
X-089997Y-006867
X-098257Y-008057
X-104077Y-007837
X-104027Y-047287
X-009987Y-058887
X-026457Y-058877
X-064777Y-058807
X-017597Y-057937
X-066337Y-056797
X-015047Y-057287
X-060747Y-007997
X-014017Y-045807
X-015837Y-050907
X-015837Y-008637
X-014007Y-007987
X-058897Y-023907
X-011707Y-050947
X-019397Y-055467
T03
X-082557Y-047197
X-052360Y-048694
X-062517Y-048777
X-020310Y-043744
X-020260Y-013224
T04
X-088447Y006673G85X-088447Y006673
X-085907Y006673G85X-085907Y006673
X-090987Y006673G85X-090987Y006673
X-059166Y-060304G85X-059166Y-060304
X-056626Y-060304G85X-056626Y-060304
X-061706Y-060304G85X-061706Y-060304
T05
X-094757Y-018407
T06
X-004727Y-009287
X-013657Y006183
T07
X-090816Y-048399
X-088276Y-048399
T08
X-034027Y005743G85X-034027Y005743
X-034027Y003203G85X-034027Y003203
X-026407Y005743G85X-026407Y005743
X-023867Y003203G85X-023867Y003203
X-026407Y003203G85X-026407Y003203
X-028947Y003203G85X-028947Y003203
X-023867Y005743G85X-023867Y005743
X-028947Y005743G85X-028947Y005743
X-031487Y003203G85X-031487Y003203
X-031487Y005743G85X-031487Y005743
T09
X-091456Y-017046
T10
X-091456Y-011787
T11
X-092556Y-020241G85X-092559Y-020237
X-082587Y-010453G85X-082587Y-010453
X-087304Y-027713G85X-087304Y-027713
X-079606Y-020015G85X-079606Y-020015
X-079830Y-032967G85X-079830Y-032967
X-071083Y-034332G85X-071083Y-034332
X-089800Y-042754G85X-089800Y-042754
T12
X-105088Y-004465G85X-105083Y-004465
X-097976Y-004465G85X-097971Y-004465
T13
X-089376Y-023421
X-083012Y-029785
T14
X-096800Y-015997
X-096800Y-015997
T15
X-103310Y-060269G85X-103305Y-060269
X008679Y-060205G85X008684Y-060205
X008641Y006902G85X008646Y006902
X-103449Y006673G85X-103444Y006673
T16
X-075587Y-015997
X-075587Y-037210
X-075587Y-015997
X-075587Y-037210
M30
@@ -0,0 +1,448 @@
M48
METRIC,LZ,000.000
;FILE_FORMAT=3:3
;TYPE=PLATED
;Layer: PTH_Through_Via
;EasyEDA v6.5.46, 2026-07-24 11:31:18
;10
;Gerber Generator version 0.2
;Holesize 1 = 0.310 mm
T01C0.310
;Holesize 2 = 0.400 mm
T02C0.400
;Holesize 3 = 0.500 mm
T03C0.500
;Holesize 4 = 0.801 mm
T04C0.801
;Holesize 5 = 0.901 mm
T05C0.901
;Holesize 6 = 1.001 mm
T06C1.001
;Holesize 7 = 1.201 mm
T07C1.201
;Holesize 8 = 1.600 mm
T08C1.600
;Holesize 9 = 3.200 mm
T09C3.200
;Holesize 10 = 4.000 mm
T10C4.000
%
G05
G90
T01
X-088930Y002230
X-088920Y001501
T02
X-065866Y-026064
X-065866Y-024804
X-064616Y-026064
X-064616Y-024814
X-103345Y-058046
X-101770Y-058694
X-104895Y-058681
X-105606Y-060307
X-101148Y-060256
X-104895Y-061780
X-103345Y-062440
X-101783Y-061793
X008643Y-057983
X010218Y-058630
X007094Y-058618
X006383Y-060243
X010840Y-060192
X007094Y-061716
X008643Y-062377
X010205Y-061729
X008605Y009124
X010180Y008476
X007056Y008489
X006345Y006864
X010802Y006914
X007056Y005390
X008605Y004730
X010167Y005378
X-103485Y008896
X-101910Y008248
X-105034Y008261
X-105746Y006635
X-101288Y006686
X-105034Y005162
X-103485Y004501
X-101923Y005149
X-052607Y-060457
X-053627Y-060447
X-088017Y-013997
X-089057Y-014837
X-069077Y-011847
X-068037Y-011857
X-066897Y-011867
X-065667Y-011887
X-064587Y-011877
X-078267Y-047197
X-071547Y-036317
X-020277Y-018847
X-020277Y004343
X-040397Y-025987
X-040575Y-032758
X-029717Y-041107
X-020287Y-034967
X-021067Y-026607
X-028927Y-018647
X-030457Y-018647
X-065667Y-021107
X-057456Y-024088
X-060737Y-024377
X-059917Y-008637
X-059927Y-023877
X-104564Y-055198
X-106453Y-018834
X-100174Y-013237
X-099374Y-013237
X-099374Y-012368
X-100174Y-012368
X-103054Y-010817
X-103044Y-010018
X011536Y-051637
X011536Y-050037
X011536Y-048617
X-012464Y-041857
X-010964Y-041857
X-009464Y-041857
X-007964Y-041857
X-006464Y-041857
X-050564Y009283
X-052064Y009283
X-049064Y009283
X-059564Y009283
X-047564Y009283
X-053564Y009283
X-055064Y009283
X-046064Y009283
X-081967Y010163
X-080467Y010163
X011536Y-047217
X011536Y-046017
X011516Y-044857
X011536Y-043527
X011366Y-055957
X011356Y-057457
X005536Y-062948
X004036Y-062937
X002536Y-062927
X001036Y-062937
X-000464Y-062937
X-001964Y-062948
X-003464Y-062957
X-004964Y-062948
X-006464Y-062948
X-007964Y-062957
X-009464Y-062957
X-010964Y-062957
X-012464Y-062957
X-013964Y-062957
X-015464Y-062957
X-016964Y-062967
X-103064Y-056187
X-101564Y-057307
X-100364Y-058458
X-106444Y-055358
X-106464Y-053857
X-106464Y-052358
X-106464Y-050857
X-106464Y-049358
X-106464Y-047857
X-104664Y001342
X-106164Y001342
X-027764Y009298
X-030577Y009273
X-032564Y009288
X-035564Y009298
X-022364Y-019708
X-004964Y-041857
X-001964Y-041857
X-000464Y-041857
X001036Y-041857
X011536Y-041857
X010036Y-041857
X008536Y-041857
X004036Y-041857
X002536Y-041857
X-102264Y001342
X-100564Y001342
X-098864Y001342
X-106164Y002843
X-104664Y002843
X-102464Y003243
X-100664Y004243
X-099664Y005843
X-099464Y007442
X-106464Y-031358
X-106464Y-032857
X-106464Y-034358
X-106464Y-035857
X-106464Y-037358
X-106464Y-038857
X-106464Y-040358
X-106464Y-041857
X-106464Y-043358
X-106464Y-044857
X-106464Y-046358
X-106364Y-056657
X-106364Y-057957
X-099164Y-059557
X-098264Y-060557
X-097364Y-061457
X-096464Y-062358
X-100964Y-063358
X-099464Y-063358
X-097964Y-063358
X-096464Y-063358
X-094964Y-063358
X-093464Y-063358
X-091964Y-063358
X-090464Y-063358
X-088964Y-063358
X-087464Y-063358
X-085964Y-063358
X-084464Y-063358
X-082964Y-063358
X-081464Y-063358
X-079964Y-063358
X-078464Y-063358
X-076964Y-063358
X-075464Y-063358
X-073964Y-063358
X-072464Y-063358
X-070964Y-063358
X-069464Y-063358
X-067964Y-063358
X-066464Y-063358
X-064964Y-063358
X-063464Y-063358
X-061964Y-063358
X-060464Y-063358
X-058964Y-063358
X-057464Y-063358
X-055964Y-063358
X-054464Y-063358
X-052964Y-063358
X-051464Y-063358
X-049964Y-063358
X-048464Y-063358
X-046964Y-063358
X-045464Y-063358
X-043964Y-063358
X-042464Y-063358
X-040964Y-063358
X-039464Y-063358
X-037964Y-063358
X-036464Y-063358
X-034957Y-063358
X-033457Y-063358
X-031957Y-063358
X-030457Y-063358
X-028957Y-063358
X-027417Y-063359
X-025847Y-063347
X-024464Y-062957
X-022964Y-062957
X-021464Y-062957
X-019964Y-062957
X-018464Y-062957
X-099467Y010163
X-097967Y010163
X-096467Y010163
X-094967Y010163
X-089467Y010163
X-087967Y010163
X-086467Y010163
X-083467Y010163
X-084967Y010163
X-078967Y010163
X-077387Y010143
X-075927Y010133
X-074687Y010153
X-072967Y010163
X-071467Y010163
X-069967Y010163
X-068467Y010163
X-066967Y010163
X-065467Y010163
X-063967Y010163
X-062467Y010163
X-058064Y009283
X-056564Y009283
X-044564Y009283
X-043064Y009288
X-041564Y009288
X-040064Y009288
X-038564Y009288
X-037064Y009288
X-025247Y009323
X-022664Y009288
X-021164Y009288
X-019364Y009288
X-017864Y009288
X-016364Y009288
X-014864Y009288
X-013364Y009288
X-011864Y009288
X-010364Y009288
X-008864Y009288
X-007364Y009288
X-005864Y009288
X-004364Y009288
X-002864Y009288
X-001364Y009288
X000136Y009288
X001636Y009288
X003136Y009288
X004636Y009288
X-025866Y-018414
X-025786Y-017264
X-026606Y-016524
X-027546Y-015804
X-024636Y-018204
X-106464Y-029857
X-106464Y-028358
X-106464Y-026358
X-106464Y-024857
X-106363Y-013064
X-106443Y-018144
X-106553Y-013824
X-099553Y-007464
X-104963Y-016144
X-038810Y-022149
X-094086Y-032454
X-040996Y-022914
X-104146Y-032414
X-041796Y-023694
X-104186Y-035004
X-042626Y-024474
X-094056Y-035014
X-043422Y-025258
X-094056Y-037584
X-042626Y-025944
X-104186Y-037524
X-041946Y-026694
X-043396Y-027454
X-104176Y-040064
X-044216Y-028244
X-093676Y-040754
X-044956Y-029034
X-044186Y-029774
X-043506Y-030644
X-045006Y-031364
X-090186Y-058954
X-092766Y-053544
X-091236Y-051324
X-044166Y-032034
X-072396Y-059084
X-043496Y-032834
X-071326Y-051254
X-042726Y-033604
X-067296Y-048904
X-094086Y-029934
X-094086Y-027394
X-094056Y-024894
X-094086Y-022334
X-045776Y-034984
X-037596Y-021174
X-045768Y-037527
X-035266Y-018514
X-036386Y-019584
X-036936Y-018674
X-033856Y-015154
X-087186Y-018554
X-033236Y-014554
X-079606Y-013464
X-032576Y-013954
X-031966Y-013294
X-099466Y-018304
X-031376Y-012654
X-056086Y-036104
X-005239Y-047337
X-005236Y-048444
X-050366Y-042604
X-005215Y-049535
X-007606Y-052754
X-005196Y-053934
X-005189Y-055027
X-045756Y-040054
X-005166Y-056124
X-051616Y-040034
X-053656Y-035944
X-056726Y-037524
X-056006Y-042604
X-050846Y-021284
X-074966Y-049034
X-069826Y-045014
X-026706Y-044984
X-068656Y-045694
X-025966Y-045694
X-072126Y-024584
X-072116Y-026684
X-075176Y-025914
X-073566Y-031704
X-091667Y-005777
X-091667Y-003097
X-088187Y003323
X-087527Y003303
X-088947Y000733
X-088957Y000103
X-072057Y-006757
X-074327Y-006797
X-074327Y-005817
X-074327Y-004707
X-074327Y-003647
X-072067Y-005837
X-072067Y-004707
X-072077Y-003627
X-072047Y-002657
X-074327Y-002667
X-102637Y-012197
X-089997Y-006867
X-098257Y-008057
X-104077Y-007837
X-104027Y-047287
X-009987Y-058887
X-026457Y-058877
X-064777Y-058807
X-017597Y-057937
X-066337Y-056797
X-015047Y-057287
X-060747Y-007997
X-014017Y-045807
X-015837Y-050907
X-015837Y-008637
X-014007Y-007987
X-058897Y-023907
X-011707Y-050947
X-019397Y-055467
T03
X-082557Y-047197
X-052360Y-048694
X-062517Y-048777
X-020310Y-043744
X-020260Y-013224
T04
X-094757Y-018407
T05
X-004727Y-009287
X-013657Y006183
T06
X-091456Y-017046
T07
X-091456Y-011787
T08
X-089376Y-023421
X-083012Y-029785
T09
X-096800Y-015997
X-096800Y-015997
T10
X-075587Y-015997
X-075587Y-037210
X-075587Y-015997
X-075587Y-037210
M30
@@ -0,0 +1,121 @@
G04 Layer: BoardOutlineLayer*
G04 EasyEDA v6.5.46, 2026-07-24 11:31:18*
G04 Gerber Generator version 0.2*
G04 Scale: 100 percent, Rotated: No, Reflected: No *
G04 Dimensions in millimeters *
G04 leading zeros omitted , absolute positions ,4 integer and 5 decimal *
%FSLAX45Y45*%
%MOMM*%
%ADD10C,0.2540*%
D10*
G75*
G01*
X-1099077Y-1257065D02*
G03*
X-1265445Y-1412005I-3999J-162495D01*
G75*
G01*
X1082783Y-1257065D02*
G03*
X1232644Y-1103395I-7769J157484D01*
G75*
G01*
X-1265445Y-3964384D02*
G03*
X-1125744Y-4092654I137677J9736D01*
G75*
G01*
X1242805Y-4238703D02*
G03*
X1101833Y-4092652I-142231J3776D01*
G75*
G01*
X910066Y-6418655D02*
G03*
X1242805Y-6089726I-18455J351431D01*
G75*
G01*
X-10731546Y-6098616D02*
G03*
X-10384836Y-6418657I337963J18303D01*
G75*
G01*
X-10396269Y1099569D02*
G03*
X-10731546Y768098I-19126J-315954D01*
G75*
G01*
X1232644Y778266D02*
G03*
X925306Y1099573I-289029J31179D01*
G75*
G01*
X1105599Y-52243D02*
G03*
X1105597Y-52845I-114999J-10D01*
G75*
G01*
X-6426401Y-52243D02*
G03*
X-6426402Y-52845I-114998J-10D01*
G75*
G01*
X737964Y-4641716D02*
G03*
X737963Y-4642083I-70000J11D01*
G75*
G01*
X735424Y-5444316D02*
G03*
X735423Y-5444683I-70000J11D01*
G75*
G01*
X-6426401Y-5294241D02*
G03*
X-6426402Y-5294844I-114998J-10D01*
X245727Y-6538655D02*
G01*
X245727Y-6418656D01*
X-10396270Y1099571D02*
G01*
X925304Y1099571D01*
X-1099075Y-1257063D02*
G01*
X1082784Y-1257063D01*
X1232644Y-1103396D02*
G01*
X1232644Y778266D01*
X-1265445Y-3964383D02*
G01*
X-1265445Y-1412006D01*
X-1125745Y-4092653D02*
G01*
X1101834Y-4092653D01*
X1242804Y-6089726D02*
G01*
X1242804Y-4238703D01*
X-10384838Y-6418656D02*
G01*
X910064Y-6418656D01*
X-10731545Y-6098618D02*
G01*
X-10731545Y768095D01*
G75*
G01*
X910069Y-6418654D02*
G03*
X1242809Y-6089726I-18454J351431D01*
G75*
G01*
X910067Y-6418654D02*
G03*
X1242807Y-6089724I-18455J351431D01*
G75*
G01*
X1105101Y-5295242D02*
G03*
X1105099Y-5295844I-114999J-10D01*
%LPD*%
M02*
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,21 @@
G04 Layer: BottomPasteMaskLayer*
G04 EasyEDA v6.5.46, 2026-07-24 11:31:18*
G04 Gerber Generator version 0.2*
G04 Scale: 100 percent, Rotated: No, Reflected: No *
G04 Dimensions in millimeters *
G04 leading zeros omitted , absolute positions ,4 integer and 5 decimal *
%FSLAX45Y45*%
%MOMM*%
%AMMACRO1*21,1,$1,$2,0,0,$3*%
%ADD10MACRO1,5X2.3X135.0000*%
%ADD11MACRO1,5X2.3X-10.0001*%
%LPD*%
D10*
G01*
X-10175539Y-1850323D03*
D11*
G01*
X-10352539Y-2976319D03*
M02*
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,106 @@
G04 Layer: BottomSolderMaskLayer*
G04 EasyEDA v6.5.46, 2026-07-24 11:31:18*
G04 Gerber Generator version 0.2*
G04 Scale: 100 percent, Rotated: No, Reflected: No *
G04 Dimensions in millimeters *
G04 leading zeros omitted , absolute positions ,4 integer and 5 decimal *
%FSLAX45Y45*%
%MOMM*%
%AMMACRO1*4,1,8,0.9335,-2.6317,-2.6317,0.9335,-2.6317,0.9756,-0.9756,2.6317,-0.9335,2.6317,2.6317,-0.9335,2.6317,-0.9756,0.9756,-2.6317,0.9335,-2.6317,0*%
%AMMACRO2*4,1,8,2.2742,-1.6203,-2.6912,-0.7448,-2.7154,-0.7103,-2.3087,1.5962,-2.2742,1.6203,2.6912,0.7448,2.7154,0.7103,2.3087,-1.5962,2.2742,-1.6203,0*%
%AMMACRO3*4,1,8,-0.8085,-0.8382,-0.8382,-0.8085,-0.8382,0.8084,-0.8085,0.8382,0.8084,0.8382,0.8382,0.8084,0.8382,-0.8085,0.8084,-0.8382,-0.8085,-0.8382,0*%
%ADD10MACRO1*%
%ADD11MACRO2*%
%ADD12C,1.8016*%
%ADD13C,1.7016*%
%ADD14C,1.6766*%
%ADD15C,1.6764*%
%ADD16MACRO3*%
%ADD17C,1.6016*%
%ADD18C,5.4016*%
%ADD20C,1.6256*%
%ADD21C,2.4016*%
%LPD*%
D10*
G01*
X-10175540Y-1850325D03*
D11*
G01*
X-10352540Y-2976321D03*
D12*
G01*
X-8844686Y667308D03*
D13*
G01*
X-8590686Y667308D03*
G01*
X-9098686Y667308D03*
D12*
G01*
X-5916574Y-6030417D03*
D13*
G01*
X-5662574Y-6030417D03*
G01*
X-6170574Y-6030417D03*
D14*
G01*
X-3402685Y574294D03*
G01*
X-3402685Y320294D03*
G01*
X-2640685Y574294D03*
D15*
G01*
X-2386685Y320294D03*
G01*
X-2640685Y320294D03*
G01*
X-2894685Y320294D03*
D16*
G01*
X-2386685Y574293D03*
D15*
G01*
X-2894685Y574294D03*
G01*
X-3148685Y320294D03*
G01*
X-3148685Y574294D03*
D17*
G01*
X-10508513Y-446481D03*
G01*
X-9797313Y-446481D03*
D18*
G01*
X-10330713Y-6026861D03*
G01*
X868146Y-6020511D03*
G01*
X864336Y690168D03*
G01*
X-10344683Y667308D03*
D20*
G01*
X-9081643Y-4839868D03*
G01*
X-8827643Y-4839868D03*
D21*
G01*
X-9255759Y-2023922D03*
G01*
X-8258733Y-1045286D03*
G01*
X-8730386Y-2771343D03*
G01*
X-7960563Y-2001520D03*
G01*
X-7982965Y-3296716D03*
G01*
X-7108291Y-3433190D03*
G01*
X-8979992Y-4275353D03*
M02*
@@ -0,0 +1,48 @@
G04 Layer: DocumentLayer*
G04 EasyEDA v6.5.46, 2026-07-24 11:31:18*
G04 Gerber Generator version 0.2*
G04 Scale: 100 percent, Rotated: No, Reflected: No *
G04 Dimensions in millimeters *
G04 leading zeros omitted , absolute positions ,4 integer and 5 decimal *
%FSLAX45Y45*%
%MOMM*%
%ADD10C,0.2540*%
%ADD11C,0.7860*%
%LPD*%
G36*
X-6899402Y125983D02*
G01*
X-6899402Y-128016D01*
X-6874002Y-128016D01*
X-6874002Y125983D01*
G37*
G36*
X-7014362Y13766D02*
G01*
X-7014362Y-11633D01*
X-6760362Y-11633D01*
X-6760362Y13766D01*
G37*
G36*
X-1475486Y12649D02*
G01*
X-1475486Y-12750D01*
X-1221486Y-12750D01*
X-1221486Y12649D01*
G37*
D10*
X-10740466Y1506118D02*
G01*
X1248333Y1506118D01*
X1248333Y-2073249D01*
X1248333Y-7218781D01*
X-10740466Y-7218781D01*
X-10740466Y1506118D01*
D11*
G75*
G01
X-7676007Y799998D02*
G03X-7676007Y799998I-39294J0D01*
M02*
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
@@ -0,0 +1,3 @@
This folder contains the PCB files for PortaPack H4 Pro. If you have any questions about the PCB files, please feel free to contact me. Thank you!
Our email address is support@opensourcesdrlab.com
@@ -0,0 +1 @@
Here are the hardware files of H4M Pro. If you have any questions about them, please feel free to contact me. Our email address is support@opensourcesdrlab.com
+1
View File
@@ -0,0 +1 @@
Superrollo;BADBEEF;1;5
+1
View File
@@ -0,0 +1 @@
Superrollo;BADBEEF;1;7
+1
View File
@@ -0,0 +1 @@
Superrollo;BADBEEF;1;3