Картинка для привлечения внимания — xkcd
Представьте себе, что вы попали на необитаемый остров. И вам жизненно необходимо запрограммировать микроконтроллер. Зачем, спросите вы? Ну, допустим, чтобы починить аварийный радиомаяк, без которого шансы на спасение резко падают.
Радуясь, что еще не забыли курс ассемблера, вы кое-как написали программу палочкой на песке. Среди уцелевших вещей каким-то чудом оказалась распечатка документации на контроллер (хорошо, что вы еще не успели пустить её на растопку!), и программу удалось перевести в машинные коды. Осталась самая ерунда — прошить её в контроллер. Но в радиусе 500 километров нет ни одного программатора, не говоря уже о компьютерах. У вас только источник питания (батарея из
Как же прошить МК фактически голыми руками?
В качестве подопытного будет выступать МК ATtiny13 фирмы Atmel. Описанная методика работает практически с любым контроллером семейства AVR, разве что коды команд могут незначительно отличаться.
Интерфейс
Самым распространенным и удобным интерфейсом для прошивки AVR является SPI (Serial Peripheral Interface). Для подключения по SPI нужно всего четыре провода, не считая земли:- SCK — тактовый сигнал, синхронизирует все операции обмена данными;
- MOSI (Master Out Slave In) — линия данных от ведущего устройства к ведомому;
- MISO (Master In Slave Out) — линия данных, наоборот, от ведомого устройства к ведущему;
- RESET — для разрешения прошивки по SPI нужно подать логический «0» на этот вывод.
Таким образом, нам необходимо сформировать три сигнала и (необязательно) прочитать один. Вот так выглядит простейшая схема для этого:
Рис. 1. Простейшая схема подключения по SPI.
Для своего же удобства можно добавить индикацию входных сигналов. Схема усложняется, но не чрезмерно:
Рис. 2. Схема с индикацией сигналов.
Защита от дребезга
К сожалению, просто используя кнопки для формированя сигналов SPI, хорошего результата мы не добьёмся. Причина этого — в неприятном явлении, которое называется дребезг контактов. При замыкании механические контакты соударяются, отскакивают друг от друга, и вместо одного импульса получается несколько. Для подавления дребезга придется собрать простую схему из пары логических элементов:
Рис. 3. RS-триггер для подавления дребезга.
Это RS-триггер, который переключается в состояние «1» в момент замыкания нижнего контакта переключателя и игнорирует остальные импульсы дребезга. Сброс триггера обратно в «0» происходит при замыкании верхнего контакта, то есть при отпускании кнопки.
«Ишь, разбежался!» — скажет читатель, — «Я же на необитаемом острове сижу. Где я тут возьму триггеры?» Хорошо, можно избавиться от дребезга и без электронных схем. Нужно только заменить «сухой» контакт на
Сигналы MOSI и RESET не требуют подавления дребезга, в отличие от SCK: здесь значение имеет только уровень сигнала в момент выборки, а не его фронты.
Как работает SPI
Рис. 4. Временная диаграмма работы SPI.
SPI является синхронным интерфейсом: все операции синхронизированы фронтами тактового сигнала (SCK), который вырабатывается ведущим устройством. Максимальная скорость передачи ограничена величиной 1/4 тактовой частоты контроллера. На минимальную же скорость нет никаких ограничений: без тактового сигнала обмен данными «замораживается», и интерфейс может оставаться в статическом состоянии сколь угодно долго.
Передача по SPI осуществляется в полнодуплексном режиме, по одному биту за такт в каждую сторону. По возрастающему фронту сигнала SCK ведомое устройство считывает очередной бит с линии MOSI, а по спадающему — выдает следующий бит на линию MISO. Все внимание на рисунок 4.
Протокол прошивки
Все общение программатора с МК состоит из отправки 32-битных команд и приема ответов контроллера. Полный перечень команд есть в datasheet'е, а здесь перечислим, какие операции необходимо выполнить для прошивки МК:- Перевод контроллера в режим программирования;
- (опционально) Чтение идентификатора устройства;
- Стирание;
- Запись во flash;
- (опционально) Проверка записанного;
Рассмотрим каждый шаг подробно.
Включение режима программирования
Режим программирования включается подачей «0» на ногу RESET. Но есть некоторые тонкости. Atmel рекомендует сначала выставить на выводах RESET и SCK низкий уровень, а только потом подавать на контроллер питание. Если такой возможности нет, нужно после включения питания подать «0» на SCK, а затем положительный импульс на RESET:
Рис. 5. Перевод МК в режим программирования.
Но и это еще не все. Далее нужно передать команду на собственно включение режима программирования: 10101100 01010011 xxxxxxxx xxxxxxxx
Рис. 6. Команда «Program Enable».
Биты, обозначенные как x, могут быть любыми. Во время передачи третьего байта контроллер должен переслать обратно второй байт (01010011). Если это произошло, значит, все хорошо, команда принята, контроллер ждет дальнейших инструкций. Если ответ отличается, нужно перезагрузить МК и попробовать все сначала.
Проверка идентификатора
Рис. 7. Команда «Read Signature Byte».
Прежде чем что-либо писать в память МК, нужно убедиться, что перед нами именно та модель, которая нужна. Каждая модель контроллера имеет свой трехбайтный идентификатор (Signature). Прочитать его можно командами вида
00110000 000xxxxx xxxxxxbb xxxxxxxx
Вместо bb (третий байт команды) следует подставить 00 для первого байта идентификатора, 01 — для второго и 10 — для третьего. Соответствующий байт идентификатора будет передан контроллером при отправке 4-го байта команды.
Для ATtiny13 значение идентификатора равно 00011110 10010000 00000111 (0x1E 90 07).
Очистка контроллера
Рис. 8. Команда «Chip Erase».
Следующим шагом будет очистка памяти МК, которая осуществляется посылкой команды «Chip Erase»
10101100 100xxxxx xxxxxxxx xxxxxxxx
Этой командой выполняется стирание содержимого Flash и EEPROM (все ячейки будут содержать FF), а также снятие lock-битов, если они установлены.
Запись во flash-память
Память программ (Flash) в ATtiny13 состоит из 512 двухбайтных слов (1К байт). Адрес слова имеет разрядность 9 бит. Flash-память разделена на страницы, каждая страница имеет размер 16 слов (всего получается 32 страницы). Запись во flash осуществляется в два этапа.
Сначала необходимо загрузить данные в буфер страницы, для этого используется команда «Load Program Memory Page»
01000000 000xxxxx xxxxbbbb iiiiiiii — для загрузки младшего байта слова, и 01001000 000xxxxx xxxxbbbb iiiiiiii — для загрузки старшего.
4 младших бита 3-го байта команды bbbb — адрес слова на странице, iiiiiiii — загружаемый байт. Сначала всегда должен загружаться младший байт слова, а затем — старший байт того же слова.
Рис. 9. Команда «Load Program Memory Page».
После того, как буфер страницы загружен, нужно выполнить команду «Write Program Memory Page» 01001100 0000000a bbbbxxxx xxxxxxxx для записи страницы непосредственно в память контроллера.
Младший бит второго байта и старшие 4 бита третьего a:bbbb — пятибитный номер страницы для записи.
Рис. 10. Команда «Write Program Memory Page».
Все это выглядит довольно запутанно, но ничего сложного нет. Адрес любого байта памяти программ состоит из 10 бит: ppppp:bbbb:w, где
ppppp — номер страницы (используется в команде «Write Program Memory Page»);
bbbb — адрес слова на странице (в команде «Load Program Memory Page»);
w — бит, определяющий старший или младший байт в слове (зашифрован в первом байте команды «Load Program Memory Page»).
Чтение flash
Рис. 11. Команда «Read Program Memory».
После записи прошивки в МК неплохо бы проверить записанное, так как никакой проверки целостности данных не выполнялось. Единственный способ проверки состоит в том, чтобы прочитать весь объем flash-памяти и сравнить с оригиналом.
Читать память программ легче, чем писать в нее. Забудьте про страничную организацию, чтение выполняется побайтно. Команда «Read Program Memory» выглядит так:
00100000 0000000a bbbbbbbb xxxxxxxx — для чтения младшего байта слова, и 00101000 0000000a bbbbbbbb xxxxxxxx — для старшего.
Младший бит второго байта и весь третий байт a:bbbbbbbb — адрес слова в памяти. Прочитанный байт возвращается во время передачи 4-го байта команды.
Завершение программирования
Пожалуй, самая простая операция. Чтобы завершить программирование и перевести МК в рабочий режим, достаточно подать на RESET логический уровень «1». Контроллер запустится и будет работать по новой программе.
Практика
Настало время воспользоваться полученными знаниями на практике. Жертва эксперимента — ATtiny13 — воткнут в макетную плату, рядом собран формирователь сигналов, всё готово:Рис. 12. Экспериментальная схема.
Шить будем программу вида «проще некуда»:
ldi R24, 0x02
out DDRB, R24
out PORTB,R24
L1: rjmp L1
Всё, что она делает — это выдает единицу на ногу PB1 и уходит в бесконечный цикл. В машинных кодах она занимает всего четыре слова:
E082 BB87 BB88 CFFF
Для прошивки её в контроллер необходимо набрать следующие команды:
1010 1100 0101 0011 0000 0000 0000 0000 // program enable
1010 1100 1000 0000 0000 0000 0000 0000 // chip erase
0100 0000 0000 0000 0000 0000 1000 0010 // load addr.0000 low byte 82
0100 1000 0000 0000 0000 0000 1110 0000 // load addr.0000 high byte E0
0100 0000 0000 0000 0000 0001 1000 0111 // load addr.0001 low byte 87
0100 1000 0000 0000 0000 0001 1011 1011 // load addr.0001 high byte BB
0100 0000 0000 0000 0000 0010 1000 1000 // load addr.0010 low byte 88
0100 1000 0000 0000 0000 0010 1011 1011 // load addr.0010 high byte BB
0100 0000 0000 0000 0000 0011 1111 1111 // load addr.0011 low byte FF
0100 1000 0000 0000 0000 0011 1100 1111 // load addr.0011 high byte CF
0100 1100 0000 0000 0000 0000 0000 0000 // write page
Ключ на старт, поехали!
Всего 425 нажатий, и МК оживает. Теперь вас точно найдут и спасут с этого проклятого острова.