З чого розпочинати програмування atmega8. Програмування мікроконтролерів AVR. Як прошивати контролер, і які додаткові прилади та аксесуари потрібні для зручної роботи з ними


У цьому навчальному курсі з 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 - файл нашої прошивки та натискаємо кнопку ОК. Тепер можна запустити симуляцію нашої схеми. Для цього натискаємо кнопку "Відтворити" у нижньому лівому куті вікна Протеус.

Здрастуйте, MySkuвчане! Герой нашого огляду – мікроконтролер Atmega8A-16PU. Я розповім вам про програмування даного мк в інтегрованому середовищі розробки CodeVisionAvr, помигаємо світлодіодом, розглянемо плюси та мінуси роботи в даному середовищі. Можливо, надалі це послужить для вас альтернативою вже «народною» Arduino. Якщо зацікавилися, гоу під cut.

Преамбула.
Так вже склалося, що своє знайомство з мк я почав з Arduino. Блимав світлодіодом, підключав різні датчики та шилди, робив різні проекти. Все працювало, мене влаштовувало, проте хотілося чогось більшого. Підвернувся мені один проект, де бере участь Atmega8A, під яку потрібно самостійно написати прошивку. Саме він підштовхнув мене до вивчення процесу програмування голого мк.
Отже, маємо мікроконтролер фірми Atmel, сімейства AVR Atmega8A.

Технічні характеристики:


Розпинування:


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

На жаль, я як шевець - без чобіт, зараз під руками немає програматора, тому я використовуватиму Arduino UNO для завантаження в мк готової прошивки та налаштування ф'юзів. Достатньо лише завантажити скетч «Arduinoisp» із папки прикладів Arduino IDE та підключити за схемою:


Однак, у цього рішення є суттєва вада, про яку розповім трохи пізніше. Перед тим, як приступити до написання програми CodeVisionAvr (далі CvAvr), нам потрібно визначитися, на якій частоті буде працювати наш мк. За замовчуванням, із заводу наш герой працює від внутрішнього rc-генератора на частоті 1Мгц (з можливістю переналаштування на 2, 4 та 8Мгц). Оскільки внутрішній rc-генератор калібрують на заводі за певних умов (точна напруга, температура), то точність його роботи в польових умовах може відрізнятися від 3% до 10%. Для завдань, де не потрібна висока точність тактування, це можна знехтувати, в інших випадках краще використовувати зовнішній кварц. У своєму проекті використав зовнішній кварц на частоту 8 МГц. Тепер нам треба "пояснити" мк, що потрібно працювати від зовнішнього кварцу. Робиться це з допомогою зміни фьюзов. Якщо пояснити «на пальцях», то це щось на зразок БІОСа, як на материнській платі, де ви вказуєте режими її роботи, аналогічно ми повідомляємо мк, в яких режимах, крім частоти, він повинен працювати. Вся інформація зберігатиметься в енергонезалежній пам'яті.
Про прошивку ф'юзів я розповім під спойлером, ті, хто вміє це робити самостійно, можуть гортати далі.

додаткова інформація

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


Тут все просто:
Clock Source – задаємо частоту (External Crystal 3 – 16 Mhz) від зовнішнього кварцу.
Start-up Time – швидкість старту мк після зняття RESET або подачі живлення (16K CK + 4.1ms fast).
Ставимо галку: Ext. Clock/RC Osc./Low-freq. Crystal: enable internal Capacitors (36 pF)
Internal R/C Osc.: leave unchecked! External Crystal: enable full swing (необхідно >8 MHz).
Таким чином, ми отримали Low Fuse 0xEF та High Fuse 0xC9. Чудово, півсправи зробили. Тепер підключаємо мк Arduino UNO, а саму Arduino до комп'ютера відповідно. Запускаємо командний рядок, переходимо до папки з AvrDude. Далі вводимо рядок: avrdude -C avrdude.conf -c avrisp -P COM13 -b 19200 -p m8 -U lfuse:w:0xef:m -U hfuse:w:0xc9:m
Ось як це виглядає на зображенні:


Розберемо рядок, який ввели:
avrisp - це тип нашого ардуїноподібного програматора
COM13 - номер com порту, яким визначається наша Arduino у системі (у вашому випадку його треба подивитися в диспетчері пристроїв)
19200 - швидкість com порту, залишаємо як є
m8 – вказуємо, що наш мк – Atmega8
-U lfuse:w:0xef:m -U hfuse:w:0xc9:m - тут вказані наші Low Fuse 0xEF і High Fuse 0xC9
Будьте УВАЖЛИВІ!!, неправильно вказані Fuse можуть призвести до керування мк (танці з бубном по відновленню нам не потрібні).
Натискаємо «Введення» і на виході отримуємо результат, як на малюнку:


Якщо не з'явилося жодних помилок у процесі, то робота виконана, наш мк тепер працюватиме від зовнішнього кварцу.
Дуже детально про ф'юзи можна прочитати і , а також використовуючи пошук у google.


Тепер ми готові розпочати програмування. Для себе я вибрав середовище розробки CvAvr. Мова програмування буде відрізняється від «ардуїновського», в CvAvr він Сі-подібний. Напишемо наш перший Blink.
Після встановлення та запуску середовища скористаємося майстром створення проектів. Вибираємо "File" - "New" - "Project". На запитання, чи використовуватимемо ми майстер, відповідаємо ствердно. Target AVR Chip Type вказуємо AT90, ATtity, ATmega.
Так виглядає майстер проектів:


На вкладці Chip вибираємо ATmega8A, Clock 8.000 000 Mhz. Переходимо до вкладки Ports. Наш світлодіод буде підключений до 14 виведення мікроконтролера, згідно з розпинуванням - PB0. На вкладці вибираємо Port B, Bit 0 перемикаємо з IN на OUT, тобто. переводимо режим роботи 14 ніжки нашого мк на вихід.


На цьому роботу майстра закінчено. Вибираємо "Program" - "Generate, Save and Exit". Зберігаємо наш проект, наприклад, під назвою Blink.

Отримаємо таку ось онучу

/*******************************************************
Цей program був створений за
CodeWizardAVR V3.12 Advanced
Automatic Program Generator
Chip type: ATmega8A
Program type: Application
AVR Core Clock frequency: 8.000000 MHz
Memory model: Small
External RAM size: 0
Data Stack size: 256
*******************************************************/
#include
#include
// Declare your global variables here

Void main(void)
{
// Declare your local variables here

// Input/Output Ports initialization
// Port B initialization
// Function: Bit7 = In Bit6 = In Bit5 = In Bit4 = In Bit3 = In Bit2 = In Bit1 = In Bit0 = Out
DDRB=(0<// State: Bit7=T Bit6=T Bit5=T Bit4=T Bit3=T Bit2=T Bit1=T Bit0=0
PORTB=(0<

// Port C initialization
// Function: Bit6=In Bit5=In Bit4=In Bit3=In Bit2=In Bit1=In Bit0=In
DDRC=(0<// State: Bit6=T Bit5=T Bit4=T Bit3=T Bit2=T Bit1=T Bit0=T
PORTC=(0<

// Port D initialization
// Function: Bit7 = In Bit6 = In Bit5 = In Bit4 = In Bit3 = In Bit2 = In Bit1 = In Bit0 = In
DDRD=(0<// State: Bit7=T Bit6=T Bit5=T Bit4=T Bit3=T Bit2=T Bit1=T Bit0=T
PORTD=(0<

// Timer/Counter 0 initialization

// Clock value: Timer 0 Stopped
TCCR0=(0<TCNT0 = 0x00;

// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: Timer1 Stopped
// Mode: Normal top=0xFFFF
// OC1A output: Disconnected
// OC1B output: Disconnected
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer1 Overflow Interrupt: Off
// Input Capture Interrupt: Off
// Compare A Match Interrupt: Off
// Compare B Match Interrupt: Off
TCCR1A=(0<TCCR1B=(0<TCNT1H = 0x00;
TCNT1L=0x00;
ICR1H = 0x00;
ICR1L = 0x00;
OCR1AH=0x00;
OCR1AL = 0x00;
OCR1BH = 0x00;
OCR1BL=0x00;

// Timer/Counter 2 initialization
// Clock source: System Clock
// Clock value: Timer2 Stopped
// Mode: Normal top=0xFF
// OC2 output: Disconnected
ASSR=0<TCCR2=(0<TCNT2 = 0x00;
OCR2 = 0x00;

// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK = (0<

// External Interrupt(s) initialization
// INT0: Off
// INT1: Off
MCUCR=(0<

// USART initialization
// USART disabled
UCSRB=(0<

// Analog Comparator initialization
// Analog Comparator: Off
// The Analog Comparator"s positive input is
// connected to the AIN0 pin
// The Analog Comparator"s negative input is
// connected to the AIN1 pin
ACSR=(1<SFIOR=(0<

// ADC initialization
// ADC disabled
ADCSRA=(0<

// SPI initialization
// SPI disabled
SPCR=(0<

// TWI initialization
// TWI disabled
TWCR=(0<

While (1)
{


Тут нічого страшного немає, у проекті вказані режими роботи портів, таймерів, переривань, USART, Analog Comparator, ADC, SPI та підключених бібліотек. Словом, всі параметри, які ми вказували в майстрі, все, крім портів і чіпа, у нас налаштовано за замовчуванням. Основний цикл програми будемо писати в while (1) (текст програми). Т.к. ми працюємо з PB0 (14 ніжка), у циклі програми напишемо:
while (1)
{
PORTB.0=1;
delay_ms (1000);
PORTB.0 = 0;
delay_ms (1000);
}
Тут ми встановлюємо високий рівень PB0, чекаємо 1 секунду і встановлюємо низький рівень, потім цикл повторюється. Ще не забуваємо підключити бібліотеку на початку проекту #include . Наша програма готова! Як бачите, все дуже просто. Тепер вибираємо "Project" - "Build All". Якщо помилок допущено був, побачимо звіт майстра:

Розмір нашої програми становив 198 bytes, і зайняв 2.4% пам'яті мк.
Далі збираємо схему:


Тепер переходимо в папку з нашим проектом, заходимо в папку Debug, потім Exe, там знаходиться файл з розширенням hex. В моєму випадку, це blink.hex.
Залишився останній крок. Копіюємо цей файл до папки з AvrDude. Знову запускаємо командний рядок, переходимо до нашої папки. Вводимо рядок avrdude -C avrdude.conf -c avrisp -P COM13 -b 19200 -p m8 -U flash:w:blink.hex
Так це виглядає на зображенні:


Якщо все ввели правильно, тиснемо «Введення»


Вітаю! Робота виконана, світлодіод повинен вам радісно блимати:)
Висновок.
На закінчення хочу сказати про нестачу ардуїноподібного програматора, CvAvr його просто не підтримує. Маючи на руках, наприклад, AVRISP mkII, виконати прошивку ф'юзів та завантаження програми можна безпосередньо з CodeVisionAvr. До речі, використовувати графічний інтерфейс AvrDude доморощений програматор теж відмовився і працював тільки з командного рядка.
З CodeVisionAvr я розібрався досить швидко, в інтернеті повно текстових та відео уроків. За кілька тижнів освоїв роботу апаратного ШІМ, переривань, таймерів, роботу з кнопками та підключення графічного дисплея. Конкретно мені треба було зробити частину свого проекту: на Atmega8 організувати апаратний 16-бітний ШІМ, підключити 2 кнопки для його керування, а також виводити режими його роботи на графічний дисплей, що з легкістю я і зробив:)

додаткова інформація





Підсумки порівняно з Arduino:
+ Розібратися в CvArv не складно, т.к. є майстер створення проектів;
+ Наявність бібліотек, що підключаються, їх достатньо;
+ Швидка компіляція;
+ Можливість симуляції проекту в Proteus, а так само налагодження його силами вбудованого debugger`а;
+ Розмір програми в рази менший. Стандартний Blink у нас зайняв 198 Байт, аналогічний скетч Arduino IDE 1084 Байт + 2 Кб завантажувач;
+ Можливість реалізувати режими, які не можна зробити на Arduino. Наприклад 16-бітний ШІМ (взагалі, на Arduino можна, але тільки з «милицями»);
+ Можливість застосовувати для своїх проектів мк типу ATtiny, ATmega там, де Arduino буде надмірним;
- Все ж новачку починати освоювати мк краще з Arduino;
- Мова програмування відрізняється від ардуїновського processing`a;
- Бібліотек до Arduino все ж таки більше;
- CodeVisionAvr є платною програмою, є безкоштовні версії з обмеженнями;
Програмуючи «голий» мк у CodeVisionAvr, я отримав величезний досвід роботи у свою скарбничку. Вивчення таймерів, регістрів, режимів роботи, архітектури, читання datasheet збільшить вашу кваліфікацію, розширить кругозір і відкриє нові аспекти роботи з мікроконтролерами.
Як бонус додаю пару фото, коли розбирався з графічним ЖК-дисплеєм, трохи погрався.





P.s. Багато чого хотів написати, але це вже вийде не огляд, а величезна стаття. Готовий відповісти на запитання у межах своєї компетенції, у приваті чи коментарях. Дуже багато уроків з AVR можна підглянути.

Мікроконтролери є невеликими, але одночасно дуже зручними пристосуваннями для тих, хто бажає створювати різні дивовижні роботизовані або автоматизовані речі у себе вдома. В рамках цієї статті буде розглянуто програмування AVR для початківців, різні аспекти та нюанси цього процесу.

Загальна інформація

Мікроконтролери можна зустріти скрізь. Вони є в холодильниках, пральних машинах, телефонах, верстатах на виробництві, розумних будинках і ще в багатьох технічних пристроях. Їх повсюдне застосування обумовлено можливістю заміни складніших і масштабніших аналогових схем пристроїв. Програмування МК AVR дозволяє забезпечити автономне керування електронними пристроями. Ці мікроконтролери можна як найпростіший комп'ютер, що може взаємодіяти із зовнішньою технікою. Так, їм під силу відкривати/закривати транзистори, отримувати дані з датчиків та виводити їх на екрани. Також мікроконтролери можуть здійснювати різну обробку вхідної інформації подібно до персонального комп'ютера. Якщо освоїти програмування AVR з нуля і дійти рівня професіонала, то відкриються практично безмежні можливості керувати різними пристроями з допомогою портів вводу/вывода, і навіть зміни їх коду.

Трохи про AVR

У рамках статті буде розглянуто сімейство мікроконтролерів, які випускають фірма Atmel. Вони мають досить непогану продуктивність, що дозволяє використовувати їх у багатьох аматорських пристроях. Широко застосовуються й у промисловості. Можна зустріти в такій техніці:

  1. Побутовий. Пральні машини, холодильники, мікрохвильові печі та інше.
  2. Мобільний. Роботи, засоби зв'язку тощо.
  3. Обчислювальної. Системи керування периферійними пристроями, материнські плати.
  4. Розважальний. Прикраси та дитячі іграшки.
  5. Транспорт. Системи безпеки та управління двигуном автомобіля.
  6. Промислове обладнання. Системи керування верстатами.

Це, звичайно, не всі сфери. Вони застосовуються там, де вигідно використовувати не набір мікросхем, що управляють, а один мікроконтролер. Це можливо завдяки низькому енергоспоживання і для написання програм використовуються мови С і Assembler, трохи змінені під сімейство мікроконтролерів. Такі зміни необхідні через слабкі обчислювальні можливості, які обчислюються, як правило, в десятках кілобайт. AVR-програмування без вивчення цих мов неможливо.

Як отримати свій перший мікроконтролер?

AVR-програмування вимагає:

  1. Наявність необхідного середовища розробки.
  2. Власне самих мікроконтролерів.

Другий пункт розглянемо докладніше. Існує три можливості обзавестися необхідним пристроєм:

  1. Купити безпосередньо сам мікроконтролер.
  2. Придбати пристрій у складі конструктора (наприклад - Arduino).
  3. Зібрати мікроконтролер самостійно.

У першому пункті нічого складного немає, тому одразу перейдемо до другого та третього.

Обзавестися пристроєм у складі конструктора

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

Самостійне складання

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

Як працювати?

Отже, припустимо, що питання з мікроконтролером вирішилося. Далі буде вважатися, що він був придбаний або куплений самостійно. Що ще потрібно, щоб освоїти програмне забезпечення AVR? Для цієї мети потрібне середовище розробки (як базис підійде і звичайний блокнот, але рекомендую зупинитися на Notepad++). Хоча існують інші програми для програмування AVR, наведене забезпечення зможе впоратися з усіма вимогами. Також потрібний програматор. Його можна придбати у найближчому магазині, замовити по інтернету або зібрати самостійно. Не завадить і друкована плата. Вона не обов'язкова, але її використання дозволяє заощадити свої нерви та час. Також купується/створюється самостійно. І останнє – це джерело харчування. Для AVR необхідно забезпечити надходження напруги на 5В.

Де та як вчитися?

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

Порти введення/виводу

Це надзвичайно важлива тема. Без розуміння того, як працюють порти вводу/виводу, неможливо внутрішньосхемне програмування AVR взагалі. Адже взаємодія мікроконтролера із зовнішніми пристроями здійснюється саме за їх посередництва. На погляд новачка може здатися, що порт - це досить заплутаний механізм. Щоб уникнути такого враження, не детально розглядатимемо схему його роботи, а лише отримаємо загальне уявлення про це. Розглянемо програмну реалізацію. Як приклад пристрою був обраний мікроконтролер AtMega8 - один із найпопулярніших із усього сімейства AVR. Порт введення/виводу є три регістри, які відповідають за його роботу. Фізично вони реалізовуються як ніжки. Кожна з них відповідає певний біт в реєстрі, що управляє. Кожна ніжка може працювати як введення інформації, так її виведення. Наприклад, на неї можна повісити функцію запалювання світлодіода або натискання кнопки. До речі, три регістри, про які йшлося, це: PORTx, PINx та DDRx. Кожен із них є восьмирозрядним (не забуваємо, що ми розглядаємо AtMega8). Тобто один біт займається певною ніжкою.

Робота регістрів

Найбільш вагомим у плані орієнтації є керуючий DDRx. Він також є восьмирозрядним. Значення для нього можуть бути записані 0 або 1. Як змінюється робота контролера під час використання нулів та одиниці? Якщо у певному біті виставити 0, то відповідна йому ніжка буде переключена у режим входу. І з неї можна буде зчитувати дані, що йдуть із зовнішніх пристроїв. Якщо встановити 1, то мікроконтролер зможе керувати чимось (наприклад, наказати транзистору пропустити напругу і запалити світлодіод). Другим за важливістю є PORTx. Він займається керуванням стану ніжки. Давайте розглянемо приклад. Допустимо, у нас є порт виведення. Якщо ми встановлюємо логічну одиницю в PORTx, то надсилається сигнал від мікроконтролера керуючого пристрою почати роботу. Наприклад, запалити світлодіод. При встановленні нуля він гаситиметься. Тобто працювати з керуючим регістром DDRx завжди, немає потреби. І насамкінець давайте про PINx. Цей регістр відповідає за відображення стану ніжки контролера, коли вона налаштована стан введення. Слід зазначити, що PINx може працювати лише у режимі читання. Записати в нього нічого не вийде. Але прочитати поточний стан ніжки - це без проблем.

Робота з аналогами

AVR не є єдиними мікроконтролерами. Цей ринок поділений між кількома великими виробниками, а також між численними китайськими пристроями, що імітують, і саморобками. Багато в чому вони подібні. Наприклад, програмування PIC/AVR не відрізняється. І якщо є розуміння чогось одного, то зрозуміти все інше буде легко. Але починати шлях рекомендуємо все ж таки з AVR завдяки його грамотній структурі, дружелюбності до розробника та наявності великої кількості допоміжних матеріалів, через що процес розробки можна значно прискорити.

Техніка безпеки

Коли вестиметься програмування мікроконтролерів AVR на "СІ" або на "Ассемблері", необхідно працювати дуже обережно. Справа в тому, що виставивши певну комбінацію регістрів та змінивши внутрішні налаштування, можна спокійно заблокувати мікроконтролер. Особливо це стосується ф'юзів. Якщо немає впевненості у правильності своїх дій, то краще відмовитись від їх використання. Це саме стосується і програматорів. Якщо купувати заводську апаратуру, вона прошиватиме мікроконтролери без проблем. При складанні своїми руками може виникнути сумна ситуація, коли програматор заблокує пристрій. Це може статися як через помилку в програмному коді, так і через неполадки в ньому самому. До речі, про ще один (цього разу позитивний) момент, який раніше мимохіть згадувався, але так і не був розкритий повністю. Зараз практично всі сучасні мікроконтролери мають функцію внутрішньосхемного програмування. Що це означає? Припустимо, що пристрій був запаяний на платі. І щоб змінити його прошивку, зараз не потрібно його випоювати, адже таке втручання може зашкодити сам мікроконтролер. Достатньо підключитися до відповідних висновків і перепрограмувати його за їх допомогою.

Яку модель вибрати?

У рамках статті було розглянуто AtMega8. Це досить посередній за своїми характеристиками мікроконтролер, якого вистачає для більшості виробів. Якщо є бажання створити щось масштабне, то можна брати своєрідних монстрів на кшталт Atmega128. Але вони розраховані більш досвідчених розробників. Тому, якщо немає достатньої кількості досвіду, краще починати з невеликих і простих пристроїв. До того ж вони і значно дешевші. Погодьтеся, одна справа випадково заблокувати мікроконтролер за сто карбованців, а зовсім інше – за півтисячі. Краще набити собі руку і розібратися в різних аспектах функціонування, щоб не втрачати значних сум. Спочатку можна почати з AtMega8, а потім вже орієнтуватися за своїми потребами.

Висновок

Ось і було розглянуто тему програмування AVR у найзагальніших рисах. Звичайно, ще багато про що можна розповідати. Приміром, був розглянуто маркування микроконтроллеров. А воно може багато про що сказати. Так, в основному мікроконтролери працюють на напрузі 5В. Тоді як наявність, наприклад, літери L може сказати про те, що для роботи пристрою достатньо лише 2,7 В. Як бачите, часом знання про маркування можуть відіграти дуже важливу роль у плані коректної та довговічної роботи пристроїв. Час функціонування мікроконтролерів – це теж цікава тема. Кожен пристрій розрахований на певний період. Так, дехто може відпрацювати тисячу годин. Інші ж мають гарантійний запас 10 000!

Мікроконтролери (далі МК) міцно увійшли до нашого життя, на просторах інтернету можна зустріти дуже багато цікавих схем, які виконані на МК. Чого тільки не можна зібрати на МК: різні індикатори, вольтметри, прилади для дому (пристрої захисту, комутації, термометри…), металошукачі, різні іграшки, роботи тощо. можна перераховувати дуже довго. Першу схему на мікроконтролері я побачив років 5-6 тому у журналі радіо, і практично відразу ж перегорнув сторінку, подумавши про себе "все одно не зможу зібрати". Дійсно, в той час МК для мене були чимось дуже складним і незрозумілим пристроєм, я не уявляв, як вони працюють, як їх прошивати, і що робити з ними у разі неправильної прошивки. Але близько року тому, я вперше зібрав свою першу схему на МК, це була схема цифрового вольтметра на 7 сегментних індикаторах і мікроконтролері ATmega8. Так вийшло, що мікроконтролер я купив випадково, коли стояв у відділі радіодеталей, хлопець переді мною купував МК, і я теж вирішив купити, і спробувати зібрати щось. У своїх статтях я розповім вам про мікроконтролери AVR, навчу вас працювати з ними, розглянемо програми для прошивки, виготовимо простий та надійний програматор, розглянемо процес прошивки та найголовніше проблеми, які можуть виникнути і не тільки у новачків.

Основні параметри деяких мікроконтролерів сімейства AVR:

Мікроконтролер

Пам'ять FLASH

Пам'ять ОЗУ

Пам'ять EEPROM

Порти введення/виводу

U харчування

Додаткові параметри МК AVR mega:

Робоча температура: -55 ... +125 * С
Температура зберігання: -65 ... +150 * С
Напруга на виводі RESET щодо GND: max 13В
Максимальна напруга живлення: 6.0В
Максимальний струм лінії введення/виводу: 40мА
Максимальний струм по лінії живлення VCC та GND: 200мА

Розміщення висновків моделей ATmega 8X

Розміщення висновків моделей ATmega48x, 88x, 168x

Розміщення висновків у моделей ATmega8515x

Розміщення висновків у моделей ATmega8535x

Розміщення висновків у моделей ATmega16, 32x

Розміщення висновків у моделей ATtiny2313

Наприкінці статті прикріплено архів з датташитами на деякі мікроконтролери.

Настановні FUSE біти MK AVR

Запам'ятайте, запрограмований фьюз – це 0, не запрограмований – 1. Обережно варто ставитись до виставлення ф'юзів, помилково запрограмований ф'юз може заблокувати мікроконтролер. Якщо ви не впевнені, який саме фьюз потрібно запрограмувати, краще на перший раз прошийте МК без фьюзів.

Найпопулярнішими мікроконтролерами у радіоаматорів є ATmega8, потім йдуть ATmega48, 16, 32, ATtiny2313 та інші. Мікроконтролери продаються в TQFP корпусах та DIP, новачкам рекомендую купувати у DIP. Якщо купите TQFP, буде проблематичніше їх прошити, доведеться купити або паяти плату т.к. у них ніжки розташовуються дуже близько одна від одної. Раджу мікроконтролери в DIP корпусах, ставити на спеціальні панельки, це зручно і практично, не доведеться випоювати МК якщо закортить перепрошити, або використовувати його для іншої конструкції.

Багато сучасні МК мають можливість внутрисхемного програмування ISP, тобто. якщо ваш мікроконтролер запаяний на плату, то для того, щоб змінити прошивку нам не доведеться випоювати його з плати.

Для програмування використовується 6 висновків:
RESET- Вхід МК
VCC- Плюс харчування, 3-5В, залежить від МК
GND- Загальний провід, мінус харчування.
MOSI- Вхід МК (інформаційний сигнал у МК)
MISO- Вихід МК (інформаційний сигнал із МК)
SCK- Вхід МК (тактовий сигнал МК)

Іноді ще використовують виводи XTAL 1 і XTAL2, на ці висновки чіпляється кварц, якщо МК буде працювати від зовнішнього генератора, ATmega 64 і 128 виводу MOSI і MISO не застосовуються для ISP програмування, замість них виведення MOSI підключають до ніжки PE0, a MISO до PE1. При з'єднанні мікроконтролера з програматором, що з'єднують дроти повинні бути якомога коротшими, а кабель, що йде від програматора на порт LPT, так само не повинен бути занадто довгим.

У маркуванні мікроконтролера можуть бути незрозумілі літери з цифрами, наприклад Atmega 8L 16PU, 8 16AU, 8A PU та ін. Літера L означає, що МК працює від нижчої напруги, ніж МК без літери L, зазвичай це 2.7В. Цифри після дефісу або пробілу 16PU або 8AU говорять про внутрішню частоту генератора, який є в МК. Якщо фьюзи виставлені на роботу від зовнішнього кварцу, кварц повинен бути встановлений на частоту, що не перевищує максимальну за даташит, це 20МГц для ATmega48/88/168, і 16МГц для інших атмег.

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

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

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

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

Перша програма на Сі для 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зварганив плагінчик для файрфокса, що полегшує навігацію по моєму сайту та форуму.
Обговорення та скачування,

Поділіться з друзями або збережіть для себе:

Завантаження...