Тема: "Языки программирования микроконтроллеров"
Исполнитель: Гусев Александр Олегович, гр. 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
Добрый день, Александр!
Мне кажется в рамках темы было бы очень круто помигать светодиодиком на rust или micropython, ну или на чем то еще, что не C)
Добрый день, Александр!
Мне кажется в рамках темы было бы очень круто помигать светодиодиком на rust или micropython, ну или на чем то еще, что не C)
Добрый день, Владимир.
Благодарю за идею, она звучит интересно. Но пока не могу ничего обещать, потому как ещё совсем не знаком с данной темой. По мере написания данного проекта планируется рассмотреть разные языки, применяемые в данной области, и обязательно будут упомянуты предложенные вами. Затем, на основе найденной информации будет (или будут) выбираться язык(-и), синтаксис которых будет мной изучен тщательней всего. Приоритетными, на данный момент, считаю наиболее популярный - C, (который предположительно пригодиться в ходе учёбы и практики) и предложенные вами. Если будет возможность, сделаю программы на двух или трёх языках
1. Прицнипы и особенности программирования микроконтроллеров
Прицнипы