Analizador de audio profesional

Ratmayor

ModeraTroll
Este es un proyecto extremadamente ambicioso que llevo dándole vueltas en la cabeza un buen rato, pero esta incompleto, así que iré subiendo los avances paulatinamente y son bienvenidos a dar sus sugerencias y opiniones.

Se trata de un equipo que permite medir distorsión armónica (THD), relación señal / ruido (SNR), distorsión / ruido (THD+N), distorsión por intermodulación (IMD), potencia, damping factor, respuesta de frecuencias, parámetros T/S entre otras cosas que pueda hacer con el mismo hardware y me vaya acordando 🤣🤣🤣

De momento iré compartiendo los diagramas y a medida que vaya investigando y diseñando iré explicando una a una las etapas y su funcionamiento, antes de pelearme con el software (Si, requiere software porque estaré usando 2 MCU)

Adjunto los diagramas de los datasheets de alguno de los ICs que usaré en el proyecto...

Etapa de entrada
Bien comencemos desde el principio el equipo cuenta con varios puntos de prueba, el primero es para conectar la fuente de sonido que se quiera probar, ya sea un amplificador o preamplificador, cuenta con 3 escalas seleccionables por medio de relés de 2.5V, 25V y 250V por lo que los divisores de tensión están calculados para el tope de escala del ADC (2.5V). En la entrada principal hay varias derivaciones que van a diferentes puntos del circuito, la linea marcada como ADC1 va para el ADC CS5389 que sera para mediciones de alta precisión como la THD, IMD, Slew Rate, etc. La línea marcada como IPR va con un sensor ACS712 para medir la corriente que circula hacia las cargas en el caso de estar probando la potencia de un amplificador, así como también la línea marcada como VPR es para medir la tensión que está ingresando, así podemos calcular la potencia y el damping factor, ya la última línea marcada como ARG va directamente al MCU de control y es para leer en que escala estamos, nos sirve como referencia para que el sistema sea auto rango y también como medida de protección en caso de exceder la escala seleccionada.

También hay un terminal marcado como Speaker que es para realizar pruebas de parámetros T/S, la línea marcada como Amp viene desde un pequeño amplificador interno basado en el LM1875 que tiene suficiente potencia para excitar el parlante y calcular su masa, para estas pruebas se usan simultáneamente los 2 ADCs internos del CS5389.

Ya por último cuenta con 2 entradas extra en donde se conectará la alimentación del amplificador que se esté probando y así poder calcular que tanta señal se le puede inyectar sin que se sature y poder calcular la eficiencia.
front-end.png
También cabe destacar que la selección de las escalas y funciones se harán por medio de pequeños relés DIP para no comprometer las mediciones, excepto los relés que activan / desactivan las cargas, esos si son relés de machos 🤣

Etapa de conversión analógica / digital
El ADC escogido es un CS5389 de 18bits, que además tiene una baja relación señal ruido de 100dB y un rango dinámico de 107dB, además entre los ADC exquisitos de encapsulado DIP (Sip, todo el proyecto es thru-hole, ya mi vista no es la de hace 20 años atrás) es el menos caro, claro que hay mejores opciones, pero estamos hablando de us$70 o más 🫠 y este se mantiene en el orden de los us$15 es un lujo, pero es manejable.
buffer_adc.png
El ADC requiere de señales balanceadas y para lograr esto, se escogió los OPA2132 ya que cuenta con la locura de 0.00008% de distorsión además de un ancho de banda de 8Mhz, lo que lo deja bastante sobrado para este proyecto. Las secciones como las de medición de potencia, damping factor, etc al no necesitar una precisión extrema, usan directamente los ADC del dsPIC30F4013I/P.

Es importante recalcar que se requiere el uso de resistores de metal film al 1% o al 0.1% para mantener la precisión del equipo, además que el sistema requiere de diferentes fuentes de alimentación que deben estar separadas, por eso se marcan como ±5VD y ±5VA en referencia a 5V digitales y 5V analógicos, incluso las conexiones a tierra también deben ir separadas y eso es para evitar que los ruidos de los clock y data se cuelen a las líneas analógicas, lo que afectaría gravemente las mediciones. No obstante, si podemos mantener el ruido al mínimo, el equipo tiene la sensibilidad de detectar niveles de distorsión del 0.001% que para ser un proyecto DIY es bastante generoso...
 

Adjuntos

  • CS5389.pdf
    1.7 MB · Visitas: 9
  • opa2132.pdf
    1.3 MB · Visitas: 1
  • DAC.pdf
    37.3 KB · Visitas: 0
  • CPU.pdf
    59.2 KB · Visitas: 0
  • CS4328.pdf
    951.2 KB · Visitas: 0
  • ADC.pdf
    62.1 KB · Visitas: 0
  • DSP.pdf
    42.1 KB · Visitas: 1
DSP, el musculo matemático
Para este proyecto escogí el dsPIC30F4013 qué es un controlador digital de señales (DSC) de alto rendimiento de 16 bits, que combina la potencia de cálculo de un procesador de señales digitales (DSP) con la facilidad de uso de un microcontrolador tradicional y además tengo varios ahí guardados :V

Al principio pensé en usar un solo DCP para todo, para los cálculos necesarios y para la interfaz de usuario, PERO, este MCU solo cuenta con 1kb de ROM y 2kb de RAM lo que se iba a convertir en un problema a la hora de calcular la FFT o correr los algoritmos Goertzel qué calculan la distorsión armónica si además tenía que atender los botones y la LCD 🤦🏻‍♂️

Bueno, vamo' a calmano', primero veamos la distribución del dsPIC33F4013, se está aprovechando el hardware del DSP, eso de andar haciendo bit-banging me da ansiedad 🤣 por lo que los pines quedaron asignados así:
Pinassing.png
Así sacamos el máximo provecho de los chips y reducimos la latencia.

Resolviendo problemas y limitaciones
Como comentaba el dsPIC30F4013 tiene una ROM de 1Kb y una RAM de 2Kb lo que para los procesos matemáticos pudiera verse limitado, así que se agregaron 2 memorias externas, una RAM 23LC1024 de 128kb y una EEPROM 24FC1025 para guardar datos de calibración y otros parámetros, también el cristal está calculado para que trabaje a ritmo con el ADC y el DAC para obtener una frecuencia de muestreo de 48kHz trabajando en el puerto DCI o Interfaz de Convertidor de Datos (DCI por sus siglas en inglés) que le permiten al dsPIC la interconexión sencilla de dispositivos, como codificadores/decodificadores de audio (códecs), convertidores A/D y convertidores D/A. El DCI gestiona automáticamente la
temporización de la interfaz asociada a estos códecs. No se requiere ninguna sobrecarga de la CPU hasta que la cantidad de datos solicitada se haya transmitido o recibido por el DCI. Se pueden transferir hasta cuatro palabras de datos entre interrupciones de la CPU, lo que vienen bien para dedicar más potencia de cálculo a los algoritmos...
DSP.png
La memoria RAM va conectada directamente en el puerto SPI nativo del MCU, la ROM va en el puerto nativo I2C y la comunicación con el otro MCU será vía UART.

El MCU recibe directamente las señales de prueba de tensión de fuente y potencia ya que como mencioné antes, no se requiere de demasiada precisión para estas mediciones, además, como me estresa ver un MCU con demasiados pines libres, se le agregaron 3 LEDs:, Blanco: MCU OK, Verde: MCU Busy, Rojo: MCU Error y también un pequeño buzzer para el bip de los botones...
 
Última edición:
Luego de pasar una virosis con todo y fibre prosigo con el proyecto :'V esta vez analicemos al director de la orquesta:

Unidad Control e Interfaz de usuario
Esta etapa está trabajando bajo el mando del PIC18F4550, ideal por tener un buen clock PLL 48Mhz, lo que le dará fluidez a la interfaz, este trabajará solo para que el usuario pueda comunicarse con el DSP (dsPIC30F4013) quien es quien realmente hace el trabajo matemático pesado y llama las funciones que hemos agregado al sistema que son:
  • Medición THD, THD+N, IMD, SINAD.
  • Ancho de banda (Barrido de 10Hz a 30kHz).
  • Analizador de espectro (FFT, lectura básica algo limitada por el dsPIC y la LCD que es de 20x4, pero podría ser de ayuda).
  • Medición de potencia RMS, Peak y DIN.
  • Medición de Factor de amortiguamiento (Damping Factor).
  • Medición de Slew-Rate.
  • Medición de parámetros T/S para parlantes.
  • Generador de frecuencia senoidal y cuadrada (10Hz a 30kHz).
  • Selector de cargas 4Ω y 8Ω
Teniendo en cuenta que el PIC18F4550 tiene puerto USB consideré que a futuro conforme reciba ayuda para el desarrollo de este equipo se pueda usar para transferir todos los datos recogidos a una PC y generar reportes.

CPU.png
Además del PIC, le agregué un desplazador de registro 74LS164 para activar / desactivar los LEDs que acompañan los botones, además de un rotary encoder para navegar entre las opciones de cada una de las pruebas. Además este MCU se encargará también de detectar el nivel de tensión que está entrando al equipo a modo de protección y para el auto rango (Pin ARG) y un pequeño LM35 para supervisar la temperatura de las resistencias de carga, además de controlar los relés que seleccionan las funciones y los rangos de la entrada (RY*) y mantiene comunicación constante con el DSP vía UART (U2TX, U2RX)

Conversión Digital - Analógica, Salidas
Esta es la etapa que más llevó revisiones, en especial porque hay pocos DAC "Exquisitos" de encapsulado DIP por los cuales no te quieran pedir un riñón, lo intenté con el AD1865, TDA1540, TDA1541, DAC745KP y el PCM1702 hasta que me topé con el DS4328 que cuenta con 18bits al igual que ADC, lo que me evitaba que no se entendiera con el ADC y que el dsPIC tenga que hacer de traductor, además de un muy bajo nivel de distorsión y ruido, además a un precio módico
DAC.png
El DAC, al ser estéreo, uso los 2 canales para funciones diferentes, el canal L o 1, es la salida de frecuencia que además servirá para enviar señales limpias al amplificador que tengamos en prueba, y el canal R o 2 lo uso exclusivamente para el LM1875 que a su vez servirá de excitador para el parlante que tengamos a prueba en el modo de prueba de parámetros T/S.

De momento esto es lo que llevo desarrollado, espero encarecidamente me puedan apoyar en el desarrollo de este proyecto y por mi parte mientras ahorro para comprar algunos ICs que me faltan y voy diseñando la PC, ir compartiendo lo que vaya programando...

Saludos a todos...
 
Última edición:
Luego de pasar una virosis con todo y fibre prosigo con el proyecto :'V esta vez analicemos al director de la orquesta:

Unidad Control e Interfaz de usuario
Esta etapa está trabajando bajo el mando del PIC18F4550, ideal por tener un buen clock PLL 48Mhz, lo que le dará fluidez a la interfaz, este trabajará solo para que el usuario pueda comunicarse con el DSP (dsPIC30F4013) quien es quien realmente hace el trabajo matemático pesado y llama las funciones que hemos agregado al sistema que son:
  • Medición THD, THD+N, IMD, SINAD.
  • Ancho de banda (Barrido de 10Hz a 30kHz).
  • Analizador de espectro (FFT, lectura básica algo limitada por el dsPIC y la LCD que es de 20x4, pero podría ser de ayuda).
  • Medición de potencia RMS, Peak y DIN.
  • Medición de Factor de amortiguamiento (Damping Factor).
  • Medición de Slew-Rate.
  • Medición de parámetros T/S para parlantes.
  • Generador de frecuencia senoidal y cuadrada (10Hz a 30kHz).
  • Selector de cargas 4Ω y 8Ω
Teniendo en cuenta que el PIC18F4550 tiene puerto USB consideré que a futuro conforme reciba ayuda para el desarrollo de este equipo se pueda usar para transferir todos los datos recogidos a una PC y generar reportes.

Ver el archivo adjunto 339938
Además del PIC, le agregué un desplazador de registro 74LS164 para activar / desactivar los LEDs que acompañan los botones, además de un rotary encoder para navegar entre las opciones de cada una de las pruebas. Además este MCU se encargará también de detectar el nivel de tensión que está entrando al equipo a modo de protección y para el auto rango (Pin ARG) y un pequeño LM35 para supervisar la temperatura de las resistencias de carga, además de controlar los relés que seleccionan las funciones y los rangos de la entrada (RY*) y mantiene comunicación constante con el DSP vía UART (U2TX, U2RX)

Conversión Digital - Analógica, Salidas
Esta es la etapa que más llevó revisiones, en especial porque hay pocos DAC "Exquisitos" de encapsulado DIP por los cuales no te quieran pedir un riñón, lo intenté con el AD1865, TDA1540, TDA1541, DAC745KP y el PCM1702 hasta que me topé con el DS4328 que cuenta con 18bits al igual que ADC, lo que me evitaba que no se entendiera con el ADC y que el dsPIC tenga que hacer de traductor, además de un muy bajo nivel de distorsión y ruido, además a un precio módico
Ver el archivo adjunto 339939
El DAC, al ser estéreo, uso los 2 canales para funciones diferentes, el canal L o 1, es la salida de frecuencia que además servirá para enviar señales limpias al amplificador que tengamos en prueba, y el canal R o 2 lo uso exclusivamente para el LM1875 que a su vez servirá de excitador para el parlante que tengamos a prueba en el modo de prueba de parámetros T/S.

De momento esto es lo que llevo desarrollado, espero encarecidamente me puedan apoyar en el desarrollo de este proyecto y por mi parte mientras ahorro para comprar algunos ICs que me faltan y voy diseñando la PC, ir compartiendo lo que vaya programando...

Saludos a todos...
El XLR será Out ¿no?
 
El XLR será Out ¿no?
Sí señor, sino perdería la profesionalidad, el buffer balanceado está conformado por un OpAmp cuádruple OPA4132

dac-png.339939

He aquí el código del dsPIC30F4013, lo generé con IA y no, no me da vergüenza admitirlo, requiere depuración, pero el sueño me venció, mañana seguimos... :cool:
C:
/* ==========================================================================
 * RatNET Audio Analyzer - Firmware DSP (dsPIC30F4013)
 * Autor: Carlos Alvarez
 * Rev. 1.2
 * ========================================================================== */

#include <30F4013.h>
#include <math.h>
#include <string.h>

// ==========================================
// 1. FUSES Y CONFIGURACIÓN DEL RELOJ
// ==========================================
// CORRECCIÓN: Configuración correcta de fuses para 12.288 MHz + PLL x4
#FUSES NOWDT, HS, NOPROTECT, NOLVP, NODEBUG, NOWRT, NOBROWNOUT, NOCPD
#FUSES XT_PLL4 
#use delay(clock=49152000)

// ==========================================
// 2. DEFINICIONES DE HARDWARE Y PINES
// ==========================================
#define LED_OK      PIN_B5
#define LED_BUSY    PIN_B6
#define LED_ERROR   PIN_B7
#define BUZZER      PIN_B8

// Comandos UART (Sin cambios - tu código original estaba bien)
#define CMD_SET_MODO_THD      0x10
#define CMD_SET_MODO_BAND     0x11
#define CMD_SET_MODO_ESPECTRO 0x12
#define CMD_SET_MODO_VU       0x13
#define CMD_Damping           0x40
#define CMD_TS_PARAMS         0x50
#define CMD_SLEW_RATE         0x60
#define CMD_GENERADOR         0x70
#define CMD_START             0x20
#define CMD_STOP              0x21

#define ACK                   0x06
#define NACK                  0x15

// ==========================================
// 3. COMUNICACIÓN UART
// ==========================================
// CORRECCIÓN: Agregar stream=UART_PIC18 para usar fprintf correctamente
#use rs232(UART2, baud=9600, xmit=PIN_F4, rcv=PIN_F5, stream=UART_PIC18, ERRORS)

// ==========================================
// 4. ESTRUCTURAS DE DATOS (Sin cambios - perfectas)
// ==========================================
typedef struct {
    float factorEscalaVoltaje;
    float factorEscalaCorriente;
    float offsetDC_ADC;
} Calibracion_t;

typedef struct {
    float dampingFactor;
    float impedanciaCarga;
    float factorPotencia;
    float potenciaAparente;
} MetricasDamping_t;

typedef struct {
    float frecuenciaResonancia;
    float impedanciaMax;
    float qts;
    float qms;
    float qes;
} MetricasTS_t;

typedef struct {
    float slewRateSubida;
    float slewRateBajada;
    float voltajePicoMax;
} MetricasSlewRate_t;

Calibracion_t calibActual;
unsigned int8 modoSeleccionado = 0x00;
int1 g_sistemaEjecutando = 0;

volatile int16 adcInterno_AN0_VIn = 0;
volatile int16 adcInterno_AN1_ACS = 0;
volatile int16 adcInterno_AN2_VccP = 0;
volatile int16 adcInterno_AN3_VccN = 0;

// ==========================================
// 5. PROTOTIPOS DE FUNCIONES
// ==========================================
void RetardoBuzzerMs(unsigned int16 ms);
void BuzzerUnBip(void);
void BuzzerTresBips(void);
void InicializarSistemaCalibracion(void);
int32 LeerADCExterno_DCI(void);  // CORRECCIÓN: Usar DCI nativo
void EscribirDACExterno_DCI(int16 muestraL, int16 muestraR);
void LeerADCsInternos(void);
void EjecutarDampingFactor(void);
void EjecutarParametrosTS(void);
void EjecutarSlewRate(void);
void EjecutarGeneradorFunciones(void);

// ==========================================
// 6. FUNCIONES DE UI (Sin cambios)
// ==========================================
void RetardoBuzzerMs(unsigned int16 ms) {
    for(unsigned int16 i = 0; i < ms; i++) { delay_ms(1); }
}

void BuzzerUnBip(void) {
    output_high(BUZZER);
    RetardoBuzzerMs(80);
    output_low(BUZZER);
}

void BuzzerTresBips(void) {
    for(int b = 0; b < 3; b++) {
        output_high(BUZZER);
        RetardoBuzzerMs(60);
        output_low(BUZZER);
        RetardoBuzzerMs(60);
    }
}

// ==========================================
// 7. RUTINAS DE AUDIO - CORRECCIÓN CRÍTICA
// ==========================================
// CORRECCIÓN: Reemplazar bit-banging por módulo DCI nativo del dsPIC
// Esto elimina jitter y libera CPU para cálculos

int32 LeerADCExterno_DCI(void) {
    // El módulo DCI lee automáticamente el frame I2S completo (32 bits)
    // Sin bit-banging, sin delays, timing perfecto por hardware
    return dci_read();
}

void EscribirDACExterno_DCI(int16 muestraL, int16 muestraR) {
    // Empaquetar muestras en frame I2S de 32 bits
    // Canal L en bits altos, Canal R en bits bajos
    int32 frame_dci = ((int32)muestraL << 16) | (muestraR & 0xFFFF);
    dci_write(frame_dci);
}

// ==========================================
// 8. ADCs INTERNOS (Mejorado)
// ==========================================
void LeerADCsInternos(void) {
    // CORRECCIÓN: Usar read_adc() de CCS en lugar de manipular ADCON directamente
    set_adc_channel(0);
    delay_us(10);
    adcInterno_AN0_VIn = read_adc();
   
    set_adc_channel(1);
    delay_us(10);
    adcInterno_AN1_ACS = read_adc();
   
    set_adc_channel(2);
    delay_us(10);
    adcInterno_AN2_VccP = read_adc();
   
    set_adc_channel(3);
    delay_us(10);
    adcInterno_AN3_VccN = read_adc();
}

// ==========================================
// 9. INICIALIZACIÓN - CORRECCIONES IMPORTANTES
// ==========================================
void InicializarSistemaCalibracion(void) {
    // Configurar ADCs internos
    setup_adc_ports(sAN0 | sAN1 | sAN4 | sAN5, VSS_VDD);
    setup_adc(ADC_CLOCK_DIV_64 | ADC_TAD_MUL_16);
   
    // Configurar LEDs y Buzzer
    bit_clear(TRISB, 5);
    bit_clear(TRISB, 6);
    bit_clear(TRISB, 7);
    bit_clear(TRISB, 8);
    output_low(LED_OK);
    output_low(LED_BUSY);
    output_low(LED_ERROR);
    output_low(BUZZER);

    // CORRECCIÓN CRÍTICA: Configurar módulo DCI para I2S nativo
    setup_dci(DCI_MODE_I2S | DCI_TX_ENABLE | DCI_RX_ENABLE);

    // CORRECCIÓN CRÍTICA: Configurar TRIS correctamente para DCI
    // CSDI (RB10) es ENTRADA al dsPIC desde el ADC
    bit_set(TRISB, 10);  
    // CSDO (RB11) es SALIDA del dsPIC al DAC
    bit_clear(TRISB, 11);
    // CSCK (RB9) y COFS (RB12) son SALIDAS (Master)
    bit_clear(TRISB, 9);
    bit_clear(TRISB, 12);

    // Valores de calibración
    calibActual.factorEscalaVoltaje = 0.0102f;
    calibActual.factorEscalaCorriente = 0.0250f;
    calibActual.offsetDC_ADC = 512.0f;
}

// ==========================================
// 10. RUTINAS DE PRUEBA - CORRECCIÓN UART
// ==========================================
void EjecutarDampingFactor(void) {
    MetricasDamping_t damp;
    unsigned int32 totalMuestras = 512;
    float sumaV = 0.0f, sumaI = 0.0f, sumaVI = 0.0f;
   
    for(unsigned int32 i = 0; i < totalMuestras; i++) {
        // CORRECCIÓN: Usar DCI nativo
        int32 rawAudio = LeerADCExterno_DCI();
        int16 rawV = (int16)(rawAudio >> 16); // Extraer canal L
       
        LeerADCsInternos();
       
        float vMuestra = ((float)rawV) * calibActual.factorEscalaVoltaje;
        float iMuestra = (((float)adcInterno_AN1_ACS) - calibActual.offsetDC_ADC) * calibActual.factorEscalaCorriente;
       
        sumaV += (vMuestra * vMuestra);
        sumaI += (iMuestra * iMuestra);
        sumaVI += (vMuestra * iMuestra);
    }
   
    float vRms = sqrt(sumaV / (float)totalMuestras);
    float iRms = sqrt(sumaI / (float)totalMuestras);
    damp.impedanciaCarga = (iRms > 0.001f) ? (vRms / iRms) : 999.9f;
   
    float zSalidaAmplificador = 0.032f;
    damp.dampingFactor = (zSalidaAmplificador > 0.0f) ? (damp.impedanciaCarga / zSalidaAmplificador) : 0.0f;
    damp.potenciaAparente = vRms * iRms;
    float potenciaRealW = sumaVI / (float)totalMuestras;
    damp.factorPotencia = (damp.potenciaAparente > 0.0f) ? (potenciaRealW / damp.potenciaAparente) : 0.0f;
    if(damp.factorPotencia > 1.0f) damp.factorPotencia = 1.0f;
   
    // CORRECCIÓN CRÍTICA: Enviar como texto ASCII, NO binario
    fprintf(UART_PIC18, "RES:DAMP:%.2f,%.2f,%.2f,%.2f\n",
            damp.dampingFactor, damp.impedanciaCarga, damp.factorPotencia, damp.potenciaAparente);
}

void EjecutarParametrosTS(void) {
    MetricasTS_t ts;
    unsigned int32 totalMuestras = 256;
    float maxImpedancia = 0.0f;
    float freqResonancia = 0.0f;
    float factorDivisorTeorico = 91.0f * calibActual.factorEscalaVoltaje;
    float valorResistenciaRef = 10.0f;
    float frecuenciasPrueba[] = {30.0f, 40.0f, 50.0f, 60.0f, 80.0f, 100.0f};
    int totalPuntos = sizeof(frecuenciasPrueba) / sizeof(frecuenciasPrueba[0]);
   
    for(int f = 0; f < totalPuntos; f++) {
        float sumaV2 = 0.0f, sumaI = 0.0f;
       
        EscribirDACExterno_DCI(0, 16000);
        delay_ms(50);
       
        for(unsigned int32 i = 0; i < totalMuestras; i++) {
            int32 rawAudio = LeerADCExterno_DCI();
            int16 rawV1 = (int16)(rawAudio >> 16); // Canal L
            int16 rawV2 = (int16)(rawAudio & 0xFFFF); // Canal R
           
            float v1 = ((float)rawV1) * factorDivisorTeorico;
            float v2 = ((float)rawV2) * factorDivisorTeorico;
            float corrienteMuestra = (v1 - v2) / valorResistenciaRef;
           
            sumaV2 += (v2 * v2);
            sumaI  += (corrienteMuestra * corrienteMuestra);
        }
        float v2Rms = sqrt(sumaV2 / (float)totalMuestras);
        float iRms  = sqrt(sumaI / (float)totalMuestras);
        float zActual = (iRms > 0.0001f) ? (v2Rms / iRms) : 0.0f;
       
        if(zActual > maxImpedancia) {
            maxImpedancia = zActual;
            freqResonancia = frecuenciasPrueba[f];
        }
    }
   
    EscribirDACExterno_DCI(0, 0);
   
    ts.frecuenciaResonancia = freqResonancia;
    ts.impedanciaMax = maxImpedancia;
   
    // CORRECCIÓN: Valores honestos en lugar de hardcodeados falsos
    ts.qms = 0.0f;
    ts.qes = 0.0f;
    ts.qts = 0.0f;
   
    fprintf(UART_PIC18, "RES:TSPARAMS:%.1f,%.2f,%.2f,%.2f,%.2f\n",
            ts.frecuenciaResonancia, ts.impedanciaMax, ts.qts, ts.qms, ts.qes);
}

void EjecutarSlewRate(void) {
    MetricasSlewRate_t sr;
    unsigned int32 totalMuestras = 512;
   
    // CORRECCIÓN: Este valor AHORA es real gracias al DCI nativo
    float periodoMuestreoUs = 20.833f; // 1/48000 Hz * 1e6
   
    float vMax = -9999.0f, vMin = 9999.0f;
    unsigned int32 indice10 = 0, indice90 = 0;
    int1 enc10 = 0, enc90 = 0;
   
    int16 muestrasBuffer[512];
    for(unsigned int32 i = 0; i < totalMuestras; i++) {
        int32 rawAudio = LeerADCExterno_DCI();
        muestrasBuffer[i] = (int16)(rawAudio >> 16);
        float muestra = ((float)muestrasBuffer[i]) * calibActual.factorEscalaVoltaje;
        if(muestra > vMax) vMax = muestra;
        if(muestra < vMin) vMin = muestra;
    }
   
    sr.voltajePicoMax = vMax;
    float umbral10 = vMin + (vMax - vMin) * 0.10f;
    float umbral90 = vMin + (vMax - vMin) * 0.90f;
   
    for(unsigned int32 i = 0; i < totalMuestras - 1; i++) {
        float m1 = ((float)muestrasBuffer[i]) * calibActual.factorEscalaVoltaje;
        float m2 = ((float)muestrasBuffer[i+1]) * calibActual.factorEscalaVoltaje;
       
        if(!enc10 && m1 <= umbral10 && m2 >= umbral10) {
            indice10 = i;
            enc10 = 1;
        }
        if(enc10 && !enc90 && m1 <= umbral90 && m2 >= umbral90) {
            indice90 = i;
            enc90 = 1;
            break;
        }
    }
   
    if(enc10 && enc90 && (indice90 > indice10)) {
        float deltaV = umbral90 - umbral10;
        float deltaT = (float)(indice90 - indice10) * periodoMuestreoUs;
        sr.slewRateSubida = deltaV / deltaT;
    } else {
        sr.slewRateSubida = 0.0f;
    }
    sr.slewRateBajada = sr.slewRateSubida;
   
    fprintf(UART_PIC18, "RES:SLEW:%.2f,%.2f,%.2f\n",
            sr.slewRateSubida, sr.slewRateBajada, sr.voltajePicoMax);
}

void EjecutarGeneradorFunciones(void) {
    EscribirDACExterno_DCI(16383, 0);
    fprintf(UART_PIC18, "ACK:GENERADOR_ON\n");
}

// ==========================================
// 11. BUCLE PRINCIPAL (Sin cambios - perfecto)
// ==========================================
void main(void) {
    InicializarSistemaCalibracion();
    delay_ms(100);
   
    while(TRUE) {
        if(kbhit(UART_PIC18)) {
            unsigned int8 comando = fgetc(UART_PIC18);
           
            switch(comando) {
                case CMD_SET_MODO_THD:
                case CMD_SET_MODO_BAND:
                case CMD_SET_MODO_ESPECTRO:
                case CMD_SET_MODO_VU:
                case CMD_Damping:
                case CMD_TS_PARAMS:
                case CMD_SLEW_RATE:
                case CMD_GENERADOR:
                    modoSeleccionado = comando;
                    BuzzerUnBip();
                    break;
                   
                case CMD_START:
                    BuzzerUnBip();         
                    output_high(LED_BUSY); 
                    g_sistemaEjecutando = 1;
                   
                    switch(modoSeleccionado) {
                        case CMD_Damping:
                            EjecutarDampingFactor();
                            break;
                        case CMD_TS_PARAMS:
                            EjecutarParametrosTS();
                            break;
                        case CMD_SLEW_RATE:
                            EjecutarSlewRate();
                            break;
                        case CMD_GENERADOR:
                            EjecutarGeneradorFunciones();
                            break;
                        default:
                            break;
                    }
                   
                    output_low(LED_BUSY);
                    BuzzerTresBips();      
                    g_sistemaEjecutando = 0;
                    break;
                   
                case CMD_STOP:
                    BuzzerUnBip();
                    g_sistemaEjecutando = 0;
                    output_low(LED_BUSY);
                    output_high(LED_ERROR);
                    delay_ms(100);
                    output_low(LED_ERROR);
                    EscribirDACExterno_DCI(0, 0);
                    break;
                   
                default:
                    break;
            }
        }
    }
}
 
Última edición:
Atrás
Arriba