Мелодия для флопотрона без оркестра

Как заставить дисководы петь

Когда-то жило беспечальное юное поколение, которое выбрало DOS Navigator, научилось форматировать дискеты в нестандартный объем 1,66 мегабайта и задорно валить плюющихся плазменными шарами какодемонов. Но канули в небытие Sound Blaster 64, 3dfx Voodoo2 и гигантские жесткие диски объемом 40 мегабайт. Остались только горы флоппи-дисководов, которые по старой памяти еще долго ставились на компьютеры. Хотя уже и были никому не нужны. Их время вернулось в 2015 году, когда поляк Павел Задрожняк собрал первый полифонический флопотрон. Совместно с инженерами «Авито» редактор N + 1 Василий Сычёв разобрался, как устроен и работает этот «оркестр», и собрал свой собственный флопотрон.

FDD

Первые флоппи-дисководы появились в середине 1970-х годов. Это были колоссальных размеров (по современным меркам) устройства, призванные считывать или записывать информацию на гибкие магнитные диски. От этих дисков устройства и получили свое название: Floppy Disk Drive (дисковод гибких дисков), или FDD. Первые дисководы принимали дискеты форм-фактора 8 дюймов. Позднее были разработаны дискеты 5,25, 3, 3,5, 2 и 2,5 дюйма. В конце 1980-х и в 1990-х годах самыми распространенными были уже 3,5-дюймовые дискеты.

Это были пластиковые конверты с металлической сдвижной защитой, которая прикрывала магнитный пленочный диск. На него и записывалась информация. В разные годы 3,5-дюймовые дискеты вмещали 360, 720, 1440 и 2880 килобайт информации. На закате магнитных дискет самыми распространенными были дискеты объемом 1,44 мегабайта. На них информация записывалась на 80 дорожках, нулевая из которых (программисты отсчет ведут с нуля) располагалась с краю, а 79-я — почти в центре.

В 1991 году компания Ritlabs выпустила файловый менеджер для операционной системы DOS, в который была встроена утилита форматирования с расширенными настройками. Она позволяла отформатировать любую стандартную 3,5-дюймовую дискету таким образом, что на ней информация записывалась уже на 83 дорожки. Объем дискеты увеличивался с 1,44 до 1,66 мегабайта. Правда, не все дисководы имели достаточную точность, чтобы записывать данные с такой плотностью — нередко на дискетах после записи появлялись так называемые «плохие сектора», участки с нечитаемыми данными.

Дисководы для 3,5-дюймовых дискет имели четырехконтактный разъем для подключения питания (первые устройства требовали питания от 5 и 12 вольт одновременно, но поздние модели уже питали от 5 вольт) и 34-контактный разъем с линиями данных и управления. Первые модели дисководов также имели несколько контактов, замыкая которые можно было назначить устройству одну из четырех системных букв — A, B, D или E. Поздние устройства таких контактов уже лишились. По умолчанию они были настроены на букву B, под которой и определялись в операционных системах.

При этом принудительное назначение буквы производилось уже заземлением одного из четырех управляющих контактов в 34-контактном разъеме. По этой причине шлейфы для подключения дисководов имели группу из нескольких повернутых на 180 градусов проводков, идущих от одного разъема к другому. Таким образом, если дисковод с по умолчанию назначенной ему буквой B подключался к разъему первого устройства на шлейфе, то на землю замыкался соответствующий контакт, и дисководу присваивалась буква A.

Во время работы — при записи или чтении — флоппи-дисководы производили характерные гудение, жужжание и щелканье. Эти звуки издавали два типа электромоторчиков: один — шаговый — отвечал за перемещение каретки с двумя магнитными головками (они-то и намагничивали напыление на гибком диске при записи или измеряли изменение магнитного поля при чтении), а второй — бесколлекторный — за вращение самого гибкого диска дискеты. При использовании одновременно нескольких дисководов шум стоял запредельный.

Железо

Надо признать, что еще до появления знаменитого флопотрона Павла Задрожняка (устройства воспроизведения музыки с помощью 64 флоппи-дисководов, восьми жестких дисков и двух планшетных сканеров), энтузиасты уже использовали дисководы для проигрывания простых мелодий. Благодаря дешевым микроконтроллерным платам вроде Arduino или Raspberry Pi, появившимся в 2010-х годах, решить эту задачу стало в целом несложно: требовались лишь умения программировать и паять, а также усидчивость. Вооружившись этими навыками, я приступил к сборке собственного флопотрона.

Для создания музыкального устройства я приобрел на «Авито» четыре флоппи-дисковода для 3,5-дюймовых дискет, компьютерный блок питания стандарта ATX, микроконтроллерную плату Arduino UNO и неработающий жесткий диск Seagate на 320 гигабайт. Первоочередной задачей, которую мне предстояло решить, было создание аппаратной основы будущего флопотрона. Для этого пришлось найти схемы распиновки 34-контактного разъема дисковода и 24-контактного разъема блока питания ATX.

От разъема блока питания много не требовалось: контакт с номером 16 (единственный зеленый провод) и любой «земляной» контакт (черный провод), например, с номером 17. Если их замкнуть друг с другом, то компьютерный блок питания включится. Если это очень хороший блок питания, то он не включится до тех пор, пока не будет подключен к какому-либо потребителю. Хотя бы к дисководу.

С самими дисководами история посложнее. К разъемам питания от блока ATX нужно было подвести 5 вольт и «землю» — проводки красного и черного цветов соответственно. На разъеме питания дисковода средние два контакта — «земля», крайний правый — 5 вольт. В 34-контактном разъеме нижний ряд штырьков (в некоторых версиях дисковода в нижнем ряду всего один штырек) — «земля», он имеет нечетную нумерацию. Верхний ряд с четными номерами — управляющие контакты, заземление которых включает запись, чтение, определяет букву дисковода, управляет движением каретки с головками.

Для создания флопотрона наибольший интерес представляют контакты 12, 16 и 18 — активация дисковода (при заземлении контакта начинает светиться зеленый светодиод), включение шагового двигателя перемещения каретки и направление вращения шагового двигателя соответственно. 12 контакт можно соединить с контактом под ним. Тогда дисковод будет постоянно активирован, а его светодиод будет постоянно гореть. Можно соединить 12-й контакт с 16-м. В этом случае светодиод будет загораться и гаснуть в зависимости от заземления 16-го контакта. Собственно, этих знаний уже достаточно для проверки дисковода: включаем блок питания и замыкаем 12 и 16 контакты на корпус устройства — каретка с магнитными головками должна начать перемещаться с характерным гулом.

Дальнейшая схема проста: 16-е контакты дисководов я подключил к четным контактам Arduino UNO, а 18-е — к нечетным. Настало время написать простейшую программу для проверки получившейся конструкции: под ее управлением Arduino UNO будет у каждого дисковода по очереди сначала двигать каретку к центру, а затем обратно к краю. К слову, на многих интернет-ресурсах пишут, что шаговый двигатель каретки способен делать лишь 80 шагов (коротких вращений вала) по числу дорожек на гибком диске. На самом деле шаговый мотор может делать 160 шагов — это необходимо специально для постройки головки под каждую из записанных на диске 80 дорожек.

Программа для Arduino UNO (компиляция кода и его загрузка в микроконтроллерную плату производятся с помощью среды разработки Arduino IDE) выглядит следующим образом:

int startPin = 2,
    endPin = 10,
    directionPin = 3,
    lastDirectPin = 9;
void setup() {
  int pinByPin = startPin;
  while (pinByPin <= endPin) {
    pinMode(pinByPin, OUTPUT);
    pinByPin++;
  }
}
void loop() {
  for (int j = 0; j < endPin; j = j + 2) {
    for (int i = startPin; i < 160; i++) {
      digitalWrite (j, !digitalRead(j));
      delay(2);
    }    
  }
  for (int i = directionPin; i <= lastDirectPin; i = i + 2) {
    digitalWrite(i, !digitalRead(i));
  }
}

В этой программе я сначала объявил целочисленные переменные для первых и последних цифровых нумерованных контактов Arduino UNO, к которым были подключены контакты шага и направления вращения разъемов дисководов. В процедуре setup () (в среде Arduino функции, объявляемые директивой void, принято называть функциями, однако, коль скоро они ничего не возвращают — например, результат сложения 2 и 2, их правильнее было бы называть процедурами) в теле цикла while производится последовательное переключение всех используемых для управления дисководами контактов в режим выхода — OUTPUT.

Режим выхода означает, что конкретно эти цифровые выходы микроконтроллера Arduino UNO будут использоваться для управления: при высоком уровне (логическая 1) на контакты будут подаваться 5 вольт, а при низком (логический 0) — они будут переключаться на землю.

Процедура loop () в Arduino означает практически непрерывный системный цикл. Если код в процедуре setup () выполняется лишь единожды при включении микроконтроллерной платы, то код в процедуре loop () исполняется по кругу от первой до последней строки снова и снова. В теле цикла loop () я поместил вложенные один в другой два цикла for. Первый цикл с переменной j определяет, каким именно дисководом управляет Arduino UNO, а второй с переменной i — перемещает каретку задействованного дисковода на 160 шагов. В том же теле loop () после вложенных циклов идет еще один цикл for. Его задача проста — переключать направление вращения шагового двигателя дисковода.

Музицируем

Ура. Все заработало. Настало время сыграть на флопотроне какой-нибудь простой мотивчик. Для этого я выбрал заглавную мелодию из «Собаки Баскервилей», двух серий советского сериала «Приключения Шерлока Холмса и доктора Ватсона», снятого в 1980-х годах. Эта мелодия крайне проста (состоит всего из 72 нот), и ее просто закольцевать (музыка будет вечной, если я не переключу рубильник).

При написании «программы Баскервилей» нужно было учесть несколько моментов. Во-первых, нужно помнить, что шаговый двигатель может перемещать каретку лишь на 160 шагов в одном направлении, а затем в другом. Таким образом, при проигрывании мелодии рано или поздно наступил бы момент, когда каретка дошла до предела.

Чтобы она пошла обратно, необходимо было бы написать программный счетчик шагов, который при достижении значения 159 (помним, что счет ведется с 0) переключал бы шаговый двигатель в обратное направление. Но в этом случае программа стала бы существенно сложнее, чего не хотелось бы. Эту проблему я решил довольно просто: при проигрывании мелодии шаговый двигатель как бы топчется на месте — шаг вперед, шаг назад.

Во-вторых, нужно понимать, что шаговые двигатели изначально не созданы для воспроизведения музыки, поэтому им доступен ограниченный набор нот. Ноты, выходящие за возможности двигателей, просто будут не слышны — каретка сдвинется, а звука почти не будет. Для решения этой проблемы пришлось провести ряд экспериментов. В итоге частота шагания двигателя представляет собой результат математических действий — деление подобранного опытным путем числа на частоту определенной ноты с делением же результата на два.

Наконец, в-третьих, при крайних положениях каретки (в центре или с краю) она будет биться об ограничители, создавая неприятный дребезг. Эту проблему можно было бы решить задействовав еще один контакт дисковода — 26-й. Он называется Track00. Если в дисковод вставлена дискета, то на этом контакте формируется напряжение 5 вольт. Если каретка с магнитными головками приезжает к нулевой дорожке гибкого диска, она закрывает оптический датчик, в результате чего линия Track00 разрывается и на контакте напряжение пропадает.

В программе можно было бы написать процедуру, которая бы после включения Arduino отгоняла каретку к краю, а затем переводила ее в середину, то есть на 80 шагов вперед. Но мне повезло — два из четырех купленных мной дисковода автоматически перемещали каретку к краю при включении. Мне оставалось только написать цикл for, с помощью которого каретка при включении флопотрона перемещается на 80 шагов вперед, где уже «топчется» все время исполнения мелодии.

Код программы получился такой:

#define step 8
#define direction 9
#define A2  110
#define D3  147
#define E3  165
#define F3  175
#define G3  196
#define A3  220
#define B3  247
#define D4  294
int note[] = { A2, D3, D3, D3, A2,
               G3, G3, G3, A2, F3,
               F3, F3, E3, D3, E3,
               F3, A2, E3, A2, E3,
               E3, E3, A2, A3, A3,
               A3, A2, G3, G3, G3,
               F3, E3, F3, G3, A2,
               F3, A2, E3, E3, E3,
               A2, B3, B3, B3, A2,
               G3, G3, G3, F3, E3,
               F3, G3, A2, F3, A2,
               E3, E3, E3, A2, D4,
               D4, D4, A2, A3, A3,
               A3, G3, F3, G3, A3,
               A2, F3 };
int duration[] = { 8, 8, 8, 8, 8,
                   8, 8, 8, 8, 8,
                   8, 16, 16, 16, 16,
                   8, 8, 8, 8, 8,
                   8, 8, 8, 8, 8,
                   8, 8, 8, 8, 16,
                   16, 16, 16, 8, 8,
                   8, 8, 8, 8, 8,
                   8, 8, 8, 8, 8,
                   8, 8, 16, 16, 16,
                   16, 8, 8, 8, 8,
                   8, 8, 8, 8, 8,
                   8, 8, 8, 8, 8,
                   16, 16, 16, 16, 8,
                   8, 8 };
void setup () {
  pinMode (direction, OUTPUT);
  pinMode (step, OUTPUT);
  delay (500);
  for (int i = 0; i < 80; i++) {
    digitalWrite (step, !digitalRead(step));
    delay(2);
  }
}
void loop () {
  for (int i = 0; i < 73; i++) {
    playNote (note[i], duration[i]);
  }
}
void playNote (float freq, float duration) {
  int tempo = 5;
  float val = (925000 / freq) / 2;
  float noteDuration = 2200 / duration;
  float time = millis () + noteDuration;
  while (millis () < time) {
    digitalWrite (direction, LOW);
    digitalWrite (step, LOW);
    delayMicroseconds (val);
    digitalWrite (step, HIGH);
    delayMicroseconds (val);
    digitalWrite (direction, HIGH);
    digitalWrite (step, LOW);
    delayMicroseconds (val);
    digitalWrite (step, HIGH);
    delayMicroseconds (val);
  }
  delay (tempo);
}

В ней я сначала директивой #define определяю константы, указывающие контакты Arduino UNO, через которые будет осуществляться управление дисководом. Затем с помощью той же директивы я указываю константы нот, которые используются в мелодии. Сама мелодия записана в двух массивах: note[] и duration[]. Первый массив хранит ноты, а второй — их длительность (для размера 4/4).

В процедуре setup () я перевожу управляющие контакты в режим выхода, приостанавливаю исполнение кода на 500 миллисекунд (использую функцию delay () со значением 500, чтобы каретка дисковода успела «запарковаться» в крайнем положении), а затем с помощью цикла for отвожу каретку на 80 шагов вперед, к центру. В процедуре loop () в теле цикла for, отрабатывающего 72 итерации по числу нот в мелодии, на каждом повторе вызываю процедуру noteDuration (), которая и отвечает за проигрывание мелодии. Процедуре передаются два параметра из массивов note[] и duration[] — частота ноты, записанная в соответствующей константе, и ее длительность.

В самой процедуре noteDuration () вычисляется частота «топтания» каретки, длительность ноты в миллисекундах (сколько именно по времени с заданной частотой каретке нужно делать шаги вперед-назад). В процедуре объявлен цикл while, в теле которого и записан код, управляющий движением каретки и направлением вращения шагового двигателя. Один полный цикл while — одна нота. После выхода из цикла программа приостанавливается на пять миллисекунд — это пауза между нотами. Вот так все просто.

Флопотрон

Эту простенькую программу можно немного расширить, чтобы она проигрывала мелодию на нескольких дисководах. При этом на простом процедурном уровне добиться полифонии не получится. Дело в том, что Arduino исполняет код построчно, причем в один момент времени выполняется только одна команда. Соответственно, если мы говорим, что нужно переместить каретку первого дисковода, одновременно переместить каретку второго уже не получится. Это ограничение можно обойти, если опуститься на так называемый низкий уровень — Arduino позволяет управлять состоянием выводов не только с помощью директив и команд, но и напрямую.

Прямое управление выводами микроконтроллерной платы реализовано с помощью регистров портов. Например, с помощью регистра DDRD = B10101000 цифровые порты Arduino с номерами 7, 5 и 3 (нумерация в регистре по убыванию слева направо) будут одновременно переведены в режим выхода (OUTPUT), а 6, 4, 2, 1 и 0 — в режим входа (INPUT). Затем с помощью регистра портов PORTD = B10101000 порты Arduino с номерами 7, 5 и 3 будут переведены в высокий уровень, то есть на них появится логическая 1 (напряжение 5 вольт). Всего у Arduino есть три порта: B, C и D. Первый управляет цифровыми контактами с номерами 8-14, второй — аналоговыми контактами, а третий — цифровыми с номерами 0-7.

Использование регистров значительно усложнит код программы и сделает его более длинным. Кроме того, полноценной полифонии все равно не получится. Для относительного упрощения кода и создания полноценной полифонии (с помощью Arduino UNO можно управлять самое большее девятью дисководами) можно использовать одну из библиотек управления встроенными в чипы семейства ATmega (на них выполнены микроконтроллерные платы Arduino; Arduino UNO построена на базе ATmega328) таймерами.

Именно по пути использования библиотеки управления таймерами — TimerOne — пошел программист под псевдонимом SammyIAm. Он разработал программное обеспечение Moppy (Musical floppy), которое состоит из двух частей. Первая часть — программа для Arduino UNO (есть также версия на Java, Python и для Raspberry Pi). Эта программа управляет портами микроконтроллерной платы, играя музыку на дисководах. Вторая часть — программа для компьютера, которая связывается с одной или несколькими платами Arduino UNO и передает им данные о нотах, полученные из файла формата MIDI.

В самом midi-файле могут содержаться одна или несколько дорожек с записям нот для разных музыкальных инструментов. Каждая из эти дорожек имеет свой номер канала для проигрывания. Именно основываясь на этом номере Moppy и распределяет проигрываемые ноты по дисководам. Таким образом ноты из канала 1 будет играть дисковод с номером 1, из канала 2 — с номером 2 и так далее. При этом в Moppy, в отличие от моей простенькой программы, ведется подсчет шагов шаговых двигателей, благодаря чему каретки ездят вперед и назад при проигрывании музыки.

Moppy также позволяет управлять дисководами через сдвиговые регистры или драйверы шаговых двигателей. Первое устройство позволяет с помощью небольшого числа портов Arduino управлять большим количеством дисководов, а вторые — напрямую шаговыми двигателями без использования элементов управления на платах устройств. Например, в флопотроне Павла Задрожняка управление сканерами выполнено с помощью драйверов шаговых двигателей, а считывающими головками жестких дисков — через транзисторные сборки.

В общем, я построил свой полифонический флопотрон из четырех дисководов с использованием программного обеспечения Moppy. Изначально планировалось, что жесткий диск будет выступать в роли барабана, выстукивая ритм. Однако мощности собственных портов Arduino UNO не хватило, чтобы раскачать звуковую катушку головки жесткого диска. Соответственно пришлось бы применить вторую плату Arduino UNO и драйвер шаговых двигателей, что усложнило бы проект. Одна Arduino UNO с Moppy не может одновременно управлять дисководами и драйверами шаговых двигателей. Да и как-то непрактично задействовать вторую микроконтроллерную плату для всего лишь одного жесткого диска.

Когда я получил готовый флопотрон, в первую очередь захотелось проверить скорость реакции шаговых двигателей дисководов. Для этого с помощью программы Speedy MIDI я из табулатурной записи для гитары мелодии «Полет шмеля» создал midi-файл, который и «скормил» Moppy. Дисководы отработали замечательно.

Тогда я решил проверить нотный диапазон шаговых двигателей. Для этого в программе Guitar Pro я написал простую мелодию с партиями для двух гитар, постаравшись раскрасить ее бендами, пулл-оффами, хаммер-онами и слайдами. С названием мелодии я заморачиваться не стал и назвал ее просто — «Простая мелодия». Послушать ее так, как она задумывалась, можно здесь. Флопотрон же при проигрывании ожидаемо растерял часть нот, которые игрались подтяжкой струн или скольжением.

Из-за музыкальной ограниченности шаговых двигателей нельзя просто так взять и проиграть на флопотроне первый попавшийся в интернете midi-файл. Его еще нужно будет адаптировать, подстроив под нотные возможности флопотрона. Во время экспериментов оказалось, что лучше всего для флопотрона подходят 8-битные по своему звучанию мелодии из старых игр и фильмов. Адаптировать midi-файлы с этими мелодиями для флопотрона оказалось проще: лишние музыкальные инструменты (например, барабаны) я просто выкидывал, а остальные пытался объединить таким образом, чтобы на выходе получился midi-файл с четырьмя дорожками — по одной на дисковод.

У простого флопотрона есть особенность: он не способен менять громкость исполняемой мелодии. Физически невозможно заставить шаговые двигатели жужжать одну ноту громче, а другую тише. По этой причине в программном обеспечении Moppy данные о громкости дорожек в midi-файле просто игнорируются. Павел Задрожняк в своем варианте флопотрона управление громкостью проигрываемых нот реализовал с помощью увеличения числа дисководов. В его аппарате партию каждого музыкального инструмента исполняют по восемь дисководов. Каждая из таких колонн дисководов управляется самодельной микроконтроллерной платой на базе чипов ATmega 16. Принцип сам по себе довольно прост — чем громче нота, тем большее количество дисководов ее играет.

Флопотрон — устройство, которое довольно просто собрать и запрограммировать. Особенно если учесть, что на аппаратном уровне все сводится к обычному подключению проводков к соответствующим контактам Arduino UNO (можно использовать и другие совместимые версии, например, Arduino Nano или Arduino Micro), а на программном — к заливке прошивки Moppy с помощью Arduino IDE. Ну, а если вы особенно талантливый и не ленивый человек, то можете создать свой собственный контроллер дисководов, 3D-принтеров, сканеров и жестких дисков, написать для него программу, музыку и удивить мир по-настоящему новым звучанием. Главное помнить: не все дисководы жужжат одинаково.

Василий Сычёв

Нашли опечатку? Выделите фрагмент и нажмите Ctrl+Enter.