Готовое устройство

И снова здравствуйте, дамы и господа. Наш Отдел Перспективных Разработок продолжает свой цикл статей о древних технологиях и опять выходит на связь с очередным передовым творением нашего сумрачного разума приближающим трепетный момент всеобщей строггофикации.

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

Итак, мы рассмотрим память на ферритовых кольцах, причем, даже не RAM, а ROM.

Теория

В ретроспективе компьютерной истории двадцатого века память на ферритовых кольцах стала одной из важных вех развития цифровых технологий. Не смотря на то, что классическое ОЗУ на ферритовых кольцах появилось аж в 1945 году, ПЗУ на похожей технологии в физическом виде реализовали несколько позже, когда понадобилось вместить некоторый объем данных в минимально возможное для технологий того времени пространство. ПЗУ на ферритовых кольцах, оно же «веревочная» память применялась как способ хранения прошивки в виде адресуемых единиц данных, например, в бортовых компьютерах СССР и НАСА того времени, летавшие зондами на Марс и на Луну (миссия «Аполлон» как широко известный пример).

Не смотря на сходный принцип работы ферритовых ОЗУ и ПЗУ, способ хранения информации в них несколько различается. Рассмотрение работы ОЗУ мы оставим для следующих статей, а ПЗУ же функционировало достаточно просто:

Ферритовое кольцо являлось, по сути, примитивнейшим трансформатором, который использовался как датчик наличия тока. Если пропустить через кольцо два проводника и подать через один проводник ток (пусть это будет измеряемый проводник), в кольце наведется магнитный поток, и во втором (измеряющем) проводнике так же возникнет ток, который возможно зафиксировать.

Однако, возникающие напряжения на измеряющем проводнике гораздо ниже, чем на измеряемом, потому, для наших целей мы его немного модифицируем - сделаем несколько витков вокруг кольца и добавим конденсатор и диод. Конденсатор позволит накапливать заряд, стекающий с «измеряющего» проводника, по наличию которого в дальнейшем мы будем принимать решение о наличии тока в «измеряемом» проводнике, а диод позволит не потерять накопленный в конденсаторе заряд.

Ферритовый датчик

Суть работы данной схемы проста:

Если подать на измеряемый проводник напряжение и считать накопленный конденсатором потенциал с датчика, то, при его наличии можно принять данный факт за установленный бит. Если напряжения нет – бит не установлен.

А теперь магия! Через кольцо возможно пропустить много проводников. Столько, сколько позволит в него запихнуть внутренний диаметр. И, если пометить каждый такой измеряемый проводник адресом, можно судить о наличие отдельного бита по данному адресу.

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

Ферритовый байт

Пропустим через кольца проводник, помеченный адресом 0. Поскольку каждое кольцо будет опознавать свой бит, то, через кольца с номером бита, который должен быть установлен в 0, мы просто не будем наш проводник пропускать.

Теперь, если подать напряжение на проводник и читать потенциалы с датчиков колец последовательно, можно составить один байт считанной информации на каждый такой проводник.

Это здорово похоже на запись узелками на веревочках, которую применяли некоторые древние народы, собственно, потому и возник термин «веревочная» память.

Практика

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

Во-первых, для считывания потенциала с восьми ферритовых колец нам возможно использовать восемь аналоговых входов (A0 – A7).

Во-вторых, для адресации проводников можно использовать цифровые выводы, коих для небольшого количества адресов вполне хватит.

Впрочем, занимать целый вывод ардуины на один адрес мне показалось расточительным, потому, я еще усложнил схему, применив для адресации выводов сдвиговый регистр из двух 74HC595.

Понимаю, что использование современных микросхем в данном случае несколько «неспортивно», но, построение вот таких вот «гибридов» древних технологий с современными – это особый шик.

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

Соберем схему для сдвигового регистра для 16 адресов, который позволит нам адресовать 16 байт:

Схема сдвигового регистра для адресации проводников

И схему подключения восьми колец на аналоговые пины A0 – A7. Конденсаторы здесь можно взять любые керамические номиналом 100 нФ, диоды так же любые. Я, например, вообще применил что нашел - германиевые 80х годов. Для зрелищности можно попробовать использовать светодиоды. Измеряющая обмотка на кольце – 10 витков любого тонкого провода.

Схема датчика для снятия наличия потенциала с ферритового кольца

Так же нужно учесть, что адресные проводники в дальнейшем будут садиться одним концом на землю. Если их все соединить в одной точке, то, при подаче напряжения каждый проводник окажется связан со всеми остальными и сможет вносить помехи в чтение с колец, наводя потенциалы там, где это не нужно. Потому, проводники на землю стоит посадить через диоды. Для наших целей понадобится 16 диодов. Соединение пропускаемых через кольца проводников на землю в данном случае будет выглядеть так:

Схема соединения проводников по адресам

Я не заморачивался с травлением и свой вариант на макетной плате спаял проводом МГТФ за вечер. Получилось нечто, что можно лицезреть в первой же картинке данной статьи.

Программирование

Итак, мы можем что-то записать в нашу память. Для этого начнем соединять измеряемые проводники:

Один конец, как упоминалось выше, садится через диод на землю. Затем проводник пропускается последовательно через все ферритовые кольца формируя биты, по принципу - если проводник проходит через кольцо, бит установлен, если проводник не проходит через кольцо, бит не установлен.

После этого второй конец проводника сажается на ножку сдвигового регистра, ассоциированную с адресом данного проводника. Всё, ПЗУ «зашито», причем, в прямом смысле.

Теперь обратимся, наконец, к нашей ардуино и попробуем прочесть то, что мы наделали.

Прошивка ардуино
//Адреса пинов снимающих сигнал с ферритовых колец
uint8_t in_pins[8] = {A0, A1, A2, A3, A4, A5, A6, A7};

//Пины управляющие сдвиговым регистром на двух 74HC595
//Пин подключен к ST_CP входу 74HC595
int latchPin = 12;
//Пин подключен к SH_CP входу 74HC595
int clockPin = 13;
//Пин подключен к DS входу 74HC595
int dataPin = 11;

//Вывод на 16-битный сдвиговый регистр числа, кодирующего пины
void shift_out_value(unsigned int val) {
  digitalWrite(latchPin, LOW);
  //Выведем на сдвиговый регистр последний байт
  shiftOut(dataPin, clockPin, MSBFIRST, highByte(val));
  //Выведем на сдвиговый регистр первый байт
  shiftOut(dataPin, clockPin, MSBFIRST, lowByte(val));
  digitalWrite(latchPin, HIGH);
}

//Разрядка конденсаторов на датчиках колец
void discharge() {
  for (int i = 0; i < 8; i++) {
    //Переключить пин на выход
    pinMode(in_pins[i], OUTPUT);
    //занулить
    digitalWrite(in_pins[i], LOW);
    //небольшая задержка
    delay(3);
    //Переключить обратно на вход
    pinMode(in_pins[i], INPUT);
  }
}

//Считывание байта с колец
uint8_t read_ferrite_byte(unsigned int address) {
  uint8_t values[] = {0, 0, 0, 0, 0, 0, 0, 0};

  //Для точности можно увеличить количество проходов чтения, но, 
  //работает и так
  //for (uint8_t phase = 0; phase < 2; phase++)
  {
    //Разрядим
    discharge();
    //Подергаем нужным проводком по нужному адресу через сдвиговый 
    //регистр
    //Это нужно чтобы создать в ферритовых кольцах магнитный поток и 
    //зарядить конденсаторы
    //на датчиках тех колец, через которые проходит проводник с нужным 
    //адресом
    for (uint8_t j = 0; j < 8; j++) {
      shift_out_value(address);
      shift_out_value(0);
    }
    //Считаем показания с датчиков всех колец
    for (uint8_t ix = 0; ix < 8; ix++)
    {
      values[ix] += analogRead(in_pins[ix]);
    }
  }

  discharge();

  //Упакуем все считанные значения в один байт, где 0 - не считано 
  //ничего, 1 - считано что-то
  uint8_t result = 0;
  for (uint8_t ix = 0; ix < 8; ix++)
  {
    result = (result << 1) | ((values[ix] > 1) ? 1 : 0);
  }
  return result;
}

void setup() {
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
  for (int i = 0; i < 8; i++) {
    pinMode(in_pins[i], INPUT_PULLUP);
    digitalWrite(in_pins[i], LOW);
  }

  Serial.begin(115200);
  
  //разрядим конденсаторы
  discharge();
  Serial.println("------------------------------");
  //Убедимся, что с ферритовых колец ничего не читается
  for (uint8_t ix = 0; ix < 8; ix++)
  {
    Serial.print(analogRead(in_pins[ix]), DEC);
    Serial.print(" ");
  }

  Serial.println();
  Serial.println();

  //Считаем и выведем значения по адресам 1..16
  for (int i = 0; i < 16; i++) {
    Serial.print("address[");
    if (i < 10) Serial.print("0");
    Serial.print( i, DEC);
    Serial.print("] = ");
    uint8_t nibbles = read_ferrite_byte((unsigned int)1 << i);
    Serial.print(nibbles, DEC);
    Serial.print(" - ");
    Serial.print(nibbles, BIN);
    if (nibbles > 32) {
      Serial.print(" - ");
      Serial.print((char)nibbles);
    }
    Serial.println();
  }
}

void loop() {
}

Алгоритм чтения следующий:

  1. Разрядить конденсаторы на датчиках ферритовых колец, поскольку, на них мог сохраниться потенциал, оставшийся от предыдущих шагов чтения, и он может внести помехи в читаемые данные.

  2. Задать на адресном регистре адрес ножки, к которой подключен проводник с нужным адресом. При этом, через проводник потечет ток, который вызовет накопление потенциала в конденсаторах датчиков тех колец, через которые пропущен данный проводник.

  3. Снять показание потенциала накопленного заряда со всех колец последовательно по номерам бит в байте и составить считанное значение.

    Результат зависит от того, что было зашито, разумеется.

Зашил слово HELLO и успешно его считал

Ну и подводный камень, о котором я упоминал ранее:

Для наведения магнитного потока в кольцах нужно подавать питание на измеряемые проводники не постоянно, а с определенной частотой. В схеме со сдвиговым регистром для того приходится совершать цикл из нескольких записей адреса и нуля:

  • Записать в регистр адрес пина нужного проводника, тем самым включив его.

  • Записать в регистр 0, выключив все проводники.

  • Повторить N раз.

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

Если начать увеличивать длину регистра, увеличивая количество адресуемых проводников, скорость записи в регистр так же вырастет, и в какой-то момент частота подаваемого тока уже будет недостаточна для формирования магнитного потока в кольцах.

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

Если у вас в голове родилась интересная схема – поделитесь, а то у меня целый мешок советской логики без дела лежит. ?

UPD: В комментариях предложили использовать ШИМ через ножку OE самого сдвигового регистра, что решает задачу.