Запускаем 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 87C51FC был вариантом на базе УФ-стираемой EPROM памяти (объемом 32КиБ), C-версия - на КМОП процессе, объем памяти увеличен до 256 байт. Ядро 8051 не было особенно производительным - т. к. даже самые простые операции требовали 12 тактов, так что при тактовой частоте 20МГц он едва мог сделать миллион операций в секунду. Также отсутствовали команды деления (16->8 бит, деление восьмибитного на восьмибитное было, за 48 тактов), впрочем, для того времени это было повсеместно. Конечно, современные 8051-совместимые ядра зачастую на порядки быстрее (и по тактовой частоте, и по количеству тактов на команду).
Пару недель назад ко мне в руки случайно попал D87C51FC-20 - и я решил его запустить, чтобы прочувствовать проверенные временем технологии.
Всегда было интересно, что там внутри и какая толщина кварцевого окна, так что я пожертвовал AMD AM27C64. Окно оказалось толщиной примерно 1mm и приклеено на удивление хорошо:
Мы привыкли думать, что плоские оптические окна не влияют на изображение и через них все можно рассмотреть без ограничений. Но это верно только для объектов на условной бесконечности, а вблизи, когда мы наблюдаем с большой числовой апертурой (что обычно делают для повышения разрешения в случае микроскопа) - плоское окно вносит сферическую аберрацию и для объективов с апертурой примерно более 0.3 - разрешение начинает ухудшаться, а не увеличиваться. Аналогичная проблема есть и при попытке увидеть данные на компакт-диске - со стандартным объективом с апертурой 0.3 видно лучше, чем с 0.7.
Тут самое время достать козырь - объектив с коррекцией толщины стекла (изначально их сделали для контроля ЖК мониторов при производстве). На фотографии ниже - слева 87C51 без коррекции стекла (низкий контраст и разрешение из-за сферической аберрации), справа - с коррекцией на 1.05mm стекла:
Теперь можно сфотографировать весь кристалл через кварцевое окно:
Стирание чипа
Чип стирается относительно высокой дозой УФ излучения, в документации написано короче 400нм. Обычно используются кварцевые лампы излучающие 254нм. Я попробовал стереть светодиодов на 245nm - но у него была настолько маленькая мощность, что даже через час ни одного бита не стерлось. Видимо для длин волн короче 350нм ртутные лампы пока безусловно разрывают полупроводниковые светодиоды.Некоторое время назад для другого проекта и купил странные ртутные лампы, требующие 10v/300mA. Их продают в версии "с озоном" (кварцевая колба, пропускает линию ртути 185нм) и "без озона" (излучение 254нм и далее). Для стирания данных, конечно, лучше использовать 254нм, но у меня была более злая версия. Интересно, что только верхняя часть спирали лампы покрыта оксидным слоем для увеличения эмиссии электронов.
Лампа работает очень необычным образом. Включать её нужно к блоку питания с напряжением ~14-15В и ограничением тока 300mA. При включении спираль нагревается до красна, и испаряет ртуть из пластины с амальгамой.
Когда давление паров достаточно повышается - зажигается разряд между верхними частями спирали, и резистивный нагрев спирали автоматически резко снижается. Эта фотография сделана в защитных очках, камера с УФ фильтром. Все тело должно быть закрыто, т. к. и 185 и 254нм вызывают рак кожи и катаракту. Озон ядовит. В общем, это лучше не повторять, и стирать лампой без озона.
Когда кварц так светится голубым - нужно бежать!
Лампа установлена в банке с огурцами (без огурцов), с алюминиевой фольгой с внутренней стороны. Обычное стекло банки не пропускает излучение короче 365нм. Точка излучения лампы была примерно в 2 сантиметрах от окна микросхемы, и содержимое стиралось примерно за 10 минут.
Прошивка
С народным программатором MiniPro TL866 - никаких сложностей (сейчас на него есть open-source софт open source software).Пишем демо-программу
Что ж, завязываем с фотографиями - пора писать код! В дополнение к стандартному мигающему светодиоду я решил также рассчитать простые числа и напечатать их через последовательный порт (чтобы задача была не слишком сложная, и не слишком тривиальная вычислительно). Изначально я попробовал пойти по пути Jay Carlson и попробовал использовать современную IDE 8051-совместимых чипов от Silicon Labs (Simplicity Studio). К сожалению, там бинарная совместимость похоже только для GPIO, но не для последовательного порта (что не удивительно, последовательный порт в оригинальном 8051 очень уж простой). Так что я перешел на open source SDCC с которым все пошло как по маслу.Я не хотел усложнять код кольцевыми буферами и прерываниями, и отправляю данные побайтно. Единственная оптимизация, которую я тут сделал - проверка завершения отправки до отправки следующего байта, а не после (как это в большинстве примеров). Это позволяет частично распараллелить работу последовательного порта и вычислений. Для того, чтобы это работало и для первого байта - я устанавливаю флаг завершения передачи предыдущего байта TI = 1 в начале программы.
#include <8051.h>
#include <stdio.h>
#include <stdbool.h>
int putchar(int c) {
/* We never start when transmission is not complete */
while (TI==0); /* Wait until stop bit transmit */
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;//To continue work while we are transmitting - we are waiting before transmission, not after
printf("Hello world!\r\n");
printf("Let's calculate some primes: 2");
while (1)
{
for (i = 3; i <= 32000; i+=2) {
loop_limit = 180;
if(i-1<loop_limit)loop_limit = i-1;//We will not calculate square root :-)
is_prime = true;
for (j = 2; j <= loop_limit; ++j) {
if (i % j == 0) {
is_prime = false;
break;
}
}
if (is_prime) {
toggle_led();
printf(" %d", i);
}
}
printf("\r\nHere we go again: 2");
}
}
Т. к. в 8751 нет PLL (что не удивительно), получить требуемую скорость работы последовательного порта может оказаться сложнее, чем кажется. Скорость последовательного порта определяется частотой кварца и предельным значением счета таймера1 (TH1). Изначально я запускал 8751 от кварца с максимальной частотой 20Мгц и TH1=256-5 - но данные последовательного порта не удавалось корректно прочитать на компьютере. А вот осциллограф декодировал корректно. Оказалось, что скорость последовательного порта получилась с ошибкой в 9% (10416 бод вместо 9600, по формуле X = F/(12*32*Y) где F это частота кварца и Y - предел счета таймера). Можно было бы сконфигурировать USB-UART адаптер CP2102 на эту частоту, или подобрать другой кварц, который дал бы меньшую ошибку.
Собираем прототип
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) печатает пробел, микроконтроллеру нужно перевести число в строку. И этот перевод без аппаратного деления 16-и битных чисел занимает достаточно большое время, так что даже на скорости 9600 бод заметно. Больше знаков - больше делений - больше задержка на конвертацию.
Резюме
Заметную часть времени до рабочего кода съели 8 итераций стереть-записать, раньше для этого были полноценные дев-борды с быстрой отладкой во внешней памяти. Самые необходимые фичи современных микроконтроллеров уже существуют 87C51 - он очень сильно обогнал своё время. Тем не менее, современные контроллеры на этой задаче ожидаемо были бы намного проще в работе:1) Производительность. 1(один) 8-bit MOPS против ~50-150 32-bit MOPS в современных микроконтроллерах за 1-2$. Разрыв в математике еще больше, т.к. теперь мы избалованы относительно быстрым делением и умножением. Вместо написания уникального ручного кода деления 16-битного числа на 10 сдвигами - можно просто делить и не переживать. Теперь и аппаратной арифметикой с плавающей точкой местами никого не удивить.
2) PLL. Сейчас можно поставить самый популярный/дешевый кварц на 8Mhz и сконфигурировать почти любую удобную частоту, не собирая библиотеку кварцев на все случаи жизни. Уникальные кварцы теперь нужны в основном только в ситуации где нужен низкий фазовый шум (высокоскоростные интерфейсы, ЦАП/АЦП высокого разрешения и проч.).
3) Аппаратный отладчик и программирование через JTAG - обеспечивает очень быстрые итерации.
4) Последовательные порты (и другие интерфейсы) с буферами и гораздо более высокими скоростями.
5) Внутренний сброс, brown-out detection, watchdog timer.
Так что, несмотря на то что зачастую 8051-совместимые микроконтроллеры дешевле, разработка под них требует больше времени (=денег). Но для применений с очень большим объемом (миллионы штук), где задача относительно несложная и не требует много математики (условная микроволновка) или уже есть наработанная база готового кода - общая стоимость проекта может оказаться минимальной, потому наследники 8051 ставят в новые продукты и сегодня (и нет предпосылок к уходу 8051 на пенсию даже при наличии свободных/условно-бесплатных ядер RISC-V).