Ви плануєте створити власний завантажувач? Тоді цей документ містить відповіді на багато питань, які в зв'язку з цим у вас з'являться. Більшість понять, які охоплюються тут не порушені досить докладно в даташітах на мікроконтролери, але тим не менше ці поради важливі для проектування надійних бутлоадер. Велика частина представленої тут інформації можна знайти на avrfreaks.net розкиданої по різних гілках. Наведені нижче питання впорядковані від простого до складного і для створення життєздатного завантажувача, ви, ймовірно, захочете зрозуміти відповіді, принаймні на перші 11 запитань.
Для успішного оволодіння матеріалом передбачається, що ви вже знаєте для чого потрібен завантажувач, володієте програмуванням на мові С, і знайомі зі створенням звичайних 8-розрядних додатків AVR. Цей документ заснований на інструменті AVR-GCC із застосуванням бібліотек AVR-Libc і на типовому Makefile. Якщо ви використовуєте інший інструмент, то все що описано в даному документі залишається в силі, але і код Makefile приклади повинні бути
відповідним чином адаптовані під ваш інструмент.
Також зверніть увагу, що приклади засновані на AT90USB162 і повинні бути пристосовані до вашої AVR до їх повторного використання.
З чого почати?
По суті, завантажувач - це просто звичайна програма AVR, яке розташоване в спеціальній області флеш-пам'яті. У простій формі, додаток можна зробити завантажувачем AVR шляхом вказівки додаткового прапора компонувальнику, який необхідно додати в свій Makefile:
Що таке області NRWW і RWW?
Ось що вам дійсно необхідно знати про особливості областей RWW і NRWW:
- Завантажувач може перепрограмувати додатки, розташовані в області RWW, при цьому виконання завантажувача не переривається;
- Завантажувач не може оновлювати свій код також просто як код додатка;
- Додатки дуже рідко оновлюють завантажувач;
- Додатки можуть оновити себе, але краще щоб це робив завантажувач.
Як завантажувач оновлює додаток?
Яким чином завантажувач отримає програму для перепрограмування контроллера залежить від вас. Поширеними каналами зв'язку є UART і USB. Протокол обміну даними по каналу може бути власний або стандартний, на зразок AVR109 або DFU. При реалізації стандартних протоколів можна використовувати вже існуючі інструменти, такі як AVR Studio, що працює по протоколу AVR109 або Atmel's Flip працює по протоколу DFU. Проте ці стандартні протоколи як правило трохи роздуті, а при реалізації простого користувача протоколу завантажувач як правило буде менше розміром і чистіше. Правда в такому випадку вам знадобиться також розробити власне рішення для передачі даних завантажувачу.
Зазвичай завантажувачу передається за раз одна сторінка пам'яті. Завантажувачу необхідно записати ці
сторінки в область флеш, призначену для застосування, тобто в RWW. AVR-Libc надає заголовки
Чи може завантажувач оновлювати значення фьюз?
З AVR це неможливо. Для цього вам буде потрібно зовнішній програматор.
Для чого потрібен BOOTLOADER_SECTION з ?
Незважаючи на свою назву макрос BOOTLOADER_SECTION не є корисним під час написання завантажувача. Цей макрос просто допомагає вам перепризначити позицію вашої функції в розділ NRWW флеш-пам'яті. Ймовірно, для цього макросу більш підходящою назвою є щось на зразок NRWW_SECTION.
Макрос BOOTLOADER_SECTION може знадобитися коли програму необхідна функція перепрограмування власного додатка, тому що така функція буде працювати тільки якщо вона буде виконана з разделаNRWW. BOOT_LOADER_SECTION насправді просто створює спеціальну секцію для коду з назвою «.bootloader» (інша назва краще не використовувати), а потім додатковий прапор компоновщика позиціонує дану секцію десь в невикористаної області пам'яті розділу NRWW. Такий підхід не дуже часто застосовується на практиці.
Деякі люди схильні використовувати BOOTLOADER_SECTION для написання програми та завантажувача відразу в одній програмі, але це не дуже гарна ідея. Завантажувач хороший у вигляді автономних програм, які ніяк не залежать від самого додатка. Це буде більш надійним рішенням, при тому що якщо завантажувач буде окремим додатком, то і створювати його теж буде простіше.
Як прошити завантажувач для мікроконтролерів?
Як прошити відразу додаток і завантажувач?
Так як, сподіваюся, ви створили окремо додаток і завантажувач окремо, то в кінцевому підсумку у вас з'явиться два окремих шістнадцятирічних файлу з прошивками (наприклад, app.hex і boot.hex). Виникає питання: як залити їх обидві в AVR. Існує кілька варіантів:
У два етапи: Залийте завантажувач за допомогою зовнішнього програматора як описано в Питанні № 6. Можливо, вам при цьому буде потрібно також встановити необхідні значення фьюз включаючи BOOTSZ і BOOTRST. Після цього можна просто використовувати звичайний механізм зв'язку завантажувача для передачі і прошивки в мікроконтролер додатки.
В один етап: Об'єднати файли app.hex і boot.hex в один і використовувати зовнішній програматор для запису об'єднаного файлу в мікроконтролер. Можливо, вам при цьому буде потрібно також встановити необхідні значення фьюз включаючи BOOTSZ і BOOTRST.
Я думаю, що найпростіший спосіб об'єднати шістнадцятиричні файли за допомогою командного рядка srec_cat. Цей інструмент входить в набір інструментів srecord. Він поставляється з WinAVR і дуже легко встановлюється на Unix-подібних ОС. Ось команда, яку ви повинні при цьому використовувати:
Також ви можете вручну об'єднати файли app.hex і boot.hex файлів з невеликими правками:
Кожен шістнадцятковий файл має одну останню запис, в якій говориться, що «файл закінчується». Таким чином, після ручного об'єднання потрібно відредагувати об'єднаний шістнадцятковий файл - знайти в ньому запис закінчення файлу, розташовану в кінці app.hex (після об'єднання десь в середині файлу) і видалити її. Запис має тип 01. Байт типу записи в шістнадцятковому форматі Intel усе своєю чергою є 4-м байтом, тому запис насправді буде виглядати приблизно так: «: 00000001FF». Вся лінія (та що в середині, а не в кінці файлу) повинна бути видалена.
Чи можна в засобі завантаження використовувати переривання?
Так, для цього достатньо сказати процесору, щоб той використовував вектор переривань, розташований в області завантажувача а не в області програми. Для цього десь на початку коду завантажувача (до використання переривань), треба додати рядки на кшталт цих:
Після чого під час переривань програмний лічильник буде переведений в відповідне положення згідно з таблицею переривань завантажувача. Для нормальної роботи, наведена вище послідовність коду повинна бути скомпільована з включеною оптимізацією, також вручну потрібно впевнитися, що в результуючу збірку записані команди, що виконуються за 4 машинних циклу.
Що повинно завантажуватися в першу чергу - завантажувач або додаток?
У більшості випадків найкраще буде встановити фьюз BOOTRST для того щоб завантажувач запускався першим після перезавантаження мікроконтролера. Коли завантажувач запускається після перезавантаження першим ви зможете перезаліть програму незалежно від того пошкоджений код програми або він зовсім відсутній.
Для кращої роботи загрузчиков необхідно щоб вони представляли собою невеликі, надійні і рідко змінювані програми. З того дня як ви відправили свій пристрій в експлуатацію вам дійсно більше не захочеться змінити на ньому завантажувач. Додаток ж навпаки вимагає більше свободи в плані оновлення. Якщо в додатку виникає помилка, що приводить до відмови або зависання, або під час заливки прошивки відбудеться збій харчування надійний завантажувач, який буде запускатися першим зможе без проблем виправити дану ситуацію.
Якщо після перезавантаження пристрою запускається додаток, то для того щоб виправити проблеми ви можете використовувати зовнішній програматор. Але якщо у пристрої не передбачений інтерфейс для підключення програматора, то пристрій швидше за все доведеться викинути або перепоювати.
Як дізнатися завантажувачу коли запускати додаток?
Є багато можливостей. Якщо пристрій має кнопку або інший механізм введення, ви можете надіслати відповідний сигнал натисканням. Тоді завантажувач, як правило, запустить додаток відразу. А є наслiдком якщо кнопка буде не натиснута під час скидання, то завантажувач продовжує виконання. Це приклад того як працює завантажувач чіпа STK500.
Іншим поширеним рішенням для завантажувача є перевірка зовнішнього каналу зв'язку на спеціальний символ під час запуску. Знову ж, завантажувач, як правило, запустить додаток відразу, а продовжує виконуватися тільки після перевірки зовнішнього каналу зв'язку і отримання певної відповіді або пошуку очікуваних даних. Прикладом такого підходу є Atmel Butterfly.
У нашому випадку, жодне з цих рішень не підходять. Наш пристрій не має кнопок і ми хотіли б виконувати цю програму в звичайних умовах дуже швидко. Єдиний зовнішній канал зв'язку є USB, на жаль, пристрій USB вимагає деякий час (в процесор вираженні) перш ніж буде можливий обмін даними з хостом.
Так ось що ми зробили: Якщо припустити, що AVR має EEPROM, ви можете зберігати в ньому байтовое значення, яке вказує, коли завантажувач повинен продовжувати працювати, а коли запустити додаток. У цього підходу два етапи. Спочатку десь на початку коду завантажувача необхідно додати щось на зразок цього:
Потім десь подалі в додатку, в тій частині коду, при обробці якого ви вже точно впевнені, що додаток працює правильно додайте наступне:
Я також пропоную спосіб примусово запустити завантажувач з програми. Вам просто необхідно очистити байт APP_RUN і запобігти його перепис пізніше. Ми реалізували це в нашому додатку, а робиться це по команді, відправленої через USB з програми, запущеної на комп'ютері.
Один з недоліків такого підходу є те, що EEPROM компанії Atmel на більшості контролерів має ресурс в 100000 циклів запису / стирання. Оскільки цей підхід виробляє два циклу стирання / запису під час кожного пуску, то виходить ресурс пристрою обмежений 50 000 циклами перезавантаження. Для нашого застосування я прикинув, що тривалість життя пристрою становить в середньому 13,7 років при 10 перезагрузках день. Ця межа можна легко розширити якщо знос осередків EEPROM розподілити.
Як завантажувач запускає додаток?
Але це ще не все. Коли ви безпосередньо запустіть додаток зазначеним способом мікроконтролер НЕ буде скинутий в початковий стан як це відбувається після перезавантаження. Значення регістрів не очищати, а периферія не вимикається. Є два найпоширеніші способи впоратися з цим. Перший підхід полягає в навмисному провокуванні скидання за допомогою сторожового таймера. Це призведе до скидання мікроконтролера в початковий стан і відновить завантажувач (якщо у вас встановлений біт BOOTRST). Якщо умови, що викликало завантажувач більше немає (наприклад, ви більше не утримуєте кнопку), то завантажувач повинен негайно запустити додаток. Необхідно почати виконання програми якомога швидше, поки зміни торкнулись якомога менше ресурсів мікроконтролера. Ось приклад:
Додаток потім має явно конфігурувати всі ресурси що воно збирається використовувати.