Комп'ютери та сучасні гаджети

Грудень 2015 р.

1. Переваги запропонованого способу

Схеми пристроїв на мікроконтролерах (МК) зазвичай відрізняються поєднанням двох важкосумісних якостей: максимальною простотою та високою функціональністю. До того ж функціональність може надалі змінюватись і розширюватися без внесення будь-яких змін до схеми – шляхом лише заміни програми (перепрошивкою). Ці особливості пояснюються тим, що творці сучасних МК постаралися розмістити на одному кристалі все, що тільки може знадобитися розробнику електронного пристрою - принаймні настільки, наскільки це можливо. В результаті відбулося зміщення акценту зі схемотехнічного та монтажного на програмний. З використанням МК тепер менше доводиться "навантажувати" схему деталями, між компонентами стає менше сполук. Це, звичайно, робить схему більш привабливою для її повторення як досвідченими, так і електронниками-початківцями. Але, як завжди, за все доводиться платити. Тут також не обійшлося без своїх складнощів. Якщо купити новий МК, встановити його в правильно зібрану зі справних деталей схему та подати живлення, то нічого не вийде – пристрій не працюватиме. Мікроконтролеру потрібна програма.

Здавалося б із цим теж все просто – в інтернеті можна зустріти безліч схем з безкоштовними прошивками. Але тут з'являється одна проблема: прошивку потрібно якось "залити" в мікроконтролер. Для того, хто ніколи цим раніше не займався, таке завдання часто стає проблемою і головним фактором, що відштовхує, нерідко змушує відмовитися від принад використання МК і пошукати схеми на "розсипуху" і жорсткій логіці. Адже все не так вже й складно, як може здатися на перший погляд.

Проаналізувавши публікації в інтернеті, можна помітити, що ця проблема вирішується найчастіше одним із двох шляхів: покупкою готового програматора або виготовленням саморобного. При цьому схеми саморобних програматорів, що публікуються, дуже часто невиправдано складні - набагато складніше, ніж це дійсно необхідно. Звичайно, якщо передбачається щодня прошивати МК, краще мати "крутий" програматор. Але якщо потреба в такій процедурі виникає нечасто, час від часу, то можна взагалі обійтися без програматора. Ні, звичайно, не йдеться про те, щоб навчитися робити це силою думки. Мається на увазі, що розуміючи, як відбувається взаємодія програматора з мікроконтролером при записі та зчитуванні інформації в режимі його програмування, ми можемо обійтися підручними засобами ширшого призначення. Ці кошти мають замінити як програмну, і апаратну частини програматора. Апаратна частина повинна забезпечити фізичне з'єднання з мікросхемою МК, можливість подавати логічні рівні на його входи та зчитувати дані з його виходів. Програмна частина має забезпечити роботу алгоритму, керуючого усіма необхідними процесами. Зазначимо також, що якість запису інформації в МК не залежить від того, наскільки "крутий" у вас є програматор. Такого поняття, як "краще записалося" чи "гірше" не існує. Є лише два варіанти: "записалося" та "не записалося". Це тим, що безпосередньо процесом запису всередині кристала керує сам МК. Потрібно лише забезпечити йому якісне харчування (відсутність перешкод та пульсацій) та правильно організувати інтерфейс. Якщо за результатами контрольного зчитування помилок не виявлено, то все в порядку можна використовувати контролер за призначенням.

Для того, щоб, не маючи програматора, записати в МК програму, нам знадобиться перетворювач порту USB-RS232TTL і , а також . Конвертер USB-RS232TTL дозволяє за допомогою порту USB створити COM-порт, який відрізняється від "справжнього" лише тим, що на його входах та виходах використовуються логічні рівні TTL, тобто напруга в інтервалі від 0 до 5 вольт (докладніше можна почитати у статті " "). Такий конвертер у будь-якому випадку корисно мати в "господарстві", тому якщо у вас його ще немає, неодмінно варто придбати. Що стосується логічних рівнів, то в нашому випадку TTL - це навіть перевага перед звичайним COM-портом, тому що входи та виходи такого порту можна безпосередньо підключати до будь-якого мікроконтролера, що живиться від напруги 5 В, у тому числі ATtiny та ATmega. Але не намагайтеся використовувати стандартний COM-порт - там використовуються напруги в інтервалі від -12 до +12 В (або -15 ... +15В). Безпосереднє з'єднання з мікроконтролером у цьому випадку неприпустимо!

Ідея створення скрипта для програми "Перпетуум М", що реалізує функції програматора, виникла після ознайомлення з рядом публікацій в інтернеті, що пропонують ті чи інші рішення щодо прошивки МК. У кожному випадку виявлялися серйозні недоліки чи надмірні складності. Часто траплялися схеми програматорів, що містять у собі мікроконтролер і при цьому цілком серйозно давалися поради типу: "...а щоб запрограмувати мікроконтролер для цього програматора нам знадобиться... правильно - інший програматор!". Далі пропонувалося сходити до друга, пошукати платну послугу тощо. Якість програмного забезпечення, що розповсюджується в мережі для цих цілей, також не вразило - помічено безліч проблем як з функціональністю, так і з "каламутністю" інтерфейсу користувача. Найчастіше потрібно витратити, щоб зрозуміти, як використовувати програму - її необхідно вивчати навіть задля здійснення найпростіших дій. Інша програма може довго і старанно щось робити, але про те, що нічого в МК не записується, користувач дізнається тільки після завершення всієї прошивки та наступного контрольного зчитування. Зустрічається і така проблема: користувач намагається вибрати зі списку кристалів, що підтримуються, свій МК, а його в списку немає. У цьому випадку скористатися програмою не вдасться - внесення до списку відсутніх МК, як правило, не передбачено. Крім того, ручний вибір контролера зі списку виглядає дивно, якщо врахувати, що програматор у багатьох випадках може сам визначити тип МК. Все це сказано не для того, щоб облити брудом існуючі продукти, а для того, щоб пояснити причину появи скрипту до програми "Перпетуум М", що описується у цій статті. Проблема дійсно існує, і вона стосується насамперед новачків, яким не завжди вдається подолати цю "стіну", щоб зробити свій перший крок у світ мікроконтролерів. У запропонованому скрипті враховано недоліки, виявлені інших програмах. Реалізовано максимальну "прозорість" роботи алгоритму, гранично простий інтерфейс користувача, що не вимагає вивчення і не залишає шансу заплутатися і "не туди натиснути". За відсутності потрібного МК серед підтримуваних можна самостійно додати його опис, взявши потрібні дані з документації, завантаженої з сайту розробника МК. І, найголовніше - скрипт відкритий для вивчення та модифікації. Кожен бажаючий може, відкривши в текстовому редакторі, вивчати і правити його на власний розсуд, змінюючи на свій смак існуючі функції та додаючи недостатні.

Першу версію скрипту було створено у червні 2015 року. У цій версії реалізована тільки підтримка Atmel'івських МК серій ATtiny та ATmega з функціями запису/читання флеш-пам'яті, з налаштуванням конфігураційних біт, з автоматичним визначенням типу контролера. Запис та читання EEPROM не реалізовані. Були плани доповнити функціональність скрипту читання EEPROM, реалізувати підтримку PIC-контролерів і т. д. З цієї причини скрипт досі не був опублікований, але через брак часу здійснення задуманого затяглося, і щоб краще не ставало ворогом хорошого, вирішено опублікувати наявну версію. реалізованих функцій виявиться недостатньо, прошу не засмучуватися.У цьому випадку ви можете спробувати самостійно додати потрібну функцію.Не стану приховувати: ідея створення даного скрипту спочатку несе в собі ще й освітній зміст. зможете глибше зрозуміти роботу МК в режимі програмування, щоб у майбутньому не опинитися в становищі дівчини перед автомоблем, що зламався, задумливо розглядає його нутрощі і не розуміє, чому "не їде".

2. Інтерфейс МК як програмування

Існує кілька різних способів перевести контролер у режим програмування та працювати з ним у цьому режимі. Найпростішим у реалізації для контролерів серій ATtiny та ATmega є, мабуть, SPI. Ним і скористаємося.

Але, як розпочати розгляду сигналів, необхідні формування SPI, зробимо ряд застережень. Мікроконтролер має конфігураційні біти. Це щось на кшталт тумблерів, перемикання яких дозволяє змінювати деякі властивості мікросхеми відповідно до потреб проекту. Фізично це осередки енергонезалежної пам'яті, на зразок тих, у які записується програма. Різниця в тому, що їх дуже мало (до трьох байт для ATmega), і вони не входять до адресного простору будь-якої пам'яті. Запис та читання конфігураційних даних виконується окремими командами режиму програмування МК. Зараз важливо відзначити, що деякі конфігураційні біти впливають на можливість використання SPI. При деяких значеннях може виявитися, що SPI не можна використовувати. Якщо вам трапиться такий мікроконтролер, то метод, який пропонується в даній статті, не допоможе. У цьому випадку доведеться або змінити налаштування конфігураційних біт програматорі, який підтримує інший режим програмування, або використовувати інший мікроконтролер. Але ця проблема стосується лише тих, хто був у вживанні МК, або тих, з якими вже хтось невдало "погрався". Справа в тому, що нові МК поставляються з налаштуваннями конфігураційних біт, що не перешкоджають використанню SPI. Це підтверджується і результатами випробувань скрипта-програматора для програми "Перпетуум М", під час яких були успішно прошиті чотири різні МК (ATmega8, ATmega128, ATtiny13, ATtiny44). Усі вони були нові. Початкова настройка конфігураційних біт відповідала документації та не заважала використанню SPI.

Зважаючи на сказане вище, слід звертати увагу на такі біти. Біт SPIEN у явному вигляді дозволяє або забороняє використання SPI, отже, у нашому випадку його значення має бути вирішальним. Біт RSTDISBL здатний перетворити один із висновків мікросхеми (заздалегідь зумовлений) у вхід сигналу "скидання", або не перетворити (залежно від записаного в цей біт значення). У нашому випадку вхід "скидання" необхідний (за його відсутності не вдасться перевести МК в режим програмування через SPI). Існують ще біти групи CKSEL, що задають джерело тактового сигналу. Вони не перешкоджають використанню SPI, але їх теж необхідно мати на увазі, тому що при повній відсутності тактових імпульсів, або при їх частоті нижче за допустиму для заданої швидкості SPI, також нічого хорошого не вийде. Зазвичай у нових МК, мають внутрішній RC-генератор, біти групи CKSEL налаштовані його використання. Нас це цілком влаштовує – тактування забезпечене без будь-яких додаткових зусиль з нашого боку. Ні кварцовий резонатор припаювати, ні зовнішній генератор не потрібно підключати. Якщо ж зазначені біти містять інше налаштування, доведеться подбати про тактування відповідно до налаштування. У цьому випадку може знадобитися підключення до МК кварцового резонатора або тактового зовнішнього генератора. Але в рамках цієї статті ми не розглядатимемо, як це робиться. Приклади підключення МК для програмування, що містяться в цій статті, розраховані на найпростіший випадок.

Мал. 1. Обмін даними по SPI у режимі програмування

Тепер звернемося до малюнка 1, взятому з документації МК ATmega128A. На ньому показаний процес передачі одного байта МК і одночасного прийому одного байта з МК. Обидва ці процесу, як бачимо, використовують одні й самі тактові імпульси, які від програматора в мікроконтролер з його вхід SCK - одне із висновків мікросхеми, котрій у режимі програмування по SPI відведена така роль. Ще дві сигнальні лінії забезпечують прийом та передачу даних по одному біту за такт. Через вхід MOSI дані надходять у мікроконтролер, а з виходу MISO знімаються дані, що зчитуються. Зверніть увагу на дві пунктирні лінії, проведені від SCK до MISO та MOSI. Вони показують, коли мікроконтролер "ковтає" виставлений на вході MOSI біт даних, і коли сам виставляє на вихід MISO свій біт даних. Все досить просто. Але щоб ввести МК в режим програмування, нам ще знадобиться сигнал RESET. Не забудемо також про загальний провід GND та живлення VCC. Загалом виходить, що до мікроконтролера для його прошивки SPI потрібно підключити всього 6 проводків. Нижче розберемо це докладніше, а поки додамо, що обмін даними з МК в режимі програмування SPI виконується пакетами по 4 байти. Перший байт кожного пакета переважно повністю відводиться під кодування команди. Другий байт залежно від першого може бути продовженням коду команди або частиною адреси, а може мати довільне значення. Третій байт використовується переважно передачі адрес, але у багатьох командах може мати довільне значення. Четвертий байт зазвичай передає дані або має довільне значення. Поруч із передачею четвертого байта у деяких командах приймаються дані, які з МК. Подробиці кожної команди можна знайти в документації на контролер у таблиці під назвою "SPI Serial Programming Instruction Set". Поки зазначимо лише, весь обмін з контролером побудований з послідовності 32-бітних пакетів, у кожному з яких передається трохи більше одного байта корисної інформації. Це не дуже оптимально, але загалом працює непогано.

3. Підключення МК для програмування

Щоб забезпечити подачу на входи мікроконтролера всіх необхідних сигналів для організації інтерфейсу SPI та читання даних з виходу MISO, не обов'язково створювати програматор. Це легко здійснити за допомогою звичайного конвертера USB-RS232TTL.

В інтернеті часто можна зустріти інформацію про те, що такі конвертери є неповноцінними, що з ними нічого серйозного зробити не можна. Але щодо більшості моделей конвертерів така думка є помилковою. Так, існують у продажу конвертери, у яких доступні не всі входи та виходи в порівнянні зі стандартним COM-портом (наприклад, тільки TXD і RXD), що мають при цьому нерозбірну конструкцію (мікросхема залита пластмасою - неможливо дістатися її висновків). Але такі й купувати не варто. У деяких випадках отримати відсутні входи і виходи порту можна, підпаявши проводки безпосередньо до мікросхеми. Приклад такого "удосконаленого" конвертера показано на малюнку 2 (мікросхема PL-2303 - докладніше про призначення її висновків у статті ""). Це одна з найдешевших моделей, але має свої переваги при використанні в саморобних конструкціях. Широко поширені і повнофункціональні шнури-перехідники зі стандартним дев'ятиконтактним роз'ємом на кінці, як у COM-порту. Від звичайного COM-порту вони відрізняються лише рівнями TTL та несумісністю із застарілим програмним забезпеченням та деяким старим обладнанням. Можна ще відзначити, що шнури на мікросхемі CH34x на різних екстремальних тестах показують себе набагато надійнішими та стабільнішими порівняно з перетворювачами на PL-2303. Втім, при звичайному використанні різниця не помітна.

При виборі конвертера USB-RS232TTL слід також звертати увагу на сумісність його драйвера з версією операційної системи, що використовується.

Розглянемо докладніше принцип з'єднання мікроконтролера та конвертера USB-RS232TTL на прикладі чотирьох різних моделей МК: ATtiny13, ATtiny44, ATmega8 та ATmega128. На малюнку 3 показано загальну схему такого з'єднання. Вас може здивувати, що сигнали RS232 (RTS, TXD, DTR та CTS) використовуються не за призначенням. Але не варто турбуватися про це: програма "Перпетуум М" здатна працювати з ними безпосередньо - встановлювати значення на виходах і читати стани входу. У всякому разі, широко поширені конвертери USB-RS232TTL на мікросхемах CH34x і PL-2303 таку можливість забезпечують - це перевірено. З іншими популярними конвертерами проблем не повинно бути, оскільки для доступу до порту використовуються стандартні функції Windows.

Резистори, показані на загальній схемі, в принципі можна не встановлювати, але краще встановити. Яке їхнє призначення? Використовуючи ТТЛ"івські входи і виходи конвертера і п'ятивольтне харчування мікроконтролера, ми тим самим позбавляємося необхідності узгодження логічних рівнів - все і так цілком коректно. Значить, з'єднання можуть бути безпосередніми. Але під час експериментів буває всяке. Саме в те місце, куди вона ніяк не могла б впасти, і замкнути те, що в жодному разі не можна замикати.В ролі "викрутки", звичайно, може виявитися все, що завгодно. одне їх призначення полягає в усуненні можливого конфлікту виходів, що після закінчення програмування мікроконтролер переходить у звичайний режим роботи, і може так вийти, що його висновок, з'єднаний з виходом конвертера (RTS, TXD або DTR) теж стає виходом, згідно Щойно записаної в МК програмі, у цьому випадку буде дуже погано, якщо два напряму з'єднані виходи "боротимуться" - намагатимуться встановити різні логічні рівні. У такій боротьбі хтось може і програти, а нам цього не треба.

Номінали трьох резисторів обрані лише на рівні 4,3 КОм. Це стосується з'єднань вихід конвертера - вхід мікроконтролера. Точність резисторів ролі не грає: можна зменшити їх опір до 1 КОм або збільшити до 10 КОм (але в другому випадку збільшується ризик перешкод при використанні довгих проводів на шляху до МК). Що ж до з'єднання вхід конвертера (CTS) - вихід мікроконтролера (MISO), то тут застосований резистор опором 100 Ом. Це пояснюється особливостями входу використаного конвертера. Під час випробувань був використаний конвертер на мікросхемі PL-2303, входи якої, судячи з усього, підтягнуті до плюсу живлення відносно низьким опором (близько кількох сотень Ом). Щоб "перебити підтяжку", довелося поставити резистор з таким маленьким опором. Втім, його можна взагалі не ставити. На конвертері завжди вхід. Виходом він стати не може, а отже, конфлікту виходів не буде за будь-якого розвитку подій.

Якщо мікросхема має окремий висновок AVCC для живлення аналогово-цифрового перетворювача (наприклад, ATmega8 або ATmega128), слід з'єднати його з виведенням загального живлення VCC. Деякі мікросхеми мають більше одного живлення VCC або більше одного GND. Наприклад, ATmega128 має 3 висновки GND та 2 висновки VCC. У постійній конструкції однойменні висновки краще поєднати між собою. У нашому випадку на час програмування можна задіяти по одному висновку VCC і GND.

А ось як виглядає підключення ATtiny13. На малюнку показано призначення висновків, що використовуються під час програмування через SPI. Поруч на фото – як тимчасове підключення виглядає насправді.


Хтось може сказати, що це несерйозно – з'єднання на проводках. Але ж ми з вами люди розсудливі. Наша мета полягає в тому, щоб запрограмувати мікроконтролер, витративши на це мінімум часу та інших ресурсів, а не в тому, щоби перед кимось покрасуватися. Якість при цьому не страждає. Метод "на проводках" у разі цілком ефективний і виправданий. Прошивка контролера - процедура разова, тому немає сенсу обвішувати її "стразиками". Якщо ж передбачається змінювати прошивку надалі, не виймаючи контролер зі схеми (у готовому виробі), це враховується в монтажі під час виготовлення пристрою. Зазвичай для цього встановлюється роз'єм (RESET, SCK, MOSI, MISO, GND), а МК може бути прошитий навіть після установки на плату. Але це вже творчі вишукування. Ми ж розглядаємо найпростіший випадок.

Тепер перейдемо до МК ATtiny44. Тут усе приблизно так само. По малюнку і фото навіть новачкові не важко розібратися з підключенням. Подібно до ATtiny44 можна підключати МК ATtiny24 і ATtiny84 - призначення висновків у цієї трійці збігається.


Ще один приклад тимчасового підключення контролера для його програмування – ATmega8. Тут висновків більше, але принцип той самий - кілька проводків, і ось уже контролер готовий до "заливання" в нього інформації. Зайвий чорний провід на фото, що йде від виводу 13, програмування участі не бере. Він призначений для зняття звукового сигналу після виходу МК з режиму програмування. Це з тим, що під час налагодження скрипта для "Перпетуум М" в МК закачувалася програма музичної шкатулки.


Часто один контролер випускається у різних корпусах. При цьому призначення висновків кожного корпусу розподілено по-своєму. Якщо корпус вашого контролера не схожий на той, що зображений на малюнку, уточніть призначення висновків з технічної документації, яку можна завантажити з сайту розробника МК.

Для повноти картини подивимося підключення мікросхеми МК із великою кількістю "ніжок". Призначення зайвого чорного дроту на фото, що йде від виведення 15, таке саме, як у випадку з ATmega8.


Ймовірно, ви вже переконалися, що все досить просто. Хто вміє рахувати висновки у мікросхем (від мітки по колу проти годинникової стрілки), той розбереться. І не забувайте про акуратність. Мікросхеми люблять акуратні і не прощають недбалого до себе ставлення.

Перш ніж переходити до програмної частини, переконайтеся, що драйвер конвертера USB-RS232TTL коректно встановлений (перевірте диспетчер пристроїв Windows). Запам'ятайте або запишіть номер віртуального COM-порту, який з'являється під час підключення конвертера. Цей номер потрібно буде вписати до тексту скрипта, про який читайте нижче.

4. Скрипт – програматор для "Перпетуум М"

З апаратною частиною "програматора" розібралися. Це вже півсправи. Тепер залишилося розібратися із програмною частиною. Її роль виконуватиме програма "Перпетуум М" під управлінням скрипта, в якому реалізовані всі необхідні функції взаємодії з мікроконтролером.

Архів зі скриптом слід розпакувати в ту саму папку, де знаходиться програма perpetuum.exe. У цьому випадку при запуску файлу perpetuum.exe на екран буде виводитись меню зі списком встановлених скриптів, серед яких буде рядок "Программатор МК AVR" (вона може бути єдиною). Саме цей рядок нам і знадобиться.

Скрипт знаходиться у папці PMS у файлі "Программатор МК AVR.pms". Цей файл можна переглядати, вивчати та редагувати за необхідності у звичайному текстовому редакторі на кшталт "Блокнота" Windows. Перед використанням скрипта потрібно буде внести зміни в текст, пов'язані з налаштуванням порту. Для цього уточніть у диспетчері пристроїв Windows ім'я порту і, при необхідності, внесіть відповідну поправку в рядок "Ім'яПорта="COM4";" - замість цифри 4 може бути інша цифра. Також при використанні іншої моделі конвертера USB-RS232TTL може знадобитися зміна настройок інвертування сигналів (рядки скрипта, що починаються зі слова "Високий"). Перевірити інвертування сигналів конвертером USB-RS232TTL можна за допомогою одного з прикладів, що міститься в інструкції до програми Перпетуум М (розділ функцій для роботи з портом).

У вкладеній папці MK_AVR знаходяться файли з описами підтримуваних контролерів. Якщо потрібного контролера не виявиться серед них, ви можете додати потрібний самостійно, діючи за аналогією. Візьміть за зразок один із файлів, і за допомогою текстового редактора введіть необхідні дані, взявши їх з документації на мікроконтролер. Головне - будьте уважні, вводьте дані без помилок, інакше МК не запрограмується або запрограмується неправильно. У вихідній версії підтримуються 6 мікроконтролерів: ATtiny13, ATtiny24, ATtiny44, ATtiny84, ATmega8 та ATmega128. У скрипті реалізовано автоматичне розпізнавання підключеного контролера – вручну вказувати не потрібно. За відсутності ліченого з МК ідентифікатора серед наявних описів, видається повідомлення, що розпізнати контролер не вдалося.

В архіві зі скриптом міститься також додаткова інформація. У папці "inc-файли контролерів AVR" знаходиться дуже корисна та велика колекція файлів описів контролерів. Ці файли використовують при написанні власних програм для МК. Ще чотири папки "MusicBox_..." містять файли з програмою на Асемблері та готовою до закачування в МК прошивкою окремо для ATtiny13, ATtiny44, ATmega8 та ATmega128. Якщо ви вже підключили один із цих МК для програмування, як це запропоновано в даній статті, то можете прямо зараз його прошити - вийде музична скринька. Про це нижче.

При виборі меню скриптів рядки " Програматор МК AVR " , скрипт починає виконуватися. При цьому він відкриває порт, посилає в МК команду переходу в режим програмування, приймає підтвердження від МК про успішний перехід, запитує ідентифікатор МК і шукає опис даного МК з його ідентифікатору серед наявних файлів з описами. Якщо не знаходить відповідного опису, видає відповідне повідомлення. Якщо опис знайдено, далі відкривається головне меню програматора. Його скріншот можна бачити на малюнку 8. Далі розібратися не складно - меню дуже просте.

У першій версії скрипту деякі функції повноцінного програматора не реалізовані. Наприклад, немає можливості читати та писати в EEPROM. Але якщо ви відкриєте скрипт у текстовому редакторі, то побачите, що він має дуже невеликий розмір, при тому, що основне в ньому вже реалізовано. Це говорить про те, що додати функції не так вже й складно - мова дуже гнучкий, він дозволяє в невеликій програмі реалізувати багату функціональність. Але для більшості випадків вистачить навіть функцій.

Деякі обмеження функціональності описані у тексті скрипта:
//реалізований запис тільки з нульової адреси (Extended Segment Address Record ігнорується, LOAD OFFSET - теж)
//порядок і безперервність слідування записів у HEX-файлі не перевіряється
//контрольна сума не перевіряється
Це стосується роботи з файлом HEX, з якого береться код прошивки для МК. Якщо файл не спотворений, перевірка контрольної суми ні на що не вплине. Якщо спотворений – засобами скрипту це виявити не вдасться. Інші обмеження здебільшого не завадять, але мати на увазі їх все-таки потрібно.

5. Музична скринька - простий виріб для початківців

Якщо у вас є один з цих мікроконтролерів: ATtiny13, ATtiny44, ATmega8 або ATmega128, ви можете легко перетворити його на музичну скриньку або музичну листівку. Для цього достатньо записати в МК відповідну прошивку - одну з чотирьох, які розміщені в папках "MusicBox_..." в одному архіві зі скриптом. Коди прошивок зберігаються у файлах із розширенням ".hex". Використовувати ATmega128 для такого виробу, звичайно, "жирновато", як і ATmega8. Але це може бути корисно для тестування чи експериментів, інакше кажучи – у навчальних цілях. Тексти програм на асемблері також додаються. Програми створювалися не з нуля - за основу було взято програму музичної скриньки з книги А.В.Бєлова "Мікроконтролери AVR у радіоаматорській практиці". Вихідна програма зазнала ряду суттєвих змін:
1. адаптована для кожного з чотирьох МК: ATtiny13, ATtiny44, ATmega8 та ATmega128
2. ліквідовані кнопки - до контролера взагалі нічого не потрібно підключати, крім живлення та звуковипромінювача (мелодії відтворюються одна за одною в нескінченному циклі)
3. тривалість кожної ноти зменшена тривалість паузи між нотами усунення порушення музичного ритму
4. підключена восьма мелодія, незадіяна у книжковій версії
5. із суб'єктивного: деякі "улучшайзинги" для оптимізації та легшого сприйняття алгоритму

У деяких мелодіях чується фальш і навіть грубі помилки, особливо в "Посмішці" - у середині. Коди мелодій взяті з книги (а точніше - завантажені з сайту автора книги разом із вихідним asm-файлом) і не зазнавали змін. Зважаючи на все, у кодуванні мелодій є помилки. Але це не проблема - хто "дружить" з музикою, легко у всьому розбереться і виправить.

В ATtiny13 через відсутність 16-бітного лічильника для відтворення нот довелося використовувати 8-бітний, що призвело до деякого зниження точності звучання нот. Але на слух це мало помітно.

Щодо конфігураційних біт. Їх налаштування має відповідати стану нового мікроконтролера. Якщо ваш МК раніше десь використовувався, потрібно перевірити стан його конфігураційних біт, і, при необхідності, привести їх у відповідність до налаштувань нового мікроконтролера. Дізнатися стан конфігураційних біт нового мікроконтролера можна з документації цей МК (розділ "Fuse Bits"). Виняток становить ATmega128. Цей МК має біт M103C, який включає режим сумісності з більш старим ATmega103. Активізація біта M103C сильно урізує можливості ATmega128, причому у нового МК цей біт активний. Потрібно скинути M103C у неактивний стан. Для маніпуляцій із конфігураційними бітами використовуйте відповідний розділ меню скрипта-програматора.

Схему музичної скриньки наводити немає сенсу: у ній тільки мікроконтролер, харчування та п'єзвуковипромінювач. Харчування подається так само, як ми це зробили при програмуванні МК. Звуковипромінювач підключається між загальним проводом (висновок GND контролера) та одним із висновків МК, номер якого можна подивитися у файлі з асемблерним кодом програми (*.asm). На початку тексту програми кожному за МК у коментарях є рядок: " звуковий сигнал формується виведенні ХХ " . При завершенні роботи скрипта-програматора мікроконтролер виходить з режиму програмування і переходить у звичайний режим роботи. Відразу починається відтворення мелодій. Підключивши звуковипромінювач, це можна перевірити. Залишати звуковипромінювач підключеним під час програмування кристала можна тільки в тому випадку, якщо звук знімається з виводу, що не задіяного в SPI, інакше додаткова ємність на виводі може перешкодити програмуванню.


У цьому навчальному курсі з avr я постарався описати все найголовніше для початківців програмувати мікроконтролери avr. Усі приклади побудовані на мікроконтролері atmega8. Це означає, що для повторення всіх уроків вам знадобиться лише один МК. Як емулятор електронних схем використовується Proteus - на мій погляд, - найкращий варіант для початківців. Програми у всіх прикладах написані на компіляторі C для CodeVision AVR AVR. Чому не на якомусь асемблері? Тому що початківець і так завантажений інформацією, а програма, яка множить два числа, на асемблері займає близько ста рядків, та й у складних жирних проектах використовують С. Компілятор CodeVision AVR заточений під мікроконтролери atmel, має зручний генератор коду, непоганий інтерфейс і прямо з нього можна прошити мікроконтролер.

У цьому навчальному курсі буде розказано та показано на простих прикладах як:

  • Почати програмувати мікроконтролери, із чого почати, що для цього потрібно.
  • Які програми використовувати для написання прошивки для avr, для симуляції та налагодження коду на ПК,
  • Які периферійні пристрої знаходяться всередині МК, як ними керувати за допомогою програми
  • Як записати готову прошивку в мікроконтролер і як її налагодити
  • Як зробити друковану плату для Вашого пристрою
Для того, щоб зробити перші кроки на шляху програмування МК, вам знадобиться лише дві програми:
  • Proteus - програма-емулятор (у ній можна розробити схему, не вдаючись до реального паяння і потім на цій схемі протестувати нашу програму). Ми всі проекти спочатку запускатимемо в протеусі, а потім вже можна і паяти реальний пристрій.
  • CodeVisionAVR - компілятор мови програмування для AVR. У ньому ми розроблятимемо програми для мікроконтролера, і прямо з нього ж можна буде прошити реальний МК.
Після встановлення Proteus, запускаємо його
Він пропонує подивитися проекти які йдуть з ним, ми чемно відмовляємося. Тепер давайте створимо у ній найпростішу схему. Для цього натисніть на значок візуально нічого не відбувається. Тепер потрібно натиснути на маленьку букву Р (вибрати із бібліотеки)на панелі списку компонентів, відкриється вікно вибору компонентів
у полі маска вводимо назву компонента, який хочемо знайти у бібліотеці. Наприклад, нам потрібно додати мікроконтролер mega8
у списку результатів тикаємо на mega8 і натискаємо кнопку ОК. У нас у списку компонентів з'являється мікроконтролер mega8
У такий спосіб додаємо до списку компонентів ще резистор, ввівши в поле маска слово resта світлодіод led

Щоб розмістити деталі на схемі, клацаємо на деталь, далі клацаємо по полю схеми, вибираємо місце розташування компонента і ще раз клацаємо. Для додавання землі або загального мінусу на схему зліва натискаємо "Термінал" і вибираємо Ground. Таким чином, додавши всі компоненти і з'єднавши їх, отримуємо ось таку просту схему
Все, тепер наша перша схема готова! Але ви, мабуть, спитаєте, а що вона може робити? А нічого. Нічого, тому що для того, щоби мікроконтролер запрацював, для нього потрібно написати програму. Програма – це список команд, які виконуватиме мікроконтролер. Нам потрібно, щоб мікроконтролер встановлював на ніжці PC0логічний 0 (0 вольт) та логічну 1 (5 вольт).

Написання програми для мікроконтролера

Програму ми писатимемо мовою С у компіляторі CodeVisionAVR. Після запуску CV він запитує нас, що ми хочемо створити: Source або Project Ми вибираємо останнє та натискаємо кнопку ОК. Далі нам буде запропоновано запустити майстер CVAVR CodeWizard (це безцінний інструмент для початківця, тому що в ньому можна генерувати основний скелет програми) обираємо Yes
Майстер запускається з активною вкладкою Chip, тут ми можемо вибрати модель нашого МК - це mega8, і частоту, на якій працюватиме МК (за замовчуванням mega8 виставлена ​​на частоту 1 мегагерц), тому виставляємо все, як показано на скріншоті вище. Переходимо у вкладку Ports
У мікроконтролера atmega8 3 порти: Port C, Port D, Port B. У кожного порту 8 ніжок. Ніжки портів можуть перебувати у двох станах:
  • Вихід
За допомогою регістру DDRx.y ми можемо встановлювати ніжку входом чи виходом. Якщо в
  • DDRx.y = 0 - висновок працює як ВХІД
  • DDRx.y = 1 висновок працює на ВИХІД
Коли ніжка налаштована як вихід, ми можемо виставляти на ній лог 1 (+5 вольт) та логічний 0 (0 вольт). Це робиться записом у регістр PORTx.y. Далі буде детально розказано про порти вводу-виводу. А зараз виставляємо все, як показано на скріншоті, і натискаємо File->Generate, Save and Exit. Далі CodeWizard запропонує нам зберегти проект, ми його зберігаємо та дивимося на код:

#include //бібліотека для створення тимчасових затримок void main(void) ( PORTB=0x00; DDRB=0x00; PORTC=0x00; DDRC=0x01; // робимо ніжку PC0 виходом PORTD=0x00; DDRD=0x00; // Timer/Counter 0 initialization TCCR0=0x00, TCNT0=0x00;// Timer/Counter 1 initialization TCCR1A=0x00; TCCR1B=0x00; TCNT1H=0x00; =0x00 OCR1BL = 0x00; // Timer / Counter 2 initialization ASSR = 0x00; TCCR2 = 0x00; TCNT2 = 0x00; ) Interrupt(s) initialization TIMSK=0x00;// Analog Comparator initialization ACSR=0x80; SFIOR=0x00; while (1) ( );


Тут вам може здатися все страшним та незнайомим, але насправді все не так. Код можна спростити, викинувши ініціалізацію периферійних пристроїв МК, що не використовуються. Після спрощення він виглядає так:

#include //Бібліотека для роботи з мікроконтролером mega8 #include //бібліотека для створення тимчасових затримок void main(void) ( DDRC=0x01; /* робимо ніжку PC0 виходом запис 0x01 може здатися вам незнайомою, а це всього лише число 1 в шістнадцятковій формі, цей рядок буде еквівалентний 0b00000001 писатиму саме так.*/ while (1) ( );


Все добре. Але для того, щоб світлодіод замиготів, нам потрібно змінювати логічний рівень на ніжці PC0. Для цього до головного циклу потрібно додати кілька рядків:

#include //Бібліотека для роботи з мікроконтролером mega8 #include //бібліотека для створення тимчасових затримок void main(void) ( DDRC=0x01; /* робимо ніжку PC0 виходом запис 0x01 може здатися вам незнайомою, а це всього лише число 1 в шістнадцятковій формі, цей рядок буде еквівалентний 0b00000001 буду писати саме так. 500 мілісекунд PORTC.0=0; //виставляємо на ніжку 0 порту З 0 delay_ms(500); //робимо затримку в 500 мілісекунд);// закривається операторна дужка головного циклу програми)


Тепер код готовий. Клацаємо на піктограму Build all Project files, щоб скомпілювати нашу програму (перекласти в інструкції процесора МК). У папці Exe, яка знаходиться в нашому проекті, має з'явитися файл з розширенням hex, це і є файл прошивки для МК. Щоб нашу прошивку згодувати віртуальному мікроконтролеру в Proteus, потрібно двічі клікнути на зображенні мікроконтролера в протеусі. З'явиться ось таке віконце
клацаємо на піктограму папки в полі Program File, вибираємо hex - файл нашої прошивки та натискаємо кнопку ОК. Тепер можна запустити симуляцію нашої схеми. Для цього натискаємо кнопку "Відтворити" у нижньому лівому куті вікна Протеус.

Я не раз і не два говорив, що вивчення МК треба починати з асемблера. Цьому було присвячено цілий курс на сайті (правда він не дуже послідовний, але поступово я його зачісую до адекватного вигляду). Так, це складно, результат буде не в перший день, зате ти навчишся розуміти що відбувається у тебе в контролері. Знатимеш як це працює, а не по мавпій копіювати чужі вихідники і намагатися зрозуміти чому воно раптом перестало працювати. Крім того, Сі набагато простіше наробити бидлокода, який вилізе вилами в самий невідповідний момент.

На жаль, всі хочуть результат негайно. Тому я вирішив піти з іншого боку — зробити взутку Сі, але з показом його нижньої білизни. Хороший програміст-ембеддер завжди міцно тримає свою залізницю за шкварник, не даючи їй ні кроку ступити без дозволу. Так що буде спочатку Сі код, потім те, що народив компілятор і як все це працює насправді:)

З іншого боку, у Сі сильна сторона це переносимість коду. Якщо, звісно, ​​писати все правильно. Поділяючи алгоритми роботи та їх залізні реалізації у різні частини проекту. Тоді для перенесення алгоритму в інший МК достатньо буде переписати лише інтерфейсний шар, де прописано все звернення до заліза, а весь робочий код залишити як є. І, звичайно ж, читальність. Сишний вихідник простіше зрозуміти з першого погляду (хоча ... мені, наприклад, вже пофігу на що фтикати - хоч си, хоч асм:)), але, знову ж таки, якщо правильно все написати. Цим моментам я теж приділятиму увагу.

Як піддослідна залозка на якій буде ставитися левова частка всіх прикладів буде моя налагоджувальна плата.

Перша програма на Сі для AVR

Вибір компілятора та встановлення середовища
Для AVR існує безліч різних компіляторів:
Насамперед це IAR AVR C- майже однозначно визнається найкращим компілятором для AVR, т.к. сам контролер створювався тісному співробітництві Atmel і фахівців з IAR. Але за все доводиться платити. І цей компілятор мало того, що є дорогим комерційним софтом, так ще володіє такою прорвою налаштувань, що просто взяти й скомпилити в ньому треба постратися. У мене з ним правда не зрослося дружби, проект загнивав на дивних помилках на етапі лінківки (пізніше з'ясував, що це був кривий кряк).

Другим йде WinAVR GCC— потужний компілятор, що оптимізує. Повний опенсорц, кросплатформний, загалом, усі радощі життя. Ще він відмінно інтегрується в AVR Studio дозволяючи вести налагодження прямо там, що дуже зручно. Загалом я вибрав його.

Також є CodeVision AVR C- дуже популярний компілятор. Став популярним у зв'язку зі своєю простотою. Робочу програму в ньому отримати можна вже за кілька хвилин — майстер стартового коду цьому дуже сприяє, штампуючи стандартині ініціалізації будь-яких уартів. Чесно кажучи, я якось з підозрою до нього ставлюся - якось доводилося дизасміть прогу написану цим компілером, каша якась а не код виходила. Жахлива кількість непотрібних рухів тіла і операцій, що виливалося в неслабкий об'єм коду і повільну швидкодію. Втім, можливо тут була помилка в ДНК, що писав вихідну прошивку. Плюс він хоче грошей. Не так багато, як IAR, але відчутно. А в деморежимі дає писати не більше ніж 2кб коду.
Кряк звичайно є, але якщо вже красти, то мільйон, у сенсі IAR:)

Ще є Image Craft AVR Cі MicroCвід мікроелектроніки Ні тим, ні іншим користуватися не доводилося, але ось SWGдуже вже нахвалює MicroPascal, мовляв, дуже зручне середовище програмування та бібліотеки. Думаю, MicroC не гірше буде, але теж платний.

Як я вже сказав, я вибрала WinAVRз трьох причин: халявний, інтегрується в AVR Studio і під нього написана просто прорва готового коду на всі випадки життя.

Так що качай собі інсталях WinAVR з і AVR Studio. Далі спочатку ставиться студія, потім зверху накочується WinAVR і чіпляється до студії у вигляді плагіна. Наполегливо рекомендую ставити WinAVR коротким шляхом, щось на кшталт C:WinAVR тим самим ти уникнеш купи проблем з шляхами.

Створення проекту
Отже, студія поставлена, Сі прикручений, пора б і спробувати щось запрограмувати. Почнемо з простого, найпростішого. Запускай студію, вибирай там новий проект як компілятор AVR GCC і вписуй назву проекту.

Відкриється робоче поле з порожнім файлом *.c.

Тепер не завадить настроїти відображення шляхів у закладках студії. Для цього злазь за адресою:
Меню Tools - Options - General - FileTabs і вибираємо у випадаючому списку "Filename Only". Інакше працювати буде неможливо - на вкладці буде повний шлях файлу і на екрані буде не більше двох вкладок.

Налаштування проекту
Взагалі, класичним вважається створення make файлу в якому були б описані всі залежності. І це, мабуть, правильно. Але мені, який виріс на повністю інтегрованих IDE начебто uVisionабо AVR Studioцей підхід є глибоко чужим. Тому робитиму за своїм, усе засобами студії.

Тикай у кнопку з шестернею.


Це налаштування твого проекту, а точніше, налаштування автоматичної генерації make файлу. На першій сторінці треба лише вписати частоту на якій працюватиме твій МК. Це залежить від ф'юз бітів, так що вважаємо, що частота у нас 8000000Гц.
Також зверніть увагу на рядок оптимізації. Зараз там стоїть -Os це оптимізація за розміром. Поки залиш як є, потім можеш спробувати погратися з цим параметром. -O0 це відсутність оптимізації взагалі.

Наступним кроком буде налаштування шляхів. Насамперед додай туди директорію твого проекту — туди підкладатимеш сторонні бібліотеки. У списку з'явиться шлях ".\"

Make файл згенерований, його ти можеш подивитися в папці default у своєму проекті, просто пробігайся очима, подивися що там є.


На цьому поки що все. Тисни скрізь ОК і переходь у вихідник.

Постановка задачі
Чистий лист так і підмиває втілити якусь хитру задумку, так як банальне миготіння діодом вже не вставляє. Давай вже відразу брати бика за роги і реалізуємо зв'язок з комп'ютером - це насамперед що я роблю.

Працюватиме так:
При приході по COM порту одиниці (код 0х31) запалюватимемо діодик, а при приході нуля (код 0х30) гаситимемо. Причому зроблено все на перериваннях, а фоновим завданням буде миготіння іншого діода. Просто і з сенсом.

Збираємо схему
Нам треба з'єднати модуль USB-USART конвертера з висновками USART мікроконтролера. Для цього беремо перемичку з двох проводків і накидаємо на штирьки навхрест. Тобто Rx контролера з'єднуємо з Tx конвертером, а Tx конвертером з Rx контролером.

Вийде, у результаті ось така схема:


Підключення решти висновків, харчування, скидання не розглядаю, воно стандартне

Пишемо код

Відразу обмовлюся, що я не заглиблюватимусь конкретно в опис самої мови Сі. Для цього існує колосальна кількість матеріалу, починаючи від класики «Мова програмування Сі» від K&R і закінчуючи різними методичками.

Одна така методу знайшлася у мене в загашнику, я колись саме по ній вивчав цю мову. Там усе коротко, зрозуміло й у справі. Я її поступово верстаю та перетягую на свій сайт.

Там, правда, ще не всі глави перенесені, але, думаю, це ненадовго.

Навряд чи я опишу краще, тому з навчального курсу, замість докладного роз'яснення сишних тонкощів, я просто даватиму прямі лінки на окремі сторінки цієї методички.

Додаємо бібліотеки.
Насамперед ми додаємо потрібні бібліотеки та заголовки з визначеннями. Адже Сі це універсальна мова і йому треба пояснити, що ми працюємо саме з AVR, так що вписуй у вихідний рядок:

1 #include

#include

Цей файл знаходиться в папці WinAVRі в ньому міститься опис усіх регістрів та портів контролера. Причому там все хитро, з прив'язкою до конкретного контролера, який передається компілятором через makeфайл у параметрі MCUі на підставі цієї змінної в твій проект підключається заголовний файл з описом адрес всіх портів і регістрів саме на цей контролер. ВО як! Без нього теж можна, але тоді ти не зможеш використовувати символічні імена регістрів на кшталт SREG або UDR і доведеться пам'ятати адресу кожного на кшталт «0xC1», а це зламати голову.

Сама ж команда #include<имя файла> дозволяє додати у твій проект вміст будь-якого текстового файлу, наприклад, файл з описом функцій або шматок іншого коду. А щоб директива могла цей файл знайти ми і вказували шляхи до нашого проекту (директорія WinAVR там уже прописана по дефолту).

Головна функція.
Програма мовою Сі вся складається з функцій. Вони можуть бути вкладеними і викликатись один з одного в будь-якому порядку та різними способами. Кожна функція має три обов'язкові параметри:

  • Значення, що повертається, наприклад, sin(x)повертає значення синусу ікс. Як у математиці, коротше.
  • Параметри, що передаються, цей ікс.
  • Тіло функції.

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

Будь-яка програма на Сі повинна містити функцію mainяк точку входу в головну програму, інакше це ніфіга не Сі:). За наявності main в чужому вихіднику з мільйона файлів можна зрозуміти, що це і є головна частина програми, звідки починається все. Ось і поставимо:

1 2 3 4 5 int main (void) (return 0;)

int main(void) ( return 0; )

Все, перша найпростіша програма написана, не біда, що вона нічого не робить, ми ж тільки почали.

Розберемо що ми зробили.
intце тип даних, яка функція main повертає.

Звичайно, у мікроконтролері mainнічого повернути в принципі не може і за ідеєю має бути void main(void), але GCC спочатку заточений на PC і там програма може повернути значення операційної системи після завершення. Тому GCC на void main(void)лається Warning'ом.

Це не помилка, працюватиме, але я не люблю варнінги.

voidце тип даних, які ми передаємо в функцію, в даному випадку mainтакож не може нічого прийняти ззовні, тому void- Пустушка. Заглушка, застосовується тоді, коли не треба нічого передавати або повертати.

Ось такі { } фігурні дужки це програмний блок, в даному випадку тіло функції main, там буде розміщуватися код.

return— це значення, що повертається, яке функція main віддасть при завершенні, оскільки у нас int, тобто число то повернути ми повинні число. Хоча це однаково немає сенсу, т.к. на мікроконтролері з main нам виходити хіба що нікуди. Я повертаю нуль. Бо нефіг. А компілятор зазвичай розумний і цього випадку код не генерує.
Хоча, якщо перекрутитися, то з mainна МК вийти можна - наприклад вивалитися в секцію бутлоадера і виконати її, але тут вже буде потрібно низькорівневе колупання прошивки, щоб підправити адреси переходу. Нижче ти сам побачиш і зрозумієш, як це зробити. Навіщо? Ось це вже інше питання, у 99.999% випадків це нафіг не треба:)

Зробили, поїхали далі. Додамо змінну, вона нам не особливо потрібна і без потрібні вводити змінні не варто, але ж ми вчимося. Якщо змінні додаються всередині тіла функції, то вони локальні і існують тільки в цій функції. Коли з функції виходиш ці змінні видаляються, а пам'ять ОЗУ віддається під найважливіші потреби. .

1 2 3 4 5 6 int main(void ) ( unsigned char i; return 0 ; )

int main(void) ( unsigned char i; return 0; )

unsignedзначить беззнаковий. Справа в тому, що в двійковому поданні у нас старший біт відводиться під знак, а значить, в один байт (char) влазить число +127/-128, але якщо знак відкинути то влізе вже від 0 до 255. Зазвичай знак не потрібен. Так що unsigned.
i— це лише ім'я змінної. Не більше того.

Тепер треба проініціалізувати порти та UART. Звичайно, можна взяти і підключити бібліотеку і викликати якийсь UartInit (9600); але тоді ти не дізнаєшся, що сталося насправді.

Робимо так:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 int main(void ) ( unsigned char i; #define XTAL 8000000L #define baudrate 9600L #define bauddivider (XTAL/(16*baudrate)-1)#define HI(x) ((x)>>8) #define LO(x) ((x)& 0xFF) UBRRL = LO(bauddivider) ; UBRRH = HI (bauddivider); UCSRA = 0; UCSRB = 1<< RXEN| 1 << TXEN| 1 << RXCIE| 0 << TXCIE; UCSRC = 1 << URSEL| 1 << UCSZ0| 1 << UCSZ1; }

int main(void) ( unsigned char i; #define XTAL 8000000L #define baudrate 9600L #define bauddivider (XTAL/(16*baudrate)-1) #define HI(x) ((x)>>8) #define LO( x) ((x)& 0xFF) UBRRL = LO (bauddivider); UBRRH = HI (bauddivider); UCSRA = 0; UCSRB = 1<

Страшна? Насправді реалного коду тут лише п'ять останніх рядків. Все, що #defineце макромова препроцесора. Майже те ж бадилля, що і в Асемблері, але синтаксис дещо інший.

Вони полегшать твої рутинні операції з обчислення потрібних коефіцієнтів. У першому рядку ми говоримо, що замість XTALможна сміливо підставляти 8000000, а L- Вказівка ​​типу, мовляв long - це тактова частота процесора. Теж саме baudrate- Частота передачі даних по UART.

bauddividerвже складніше, замість нього буде підставлятись вираз обчислений за формулою з двох попередніх.
Ну а LOі HIцього результату візьмуть молодший і старший байти, т.к. в один байт воно може не влізти. У HIробиться зсув ікса (вхідний параметр макросу) вісім разів направо, в результаті від нього залишиться лише старший байт. А в LOми робимо побітове І з числом 00FF, в результаті залишиться лише молодший байт.

Так що все, що зроблено як #defineможна сміливо викинути, а потрібні числа підрахувати на калькуляторі і відразу вписати їх у рядки UBBRL = …. та UBBRH = …..

Можна, можливо. Але! Робити цього КАТЕГОРИЧНО НЕ МОЖНА!

Працюватиме і так і так, але в тебе в програмі з'являться так звані магічні числа— значення взяті незрозуміло звідки й незрозуміло навіщо і якщо ти через пару років відкриєш такий проект, то зрозуміти що це за значення буде дуже важко. Та й зараз, захочеш ти змінити швидкість, або зміниш частоту кварцу і все доведеться перераховувати заново, а так змінив пару циферок у коді і все саме. Загалом, якщо не хочеш уславитися бидлодером, то роби код таким, щоб він легко читався, був зрозумілий і легко модифікувався.

Далі все просто:
Всі ці "UBRRL і C" це регістри конфігурації UART передавача за допомогою якого ми будемо спілкуватися зі світом. І зараз ми надали їм потрібні значення, налаштувавши на потрібну швидкість і потрібний режим.

Запис виду 1<Це означає: взяти 1 і поставити її на місце RXENу байті. RXENце 4й біт регістру UCSRB, так що 1<утворює двійкове число 00010000, TXEN— це третій біт, а 1<дасть 00001000. Поодинока «|» це побітове АБО, Отже 00010000 | 00001000 = 00011000. Таким же чином виставляються і додаються в загальну купу інші необхідні біти конфігурації. У результаті зібране число записується в UCSRB. Докладніше розписано в датасіті на МК в розділі USART. Тож не відволікаємось на технічні деталі.

Готово, настав час подивитися що вийшло. Тисніть на компіляцію та запуск емуляції (Ctrl+F7).

Налагодження
Пробігли всілякі прогреси, студія змінилася і біля входу в функцію main з'явилася жовта стрілочка. Це де процесор в даний момент, а симуляція на паузі.

Справа в тому, що спочатку, насправді, вона стояла на рядку UBRRL = LO (bauddivider); Адже те, що у нас в define це не код, а просто попередні обчислення, ось симулятор трохи і затупив. Але тепер він усвідомив, першу інструкцію виконано і якщо ти залізеш у дерево I/O View, в розділ USART і подивишся там на байт UBBRL то побачиш, що там значення вже є! 0х33.

Зроби ще один крок. Подивись як зміниться вміст іншого регістру. Так пройди їх усі, зверни увагу на те, що всі зазначені біти виставляються як я тобі і говорив, причому виставляються одночасно для всього байта. Далі Return справа не піде – програма скінчилася.

Розтин
Тепер скинь симуляцію у нуль. Натисніть там Reset (Shift+F5). Відкривай дизассембльований лістинг, зараз ти побачиш, що відбувається в контролері насправді. View -> Disassembler. І не ЫЫАААА! Асемблер! ЖАХ!!! А ТРЕБА. Щоб потім, коли щось піде не так, не тупив у код і не ставив ламерських питаннях на форумах, а відразу ж ліз у тельбухи і дивився де в тебе затик. Нічого страшного там немає.

Спочатку буде бадилля із серії:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 +00000000: 940C002A JMP 0x0000002A Jump +00000002: 940C0034 JMP 0x00000034 Jump +00000004: 940C0034 JMP 0x0000000000000 MP 0x00000034 Jump +00000008: 940C0034 JMP 0x00000034 Jump +0000000A: 940C0034 JMP 0x00000034 Jump +0000000C: 940C0 0000000E: 940C0034 JMP 0x00000034 Jump +00000010: 940C0034 JMP 0x00000034 Jump +00000012: 940C0034 JMP 0x00000000000 MP 0x00000034 Jump +00000016: 940C0034 JMP 0x00000034 Jump +00000018: 940C0034 JMP 0x00000034 Jump +0000001A: 94000 1C : 940C0034 JMP 0x00000034 Jump +0000001E: 940C0034 JMP 0x00000034 Jump +00000020: 940C0034 JMP 0x00000034 Jump +00 0034 Jump +00000024: 940C0034 JMP 0x00000034 Jump +00000026: 940C0034 JMP 0x00000034 Jump +00000028: 940C0034 JMP0

00000000: 940C002A JMP 0x0000002A Jump +00000002: 940C0034 JMP 0x00000034 Jump +00000004: 940C0034 JMP 0x000000000000 MP 0x00000034 Jump +00000008: 940C0034 JMP 0x00000034 Jump +0000000A: 940C0034 JMP 0x00000034 Jump +0000000C: 94000 0E : 940C0034 JMP 0x00000034 Jump +00000010: 940C0034 JMP 0x00000034 Jump +00000012: 940C0034 JMP 0x00000034 Jump +00 0034 Jump +00000016: 940C0034 JMP 0x00000034 Jump +00000018: 940C0034 JMP 0x00000034 Jump +0000001A: 940C0034 JMP00 940C0034 JMP 0x00000034 Jump +0000001E: 940C0034 JMP 0x00000034 Jump +00000020: 940C0034 JMP 0x00000034 Jump +00000000 34 Jump +00000024: 940C0034 JMP 0x00000034 Jump +00000026: 940C0034 JMP 0x00000034 Jump +00000028: 940C0034 JMP 0x0

Це таблиця векторів переривань. До неї ми ще повернемося, поки просто подивися і запам'ятай, що вона є. Перша колонка - адреса осередку флешу в якій лежить команда, друга код команди третя мнемоніка команди, та сама асемблерна інструкція, третя операнди команди. Та й автоматичний комент.
Так от, якщо ти подивишся, то тут суцільні переходи. А код команди JMP чотирьох байтний, у ньому міститься адреса переходу, записана задом наперед - молодший байт за молодшою ​​адресою та код команди переходу 940C

0000002B: BE1F OUT 0x3F,R1 Перейти до I/O location

Запис цього нуля на адресу 0x3F, Якщо ти подивишся в колонку I/O view, то побачиш що адресу 0x3F це адреса регістра SREG — прапорового регістру контролера. Тобто. ми обнулюємо SREG, щоб запустити програму на нульових умовах.

1 2 3 4 +0000002C: E5CF LDI R28,0x5F Load immediate +0000002D: E0D4 LDI R29,0x04 Load immediate +0000002E: BFDE OUT 0x3E,R29 Out to I/O location +0000002F: BFCD OUT 0x3D,R28 Out to I/O location

0000002C: E5CF LDI R28,0x5F Load immediate +0000002D: E0D4 LDI R29,0x04 Load immediate +0000002E: BFDE OUT 0x3E,R29 Out to I/O location +0000002F: BFCD OUT 0x3D,R28 Out to I/O location

Це завантаження покажчика стека. Безпосередньо вантажити в I/O регістри не можна, тільки через проміжний регістр. Тому спочатку LDI проміжний, а потім звідти OUT в I/O. Про стеку я також розповім детальніше. Поки знай, що це така динамічна область пам'яті, висить в кінці ОЗУ і зберігає в собі адреси і проміжні змінні. Ось зараз ми вказали на те, звідки у нас починатиметься стек.

00000032: 940C0041 JMP 0x00000041 Jump

Стрибок у сааааамий кінець програми, а там у нас заборона переривань та зациклювання наглухо саме на себе:

1 2 +00000041: 94F8 CLI Global Interrupt Disable +00000042: CFFF RJMP PC-0x0000 Relative jump

00000041: 94F8 CLI Global Interrupt Disable +00000042: CFFF RJMP PC-0x0000 Relative jump

Це випадок непередбачених обставин, наприклад виходу з функції main. З такого зациклювання контролер можна вивести або апаратним скиданням, або, ймовірно, скиданням від сторожового собаки - watchdog. Ну або, як я говорив вище, підправити це місць у хекс редакторі і поїхати куди нам душі завгодно. Також зверніть увагу на те, що буває два типи переходів JMP і RJMP перший це прямий перехід за адресою. Він займає чотири байти і може зробити прямий перехід по всій області пам'яті. Другий тип переходу – RJMP – відносний. Його команда займає два байти, але перехід він робить від поточного положення (адреси) на 1024 кроки вперед або назад. І його параметрах вказується зміщення від поточної точки. Використовується найчастіше, т.к. займає вдвічі менше місця у флеші, а довгі переходи потрібні рідко.

1 +00000034: 940C0000 JMP 0x00000000 Jump

00000034: 940C0000 JMP 0x00000000 Jump

А це стрибок на початок коду. Перезавантаження свого роду. Можеш перевірити, чи всі вектори стрибають сюди. З цього висновок — якщо ти зараз дозволиш переривання (вони дефолтом заборонені) і в тебе переривання пройде, а обробника немає, то буде програмне скидання — програму кине на початок.

Функція main. Все аналогічно навіть можна і не описувати. Подивися щойно до регістру заноситься вже обчислене число. Препроцесор компілятора рулить! Тож жодних «магічних» чисел!

1 2 3 4 5 6 7 8 9 10 11 12 <

00000036: E383 LDI R24,0x33 Load immediate +00000037: B989 OUT 0x09,R24 Перейти до I/O location 15: UBRRH = HI(bauddivider); +00000038: BC10 OUT 0x20,R1 Перейти до I/O location 16: UCSRA = 0; +00000039: B81B OUT 0x0B,R1 Перейти до I/O location 17: UCSRB = 1<

А ось тут косяк:

1 2 3 +0000003E: E080 LDI R24,0x00 Load immediate +0000003F: E090 LDI R25,0x00 Load immediate +00000040: 9508 RET Subroutine return

0000003E: E080 LDI R24,0x00 Load immediate +0000003F: E090 LDI R25,0x00 Load immediate +00000040: 9508 RET Subroutine return

Постає питання, навіщо це компілятор додає таке бадилля? А це не що інше, як Return 0, функцію ми визначили як int main(void) ось і просрали ще цілих чотири байти не зрозумій на що:) А якщо зробити void main(void) то залишиться тільки RET, але з'явиться варнінг, що мовляв, у нас функція main нічого не повертає. Загалом, роби як хоч:)

Важко? Начебто ні. Пощелкай покрокове виконання в режимі дизассемблера і пози як процесор виконує окремі інструкції, що при цьому відбувається з регістрами. Як відбувається переміщення по командам та підсумкове зациклювання.

Продовження слідує через пару днів.

Offtop:
Alexei78зварганив плагінчик для файрфокса, що полегшує навігацію по моєму сайту та форуму.
Обговорення та скачування,

Всім привіт. Як і обіцяв, із сьогоднішнього дня починаємо вивчати програмування AVR мікроконтролерів (на прикладі Atmega8). Тим же читачам, яким цікаве програмування плати ардуїно, не хвилюйтеся, статті за цим напрямком триватимуть 🙂 .

Можна поставити логічне питання, чому з інших мікроконтролерів (далі — МК) як піддослідний обраний саме МК AVR. На це є кілька причин:

  • МК AVRповсюдно доступні;
  • У них досить низька ціна;
  • В інтернеті можна знайти багато безкоштовних програм, що допоможуть під час роботи з даними МК.
  • Крім цього, існує безліч написаних статей та форумів, на яких можна поставити запитання за даними МК AVR.

Як говорив раніше, як піддослідний використовуватимемо МК Atmega8. Чому саме його?

Даний мікроконтролер може похвалитися наявністю 3 портів вводу/виводу. Крім цього, він досить дешевий.

Під портами розуміють шини даних, які можуть працювати у двох протилежних напрямках (тобто на висновок і на введення).

У Atmega8 3 порти. Порт B складається з 8 ніжок-висновків (нумерація 0,1,2,3,4,5,6,7). Порт С складається з 7 ніжок-висновків (нумерація 0,1,2,3,4,5,6). Порт D складається з 8 ніжок-висновків (нумерація 0,1,2,3,4,5,6,7).

Запитувати мікроконтролер можна від 3,3 та 5 В. При напрузі живлення 5 В максимальна частота тактування становить 16 МГц, а при напрузі живлення 3,3 В – максимальна частота тактування 8 МГц. Поки не морочимось щодо частот тактування.

Живлення подається на 7 ніжку-виведення, а «земля» підводиться до 8 ніжки.

Завантажується безкоштовно. Завантажили, встановили, запустили 🙂

Перше, з чого слід розпочати знайомство з Atmel Studio – це створення проекту.

Вибираємо File->new->project.

Відкриється вікно вибору. Вибираємо папку «Browse», в якій зберігатимемо написані проекти. Теку для проектів створив заздалегідь.

Привласнюємо ім'я проекту, у моєму випадку lesson_avr_1

Зверніть увагу на галочку create directory for solution. Якщо позначка стоїть, то в тій папці, яку ми вибрали для збереження проектів, буде створено окрему папку під поточний проект.

На цьому все – проект створено.

Займемося налаштуванням створеного нами проекту. Натискаємо Projest -> lesson_avr_1 properties або (alt+F7)

Переходимо на вкладку Tool. Вибираємо – симулятор. Вчинені нами дії уможливлять налагодження написаного коду. Зберігаємо зміни. Можна зберегти зміни в одному (поточному) файлі або у всіх файлах проекту відразу. Закриваємо налаштування.

Завдання: Розробимо програму керування одним світлодіодом. При натисканні на кнопку світлодіод світиться, при відпусканні гасне.

Для початку розробимо важливу схему пристрою. Для підключення до мікроконтролера будь-яких зовнішніх пристроїв використовуються порти вводу-виводу. Кожен із портів здатний працювати як на вхід так і на вихід. Підключимо світлодіод до одного з портів, а кнопку до іншого. Для цього досвіду ми будемо використовувати контролер Atmega8. Ця мікросхема містить 3 порти вводу-виводу, має 2 восьмирозрядні і 1 шістнадцятирозрядний таймер/лічильник. Також на борту є 3-х канальний ШІМ, 6-ти канальний 10-ти бітний аналого-цифровий перетворювач та багато іншого. На мою думку, мікроконтролер чудово підходить для вивчення основ програмування.

Для підключення світлодіода ми використовуватимемо лінію PB0, а для зчитування інформації з кнопки скористаємося лінією PD0. Схема наведено на рис.1.

Мал. 1

Через резистор R2 на вхід PD0 подається плюс напруги живлення, що відповідає сигналу логічної одиниці. При замиканні кнопки напруга знижується до нуля, що відповідає логічному нулю. Надалі R2 можна виключити зі схеми, замінюючи його на внутрішній резистор навантаження, ввівши необхідні налаштування в програмі. Світлодіод підключений до виходу порту PB0 через резистор R3, що обмежує струм. Для того, щоб запалити світлодіод, треба подати в лінію PB0 сигнал логічної одиниці. Задає тактовий генератор використовуватимемо внутрішній на 4MHz, так як у пристрої немає високих вимог до стабільності частоти.

Тепер пишемо програму. Для написання програм я використовую програмне середовище AVR Studioі WinAvr.Відкриваємо AVR Studio, спливає віконце вітання, натискаємо кнопку "Створити новий проект" (New project), далі вибираємо тип проекту - AVR GCC, пишемо ім'я проекту наприклад "cod1", ставимо обидві галочки "Створити папку проекту" та "Створити файл ініціалізації" , натискаємо кнопку "Далі", у лівому вікні вибираємо "AVR Simulator", а в правому тип мікроконтролера "Atmega8", натискаємо кнопку "Фініш", відкривається редактор та дерево категорій проекту - початкові установки закінчені.

Для початку додамо стандартний текст описів Atmega8 за допомогою оператора приєднання зовнішніх файлів: #include

синтаксис директиви #include

#include<имя_файла.h>
#include “ім'я_файлу.h”

Кутові дужки< и >вказують компілятору, що файли, що підключаються, потрібно спочатку шукати в стандартній папці WinAvr з ім'ям include. Подвійні лапки "і" вказують компілятор починати пошук з директорії, в якій зберігається проект.

Для кожного типу мікроконтролера є свій заголовний файл. Для ATMega8 цей файл називається iom8.h, для ATtiny2313 – iotn2313.h. На початку кожної програми ми повинні підключати заголовний файл мікроконтролера, який ми використовуємо. Але є й загальний заголовний файл io.h. Препроцесор обробляє цей файл і в залежності від налаштувань проекту включає в нашу програму потрібний файл заголовка.

Для нас перший рядок програми виглядатиме ось так:

#include

Будь-яка програма мовою Сі повинна обов'язково містити одну головну функцію. Вона має ім'я main. Виконання програми завжди починається із виконання функції main. Функція має заголовок - int main(void) і тіло - воно обмежене фігурними дужками ().

int main(void)
{
тіло функції
}

У тіло функції ми і додаватимемо наш код. Перед ім'ям функції вказується тип значення, що повертається. Якщо функція не повертає значення – використовується ключове void.

int- це ціле 2-байтне число, діапазон значень від - 32768 до 32767

Після імені функції у дужках () вказуються параметри, які передаються функції під час її виклику. Якщо функція без параметрів – використовується ключове слово void. Функція mainмістить у собі набір команд, налаштування системи та головний цикл програми.

Далі налаштовуємо порт Dна вхід. Режим роботи порту визначається вмістом регістру DDRD(Регістр напряму передачі інформації). Записуємо в цей регістр число "0x00" (0b0000000 – у двійковому вигляді), крім кнопки до цього порту нічого не підключено, тому налаштовуємо весь порт D на вхід. Налаштувати порт порозрядно можна записавши в кожен біт регістра числа 0 або 1 (0-вхід, 1-вихід), наприклад DDRD = 0x81 (0b10000001) - перша та остання лінія порту D працюють на вихід, решта на вхід. Необхідно також підключити внутрішній резистор навантаження. Увімкненням та вимкненням внутрішніх резисторів керує регістр PORTx, якщо порт знаходиться в режимі введення. Запишемо туди одиниці.

Налаштовуємо порт Bна вихід. Режим роботи порту визначається вмістом регістру DDRB. Нічого, крім світлодіода до порту Bне підключено, тому весь порт можна налаштувати на вихід. Це робиться записом у регістр DDRBчисла "0xFF". Для того, щоб при першому включенні світлодіод не загорівся запишемо в порт Bлогічні нулі. Це робиться записом PORTB= 0x00;

Для присвоєння значень використовується символ "=" і називається оператором присвоювання, не можна плутати зі знаком "рівно"

Налаштування портів виглядатиме так:

DDRD = 0x00;
PORTD = 0xFF;
DDRB = 0xFF;
PORTB = 0x00;

Пишемо основний цикл програми. while( " поки " з англ.) - ця команда організує цикл, багаторазово повторюючи тіло циклу до тих пір поки виконується умова, т. е поки вираз у дужках є істинним. У мові Сі прийнято вважати, що вираз істинний, якщо він не дорівнює нулю, і хибно, якщо дорівнює.

Команда виглядає так:

while (умова)
{
тіло циклу
}

У нашому випадку основний цикл складатиметься лише з однієї команди. Ця команда надає регістру PORTBзначення регістра, що інвертується PORTD.

PORTB = ~PIND; //Взяти значення з порту D, проінвертувати його і присвоїти PORTB (записати в PORTB)

// вирази мовою Сі читаються справа наліво

PINDрегістр введення інформації. Щоб прочитати інформацію із зовнішнього виведення контролера, потрібно спочатку перевести потрібний розряд порту в режим введення. Тобто записати у відповідний біт регістру DDRxнуль. Тільки після цього на цей висновок можна подавати цифровий сигнал із зовнішнього пристрою. Далі мікроконтролер прочитає байт із регістру PINx. Вміст відповідного біта відповідає сигналу зовнішньому виведенні порту. Наша програма готова і виглядає так:

#include int main (void) (DDRD = 0x00; // порт D - вхід PORTD = 0xFF; // підключаємо навантажувальний резистор DDRB = 0xFF; // порт B - вихід PORTB = 0x00; // встановлюємо 0 на виході while (1) ( PORTB = ~PIND; //~ знак порозрядного інвертування)))

У мові Сі широко використовуються коментарі. Є два способи написання.

/*Коментар*/
//Коментар

При цьому компілятор не звертатиме уваги на те, що написано в коментарі.

Якщо використовуючи цю ж програму і підключити до мікроконтролера 8 кнопок і 8 світлодіодів, як показано на малюнку 2, то буде зрозуміло, що кожен біт порту Dвідповідає своєму биту порту B. Натискаючи кнопку SB1 – загоряється HL1, натискаючи кнопку SB2 – загоряється HL2 тощо.

Малюнок 2

У статті були використані матеріали із книги Бєлова А.В. "Самовчитель розробника пристроїв на AVR"

Якщо помітили помилку, виділіть фрагмент тексту та натисніть Ctrl+Enter
ПОДІЛИТИСЯ:
Комп'ютери та сучасні гаджети