Запускаем Intel 87C51 — первый крупносерийный микроконтроллер (1980)
Мы принимаем как должное удобства современных микроконтроллеров — все ключевые компоненты интегрированы в один корпус: флэш-память/EEPROM, SRAM, само процессорное ядро, PLL, ADC/DAC, PWM, последовательные порты и многое другое.
Но так было не всегда. Первым монолитным микроконтроллером был Intel 8048 (MCS-48) выпущенный в 1976 по n-МОП технологии. Не планировалось что у него будет длинный жизненный цикл и уже через 4 года в 1980 на смену ему пришел Intel 8051 (MCS-51), завоевавший мир. Он имел на борту 4КиБ однократно-программируемой памяти, 128 байт SRAM, GPIO, последовательные порт и, собственно, 8-битное процессорное ядро. Intel 8751 был вариантом на базе УФ-стираемой EPROM памяти, и C-версии на КМОП процессе. Ядро 8051 не было особенно производительным — т. к. даже самые простые операции требовали 12 тактов, так что при тактовой частоте 20МГц он едва мог сделать миллион операций в секунду. Также отсутствовали команды деления и умножения, впрочем, для того времени это было повсеместно. Конечно, современные 8051-совместимые ядра зачастую на порядки быстрее (и по тактовой частоте, и по количеству тактов на команду).
Пару недель назад ко мне в руки случайно попал D87C51FC-20 — и я решил его запустить, чтобы прочувствовать проверенные временем технологии. Сразу отмечу — не стоит тут искать практической пользы, это скорее экскурс в историю на 44 года назад…
Мне всегда было интересно, что там внутри и какая толщина кварцевого окна, так что я пожертвовал AMD AM27C64. Окно оказалось толщиной примерно 1 мм и приклеено на удивление хорошо:
Мы привыкли думать, что плоские оптические окна не влияют на изображение и через них все можно рассмотреть без ограничений. Но это верно только для объектов на условной бесконечности, а вблизи, когда мы наблюдаем с большой числовой апертурой (что обычно делают для повышения разрешения в случае микроскопа) — плоское окно вносит сферическую аберрацию и для объективов с апертурой примерно более 0.3 — разрешение начинает ухудшаться, а не увеличиваться. Аналогичная проблема есть и при попытке увидеть данные на компакт-диске — со стандартным объективом с апертурой 0.3 видно лучше, чем с 0.7.
Тут самое время достать козырь — объектив с коррекцией толщины стекла (изначально их делали для контроля ЖК мониторов на производстве). На фотографии ниже — слева 87C51 без коррекции толщины стекла (низкий контраст и разрешение из-за сферической аберрации), справа — с коррекцией на 1.05mm стекла:
Теперь можно сфотографировать весь кристалл через кварцевое окно:
Стираем старые данные
Чип стирается относительно высокой дозой УФ излучения. Обычно используются кварцевые лампы излучающие 254нм. Я попробовал стереть светодиодом на 245nm -, но у него была настолько маленькая оптическая мощность / крошечный КПД, что даже через час ни одного бита не стерлось. Видимо для длин волн короче 350нм ртутные лампы пока безусловно разрывают полупроводниковые светодиоды.
Некоторое время назад для другого проекта я купил странные ртутные лампы требующие 10v/300mA. Их продают в версии «с озоном» (кварцевая колба, пропускает линию ртути 185нм) и «без озона» (излучение 254нм и далее). Для стирания данных, конечно, лучше использовать 254нм, но у меня была более злая версия. Интересно, что только верхняя часть спирали лампы покрыта оксидным слоем для увеличения эмиссии электронов.
Лампа работает очень необычным образом. Включать её нужно блоком питания с напряжением ~14–15В и ограничением тока 300mA. При включении спираль нагревается до красна, и испаряет ртуть из пластины с амальгамой.
Когда давление паров достаточно повышается — зажигается разряд между верхними частями спирали, и резистивный нагрев спирали автоматически резко снижается. Эта фотография сделана в защитных очках, камера с УФ фильтром. Все тело должно быть закрыто, т. к. и 185 и 254нм вызывают рак кожи и катаракту. Озон ядовит. В общем, это лучше не повторять, и стирать лампой без озона.
Когда кварц так светится голубым — нужно бежать!
Лампа установлена в банке с огурцами (без огурцов), с алюминиевой фольгой с внутренней стороны. Обычное стекло банки не пропускает излучение короче 365нм. Точка излучения лампы была примерно в 2 сантиметрах от окна микросхемы, и содержимое стиралось примерно за 10 минут.
Прошивка
С народным программатором MiniPro TL866 — никаких сложностей (сейчас на него есть open-source софт).
Пишем демо-программу
Что ж, завязываем с фотографиями — пора писать код! В дополнение к стандартному мигающему светодиоду я решил также найти простые числа и напечатать их через последовательный порт (чтобы задача была не слишком сложная, и не слишком тривиальная вычислительно). Изначально я попробовал пойти по пути Jay Carlson и попробовал использовать современную IDE 8051-совместимых чипов от Silicon Labs (Simplicity Studio). К сожалению, там бинарная совместимость похоже только для GPIO, но не для последовательного порта (что не удивительно, последовательный порт в оригинальном 8051 очень уж простой). Так что я перешел на open source SDCC с которым все пошло как по маслу.
Я не хотел усложнять код кольцевыми буферами и прерываниями, и отправляю данные побайтно. Единственная оптимизация, которую я тут сделал — проверка завершения отправки до отправки следующего байта, а не после (как это сделано в большинстве примеров). Это позволяет частично распараллелить работу последовательного порта и вычислений. Для того, чтобы это работало и для первого байта — я устанавливаю флаг завершения передачи предыдущего байта TI = 1 в начале программы.
#include <8051.h>
#include
#include
int putchar(int c) {
while (TI==0); /* Wait until transmission is complete */
TI = 0;
SBUF = c;
return c;
}
void toggle_led(void)
{
P1_0 = !P1_0;
}
int main (void)
{
int i, j, loop_limit;
bool is_prime;
//Serial port speed. Perfect 9600 for 18.432MHz crystal
TH1 = (unsigned char)(256-5);//No "overflow in implicit constant conversion"
TMOD = 0x20;
SCON = 0x50;
TR1 = 1;
TI = 1;
printf("Hello world!\r\n");
printf("Let's calculate some primes: 2");
while (1)
{
for (i = 3; i <= 32000; i+=2) {
loop_limit = 180;
//We should not calculate square root on 8051 :-)
if(i-1
Т. к. в 8751 нет PLL (что не удивительно), получить требуемую скорость работы последовательного порта может оказаться сложнее, чем кажется. Скорость последовательного порта определяется частотой кварца и предельным значением счета таймера1 (TH1). Изначально я запускал 8751 от кварца с максимальной частотой 20Мгц и TH1=256–5 -, но данные последовательного порта не удавалось корректно прочитать на компьютере. А вот осциллограф декодировал корректно. Оказалось, что скорость последовательного порта получилась с ошибкой в 9% (10416 бод вместо 9600, по формуле X = F/(12×32*Y) где F это частота кварца и Y — предел счета таймера). Можно было бы сконфигурировать USB-UART адаптер CP2102 на эту частоту, или подобрать другой кварц, который дал бы меньшую ошибку.
Оказалось, что с кварцем на 18.432Mhz и таймером 256–5 — частота получается идеальная, как будто этот кварц и был создан для этой задачи. (Комментатор за кадром: Именно так)
Собираем прототип
P1.0 (pin 1): LED.
Reset (pin 9): Active high. Притягиваем к GND резистором, и к VCC через небольшой электролитический конденсатор. Это обеспечит сброс микроконтроллера при подаче питания.
TXD (pin 11): Выход последовательного порта.
XTAL1 и XTAL2 (pins 18 and 19): Кристалл на 18.432Mhz. Заработало без внешних конденсаторов, что вероятно дает некоторую незначительную ошибку частоты и меньшую стабильность. Паразитных емкостей макетной платы однозначно недостаточно (~2pF).
GND (pin 20) и VCC (pin 40): 5V питание и земля.
EA (pin 31): Для отключения внешней памяти (её у нас нет) — подключаем к VCC.
Запускаем
С корректными настройками последовательного порта — все работает!
Если посмотрим на TX осциллографом при передачи разных простых чисел, можно увидеть что-то неожиданное:
Когда печатаем трехзначное число — задержка между пробелом и первой цифрой ~1.25ms, 1.8ms для четырехзначного и 2.5ms для пятизначного. Почему так получается?
После того как printf (» %d», i) печатает пробел, микроконтроллеру нужно конвертировать число в строку и этот перевод без аппаратного деления занимает достаточно большое время, так что даже на скорости 9600 бод заметно. Больше знаков — больше делений — больше задержка на конвертацию.
Резюме
Заметную часть времени до рабочего кода съели 8 итераций стереть-записать, раньше для этого были полноценные отладочные платы с кодом во внешней памяти. Самые необходимые фичи современных микроконтроллеров уже существуют 87C51 — он очень сильно обогнал своё время. Тем не менее, современные контроллеры на этой задаче ожидаемо были бы намного проще в работе:
Производительность. 1(один) 8-bit MOPS против ~50–150 32-bit MOPS в современных микроконтроллерах за 1–2$. Разрыв в математике еще больше, т.к. теперь мы избалованы относительно быстрым делением и умножением. Вместо написания уникального ручного кода деления и умножения на 10 сдвигами — можно просто делить и не переживать. Теперь и аппаратной арифметикой с плавающей точкой местами никого не удивить.
PLL. Сейчас можно поставить самый популярный/дешевый кварц на 8Mhz и сконфигурировать почти любую удобную частоту, не собирая библиотеку кварцев на все случаи жизни. Уникальные кварцы теперь нужны в основном только в ситуации где нужен низкий фазовый шум (высокоскоростные интерфейсы, ЦАП/АЦП высокого разрешения и проч.).
Аппаратный отладчик и программирование через JTAG — обеспечивает очень быстрые итерации.
Последовательные порты (и другие интерфейсы) с буферами и гораздо более высокими скоростями.
Внутренний сброс, brown-out detection, watchdog timer.
Список, конечно, можно продолжать…
Так что, несмотря на то что зачастую 8051-совместимые микроконтроллеры дешевле, разработка под них требует больше усилий и времени (=денег). Но для применений с очень большим объемом (миллионы штук), где задача относительно несложная и не требует много математики (условная микроволновка) или уже есть наработанная база готового кода — общая стоимость проекта может оказаться минимальной, потому наследники 8051 ставят в новые продукты и сегодня (и нет предпосылок к уходу 8051 на пенсию даже при наличии свободных/условно-бесплатных ядер RISC-V).