Sistema PCM entre ESP32
1. Descripción de bloquesDiagram
uint16_t).Nota: Este código es simplemente ilustrativo y puede requerir ajustes a la librería o a la implementación concreta de mu-law. Las funciones de compresión y la tabla deben crearse según la curva μ-law.
Cpp
/* Ejemplo Transmisor PCM con μ-law (ESP32) */ #include <driver/adc.h> #include <HardwareSerial.h> #define FS 16000 // Frecuencia de muestreo deseada #define SYNC_PIN 4 // Pin de sincronización (salida) #define ADC_CHANNEL ADC1_CHANNEL_0 // GPIO36 en algunos ESP32 HardwareSerial SerialPCM(2); // UART2 // Tabla de compresión mu-law (256 valores) uint8_t muLawCompressTable[4096]; // Ajustar según max. 12 bits si se desea // (Se genera offline o con una rutina setupMukawTables()) hw_timer_t *timer = NULL; // Rutina de interrupción de muestreo void IRAM_ATTR onTimer() { // 1. Leer ADC (12 bits) int raw = adc1_get_raw(ADC_CHANNEL); // 0-4095 (aprox. con atenuación=0) // 2. Comprimir a 8 bits mu-law // Suponiendo que "raw" se normaliza de 0 a 4095, se centra en +/-2048, etc. // Ejemplo pseudo-línea: uint8_t muValue = muLawCompressTable[ raw & 0xFFF ]; // 3. Generar pulso de sincronización digitalWrite(SYNC_PIN, HIGH); // 4. Enviar el byte por UART SerialPCM.write(muValue); // 5. Bajar pulso digitalWrite(SYNC_PIN, LOW); } void setup() { // Config ADC adc1_config_width(ADC_WIDTH_BIT_12); adc1_config_channel_atten(ADC_CHANNEL, ADC_ATTEN_DB_0); // Config pin de sincronización pinMode(SYNC_PIN, OUTPUT); digitalWrite(SYNC_PIN, LOW); // Config UART SerialPCM.begin(250000, SERIAL_8N1, -1, -1, false); // TX default pin GPIO17 // (Inicializa tabla muLawCompressTable) => setupMuLawTables(); // Config timer a FS = 16 kHz timer = timerBegin(0, 80, true); // A 80 MHz / 80 = 1 MHz -> se cuentan 1 microseg = 1 tick // Queremos 16000 Hz => 1/16000 = 62.5us, => 62.5 ticks timerAttachInterrupt(timer, &onTimer, true); timerAlarmWrite(timer, 62, true); timerAlarmEnable(timer); } void loop() { // Hacer otras tareas }
muLawCompressTable[] para obtener el valor 8 bits.Cpp
/* Ejemplo Receptor PCM con μ-law (ESP32) */ #include <driver/dac.h> #include <HardwareSerial.h> #define FS 16000 #define SYNC_PIN 5 // Pin de entrada de sincronización #define DAC_CHANNEL DAC_CHANNEL_1 // DAC: GPIO25 en ESP32 HardwareSerial SerialPCM(2); // UART2 hw_timer_t *timer = NULL; volatile uint8_t lastSample = 0; // Tabla inversa (para 8 bits a 12 o 16 bits) – de preferencia 256 valores uint16_t muLawDecompressTable[256]; void IRAM_ATTR onSync() { // Leer el byte disponible en UART (blocking o buffer) if (SerialPCM.available()) { lastSample = SerialPCM.read(); } // Alternativa: se podría alinear la interrupción con un timer si queremos exactitud // de muestreo local. Depende del diseño final. } void IRAM_ATTR onTimer() { // Reconstruir muestra 12 bits uint16_t expanded = muLawDecompressTable[lastSample]; // 0..4095 (~12 bits) // Reducir a 8 bits para DAC uint8_t dacValue = expanded >> 4; // descartar 4 bits dacWrite(DAC_CHANNEL_1, dacValue); } void setup() { // Config DAC dacOutputEnable(DAC_CHANNEL_1); // Config UART SerialPCM.begin(250000, SERIAL_8N1, -1, -1, true); // "true" => Invertir pines si es necesario. Ajustar según tu ESP32. // Config pin de sincronización pinMode(SYNC_PIN, INPUT_PULLDOWN); attachInterrupt(digitalPinToInterrupt(SYNC_PIN), onSync, RISING); // (Inicializa tabla muLawDecompressTable) // Config timer // Nota: si se quiere un playback sin dependencia exacta del pin SYNC, // se programa el timer a 16 kHz y en la callback onTimer() se lee "lastSample". // O se puede directamente emplear la interrupción onSync para doEverything. timer = timerBegin(1, 80, true); timerAttachInterrupt(timer, &onTimer, true); timerAlarmWrite(timer, 62, true); timerAlarmEnable(timer); } void loop() { // Otras tareas }
lastSample se actualiza de forma asíncrona.onTimer() genera la salida de audio al DAC con la última muestra recibida.Table
| Parámetro | Valor/Recomendación | Observaciones |
|---|---|---|
| Frecuencia de Muestreo (fs) | 16 kHz | Asegura ancho de banda hasta ~8 kHz (sobrado para voz a 4 kHz). |
| Atenuación ADC ESP32 | 0 dB (por defecto) | Ajustar si la señal de entrada excede 3.3V. |
| Bits finales (compresión) | 8 | μ-law. |
| μ (Factor de compresión) | 128 | Ajustable: 255 suele ser tambien frecuente. |
| Baud Rate UART | 250 kBaud (mín) | Ajustar para no superar el tiempo de muestreo. |
| Filtro Antialias (RC) | ~5 kHz | Depende de la señal de entrada. |
| Filtro Reconstrucción (RC) | ~8-10 kHz | Para suavizar la salida a 8 bits. |
| Pin SYNC adicional | 1 señal digital | Alinea muestras; se puede omitir con buffers FIFO. |
| Amplificador de salida | Transistor push-pull o Amp audio bajo consumo | Para parlante de 5 Ω. |
johanrro
copilot
-
Inputs and Outputs:
- The ALU takes two N-bit wide inputs, A and B, and a 3-bit control input to determine the operation to perform.
- It produces an N-bit wide result and a carry/borrow flag for arithmetic operations (add/subtract).
-
Operation Modules:
- The add_sub module performs addition or subtraction between A and B based on the least significant bit of the control signal,
- Logical operations include AND, OR, and XOR, handled by corresponding modules.
- The not_nbits module complements the bits of A.
- Shift operations (left and right) manipulate the bit positions of A.
-
Multiplexer (MUX):
- A multiplexer selects which operation's result to output based on the control signal. Each control pattern corresponds to a different operation:
-
Carry/Borrow:
- The carry/borrow is only relevant for addition or subtraction, so it is passed through only when the control is for the sum operation (
Design and Implementation Considerationscontrol[0].3'b000: Addition or Subtraction3'b001: AND3'b010: OR3'b011: XOR3'b100: NOT3'b101: Shift Left3'b110: Shift Right3'b000).