Analizador de audio profesional

Carlitos, te enredas un poco vos solo... Esta bueno lo que estas haceindo, pero como es un analizador de Audio, cada granito de arena que mejores en algunas funciones, suma... Fijate que tenes un par de puntos que son bloqueantes.
WaitForACK en espera activa hasta 500ms (una eternidad), el while interno de ExecuteTest (otros 500ms), el for de ProcessQueue que encadena todas las pruebas sin dar aire al usb_task(), el delay_ms(200) en cada botón, y el delay_ms(2000) de la alerta de temperatura. Durante todo eso el bridge USB↔UART se congela, que es justo lo peor para tu segunda version del analizador de audio en vivo.
Otra cosa que me parecio ver, pero no estoy 100% seguro, es un bug de protocolo: WaitForACK lee el byte de ACK con kbhit()/fgetc() directo sobre el UART, pero la ISR (#INT_RDA) sigue activa y se come ese mismo byte primero (porque no es "\n" "\r", lo mete en rx_buffer). El ACK probablemente nunca lo detecta por esa vía.
Tenes algo armado provisorio? proba esta idea que de seguro algun bugs tiene, pero trabaje y edite tu escencia tratando de sacarte todo lo que ralentiza... miralo y lo seguimos viendo sobre la marcha

C:
/* ==========================================================================
RatNET Audio Analyzer - Firmware Master UI (PIC18F4550)
Autor: Carlos Alvarez (Power by AlfinPatagonico)
Rev. 2.3 - USB - Edicion no bloqueante
========================================================================== */
#include <18F4550.h>

// ¡VREGEN es OBLIGATORIO para el USB en el 18F4550!
#FUSES HSPLL, NOWDT, NOPROTECT, NOLVP, NODEBUG, USBDIV, PLL5, CPUDIV1, VREGEN
#use delay(clock=48000000)

// USB CDC: Debe ir justo después del delay, sin #use timer
#include <usb_cdc.h>

// ============================================================================
// CONFIGURACIÓN DE PERIFÉRICOS
// ============================================================================
#use rs232(UART1, baud=9600, xmit=PIN_C6, rcv=PIN_C7, stream=DSP_UART, ERRORS)

#define LCD_BL   PIN_B3
#include <lcd420.c>

// ============================================================================
// DEFINICIÓN DE PINES
// ============================================================================
#define SHIFT_DATA   PIN_E0
#define SHIFT_CLK    PIN_E1
#define SHIFT_RST    PIN_E2

#define RELAY_MAX264    PIN_D0
#define RELAY_TS        PIN_D1
#define RELAY_LOAD1     PIN_D2
#define RELAY_SCALE100V PIN_D3
#define RELAY_SCALE1V   PIN_D4
#define RELAY_SCALE10V  PIN_D5
#define RELAY_LOAD2     PIN_D6

#define ADC_TEMP        0
#define ADC_AUTORANGE   1
#define ADC_KEYS1       2
#define ADC_KEYS2       3

#define ENC_BTN   PIN_C0
#define ENC_A     PIN_C1
#define ENC_B     PIN_C2

// ============================================================================
// COMANDOS UART
// ============================================================================
#define CMD_UPDATE_CONFIG  0xB0
#define CMD_EXECUTE        0xB1
#define CMD_READ_RESULT    0xB2
#define CMD_STOP           0x21
#define ACK                0x06

#define ID_THD        0x10
#define ID_SLEW       0x60
#define ID_POWER      0x30
#define ID_TS_PARAMS  0x50
#define ID_FREQ_RESP  0x80

// ============================================================================
// CONSTANTES
// ============================================================================

#define ADC_TOLERANCE     30
#define TEMP_MAX_ADC      143
#define ENC_DEBOUNCE_MS   50
#define BTN_DEBOUNCE_MS   30
#define UART_TIMEOUT_MS   500
#define MAX_QUEUE_SIZE    6
#define MAX_RESULTS       6
#define LONG_PRESS_MS     1500
#define TEMP_ALERT_HOLD_MS 2000

#define BTN_THRESHOLD_1   1002
#define BTN_THRESHOLD_2   958
#define BTN_THRESHOLD_3   880
#define BTN_THRESHOLD_4   729
#define BTN_THRESHOLD_5   445

#define STATE_IDLE        0
#define STATE_RUNNING     1

#define THD_MODE_GOERTZEL   0
#define THD_MODE_RESIDUAL   1
#define THD_MODE_IMD        2

// Máquina de estados de ejecución de la cola (no bloqueante)
#define QSTATE_IDLE        0
#define QSTATE_WAIT_ACK    1
#define QSTATE_WAIT_RESULT 2

#define BTN_THD       ID_THD
#define BTN_SWEEP     ID_FREQ_RESP
#define BTN_RMS       ID_POWER
#define BTN_SLEW      ID_SLEW
#define BTN_TS        ID_TS_PARAMS
#define BTN_DDS       0x90
#define BTN_START     0x01
#define BTN_CLEAR     0x02
#define BTN_LOAD      0x03
#define BTN_SCALE     0x04

// ============================================================================
// VARIABLES GLOBALES
// ============================================================================
unsigned int32 system_ms = 0; // Reemplazo seguro para get_ticks()

int8 queue[MAX_QUEUE_SIZE];
int8 queue_size = 0;
int8 queue_index = 0;

int8 system_state = STATE_IDLE;

int8 thd_mode = THD_MODE_GOERTZEL;
float thd_freq = 1000.0;
int8 scale_mode = 0;
int8 load_mode = 0;
int8 load_rotation = 0;
int1 auto_range = 1;

#define RX_BUF_SIZE 128
char rx_buffer[RX_BUF_SIZE];
volatile int8 rx_idx = 0;
volatile int1 line_ready = 0;
volatile int1 ack_received = 0; // Set por la ISR cuando llega el byte ACK

int8 enc_last_state = 0;
int8 enc_direction = 0;
int1 enc_btn_pressed = 0;

typedef struct {
    int8 test_id;
    int8 num_values;
    float values[10];
    int1 completed;
} TestResult_t;

TestResult_t result_buffer[MAX_RESULTS];
int8 result_count = 0;
int8 current_page = 0;
int8 total_pages = 0;

unsigned int32 last_enc_read = 0;
unsigned int32 last_temp_check = 0;
unsigned int32 button_press_time = 0;
int8 last_button = 0;

// Debounce de botones no bloqueante
int8 button_candidate = 0;
unsigned int32 button_candidate_time = 0;

// Estado de ejecución de la cola (reemplaza al ProcessQueue/ExecuteTest bloqueantes)
int8 queue_run_state = QSTATE_IDLE;
unsigned int32 queue_run_timer = 0;

// Alerta de temperatura no bloqueante
int1 temp_alert_active = 0;
unsigned int32 temp_alert_timer = 0;

// ============================================================================
// PROTOTIPOS
// ============================================================================
void InitSystem(void);
void ShiftRegister_Write(int8 data);
void UpdateLEDs(void);
void SetRelay(int8 relay, int1 state);
void SetScale(int8 scale);
void SetLoad(int8 load);
int8 ReadButtonAN2(void);
int8 ReadButtonAN3(void);
void ProcessButtons(void);
void ReadEncoder(void);
void StartTest(int8 test_id);
void StartQueueExecution(void);
void UpdateQueueExecution(void);
void AdvanceQueue(void);
void SendCommand(int8 cmd, int8 *params, int8 len);
void ParseAndStoreResult(char *rx_buf);
void UpdateLCD(void);
void DisplayCurrentPage(void);
void CheckTemperature(void);
void AutoRange(void);
int8 ComputeChecksum(int8 *data, int8 len);
void ClearResults(void);
void ClearQueue(void);
void ReturnToIdle(void);
void RemoveFromQueue(int8 test_id);

// ============================================================================
// INTERRUPCIÓN UART
// ============================================================================
// Detecta el ACK de un solo byte sin esperar salto de línea (antes se perdía:
// la ISR igual consumía ese byte hacia rx_buffer y WaitForACK() nunca lo veía).
#INT_RDA
void uart_isr(void) {
    char c = fgetc(DSP_UART);
    if (c == ACK) {
        ack_received = 1;
        return;
    }
    if (c == '\n' || c == '\r') {
        if (rx_idx > 0) {
            rx_buffer[rx_idx] = '\0';
            line_ready = 1;
            rx_idx = 0;
        }
    } else {
        if (rx_idx < RX_BUF_SIZE - 1) {
            rx_buffer[rx_idx++] = c;
        } else {
            rx_idx = 0;
        }
    }
}

// ============================================================================
// FUNCIONES DE HARDWARE
// ============================================================================
void ShiftRegister_Write(int8 data) {
    int8 i;
    output_low(SHIFT_RST);
    delay_us(1);
    output_high(SHIFT_RST);
    for (i = 0; i < 8; i++) {
        if (bit_test(data, 7)) output_high(SHIFT_DATA);
        else output_low(SHIFT_DATA);
        output_low(SHIFT_CLK);
        delay_us(1);
        output_high(SHIFT_CLK);
        data = data << 1;
    }
}

void UpdateLEDs(void) {
    int8 leds = 0;
    int8 i;
    for (i = 0; i < queue_size; i++) {
        if (queue[i] == ID_THD) bit_set(leds, 0);
        if (queue[i] == ID_FREQ_RESP) bit_set(leds, 1);
        if (queue[i] == ID_POWER) bit_set(leds, 2);
        if (queue[i] == ID_SLEW) bit_set(leds, 3);
        if (queue[i] == ID_TS_PARAMS) bit_set(leds, 4);
        if (queue[i] == 0x90) bit_set(leds, 5);
    }
    if (load_mode == 0) bit_set(leds, 6);
    if (load_mode == 1) bit_set(leds, 7);
    ShiftRegister_Write(leds);
}

void SetRelay(int8 relay, int1 state) {
    if (state) output_high(relay); else output_low(relay);
}

void SetScale(int8 scale) {
    SetRelay(RELAY_SCALE1V, 0); SetRelay(RELAY_SCALE10V, 0); SetRelay(RELAY_SCALE100V, 0);
    if (scale == 1) SetRelay(RELAY_SCALE1V, 1);
    else if (scale == 2) SetRelay(RELAY_SCALE10V, 1);
    else if (scale == 3) SetRelay(RELAY_SCALE100V, 1);
}

// ============================================================================
// FUNCIÓN: SetLoad (Con alternancia de desgaste y estado OFF)
// ============================================================================
void SetLoad(int8 load) {
    if (load == 0) {
        // 8O: Alternar entre RELAY_LOAD1 (RD2) y RELAY_LOAD2 (RD6)
        if (load_rotation == 0) {
            SetRelay(RELAY_LOAD1, 1);
            SetRelay(RELAY_LOAD2, 0);
        } else {
            SetRelay(RELAY_LOAD1, 0);
            SetRelay(RELAY_LOAD2, 1);
        }
        // Invertir el estado para la próxima vez que se active 8O
        load_rotation = 1 - load_rotation;
    }
    else if (load == 1) {
        // 4O: Ambas en paralelo
        SetRelay(RELAY_LOAD1, 1);
        SetRelay(RELAY_LOAD2, 1);
    }
    else {
        // 2: OFF (Sin carga activa)
        SetRelay(RELAY_LOAD1, 0);
        SetRelay(RELAY_LOAD2, 0);
    }
}

int8 ReadButtonAN2(void) {
    int16 adc_val;
    set_adc_channel(ADC_KEYS1); delay_us(20); adc_val = read_adc();
    if (adc_val > (BTN_THRESHOLD_1 - ADC_TOLERANCE) && adc_val < (BTN_THRESHOLD_1 + ADC_TOLERANCE)) return BTN_THD;
    if (adc_val > (BTN_THRESHOLD_2 - ADC_TOLERANCE) && adc_val < (BTN_THRESHOLD_2 + ADC_TOLERANCE)) return BTN_SWEEP;
    if (adc_val > (BTN_THRESHOLD_3 - ADC_TOLERANCE) && adc_val < (BTN_THRESHOLD_3 + ADC_TOLERANCE)) return BTN_RMS;
    if (adc_val > (BTN_THRESHOLD_4 - ADC_TOLERANCE) && adc_val < (BTN_THRESHOLD_4 + ADC_TOLERANCE)) return BTN_SLEW;
    if (adc_val > (BTN_THRESHOLD_5 - ADC_TOLERANCE) && adc_val < (BTN_THRESHOLD_5 + ADC_TOLERANCE)) return BTN_TS;
    return 0;
}

int8 ReadButtonAN3(void) {
    int16 adc_val;
    set_adc_channel(ADC_KEYS2); delay_us(20); adc_val = read_adc();
    if (adc_val > (BTN_THRESHOLD_1 - ADC_TOLERANCE) && adc_val < (BTN_THRESHOLD_1 + ADC_TOLERANCE)) return BTN_DDS;
    if (adc_val > (BTN_THRESHOLD_2 - ADC_TOLERANCE) && adc_val < (BTN_THRESHOLD_2 + ADC_TOLERANCE)) return BTN_START;
    if (adc_val > (BTN_THRESHOLD_3 - ADC_TOLERANCE) && adc_val < (BTN_THRESHOLD_3 + ADC_TOLERANCE)) return BTN_CLEAR;
    if (adc_val > (BTN_THRESHOLD_4 - ADC_TOLERANCE) && adc_val < (BTN_THRESHOLD_4 + ADC_TOLERANCE)) return BTN_LOAD;
    if (adc_val > (BTN_THRESHOLD_5 - ADC_TOLERANCE) && adc_val < (BTN_THRESHOLD_5 + ADC_TOLERANCE)) return BTN_SCALE;
    return 0;
}

// ============================================================================
// LÓGICA DE UI
// ============================================================================
// Antes: cada acción hacía delay_ms(200), congelando usb_task()/UART durante
// 200ms por cada toque de botón. Ahora: debounce por tiempo (no bloqueante,
// dos lecturas estables separadas por BTN_DEBOUNCE_MS) y disparo único por
// flanco de pulsación (edge), sin ningún delay_ms.
void ProcessButtons(void) {
    int8 btn_func = ReadButtonAN2();
    int8 btn_ctrl = ReadButtonAN3();
    int8 current_btn = btn_func | btn_ctrl;

    if (current_btn != button_candidate) {
        button_candidate = current_btn;
        button_candidate_time = system_ms;
        return; // todavía rebotando, se confirma en una vuelta futura del loop
    }
    if ((system_ms - button_candidate_time) < BTN_DEBOUNCE_MS) return;

    // current_btn es ahora una lectura estable y confirmada
    if (current_btn != 0 && current_btn == last_button) {
        if (system_state == STATE_IDLE && current_btn > 0x04 &&
            (system_ms - button_press_time) > LONG_PRESS_MS) {
            RemoveFromQueue(current_btn);
            last_button = 0; // evita retrigger hasta soltar el botón
        }
        return;
    }

    last_button = current_btn;
    button_press_time = system_ms;
    if (current_btn == 0) return; // fue una liberación, no una pulsación nueva

    if (btn_ctrl == BTN_START) {
        if (system_state == STATE_IDLE) {
            if (queue_size > 0) {
                system_state = STATE_RUNNING;
                ClearResults();
                lcd_gotoxy(1, 4);
                printf(lcd_putc, "Ejecutando...     ");
                StartQueueExecution();
            }
        } else {
            fputc(CMD_STOP, DSP_UART);
            queue_run_state = QSTATE_IDLE;
            ReturnToIdle();
        }
        return;
    }

    if (system_state == STATE_RUNNING) return;

    if (btn_ctrl == BTN_CLEAR) {
        ClearQueue(); ClearResults(); current_page = 0; total_pages = 0;
        lcd_gotoxy(1, 4); printf(lcd_putc, "Cola limpiada     ");
        UpdateLEDs(); return;
    }
    // Botón Load Select (Cicla: 8O -> 4O -> OFF -> 8O)
    if (btn_ctrl == BTN_LOAD) {
        load_mode++;
        if (load_mode > 2) load_mode = 0;

        SetLoad(load_mode);
        UpdateLEDs();
        lcd_gotoxy(1, 4);

        if (load_mode == 0) {
            printf(lcd_putc, "Load: 8%c      ",0xE4);
        } else if (load_mode == 1) {
            printf(lcd_putc, "Load: 4%c      ",0xE4);
        } else {
            printf(lcd_putc, "Load: OFF        ");
        }
        return;
    }
    if (btn_ctrl == BTN_SCALE) {
        scale_mode++; if (scale_mode > 3) scale_mode = 0;
        if (scale_mode == 0) { auto_range = 1; lcd_gotoxy(1, 4); printf(lcd_putc, "Scale: AUTO      "); }
        else {
            auto_range = 0; SetScale(scale_mode); lcd_gotoxy(1, 4);
            if (scale_mode == 1) printf(lcd_putc, "Scale: 1V        ");
            else if (scale_mode == 2) printf(lcd_putc, "Scale: 10V       ");
            else printf(lcd_putc, "Scale: 100V      ");
        }
        return;
    }
    if (btn_func != 0) {
        if (btn_func == BTN_TS || btn_func == BTN_DDS) {
            ClearQueue(); queue[0] = btn_func; queue_size = 1;
        } else {
            int1 already_in_queue = 0; int8 i;
            for (i = 0; i < queue_size; i++) { if (queue[i] == btn_func) { already_in_queue = 1; break; } }
            if (!already_in_queue && queue_size < MAX_QUEUE_SIZE) queue[queue_size++] = btn_func;
        }
        UpdateLEDs(); lcd_gotoxy(1, 4); printf(lcd_putc, "Cola: %u pruebas    ", queue_size);
    }
}

void RemoveFromQueue(int8 test_id) {
    int8 found = 0; int8 i, j;
    for (i = 0; i < queue_size; i++) {
        if (queue[i] == test_id) {
            found = 1;
            for (j = i; j < queue_size - 1; j++) queue[j] = queue[j + 1];
            queue_size--; break;
        }
    }
    if (found) { UpdateLEDs(); lcd_gotoxy(1, 4); printf(lcd_putc, "Quitado de cola   "); }
}

void ReadEncoder(void) {
    int8 state = 0;
    if (!input(ENC_A)) state |= 0x01;
    if (!input(ENC_B)) state |= 0x02;
    if (!input(ENC_BTN)) state |= 0x04;
    if (state != enc_last_state) {
        if ((enc_last_state == 0x00 && state == 0x01) || (enc_last_state == 0x01 && state == 0x03) ||
            (enc_last_state == 0x03 && state == 0x02) || (enc_last_state == 0x02 && state == 0x00)) enc_direction = 1;
        else if ((enc_last_state == 0x00 && state == 0x02) || (enc_last_state == 0x02 && state == 0x03) ||
                 (enc_last_state == 0x03 && state == 0x01) || (enc_last_state == 0x01 && state == 0x00)) enc_direction = 2;
        if (state & 0x04) enc_btn_pressed = 1;
        enc_last_state = state;
    }
}

// ============================================================================
// EJECUCIÓN DE PRUEBAS (máquina de estados no bloqueante)
// ============================================================================
// Antes: ProcessQueue() era un for{} que llamaba ExecuteTest() de forma
// sincrónica para cada prueba, y ExecuteTest() a su vez bloqueaba con
// WaitForACK() (hasta 500ms) y un while(!line_ready) (hasta 500ms más).
// Mientras tanto usb_task() no se llamaba: el enlace USB podía caerse o
// perder datos durante todo el tiempo que tomara ejecutar la cola completa.
//
// Ahora: StartTest() solo transmite y arma el estado; UpdateQueueExecution()
// se llama una vez por vuelta del loop principal y avanza un paso cuando
// corresponde (ACK recibido, resultado recibido, o timeout). El resto del
// sistema (USB, botones, encoder, temperatura) sigue respondiendo en todo
// momento, incluso con la cola en ejecución.

void StartTest(int8 test_id) {
    int8 config[8]; int8 checksum;

    lcd_gotoxy(1, 4); printf(lcd_putc, "Test   %u/%u: ", queue_index + 1, queue_size);
    switch (test_id) {
        case ID_THD: printf(lcd_putc, "THD"); break;
        case ID_SLEW: printf(lcd_putc, "SLEW"); break;
        case ID_POWER: printf(lcd_putc, "POWER"); break;
        case ID_TS_PARAMS: printf(lcd_putc, "T/S"); break;
        case ID_FREQ_RESP: printf(lcd_putc, "FRESP"); break;
        case 0x90: printf(lcd_putc, "DDS"); break;
    }

    if (test_id == ID_THD) {
        int16 muestras = 4800; int16 freq = (int16)thd_freq;
        config[0] = (muestras >> 8) & 0xFF; config[1] = muestras & 0xFF;
        config[2] = (freq >> 8) & 0xFF; config[3] = freq & 0xFF;
        config[4] = thd_mode; config[5] = 0; config[6] = 0; config[7] = 0;
        checksum = ComputeChecksum(config, 8);
        SendCommand(CMD_UPDATE_CONFIG, &test_id, 1); SendCommand(CMD_UPDATE_CONFIG, config, 9);
    } else if (test_id == ID_SLEW || test_id == ID_POWER) {
        int32 muestras = 512; float carga = 8.0;
        config[0] = (muestras >> 24) & 0xFF; config[1] = (muestras >> 16) & 0xFF;
        config[2] = (muestras >> 8) & 0xFF; config[3] = muestras & 0xFF;
        int8 i; int8 *carga_ptr = (int8*)&carga;
        for (i = 0; i < 4; i++) config[4 + i] = carga_ptr[i];
        checksum = ComputeChecksum(config, 8);
        SendCommand(CMD_UPDATE_CONFIG, &test_id, 1); SendCommand(CMD_UPDATE_CONFIG, config, 9);
    }

    ack_received = 0;
    SendCommand(CMD_EXECUTE, &test_id, 1);
    queue_run_state = QSTATE_WAIT_ACK;
    queue_run_timer = system_ms;
}

void StartQueueExecution(void) {
    queue_index = 0;
    StartTest(queue[queue_index]);
}

void AdvanceQueue(void) {
    queue_index++;
    if (queue_index < queue_size) {
        StartTest(queue[queue_index]);
    } else {
        queue_run_state = QSTATE_IDLE;
        ReturnToIdle();
    }
}

void UpdateQueueExecution(void) {
    if (system_state != STATE_RUNNING) return;

    switch (queue_run_state) {
        case QSTATE_WAIT_ACK:
            if (ack_received) {
                ack_received = 0;
                SendCommand(CMD_READ_RESULT, &queue[queue_index], 1);
                queue_run_state = QSTATE_WAIT_RESULT;
                queue_run_timer = system_ms;
            } else if ((system_ms - queue_run_timer) > UART_TIMEOUT_MS) {
                lcd_gotoxy(1, 4); printf(lcd_putc, "Error: Timeout    ");
                AdvanceQueue();
            }
            break;

        case QSTATE_WAIT_RESULT:
            if (line_ready) {
                ParseAndStoreResult(rx_buffer);
                line_ready = 0;
                total_pages = result_count;
                if (current_page >= total_pages) current_page = total_pages - 1;
                DisplayCurrentPage();
                AdvanceQueue();
            } else if ((system_ms - queue_run_timer) > UART_TIMEOUT_MS) {
                lcd_gotoxy(1, 4); printf(lcd_putc, "Error: Timeout    ");
                AdvanceQueue();
            }
            break;

        default:
            break;
    }
}

void SendCommand(int8 cmd, int8 *params, int8 len) {
    disable_interrupts(INT_RDA);
    fputc(cmd, DSP_UART); int8 i;
    for (i = 0; i < len; i++) fputc(params[i], DSP_UART);
    enable_interrupts(INT_RDA);
}

void ParseAndStoreResult(char *rx_buf) {
    int8 start = 0; int8 count = 0; int8 i = 0; int8 str_idx = 0;
    char num_str[20]; float values[10]; int8 test_id = 0;
    if (rx_buf[0] != 'R' || rx_buf[1] != 'E' || rx_buf[2] != 'S' || rx_buf[3] != ':') return;
    start = 4;
    while (rx_buf[start] != ':' && rx_buf[start] != '\0') start++;
    if (rx_buf[start] == '\0') return; start++;
    while (rx_buf[start] >= '0' && rx_buf[start] <= '9') { test_id = test_id * 10 + (rx_buf[start] - '0'); start++; }
    while (rx_buf[start] != ':' && rx_buf[start] != '\0') start++;
    if (rx_buf[start] == '\0') return; start++;
    while (rx_buf[start] != '\0' && count < 10) {
        str_idx = 0;
        while (rx_buf[start] != ',' && rx_buf[start] != '\0' && str_idx < 19) num_str[str_idx++] = rx_buf[start++];
        num_str[str_idx] = '\0';
        float result = 0.0; float decimal = 0.1; int1 is_negative = 0; int8 idx = 0;
        if (num_str[idx] == '-') { is_negative = 1; idx++; }
        while (num_str[idx] >= '0' && num_str[idx] <= '9') { result = result * 10.0 + (num_str[idx] - '0'); idx++; }
        if (num_str[idx] == '.') { idx++; while (num_str[idx] >= '0' && num_str[idx] <= '9') { result += (num_str[idx] - '0') * decimal; decimal *= 0.1; idx++; } }
        if (is_negative) result = -result;
        values[count] = result; count++;
        if (rx_buf[start] == ',') start++;
    }
    if (result_count < MAX_RESULTS) {
        result_buffer[result_count].test_id = test_id; result_buffer[result_count].num_values = count;
        for (i = 0; i < count && i < 10; i++) result_buffer[result_count].values[i] = values[i];
        result_buffer[result_count].completed = 1; result_count++;
    }
}

void DisplayCurrentPage(void) {
    if (total_pages == 0 || current_page >= result_count) {
        lcd_gotoxy(1, 1); printf(lcd_putc, "No Results        ");
        lcd_gotoxy(1, 2); printf(lcd_putc, "                  ");
        lcd_gotoxy(1, 3); printf(lcd_putc, "                  ");
        lcd_gotoxy(1, 4); printf(lcd_putc, "Encoder: Browse   "); return;
    }
    TestResult_t *result = &result_buffer[current_page];
    lcd_gotoxy(1, 1); lcd_gotoxy(1, 2); lcd_gotoxy(1, 3); lcd_gotoxy(1, 4);
    switch (result->test_id) {
        case ID_THD:
            if (thd_mode == THD_MODE_IMD) {
                printf(lcd_putc, "IMD:  %f%%       ", result->values[0]);
                printf(lcd_putc, "F1:   %fVrms     ", result->values[1]);
                printf(lcd_putc, "F2:   %fVrms     ", result->values[2]);
                printf(lcd_putc, "SMPTE 60+7kHz       ");
            } else {
                printf(lcd_putc, "THD:%f%% N:%f%%", result->values[0], result->values[1]);
                printf(lcd_putc, "SINAD:%fdB SNR:%f", result->values[2], result->values[3]);
                printf(lcd_putc, "Fund:%fVrms       ", result->values[4]);
                printf(lcd_putc, "Freq:%fHz M:", thd_freq);
                if (thd_mode == 0) printf(lcd_putc, "Goertzel"); else printf(lcd_putc, "Residual");
            } break;
        case ID_SLEW:
            printf(lcd_putc, "Slew Rate:          ");
            printf(lcd_putc, " %f V/us        ", result->values[0]);
            printf(lcd_putc, "Vmax: %fV        ", result->values[2]);
            printf(lcd_putc, "Freq:1000Hz Sq      "); break;
        case ID_POWER:
            if (current_page % 2 == 0) {
                printf(lcd_putc, "Power:%fW        ", result->values[0]);
                printf(lcd_putc, "Peak: %fW        ", result->values[1]);
                printf(lcd_putc, "Eff:  %f%%        ", result->values[5]);
                printf(lcd_putc, "Vrail:%fV        ", result->values[2]);
            } else {
                printf(lcd_putc, "Damp: %f         ", result->values[6]);
                printf(lcd_putc, "Zout: %f Ohm     ", result->values[7]);
                printf(lcd_putc, "VA:   %f VA      ", result->values[8]);
                printf(lcd_putc, "PF:   %f         ", result->values[9]);
            } break;
        case ID_TS_PARAMS:
            if (current_page % 2 == 0) {
                printf(lcd_putc, "Thiele-Small        ");
                printf(lcd_putc, "Fs:   %f Hz      ", result->values[0]);
                printf(lcd_putc, "Qts:  %f         ", result->values[1]);
                printf(lcd_putc, "Qes:  %f         ", result->values[2]);
            } else {
                printf(lcd_putc, "Thiele-Small        ");
                printf(lcd_putc, "Qms:  %f         ", result->values[3]);
                printf(lcd_putc, "Re:   %f Ohm     ", result->values[4]);
                printf(lcd_putc, "Zmax: %f Ohm     ", result->values[5]);
            } break;
        case ID_FREQ_RESP:
            if (current_page == 0) {
                printf(lcd_putc, "Freq Response       ");
                printf(lcd_putc, "Points: 32          ");
                printf(lcd_putc, "Noise:%f dB      ", result->values[32]);
                printf(lcd_putc, "DynSNR:%f dB     ", result->values[33]);
            } else {
                int8 freq_idx = (current_page - 1) * 4;
                if (freq_idx < 32) printf(lcd_putc, "%fHz:%f dB     ", (float)freq_idx, result->values[freq_idx]);
                if (freq_idx + 1 < 32) printf(lcd_putc, "%fHz:%f dB     ", (float)(freq_idx+1), result->values[freq_idx+1]);
                if (freq_idx + 2 < 32) printf(lcd_putc, "%fHz:%f dB     ", (float)(freq_idx+2), result->values[freq_idx+2]);
                if (freq_idx + 3 < 32) printf(lcd_putc, "%fHz:%f dB     ", (float)(freq_idx+3), result->values[freq_idx+3]);
            } break;
    }
    if (total_pages > 1) { lcd_gotoxy(16, 4); printf(lcd_putc, "%u/%u", current_page + 1, total_pages); }
}

void UpdateLCD(void) {
    if (system_state == STATE_IDLE) {
        lcd_gotoxy(1, 1); printf(lcd_putc, "RatNET Analyzer   ");
        lcd_gotoxy(1, 2); printf(lcd_putc, "THD Freq: %f Hz  ", thd_freq);
        lcd_gotoxy(1, 3);
        if (auto_range) printf(lcd_putc, "Scale:AUTO Load:"); else printf(lcd_putc, "Scale:    Load:");
        if (load_mode == 0) printf(lcd_putc, "8"); else printf(lcd_putc, "4");
        printf(lcd_putc, "%c             ",0xE4);
        lcd_gotoxy(1, 4); printf(lcd_putc, "Queue: %u tests     ", queue_size);
    }
}

// Antes: delay_ms(2000) bloqueante en cada ciclo con sobretemperatura, lo que
// congelaba USB/UART/botones 2 segundos completos. Ahora el mensaje se
// sostiene con un temporizador de system_ms y el loop principal sigue vivo.
void CheckTemperature(void) {
    set_adc_channel(ADC_TEMP); delay_us(20); int16 temp_adc = read_adc();
    if (temp_adc > TEMP_MAX_ADC) {
        if (!temp_alert_active) {
            temp_alert_active = 1;
            SetRelay(RELAY_LOAD1, 0); SetRelay(RELAY_LOAD2, 0);
            if (system_state == STATE_RUNNING) {
                fputc(CMD_STOP, DSP_UART);
                queue_run_state = QSTATE_IDLE;
                ReturnToIdle();
            }
        }
        temp_alert_timer = system_ms;
        lcd_gotoxy(1, 4); printf(lcd_putc, "HI TEMP ALERT!      ");
    } else if (temp_alert_active && (system_ms - temp_alert_timer) > TEMP_ALERT_HOLD_MS) {
        temp_alert_active = 0;
    }
}

void AutoRange(void) {
    if (!auto_range) return;
    set_adc_channel(ADC_AUTORANGE); delay_us(20); int16 adc_val = read_adc();
    if (adc_val < 20) SetScale(1); else if (adc_val < 200) SetScale(2); else SetScale(3);
}

int8 ComputeChecksum(int8 *data, int8 len) {
    int8 checksum = 0; int8 i;
    for (i = 0; i < len; i++) checksum ^= data[i];
    return checksum;
}

void ClearResults(void) { result_count = 0; current_page = 0; total_pages = 0; }
void ClearQueue(void) { queue_size = 0; queue_index = 0; }
void ReturnToIdle(void) { system_state = STATE_IDLE; queue_index = 0; UpdateLCD(); }

// ============================================================================
// INICIALIZACIÓN
// ============================================================================
void InitSystem(void) {
    setup_adc_ports(AN0_TO_AN3);
    setup_adc(ADC_CLOCK_DIV_64);

    output_low(RELAY_MAX264); output_low(RELAY_TS); output_low(RELAY_LOAD1);
    output_low(RELAY_SCALE100V); output_low(RELAY_SCALE1V); output_low(RELAY_SCALE10V); output_low(RELAY_LOAD2);

    output_high(LCD_BL);
    lcd_init();
    lcd_gotoxy(1, 1); printf(lcd_putc, "      RatNET       ");
    lcd_gotoxy(1, 2); printf(lcd_putc, "  Audio  Analyzer  ");
    delay_ms(1000); // Único delay bloqueante que queda: splash de arranque, una sola vez.

    ShiftRegister_Write(0x00);
    SetLoad(0);
    auto_range = 1;

    // Inicializar USB CDC (Nativo de CCS)
    usb_cdc_init();

    enable_interrupts(INT_RDA);
    enable_interrupts(GLOBAL);

    lcd_putc('\f');
    UpdateLCD();
}

// ============================================================================
// FUNCIÓN PRINCIPAL
// ============================================================================
void main(void) {
    InitSystem();

    while (TRUE) {
        system_ms += 10; // Contador de tiempo seguro (10ms por iteración)

        // 1. Mantener el stack USB vivo (CRÍTICO: debe ser lo primero)
        usb_task();

        // 2. Puente USB -> UART (La PC envía comandos al dsPIC)
        if (usb_enumerated()) {
            if (usb_cdc_kbhit()) {
                char c = usb_cdc_getc();
                fputc(c, DSP_UART);
            }
        }

        // 3. UI Frontal - Botones (no bloqueante, debounce por tiempo)
        ProcessButtons();

        // 4. Encoder
        unsigned int32 enc_elapsed = system_ms - last_enc_read;
        if (enc_elapsed > ENC_DEBOUNCE_MS) {
            ReadEncoder();
            last_enc_read = system_ms;
            if (enc_direction == 1 && current_page < total_pages - 1) {
                current_page++;
                DisplayCurrentPage();
            } else if (enc_direction == 2 && current_page > 0) {
                current_page--;
                DisplayCurrentPage();
            }
            enc_direction = 0;
        }

        // 5. Puente UART -> USB (El dsPIC envía resultados a la PC)
        if (line_ready) {
            if (usb_cdc_connected()) {
                int8 i = 0;
                while (rx_buffer[i] != '\0') {
                    usb_cdc_putc(rx_buffer[i]);
                    i++;
                }
                usb_cdc_putc('\r');
                usb_cdc_putc('\n');
            }
            // Si la cola está corriendo, UpdateQueueExecution() consume line_ready
            // en este mismo ciclo (paso 7). Si no, lo consumimos acá para no
            // dejar basura pendiente ni parsear dos veces la misma línea.
            if (system_state != STATE_RUNNING) {
                ParseAndStoreResult(rx_buffer);
                line_ready = 0;
                if (result_count > 0) {
                    total_pages = result_count;
                    DisplayCurrentPage();
                }
            }
        }

        // 6. Temperatura
        unsigned int32 temp_elapsed = system_ms - last_temp_check;
        if (temp_elapsed > 1000) {
            CheckTemperature();
            last_temp_check = system_ms;
        }

        // 7. Avance no bloqueante de la cola de pruebas en ejecución
        UpdateQueueExecution();

        // 8. Autorango
        if (auto_range && system_state == STATE_IDLE) {
            AutoRange();
        }

        delay_ms(10); // Tick fijo del scheduler cooperativo, no una espera de operación
    }
}

Donde meti mano? te deje comentado, pero para que no se te pongan los ojos bizcochos, te comento acá...

1) Botones: sin delay_ms(200); O sea, antes cada toque congelaba todo (USB incluido) por 200ms. Ahora hay un debounce por tiempo real (BTN_DEBOUNCE_MS, no bloqueante): se exige que la lectura del ADC se mantenga estable durante 30ms antes de aceptarla, y la acción se dispara una sola vez por flanco de pulsación. Ahora el loop nunca se detiene esperando.

2) Cola de pruebas: mande tijeras y de el for bloqueante pasamos a máquina de estados.
ProcessQueue()/ExecuteTest()/WaitForACK() desaparecen como funciones bloqueantes. Ahora quedaria:

StartTest() solo transmite el comando y arma el estado (QSTATE_WAIT_ACK).
UpdateQueueExecution() se llama una vez por vuelta del while(TRUE) y avanza cuando corresponde (ACK, resultado, o timeout por system_ms).

Esta es la espectativa: antes, mientras corría una prueba (sobre todo FREQ_RESP, que puede tardar), usb_task() no se llamaba nunca — eso puede tirar la enumeración USB. Ahora el bridge USB↔UART sigue vivo durante toda la ejecución.

3) Bug de protocolo con el ACK (esto es beta... revisalo)
WaitForACK() leía el byte 0x06 directo del UART con kbhit()/fgetc(), pero la ISR (#INT_RDA) seguía activa y se comía ese mismo byte primero (lo metía en rx_buffer porque no es "\n" "\r"). El ACK probablemente nunca llegaba a detectarse por esa vía, o corrompía el siguiente resultado. Ahora, en teoria, la propia ISR detecta el ACK y levanta un flag (ack_received), sin ambiguedad.

4) Alerta de temperatura sin delay_ms(2000).. O sea, ahora se sostiene el mensaje en pantalla con un temporizador (temp_alert_timer) en vez de bloquear 2 segundos completos.

5. El unico delay que queda es el delay_ms(1000) del splash inicial (una sola vez, al arranque) y el delay_ms(10) final del loop, que ahora es el "tick" del scheduler cooperativo, no una espera de operación — 10ms fijos vs. los cientos/miles que había antes que posta, practicamente eran como la maquina esa que inventaste y tira humo (RatAMPS).

Una cosa a verificar en tu placa experimental si es que la hiciste... confírmame que tan rápido responde el dsPIC al CMD_EXECUTE en el peor caso (sobre todo FREQ_RESP con barrido de 32 puntos) — si supera UART_TIMEOUT_MS (500ms) vas a necesitar subir ese valor o hacer el timeout dependiente del test. Si tenes prototipo funcional, creo que tenes que subirte un videíto ;)
Vos no dijistes que tenias STM32? o mencionaste en algun momento que tenias por ahi una blackpill?
Deja lo correo en el simulador a ver que onda, y si tengo unos STM32F429IIT6 que les saqué gratis a ST hace años, sólo que no me he puesto a aprender a programarlos, y hacerlo con IA no es una opción, si mete la pata no me daría cuenta de donde 🤣🤣🤣🤣🤣 de momento estoy estoy esperando a que terminen de llegar los componentes y ahorrar para mandar a hacer las PCBs, seguiremos informando
 
Deja lo correo en el simulador a ver que onda, y si tengo unos STM32F429IIT6 que les saqué gratis a ST hace años, sólo que no me he puesto a aprender a programarlos, y hacerlo con IA no es una opción, si mete la pata no me daría cuenta de donde 🤣🤣🤣🤣🤣 de momento estoy estoy esperando a que terminen de llegar los componentes y ahorrar para mandar a hacer las PCBs, seguiremos informando
Es que no necesitas IA para hacer esto... Quizas, si estas solo, podes usarla para debuguear, pero no hace falta.
Cuchame... En el simulador no vas a ver nada que te de un indicio favorable o no... Hace una semana que estoy con esto y estuve tentado en replicar alguna cosas con proteus, pero te vuelvo a repetir, espectativa/realidad... Yo solo te mande tijera donde me parecio que se podria mejorar... Respecto al lenguane del stm32, ya sabes ccs, usa El C++ de Arduino, o mas fscil aún, usa el c nativo que es igual y tenes unas adaptaciones para esa arquitectura incluido librerias como HAL...
1000288076.png
No sigas perdie do tie po con los pic viejos...
Esto lo achicas y mejoras con otras arquitecturas, que incluso, vuelan...
Igual, termina esta primero... Despues segui con la siguente version

PD: Te acordas los PDF de CCS que me distes en el dosmil y moneda? Jajajaja como salia corriendo y no queria saber nada ajajaja bueno, estas haciendo lo mismo y dentro de un par de años vas a meterte de lleno cuack!
 
Atrás
Arriba