
И снова здравствуйте, дамы и господа. Наш Отдел Перспективных Разработок продолжает свой цикл статей о древних технологиях и опять выходит на связь с очередным передовым творением нашего сумрачного разума приближающим трепетный момент всеобщей строггофикации.
Сегодня мы совершим небольшой экскурс в историю прошлого века и выдернем оттуда знания об артефакте, практическая пригодность которого на сегодняшний день такова, что перед ней спасует даже наша отдельная лаборатория, которая всем этим поделкам назначение придумывает. В смысле, нулевая практическая пригодность. Однако, вполне сгодится на лабораторную работу для школьника с ардуиной, или занятие на вечер для тебя, мой дорогой инженер с унылой работой, женой, дочкой, двумя котами и кризисом среднего возраста.
Итак, мы рассмотрим память на ферритовых кольцах, причем, даже не 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() { }
Алгоритм чтения следующий:
-
Разрядить конденсаторы на датчиках ферритовых колец, поскольку, на них мог сохраниться потенциал, оставшийся от предыдущих шагов чтения, и он может внести помехи в читаемые данные.
-
Задать на адресном регистре адрес ножки, к которой подключен проводник с нужным адресом. При этом, через проводник потечет ток, который вызовет накопление потенциала в конденсаторах датчиков тех колец, через которые пропущен данный проводник.
-
Снять показание потенциала накопленного заряда со всех колец последовательно по номерам бит в байте и составить считанное значение.
Результат зависит от того, что было зашито, разумеется.

Ну и подводный камень, о котором я упоминал ранее:
Для наведения магнитного потока в кольцах нужно подавать питание на измеряемые проводники не постоянно, а с определенной частотой. В схеме со сдвиговым регистром для того приходится совершать цикл из нескольких записей адреса и нуля:
-
Записать в регистр адрес пина нужного проводника, тем самым включив его.
-
Записать в регистр 0, выключив все проводники.
-
Повторить N раз.
Если регистр небольшой, скорость записи в него позволит сформировать некоторую частоту подаваемого на проводники тока, позволяющую завести ферритовые кольца и накопить в их датчиках некоторый потенциал.
Если начать увеличивать длину регистра, увеличивая количество адресуемых проводников, скорость записи в регистр так же вырастет, и в какой-то момент частота подаваемого тока уже будет недостаточна для формирования магнитного потока в кольцах.
В таком случае, интересным домашним заданием становится решение подобной задачи масштабирования памяти. Можно попробовать для увеличения количества адресов памяти использовать какую-нибудь каскадную адресацию сдвиговых регистров, где сдвиговый регистр будет ссылаться на следующий сдвиговый регистр. Или использовать логические дешифраторы, повешенные на тот же регистр.
Если у вас в голове родилась интересная схема – поделитесь, а то у меня целый мешок советской логики без дела лежит. ?
UPD: В комментариях предложили использовать ШИМ через ножку OE самого сдвигового регистра, что решает задачу.
ссылка на оригинал статьи https://habr.com/ru/post/687284/
Добавить комментарий