Найти в Дзене
Разумный мир

Загрузчик, как много в этом слове... Часть 3. Микроконтроллеры

Оглавление

Нам осталось рассмотреть последний случай - загрузчик в микроконтроллерах.Во всяком случае, это последний случай в запланированном цикле статей. И сегодняшняя статья будет проще, чем статья о связывающем перемещающем загрузчике. Но при этом и более специфична, так как с микроконтроллерами работает меньше читателей, чем с обычными ЭВМ (пусть и ПК).

Микроконтроллер это микросхема, специализированная однокристальная ЭВМ (они так раньше и назывались) с дополнительными внешними устройствами в одном корпусе. Из всех особенностей микроконтроллеров для себя будет важной только организация памяти. Как и в универсальных ЭВМ память микроконтроллера разделена на ПЗУ и ОЗУ. При этом ОЗУ используется как память данных (переменных), а программный код хранится и выполняется в ПЗУ.

Загрузка программ в ПЗУ микроконтроллера выполняется редко, иногда всего один раз, при изготовлении устройства. Этот процесс часто называется просто - "прошивка". При разработке и отладке устройств на микроконтроллерах прошивку, программу в ПЗУ, необходимо заменять - "перепрошивать" микроконтроллер. Для отладочных целей иногда выпускаются специальные отладочные микросхемы в которых вместо ПЗУ устанавливается ОЗУ. Но такие микросхемы значительно дороже, при выключении питания программа уничтожается, и доступны они не для всех микроконтроллеров.

При эксплуатации устройства тоже иногда может возникать необходимость обновления программы в микроконтроллере. Причем не силами сервисного инженера, а силами самого пользователя. И у пользователя может отсутствовать и специализированное оборудование (программатор) и необходимые знания.

Кроме того, в микроконтроллерах могут быть и специализированные области памяти. Например, информация о конфигурации, настраиваемая программистом, служебные идентификаторы, EEPROM, и так далее.

Для облегчения загрузки программ в микроконтроллер, причем без использования специальных программаторов, и предназначен загрузчик - специализированная программа в памяти микроконтроллера. Давайте попробуем заглянуть внутрь микроконтроллера и посмотреть, как такой загрузчик устроен.

Классические способы загрузки программ в микроконтроллеры - программаторы

Сам термин "прошивка" появился очень давно и относился к ПЗУ на ферритовых сердечниках (трансформаторах)

Принцип работы ферритового ПЗУ. Иллюстрация моя
Принцип работы ферритового ПЗУ. Иллюстрация моя

Каждый бит такого ПЗУ представлял из себя трансформатор. Выходная обмотка (на иллюстрации показана сверху) подключалась к усилителям формирователям. Входная обмотка была токовой, один проводник, который пропускался через сердечник если бит должен быть единичным, или обходил сердечник, если бит должен быть нулевым. На иллюстрации показано, как входная обмотка проходит через сердечники для формирования тетрады 1011.

Количество проводников, входных обмоток, определяется объемом ПЗУ. Например, если ПЗУ хранит 512 байт, то будет иметь 8 сердечников и 8 выходных обмоток с усилителями, 512 проводников (входных обмоток). Разумеется, на входе будет адресный дешифратор, но нам это не важно сегодня.

Проводники входных обмоток протаскивали через сердечники. Это было похоже на шитье, только вместо нити проводник, а вместо иглы специальный челнок. Отсюда и термин.

С развитием технологий появились более компактные ПЗУ. Сначала с расплавляемыми (разрушаемыми) перемычками. Например, 556РТ4 или 556РТ5. Такие ПЗУ программировались только один раз, изменить информацию было невозможно. Немного позже появились ПЗУ, информацию в которых можно было изменять. Например, 573РФ1 с УФ стиранием.

Появились и ПЗУ с возможностью электрического стирания информации. Например, 573РР2. Дальнейшее развитие этого направления привело к появлению привычных сегодня EEPROM/Flash. Отдельным направлением стали ПЗУ FRAM, которые используются, например, в MSP430.

Давайте абстрагируемся от технологии ПЗУ и посмотрим, как можно загрузить программу в микроконтроллер, прошить его. При том, что микроконтроллер, в отличии от микропроцессора, часто не имеет внешних шин адреса/данных. А для прошивки часто требовалось и повышенное напряжение.

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

Пример подключения программатора к микроконтроллеру в режиме параллельного программирования. Иллюстрация моя
Пример подключения программатора к микроконтроллеру в режиме параллельного программирования. Иллюстрация моя

В данном случае, порт 0 микроконтроллера используется программатором для задания адреса ПЗУ, а порт 1 для как шина данных. Логика перевода микроконтроллера в режим программирования не показана. Вход сброса микроконтроллера одновременно является входом высокого напряжения (например, 12 В) программирования.

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

Если прошивку нужно изменить, микроконтроллер требуется снять с платы. Это не очень сложно, если он установлен в панельку. И значительно сложнее, если впаян. Разумеется, речь идет только о микроконтроллерах, которые могут программироваться более одного раза.

Большое количество выводов для параллельного программирования является проблемой для микроконтроллеров с малым количеством выводов. Поэтому естественным шагом был переход к передаче адреса и данных в последовательном виде. Причем можно и адрес, и данные, передавать через один и тот же вывод микросхемы, последовательно друг за другом.

Пример подключения программатора к микроконтроллеру в режиме последовательного программирования. Иллюстрация моя
Пример подключения программатора к микроконтроллеру в режиме последовательного программирования. Иллюстрация моя

Теперь для загрузки программы в микроконтроллер нужно всего 3 вывода, если по прежнему требуется источник высокого напряжения. Если высокое напряжение для программирования не требуется, или формируется внутри самого микроконтроллера, то достаточно всего пары выводов. Первый вывод для передачи информации в последовательном коде, второй для тактовых импульсов.

И это не просто небольшое эволюционное изменение. Это дает возможность загружать программу в микроконтроллер уже установленный в устройство, и возможность изменять прошивку.

Это называется внутрисхемным программированием, но аббревиатуры могут быть разными. Для Microchip это называется ICSP, для STM8 - SWI, для MSP430 - SBW. Конкретная реализация нам сегодня не интересна. Важно, что по прежнему требуется специальный программатор.

Да, теперь сервисный инженер может обновить прошивку устройства непосредственно в месте его установки. Или можно сделать универсальное устройство, конкретный функционал которого определяется прошивкой в момент продажи/поставки. Но обновление прошивки самим пользователем все еще является проблемой. Прежде всего тем, что требуется приобретение программатора, часто довольно дорогого.

Есть и еще одна проблема, не видимая на первый взгляд. Пользователь сможет не только обновить прошивку, но и дизассемблировать ее. Ведь стандартные программаторы просто загружают программу в микроконтроллер в том виде, как есть. Сама возможность прочитать программу из микроконтроллера может быть заблокирована, но она ведь уже и так есть у пользователя.

На сцене появляется загрузчик

Нам нужно как то отказаться от жестких протоколов обмена, которые задаются схемотехникой микроконтроллера. Это позволит обойтись и без специализированного программатора. Причем программаторы для разных семейств тоже разные.

Отказ от заданных аппаратно протоколов обмена обозначает переход от аппаратных решений к программным. То есть, нам нужно в памяти микроконтроллера, в ПЗУ, выделить место под размещение специализированной программы, которая сможет обмениваться с внешним миром и осуществлять запись/изменение другой части ПЗУ - собственно основной программы.

Вот эта специализированная программа и называется загрузчиком. Как и ранее рассмотренные начальный загрузчик и связывающий загрузчик, загрузчик в микроконтроллере предназначен для получения программы "извне" и помещения ее в память машины для последующего выполнения. Только теперь сама процедура записи в память будет сложнее, так как вместо ОЗУ установлено ПЗУ.

При этом нам сегодня не важно, какой именно канал связи с внешним миром используется. Это может быть UART, например, в виде RS-232 или RS-485. Это может быть SPI или 1-Wire. Это может быть карта памяти, на которую записывается обновление прошивки. Важно только то, что загрузчик может получить/найти нужную информацию.

Например, несколько устройств, расположенных на значительном удалении, может быть подключена по RS-485 к ЭВМ, которая ими управляет. И обновление прошивки может быть выполнено программой на этой ЭВМ, которая будет взаимодействовать с загрузчиком в микроконтроллерах. Или автономное устройство может быть временно подключено по USB к ПК, на котором запущена программа обновления прошивки. Использование карты памяти ничего не меняет. Просто загрузчику потребуется разобраться с файловой системой и найти файл прошивки.

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

Возникает вопрос, почему нельзя всегда использовать загрузчик, раз он дает такие преимущества? Есть две основные причины. Во первых, загрузчик занимает место в памяти микроконтроллера. И чем он сложнее, тем больше памяти требует для своего размещения. Во вторых, микроконтроллер должен обеспечивать возможность записи в память программ средствами собственно программы.

Как это работает

Давайте сначала рассмотрим абстрактный микроконтроллер с аппаратной поддержкой работы загрузчика

Микроконтроллер с аппаратной поддержкой работы загрузчика. Иллюстрация моя
Микроконтроллер с аппаратной поддержкой работы загрузчика. Иллюстрация моя

Аппаратная поддержка, в данном случае, проста. Во первых, есть регистр размера загрузчика, который скорее всего располагается в области конфигурационной информации, которую задает программист при разработке программы устройства. Этот регистр определяет своего рода водораздел между программой-загрузчиком и прикладной программой. Регистр размера загрузчика может задавать не конкретный адрес раздела, а код размера. Например, в блоках по 512 байт.

Во вторых, есть бит конфигурации, который определяет адрес старта микроконтроллера после сброса или включения питания. Например, если бит сброшен, то после сброса управление передается прикладной программе, если установлен, загрузчику. Бит точки старта не обязательно может быть битом конфигурации. Этот бит, например, может устанавливаться/сбрасывать, например, при удержании вывода сброса в течении нескольких секунд после подачи питания.

А если микроконтроллер не имеет аппаратной поддержки загрузчика, но все таки имеет возможность записи в память программ из самой программы? В этом случае при включении питания управление будет всегда получать загрузчик и предопределенной границы между загрузчиком и программой не будет. В начале работы загрузчик будет, например, проверять какой-либо специальный признак, например, состояние вывода порта (перемычки) и либо продолжать работу, либо передавать управление программе.

Однако, аппаратное разделение ПЗУ на память программ и память загрузчика позволяет, например, выполнять обязательное стирание памяти программ за одну операцию. И эта операция не затронет сам загрузчик. Без аппаратной поддержки загрузчика это сделать не получится, так как будет уничтожен и сам загрузчик.

Производитель микроконтроллера может записывать в ПЗУ некий простой стандартный вариант загрузчика. Но разработчик устройства на микроконтроллере имеет возможность написать собственный загрузчик, который обладает требуемой функциональность. Разумеется, для замены загрузчика в микроконтроллере потребуется программатор. Но у разработчика то он есть, а пользователь будет использовать загрузчик.

Работа загрузчика состоит из двух частей:

  • взаимодействие с внешним миром
  • запись программы в память микроконтроллера

И начать нам придется с работы с памятью программ, так как она является первичной.

Запись программы в ПЗУ программ

ПЗУ программ в современных микроконтроллерах чаще всего не предусматривает записи отдельных байт. Обычно память программ разделена на небольшие блоки и записать можно только блок целиком. При этом читать память можно не блоками, а отдельными байтами. Таким образом, загрузчик сначала должен сформировать, обычно в ОЗУ, блок информации для записи в ПЗУ.

Кроме того, доступ к памяти программ на запись обычно требует выполнения специальной последовательности действий, которая позволяет разблокировать память на время записи одного блока. Это сделано для защиты от случайной модификации памяти программ. Но и это еще не все. Блок памяти нужно сначала стереть, иначе запись в ПЗУ не будет выполнена.

В упрощенном и обобщенном виде процедура записи в ПЗУ программ одного блока будет такой (при отсутствии шифрования):

  1. Если требуется изменить не весь блок, а только его часть, прочитать в буфер в ОЗУ изменяемый блок ПЗУ
  2. Изменить считанный на первом шаге информационный блок во временном буфере в ОЗУ или сформировать новый блок
  3. Разблокировать доступ к ПЗУ и стереть выбранный блок в ПЗУ
  4. Разблокировать доступ к ПЗУ и переписать информационный блок из временного буфера в ОЗУ в ПЗУ.
  5. Прочитать только что записанный блок из ПЗУ и сравнить его с буфером в ОЗУ. Если информация не совпадает, зафиксировать ошибку и остановить работу.

Во многих случаях программа, которую загрузчик записывает в ПЗУ уже организована в блоки соответствующего размера. Поэтому дополнительных действий по частичному изменению содержимого блока ПЗУ не требуется. Но если нужно изменить только несколько байт, например, для исправления ошибки, нет смысла перезаписывать все ПЗУ.

Взаимодействие с внешним миром

Для простоты рассмотрим вариант получения команд и данных по одному из вариантов UART (RS-232 или RS-485). Не смотря на то, что обмен данными можно вести в двоичном режиме, достаточно часто используют текстовый формат.

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

Поэтому загрузчик принимает по линии связи команды и дает на них ответы. Команды могут быть, например, такими:

  • E adr1:adr2. Стирание ПЗУ начиная с адреса adr1 и заканчивая адресом adr2. Просто стирание. После завершения операции загрузчик может ответить ОК, если стирание успешно, или ER, если возникла ошибка
  • W adr1:adr2 <блок данных>. Запись передаваемых как <блок данных> байт в ПЗУ по указанным адресам. Данные передаются в текстовом виде как шестнадцатиричные числа. Ответ загрузчика аналогичен ответу для команды стирания
  • R adr1:adr2. Чтение блока ПЗУ по указанным адресам. В ответ загрузчик передает не только статус завершения операции, но и шестнадцатиричные числа считанного блока ПЗУ. Это может использоваться для проверки корректности записи
  • V adr1:adr2 <блок данных>. Сравнить блок в ПЗУ с передаваемыми в виде блока данных байтами

Заключение

Загрузчик в микроконтроллере чаще всего является достаточно простой и компактной программой. И написать такую программу может почти любой программист, который разрабатывает программы для микроконтроллеров. При этом требуется учитывать довольно много нюансов работы конкретного микроконтроллера и понимать, как работают его компоненты.

Функции загрузчика тоже просты. По сути, просто прием по линии связи данных и запись их в ПЗУ. Конечно, загрузчики работающие с файловой системой, например, на карте памяти, значительно сложнее, но суть та же самая.

Приглашаю читателей посетить дружественный канал

Робототехника | Яндекс Дзен

До новых встреч!