Как программировать arduino на ассемблере
Среда программирования Arduino IDE позволяет в программу на языке C++ вставлять фрагменты кода на языке AVR Assemler.
Эта статья для начинающих программировать в среде Ардуино. Здесь мы приведём программу Blink из стандартных примеров Arduino IDE и перепишем её на языке AVR Assembler в среде Arduino IDE.
На многих Ардуино совместимых платах, в том числе и на плате Arduino UNO или Arduino NANO, присутствует светодиод, который можно использовать для тестирования работоспособности платы.
void setup() < pinMode(LED_BUILTIN, OUTPUT); >void loop()
Скетч использует 932 байт (2%) памяти устройства. Всего доступно 32256 байт. Глобальные переменные используют 9 байт (0%) динамической памяти, оставляя 2039 байт для локальных переменных. Максимум: 2048 байт.
Пример 1. Программа мигающая светодиодом на плате Arduino UNO или Arduino NANO.
В программе из примера 1 есть ключевое слово, несколько функций (команд) и несколько констант.
| Слово | Единица | Описание |
|---|---|---|
| void | ключевое слово | Определяет тип следующего за ним объекта как не возвращающий значения |
| setup() | функция | Обязательно должна присутствовать в Ардуино программе. |
| loop() | функция | Обязательно должна присутствовать в Ардуино программе. Организует бесконечный цикл. |
| pinMode() | функция | Настраивает функционирование определённой ножки (pin) микроконтроллера. |
| digitalWrite() | функция | Включает определённый логический уровень на ножке (pin), если эта ножка настроена как выход. |
| delay() | функция | Останавливает выполнение программы на заданное время |
| LED_BUILTIN | константа | Значение для Arduino UNO 13 |
| OUTPUT | константа | true или 1 |
| LOW | константа | false или 0 |
| HIGH | константа | true или 1 |
Рассмотрим часть принципиальной электрической схемы платы Arduino.
Рис. 1. Светодиод на плате Ардуино подключён к выводу D13.
На рис. 1 мы видим, что порт PB5 микроконтроллера ATMega328 соединён с выводом D13 платы Ардуино. Когда на выводе D13 установлен логический 0, светодиод LD1 не горит. Когда — логическая 1 (~4,5 Вольт), ток идёт из микроконтроллера вывод D13 платы Ардуино через резистор R1, светодиод LD1 на общий провод с потенциалом 0 Вольт. Светодиод горит.
В программе из примера 1 в функции setup() функция pinMode(LED_BUILTIN, OUTPUT) настраивает вывод D13 платы Ардуино как выход. LED_BUILTIN=13.
В функции loop() функция digitalWrite(LED_BUILTIN, LOW) устанавливает на выводе D13 логический 0. Светодиод LD1 не горит. Функция digitalWrite(LED_BUILTIN, HIGH) устанавливает на выводе D13 логическую 1. Светодиод LD1 горит.
Микроконтроллер ATMega328 установленный на плате Ардуино работает очень быстро. Он выполняет 1 ассемблерную команду за 1 такт тактового генератора. Тактовая частота микроконтроллера ATMega328 на плате Ардуино 16 МГц. Значит период 1 такта 0,0000000625 сек (1/16000000). Именно за это время выполняется каждая ассемблерная команда. Если с такой скоростью переключать светодиод мы мерцаний не заметим. Поэтому в программе пример 1 добавлены функции delay(), которые выполняют задержку. После включения светодиода задержка выполнения программы 450 миллисекунд., а после включения 50 миллисекунд.
Подытожим в функции loop(), в цикле, то есть непрерывно повторяясь, светодиод LD1 выключен 450 миллисекунд и включается на 50 миллисекунд.
Перепишем программу из примера 1 учитывая значения констант и тот факт, что электрические сигналы на ножках микроконтроллера ATMega328 зависят от значений записанных в 8-ми битные управляющие регистры микроконтроллера. Уровень сигнала на порту PB5 зависит от состояния 5-го бита в регистре PORTB, а настройка порта PB5 как вход или выход зависит от состояния 5-го бита в регистре DDRB.
void setup() < DDRB = 0b00100000; >void loop()
Скетч использует 654 байт (2%) памяти устройства. Всего доступно 32256 байт. Глобальные переменные используют 9 байт (0%) динамической памяти, оставляя 2039 байт для локальных переменных. Максимум: 2048 байт.
Пример 2. Программа мигающая светодиодом на плате Arduino UNO или Arduino NANO.
Программы из примера 1 и примера 2 не тождественны, но работают одинаково.
Заменим в программе из примера 2 операторы присваивания (=) ассемблерными вставками.
void setup() < asm volatile ( "ldi R16, 0b00100000 \n" "out 0x04, R16 \n" ); >void loop()
Скетч использует 654 байт (2%) памяти устройства. Всего доступно 32256 байт. Глобальные переменные используют 9 байт (0%) динамической памяти, оставляя 2039 байт для локальных переменных. Максимум: 2048 байт.
Пример 3. Программа мигающая светодиодом на плате Arduino UNO или Arduino NANO с ассемблерными вставками.
В примере 3 вы видите 3 ассемблерные вставки. Все они практически одинаковые и состоят из 2 строк кода на AVR Assembler. команда LDI загружает в регистр общего назначения восьми-битное число. Команда OUT копирует значение из регистра общего назначения (РОН) в регистр (РВВ). К сожалению, указать в команде OUT имя регистра ввода вывода DDRB или PORTB в среде разработки Arduino нельзя. Мы взяли адреса этих регистров в памяти микроконтроллера из файла m328Pdef.inc. Файл m328Pdef.inc приведён в конце статьи.
Как видите, ассемблерные вставки в скетче Ардуино не являются удобным инструментом. Мне кажется более удобно хранить программу в 2-х файлах, отдельно Си-код и отдельно код на Ассемблере. Причем, если файл с программой на Ассемблере имеет расширение «S», и хранится в той же папке что и файл с программой на Си с расширением ino, то оба файла при компиляции объединяются в одну программу автоматически. Arduino IDE открывает оба файла на редактирование.
.global D13OUT .global D13OFF .global D13ON .equ DDRB, 0x04 .equ PORTB, 0x05 D13OUT: ldi R16, 0b00100000 out DDRB, R16 ret D13OFF: ldi R16, 0b00000000 out PORTB, R16 ret D13ON: ldi R16, 0b00100000 out PORTB, R16 ret
Файл с расширением «S».
extern "C" < extern __FlashStringHelper *D13OUT(void); extern __FlashStringHelper *D13OFF(void); extern __FlashStringHelper *D13ON(void); >void setup() < D13OUT(); >void loop()
Файл с расширением «ino».
Скетч использует 672 байт (2%) памяти устройства. Всего доступно 32256 байт.
Глобальные переменные используют 9 байт (0%) динамической памяти, оставляя 2039 байт для локальных переменных. Максимум: 2048 байт.
Пример 4. Программа мигающая светодиодом на плате Arduino UNO или Arduino NANO с функциями на ассемблере.
В программе из примера 4 на ассемблере написано 3 функции, а в коде на C++ эти функции подключаются как внешние (extern) и затем используются самым обычным образом.
Перепишем программу из примера 4 с передачей параметра.
.global D13OnOff .equ DDRB, 0x04 .equ PORTB, 0x05 D13OnOff: ldi R16, 0b00100000 out DDRB, R16 out PORTB, R24 ret
Файл с расширением «S».
extern "C" < extern __FlashStringHelper *D13OnOff(byte); >void setup() < >void loop()
Файл с расширением «ino».
Скетч использует 662 байт (2%) памяти устройства. Всего доступно 32256 байт.
Глобальные переменные используют 9 байт (0%) динамической памяти, оставляя 2039 байт для локальных переменных. Максимум: 2048 байт.
Пример 5. Программа мигающая светодиодом на плате Arduino UNO или Arduino NANO с функцией на ассемблере.
;***** THIS IS A MACHINE GENERATED FILE - DO NOT EDIT ******************** ;***** Created: 2011-02-09 12:03 ******* Source: ATmega328P.xml ********** ;************************************************************************* ;* A P P L I C A T I O N N O T E F O R T H E A V R F A M I L Y ;* ;* Number : AVR000 ;* File Name : "m328Pdef.inc" ;* Title : Register/Bit Definitions for the ATmega328P ;* Date : 2011-02-09 ;* Version : 2.35 ;* Support E-mail : avr @ atmel.com ;* Target MCU : ATmega328P ;* ;* DESCRIPTION ;* When including this file in the assembly program file, all I/O register ;* names and I/O register bit names appearing in the data book can be used. ;* In addition, the six registers forming the three data pointers X, Y and ;* Z have been assigned names XL - ZH. Highest RAM address for Internal ;* SRAM is also defined ;* ;* The Register names are represented by their hexadecimal address. ;* ;* The Register Bit names are represented by their bit number (0-7). ;* ;* Please observe the difference in using the bit names with instructions ;* such as "sbr"/"cbr" (set/clear bit in register) and "sbrs"/"sbrc" ;* (skip if bit in register set/cleared). The following example illustrates ;* this: ;* ;* in r16,PORTB ;read PORTB latch ;* sbr r16,(1<
Файл m328Pdef.inc
- Вы здесь:
- Главная
- Робототехника
- Assembler в Arduino
- Быки и коровы на С++
- while Игра кто быстрее 2023
- Функция yield()
- Пуговица и бусина
- Linux или FreeBSD 2023
- Сокеты
- Арифметические и унарные операции
- Коробка 3х3
- Пазлы
- Сапёр 2023
- Пятнадцать 2023
- Что ест уж 2023
- Игровое поле 2023
- Быки и коровы 2023
- Термины и определения
- Программа SOS
- Arduino Blink
- Китайский волчёк
- Головоломка Куб дяди Мити
- Головоломка Ханойская башня
- Головоломка Клёцки
- Головоломка Чайный сервиз
- Головоломка Тетраэдр
- Головоломка Что ест уж
- Головоломка косой Узел
- FreeCAD корпус для Orange Pi 3 LTS
- FreeCad корпус для Raspberry
- Решето Эратосфена
- ESP8266 tty терминал
- Blender отверстия
- Raspberry Pi Pico в Arduino IDE
- MicroPython на Raspberry Pi Pico
- Blink для ESP-C3-13-Kit на MicroPython
- MicroPython MicroREPL
- MicroPython WebREPL
- Драйвер для CH340 в Ubuntu 22.04
- MicroPython для ESP
- MicroPython и GPIO
- Робот на ESP8266 с датчиком HC-SR04
- Задача 001
- Esp8266 и HC-SR04
- ESP8266, драйвер MX1508 и сервопривод
- FORTH на Arduino
- Извлекаем слова из Flash памяти
- Матричная клавиатура
- Игра Flip-Flop
- Lines98 v2
- Логические операции
- Операции сравнения
- Esp8266 управление через web-интерфейс
- Игра 2048
- Битовые операции
- Игра PyNetWalk
- Энкодер
- Игра-головоломка Чайный сервиз v2
- Flip-flop 2x2x5 v2
- Игра Сапёр v3 на Python
- Игра Flip-Flop v3
- Lines98
- Микрофон
- Калькулятор v3
- Где ест уж v3
- Транзистор и фоторезистор.
- Датчик препятствий
- Игровое поле из Button
- Игра Memory
- Датчик инфракрасных импульсов
- Типы C++
- 3-D модель катушки ротора
- ESP32-C3 Wi-Fi точка доступа
- ESP32-C3 FTM
- ESP32-C3 Sigma-Delta модуляция
- Установка Arduino IDE для ESP32-C3
- ESP32-C3 analogReadMilliVolts
- ESP32-C3 Serial.print
- ledcWriteNote для ESP-C3-Kit
- Плата ESP-C3-32S Kit
- ШИМ в ESP-C3 Kit
- Программа Blink для ESP-C3 Kit
- Подключение ESP-C3-Kit к Arduino IDE
- Плата ESP-C3-13 Kit
- Калькулятор с tkinter
- Драйвер моторов MX1508
- Калькулятор на Arduino
- Raspberry Pi Pico Python SDK
- Raspberry Pi Pico C/C++ SDK
- Программирование на MMBASIC
- PicoMiteVGA
- Сервопривод и Ардуино
- Arduino машина с ИК управлением
- Двигатель постоянного тока
- ИК пульт ДУ
- Ультразвуковой дальномер HC-SR04
- АЦП и ШИМ в Arduino
- Крестики нолики v2.0
- Программа для музыкальной шкатулки
- Ханойские башни, игра
- Flip-Flop 4x4 и ООП
- AT90S2013 с внешним генератором
- Игра Кто быстрее
- Игра головоломка Peg
- Поход в пустыню
- Оригинальная игра Сапёр
- Программирование ATtiny861
- Программирование AT90S2013
- StringVar или ООП
- Клеточный автомат Конвея
- Flip-Flop 4x4 .
- ООП, after() функция задержки в tkinter
- Программирование AtTiny 13, 45, 85
- Игра-головоломка Где ест уж
- Игра-головоломка Чайный сервиз
- Пишем игру Flip-Flop v2
- Игра Быки и коровы на Python v2
- Крестики нолики
- Python сортировка
- Игра Красный или Синий?
- Индикатор 788BS
- Python Факториал
- Генератор псевдослучайных чисел
- Датчик температуры в ATtiny88
- Serial порт в ATtiny88
- Пишем библиотеку для MAX7219 и LED матрицы
- MAX7219 и Arduino
- Прерывания PCINT в Arduino
- Функция sleep() в Arduino для ATtiny88
- ATtiny88 datasheet на русском
- Фьюзы ATtiny88
- Arduino Fading and Blink
- Алгоритм Евклида. Нахождение НОД
- Python Числа Фибоначчи
- Python Tkinter игра Пикассо и Модильяни
- Ищем программатор для STM 32F030F4P6
- Python Tkinter игра Раскраска
- Пишем игру Быки и Коровы на Python
- Головоломка Ханойские башни на Python
- Головоломка Ханойские башни на Си
- Пишем игру Сапёр на Python
- Raspberry Pi Pico fading.py
- LCD МТ-16S2H и LiquidCrystal_74HC595
- EasyEDA для инженеров-электронщиков
- LCD МТ-16S2H и LiquidCrystalRus
- Raspberry Pi Pico и MicroPython
- Пишем игру пятнашки на Python
- Пишем игру на Python
- ESP8266 версии плат
- Регистр К155ИР13
- Linux или FreeBSD
- Триггеры
- Счетчик импульсов на 7493
- Счетчик импульсов на D-триггерах
- Цифровые индикаторы с общим катодом
- ATtiny88 программируем в Arduino IDE
- Конденсатор в кружке Робототехника
- Генератор на 555-м таймере
- Генератор НЧ на LM358
- Tkinter виджеты
- Pydoc в Python
- LM358 управление голосом
- Несимметричный мультивибратор
- QX5252F схема включения
- DC-DC uk преобразователь на QX5252
- DC-DC преобразователь на QX5252
- Python с Pygame обработка столкновений
- Логика в Python
- Сова на телевизор
- Транзисторы p-n-p и n-p-n
- IDLE
- Thonny установка и настройка
- Timer/Counter1 ATmega328
- Arduino IDE
- ATMEGA8
- Прерывания по таймерам в Arduino
- DC-DC преобразователь
- LED лампа светодиодная
- MOSFET
- Концепция музыкальной программы для Arduino
- Стробоскоп на 555-м таймере
- ШИМ на 555-м таймере
- ШИМ управление мощностью нагрузки
- Вентилятор для CPU и Arduino
- ATmega328P
- Храним константы в Flash-памяти программ
- Храним константы в EEPROM
- Параметры по умолчанию
- Цикл for в Arduino
- Драйвер MAX7219 и светодиодная матрица 8х8
- WS2811 и RGB светодиод
- Assembler в Arduino
- Python Gtk игра Раскраска
- LGT8F328P в Arduino IDE
- Адрес i2c
- Музыкальная шкатулка
- LCD 1602 i2c и Arduino
- Корпус VESA для Orange Pi PC 2
- Blink для адресуемых RGB светодиодов
- ESP8266-01 Web-сервер
- ESP8266 прошивка AT-espressif
- Edragon, ESP firmware
- Esptool
- ESP8266 в Arduino IDE
- ESP8266-01 подключение USB-UART
- ESP8266-01 AT интерпретатор
- CuteCom монитор порта
- ESP8266-01 подключение
- SSD1306 IIC print()
- ATMega328 в Arduino без кварца
- Фьюзы в Arduino UNO
- Программирование Arduino Pro Mini
- L7805 стабилизатор напряжения
- MLX90614 - ИК термометр
- Датчик ИК импульсов
- Arduino-Hava Nagila
- Arduino-Финская полька
- Arduino-Гимн РФ
- Arduino-Григ В пещере Горного Короля
- heaptrack профилировщик памяти
- Консольная программа на Visual J#
- Консольная программа на C#
- Консольная программа на Visual Basic.NET
- Blender на русском
- Arduino Digispark ATTiny85
- cairo.Context object Деформации
- cairo.Context object Фигуры Лиссажу
- cairo.Context object Движение по криволинейной траектории
- cairo.Context object Пинг-понг по стенкам
- cairo.Context object Загружаем картинку
- cairo.Context object Трансформация прямоугольных координат
- cairo.Context object Штриховые линии
- cairo.Context object Шар с радиальной заливкой
- cairo.Context object Градиентная заливка
- cairo.Context object Сдвигаем и вращаем начало координат
- cairo.Context object Начало координат
- cairo.Context object Сглаживание контура изображения или шрифта
- cairo.Context object Углы соединения линий
- cairo.Context object Рисуем линии
- Gtk Drawin Area и GObject
- Gtk Drawin Area и PangoCairo
- Python Gtk окно с текстом
- Python Gtk игра Flip-Flop
- Python Gtk Крестики - нолики
- Anjuta Gtk Python Кнопка
- Visual Studio Code редактор
- Vala язык программирования
- Anjuta Gtk Python
- Glade Gtk Python сигналы
- Glade Gtk Python
- Python графическая библиотека Turtle
- Python графическая библиотека GTK
- Python графическая библиотека Tkinter
- Инкубатор
- Пример программы на Python с библиотекой Pygame
- Создание игр на Python с Pygame
- Классическая игра Жизнь
- Игра Жизнь на дисплее SSD1306 и Arduino
- SSD1306 Display
- Импульсный регулятор мощности на Ардуино
- Оператор switch case. Электронная игра на Arduino.
- Игра инверсия
- Android пишем программу на C++
- Цикл while. Алгоритм Евклида.
- Geany пишем программу на C++
- Как скомпилировать cpp под Linux
- Схема преобразователя напряжения на транзисторе
- Схема фонарика с 2-мя батарейками
- Author Login
- Карта сайта
© 2023 Системный интегратор
Ассемблерные вставки в Arduino
Arduino IDE основана на ассемблере GNU. Таким образом, можно использовать avr-gcc для программирования микроконтроллера на ассемблере через интерфейс ISP. Однако многие мнемоники и макросы ассемблера также можно использовать в среде C. Это стало возможным благодаря библиотеке, автоматически интегрированной в Arduino IDE, которая, в свою очередь, вставляет и различные другие библиотеки ввода-вывода.
На этом этапе следует дать первое представление о прямом использовании мнемоники, а также первое представление об ассемблере и процессе компиляции. В качестве примера служит скетч «Blink», который можно открыть аналогичным образом в IDE в разделе «основы».
void setup() < pinMode(13, OUTPUT); >void loop()
В функции void setup() видим одну единственную инструкцию pinMode(13, OUTPUT); Эта инструкция устанавливает пин 13 платы Ардуино в режим цифрового выхода. Эта инструкция должна быть заменена соответствующим образом. Прежде всего, необходимо обратиться к принципиальной схеме платы Arduino Uno.

Посмотрим какой порт микроконтроллера задействован для вывода 13 платы. На принципиальной схеме видим что PIN 13 платы Uno подключен напрямую к PB5 ATmega 328P, поэтому он принадлежит порту B микроконтроллера Все настройки портов ввода-вавода организованы в 8-битных регистрах (8-битная архитектура, биты от 0 до 7). Регистр с именем DDRx (регистр направления данных) определяет режим работы вывода контроллера. Логическая «1» настраивает пин на выход, логический «0» — вход.
Теперь получаем для порта B нужное нам значение, которое позволит настроить пин PB5 на вывод. за направление работы пинов порта B отвечает регистр DDRB. Соответственно, 5-й бит этого 8-битного регистра должен быть установлен в «1». Получаем двоичное значение регистра DDRB = 00100000. Это соответствует 20 в шестнадцатеричном и 32 в десятичном исчислении. Остальные семь бит для нашего скетча не имеют значения. На языке Си это будет выглядеть так:
void setup() < DDRB = 0b00100000; >void loop()
Inline Assembler
Для интеграции небольших фрагментов ассемблера непосредственно в код C.
компилятор GCC поддерживает встроенный ассемблер [GCC EXTENDED-ASM]
asm ("hier steht der assembler code");
Для того, чтобы компилятор не оптимизировал код перед asm необходимо добавить ключевое слово volatile:
asm volatile ("hier steht der assembler code");
Содержимое регистров DDRx не может быть перезаписано напрямую. Это сделано в качестве меры защиты. Микроконтроллер имеет 32 так называемых рабочих регистра с r0 по r31 (рабочие регистры общего назначения). Они используются, например, в качестве буфера между регистрами ввода-вывода и ОЗУ.

Структура памяти Mega328
Например Регистр r16 может использоваться любой командой ассемблера. Команда ldi (LoaD Immediate) используется для записи значения в такой рабочий регистр. Затем значение из r16 загружается в DDRB с помощью команды out. DDRB имеет адрес 0x04, так что теперь получается следующее:
void setup() < asm volatile ( "ldi r16,0x20\n" "out 0x04,r16\n" ); >void loop()
Выражение \n используется для обозначения разрыва строки для разделения командных строк asm.
Здесь следует подчеркнуть, что это очень простой пример, который также легко реализовать. В дополнение к обычным командам ассемблера, которые описаны в операторе инструкции Atmel, при программировании на ассемблере необходимо учитывать гораздо больше, чем принято в языках высокого уровня, например, специфические значения, места, где параметры передаются в определенной форме, подключение переменных и регистров и более подробное изучение используемого оборудования. Последнее делает необходимым изучение даташита на микроконтроллер. Тем не менее, работа с аппаратным языком ассемблера имеет смысл. После небольшой тренировки его использование не намного сложнее, чем программирование на языке высокого уровня.
Ассемблер: преимущества и недостатки
Преимущества ассемблера:
Инструкции на ассемблере подходят для машинных кодов и поэтому «ближе к железу», чем языки высокого уровня, такие как C. В принципе, одна инструкция на ассемблере (мнемоника) соответствует соответствующей машинной инструкции. Таким образом, программы, написанные на чистом ассемблере (не на встроенном ассемблере), как правило, компактнее и быстрее, чем скомпилированный код, сгенерированный языками высокого уровня. Например, вы можете включать и выключать выводы контроллера одновременно за очень короткое время, так как вы можете обойти библиотеку wireing.c. Для этого требуется много строк для каждого порта, и все они должны обрабатываться одна за другой в цикле процессора. Для критичных по времени приложений предпочтительнее чистый ассемблер, нежели язык высокого уровня.
Встроенный ассемблер не предлагает такого оптимального использования, поскольку он интегрирован в среду C. Однако такие маленькие кусочки ассемблера можно встроить прямо в код C. Это может быть значительно более оптимально с точки зрения использования регистров, поскольку GCC точно знает, какие регистры нужны, а какие нет. Для приложений, чувствительных ко времени, inlineAssembler также может быть очень полезен.
Недостатки ассемблера:
• Изучение с ассемблера, устранение ошибок и сопровождение программы сложнее, чем в случае с языками высокого уровня.
• Ограничено или вообще невозможно перенесение программы на другие микроконтроллеры.
Машинный язык
Компилятор/ассемблер/отладчик Ardunio IDE создает файл *.hex, который автоматически загружается в память ATmega через USB-интерфейс платы Arduino встроенным загрузчиком. Понятные человеку аббревиатуры переводятся в двоичный код, понятный микроконтроллеру. Этот файл *.hex находится на жестком диске компьютера. Чтобы отобразить местоположение файла, вы можете установить галочку «Показать подробный вывод при компиляции» в разделе «Настройки» в среде IDE, после чего соответствующее примечание появится в поле комментариев среды IDE в разделе «Проверка».
Для простоты сократим наш пример кода до описания рабочего регистра:
void setup() < asm volatile ( "ldi r16,0x20\n" ); >void loop()
При этом компилятор сгенерирует HEX файл следующего содержания:
:100000000C9434000C9451000C9451000C94510049 :100010000C9451000C9451000C9451000C9451001C :100020000C9451000C9451000C9451000C9451000C :100030000C9451000C9451000C9451000C945100FC :100040000C9465000C9451000C9451000C945100D8 :100050000C9451000C9451000C9451000C945100DC :100060000C9451000C94510011241FBECFEFD8E026 :10007000DEBFCDBF11E0A0E0B1E0E4EDF1E002C0F1 :1000800005900D92A030B107D9F711E0A0E0B1E0E2 :1000900001C01D92A930B107E1F70E9456000C94EF :1000A000E8000C94000000E208950895CF93DF93D8 :1000B0000E94AD000E945300C0E0D0E00E945500B5 :1000C0002097E1F30E940000F9CF1F920F920FB624 :1000D0000F9211242F933F938F939F93AF93BF93CE :1000E0008091040190910501A0910601B091070152 :1000F000309108010196A11DB11D232F2D5F2D37D1 :1001000020F02D570196A11DB11D20930801809369 :10011000040190930501A0930601B093070180911B :10012000000190910101A0910201B091030101969B :10013000A11DB11D8093000190930101A0930201C4 :10014000B0930301BF91AF919F918F913F912F91F8 :100150000F900FBE0F901F901895789484B5826011 :1001600084BD84B5816084BD85B5826085BD85B55B :10017000816085BDEEE6F0E0808181608083E1E80A :10018000F0E0108280818260808380818160808342 :10019000E0E8F0E0808181608083E1EBF0E0808145 :1001A00084608083E0EBF0E0808181608083EAE717 :1001B000F0E08081846080838081826080838081A0 :1001C000816080838081806880831092C10008955F :0401D000F894FFCFD1 :00000001FF
Это так называемый формат INTEL Hex, который уже использовался в 1970-х годах для загрузки программ с перфоленты. Предложение всегда начинается с двоеточия, за которым следуют различные управляющие биты, такие как биты адреса, биты идентификации типа, фактические биты данных и контрольные суммы для каждой строки. Затем программа загрузки удаляет дополнительные данные. Чтобы снова найти инструкцию для r16, требуется опкод (код операции) для команды ldi:
![]()
Синтаксис: LDI Rd,K. Значение для K — шестнадцатеричное «20», эквивалентное двоичному «100000». Это приведет нас к коду операции
![]()
Используемый регистр является первым используемым регистром, поэтому он имеет значение «0» в коде операции:
![]()
Это приводит к шестнадцатеричному виду для 2 байтов: «e200»
Этот фрагмент кода фактически встречается один раз в приведенном выше коде .hex (одиннадцатая строка в листингк HEX файла).
Языки программирования
Итак, у вас есть процессор. Вы наверняка понимаете, что процессор можно как-то запрограммировать, чтобы он делал то, что вы хотите. Но чтобы сделать какую-то полезную работу, необходимо сперва написать полезную программу и отдать её процессору для исполнения.
В целом, неважно, с каким именно процессором вы имеете дело: это может быть последний Intel Pentium в вашем ноутбуке или микроконтроллер на плате Arduino. Общие принципы написания программы, т. е. программирования, в обоих случаях одни и те же. Различается лишь быстродействие и объём возможностей по работе с другими устройствами.
Что такое программа и куда её писать
Процессор, несмотря на всю сложность разработки и производства, по сути своей, довольно простая и прямолинейная вещь. Думать он не умеет: то есть, ставить новые задачи, искать и находить решения нетиповых уникальных задач процессор не может (и не сможет). Он умеет лишь тупо, байт за байтом, команда за командой, исполнять инструкции, которые ему подсунули. Этакий тупой, но ненасытный пожиратель команд и хладнокровный исполнитель инструкций. Можно привести грубый пример последовательности инструкций:
| Байт инструкции | Что он означает для процессора |
|---|---|
| 00001001 | означает: взять следующий байт и запомнить его в ячейке №1 |
| 00000110 | …это как раз следующий байт, который мы запоминаем в ячейке №1: число 5 |
| 00011001 | означает: отнять от значения в ячейке №1 единицу и оставить там обновлённый результат |
| 00101001 | означает: сравнить значение в ячейке №1 с нулём, и если оно ноль — перепрыгнуть через столько байт, сколько указано в следующем байте |
| 00000100 | …если результат был ноль, мы хотим прыгнуть через 4 байта, к предпоследней инструкции |
| 10000011 | означает, что мы хотим вывести на экран символ, код которого записан в следующем байте |
| 01000001 | …букве «A» как раз соответствует этот код |
| 00101000 | означает, что мы хотим прыгнуть назад на столько байт, сколько указано в следующем байте |
| 00000110 | …прыгать будем на 6 байт назад, к инструкции №3 |
| 10000011 | означает, что мы хотим вывести на экран символ, код которого записан в следующем байте |
| 00100001 | …знаку «!» как раз соответствует этот код |
В результате исполнения такой последовательности инструкций на экран будет выведена паническая фраза «АААА!».
Довольно много кода для такой простой цели! Понятно, что если бы все программы писались вот так, непосредственно, разработка сложных продуктов занимала бы века. А если ещё принять во внимание, что процессоры разрабатываются и производятся во множестве различных архитектур и систем команд (инструкций), то написание идентичной по алгоритму программы для процессоров различных архитектур и систем команд потребует многократного увеличения времени как на их изучение и освоение, так и непосредственно на написание собственного кода для каждого оригинального процессора.
Зачем нужны языки программирования
Для решения двуединой задачи: упрощения написания и понимания программ человеком, а также для исключения зависимости исходного кода программы от "железа" (от системы команд-инструкций конкретного процессора) были придуманы и созданы языки программирования. Их очень много и даже из тех что постоянно на слуху можно быстро вспомнить десяток-другой: Assembler, C, C++, C#, Java, Python, Ruby, PHP, Scala, JavaScript.
Программы на этих языках гораздо ближе к естественному языку человека. Следовательно, их проще, быстрее и приятнее писать. И что самое главное, их гораздо проще читать: вам сразу после написания, вам через год или вашему коллеге.
Проблема в том, что такие языки непонятны процессору, и перед тем, как дать ему эту программу на выполнение, её нужно транслировать (translate): перевести с естественного языка на язык процессора (на машинный язык). Трансляция-перевод может осуществляться непосредственно слово за словом (выражение за выражением) — это осуществляют программы-интерпретаторы языков (англ. interpreter — истолкователь). Их действие аналогично синхронному переводу, когда переводчики-синхронисты переводят фразы, выражающие законченный смысл, в темпе их произнесения или поступления. Трансляция-перевод может осуществляться и всего текста (листинга) программы целиком — это осуществляют программы-компиляторы (англ. compiler — составитель, собиратель). Их действие схоже с переводом обычных законченных текстов. У каждого языка, если только он не остался на уровне фантазий, есть свой транслятор-переводчик: либо интерпретатор либо компилятор, или и тот, и другой. Для популярных языков их обычно несколько на выбор, от разных производителей и для разных платформ. Большинство из них свободно доступно в интернете.
Итак, есть программы на вполне понятном человеку языке: их ещё называют «исходным кодом», просто «кодом» или «исходниками». Они пишутся в простые текстовые файлы с помощью любого текстового редактора, хоть с помощью Notepad. Затем они превращаются в понятные процессору наборы нулей и единиц с помощью транслятора (интерпретатора или компилятора), который интерпретирует исходный код и непосредственно выполняет заключённые в нём инструкции, используя ресурсы процессора, а компилятор получает на вход исходный код, а на выходе создаёт бинарный (двоичный) исполняемый файл, тот самый, который понятен процессору и называется исполняемой программой.
Бинарные файлы непригодны для чтения людьми и предназначены, в общем, лишь для исполнения процессором. Они могут иметь разный тип в зависимости от того, для чего получены: .exe — это файл исполняемой программы для операционной системы Windows, .hex — это файл исполняемой программы, приготовленный для загрузки на выполнение микроконтроллером типа Arduino, т. е. для выполнения практически на «чистом железе» (без ОС и прочих системных программ).
Почему же существует столько языков программирования и в чём разница?
Почему? Потому что на Земле много людей и компаний, и многие считали, что могут сделать лучше всех: удобнее, понятнее, быстрее, стройнее.
В чём разница: разные языки — это разный баланс скорости написания, понятности при чтении и скорости исполнения.
Посмотрим на одну и ту же программу, которая выводит на экран песенку про 99 бутылок пива на разных языках программирования.
Например, язык Perl. Пишется быстро; понять, что имел в виду программист невозможно; исполняется медленно:
sub b;$w=" on the wall"; die map{b."$w,\n".b.",\nTake one down, pass it around,\n".b(0)."$w.\n\n"}0..98
Язык Java. Пишется относительно долго; читается просто; исполняется довольно быстро, но занимает много памяти:
class bottles { public static void main(String args[]) { String s = "s"; for (int beers=99; beers>-1;) { System.out.print(beers + " bottle" + s + " of beer on the wall, "); System.out.println(beers + " bottle" + s + " of beer, "); if (beers==0) { System.out.print("Go to the store, buy some more, "); System.out.println("99 bottles of beer on the wall.\n"); System.exit(0); } else System.out.print("Take one down, pass it around, "); s = (--beers == 1) ? "" : "s"; System.out.println(beers + " bottle" + s + " of beer on the wall.\n"); } } }
Язык Assembler. Пишется долго; читается сложно; исполняется очень быстро:
code segment assume cs:code,ds:code org 100h start: ; Main loop mov cx, 99 ; bottles to start with loopstart: call printcx ; print the number mov dx,offset line1 ; print the rest of the first line mov ah,9 ; MS-DOS print string routine int 21h call printcx ; print the number mov dx,offset line2_3 ; rest of the 2nd and 3rd lines mov ah,9 int 21h dec cx ; take one down call printcx ; print the number mov dx,offset line4 ; print the rest of the fourth line mov ah,9 int 21h cmp cx, 0 ; Out of beer? jne loopstart ; if not, continue int 20h ; quit to MS-DOS ; subroutine to print CX register in decimal printcx: mov di, offset numbufferend ; fill the buffer in from the end mov ax, cx ; put the number in AX so we can divide it printcxloop: mov dx, 0 ; high-order word of numerator - always 0 mov bx, 10 div bx ; divide DX:AX by 10. AX=quotient, DX=remainder add dl,'0' ; convert remainder to an ASCII character mov [ds:di],dl ; put it in the print buffer cmp ax,0 ; Any more digits to compute? je printcxend ; if not, end dec di ; put the next digit before the current one jmp printcxloop ; loop printcxend: mov dx,di ; print, starting at the last digit computed mov ah,9 int 21h ret ; Data line1 db ' bottles of beer on the wall,',13,10,'$' line2_3 db ' bottles of beer,',13,10,'Take one down, pass it around,',13,10,'$' line4 db ' bottles of beer on the wall.',13,10,13,10,'$' numbuffer db 0,0,0,0,0 numbufferend db 0,'$' code ends end start
На чём программируется Arduino
Если говорить об Arduino или о микроконтроллерах компании Atmel, на каком языке можно писать программы для них? Теоретический ответ: на любом. Но на практике выбор ограничивается языками Assembler, C и C++. Это связанно с тем, что в сравнении с настольным компьютером у них очень ограниченные ресурсы. Килобайты памяти, а не гигабайты. Мегагерцы на процессоре, а не гигагерцы. Это плата за дешевизну и энергоэффективность.
Поэтому нужен язык, который может компилироваться и исполняться эффективно. То есть переводиться в те самые нули и единицы из инструкций как можно оптимальнее, без расходов драгоценных инструкций и памяти впустую. Подобной эффективностью как раз и обладают названные языки. Используя их, даже в узких рамках ресурсов микроконтроллера можно писать богатые возможностями программы, которые работают быстро.
Assembler, как вы видели, нельзя назвать самым простым и элегантным и, естественно, как результат, флагманским языком для Arduino является C/C++.
Во многих источниках говорится, что Arduino программируется на особом языке: Processing, Wiring. Это не совсем корректное утверждение. Контроллер программируется на C/C++, а то, что называется этими словами — это просто удобный «обвес», который позволяет решать многие типичные задачи, скрывая частности и детали, да не изобретая велосипед каждый раз.
Почему C и C++ упоминаются в одном предложении? C++ — это надстройка над C. Всякая программа на C является корректной программой для C++, но не наоборот. Вы можете пользоваться и тем, и другим. Чаще всего вы даже не будете задумываться о том, что используете, решая текущую задачу.
Ближе к делу: первая программа
Давайте напишем первую программу для Arduino и заставим плату её исполнять. Вам необходимо создать текстовый файл с исходным кодом, скомпилировать его и подсунуть полученный бинарный файл микроконтроллеру на плате.
Пойдём по порядку. Напишем исходный код. Можно написать его в блокноте или любом другом редакторе. Однако для того, чтобы работа была удобной, существуют так называемые среды разработки (IDE, Integrated Development Environment). Они в виде единого инструмента предоставляют и текстовый редактор с подсветкой и подсказками, и компилятор, запускаемый по кнопке, и много других радостей. Для ардуинок такая среда называется Arduino IDE. Она свободно доступна для скачивания на нашем сайте.
Установите среду и запустите её. В появившемся окне вы увидите, что большая часть места отдана текстовому редактору. В него и пишется код. Код в мире Arduino ещё называют скетчем (англ. sketch — набросок, эскиз). К слову, «скетч» как имя нарицательное пришло в мир искусства программирования из мира искусства и художников.
Итак, давайте напишем скетч (набросаем эскиз), который ничего не делает. То есть минимально возможную правильную программу на C++, которая просто прожигает время.
void setup() { } void loop() { }
Не будем пока заострять внимание на значении написанного кода. Скомпилируем его. Для этого в Arduino IDE на панели инструментов есть кнопка «Verify». Нажмите её, и через несколько секунд бинарный файл будет готов. Об этом возвестит надпись «Done compiling» под текстовым редактором.

В результате у нас получился бинарный файл с расширением .hex , который может исполнять микроконтроллер.
Теперь необходимо подсунуть его контроллеру. Этот процесс называется загрузкой, прошивкой или заливкой. Для загрузки полученной программы на устройство в среде Arduino IDE есть кнопка «Upload» на панели инструментов. Соедините вашу плату с компьютером через USB-кабель, нажмите «Upload», и через несколько мгновений программа будет загружена в ардуинку. При этом программа, которая была в контроллере ранее, будет стёрта.
Об успешной прошивке возвестит надпись «Done Uploading».

Если при попытке загрузки вы столкнулись с ошибкой убедитесь, что:
В меню Tools → Board выбран тот порт, к которому действительно подключена Arduino. Можете вынуть и вставить пару-тройку раз USB-кабель, чтобы понять какой порт появляется и исчезает: это и есть ваша плата.
Вы установили необходимые драйверы для вашей платы. Это необходимо для Windows, не требуется под Linux и необходимо только для старых плат на MacOS.
Поздравляем! Вы прошли весь путь от чистого листа до работающей на контроллере программы. Пусть она ничего и не делает, но это уже успех.
Если не указано иное, содержимое этой вики предоставляется на условиях следующей лицензии: CC Attribution-Noncommercial-Share Alike 4.0 International
Производные работы должны содержать ссылку на http://wiki.amperka.ru, как на первоисточник, непосредственно перед содержимым работы.
Вики работает на суперском движке DokuWiki.
программирование/языки-программирования.txt · Последние изменения: 2021/09/15 13:15 — mik
Инструменты страницы
- Показать исходный текст
- История страницы
- Ссылки сюда
- Наверх
Что лучше выбрать для программирования микроконтроллеров C или Assembler?
Я создал свою схему работы инвертора, на бумаге все готово, осталось только собрать, однако я столкнулся со следующей проблемой-скоростью работы программы, и тут я понял, что обычное ардуино не подойдет, т.к слишком медленное исполнение написанного кода. Суть программы такова - мне нужно быстро замерять силу тока в цепи и выдавать определенные значения выходного сигнала на транзисторы. Какой ЯП лучше подойдет под эту цель: C, Assembler или какой-то другой?
Отслеживать
13.7k 12 12 золотых знаков 43 43 серебряных знака 75 75 бронзовых знаков
задан 20 фев 2021 в 19:46
49 5 5 бронзовых знаков
Какой контроллер?
20 фев 2021 в 20:03
gbg, atmega 328p, armega8 также есть
20 фев 2021 в 20:17
Если есть возможность, то C++.
21 фев 2021 в 7:28
Рассмотрите использование Zephyr Project (это до некоторой степени POSIX-совместимая RTOS), тогда ваши програмки превратятся во что-то более ли менее простое на Си).
1 мар 2021 в 15:33
2 ответа 2
Сортировка: Сброс на вариант по умолчанию
Индустриальный вариант - использование сочетания C/asm в общей связке. Для тех участков кода, где время критично, можно выкатить дизассемблер горячего места и посмотреть, что там наворотил компилятор.
Альтернативно, можно вставить в программу кусок чисто в асм.
Однако, такая эквилибристика между языками потребует отличного знания как стандарта языка C и опций компилятора, так и ассемблера - вместо поиска одной проблемы, у вас будет сразу две - в C и в asm.
Так что если ваше C-кунгфу еще недостаточно сильно, выбросьте его из уравнения и сосредоточьтесь на ассемблере, благо у AVR аритектура прямолинейна и дубова.
Важно также понимать, что на процессорах без SIMD, кэшей, конвейеров, и прочих наворотов взрослых Пентиумов и Core, написание на ассемблере дает возможность получить код с предсказуемым и гарантированным временем исполнения - можно просто посчитать машинные инструкции и свериться с табличкой в даташите под названием "Количество тактов, потребное для выполнения инструкций"
Отслеживать
13.7k 12 12 золотых знаков 43 43 серебряных знака 75 75 бронзовых знаков
ответ дан 21 фев 2021 в 6:58
22.2k 2 2 золотых знака 32 32 серебряных знака 52 52 бронзовых знака
Некоторые вещи в принципе на Си сделать нельзя (думаю это относится к 90+% современных микроконтроллеров с высокой разрядностью шины).
1 мар 2021 в 15:30
Дело в том что конкретно в вашем случае всё просто вопрос в быстродействии, в идеале С+Ассемблер в критически важных местах. Asm или C - оба довольно близки к железу. К примеру я поступил следующим образом. Накупил на алиэкспрессе горсть Attiny13a, рассыпуху, раздичных переключателей, мелочевки, светодиодов, макетных плат, несколько AVRASP почему attiny13a? потому что проще некуда, простенькая в освоении, дешевая. И начал буквально с азов, с понимания что такое p-n переход, как устроенны микросхемы, процессоры, начал с железа. Изучил датащит тини13 от корки до корки, там и англ парралельно подтягивал, что вобщем то нужное в нашем деле. Ну и поскольку я так дотошно начал со всем разбираться, было решено так же поступить и с языком, было решено учить asm. В целом, нужно понимать, что современные языки всё выше и выше, всё дальше от железа, AVR что уж там греха таить, постепенно отходят уступая место STM32. Поэтому мне хотелось отдать дань уважения 8 битным уходящим в закат AVR и понять суть, философию работы блоков, регистров, портов, интерефейсов на уровне железа, то как это взаимодействует между собой. Это потрясающе, завараживает, в каком то смысле это словно магия, волшебство, потрясающе осознавать что на таком крохотном кристалике столько разных блоков, которые взаиможействуют между собой, а ты будто волшеблик пишешь заклинание(код). И ASM как нельзя к стати даёт прочувствовать как же всё устроено, как всё работает. На ASM вы буквально за ручку водите микроконтроллер от команды к команде, от порта к регистру, от трегистра к стеку, вы знаете что происходит в каждом такте, полный контроль, люблю контроль. К тому же это не так уж и сложно, даже напротив - ASM довольно таки интересен. Советую очень хорошую книгу для начинающих, написанную очень доступно и понятно (Мортон Д. - Микроконтроллеры AVR. Вводный курс). Что уж там греха таить, по началу спалил и залочил не правильными фьюзами пару тройку камней, но это attiny13a, при его стоимости не так жалко. Ну а потом начал учить С, к слову в процессе изучения. В идеале это С код, но в моментах где нужно очень ответственно проследить выполнение того или иного участка, там где нужна 100% контроль, безотказность, надежность, вот там очень камельфо делать ассемблерные вставки. Если говорить о скорости, то она вобщем то одинаковая, с поправкой на ветер в пользу ASM который естественно на чуточку где то там будет и быстрей. ОДНАКО! если говорить о читаемости, скорости разработки, хоть какой то там переносимости, то тут С конечно же впереди. Вы конечно же можете не занудничать как я, к примеру возьмите сразу мегу328, изучите asm хотя бы поверхностно, да бы понять суть, философию, хотя бы светодиодиком поморгайте по крассике, напишите свой delay, как фообще та задержка то формируется разберитесь))), это очень интересно, это вам не delay(1000); В любом случае, на сколько я понял вас не устроила ардуина, это радует, это уже отлично, на верном пути, а C или ASM это уже другой вопрос, и как по мне кодить удобней, читабельней и комфортней на С, но когда нужно пройти ооочень важный момент - делаем ассемблерную вставочку. Удачи!
Отслеживать
ответ дан 21 фев 2021 в 4:32
19 2 2 бронзовых знака
Ответ нормальный, но вы бы отформатировали текст, разбив его на параграфы, да исправили синтаксис с пунктуацией (внизу, под ответом есть "Править" ), а то ведь вконец заминусуют.
