Reproducción de sonidos en un AVR MCU [cerrado]

2

Nunca tuve que lidiar con el sonido y estoy bastante seguro de que algunos de ustedes tienen algo de experiencia en ese tipo de aplicación.

Mi pregunta es más sobre cómo obtener algunos consejos sobre las diferentes formas de implementar un sistema integrado basado en un AVR que reproduce sonidos. Más precisamente:

  • ¿Qué biblioteca debo usar?
  • ¿Qué formato de sonido debo elegir? ¿Necesito necesariamente un chip de memoria dedicado a un lado de la MCU? Si es así, ¿cuál recomendaría?
  • ¿Tienes algún ejemplo de código para compartir?

Como siempre, sé que todo depende de la aplicación de destino. Aquí hay algunas precisiones:

  • Quiero hacer un juguete para los hijos de un amigo mío, la idea es tener un sonido corto que no dure más de 30 [s]
  • No estoy interesado en el sonido de alta fidelidad
  • El consumo de energía importa
pregunta Buddyshot

3 respuestas

5

Usé un ATtiny85 para conducir un altavoz a través de un LPF a través de dos salidas PWM complementarias (OC1B / nOC1B) al presionar un botón. Menos de 0.5k de código deja 7.5k para el audio. Los chips más grandes dejan más espacio para el audio o la capacidad de usar SPI real o I 2 C para el almacenamiento. El volumen depende de la tensión de alimentación, pero aún puede obtener un rendimiento decente de una celda de ion de litio medio muerta.

Qué diablos, me siento generoso. Tenga en cuenta que nada de esto intenta ser muy consciente del poder; Consulte la licencia para detalles de modificación. Y no intentes conducir a un demasiado a través de un altavoz. Y ten cuidado con los comentarios de mierda.

/***
 * Copyright © 2014 Ignacio Vazquez-Abrams
 * This work is free. You can redistribute it and/or modify it under the
 * terms of the WTFPL, Version 2,
 * as published by Sam Hocevar. See http://www.wtfpl.net/ for more details.
 */

#define F_CPU 16000000

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <avr/sleep.h>
#include <avr/power.h>

#include "audio8k.h"

// Generate audio using PWM on OC1B/~OC1B H-bridge on ATtinyX5
// This leaves the USI pins available for expansion
// Timer 1 is used to drive PWM, timer 0 is used to load the next
// sample

// References:
// http://playground.arduino.cc/Code/PCMAudio
// http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_binarydata

// play state information
typedef enum {
  STATE_STOPPED,
  STATE_STARTING,
  STATE_PLAYING,
  STATE_STOPPING
} playState;

playState currentState __attribute__ ((section (".noinit")));

// how far into the audio
unsigned long sampleCount __attribute__ ((section (".noinit")));

static void start_playing();
static void ramp_up();
static void play();
static void ramp_down();
static void stop_playing();

void start_playing()
{
  cli();

  // connect OC1B
  GTCCR |= (_BV(PWM1B) | _BV(COM1B0));
  DDRB |= (_BV(PB4) | _BV(PB3));

  // clki/o/8 (2MHz) prescaling for timer 0
  TCCR0B = (_BV(CS01));

  // tune timer 0 to 32kHz
  OCR0A = 63;

  // pck (64MHz, 500kHz PWM) prescaling for timer 1
  TCCR1 = (_BV(CS10));

  // prime the PWM
  OCR1B = 0x00;

  // unprescale clock
  clock_prescale_set(clock_div_1);

  // prepare for liftoff
  sampleCount = 0;
  currentState = STATE_STARTING;

  sei();
}

void ramp_up()
{
  unsigned char firstSample = pgm_read_byte(&audio8k[0]);
  OCR1B += 1;
  if (OCR1B >= firstSample)
  {
    OCR1B = firstSample;
    currentState = STATE_PLAYING;
  }
}

void play()
{
  // audio from PROGMEM
  OCR1B = pgm_read_byte(&audio8k[sampleCount++ >> 2]);
  if ((sampleCount >> 2) >= (audio8k_end - audio8k))
  {
    currentState = STATE_STOPPING;
  }
}

void ramp_down()
{
  if (OCR1B > 1)
  {
    OCR1B -= 1;
  }
  else
  {
    OCR1B = 0;
    stop_playing();
  }
}

void stop_playing()
{
  cli();

  // clki/o/8 (2MHz) prescaling for timer 0
  TCCR0B = (_BV(CS01));

  // tune timer 0 to 7812.5Hz
  OCR0A = 0x00;

  // prescale clock by 256
//XX UNCOMMENT FOR PRODUCTION  clock_prescale_set(clock_div_256);

  // kthxbye
  currentState = STATE_STOPPED;
  sampleCount = 0;

  sei();
}

void setup()
{
  // ssshhhh....
  cli();

  // speaker is connected to pins 3 and 2, PB4/PB3 (OC1B/~OC1B)
  DDRB |= (_BV(PB4) | _BV(PB3));

  // button pullup goes here
  PORTB |= _BV(PB2);

  // use CTC on timer 0 for finer tuning
  TCCR0A = _BV(WGM01);

  // enable the timer 0 output compare A interrupt
  TIMSK |= _BV(OCIE0A);

  // enable async clock (PCK) for timer 1
  PLLCSR |= _BV(PCKE);

  // set sleep mode for later, leave i/o running
  set_sleep_mode(SLEEP_MODE_IDLE);

  // sane defaults
  stop_playing();

  // and... go!
  sei();
}

// state machine dispatch
ISR(TIM0_COMPA_vect)
{
  switch (currentState)
  {
    case STATE_STOPPED:
    {
      if (!(PINB & _BV(PB2)))
      {
        start_playing();
      }

      break;
    }
    case STATE_STARTING:
    {
      ramp_up();

      break;
    }
    case STATE_PLAYING:
    {
      play();

      break;
    }
    case STATE_STOPPING:
    {
      ramp_down();

      break;
    }
  }
}

void loop()
{
// this loop should do nothing; everything is handled by interrupts
  sleep_enable();
  sleep_cpu();
  sleep_disable();
}

int main()
{
  setup();
  while (1)
    loop();
}
    
respondido por el Ignacio Vazquez-Abrams
3
  

¿Qué formato de sonido debo elegir?

El formato de archivo .wav sería más fácil de descodificar.

  

¿Necesito un chip de memoria dedicado a un lado de la MCU? Si es así, ¿cuál recomendaría?

Puede utilizar EEPROM o memoria flash para almacenar los datos. Consulte esta pregunta: ¿Cómo almacenar datos de audio para el proyecto AVR?

  

¿Tienes algún ejemplo de código para compartir?

La búsqueda de avr wav player le dará muchos códigos fuente de muestra. Pero la mayoría de ellos estarán basados en tarjetas SD como esto . Consulte la respuesta de Ignacio Vázquez-Abrams.

    
respondido por el nidhin
3

Echa un vistazo a este proyecto de prueba de concepto mío .

Básicamente está programado con notas musicales en un archivo de texto simple melodies/<melodyFile> . Las notas en este archivo de texto luego se convierten usando un pequeño script de perl ( ./converter ) a números utilizados para generar la frecuencia requerida usando uno de los temporizadores del AVR ( melody.c ). Cuando se programa el controlador, la melodía se incluye con la fuente del reproductor y se muestra en el controlador como un todo.

Aquí hay un video del proyecto , el sonido del controlador no se amplifica un poco.

Ventajas:

  • huella de memoria pequeña
  • código simple
  • no requiere componentes adicionales que no sean un altavoz piezoeléctrico

Desventajas:

  • requiere algo de paciencia para programar una nueva melodía
  • mejores resultados a 8MHz, pero eso puede derivarse del oscilador RC interno

No probé el consumo de energía porque es solo una prueba de concepto, pero hay espacio para mejorar la eficiencia de energía cuando es necesario, al poner el controlador en modo de suspensión mientras espera la próxima "señal". Se debe configurar un segundo temporizador para eso.

La herramienta de conversión está escrita en Perl y, aunque funciona bien, puede hacerlo con más comentarios.

    
respondido por el jippie

Lea otras preguntas en las etiquetas