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

Собственно программатор — голый Arduino с прошивкой, разбирающей HEX-файл построчно и реализующий программирование целевого чипа по SPI. Прошивка и инструкции сложены в репозитории на гитхабе: At89s-prog.

Конкретные чипы (At89s...) здесь скорее для примера. Мы не будем воспевать достоинства 8051-й архитектуры (конечно, они архаичны но у них есть плюсы, благодаря которым они до сих пор сохраняют популярность). У меня в какой‑то момент оказалась их горстка а программатора под рукой не нашлось. Они (те что с индексом S) программируются через SPI, а не стандартным «многоногим» интерфейсом что делает удобным их применение в любительских поделках.

Предисловие

Когда вы занимаетесь микроконтроллерами в промышленных условиях, обычно используете более‑менее однотипные чипы и нужный инвентарь — программаторы, отладчики и т. п. — есть под рукой. Типичный случай — утилита для прошивки устанавливается на компьютер — подключается специальный девайс (программатор) и уже он подключается к схеме содержащей программируемый контроллер. На картинке ниже этот случай изображён сверху. Снизу представлена ситуация описанная в этой статье — мы исключаем утилиту на стороне компьютера, а в качестве программатора используем Arduino.

Упрощённый подход к прошивке чипов
Упрощённый подход к прошивке чипов

В любительской практике бывает и так, что в одной поделке использовал AVR, в другой ST32, в третьей MSP430, в четвёртой ещё что‑нибудь. Зоопарк чипов в хобби‑проектах имеет свойство копиться со временем. Закупаться соответствующим количеством нужных программаторов не всегда имеет смысл. Дело даже не в деньгах а в том что (как многие «электронщики» знают — со временем становится затруднительно вспомнить где что лежит).

Сам я поэтому часто использую чипы у которых есть встроенный UART‑загрузчик (например ST32 и MSP430 упомянутые). Но в частности Atmel издавна упорно шёл другим путём и многие их контроллеры программируются по SPI. Большинство из них поддержаны в AvrDude и программаторе AVRISP — эта прошивка доступна в примерах Arduino IDE.

Однако с попавшими мне в руки 8051-ми клонами (изначально At89s4051 — удобными 20-ногими узкими чипами — а после и At89s52 — они в стандартных 40-ногих корпусах) — эти инструменты не работают. При этом чипы удобные и стоят (на «али») очень недорого.

Открыв datasheet и почитав раздел про SPI команды я подумал что могу быстренько состряпать самодельный программатор.

При этом удобным показалось чтобы к нему ещё не требовалась дополнительная утилита на стороне компьютера. Отправил файл в последовательный порт — и ура!

Вещь получилась рабочая — одна из первых поделок которые я с её помощью разрабатывал — этот радиоприёмник на Si4735:

ну, вот такой коротковолновый радиоприёмник в наше время :)
ну, вот такой коротковолновый радиоприёмник в наше время :)

Атмеловские 8051-е клоны

Мы рассматриваем такие модификации серии At89... (этот префикс у них означает 8051-е чипы):

  • At89s52 и At89s51 (различаются как 8052 и 8051 — 8 против 4 кб флеш‑памяти, лишний таймер) — в «полных» 40-ногих корпусах, или например в архаичных но милых PLCC

  • At89s4051 и At89s2051 — аналоги 8051 но в уменьшенных 20-ногих корпусах, выведены только P1 и P3.

Обратите внимание на букву S после At89 — она означает модели с программированием по SPI (параллельное тоже доступно впрочем но нас не интересует).

Вообще сейчас есть и более интересные 8051-совместимые контроллеры — наверняка многие замечали китайские STC... — в частности STC8G1K08 — тут и прошивка по UART и встроенный RC‑генератор тактовой частоты, и АЦП. И корпуса похожие. Но документация может оставить чувство лёгкого раздражения с непривычки. В любом случае они за пределами нашей сегодняшней темы.

Применение

Возьмите прошивку (arduino-проект) из репозитория указанного выше.

Прошейте её в какой-нибудь Arduino-модуль, который найдётся под рукой. Там нет какой-то специфики и скорее всего подойдёт любой.

Для примера тут же представлены файл blink.asm и скомпилированный из него файл blink.hex — хотя вы можете написать и скомпилировать собственный код. Например код для цифрового радио на Si4735, упомянутый выше — там уже прошивка объёмистая.

Подключите ваш программатор (прошитый) ардуино к программируемому чипу:

  • подключите питание (GND, VCC и если есть отдельный — то VPP)

  • RST к 9й ноге, SCK, MISO, MOSI к 12, 11, 10 соответственно

  • также нужен кварц на XTAL или внешний генератор тактовой частоты

Например вот подключение «голого» At89s52. Для демонстрации добавлены 2 светодиода которыми прошивка blink.hex будет перемигивать:

слева старый ардуино, справа At89s52 в классическом 40-ногом корпусе
слева старый ардуино, справа At89s52 в классическом 40-ногом корпусе

То же самое с 20-ногим узким At89s4051:

У этих VPP не выведен отдельно - поэтому одним проводом меньше
У этих VPP не выведен отдельно - поэтому одним проводом меньше

Теперь нужно лишь настроить последовательный порт (с которым всё ещё соединён Arduino) на параметры 1200 бод, 1 стоп‑бит, без чётности.

Низкая скорость выбрана чтобы процесс прошивки «успевал» за подачей данных. С некоторыми оговорками её можно увеличить (см. ниже) но м.б. не очень актуально.

Удобно использовать какую‑нибудь терминальную программулину, которая позволит и файл отправить — и следить что программатор отвечает. Однако можно использовать и стандартные инструменты ОС — в частности примеры приведены в виде shell‑файлов под линукс:

stty -F /dev/ttyUSB0 1200 -cstopb -parenb cs8 -icrnl -echo

Настроили последовательный порт. Кроме вышеупомянутых настроек тут ещё параметры для подавления эха и формата переводов строк.

cat /dev/ttyUSB0 &

в бэкграунде читаем (и дампаем) что пишет программатор обратно по ходу работы

cat blink.hex >/dev/ttyUSB0

отправляем файл с прошивкой в последовательный канал.

В случае успеха программатор пишет некую информацию по ходу обработки получаемого файла, выглядит это так:

прошивка, тут всего 30 байт - две строки hex-файла
прошивка, тут всего 30 байт - две строки hex-файла

PRG EN — это ответ на команду перехода в режим программирования (не все чипы содержательно на неё отвечают). SIG — сигнатура чипа. Если она совпадает с даташитом, по крайней мере понятно что интерфейс SPI работает.

Дальше строчки с W@ расшифровываются как «write at <addr> <count>» — вот мы записываем 16 байт по адресу 0x0 (первая строка hex-файла) потом ещё 14 по адресу 0×10 (вторая строка).

В третьей строке программатор обнаруживает признак завершения и пишет `Prg mode off`. Напоследок он дёргает ещё раз ресет — где‑то была такая рекомендация, хотя по-моему и без этого чип нормально запустится в рабочий режим.

Из скрипта burn.sh после выполнения выходим по Ctrl‑C — иначе он висит продолжая ожидать ещё чего-нибудь от программатора :)

Стирание чипа требуется (обычно) перед каждой перезаписью. Поскольку время стирания может быть разным, пока оно сделано отдельной командой. Нужно просто отправить в порт восклицательный знак. Для удобства это реализовано в файле erase.sh в виде команды:

echo "!" >/dev/ttyUSB0

очистка чипа
очистка чипа

Иногда полезно считать программу обратно из чипа - чтобы верифицировать нет ли ошибки записи, например. Для этого пока реализованаочень простая «фича» — используются такие же команды как для записи строк HEX‑файла, но с вопросительным знаком вместо двоеточия в начале. Поэтому из строки на самом деле берется только адрес и длина считываемых данных, например (в readmem.sh):

echo "?10000000" >/dev/ttyUSB0

Считаем 0x10 байт с нулевого адреса:

считываем 16 байт из памяти программ - совпадают с hex-файлом
считываем 16 байт из памяти программ — совпадают с hex-файлом

Замечания по коду прошивки программатора

Программатор сам возвращается в «ждущий» режим. То есть вы можете отправить ему команду на стирание, потом закачать новый hex‑файл, потом считать память для проверки — между этими действиями не требуется каких‑то дополнительных манипуляций.

Запись в чип происходит после считывания очередной строки hex‑файла — и ведётся она в побайтовом режиме. Постраничный я сначала использовал, но преимуществ у него не нашёл а размер страниц у чипов разный, да ещё и файлы собранные сложными компиляторами (вроде Keil или SDCC) будут содержать строки в сложном порядке и код собирающий данные для страницы выглядит достаточно мутно.

В строке берутся первые четыре байта (9 символов) чтобы определить режим (чтение‑запись), адрес, количество байт — и тип операции (01 — останов). После чего следующие байты непосредственно записываются.

При этом скорость приёма байт по UART должна быть согласована со скоростью операций записи (сейчас сделано с запасом) — да ещё и со скоростью вывода информации обратно пользователю. В частности из‑за этого некоторые отладочные строчки были сокращены — замените W@ на «writing data at» и что‑нибудь в этом духе — и на достаточно объёмных прошивках вы можете получить пропущенные байты при записи:)

Вероятно эту ситуацию можно улучшить с использованием механизма Xon / Xoff, но у меня с первой попытки не получилось — поэтому пока отложил как некритичный импрувмент.

Заключение

Как сказано выше, цель статьи больше в том чтобы обратить внимание на возможность убрать одно из «звеньев» в инструментах для прошивки. С чипами имеющими встроенный загрузчик можно бы убрать и «программатор». Только к сожалению дефолтных загрузчиков использующих hex‑файлы я пока не встречал. А без этого выходит что всё равно сначала нужен программатор чтобы кастомный загрузчик прошить.

Комментарии (13)


  1. mark_ablov
    27.05.2025 06:00

    А чем обычный usb2uart не подошёл? Тот же ft232. Там хоть SPI, хоть JTAG, хоть ногами дрыгай вручную.


    1. RodionGork Автор
      27.05.2025 06:00

      Тем что на стороне компа нужна будет софтина. FT232 в таком случае всё равно остаётся мудрёным "хардварным" звеном между компьютером и программируемым девайсом, но ещё нужна кастомная тула (да на разные ОС).

      Собственно это как раз то чего мы хотели избежать. Иными словами Arduino в нашем случае - тот же USB2SPI переходник только с возможностью накинуть нужную логику в него.


      1. mark_ablov
        27.05.2025 06:00

        Тем что на стороне компа нужна будет софтина.

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

        А код для хоста - это 5 строк на Python'е, которые вполне легко портируются.


        1. RodionGork Автор
          27.05.2025 06:00

          это точно не 5 строк

          насчёт разницы:

          • либо для программирования нужен только хардварный девайс (кабель USB2UART или вот Arduino)

          • либо нужен хардварный девайс и кастомная утилита, пусть на питоне, только вам ещё на каждом компе где вы её захотите подключить нужно будет py_serial устанавливать

          Вы точно разницы не замечаете?


          1. mark_ablov
            27.05.2025 06:00

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

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

            PS: если не нравится скрипт на питоне, то можно использовать avrdude - он поддерживает пачку переходников, на ft232 в том числе.


            1. NutsUnderline
              27.05.2025 06:00

              в котором цеплять железку в usb-порт можно, а вот запускать/ставить софт - нет, но это прям звучит как весьма редкое стечение обстоятельств.

              я бы характеризовал по другому: бывает ограничении на админские права. то есть нельзя поставить драйвер ft232 а вот свой софт без инсталятора запустить вполне можно. в таком случае хороши шить через hid интерфейс, и что характерно фирменные программаторы/отладчики зачастую так и работают.


          1. NutsUnderline
            27.05.2025 06:00

            либо для программирования нужен только хардварный девайс 

            лучше бы этой девайс тогда притворялся флешкой и умел .bin .hex, а еще прикольнее - еще бы и работал вообще без компа


      1. iamkisly
        27.05.2025 06:00

        Это все конечно здорово, но было бы лучше если бы вы задули PR с нужной функциональностью в avrdude. Потому что дудка это неизменная часть джентельменского набора ардуинщика, она как мемный "топор для рубки мяса, в доме есть у каждого"


        1. RodionGork Автор
          27.05.2025 06:00

          неизменная часть джентельменского набора ардуинщика

          так 8051-е это не для ардуинщиков всё же :)

          хотя соглашусь, идея резонная. гляну попозже можно ли там малой кровью справиться


      1. Mizar91
        27.05.2025 06:00

        Если не использовать чит в виде Линукса с его философией периферии, то все равно потребуется софтина, чтобы лить код в UART. В моем понимании загрузчик, не требующий софта на стороне PC - это что-то вроде MassStorage через USB, куда закидываешь hex-файл, а он уже сам льет его куда нужно.


        1. RodionGork Автор
          27.05.2025 06:00

          не очень понял про какой именно "чит" речь

          отправить файл в ком-порт я могу и в винде. я просто не помню уже команды. скорее всего `mode comX ...` и `copy -b myfile.hex comX` что-то в этом духе


  1. sse
    27.05.2025 06:00

    А нельзя ли тактовую частоту генерировать на ардуине тоже? Тогда и кварц не понадобится


    1. RodionGork Автор
      27.05.2025 06:00

      да, можно конечно - достаточно запустить таймер где-нибудь на 4 МГц

      просто для in-system programming в этом не очень большой смысл - в целевом устройстве собственный генератор или кварц должен быть иначе после отцепляния ардуины оно не будет работать :(