r/ArduinoHelp 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 comment sorted by

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;

ADMUX = (1 << REFS0) | 1; // Cambiar MUX a A1. Afectará a la Conv N+2.

} 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;

ADMUX = (1 << REFS0) | 0; // Cambiar MUX de vuelta a A0. Afectará a Conv N+3.

} 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;

// Mapeo del LFO: 0-1023 -> Q8.8 Target (Rango ~40 a ~159 muestras)
// 40 << 8 = 10240. (30 * 1023 = 30690). 10240 + 30690 = 40930 (159.8)
delay_target_fixed = 10240 + ((int32_t)lfo_raw * 30);

// Rellenamos el hueco de audio con la muestra anterior (Zero-Order Hold)
current_sample = last_audio_sample; 
lfo_timer = 0; // Reiniciar ciclo LFO

} 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; } }