В предыдущей части мы смогли воспроизвести звуковой фрагмент из памяти МК, но полусекундный пшик — это не интересно. Необходима внешняя память. Моим предпочтением будет 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(); } |
Это дешево и сердито. В будущем, я советую вам использовать промежуточный звуковой буфер.
Проигрыватель
Схема подключения будет следующей:
В программной части нам осталось написать: инициализация периферии, обработка файлов. Чтобы было по-проще, мы не будем привязываться к какому-то конкретному файлу, а будем воспроизводить все, что имеется в коре 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 я использовал не пользовательский таймер (может повлиять на работу других библиотек)
я в етом ламер но может что нужно поставить побольше конденсатор на динамик ну или, в чем я сомневаюсь увиличи сериал.
Проблема частично решилась. Нужно поставить на выход RC цепочку из этого поста.
Что то у меня не играет, подправил выходы под UNO, в ком-порте появляется название песни, но звука из динамика нет, в чем может быть проблема? ❓
Что конкретно вы подправили?
Нужно лишь изменить SD.begin(10);, так как SS(CS) пин у ArduinoUno — 10; также speakerPin поменять на 11. Подключение динамика и карты памяти — соответствующее.
Как можно поменять speakerpin на 11, если на 11 висит DI карточки?
Код обновлен.
не могу запустить на Nano, вместо звука треск какойто. CS на 10 пине, динамик на 11
Перекиньте CS на 4пин и в скетче SD.begin() подправьте. Попробуйте подключить динамик и к 10, и к 11 пину.
Карта инициализируется? Попробуйте провести стандартный тест.(естественно, с изменениями, приведенными выше)
карта инициализируется, ардуинка в консоль пишет треки, реагирует на команду n. тест работает, вариант музыки без флешки выдает звук.
Код обновлен.
Код для ArduinoUno и ее клонов:
CS карточки подключаем к 10-му пину, а звук будет на 6-ом.
А как можно переделать скетч так, чтобы использовать не ШИМ а r2r ЦАП, и задействовать для этого целый порт?
Никаких проблем
ISR(TIMER1_COMPA_vect) {
PORTX = file.read(); //Записываем в регистр сравнения значение,
// полученное из файла
}