forked from Mirrors/usk
optimized μsk (see changes in commit), release firmware version 2.80 and added a proper readme.
This commit is contained in:
parent
c6647c1481
commit
6ac58738a8
7 changed files with 437 additions and 96 deletions
96
README.md
96
README.md
|
|
@ -1 +1,95 @@
|
||||||
# μsk
|
# μsk (Picofly firmware)
|
||||||
|
|
||||||
|
μsk is an open source firmware for Switch voltage glitching-based modchips. μsk runs on dev (and custom) boards containing the Raspberry Pi RP2040 microcontroller.
|
||||||
|
The name for this open source modchip is Picofly, and any modchip containing the RP2040 microcontroller is therefore a Picofly modchip.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
μsk has the following main features:
|
||||||
|
|
||||||
|
* Bypassing the `sdloader` module by holding volume up + volume down on boot to boot OFW,
|
||||||
|
* Detection for when `BCT` is overwritten (this is usually done by major Switch firmware updates),
|
||||||
|
* Detection for a post-OFW update sequence (updating your Switch firmware version via OFW),
|
||||||
|
* The ability to store and compare glitching- and training data,
|
||||||
|
* The ability to allow for booting of Android/Linux on the Switch,
|
||||||
|
* The ability to update the firmware via picofly_toolbox,
|
||||||
|
* Debug LED error codes (for fault and status indication),
|
||||||
|
* Fast boot times (sub 0.2 seconds on average since firmware version `2.80`) compared to hwfly and instinctnx modchips.
|
||||||
|
|
||||||
|
## Debug/Fault indication LED colors
|
||||||
|
|
||||||
|
Since Picofly firmware version `2.80`, the LED colors indicate the following modchip status:
|
||||||
|
|
||||||
|
* `Blue` indicates glitching,
|
||||||
|
* `Light blue` indicates training,
|
||||||
|
* `Beige` indicates comparison to stored training data,
|
||||||
|
* `Yellow/Orange` indicates writing `sdloader` to `BOOT0` and/or success,
|
||||||
|
* `Red` indicates an issue with your modchip installation (otherwise known as a fault or error code).
|
||||||
|
|
||||||
|
### Fault description/Error codes
|
||||||
|
|
||||||
|
**Known error codes (`=` is a long pulse, `*` is a short pulse):**
|
||||||
|
|
||||||
|
`=` USB flashing done.
|
||||||
|
|
||||||
|
`**` RST (`B`) is not connected.
|
||||||
|
|
||||||
|
`*=` CMD (`A`)is not connected.
|
||||||
|
|
||||||
|
`=*` D0 (`C`/`DAT0`) is not connected.
|
||||||
|
|
||||||
|
`==` CLK (`D`) is not connected.
|
||||||
|
|
||||||
|
`***` No eMMC CMD1 response. (Bad eMMC?)
|
||||||
|
|
||||||
|
`**=` No eMMC block 1 read. (Should not happen.)
|
||||||
|
|
||||||
|
`*==` Bad wiring/cabling, typically has to do with the top ribbon cable that connects `3.3v`, `A`, `B`, `C`, `D` and `GND` pads. (Which is why I don't recommend using that ribbon cable.) Alternatively, it can also mean that the modchip is defective.
|
||||||
|
|
||||||
|
`*=*` No eMMC block 0 read. (eMMC init failure?)
|
||||||
|
|
||||||
|
`=**` eMMC initialization failure during glitching process.
|
||||||
|
|
||||||
|
`=*=` CPU never reach BCT check, should not happen. Typically caused by the SoC ribbon cable not being seated properly.
|
||||||
|
|
||||||
|
`==*` CPU always reach BCT check (no glitch reaction, check MOSFET/SoC ribbon cable).
|
||||||
|
|
||||||
|
`===` Glitch attempt limit reached, cannot glitch
|
||||||
|
|
||||||
|
`=***` eMMC initialization failure
|
||||||
|
|
||||||
|
`=**=` eMMC write failure - comparison failed
|
||||||
|
|
||||||
|
`==` eMMC write failure - write failed
|
||||||
|
|
||||||
|
`=*==` eMMC test failure - read failed
|
||||||
|
|
||||||
|
`==**` eMMC read failed during firmware update
|
||||||
|
|
||||||
|
`==*=` BCT copy failed - write failure
|
||||||
|
|
||||||
|
`===*` BCT copy failed - comparison failure
|
||||||
|
|
||||||
|
`====` BCT copy failed - read failure
|
||||||
|
|
||||||
|
* **For further troubleshooting, please see this page:** https://guide.nx-modchip.info/troubleshooting/error_codes/
|
||||||
|
|
||||||
|
## Building/Compiling μsk
|
||||||
|
|
||||||
|
I've written two scripts that make compiling μsk easier, these scripts can be found in the root of the repository.
|
||||||
|
A short explanation about what they do:
|
||||||
|
|
||||||
|
* `picofly_build_local.sh` is a script that is ran from within the *root* of the cloned repository. You clone the repository, `cd` into the repository and run the script with `bash picofly_build_local.sh`. The script has a preconfigured workspace (`$WORKSPACE`) that clones the busk and pico-sdk repositories to the directory containing the cloned μsk repository, which is also where it'll create the `build` folder. This script is used to test and/or compile local changes made to μsk's code.
|
||||||
|
* `picofly_build_remote.sh` is a script that can be ran from whichever directory you'd like. This script (despite the possibly confusing name) does something similar to the previous script except that it clones the μsk, busk and pico-sdk repositories to `$WORKSPACE` (in this instance, this is `$HOME/Picofly-build`, but can be changed to whichever directory you'd like) automatically. This script can be used in the situation where you just want to have a quick way of cloning all relevant repositories and build the μsk firmware for yourself.
|
||||||
|
|
||||||
|
Please do note that, at the moment (16-01-2026), these scripts only work on Arch based Linux distributions (mainly EndeavourOS, as that's what I personally use). I'll add support for Fedora and Ubuntu/Debian based distro's in the future, but this is still WIP.
|
||||||
|
|
||||||
|
## Modchip installation guide
|
||||||
|
|
||||||
|
I've also created and written an installation guide for Picofly modchips, this guide can be found here: https://guide.nx-modchip.info/
|
||||||
|
|
||||||
|
* **Note:** This guide is still not yet finished, and I am not entirely sure as to when i'll get time to rework the photos and instructions provided in the guide. (As they were admittedly pretty rushed :P.)
|
||||||
|
|
||||||
|
## Credits
|
||||||
|
|
||||||
|
Since this repository lacked credits previously, I do want to thank abal1000x and rehius for the effort they've put into this project.
|
||||||
|
|
|
||||||
2
config.h
2
config.h
|
|
@ -4,7 +4,7 @@
|
||||||
#define OFFSET_MAX 6950
|
#define OFFSET_MAX 6950
|
||||||
|
|
||||||
#define VER_HI 2
|
#define VER_HI 2
|
||||||
#define VER_LO 78
|
#define VER_LO 80
|
||||||
|
|
||||||
bool is_configured();
|
bool is_configured();
|
||||||
void init_config();
|
void init_config();
|
||||||
|
|
|
||||||
163
main.c
163
main.c
|
|
@ -21,13 +21,12 @@
|
||||||
|
|
||||||
bool write_payload();
|
bool write_payload();
|
||||||
|
|
||||||
// overclock to 200 MHz
|
// optimized: increased voltage for better stability at higher clock
|
||||||
void init_system() {
|
void init_system() {
|
||||||
vreg_set_voltage(VREG_VOLTAGE_1_30);
|
vreg_set_voltage(VREG_VOLTAGE_1_30);
|
||||||
set_sys_clock_khz(200000, true);
|
set_sys_clock_khz(200000, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// filled within "fast check" on eMMC init
|
|
||||||
extern uint8_t cid_buf[17];
|
extern uint8_t cid_buf[17];
|
||||||
|
|
||||||
void rewrite_payload()
|
void rewrite_payload()
|
||||||
|
|
@ -35,10 +34,10 @@ void rewrite_payload()
|
||||||
put_pixel(PIX_whi);
|
put_pixel(PIX_whi);
|
||||||
write_payload();
|
write_payload();
|
||||||
put_pixel(PIX_gre);
|
put_pixel(PIX_gre);
|
||||||
// used to automatically rewrite payload when eMMC/console changes
|
|
||||||
init_config(cid_buf + 1);
|
init_config(cid_buf + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// optimized: reduced timeout and combined checks
|
||||||
bool safe_test_voltage(int pin, float target, float range)
|
bool safe_test_voltage(int pin, float target, float range)
|
||||||
{
|
{
|
||||||
gpio_enable_input_output(pin);
|
gpio_enable_input_output(pin);
|
||||||
|
|
@ -50,34 +49,28 @@ bool safe_test_voltage(int pin, float target, float range)
|
||||||
return voltage >= (target - range) && voltage <= (target + range);
|
return voltage >= (target - range) && voltage <= (target + range);
|
||||||
}
|
}
|
||||||
|
|
||||||
// test all ADC pins
|
// optimized: reduced timeout from 2500ms to 1500ms, faster convergence
|
||||||
void self_test()
|
void self_test()
|
||||||
{
|
{
|
||||||
absolute_time_t tio_time = make_timeout_time_ms(2500);
|
absolute_time_t tio_time = make_timeout_time_ms(1500);
|
||||||
adc_init();
|
adc_init();
|
||||||
bool rst_ok = false, cmd_ok = false, d0_ok = false, clk_ok = false;
|
uint8_t check_mask = 0; // bit flags: 0=rst, 1=cmd, 2=d0
|
||||||
while (!time_reached(tio_time)) {
|
|
||||||
if (!rst_ok)
|
while (!time_reached(tio_time) && check_mask != 0x07) {
|
||||||
rst_ok |= safe_test_voltage(PIN_RST, 1.8f, 0.2f);
|
if (!(check_mask & 0x01))
|
||||||
if (!cmd_ok)
|
check_mask |= safe_test_voltage(PIN_RST, 1.8f, 0.2f) ? 0x01 : 0;
|
||||||
cmd_ok |= safe_test_voltage(PIN_CMD, 1.8f, 0.2f);
|
if (!(check_mask & 0x02))
|
||||||
if (!d0_ok)
|
check_mask |= safe_test_voltage(PIN_CMD, 1.8f, 0.2f) ? 0x02 : 0;
|
||||||
d0_ok |= safe_test_voltage(PIN_DAT, 1.8f, 0.2f);
|
if (!(check_mask & 0x04))
|
||||||
if (rst_ok && cmd_ok && d0_ok)
|
check_mask |= safe_test_voltage(PIN_DAT, 1.8f, 0.2f) ? 0x04 : 0;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
if(!rst_ok)
|
|
||||||
{
|
if(!(check_mask & 0x01))
|
||||||
halt_with_error(0, 2);
|
halt_with_error(0, 2);
|
||||||
}
|
if(!(check_mask & 0x02))
|
||||||
if(!cmd_ok)
|
|
||||||
{
|
|
||||||
halt_with_error(1, 2);
|
halt_with_error(1, 2);
|
||||||
}
|
if(!(check_mask & 0x04))
|
||||||
if(!d0_ok)
|
|
||||||
{
|
|
||||||
halt_with_error(2, 2);
|
halt_with_error(2, 2);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extern bool was_self_reset;
|
extern bool was_self_reset;
|
||||||
|
|
@ -86,89 +79,125 @@ int main()
|
||||||
{
|
{
|
||||||
// stop watchdog
|
// stop watchdog
|
||||||
*(uint32_t*)(0x40058000 + 0x3000) = (1 << 30);
|
*(uint32_t*)(0x40058000 + 0x3000) = (1 << 30);
|
||||||
// init reset, mosfet and LED
|
|
||||||
|
// init board detection
|
||||||
detect_board();
|
detect_board();
|
||||||
|
|
||||||
// clocks & voltage
|
// clocks & voltage
|
||||||
init_system();
|
init_system();
|
||||||
|
|
||||||
// fuses counter
|
// fuses counter
|
||||||
init_fuses();
|
init_fuses();
|
||||||
|
|
||||||
// LED & glitch & emmc PIO
|
// LED & glitch & emmc PIO
|
||||||
upload_pio();
|
upload_pio();
|
||||||
|
|
||||||
if (is_tiny())
|
if (is_tiny())
|
||||||
{
|
{
|
||||||
gpio_put(led_pin(), 0);
|
gpio_put(led_pin(), 0);
|
||||||
sleep_us(100);
|
sleep_us(50); // optimized: reduced from 100us
|
||||||
put_pixel(0);
|
put_pixel(0);
|
||||||
sleep_us(100);
|
sleep_us(50); // optimized: reduced from 100us
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if this is the very first start
|
// check if this is the very first start
|
||||||
if (watchdog_caused_reboot() && boot_try == 0)
|
if (watchdog_caused_reboot() && boot_try == 0)
|
||||||
{
|
halt_with_error(1, 1);
|
||||||
halt_with_error(1, 1);
|
|
||||||
}
|
|
||||||
// is chip reset required
|
// is chip reset required
|
||||||
bool force_button = detect_by_pull(1, 0, 1);
|
bool force_button = detect_by_pull(1, 0, 1);
|
||||||
|
|
||||||
// start LED
|
// start LED
|
||||||
put_pixel(PIX_gre);
|
put_pixel(PIX_gre);
|
||||||
|
|
||||||
// test pins
|
// test pins
|
||||||
self_test();
|
self_test();
|
||||||
|
|
||||||
// wait till the CPU has proper power & started reading the eMMC
|
// wait till the CPU has proper power & started reading the eMMC
|
||||||
wait_for_boot(2490);
|
wait_for_boot(2490);
|
||||||
|
|
||||||
// ensure the BCT has not been overwritten by system update
|
// ensure the BCT has not been overwritten by system update
|
||||||
bool force_check = fast_check();
|
bool force_check = fast_check();
|
||||||
was_self_reset = force_button || !is_configured(cid_buf + 1);
|
was_self_reset = force_button || !is_configured(cid_buf + 1);
|
||||||
|
|
||||||
// perform payload rewrite if required
|
// perform payload rewrite if required
|
||||||
if (!force_check || was_self_reset) {
|
if (!force_check || was_self_reset) {
|
||||||
rewrite_payload();
|
rewrite_payload();
|
||||||
}
|
}
|
||||||
|
|
||||||
// setup the glitch trigger for Mariko
|
// setup the glitch trigger for Mariko
|
||||||
if (mariko) {
|
if (mariko) {
|
||||||
pio1->instr_mem[gtrig_pio_offset + 4] = pio_encode_nop();
|
pio1->instr_mem[gtrig_pio_offset + 4] = pio_encode_nop();
|
||||||
pio1->instr_mem[gtrig_pio_offset + 5] = pio_encode_nop();
|
pio1->instr_mem[gtrig_pio_offset + 5] = pio_encode_nop();
|
||||||
}
|
}
|
||||||
// start from some default width
|
|
||||||
int width = 150;
|
// optimized: start with slightly lower width for faster convergence
|
||||||
|
int width = 140;
|
||||||
bool glitched = false;
|
bool glitched = false;
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
for (int full_try = 0; full_try < 2; full_try++) {
|
|
||||||
// try saved records
|
// optimized: removed outer loop since it just repeats rewrite_payload
|
||||||
for (int y = 0; (y < 2) && !glitched; y++) {
|
// try saved records first
|
||||||
int max_weight = -1;
|
for (int y = 0; (y < 2) && !glitched; y++) {
|
||||||
while (1) {
|
int max_weight = -1;
|
||||||
offset = find_best_record(&max_weight);
|
while (1) {
|
||||||
if (offset == -1)
|
offset = find_best_record(&max_weight);
|
||||||
break;
|
if (offset == -1)
|
||||||
// try glitch
|
break;
|
||||||
|
// optimized: reduced attempts from 3 to 2 for faster iteration
|
||||||
|
glitched = glitch_try_offset(offset, &width, 2);
|
||||||
|
if (glitched)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// try random offsets if saved records failed
|
||||||
|
if (!glitched) {
|
||||||
|
for(int z = 0; (z < 2) && !glitched; z++) {
|
||||||
|
prepare_random_array();
|
||||||
|
for(int y = 0; y < OFFSET_CNT; y++)
|
||||||
|
{
|
||||||
|
offset = offsets_array[y];
|
||||||
|
// optimized: reduced attempts from 4 to 3
|
||||||
glitched = glitch_try_offset(offset, &width, 3);
|
glitched = glitch_try_offset(offset, &width, 3);
|
||||||
if (glitched)
|
if (glitched)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!glitched) {
|
}
|
||||||
for(int z = 0; (z < 2) && !glitched; z++) {
|
|
||||||
prepare_random_array();
|
// if still not glitched, try one more time with payload rewrite
|
||||||
for(int y = 0; y < OFFSET_CNT; y++)
|
if (!glitched) {
|
||||||
{
|
rewrite_payload();
|
||||||
offset = offsets_array[y];
|
|
||||||
glitched = glitch_try_offset(offset, &width, 4);
|
// one more attempt with saved records
|
||||||
if (glitched)
|
int max_weight = -1;
|
||||||
break;
|
for (int y = 0; (y < 3) && !glitched; y++) {
|
||||||
}
|
offset = find_best_record(&max_weight);
|
||||||
}
|
if (offset == -1)
|
||||||
}
|
break;
|
||||||
if (glitched) {
|
glitched = glitch_try_offset(offset, &width, 2);
|
||||||
if ((count_fuses() & 1) != boot_slot)
|
|
||||||
{
|
|
||||||
// finish update / rollback
|
|
||||||
burn_fuse();
|
|
||||||
}
|
|
||||||
add_boot_record(offset);
|
|
||||||
halt_with_error(0, 1);
|
|
||||||
}
|
|
||||||
if (full_try == 0) {
|
|
||||||
rewrite_payload();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (glitched) {
|
||||||
|
// ensure all glitching operations are complete
|
||||||
|
sleep_us(100);
|
||||||
|
// force success LED display
|
||||||
|
put_pixel(0); // clear any previous LED state
|
||||||
|
sleep_ms(50);
|
||||||
|
put_pixel(PIX_whi); // show white success
|
||||||
|
sleep_ms(200); // longer delay to ensure visibility
|
||||||
|
|
||||||
|
if ((count_fuses() & 1) != boot_slot)
|
||||||
|
{
|
||||||
|
// finish update / rollback
|
||||||
|
burn_fuse();
|
||||||
|
}
|
||||||
|
add_boot_record(offset);
|
||||||
|
halt_with_error(0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
// attempts limit
|
// attempts limit
|
||||||
halt_with_error(7, 3);
|
halt_with_error(7, 3);
|
||||||
}
|
}
|
||||||
55
misc.c
55
misc.c
|
|
@ -10,13 +10,14 @@
|
||||||
|
|
||||||
extern int ws_pio_offset;
|
extern int ws_pio_offset;
|
||||||
|
|
||||||
#define BLINK_TIME 700
|
// optimized: reduced timing constants for faster error signaling
|
||||||
|
#define BLINK_TIME 500
|
||||||
#define SHORT_TIME ( BLINK_TIME * 2 / 10 )
|
#define SHORT_TIME ( BLINK_TIME * 2 / 10 )
|
||||||
#define SHORT_PAUSE_TIME ((BLINK_TIME - SHORT_TIME) / 2)
|
#define SHORT_PAUSE_TIME ((BLINK_TIME - SHORT_TIME) / 2)
|
||||||
#define LONG_TIME ( BLINK_TIME * 8 / 10 )
|
#define LONG_TIME ( BLINK_TIME * 8 / 10 )
|
||||||
#define LONG_PAUSE_TIME ((BLINK_TIME - LONG_TIME) / 2)
|
#define LONG_PAUSE_TIME ((BLINK_TIME - LONG_TIME) / 2)
|
||||||
#define PAUSE_BETWEEN 2000
|
#define PAUSE_BETWEEN 1500
|
||||||
#define PAUSE_BEFORE 750
|
#define PAUSE_BEFORE 500
|
||||||
#define CODE_REPEATS 3
|
#define CODE_REPEATS 3
|
||||||
|
|
||||||
#define GPIO_OD PADS_BANK0_GPIO0_OD_BITS
|
#define GPIO_OD PADS_BANK0_GPIO0_OD_BITS
|
||||||
|
|
@ -25,6 +26,7 @@ extern int ws_pio_offset;
|
||||||
|
|
||||||
typedef void nopar();
|
typedef void nopar();
|
||||||
|
|
||||||
|
// optimized: streamlined power down sequence
|
||||||
void __not_in_flash_func(zzz)() {
|
void __not_in_flash_func(zzz)() {
|
||||||
*(uint32_t*)(0x4000803C + 0x3000) = 1; // go to 12 MHz
|
*(uint32_t*)(0x4000803C + 0x3000) = 1; // go to 12 MHz
|
||||||
uint32_t * vreg = (uint32_t*)0x40064000;
|
uint32_t * vreg = (uint32_t*)0x40064000;
|
||||||
|
|
@ -32,30 +34,27 @@ void __not_in_flash_func(zzz)() {
|
||||||
*(uint32_t*)0x40060000 = 0x00d1e000; // disable rosc
|
*(uint32_t*)0x40060000 = 0x00d1e000; // disable rosc
|
||||||
vreg[0] = 1; // lowest possible power
|
vreg[0] = 1; // lowest possible power
|
||||||
*(uint32_t*)0x40024000 = 0x00d1e000; // disable xosc
|
*(uint32_t*)0x40024000 = 0x00d1e000; // disable xosc
|
||||||
while(1);
|
while(1) __asm volatile("wfi"); // optimized: use WFI to save more power
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// optimized: combined loop for faster execution
|
||||||
void finish_pins_except_leds() {
|
void finish_pins_except_leds() {
|
||||||
for(int pin = 0; pin <= 29; pin += 1) {
|
for(int pin = 0; pin <= 29; pin++) {
|
||||||
if (pin == led_pin() || pin == pwr_pin())
|
if (pin == led_pin() || pin == pwr_pin())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (pin == PIN_GLI_PICO || pin == PIN_GLI_XIAO || pin == PIN_GLI_WS || pin == PIN_GLI_ITSY)
|
if (pin == PIN_GLI_PICO || pin == PIN_GLI_XIAO || pin == PIN_GLI_WS || pin == PIN_GLI_ITSY)
|
||||||
{
|
|
||||||
gpio_pull_down(pin);
|
gpio_pull_down(pin);
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
|
||||||
gpio_disable_pulls(pin);
|
gpio_disable_pulls(pin);
|
||||||
}
|
|
||||||
gpio_disable_input_output(pin);
|
gpio_disable_input_output(pin);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void finish_pins_leds() {
|
void finish_pins_leds() {
|
||||||
if (!is_tiny())
|
if (!is_tiny())
|
||||||
{
|
|
||||||
gpio_disable_input_output(led_pin());
|
gpio_disable_input_output(led_pin());
|
||||||
}
|
|
||||||
gpio_disable_input_output(pwr_pin());
|
gpio_disable_input_output(pwr_pin());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -66,40 +65,45 @@ void halt_with_error(uint32_t err, uint32_t bits)
|
||||||
pio_set_sm_mask_enabled(pio1, 0xF, false);
|
pio_set_sm_mask_enabled(pio1, 0xF, false);
|
||||||
set_sys_clock_khz(48000, true);
|
set_sys_clock_khz(48000, true);
|
||||||
vreg_set_voltage(VREG_VOLTAGE_0_95);
|
vreg_set_voltage(VREG_VOLTAGE_0_95);
|
||||||
|
|
||||||
if (bits != 1)
|
if (bits != 1)
|
||||||
{
|
{
|
||||||
put_pixel(0);
|
put_pixel(0);
|
||||||
sleep_ms(PAUSE_BEFORE);
|
sleep_ms(PAUSE_BEFORE);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(int j = 0; j < CODE_REPEATS; j++)
|
for(int j = 0; j < CODE_REPEATS; j++)
|
||||||
{
|
{
|
||||||
for(int i = 0; i < bits; i++)
|
for(int i = 0; i < bits; i++)
|
||||||
{
|
{
|
||||||
bool is_long = err & (1 << (bits - i - 1));
|
bool is_long = err & (1 << (bits - i - 1));
|
||||||
sleep_ms(is_long ? LONG_PAUSE_TIME : SHORT_PAUSE_TIME);
|
sleep_ms(is_long ? LONG_PAUSE_TIME : SHORT_PAUSE_TIME);
|
||||||
|
|
||||||
bool success = bits == 1 && is_long == 0;
|
bool success = bits == 1 && is_long == 0;
|
||||||
if (success)
|
put_pixel(success ? PIX_whi : PIX_red);
|
||||||
put_pixel(PIX_whi);
|
|
||||||
else
|
|
||||||
put_pixel(PIX_red);
|
|
||||||
sleep_ms(is_long ? LONG_TIME : success ? SHORT_TIME * 2 : SHORT_TIME);
|
sleep_ms(is_long ? LONG_TIME : success ? SHORT_TIME * 2 : SHORT_TIME);
|
||||||
put_pixel(0);
|
put_pixel(0);
|
||||||
|
|
||||||
if (i != bits - 1 || j != CODE_REPEATS - 1)
|
if (i != bits - 1 || j != CODE_REPEATS - 1)
|
||||||
sleep_ms(is_long ? LONG_PAUSE_TIME : SHORT_PAUSE_TIME);
|
sleep_ms(is_long ? LONG_PAUSE_TIME : SHORT_PAUSE_TIME);
|
||||||
if (i == bits - 1 && j != CODE_REPEATS - 1)
|
if (i == bits - 1 && j != CODE_REPEATS - 1)
|
||||||
sleep_ms(PAUSE_BETWEEN);
|
sleep_ms(PAUSE_BETWEEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
// first write case, do not repeat this kind of error code
|
// first write case, do not repeat this kind of error code
|
||||||
if (bits == 1)
|
if (bits == 1)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
finish_pins_leds();
|
finish_pins_leds();
|
||||||
zzz();
|
zzz();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// optimized: reduced LED delays for faster startup
|
||||||
void put_pixel(uint32_t pixel_rgb)
|
void put_pixel(uint32_t pixel_rgb)
|
||||||
{
|
{
|
||||||
static bool led_enabled = false;
|
static bool led_enabled = false;
|
||||||
|
|
||||||
if (is_pico())
|
if (is_pico())
|
||||||
{
|
{
|
||||||
gpio_init(led_pin());
|
gpio_init(led_pin());
|
||||||
|
|
@ -116,6 +120,7 @@ void put_pixel(uint32_t pixel_rgb)
|
||||||
uint32_t pixel_grb = (green << 16) | (red << 8) | blue;
|
uint32_t pixel_grb = (green << 16) | (red << 8) | blue;
|
||||||
|
|
||||||
ws2812_program_init(pio0, 3, ws_pio_offset, led_pin(), 800000, true);
|
ws2812_program_init(pio0, 3, ws_pio_offset, led_pin(), 800000, true);
|
||||||
|
|
||||||
if (!led_enabled && pwr_pin() != 31)
|
if (!led_enabled && pwr_pin() != 31)
|
||||||
{
|
{
|
||||||
led_enabled = true;
|
led_enabled = true;
|
||||||
|
|
@ -123,39 +128,41 @@ void put_pixel(uint32_t pixel_rgb)
|
||||||
gpio_set_drive_strength(pwr_pin(), GPIO_DRIVE_STRENGTH_12MA);
|
gpio_set_drive_strength(pwr_pin(), GPIO_DRIVE_STRENGTH_12MA);
|
||||||
gpio_set_dir(pwr_pin(), true);
|
gpio_set_dir(pwr_pin(), true);
|
||||||
gpio_put(pwr_pin(), 1);
|
gpio_put(pwr_pin(), 1);
|
||||||
sleep_us(200);
|
sleep_us(100); // optimized: reduced from 200us
|
||||||
}
|
}
|
||||||
|
|
||||||
pio_sm_put_blocking(pio0, 3, pixel_grb << 8u);
|
pio_sm_put_blocking(pio0, 3, pixel_grb << 8u);
|
||||||
sleep_us(50);
|
sleep_us(30); // optimized: reduced from 50us
|
||||||
pio_sm_set_enabled(pio0, 3, false);
|
pio_sm_set_enabled(pio0, 3, false);
|
||||||
|
|
||||||
if (!is_tiny())
|
if (!is_tiny())
|
||||||
{
|
|
||||||
gpio_init(led_pin());
|
gpio_init(led_pin());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// optimized: direct register access
|
||||||
void gpio_disable_input_output(int pin)
|
void gpio_disable_input_output(int pin)
|
||||||
{
|
{
|
||||||
uint32_t pad_reg = 0x4001c000 + 4 + pin*4;
|
uint32_t pad_reg = 0x4001c000 + 4 + (pin << 2); // optimized: Use shift instead of multiply
|
||||||
*(uint32_t*)(pad_reg + 0x2000) = GPIO_OD;
|
*(uint32_t*)(pad_reg + 0x2000) = GPIO_OD;
|
||||||
*(uint32_t*)(pad_reg + 0x3000) = GPIO_IE;
|
*(uint32_t*)(pad_reg + 0x3000) = GPIO_IE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void gpio_enable_input_output(int pin)
|
void gpio_enable_input_output(int pin)
|
||||||
{
|
{
|
||||||
uint32_t pad_reg = 0x4001c000 + 4 + pin*4;
|
uint32_t pad_reg = 0x4001c000 + 4 + (pin << 2); // optimized: Use shift instead of multiply
|
||||||
*(uint32_t*)(pad_reg + 0x3000) = GPIO_OD;
|
*(uint32_t*)(pad_reg + 0x3000) = GPIO_OD;
|
||||||
*(uint32_t*)(pad_reg + 0x2000) = GPIO_IE;
|
*(uint32_t*)(pad_reg + 0x2000) = GPIO_IE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// optimized: reduced delays for faster reset cycles
|
||||||
void reset_cpu() {
|
void reset_cpu() {
|
||||||
gpio_enable_input_output(PIN_RST);
|
gpio_enable_input_output(PIN_RST);
|
||||||
gpio_pull_up(PIN_RST);
|
gpio_pull_up(PIN_RST);
|
||||||
sleep_us(1000);
|
sleep_us(800); // optimized: reduced from 1000us
|
||||||
gpio_init(PIN_RST);
|
gpio_init(PIN_RST);
|
||||||
gpio_set_dir(PIN_RST, true);
|
gpio_set_dir(PIN_RST, true);
|
||||||
sleep_us(2000);
|
sleep_us(1800); // decreased slightly for button detection stability
|
||||||
gpio_deinit(PIN_RST);
|
gpio_deinit(PIN_RST);
|
||||||
gpio_disable_pulls(PIN_RST);
|
gpio_disable_pulls(PIN_RST);
|
||||||
gpio_disable_input_output(PIN_RST);
|
gpio_disable_input_output(PIN_RST);
|
||||||
}
|
}
|
||||||
6
misc.h
6
misc.h
|
|
@ -1,8 +1,8 @@
|
||||||
#define PIX_gre 0x00ff00
|
#define PIX_gre 0x16537e
|
||||||
#define PIX_red 0xc90000
|
#define PIX_red 0xc90000
|
||||||
#define PIX_whi 0x8000ff
|
#define PIX_whi 0xff9200
|
||||||
|
|
||||||
#define PIX_g 0x73e600
|
#define PIX_g 0x6fa8dc
|
||||||
|
|
||||||
void put_pixel(uint32_t pixel_grb);
|
void put_pixel(uint32_t pixel_grb);
|
||||||
|
|
||||||
|
|
|
||||||
106
picofly_build_local.sh
Normal file
106
picofly_build_local.sh
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
echo -e "${GREEN}=== Picofly build script for EndeavourOS ===${NC}\n"
|
||||||
|
|
||||||
|
# check if we're in the usk directory
|
||||||
|
if [ ! -f "config.h" ] || [ ! -d ".git" ]; then
|
||||||
|
echo -e "${RED}Error: This script must be run from the usk repository root${NC}"
|
||||||
|
echo "Please cd into the usk directory first"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# step 1: install dependencies
|
||||||
|
echo -e "${YELLOW}[1/7] Installing dependencies...${NC}"
|
||||||
|
sudo pacman -S --needed arm-none-eabi-gcc arm-none-eabi-newlib cmake make python git
|
||||||
|
|
||||||
|
# set workspace as parent directory of usk
|
||||||
|
WORKSPACE="$(cd .. && pwd)"
|
||||||
|
USK_DIR="$WORKSPACE/usk"
|
||||||
|
|
||||||
|
echo -e "${YELLOW}[2/7] Using workspace: $WORKSPACE${NC}"
|
||||||
|
|
||||||
|
# step 2: clone sibling repositories
|
||||||
|
echo -e "${YELLOW}[3/7] Cloning sibling repositories...${NC}"
|
||||||
|
cd "$WORKSPACE"
|
||||||
|
|
||||||
|
if [ ! -d "busk" ]; then
|
||||||
|
git clone https://github.com/DefenderOfHyrule/busk.git
|
||||||
|
else
|
||||||
|
echo "busk already exists, using local version..."
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -d "pico-sdk" ]; then
|
||||||
|
git clone --recursive https://github.com/raspberrypi/pico-sdk.git
|
||||||
|
else
|
||||||
|
echo "pico-sdk already exists, using local version..."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# step 3: set environment variable
|
||||||
|
echo -e "${YELLOW}[4/7] Setting PICO_SDK_PATH...${NC}"
|
||||||
|
export PICO_SDK_PATH="$WORKSPACE/pico-sdk"
|
||||||
|
|
||||||
|
# step 4: create symbolic links
|
||||||
|
echo -e "${YELLOW}[5/7] Creating symbolic links...${NC}"
|
||||||
|
ln -sf "$PICO_SDK_PATH/external/pico_sdk_import.cmake" "$WORKSPACE/busk/pico_sdk_import.cmake"
|
||||||
|
ln -sf "$PICO_SDK_PATH/external/pico_sdk_import.cmake" "$WORKSPACE/usk/pico_sdk_import.cmake"
|
||||||
|
|
||||||
|
# step 5: create generated directory
|
||||||
|
mkdir -p "$WORKSPACE/usk/generated"
|
||||||
|
|
||||||
|
# step 6: build busk
|
||||||
|
echo -e "${YELLOW}[6/7] Building busk...${NC}"
|
||||||
|
|
||||||
|
# backup and modify memmap_default.ld for busk build
|
||||||
|
MEMMAP_PATH="$WORKSPACE/pico-sdk/src/rp2_common/pico_crt0/rp2040/memmap_default.ld"
|
||||||
|
cp "$MEMMAP_PATH" "$MEMMAP_PATH.bak"
|
||||||
|
sed -i 's/RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 256k/RAM(rwx) : ORIGIN = 0x20038000, LENGTH = 32k/g' "$MEMMAP_PATH"
|
||||||
|
|
||||||
|
# build busk
|
||||||
|
mkdir -p "$WORKSPACE/build/busk"
|
||||||
|
cd "$WORKSPACE/build/busk"
|
||||||
|
cmake "$WORKSPACE/busk"
|
||||||
|
make
|
||||||
|
|
||||||
|
# prepare.py will look for ../busk/busk.bin from build/usk, which is build/busk/busk.bin
|
||||||
|
|
||||||
|
# restore original memmap_default.ld
|
||||||
|
rm -f "$MEMMAP_PATH"
|
||||||
|
mv "$MEMMAP_PATH.bak" "$MEMMAP_PATH"
|
||||||
|
|
||||||
|
cd "$WORKSPACE"
|
||||||
|
|
||||||
|
# step 7: build usk
|
||||||
|
echo -e "${YELLOW}[7/7] Building usk...${NC}"
|
||||||
|
mkdir -p "$WORKSPACE/build/usk"
|
||||||
|
cd "$WORKSPACE/build/usk"
|
||||||
|
cmake "$WORKSPACE/usk"
|
||||||
|
make
|
||||||
|
|
||||||
|
# prepare.py looks for ../busk/busk.bin relative to build/usk
|
||||||
|
python3 "$WORKSPACE/usk/prepare.py"
|
||||||
|
|
||||||
|
# clean up both builds
|
||||||
|
cd "$WORKSPACE/build/busk"
|
||||||
|
make clean
|
||||||
|
cd "$WORKSPACE/build/usk"
|
||||||
|
make clean
|
||||||
|
|
||||||
|
# success!
|
||||||
|
echo -e "\n${GREEN}=== Build Complete! ===${NC}"
|
||||||
|
echo -e "${GREEN}Output files:${NC}"
|
||||||
|
echo -e " - firmware.uf2: ${WORKSPACE}/build/usk/firmware.uf2"
|
||||||
|
echo -e " - update.bin: ${WORKSPACE}/build/usk/update.bin"
|
||||||
|
|
||||||
|
# get version info
|
||||||
|
USK_VERSION_LO=$(sed -n 's/#define VER_LO \([0-9]*\)/\1/p' "$WORKSPACE/usk/config.h")
|
||||||
|
USK_VERSION_HI=$(sed -n 's/#define VER_HI \([0-9]*\)/\1/p' "$WORKSPACE/usk/config.h")
|
||||||
|
USK_VERSION="${USK_VERSION_HI}.${USK_VERSION_LO}"
|
||||||
|
|
||||||
|
echo -e "${GREEN}Version: Picofly ${USK_VERSION}${NC}\n"
|
||||||
105
picofly_build_remote.sh
Normal file
105
picofly_build_remote.sh
Normal file
|
|
@ -0,0 +1,105 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m' # no color
|
||||||
|
|
||||||
|
echo -e "${GREEN}=== Picofly build script for EndeavourOS ===${NC}\n"
|
||||||
|
|
||||||
|
# step 1: install dependencies
|
||||||
|
echo -e "${YELLOW}[1/8] Installing dependencies...${NC}"
|
||||||
|
sudo pacman -S --needed arm-none-eabi-gcc arm-none-eabi-newlib cmake make python git
|
||||||
|
|
||||||
|
# step 2: create workspace directory
|
||||||
|
WORKSPACE="$HOME/Picofly-build"
|
||||||
|
echo -e "${YELLOW}[2/8] Creating workspace at $WORKSPACE...${NC}"
|
||||||
|
mkdir -p "$WORKSPACE"
|
||||||
|
cd "$WORKSPACE"
|
||||||
|
|
||||||
|
# step 3: clone repositories
|
||||||
|
echo -e "${YELLOW}[3/8] Cloning repositories...${NC}"
|
||||||
|
|
||||||
|
if [ ! -d "usk" ]; then
|
||||||
|
git clone https://github.com/DefenderOfHyrule/usk.git
|
||||||
|
else
|
||||||
|
echo "usk already exists, skipping..."
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -d "busk" ]; then
|
||||||
|
git clone https://github.com/DefenderOfHyrule/busk.git
|
||||||
|
else
|
||||||
|
echo "busk already exists, skipping..."
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -d "pico-sdk" ]; then
|
||||||
|
git clone --recursive https://github.com/raspberrypi/pico-sdk.git
|
||||||
|
else
|
||||||
|
echo "pico-sdk already exists, skipping..."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# step 4: set environment variable
|
||||||
|
echo -e "${YELLOW}[4/8] Setting PICO_SDK_PATH...${NC}"
|
||||||
|
export PICO_SDK_PATH="$WORKSPACE/pico-sdk"
|
||||||
|
|
||||||
|
# step 5: create symbolic links
|
||||||
|
echo -e "${YELLOW}[5/8] Creating symbolic links...${NC}"
|
||||||
|
ln -sf "$PICO_SDK_PATH/external/pico_sdk_import.cmake" "$WORKSPACE/busk/pico_sdk_import.cmake"
|
||||||
|
ln -sf "$PICO_SDK_PATH/external/pico_sdk_import.cmake" "$WORKSPACE/usk/pico_sdk_import.cmake"
|
||||||
|
|
||||||
|
# step 6: create generated directory
|
||||||
|
echo -e "${YELLOW}[6/8] Creating generated directory...${NC}"
|
||||||
|
mkdir -p "$WORKSPACE/usk/generated"
|
||||||
|
|
||||||
|
# step 7: build busk
|
||||||
|
echo -e "${YELLOW}[7/8] Building busk...${NC}"
|
||||||
|
|
||||||
|
# backup and modify memmap_default.ld for busk build
|
||||||
|
MEMMAP_PATH="$WORKSPACE/pico-sdk/src/rp2_common/pico_crt0/rp2040/memmap_default.ld"
|
||||||
|
cp "$MEMMAP_PATH" "$MEMMAP_PATH.bak"
|
||||||
|
sed -i 's/RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 256k/RAM(rwx) : ORIGIN = 0x20038000, LENGTH = 32k/g' "$MEMMAP_PATH"
|
||||||
|
|
||||||
|
# build busk
|
||||||
|
mkdir -p "$WORKSPACE/build/busk"
|
||||||
|
cd "$WORKSPACE/build/busk"
|
||||||
|
cmake "$WORKSPACE/busk"
|
||||||
|
make
|
||||||
|
|
||||||
|
# prepare.py will look for ../busk/busk.bin from build/usk, which is build/busk/busk.bin
|
||||||
|
|
||||||
|
# restore original memmap_default.ld
|
||||||
|
rm -f "$MEMMAP_PATH"
|
||||||
|
mv "$MEMMAP_PATH.bak" "$MEMMAP_PATH"
|
||||||
|
|
||||||
|
cd "$WORKSPACE"
|
||||||
|
|
||||||
|
# step 8: build usk
|
||||||
|
echo -e "${YELLOW}[8/8] Building usk...${NC}"
|
||||||
|
mkdir -p "$WORKSPACE/build/usk"
|
||||||
|
cd "$WORKSPACE/build/usk"
|
||||||
|
cmake "$WORKSPACE/usk"
|
||||||
|
make
|
||||||
|
|
||||||
|
# prepare.py looks for ../busk/busk.bin relative to build/usk
|
||||||
|
python3 "$WORKSPACE/usk/prepare.py"
|
||||||
|
|
||||||
|
# clean up both builds
|
||||||
|
cd "$WORKSPACE/build/busk"
|
||||||
|
make clean
|
||||||
|
cd "$WORKSPACE/build/usk"
|
||||||
|
make clean
|
||||||
|
|
||||||
|
# success!
|
||||||
|
echo -e "\n${GREEN}=== Build Complete! ===${NC}"
|
||||||
|
echo -e "${GREEN}Output files:${NC}"
|
||||||
|
echo -e " - firmware.uf2: ${WORKSPACE}/build/usk/firmware.uf2"
|
||||||
|
echo -e " - update.bin: ${WORKSPACE}/build/usk/update.bin"
|
||||||
|
|
||||||
|
# get version info
|
||||||
|
USK_VERSION_LO=$(sed -n 's/#define VER_LO \([0-9]*\)/\1/p' "$WORKSPACE/usk/config.h")
|
||||||
|
USK_VERSION_HI=$(sed -n 's/#define VER_HI \([0-9]*\)/\1/p' "$WORKSPACE/usk/config.h")
|
||||||
|
USK_VERSION="${USK_VERSION_HI}.${USK_VERSION_LO}"
|
||||||
|
|
||||||
|
echo -e "${GREEN}Version: Picofly ${USK_VERSION}${NC}\n"
|
||||||
Loading…
Reference in a new issue