Pull to refresh

Запускаем Intel 87C51 — первый крупносерийный микроконтроллер (1980)

Level of difficultyEasy
Reading time7 min
Views6.6K

  Мы принимаем как должное удобства современных микроконтроллеров - все ключевые компоненты интегрированы в один корпус: флэш-память/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/8->8), впрочем, для того времени это было повсеместно. Конечно, современные 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 <stdio.h>
#include <stdbool.h>

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<loop_limit)loop_limit = i-1;
          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 на эту частоту, или подобрать другой кварц, который дал бы меньшую ошибку.

Оказалось, что с кварцем на 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) печатает пробел, микроконтроллеру нужно конвертировать число в строку и этот перевод без аппаратного деления 16-и битных целых чисел занимает достаточно большое время, так что даже на скорости 9600 бод заметно. Больше знаков - больше делений - больше задержка на конвертацию.

Резюме

  Заметную часть времени до рабочего кода съели 8 итераций стереть-записать, раньше для этого были полноценные отладочные платы с кодом во внешней памяти. Самые необходимые фичи современных микроконтроллеров уже существуют 87C51 - он очень сильно обогнал своё время. Тем не менее, современные контроллеры на этой задаче ожидаемо были бы намного проще в работе:

  1. Производительность. 1(один) 8-bit MOPS против ~50-150 32-bit MOPS в современных микроконтроллерах за 1-2$. Разрыв в математике еще больше, т.к. теперь мы избалованы относительно быстрым делением и умножением. Вместо написания уникального ручного кода деления на 10 сдвигами - можно просто делить и не переживать. Теперь и аппаратной арифметикой с плавающей точкой местами никого не удивить.

  2. PLL. Сейчас можно поставить самый популярный/дешевый кварц на 8Mhz и сконфигурировать почти любую удобную частоту, не собирая библиотеку кварцев на все случаи жизни. Уникальные кварцы теперь нужны в основном только в ситуации где нужен низкий фазовый шум (высокоскоростные интерфейсы, ЦАП/АЦП высокого разрешения и проч.).

  3. Аппаратный отладчик и программирование через JTAG - обеспечивает очень быстрые итерации.

  4. Последовательные порты (и другие интерфейсы) с буферами и гораздо более высокими скоростями.

  5. Внутренний сброс, brown-out detection, watchdog timer.

    Список, конечно, можно продолжать...

  Так что, несмотря на то что зачастую 8051-совместимые микроконтроллеры дешевле, разработка под них требует больше усилий и времени (=денег). Но для применений с очень большим объемом (миллионы штук), где задача относительно несложная и не требует много математики (условная микроволновка) или уже есть наработанная база готового кода - общая стоимость проекта может оказаться минимальной, потому наследники 8051 ставят в новые продукты и сегодня (и нет предпосылок к уходу 8051 на пенсию даже при наличии свободных/условно-бесплатных ядер RISC-V).

Tags:
Hubs:
+76
Comments46

Articles