У цьому матеріалі я хочу розповісти про те, як писати програми для мікроконтролерів (Microcontroller Unit, MCU) Cortex-M, як STM32, використовуючи лише набір інструментів ARM і документацію, підготовлену STMicroelectronics. У деяких читачів може постати питання про те, чому комусь це може знадобитися. Якщо вам ця ідея, на перший погляд, не здалася дуже страшною, то, можливо, вам буде цікаво те, про що піде мова в цьому матеріалі. І, до речі, подумаємо про те, кому і навіщо це може стати в нагоді. Звичайно, розробляти програми для MCU STM32 можна за допомогою наявних фреймворків. Це може бути ST HAL, звичайний CMSIS, або навіть щось, ближче Arduino. Але… що тут цікавого? Адже, зрештою, той, хто користується якимось фреймворком, повністю залежить від документації до нього та від його розробників. І, з іншого боку, якщо документація до STM32 здається комусь, що працює з цією платформою, так би мовити, маренням сивої кобили, то чи можна говорити про те, що ця людина по-справжньому розуміє цю платформу? Тому давайте поговоримо про низькорівневе програмування STM32 і дістанемося від включення живлення STM32 до Hello, World. На низькому рівні мікроконтролер не дуже відрізняється від повнофункціонального комп'ютера, заснованого на процесорі від Intel або AMD. Тут є як мінімум одне процесорне ядро, яке ініціалізується після подачі та стабілізації зовнішнього живлення. У цей момент проводиться зчитування завантажувача, код якого знаходиться за адресою, наперед відомому мікроконтролеру. А у звичайних комп'ютерах подібну роль відіграє BIOS.У випадку з MCU це код, що знаходиться по певному зміщенню в (зазвичай) інтегрованої пам'яті, призначеної тільки для читання. Те, що відбувається потім, повністю залежить від цього коду. Загалом цей код вирішує основні завдання з підготовки системи до роботи. Наприклад, задає таблицю векторів переривань і записує певні дані деякі регістри. Дуже важливим завданням є ініціалізація покажчика стека (Stack Pointer, SP). На початку роботи системи деякі дані з ROM копіюються в RAM. У результаті викликається функція main() , що схоже на запуск операційної системи комп'ютера, який виконується після завершення підготовки системи до роботи засобами BIOS. Можливо, аналогом Hello, World для STM32 можна назвати приклад з мого фреймворку для STM32 Nodate, який я ласкаво називаю Pushy. Він ще простіше, ніж традиційний приклад Blinky, оскільки він використовує лише регістри управління тактуванням та скиданням (Reset & Clock Control, RCC) та базові можливості інтерфейсу введення/виводу загального призначення (General-Purpose Input/Output, GPIO). Код цього прикладу зчитує вхідний регістр GPIO-піну та підлаштовує значення на вихідному піні відповідно до вхідного. Завдяки цьому можна, за допомогою кнопки, вмикати та вимикати світлодіод. Ось код цього прикладу: Тут можна відразу звернути увагу на два найпомітніші елементи. Перший - це функція main(), яку викликає система. Другий – це підключення модуля GPIO. Цей модуль містить статичний C++-клас, можливості якого використовуються для запису даних у GPIO-вихід, до якого підключено світлодіод. Його можливості застосовуються і під час читання даних із входу, якого підключена кнопка.Тут можна бачити ще й згадку імен пінів плати Blue Pill (STM32F103C8), але в прикладі є і для інших плат, які можна активувати, розкоментувавши відповідні рядки. Де саме у цьому прикладі використовуються регістри групи RCC? У назві цих регістрів міститься натяк на те, що вони дозволяють керувати тактовою частотою MCU. Їх можна порівняти з перемикачами, які можуть перебувати у двох станах - "включено" або "вимкнено", включаючи та відключаючи відповідні можливості MCU. Якщо подивитися, наприклад, на опис регістра RCC_AHBENR у розділі 6.4 посібника з STM32F0xx, то ми побачимо біт, маркований як IOPAEN (Input/Output Port A ENable, включення порту вводу/виводу A), який керує частотою для периферії, підключеної до . Те саме стосується й інших портів.
Розділ 6.4.6 посібника з STM32F0xx, опис регістра RCC_AHBENR
Як можна бачити на наведеній вище ілюстрації, RCC_AHBENR — це регістр, що відповідає за включення AHB. Це одна з шин усередині MCU, до якої підключені процесорне ядро, SRAM, ROM і периферійні пристрої. Шини AHB (Advanced High-performance Bus) та APB (Advanced Peripheral Bus) описані у специфікації AMBA фірми Arm.
Розділ 2.1 посібника з STM32F0xx, архітектура STM32F0xx
В цілому можна відзначити, що AHB - це швидша шина, що з'єднує процесорне ядро зі SRAM, ROM і з високошвидкісною периферією. Більш повільна периферія підключається до повільнішої шини APB. Між AHB та APB є міст, що дозволяє пристроям, підключеним до них, взаємодіяти один з одним. Як було зазначено, першим при включенні STM32 запускається код завантажувача.У випадку MCU STM32F042x6 універсальний код завантажувача, написаний на асемблері Thumb, можна знайти тут. Це звичайний код, що надається STMicroelectronics (наприклад, для STM32F0xx) разом із CMSIS-пакетом. Він ініціалізує MCU та викликає функцію SystemInit() , оголошену в низькорівневому C-коді CMSIS (ось приклад для STM32F0xx). Функція SystemInit() скидає системні регістри, що відповідають за частоту, що призводить до використання стандартної частоти HSI (High Speed Internal oscillator, високошвидкісний внутрішній генератор). Після виконання процедур налаштування libc (у даному випадку використовується Newlib — допоміжна C/C++-бібліотека), вона нарешті викликає функцію main() наступною командою: Ця інструкція, назва якої розшифровується як Branch with Link (перехід із збереженням адреси повернення), призводить до переходу до заданої позначки. У цей момент ми опиняємось у функції main() нашого прикладу Pushy. Після цього у справу вступають можливості класу GPIO. Перший метод класу, що викликається нами, — це GPIO::set_output() . Він дозволяє зробити зазначений пін (з підключеним до нього резистором, що підвищує) вихідним. Саме тут ми зустрічаємося з першою різницю між сімействами MCU STM32. Справа в тому, що більш старі MCU, засновані на Cortex-M3 F1, мають GPIO-периферію, що дуже сильно відрізняється від тієї, яка використовується в їх новіших побратимах сімейств F0, F4 і F7. Це виявляється у тому, що з роботі з пінами STM32F1xx потрібно записувати в єдиний регістр безліч опцій: А в інших згаданих сімействах MCU є окремі регістри для кожної опції (режим, швидкість, резистор, що підвищує або знижує, тип): Запис опцій в регістри виконується за допомогою побітових операцій, що використовуються для запису значень із застосуванням бітових масок. Імена регістрів зазвичай добре описують їх призначення. ». Те, в якому стилі працювати, залежить від програміста.Однак, у випадку з налаштуванням вхідних пінів, я віддаю перевагу більш сучасному способу налаштування GPIO. В результаті виходить компактний і акуратний код, що нагадує наступний, а не той жах, який характерний для STM32F1xx: Для читання даних із вхідного піна ми користуємося IDR (Input Data Register, регістр вхідних даних) для банку GPIO, з яким працюємо: Аналогічно виглядає і використання ODR (Output Data Register, регістр вихідних даних), за допомогою якого здійснюється виведення даних на пін: І, нарешті, у наведеному вище коді є сутність instance , яка являє собою посилання на запис у структурі std::vector . Вона була статично створена під час запуску MCU. У ній зареєстровані властивості периферії: Якщо периферійний пристрій існує (тобто є в CMSIS-заголовку для конкретного MCU, наприклад, для STM32F042), то в структурі GPIO_instance створюється запис, що вказує на пам'ять, відповідну регістрів цього пристрою ( regs ). До цих записів, як і до мета-інформації, що міститься у яких, потім можна звертатися. Наприклад, можна дізнатися про стан пристрою: Перевага такого підходу, як ми вже бачили, полягає в тому, що для роботи з різними периферійними пристроями, незалежно від того, що це за пристрої, можна користуватися одним і тим самим кодом. Справа в тому, що ці пристрої ідентичні в плані регістрів, що використовуються. Клас RCC теж цікавиться тим, чи існує той чи інший периферійний пристрій. Робиться це для того, щоб уникнути різного роду сюрпризів. При цьому використовуються самі визначення препроцесора CMSIS. Після перевірки існування пристрою увімкнути його досить просто: На додаток до зміни значення відповідного біта (ph.enable) ми підраховуємо посилання.Це робиться для того, щоб випадково не вимкнути периферійний пристрій, який використовується в іншому місці коду. Після того, як ми розібралися з наведеним вище матеріалом, у нас має з'явитися деяке розуміння того, як приклад Pushy працює на низькому рівні. Тепер ми можемо його зібрати та запустити. Для цього нам знадобиться, як було сказано, набір інструментів ARM і фреймворк Nodate. Перший можна встановити за допомогою використовуваного вами менеджера пакетів (йдеться про пакет arm-none-eabi-gcc) або завантаживши його з сайту Arm. Фреймворк Nodate можна встановити із GitHub. Після цього шлях до кореневої папки фреймворку потрібно записати в глобальну системну змінну NODATE_HOME. Після того, як ці завдання вирішені, потрібно перейти в папку Nodate , а потім в підпапку examples/stm32/pushy . У ній треба відкрити файл Makefile і вказати передустановки, розраховані на плату (там зараз є передустановки для Blue Pill, Nucleo-F042K6, STM32F4-Discovery, Nucleo-746ZG). Далі, треба відкрити файл src/pushy.cpp і розкоментувати рядки, які стосуються цільової плати. Далі, у папці, в якій знаходиться Makefile, потрібно зібрати проект командою make. Цільова плата має бути підключена до комп'ютера за допомогою ST-Link, на комп'ютері має бути встановлена програма OpenOCD. Якщо це так, MCU можна прошити командою make flash. Після цього відповідний образ буде записаний у пам'ять пристрою. Коли кнопка підключена до піну і Vdd, що використовується в коді, натискання на цю кнопку повинно запалювати відповідним чином підключений до плати світлодіод. Тут показано простий приклад низькорівневого програмування STM32.Освоївши його, ви, фактично, знаходитесь лише трохи нижче рівня прикладу Blinky. Сподіваюся, я змогла показати те, що низькорівневе програмування STM32 це зовсім нескладно. Які інструменти ви використовуєте для написання програм для STM32? Тепер, коли ми вже ознайомлені з деякими можливостями та функціями мікроконтролерів, природно виникає логічне питання: що потрібно для програмування мікроконтролерів? Які необхідні програми та пристрої, де їх взяти? Для того щоб мікроконтролер міг вирішувати завдання і виконувати певні функції, його потрібно запрограмувати, тобто записати в нього програму або код програми. Насамперед, перш ніж приступити до написання будь-якої програми, а точніше коду програми, слід чітко уявляти, які функції виконуватиме мікроконтролер. Тому спочатку потрібно визначити кінцеву мету програми. Коли вона визначена та цілком зрозуміла, тоді складається алгоритм роботи програми. Алгоритм – це послідовність виконання команд. Застосування алгоритмів дозволяє чіткіше структурувати процес написання коду, а при написанні складних програм часто дозволяє скоротити час, що витрачається на їх розробку та налагодження. Наступним етапом після складання алгоритму є безпосереднє написання програмного коду. Програми для мікроконтролерів пишуться мовою Сі або Асемблері. Тільки асемблер більше відноситься до набору інструкцій, ніж до мови програмування і є мовою низького рівня. Ми будемо писати програми на Сі, яка відноситься до мови високого рівня.Програми на Сі пишуться набагато швидше, порівняно з аналогічними на Асемблері. До того ж, всі складні програми пишуться переважно на Сі. Тут ми не будемо порівнювати переваги та недоліки написання програм на Ассемблері та Сі. Згодом, придбавши деякий досвід у програмуванні МК, ви самі собі зробите корисні висновки. Сам код програми можна писати в будь-якому стандартному текстовому редакторі, наприклад, у Блокноті. Однак на практиці користуються зручнішими редакторами, про які буде сказано далі. Написаний нами код на Сі ще зовсім не зрозумілий мікроконтролеру, оскільки МК розуміє команди тільки в двійковій (або шістнадцятковій) системі, яка є набір нулів і одиниць. Тому Си-шний код потрібно перетворити на нулі та одиниці. Для цього застосовується спеціальна програма, яка називається компілятор, а сам процес перетворення коду називається компіляція. Далі відкомпільований готовий код потрібно помістити в мікроконтролер, а точніше записати його на згадку про мікроконтролер або, простіше кажучи, прошити мікроконтролер. Для прошивки МК застосовується пристрій, що називається програматор. Залежно від типу програматора вхід його підключається до COM або USB порту, а вихід до певних висновків мікроконтролера. Існує широкий вибір програматорів та налагоджувальних плат, проте нас цілком влаштує найпростіший програматор USBASP, який у Китаї коштує не більше 3$. Після того, як мікроконтролер прошитий, виконується налагодження та тестування програми на реальному пристрої або, як ще кажуть, на «залізі». Тепер давайте підсумуємо етапи програмування мікроконтролерів. При написанні простих програм можна уникнути другого пункту, т. е. без складання алгоритму на папері, його досить пам'ятати. Слід зауважити, що налагодження та тестування програми також виконують до прошивки МК. Існує безліч корисних та зручних програм для програмування МК. Вони бувають як платні, так і безкоштовні. Всі ці програми відносяться до IDE – Integrated Development Environment – інтегроване середовище розробки. У них можна писати код, компілювати та налагоджувати його. Слід звернути увагу на Code Vision AVR. Ця IDE дозволяє спростити і прискорити написання коду. На початковому етапі програмування всі програми краще прописувати вручну, без будь-яких спрощень. -перше, вона абсолютно безкоштовна і постійно оновлюється, а по-друге вона розроблена компанією, що виготовляє мікроконтролери на яких ми навчатимемося програмувати. Прошиватимемо мікроконтролери ми будемо за допомогою додаткової програми AVRDUDE . Якщо мікроконтролера в наявності немає, його роботу можна емітувати за допомогою програми Proteus. Вона значно спрощує процес налагодження програми навіть за наявності МК, щоб його часто не перепрошувати, адже будь-який МК має кінцеву кількість перезаписів, хоча це число і досить велике. При прошивці та налагодженні МК його зручно розташовувати на макетній платі, але це не обов'язково.Тому для більшої зручності нагоді і макетна плата. Існує великий вибір макетних плат, проте я вам рекомендую брати ту, яка має по можливості більше отворів. Ще один важливий елемент, який нам знадобиться – це технічна документація на МК, яка називається datasheet. Загалом, потрібно завантажити dataheet на мікроконтролер ATmega8. Отже, повний набір для програмування МК складається з таких елементів: 5) Програматор USB ASP (+ драйвер на нього) 7) Мікроконтролер ATmega8 Якщо мікроконтролера немає, не варто відкладати вивчення мікроконтролерів на потім, достатньо завантажити і встановити: Тепер, коли ми вже ознайомлені з деякими можливостями та функціями мікроконтролерів, природно виникає логічне питання: що потрібно для програмування мікроконтролерів?.. Які необхідні програми та пристрої, де їх взяти? Для того щоб мікроконтролер міг вирішувати завдання і виконувати певні функції, його потрібно запрограмувати, тобто записати в нього програму або код програми. Перш за все, перш ніж приступити до написання будь-якої програми, а точніше коду програми, слід чітко уявляти, які функції буде виконувати мікроконтролер. послідовність виконання команд.Застосування алгоритмів дозволяє чіткіше структурувати процес написання коду, а при написанні складних програм часто дозволяє скоротити час, що витрачається на їх розробку та налагодження. Наступним етапом після складання алгоритму є безпосереднє написання програмного коду. Програми для мікроконтролерів пишуться мовою Сі або Асемблері. Тільки асемблер більше відноситься до набору інструкцій, ніж до мови програмування і є мовою низького рівня. Ми будемо писати програми на Сі, яка відноситься до мови високого рівня. Програми на Сі пишуться набагато швидше, порівняно з аналогічними на Асемблері. До того ж, всі складні програми пишуться переважно на Сі. Тут ми не будемо порівнювати переваги та недоліки написання програм на Ассемблері та Сі. Згодом, придбавши деякий досвід у програмуванні МК, ви самі собі зробите корисні висновки. Сам код програми можна писати в будь-якому стандартному текстовому редакторі, наприклад, у Блокноті. Однак на практиці користуються зручнішими редакторами, про які буде сказано далі. Написаний нами код на Сі ще зовсім не зрозумілий мікроконтролеру, оскільки МК розуміє команди тільки в двійковій (або шістнадцятковій) системі, яка є набір нулів і одиниць. Тому Си-шний код потрібно перетворити на нулі та одиниці. Для цього застосовується спеціальна програма, яка називається компілятор, а сам процес перетворення коду називається компіляція. Далі відкомпільований готовий код потрібно помістити в мікроконтролер, а точніше записати його на згадку про мікроконтролер або, простіше кажучи, прошити мікроконтролер. Для прошивки МК застосовується пристрій, що називається програматор. Залежно від типу програматора вхід його підключається до COM або USB порту, а вихід до певних висновків мікроконтролера. Існує широкий вибір програматорів та налагоджувальних плат, проте нас цілком влаштує найпростіший програматор USBASP, який у Китаї коштує не більше 3$. Після того, як мікроконтролер прошитий, виконується налагодження та тестування програми на реальному пристрої або, як ще кажуть, на «залізі». Тепер давайте підсумуємо етапи програмування мікроконтролерів. При написанні найпростіших програм можна обійтися без другого пункту, тобто. е. без складання алгоритму на папері, його достатньо пам'ятати. Слід зауважити, що налагодження та тестування програми також виконують до прошивки МК. Існує безліч корисних та зручних програм для програмування МК. Вони бувають як платні, і безкоштовні. Серед них можна виділити три основні: Всі ці програми відносяться до IDE – Integrated Development Environment – інтегроване середовище розробки. Вони можна писати код, компілювати і налагоджувати його. Потрібно звернути увагу на Code Vision AVR. Ця IDE дозволяє спростити та прискорити написання коду. Проте програма платна. На початковому етапі програмування всі програми краще прописувати вручну, без будь-яких спрощень. Це допоможе швидко набути необхідні навички, а надалі добре розуміти та редагувати під свої потреби коди, написані кимось іншим. Тому рекомендую використовувати програму Atmel Studio. По-перше, вона абсолютно безкоштовна і постійно оновлюється, а по-друге вона розроблена компанією, що виготовляє мікроконтролери, на яких ми будемо вчитися програмувати. Прошиватимемо мікроконтролери ми будемо за допомогою додаткової програми AVRDUDE . Якщо мікроконтролера в наявності немає, його роботу можна емітувати за допомогою програми Proteus. Вона значно спрощує процес налагодження програми навіть за наявності МК, щоб його часто не перепрошувати, адже будь-який МК має кінцеву кількість перезаписів, хоча це і досить велике. При прошивці та налагодженні МК його зручно розташовувати на макетній платі, але це не обов'язково. Тому для більшої зручності стане в нагоді і макетна плата. Існує великий вибір макетних плат, проте я вам рекомендую брати ту, яка має якомога більше отворів. Коли ми почнемо підключати семисегментні індикатори, ви оціните переваги великих макетних плат. Ще один важливий елемент, який стане нам у нагоді – це технічна документація на МК, звана datasheet. Загалом потрібно скачати dataheet на мікроконтролер ATmega8. Отже, повний набір для програмування МК складається з таких елементів: 5) Програматор USB ASP (+ драйвер на нього) 7) Мікроконтролер ATmega8 Якщо мікроконтролера немає, не варто відкладати вивчення мікроконтролерів на потім, достатньо завантажити і встановити: Сучасну електроніку важко уявити без мікроконтролерів. Застосування мікроконтролерів дозволяє спростити процес розробки пристрою, дозволяє здешевити його, зробити його більш гнучким та інтелектуальним. На даний момент існує безліч мікроконтролерів різних фірм, наприклад PIC-контролери фірми Microchip, AVR-контролери фірми Atmel, STM-мікроконтролери фірми STMicroelectronics, мікроконтролери на ядрі ARM різних виробників. Я займаюся розробкою програм для пристроїв на мікроконтролерах. Мікроконтролери, на які ми розробляємо програми:
Як перевіряється працездатність програми:
Якими середовищами розробки ми користуємось:
Мови, що використовуються для написання програм:
Можлива також наша розробка схеми пристрою та розведення двосторонньої друкованої плати у програмі P-CAD 2006 за додаткову плату. Як відбувається оплата і що Ви отримуєте наприкінці розробки:
Замовити програму Ви можете зараз, заповнивши форму замовлення. Я отримаю від Вас повідомлення та відповім Вам відразу ж як з'явиться можливість електронною поштою, вказаною вами у формі замовлення. Далі можна буде вказати Skype, ICQ чи інші засоби спілкування через Інтернет.
Низькорівневе програмування STM32: від включення живлення до Hello, World
STM32 дуже схожий на компьютер
Приклад Pushy
#include int main() < // const uint8_t led_pin = 3; // Nucleo-f042k6: Port B, pin 3. // Const GPIO_ports led_port = GPIO_PORT_B; // const uint8_t led_pin = 13; // STM32F4-Discovery: Port D, pin 13 (помаранчевий) // const GPIO_ports led_port = GPIO_PORT_D; // const uint8_t led_pin = 7; // Nucleo-F746ZG: Port B, pin 7 (синій) // const GPIO_ports led_port = GPIO_PORT_B; const uint8_t led_pin = 13; / / Blue Pill: Port C, pin 13.const GPIO_ports led_port = GPIO_PORT_C; // const uint8_t button_pin = 1; // Nucleo-f042k6 (PB1) // const GPIO_ports button_port = GPIO_PORT_B; // const uint8_t button_pin = 0; // STM32F4-Discovery (PA0) // const GPIO_ports button_port = GPIO_PORT_A; // const uint8_t button_pin = 13; //Nucleo-F746ZG (PC13) //const GPIO_ports button_port = GPIO_PORT_C; const uint8_t button_pin = 10; // Blue Pill const GPIO_ports button_port = GPIO_PORT_B; // Встановити режим виведення на піні, до якого підключено світлодіод GPIO :: set_output (led_port, led_pin, GPIO_PULL_UP); GPIO::write(led_port, led_pin, GPIO_LEVEL_LOW); // Встановити режим введення на піні, якого підключена кнопка GPIO::set_input(button_port, button_pin, GPIO_FLOATING); // Якщо кнопка натиснута (перехід від високого до низького стану), то 'button_down' буде в низькому стані в тому випадку, якщо кнопка буде натиснута. // Якщо кнопка не натиснута (перехід від низького стану до високого, Vdd), то 'button_down' буде у високому стані в тому випадку, якщо кнопка буде натиснута. uint8_t button_down; while (1) < button_down = GPIO::read(button_port, button_pin); if (button_down == 1) < GPIO::write(led_port, led_pin, GPIO_LEVEL_HIGH); >else < GPIO::write(led_port, led_pin, GPIO_LEVEL_LOW); >> return 0; >
Низькорівневе програмування
Клас GPIO
// Управління введенням/виводом розподілено між двома комбінованими регістрами (CRL, CRH). if (pin < 8) < // Встановимо регістр CRL (CNF & MODE).uint8_t pinmode = pin * 4; uint8_t pincnf = pinmode + 2; if (speed == GPIO_LOW) < instance.regs-> CRL | 0x1 else if (speed == GPIO_HIGH) < instance.regs->CRL |= (0x3 if (type == GPIO_PUSH_PULL) < instance.regs->CRL &= ~(0x1 else if (type == GPIO_OPEN_DRAIN) < instance.regs->CRL |= ( 0x1 > else < // Встановимо регістр CRH. pinmode = (pin - 8) * 4; uint8_t pincnf = pinmode + 2; if (speed == GPIO_LOW) |= (0x1 else if (speed == GPIO_HIGH) < instance.regs->CRH |= (0x3 if (type == GPIO_PUSH_PULL) < instance.regs->CRH &= ~(0x1 else if (type == GPIO_OPEN_DRAIN) < instance.regs->CRH |= (0x1 >
uint8_t pin2 = pin * 2; = (0x2 if (type == GPIO_PUSH_PULL) < instance.regs->OTYPER &= ~(0x1 else if (type == GPIO_OPEN_DRAIN) < instance.regs->OTYPER |= (0x1 if (speed == GPIO_LOW) < instance.regs->OSPEEDR &= ~(0x3 else) if (speed == GPIO_MID) < instance.regs->OSPEEDR &= ~(0x3 OSPEEDR |= (0x1 else if (speed == GPIO_HIGH) < instance.regs->OSPEEDR &= ~(0x3 OSPEEDR |= (0x3
uint8_t pin2 = pin * 2; instance.regs->MODER &= ~(0x3 PUPDR &= ~(0x3 PUPDR |= (0x1 else < instance.regs->PUPDR |= (0x2
uint32_t idr = instance.regs->IDR; out = (idr >> pin) & 1U; // Прочитати необхідний біт.
if (level == GPIO_LEVEL_LOW) < instance.regs->ODR &= ~(0x1 else if (level == GPIO_LEVEL_HIGH) < instance.regs->ODR |= (0x1
std::vector* GPIO_instances() < GPIO_instance instance; static std::vector* instancesStatic = new std::vector(12, instance); #if defined RCC_AHBENR_GPIOAEN || defined RCC_AHB1ENR_GPIOAEN || defined RCC_APB2ENR_IOPAEN ((*instancesStatic))[GPIO_PORT_A].regs = GPIOA; #endif #if defined RCC_AHBENR_GPIOBEN || defined RCC_AHB1ENR_GPIOBEN || defined RCC_APB2ENR_IOPBEN ((*instancesStatic))[GPIO_PORT_B].regs = GPIOB; #endif [..] return instancesStatic; > static std::vector* instancesStatic = GPIO_instances();
GPIO_instance &instance = (*instancesStatic)[port]; // Перевіряємо, чи є порт активним. Якщо це не так – активуємо його. if (!instance.active) < if (Rcc::enablePort((RccPort) port)) < instance.active = true; >else < return false; >>
Клас RCC
bool Rcc::enable(RccPeripheral peripheral) < uint8_t perNum = (uint8_t) peripheral; RccPeripheralHandle &ph = (*perHandlesStatic)[perNum]; if (ph.exists == false) < return false; >// Перевірка стану поточного периферійного пристрою. if (ph.count > 0) < if (ph.count > = handle_max) < return false; >// Збільшення кількості обробників на 1. ph.count++; > else < // Активація периферійного пристрою. ph.count = 1; * (ph.enr) | = (1 return true; >
Запуск прикладу
Розробка програм для мікроконтролерів
Структура та порядок написання програми
Компіляція програми
Необхідний набір програм
Прошивка та налагодження програми
Структура та порядок написання програми
Компіляція програми
Необхідний набір програм
Прошивка та налагодження програми