Как добавить звук в Arduino //0×1

В предыдущей части мы смогли воспроизвести звуковой фрагмент из памяти МК, но полусекундный пшик — это не интересно. Необходима внешняя память. Моим предпочтением будет SD/MMC карта, так как она имеет минимум ограничений и весьма проста и доступна.

Карта памяти

Просто так подключить SD/MMC никак не получится, для этого нам нужен специальный переходник. Также стандартная библиотека SD не имеет достаточной скорости передачи данных, поэтому я ее модифицировал — *ссылка*.

В силу того, что контроллеры уже научились полноценно читать файловую систему карточек, наш звуковой фрагмент(-ы) мы запишем в формате .wav файла с параметрами:

  • Кодек: PCM
  • Частота: 24.5кГц
  • Разрядность: 8-бит
  • Каналы: Моно

Идея

С помощью функции file.read() мы можем читать файл по 8бит. В обработчике прерываний ISR(TIMERx) мы передаем значение из массива в регистр ШИМ.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
ISR(TIMER1_COMPA_vect) {
    if (sample >= data_size) {
        if (sample == data_size + lastSample) {
            stopPlayback();
        }
        else {
            OCR2A = data_size + lastSample - sample;
        }
    }
    else {
        OCR2A = pgm_read_byte(&data[sample]);
    }
    ++sample;
}

Нужно просто заменить чтение массива на чтение из SD/MMC карточки!

1
2
3
4
5
ISR(TIMER1_COMPA_vect) {
 
  OCR2A = file.read();
 
}

Это дешево и сердито. В будущем, я советую вам использовать промежуточный звуковой буфер.

Проигрыватель

Схема подключения будет следующей:

schematicuo7

В программной части нам осталось написать: инициализация периферии, обработка файлов. Чтобы было по-проще, мы не будем привязываться к какому-то конкретному файлу, а будем воспроизводить все, что имеется в коре SD/MMC.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#include <SD.h>
 
File file;
 
ISR(TIMER1_COMPA_vect) {
 
  OCR0A = file.read(); //Записываем в регистр сравнения значение,
  // полученное из файла
 
}
 
void startPlayback(char file_name[13]) {
  pinMode(6, OUTPUT);
 
  file = SD.open(file_name); //Открываем файл
 
  ASSR &= ~(_BV(EXCLK) | _BV(AS2));
  TCCR0A |= _BV(WGM01) | _BV(WGM00);
  TCCR0B &= ~_BV(WGM02);
  TCCR0A = (TCCR0A | _BV(COM0A1)) & ~_BV(COM0A0);
  TCCR0A &= ~(_BV(COM0B1) | _BV(COM0B0));
  TCCR0B = (TCCR0B & ~(_BV(CS12) | _BV(CS11))) | _BV(CS10);
 
  cli();
  TCCR1B = (TCCR1B & ~_BV(WGM13)) | _BV(WGM12);
  TCCR1A = TCCR1A & ~(_BV(WGM11) | _BV(WGM10));
  TCCR1B = (TCCR1B & ~(_BV(CS12) | _BV(CS11))) | _BV(CS10);
  OCR1A = F_CPU / 25000;  
  TIMSK1 |= _BV(OCIE1A);
  sei();
 
//Крутимся в цикле, пока не кончится файл или в ком порту "n".
  while(file.available()) { 
    if (Serial.read()=='n') {
      break;
    }
  }
 
}
 
void stopPlayback() {
  TIMSK1 &= ~_BV(OCIE1A);
  TCCR1B &= ~_BV(CS10);
  TCCR0B &= ~_BV(CS10);
  digitalWrite(6, LOW);
}
 
void setup() {
  Serial.begin(9600);
  SD.begin(10); //Инициализация карты.
} 
 
 
void loop() { 
  File root;
  root = SD.open("/"); //Открываем root директорию
 
//Крутимся в цикле, пока не закончатся файлы
  while(true) {
 
    File entry =  root.openNextFile(); //Открываем первый или следующий файл
    if (! entry) {
      //нет больше фалов
      Serial.println("**nomorefiles**");
      break;
    }
    Serial.println(entry.name());
    startPlayback(entry.name()); //Начинаем проигрывать ? файл
    stopPlayback(); //Выкл таймеры
    file.close(); 
  }
 
}

Код для Arduino Mega будет отличаться:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#include <SD.h>
 
File file;
 
ISR(TIMER1_COMPA_vect) {
 
  OCR0A = file.read(); //Записываем в регистр сравнения значение,
  // полученное из файла
 
}
 
void startPlayback(char file_name[13]) {
  pinMode(10, OUTPUT);
 
  file = SD.open(file_name); //Открываем файл
 
  ASSR &= ~(_BV(EXCLK) | _BV(AS2));
  TCCR2A |= _BV(WGM21) | _BV(WGM20);
  TCCR2B &= ~_BV(WGM22);
  TCCR2A = (TCCR2A | _BV(COM2A1)) & ~_BV(COM2A0);
  TCCR2A &= ~(_BV(COM2B1) | _BV(COM2B0));
  TCCR2B = (TCCR2B & ~(_BV(CS12) | _BV(CS11))) | _BV(CS10);
 
  cli();
  TCCR1B = (TCCR1B & ~_BV(WGM13)) | _BV(WGM12);
  TCCR1A = TCCR1A & ~(_BV(WGM11) | _BV(WGM10));
  TCCR1B = (TCCR1B & ~(_BV(CS12) | _BV(CS11))) | _BV(CS10);
  OCR1A = F_CPU / 25000;    
  TIMSK1 |= _BV(OCIE1A);
  sei();
 
//Крутимся в цикле, пока не кончится файл или в ком порту "n".
  while(file.available()) { 
    if (Serial.read()=='n') {
      break;
    }
  }
 
}
 
void stopPlayback() {
  TIMSK1 &= ~_BV(OCIE1A);
  TCCR1B &= ~_BV(CS10);
  TCCR2B &= ~_BV(CS10);
  digitalWrite(10, LOW);
}
 
void setup() {
  Serial.begin(9600);
  SD.begin(53); //Инициализация карты.
} 
 
 
void loop() { 
  File root;
  root = SD.open("/"); //Открываем root директорию
 
//Крутимся в цикле, пока не закончатся файлы
  while(true) {
 
    File entry =  root.openNextFile(); //Открываем первый или следующий файл
    if (! entry) {
      //нет больше фалов
      Serial.println("**nomorefiles**");
      break;
    }
    Serial.println(entry.name());
    startPlayback(entry.name()); //Начинаем проигрывать ? файл
    stopPlayback(); //Выкл таймеры
    file.close(); 
  }
 
}

Вам лишь остается закинуть как попало ваши wav файлы в корень карты памяти. При отправки символа n в ком-порт произойдет переключение трека.

Из-за секторного чтения SD/MMC происходит периодическая задержка во время запроса данных, что искажает частоту дискретизации. А последствиями такого искажения ничего хорошего не будет.

Записи

PS: В коде для Arduino Uno/168 я использовал не пользовательский таймер (может повлиять на работу других библиотек)


Все части статьи

Вы можите оставить комментарий, или поставить трэкбек со своего сайта.

11 комментариев к “Как добавить звук в Arduino //0×1”

  1. Сергей:

    я в етом ламер но может что нужно поставить побольше конденсатор на динамик ну или, в чем я сомневаюсь увиличи сериал.

  2. Дмитрий:

    Что то у меня не играет, подправил выходы под UNO, в ком-порте появляется название песни, но звука из динамика нет, в чем может быть проблема? ❓

  3. Даниил:

    не могу запустить на Nano, вместо звука треск какойто. CS на 10 пине, динамик на 11

    • Перекиньте CS на 4пин и в скетче SD.begin() подправьте. Попробуйте подключить динамик и к 10, и к 11 пину.
      Карта инициализируется? Попробуйте провести стандартный тест.(естественно, с изменениями, приведенными выше)

  4. Код для ArduinoUno и ее клонов:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    
    #include <SD.h>
    #define SAMPLE_RATE 25000
     
    File file;
    int speakerPin = 6;
     
    //Прерывание, срабатывающее за 1тик 16бит таймера
    ISR(TIMER1_COMPA_vect) {
     
      OCR0A = file.read(); //Записываем в регистр сравнения значение,
      // полученное из файла
     
    }
     
    void startPlayback(char file_name[13])
    {
     
     
      pinMode(speakerPin, OUTPUT);
      file = SD.open(file_name); //Открываем файл
     
     
      //***Настройка ШИМ-а и 16бит таймера***
     
      // Use internal clock (datasheet p.160)
      ASSR &= ~(_BV(EXCLK) | _BV(AS2));
     
      // Set fast PWM mode  (p.157)
      TCCR0A |= _BV(WGM01) | _BV(WGM00);
      TCCR0B &= ~_BV(WGM02);
     
      // Do non-inverting PWM on pin OC2A (p.155)
      // On the Arduino this is pin 11.
      TCCR0A = (TCCR0A | _BV(COM0A1)) & ~_BV(COM0A0);
      TCCR0A &= ~(_BV(COM0B1) | _BV(COM0B0));
     
      // No prescaler (p.158)
      TCCR0B = (TCCR0B & ~(_BV(CS12) | _BV(CS11))) | _BV(CS10);
     
      cli();
     
      // Set CTC mode (Clear Timer on Compare Match) (p.133)
      // Have to set OCR1A *after*, otherwise it gets reset to 0!
      TCCR1B = (TCCR1B & ~_BV(WGM13)) | _BV(WGM12);
      TCCR1A = TCCR1A & ~(_BV(WGM11) | _BV(WGM10));
     
      // No prescaler (p.134)
      TCCR1B = (TCCR1B & ~(_BV(CS12) | _BV(CS11))) | _BV(CS10);
     
      // Set the compare register (OCR1A).
      // OCR1A is a 16-bit register, so we have to do this with
      // interrupts disabled to be safe.
      OCR1A = F_CPU / SAMPLE_RATE;    // 16e6 / 8000 = 2000
     
      // Enable interrupt when TCNT1 == OCR1A (p.136)
      TIMSK1 |= _BV(OCIE1A);
     
      sei();
     
    //Крутимся в цикле, пока не кончится файл или в ком порту "n".
      while(file.available()) { 
        if (Serial.read()=='n') {
          break;
        }
      }
     
    }
     
    void stopPlayback()
    {
      //***Выводим из режима работы все таймеры***
      // Disable playback per-sample interrupt.
      TIMSK1 &= ~_BV(OCIE1A);
     
      // Disable the per-sample timer completely.
      TCCR1B &= ~_BV(CS10);
     
      // Disable the PWM timer.
      TCCR0B &= ~_BV(CS10);
     
      digitalWrite(speakerPin, LOW);
    }
     
    void setup()
    {
      Serial.begin(9600);
      SD.begin(10); //Инициализация карты.
     
    } 
     
     
    void loop()
    { 
      File root;
      root = SD.open("/"); //Открываем root директорию
     
    //Крутимся в цикле, пока не закончатся файлы
      while(true) {
     
        File entry =  root.openNextFile(); //Открываем первый или следующий файл
        if (! entry) {
          //нет больше фалов
          Serial.println("**nomorefiles**");
          break;
        }
        Serial.println(entry.name());
        startPlayback(entry.name()); //Начинаем проигрывать ? файл
        stopPlayback(); //Выкл таймеры
        file.close(); 
      }
     //Если файлов больше нет.
      Serial.println("***//////End of PlayList//////***");
     
     
    }

    CS карточки подключаем к 10-му пину, а звук будет на 6-ом.

Написать комментарий

XHTML: Вы можете использовать эти теги: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Bug Report
Локализовано: шаблоны Wordpress