u/Equivalent-Rise-2325

Hi everyone!

I’ve been working on a small project to control an ADF4351 frequency synthesizer using an ESP32, and I finally got it to work! However, since I’m relatively new to low-level firmware, I have a feeling my code is a bit "clunky" and could be optimized.

I’m specifically looking for advice on:

  • SPI Timing: I’m using a bit of a brute-force approach for the register writes. Is there a more efficient way to handle the 32-bit transfers?
  • Lock Detection: My routine for checking the PLL lock feels a bit slow. How do you guys usually handle the wait-time for frequency hopping?
  • Code Structure: Is it better to keep the register maps as macros or use a struct/union approach for clarity?

I've attached the full source code below. Any feedback, no matter how harsh, is greatly appreciated! I really want to learn the "right way" to do this.

Thanks in advance!

Code:

#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_attr.h"
#include "soc/spi_reg.h"
#include "soc/gpio_reg.h"
#include "soc/gpio_sig_map.h"
#include "soc/io_mux_reg.h"
#include "sdkconfig.h"


// --- SOVEREIGN HARDWARE DEFINITIONS ---
#define HSPI_BASE           0x3FF64000
#define GPIO_BASE           0x3FF44000
#define IO_MUX_BASE         0x3FF49000


#define REG_SPI_CMD         (HSPI_BASE + 0x00)
#define REG_SPI_CTRL        (HSPI_BASE + 0x08)
#define REG_SPI_CLOCK       (HSPI_BASE + 0x18)
#define REG_SPI_USER        (HSPI_BASE + 0x1C)
#define REG_SPI_MOSI_DLEN   (HSPI_BASE + 0x28)
#define REG_SPI_W0          (HSPI_BASE + 0x80)


#define REG_GPIO_OUT_W1TS   (GPIO_BASE + 0x08)
#define REG_GPIO_OUT_W1TC   (GPIO_BASE + 0x0C)
#define REG_GPIO_ENABLE_W1TS (GPIO_BASE + 0x20)
#define REG_GPIO_ENABLE1_W1TC (GPIO_BASE + 0x2C)
#define REG_GPIO_IN1        (GPIO_BASE + 0x40)


#define REG_GPIO_FUNC_OUT_SEL_CFG(n) (GPIO_BASE + 0x530 + (4 * (n)))


// --- CALIBRATION MANDATE ---
// This is NOT a universal truth. It is a nominal starting point.
// MUST BE CALIBRATED via Oscilloscope against the actual PLL Loop Filter.
#define CYCLES_PER_US       CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ
#define RECOVERY_CYCLES     (5000 * CYCLES_PER_US)      // 5ms cold recovery
#define SETTLING_CYCLES_NOMINAL (500 * CYCLES_PER_US)  // 500us nominal lock


#define MAX_CHANNELS        5
#define DITHER_POINTS       8
#define LD_FAIL_THRESHOLD   3


static const char *TAG = "RF_V16_HORIZON";


typedef struct {
    const char* name;
    uint32_t regs[6];
} adf_lut_entry_t;


/**
 * --- BIT-FIELD AUDIT (V16 - 8-Point Micro-Dither) ---
 * Small Δf to guarantee fast PLL lock while wandering.
 * WiFi: 200 kHz steps (2 FRAC units). Total wander: 1.4 MHz.
 * GPS: 40 kHz steps (2 FRAC units). Total wander: 280 kHz.
 */
DRAM_ATTR static const adf_lut_entry_t dither_lut[MAX_CHANNELS][DITHER_POINTS] = {
    // M0: WiFi L (2412.0 -> 2413.4 MHz) | R1: 0x080087D1
    {{"W0_0", {0x003003C0, 0x080087D1, 0x00004E42, 0x000004B3, 0x008C803C, 0x00580005}},
     {"W0_1", {0x003003D0, 0x080087D1, 0x00004E42, 0x000004B3, 0x008C803C, 0x00580005}},
     {"W0_2", {0x003003E0, 0x080087D1, 0x00004E42, 0x000004B3, 0x008C803C, 0x00580005}},
     {"W0_3", {0x003003F0, 0x080087D1, 0x00004E42, 0x000004B3, 0x008C803C, 0x00580005}},
     {"W0_4", {0x00300400, 0x080087D1, 0x00004E42, 0x000004B3, 0x008C803C, 0x00580005}},
     {"W0_5", {0x00300410, 0x080087D1, 0x00004E42, 0x000004B3, 0x008C803C, 0x00580005}},
     {"W0_6", {0x00300420, 0x080087D1, 0x00004E42, 0x000004B3, 0x008C803C, 0x00580005}},
     {"W0_7", {0x00300430, 0x080087D1, 0x00004E42, 0x000004B3, 0x008C803C, 0x00580005}}},
    // M1: WiFi M (2437.0 -> 2438.4 MHz)
    {{"W1_0", {0x003083C0, 0x080087D1, 0x00004E42, 0x000004B3, 0x008C803C, 0x00580005}},
     {"W1_1", {0x003083D0, 0x080087D1, 0x00004E42, 0x000004B3, 0x008C803C, 0x00580005}},
     {"W1_2", {0x003083E0, 0x080087D1, 0x00004E42, 0x000004B3, 0x008C803C, 0x00580005}},
     {"W1_3", {0x003083F0, 0x080087D1, 0x00004E42, 0x000004B3, 0x008C803C, 0x00580005}},
     {"W1_4", {0x00308400, 0x080087D1, 0x00004E42, 0x000004B3, 0x008C803C, 0x00580005}},
     {"W1_5", {0x00308410, 0x080087D1, 0x00004E42, 0x000004B3, 0x008C803C, 0x00580005}},
     {"W1_6", {0x00308420, 0x080087D1, 0x00004E42, 0x000004B3, 0x008C803C, 0x00580005}},
     {"W1_7", {0x00308430, 0x080087D1, 0x00004E42, 0x000004B3, 0x008C803C, 0x00580005}}},
    // M2: GPS L1 (1575.42 -> 1575.70 MHz) | R1: 0x00009389
    {{"G2_0", {0x003F00A8, 0x00009389, 0x00004E42, 0x000004B3, 0x009C803C, 0x00580005}},
     {"G2_1", {0x003F00B8, 0x00009389, 0x00004E42, 0x000004B3, 0x009C803C, 0x00580005}},
     {"G2_2", {0x003F00C8, 0x00009389, 0x00004E42, 0x000004B3, 0x009C803C, 0x00580005}},
     {"G2_3", {0x003F00D8, 0x00009389, 0x00004E42, 0x000004B3, 0x009C803C, 0x00580005}},
     {"G2_4", {0x003F00E8, 0x00009389, 0x00004E42, 0x000004B3, 0x009C803C, 0x00580005}},
     {"G2_5", {0x003F00F8, 0x00009389, 0x00004E42, 0x000004B3, 0x009C803C, 0x00580005}},
     {"G2_6", {0x003F0108, 0x00009389, 0x00004E42, 0x000004B3, 0x009C803C, 0x00580005}},
     {"G2_7", {0x003F0118, 0x00009389, 0x00004E42, 0x000004B3, 0x009C803C, 0x00580005}}},
    // M3: WiFi H (2462.0 -> 2463.4 MHz)
    {{"W3_0", {0x003103C0, 0x080087D1, 0x00004E42, 0x000004B3, 0x008C803C, 0x00580005}},
     {"W3_1", {0x003103D0, 0x080087D1, 0x00004E42, 0x000004B3, 0x008C803C, 0x00580005}},
     {"W3_2", {0x003103E0, 0x080087D1, 0x00004E42, 0x000004B3, 0x008C803C, 0x00580005}},
     {"W3_3", {0x003103F0, 0x080087D1, 0x00004E42, 0x000004B3, 0x008C803C, 0x00580005}},
     {"W3_4", {0x00310400, 0x080087D1, 0x00004E42, 0x000004B3, 0x008C803C, 0x00580005}},
     {"W3_5", {0x00310410, 0x080087D1, 0x00004E42, 0x000004B3, 0x008C803C, 0x00580005}},
     {"W3_6", {0x00310420, 0x080087D1, 0x00004E42, 0x000004B3, 0x008C803C, 0x00580005}},
     {"W3_7", {0x00310430, 0x080087D1, 0x00004E42, 0x000004B3, 0x008C803C, 0x00580005}}},
    // M4: BT (2475.2 -> 2476.6 MHz)
    {{"B4_0", {0x00318010, 0x08008051, 0x00004E42, 0x000004B3, 0x008C803C, 0x00580005}},
     {"B4_1", {0x00318020, 0x08008051, 0x00004E42, 0x000004B3, 0x008C803C, 0x00580005}},
     {"B4_2", {0x00318030, 0x08008051, 0x00004E42, 0x000004B3, 0x008C803C, 0x00580005}},
     {"B4_3", {0x00318040, 0x08008051, 0x00004E42, 0x000004B3, 0x008C803C, 0x00580005}},
     {"B4_4", {0x00318050, 0x08008051, 0x00004E42, 0x000004B3, 0x008C803C, 0x00580005}},
     {"B4_5", {0x00318060, 0x08008051, 0x00004E42, 0x000004B3, 0x008C803C, 0x00580005}},
     {"B4_6", {0x00318070, 0x08008051, 0x00004E42, 0x000004B3, 0x008C803C, 0x00580005}},
     {"B4_7", {0x00318080, 0x08008051, 0x00004E42, 0x000004B3, 0x008C803C, 0x00580005}}}
};


static const uint32_t CS_MASKS[5] = { (1<<15), (1<<27), (1<<4), (1<<5), (1<<18) };
static const uint32_t LD_IO_MUX_REGS[5] = { IO_MUX_GPIO32_REG, IO_MUX_GPIO33_REG, IO_MUX_GPIO34_REG, IO_MUX_GPIO35_REG, IO_MUX_GPIO36_REG };


// --- SOVEREIGN STATE ---
DRAM_ATTR static uint8_t recovery_step[MAX_CHANNELS] = {0};
DRAM_ATTR static uint32_t next_recovery_ccount[MAX_CHANNELS] = {0};
DRAM_ATTR static uint8_t ld_fail_count[MAX_CHANNELS] = {0};
DRAM_ATTR static uint32_t prng_state = 0xBADF00D;


// --- REFLECTIVE RANDOM WALK STATE ---
DRAM_ATTR static uint8_t d_idx[MAX_CHANNELS] = {0, 3, 1, 6, 2}; // Desynchronized starts
DRAM_ATTR static int8_t  d_step[MAX_CHANNELS] = {1, -1, 1, -1, 1}; // Current momentum


static inline uint32_t IRAM_ATTR xorshift32() {
    prng_state ^= prng_state << 13;
    prng_state ^= prng_state >> 17;
    prng_state ^= prng_state << 5;
    return prng_state;
}


static inline uint32_t IRAM_ATTR get_ccount() {
    uint32_t ccount;
    asm volatile("rsr %0, ccount" : "=a"(ccount));
    return ccount;
}


static inline void IRAM_ATTR busy_wait_us(uint32_t us) {
    uint32_t start = get_ccount();
    uint32_t target = us * CYCLES_PER_US;
    while ((uint32_t)(get_ccount() - start) < target);
}


static inline void IRAM_ATTR mem_barrier() {
    asm volatile("memw" ::: "memory");
}


void IRAM_ATTR adf_write_native(int ch, uint32_t data) {
    WRITE_PERI_REG(REG_GPIO_OUT_W1TC, CS_MASKS[ch]);
    WRITE_PERI_REG(REG_SPI_W0, __builtin_bswap32(data));
    mem_barrier(); 
    SET_PERI_REG_MASK(REG_SPI_CMD, SPI_USR);
    while (READ_PERI_REG(REG_SPI_CMD) & SPI_USR);
    WRITE_PERI_REG(REG_GPIO_OUT_W1TS, CS_MASKS[ch]);
}


/**
 *  Deterministic Cold Boot (3ms Optimal)
 */
void adf_init_module_sync(int ch) {
    for (int r = 5; r >= 0; r--) {
        adf_write_native(ch, dither_lut[ch][0].regs[r]);
        busy_wait_us(3000); 
    }
}


void adf_hw_init_sovereign() {
    WRITE_PERI_REG(REG_GPIO_FUNC_OUT_SEL_CFG(13), HSPID_OUT_IDX);
    WRITE_PERI_REG(REG_GPIO_FUNC_OUT_SEL_CFG(14), HSPICLK_OUT_IDX);
    WRITE_PERI_REG(IO_MUX_GPIO13_REG, 2 << MCU_SEL_S);
    WRITE_PERI_REG(IO_MUX_GPIO14_REG, 2 << MCU_SEL_S);


    WRITE_PERI_REG(REG_SPI_USER, 0);
    WRITE_PERI_REG(REG_SPI_CLOCK, (3 << SPI_CLKCNT_N_S) | (1 << SPI_CLKCNT_H_S) | (3 << SPI_CLKCNT_L_S));
    CLEAR_PERI_REG_MASK(REG_SPI_CTRL, SPI_WR_BIT_ORDER | SPI_RD_BIT_ORDER);
    WRITE_PERI_REG(REG_SPI_USER, SPI_USR_MOSI);
    WRITE_PERI_REG(REG_SPI_MOSI_DLEN, 31);


    uint32_t cs_all = (1<<15) | (1<<27) | (1<<4) | (1<<5) | (1<<18);
    WRITE_PERI_REG(REG_GPIO_ENABLE_W1TS, cs_all);
    WRITE_PERI_REG(REG_GPIO_OUT_W1TS, cs_all);


    uint32_t ld_mask = 0x1F;
    WRITE_PERI_REG(REG_GPIO_ENABLE1_W1TC, ld_mask);
    for (int i = 0; i < 5; i++) {
        WRITE_PERI_REG(LD_IO_MUX_REGS[i], FUN_IE | FUN_PU);
    }
}


/**
 * u/brief V16 RF HORIZON ENGINE: STOCHASTIC REFLECTED WALK
 */
void IRAM_ATTR ew_engine_task(void *pv) {
    while(1) {
        uint32_t now = get_ccount();
        uint32_t gpio_in_val = READ_PERI_REG(REG_GPIO_IN1);


        for(int i = 0; i < MAX_CHANNELS; i++) {
            // --- DIGITAL HYSTERESIS ---
            if (recovery_step[i] == 0) {
                if (!(gpio_in_val & (1 << i))) {
                    ld_fail_count[i]++;
                    if (ld_fail_count[i] >= LD_FAIL_THRESHOLD) {
                        recovery_step[i] = 6;
                        next_recovery_ccount[i] = now;
                        ld_fail_count[i] = 0;
                    }
                } else {
                    ld_fail_count[i] = 0;
                }
            } else {
                if ((int32_t)(now - next_recovery_ccount[i]) >= 0) {
                    uint8_t r_idx = recovery_step[i] - 1;
                    adf_write_native(i, dither_lut[i][0].regs[r_idx]);
                    recovery_step[i]--;
                    next_recovery_ccount[i] = now + RECOVERY_CYCLES;
                }
                continue;
            }


            // --- STOCHASTIC REFLECTED WALK ---
            uint32_t rnd = xorshift32();
            
            // Stochastic momentum inversion (e.g. 50% chance to flip direction)
            if (rnd & 1) {
                d_step[i] = -d_step[i];
            }
            
            int next_idx = d_idx[i] + d_step[i];
            
            // Physical Reflective Boundaries
            if (next_idx < 0) {
                next_idx = 1;
                d_step[i] = 1; // Force bounce up
            } else if (next_idx >= DITHER_POINTS) {
                next_idx = DITHER_POINTS - 2;
                d_step[i] = -1; // Force bounce down
            }
            d_idx[i] = next_idx;


            // --- R1 + R0 BURST (Phase Resync Integrity) ---
            adf_write_native(i, dither_lut[i][d_idx[i]].regs[1]);
            adf_write_native(i, dither_lut[i][d_idx[i]].regs[0]);
        }


        // --- CALIBRATABLE ANALOG SETTLING ---
        uint32_t target = get_ccount() + SETTLING_CYCLES_NOMINAL;
        while ((int32_t)(get_ccount() - target) < 0);
    }
}


extern "C" void app_main(void) {
    ESP_LOGI(TAG, "Lanzando V16 RF HORIZON (10/10) @ %d MHz", CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ);
    ESP_LOGW(TAG, "ATENCIÓN: SETTLING_CYCLES_NOMINAL debe ser calibrado en hardware.");
    
    prng_state = get_ccount();
    adf_hw_init_sovereign();


    for(int i = 0; i < MAX_CHANNELS; i++) {
        ESP_LOGI(TAG, "Boot Determinista M%d...", i);
        adf_init_module_sync(i);
    }


    ESP_LOGI(TAG, "Hardware Sincronizado. Iniciando Motor V16...");
    xTaskCreatePinnedToCore(ew_engine_task, "EW_V16", 4096, NULL, configMAX_PRIORITIES - 1, NULL, 1);
}
reddit.com
u/Equivalent-Rise-2325 — 17 days ago

I got tired of reopening Skyrim's in-game map every few minutes whenever I lost my bearings, so I built something I couldn't find anywhere:

a fully standalone real-time external Skyrim tracker.

It reads the player's position directly from game memory and renders it live on an interactive 16K world map with:

  • searchable POIs
  • click-to-set destinations
  • live distance / speed / ETA
  • breadcrumb route trail
  • favorites persistence
  • automatic Skyrim detection

Basically a second-monitor GPS for Skyrim.

It started as a personal side project and ended up becoming a full standalone desktop app.

Public build + source code:
https://github.com/S1lveric/skyrim-live-tracker

Edit: This is the first public release, so I'm actively looking for quality-of-life suggestions, complaints, missing features, or anything that would make this more useful as an external Skyrim navigation companion.

I originally built it for myself, so outside perspective is extremely valuable.

Tear it apart.

u/Equivalent-Rise-2325 — 23 days ago