Тема: "Языки программирования микроконтроллеров"
Исполнитель: Гусев Александр Олегович, гр. 341-об,
Научный руководитель - Русинов Владислав Леонидович, СКБ "Промышленная робототехника и автоматизация".
План:
1. Изучить принципы и особенности программирования микроконтроллеров
2. Изучить классификацию и примеры языков программирования микроконтроллеров
3. Создать пробную программу на одном из языков программирования микроконтроллеров
Введение
Современные микроконтроллеры являются неотъемлемой частью огромного количества устройств - от простых бытовых приборов до сложных промышленных систем. Сердцем каждого из этих устройств является программное обеспечение, определяющее его функциональность и поведение. Языки программирования микроконтроллеров играют ключевую роль в создании этого программного обеспечения, определяя возможности, производительность и эффективность использования ресурсов.
Микроконтроллеры представляют собой мощный вычислительный инструмент и прекрасную основу для создания современных высокопроизводительных и экономичных встраиваемых систем многоцелевого назначения. В одной микросхеме микроконтроллер включает в себя микропроцессор, память программ (обычно на основе ПЗУ), память данных (обычно на основе ОЗУ), устройство ввода/вывода, генератор тактовых сигналов, аппаратную поддержку интерфейсов I2C, SPI и многое другое.
1. Принципы и особенности программирования микроконтроллеров
Архитектура микроконтроллера (МК) определяет его внутреннюю организацию, функциональные блоки и способы их взаимодействия, влияя на производительность, энергопотребление, стоимость и применимость. Основные компоненты включают ядро процессора (CPU Core) с арифметико-логическим устройством (ALU), регистрами (аккумулятор, счетчик команд, указатель стека, регистр состояния) и блоком управления; память, состоящую из FLASH-памяти (программная память), RAM (оперативная память) и EEPROM; периферию (интерфейсы ввода/вывода), включающую GPIO, таймеры/счетчики, ADC, DAC и коммуникационные интерфейсы (UART, SPI, I2C, USB, CAN); и шины (адреса, данных и управления). Основные архитектурные особенности включают гарвардскую архитектуру (разделение памяти команд и данных), RISC (упрощенный набор инструкций), однокристальное исполнение, ориентированность на прерывания и энергоэффективность. Архитектура МК влияет на программирование, требуя понимания набора инструкций, модели памяти, использования регистров, настройки и использования прерываний, и управления периферией. Программирование микроконтроллеров существенно отличается от разработки для персональных компьютеров, обусловлено это жесткими ограничениями ресурсов и необходимостью тесной интеграции с аппаратным обеспечением.
Ключевые принципы включают в себя тщательную оптимизацию использования памяти, минимизацию потребления энергии и максимально эффективное использование вычислительной мощности процессора, часто работающего на относительно невысоких тактовых частотах. Разработчики должны уметь писать компактный код, избегать утечек памяти и использовать энергосберегающие режимы.
Управление аппаратной частью -- это ключевой аспект в процессе создания программного обеспечения для микроконтроллеров. Для эффективной работы с МК необходимо иметь прямой доступ к регистрам периферийных устройств, чтобы настраивать и контролировать их. Также важно уметь эффективно обрабатывать прерывания, чтобы программа могла быстро реагировать на внешние события, не прерывая основной поток выполнения.
Для работы с МК важно уметь точно настраивать таймеры и генераторы сигналов, а также работать с аналоговыми сигналами, используя аналого-цифровые преобразователи (АЦП) и цифро-аналоговые преобразователи (ЦАП).
При программировании МК часто приходится работать в режиме реального времени. Это требует предсказуемого времени выполнения кода, чтобы программа могла своевременно реагировать на события, планировать задачи и надёжно обрабатывать возникающие события.
Интегрированные среды разработки (IDE) играют критически важную роль в процессе программирования микроконтроллеров (МК), предоставляя разработчикам комплексный набор инструментов для написания, компиляции, отладки и программирования кода. Они значительно упрощают разработку встроенных систем, ускоряя процесс и повышая качество конечного продукта. Среди используемых IDE можно выделить наболее популярные и широко применяемые:
Keil MDK-ARM (Keil Microcontroller Development Kit):
Поддержка: ARM Cortex-M (и другие ARM-архитектуры)
Особенности: мощный компилятор ARM Compiler, обеспечивающий высокую производительность и оптимизацию кода; комплексный отладчик ULINK, поддерживающий JTAG и SWD интерфейсы; RTOS-ориентированная отладка (например, поддержка CMSIS-RTOS); библиотеки CMSIS (Cortex Microcontroller Software Interface Standard); поддержка множества производителей МК на базе ARM.
Преимущества: Широко используется в промышленности, отличная поддержка ARM, мощные инструменты оптимизации и отладки.
Недостатки: Коммерческая лицензия, может быть дорогостоящей для небольших проектов.
IAR Embedded Workbench:
Поддержка: ARM, AVR, MSP430, RISC-V и другие архитектуры.
Особенности: высоко оптимизированный компилятор C/C++; C-SPY Debugger - мощный отладчик с широкими возможностями; поддержка RTOS; интегрированный статический анализатор кода C-STAT.
Преимущества: Очень эффективный компилятор, стабильность, широкий спектр поддерживаемых архитектур.
Недостатки: Коммерческая лицензия, высокая стоимость.
Eclipse IDE (с плагинами):
Поддержка: Широкий спектр архитектур (ARM, AVR, PIC, RISC-V и др.) через различные плагины.
Особенности: бесплатная и открытая платформа; гибкая настройка и расширяемость с помощью плагинов; поддержка различных компиляторов (GCC, Clang); интеграция с отладчиками GDB (GNU Debugger); поддержка систем контроля версий (Git, SVN).
Преимущества: Бесплатная, открытая, гибкая, широкое сообщество пользователей и разработчиков.
Недостатки: Требует настройки и установки плагинов, может быть сложной для начинающих.
Atmel Studio/Microchip Studio (ранее Atmel Studio, теперь Microchip Studio после приобретения Atmel компанией Microchip):
Поддержка: AVR и SAM (ARM Cortex-M) микроконтроллеры Microchip.
Особенности: бесплатная IDE, основанная на Visual Studio; интегрированный отладчик с поддержкой JTAG и debugWIRE; AVR/SAM Toolchain (компилятор, линкер, ассемблер); CodeVisionAVR (опционально, коммерческий C компилятор); графический интерфейс Atmel START для конфигурации периферии.
Преимущества: Бесплатная, тесная интеграция с микроконтроллерами Microchip, удобный графический интерфейс.
Недостатки: Ограничена микроконтроллерами Microchip.
Arduino IDE:
Поддержка: Arduino-совместимые платы на базе AVR, ARM и других архитектур.
Особенности: простой и удобный интерфейс, ориентированный на начинающих; библиотеки для упрощения работы с периферией; встроенный загрузчик (bootloader) для упрощенной прошивки.
Преимущества: Очень проста в использовании, большое сообщество, много готовых библиотек.
Недостатки: Ограниченные возможности отладки, менее эффективный код по сравнению с профессиональными IDE.
2. Классификация и примеры языков программирования микроконтроллеров
По уровню абстракции от аппаратной платформы:
Низкоуровневые языки
Характеризуются минимальным уровнем абстракции от архитектуры микроконтроллера. Трансляция кода в исполняемый машинный код осуществляется с минимальным преобразованием, зачастую представляя собой непосредственное отображение на набор инструкций центрального процессора. Это обеспечивает прямой контроль над ресурсами, такими как регистры, память и периферийные устройства.
Примеры таких языков: Assembler, C, VHDL, Verilog, Forth.
Высокоуровневые языки
Предлагают более высокий уровень абстракции, скрывая детали реализации и предоставляя разработчику возможность оперировать с более сложными структурами данных и алгоритмами. Трансляция кода осуществляется посредством компиляторов или интерпретаторов, генерирующих машинный код для целевой платформы. Разработчик работает с абстракциями, предоставляемыми языком, а компилятор отвечает за эффективное отображение этих абстракций на конкретную аппаратную платформу.
Примеры таких языков: C/C++, MicroPython, Rust, Ada, BASCOM-AVR (диалект BASIC)
По доминирующей парадигме программирования:
Императивные языки
Определяют вычисления как последовательность изменяющих состояние программы инструкций. Разработчик явно указывает шаги, которые необходимо выполнить для достижения желаемого результата.
Примеры таких языков: Assembler, C, BASCOM-AVR
Объектно-ориентированные языки
Строят программу на основе концепции объектов, которые объединяют данные и методы, работающие с этими данными. Объектно-ориентированное программирование (ООП) способствует модульности, повторному использованию кода и упрощает разработку сложных систем.
Примеры таких языков: C++, Rust
Скриптовые языки
Предназначены для автоматизации задач и быстрого прототипирования. Обычно интерпретируются, а не компилируются, что позволяет быстро вносить изменения и тестировать код.
Примеры таких языков: MicroPython (самый популярный в своем классе для программирования микроконтроллеров), возможно применение JavaScript (Espruino) и Lua (NodeMCU)
По степени специализации и целевой области применения:
Языки общего назначения
Разработаны для широкого спектра задач и не имеют жесткой специализации. Они предоставляют универсальные инструменты для разработки в различных областях.
Примеры таких языков: C/C++, MicroPython, Rust
Языки предметно-ориентированные
Предназначены для решения задач в узкой предметной области и предоставляют специализированные средства для упрощения разработки. Предметно-ориентированные языки часто имеют более простой синтаксис и понятные разработчикам в конкретной области термины.
Примеры таких языков: Ladder Logic, Function Block Diagram, Structured Text, BASCOM-AVR.
3. Примеры программ
Язык C занимает доминирующее положение в программировании микроконтроллеров (МК) благодаря своей гибкости, эффективности и близости к аппаратному обеспечению. Он позволяет разработчикам напрямую управлять ресурсами МК, обеспечивая оптимальную производительность и контроль над периферийными устройствами. Благодаря богатой экосистеме компиляторов, библиотек и инструментов разработки, C является надежным выбором для создания встроенных систем, начиная от простых устройств и заканчивая сложными промышленными контроллерами. Низкоуровневые возможности C, такие как работа с указателями и битовыми операциями, позволяют эффективно использовать ограниченные ресурсы МК и реализовывать сложные алгоритмы управления.
Приведем наглядный пример программы для микроконтроллера типа AVR на языке C
#include <avr/io.h>
#include <util/delay.h>
#define LED_PIN PB5
int main(void) {
DDRB |= (1 << LED_PIN);
while (1) {
PORTB |= (1 << LED_PIN);
_delay_ms(500);
PORTB &= ~(1 << LED_PIN);
_delay_ms(500);
}
return 0;
}
Пояснения:
#include <avr/io.h> - Этот заголовочный файл содержит определения для работы с регистрами ввода/вывода (I/O) AVR микроконтроллера. Он необходим для настройки и управления портами, к которым подключены внешние устройства.
#include <util/delay.h> - Этот заголовочный файл предоставляет функцию _delay_ms() для создания задержек в миллисекундах. Он используется для управления скоростью мигания светодиода.
#define LED_PIN PB5 - Это определение создает константу LED_PIN, которая равна PB5. PB5 представляет собой 5-й пин порта B (Port B, Pin 5) на микроконтроллере. Эта строка говорит компилятору, что светодиод подключен к этому конкретному пину.
int main(void) - Это главная функция программы, с которой начинается выполнение кода.
DDRB |= (1 << LED_PIN); - Эта строка настраивает пин LED_PIN на выход. DDRB (Data Direction Register B) определяет направление данных для порта B. Установка бита в DDRB для конкретного пина делает этот пин выходом.
* (1 << LED_PIN) сдвигает единицу на LED_PIN позиций влево, создавая битовую маску.
* |= выполняет побитовое ИЛИ с присваиванием. Это устанавливает бит, соответствующий LED_PIN, в DDRB, не изменяя другие биты.
while (1) - Создает бесконечный цикл. Программа будет повторять код внутри этого цикла до тех пор, пока микроконтроллер включен.
PORTB |= (1 << LED_PIN); - Включает светодиод. PORTB является регистром данных для порта B. Установка бита в PORTB для конкретного пина устанавливает напряжение на этом пине в высокое состояние (обычно 5V или 3.3V), что включает светодиод.
_delay_ms(500); - Создает задержку в 500 миллисекунд. Это необходимо, чтобы светодиод оставался включенным в течение некоторого времени.
PORTB &= ~(1 << LED_PIN);: Выключает светодиод. &= ~ выполняет побитовое И с инверсией и присваиванием. Это сбрасывает бит, соответствующий LED_PIN, в PORTB, устанавливая напряжение на этом пине в низкое состояние (0V), что выключает светодиод.
_delay_ms(500); - Создает еще одну задержку в 500 миллисекунд. Это необходимо, чтобы светодиод оставался выключенным в течение некоторого времени.
Рассмотрим более сложную программу, написанную на языке MicroPython:
import machine
import time
import random
LED_PIN = machine.Pin(5, machine.Pin.OUT)
BUTTON_PIN = machine.Pin(4, machine.Pin.IN, machine.Pin.PULL_UP)
BUZZER_PIN = machine.Pin(0, machine.Pin.OUT)
def play_tone(frequency, duration_ms):
period_us = int(1000000 / frequency)
half_period_us = int(period_us / 2)
start_time = 0
while start_time < duration_ms * 1000:
BUZZER_PIN.on()
time.sleep_us(half_period_us)
BUZZER_PIN.off()
time.sleep_us(half_period_us)
start_time += period_us
def button_pressed_callback(pin):
global random_delay
LED_PIN.on()
random_delay = random.randint(1000, 5000)
time.sleep_ms(random_delay)
LED_PIN.off()
play_tone(1000, 200) # 1000 Hz, 200ms
button_interrupt = BUTTON_PIN.irq(trigger=machine.Pin.FALLING, handler=button_pressed_callback) # FALLING - нажатие кнопки
print("Игра на реакцию запущена!")
while True:
time.sleep(1)
Эта программа имитирует простую игру на реакцию. Когда пользователь нажимает кнопку, включается светодиод. Через случайный промежуток времени (от 1 до 5 секунд) светодиод выключается, и звучит короткий сигнал. Цель - нажать кнопку как можно быстрее после того, как светодиод выключится.
Пояснения:
import machine - Импортирует модуль machine, который предоставляет доступ к аппаратным функциям микроконтроллера (пины, таймеры, прерывания и т.д.).
import time - Импортирует модуль time, который предоставляет функции для работы со временем (задержки, таймеры).
import random - Импортирует модуль random, который предоставляет функции для генерации случайных чисел.
LED_PIN = machine.Pin(5, machine.Pin.OUT) - Создает объект Pin для управления светодиодом. 5 - это номер пина (D1 на NodeMCU), machine.Pin.OUT указывает, что это выходной пин.
BUTTON_PIN = machine.Pin(4, machine.Pin.IN, machine.Pin.PULL_UP) - Создает объект Pin для кнопки. 4 - это номер пина (D2 на NodeMCU), machine.Pin.IN указывает, что это входной пин, а machine.Pin.PULL_UP включает внутренний подтягивающий резистор.
play_tone(frequency, duration_ms) - Функция для воспроизведения звука заданной частоты и длительности. Она переключает выход BUZZER_PIN с заданной частотой, имитируя звук.
button_pressed_callback(pin) - Это функция обратного вызова (callback), которая вызывается при нажатии кнопки (прерывание). Она включает светодиод, генерирует случайную задержку, ждет эту задержку, выключает светодиод и воспроизводит короткий звуковой сигнал.
random_delay = random.randint(1000, 5000) - Генерирует случайную задержку в диапазоне от 1000 до 5000 миллисекунд (от 1 до 5 секунд).
button_interrupt = BUTTON_PIN.irq(trigger=machine.Pin.FALLING, handler=button_pressed_callback) - Создает объект прерывания (IRQ).
trigger=machine.Pin.FALLING указывает, что прерывание должно срабатывать при переходе пина от высокого уровня к низкому (при нажатии кнопки).
handler=button_pressed_callback указывает, какая функция должна быть вызвана при срабатывании прерывания.
while True: time.sleep(1) - Основной цикл программы. В данном случае, он просто ждет 1 секунду и ничего не делает, так как вся логика игры обрабатывается в прерывании.
Подключение (для NodeMCU ESP8266):
Подключите светодиод к пину D1 (GPIO5) через резистор (220 Ом - 1 кОм).
Подключите кнопку к пину D2 (GPIO4).
Подключите пьезопищалку к пину D3 (GPIO0).
Как использовать эту программу:
Установите MicroPython на ESP8266: Следуйте инструкциям для установки MicroPython на вашу плату ESP8266. Вам потребуется программа для прошивки (например, esptool.py).
Загрузите код на ESP8266: Используйте инструмент для загрузки файлов (например, ampy или Thonny) для загрузки этого кода в файл main.py на ESP8266.
Перезагрузите ESP8266: Перезагрузите ESP8266, чтобы программа начала выполняться.
Литература
1. Васильев А.С., Лашманов О.Ю., Пантюшин А.В. Основы
программирования микроконтроллеров. - СПб: Университет ИТМО, 2016.
- 95с.
2. Программирование микроконтроллеров: учебное пособие для вузов /
В. Г. Козырев; Севастопольский государственный университет. - Севастополь: СевГУ, 2023. - 244 с. - Текст : электронный.
3. Электронный ресурс sky.pro