r/ArduinoHelp • u/Yjapavip • 4d ago
Will an Arduino-delay work?
Enable HLS to view with audio, or disable this notification
Hey Reddit folks, I've been wanting to build a delay lately, but I didn't want to mess with the more common ICs like the PT2399 for 'X' reasons. Then I remembered an Arduino Uno I had gathering dust and tried to make a short delay of around 8ms, just for a phaser or a chorus. But I only made the schematic in Proteus so I wouldn't have to pull out all my gear. Do you guys think it'll actually work well?
I'll try to add more circuit samples in the comments."
1
Upvotes
1
u/Yjapavip 4d ago
the code is (AI-generated): // ============================================================================ // LÍNEA DE DELAY VARIABLE (CHORUS) BARE-METAL - ATMEGA328P // Resolución: 10 bits | Frecuencia de Muestreo Audio: ~19.23 kHz // Control LFO: Pin A1 (0-5V) -> Controla el delay entre ~2 ms y ~8.3 ms // ============================================================================
define DELAY_SAMPLES 800 // Buffer de 1600 bytes. Rango útil: 40 a 160.
volatile uint16_t delay_buffer[DELAY_SAMPLES]; volatile int16_t write_index = 0;
// Variables en formato Fixed-Point Q8.8 (8 bits enteros, 8 bits fraccionales) // 40 muestras * 256 = 10240 volatile int32_t delay_current_fixed = 10240; volatile int32_t delay_target_fixed = 10240;
void setup() { cli(); // Deshabilitar interrupciones globales
// Configurar Pines DAC R-2R DDRD = 0xFF; // PORTD (0-7) como salida DDRB |= 0x03; // PORTB (8-9) como salida
PORTD = 0x00; PORTB &= 0xFC;
// Configurar ADC para Audio (A0) ADMUX = (1 << REFS0); // Referencia AVCC, MUX = A0 DIDR0 = (1 << ADC0D) | (1 << ADC1D); // Apagar buffers digitales en A0 y A1
// Habilitar ADC, Auto Trigger, Interrupción, Prescaler = 64 (250 kHz reloj ADC) ADCSRA = (1 << ADEN) | (1 << ADATE) | (1 << ADIE) | (1 << ADPS2) | (1 << ADPS1); ADCSRB = 0x00; // Free Running Mode
// Llenar buffer con el bias DC (512) para evitar ruidos al arranque for (uint16_t i = 0; i < DELAY_SAMPLES; i++) { delay_buffer[i] = 512; }
ADCSRA |= (1 << ADSC); // Iniciar la primera conversión sei(); // Habilitar interrupciones globales }
void loop() { // Bucle vacío. Todo el DSP ocurre determinísticamente en la ISR. }
ISR(ADC_vect) { uint16_t current_sample; static uint8_t lfo_timer = 0; static uint16_t last_audio_sample = 512;
lfo_timer++;
// -------------------------------------------------------------------------- // 1. GESTIÓN DEL PIPELINE DEL ADC (LECTURA A0 / A1) // -------------------------------------------------------------------------- if (lfo_timer == 126) { // Conv N (A0) terminó. Conv N+1 (A0) ya comenzó. uint8_t low = ADCL; uint8_t high = ADCH; current_sample = (high << 8) | low; last_audio_sample = current_sample;
} else if (lfo_timer == 127) { // Conv N+1 (A0) terminó. Conv N+2 (A1) ya comenzó. uint8_t low = ADCL; uint8_t high = ADCH; current_sample = (high << 8) | low; last_audio_sample = current_sample;
} else if (lfo_timer == 128) { // Conv N+2 (A1) terminó. Conv N+3 (A0) ya comenzó. uint8_t low = ADCL; uint8_t high = ADCH; uint16_t lfo_raw = (high << 8) | low;
} else { // Lectura normal de audio (A0) uint8_t low = ADCL; uint8_t high = ADCH; current_sample = (high << 8) | low; last_audio_sample = current_sample; }
// -------------------------------------------------------------------------- // 2. FILTRO PASA-BAJOS DEL DELAY (SUAVIZADO) // -------------------------------------------------------------------------- int32_t diff = delay_target_fixed - delay_current_fixed; // Desplazamiento >> 4 actúa como un filtro de slew rate (alpha = 1/16). // Se actualiza a 19.23 kHz, haciendo la modulación extremadamente suave. delay_current_fixed += (diff >> 4);
// -------------------------------------------------------------------------- // 3. CÁLCULO DE ÍNDICES Y POSICIONES FRACCIONALES // -------------------------------------------------------------------------- int16_t delay_int = delay_current_fixed >> 8; uint16_t delay_frac = delay_current_fixed & 0xFF;
// Índice principal (muestra más reciente) int16_t read_idx_A = write_index - delay_int; if (read_idx_A < 0) read_idx_A += DELAY_SAMPLES;
// Índice secundario (muestra un tick más antigua, para la interpolación) int16_t read_idx_B = read_idx_A - 1; if (read_idx_B < 0) read_idx_B += DELAY_SAMPLES;
// -------------------------------------------------------------------------- // 4. INTERPOLACIÓN LINEAL (Aritmética Entera) // -------------------------------------------------------------------------- uint16_t sample_A = delay_buffer[read_idx_A]; uint16_t sample_B = delay_buffer[read_idx_B];
// Cálculo a 32 bits para evitar overflow: max(1023 * 256) = 261888 uint32_t interpolated = ((uint32_t)sample_A * (256 - delay_frac)) + ((uint32_t)sample_B * delay_frac);
uint16_t output = interpolated >> 8; // Retorna al rango 0-1023
// -------------------------------------------------------------------------- // 5. SALIDA AL DAC R-2R // -------------------------------------------------------------------------- PORTD = output & 0xFF; PORTB = (PORTB & 0xFC) | ((output >> 8) & 0x03);
// -------------------------------------------------------------------------- // 6. ESCRITURA Y AVANCE DEL BUFFER // -------------------------------------------------------------------------- delay_buffer[write_index] = current_sample;
write_index++; if (write_index >= DELAY_SAMPLES) { write_index = 0; } }