Rust embedded. Stopwatch.
Rust embedded. Spi и embedded-graphics.
Rust embedded. Gpio.
Rust embedded. Сиквел.
Rust embedded.
Rust – это язык системного программирования, предназначенный для обеспечения безопасности, скорости и параллелизма. Rust имеет множество функций времени и безопасности во время компиляции, чтобы избежать сбоев данных и общих ошибок, все с минимальными издержками до нуля. Язык сфокусирован на безопасной работе с памятью, обеспечивает автоматическое управление памятью и предоставляет средства для достижения высокого параллелизма выполнения заданий, при этом обходясь без использования сборщика мусора и runtime. Автоматическое управление памятью в Rust избавляет разработчика от манипулирования указателями и защищает от проблем, возникающих из-за низкоуровневой работы с памятью, таких как обращение к области памяти после её освобождения, разыменование нулевых указателей, выход за границы буфера и т.п. Для распространения библиотек, обеспечения сборки и управления зависимостями проектом развивается пакетный менеджер Cargo, позволяющий получить нужные для программы библиотеки в один клик.
Rustup – установщик Rust и инструмент для управления версиями. Основным способом установки Rust, который используют разработчики, является Rustup – инструмент для установки и управления версиями Rust. Для загрузки Rustup и установки Rust на Linux, запустите следующее в вашем терминале и следуйте инструкциям (у меня Linux).
curl –proto ‘=https’ –tlsv1.2 -sSf https://sh.rustup.rs | sh
Cargo: Менеджер пакетов и инструмент сборки для Rust. При установке через Rustup, вы получаете последнюю стабильную версию пакетного менеджера и средства сборки Rust, известного, как Cargo. Cargo делает многие вещи:
- собирает ваш проект с cargo build
- запускает ваш проект с cargo run
- тестирует ваш проект с cargo test
- собирает документацию для вашего проекта с cargo doc
- публикует библиотеку на crates.io с cargo publish
Чтобы удостовериться, что Rust и Cargo установлены, вы можете запустить в терминале следующую команду: - cargo –version
Cargo – это инструмент, который позволяет указывать необходимые зависимости для проектов на языке Rust и убедиться, что вы получите воспроизводимые сборки.
Для достижения этих целей, Cargo выполняет следующие действия:
- Создает два файла с некоторой необходимой информацией о проекте.
- Получает и собирает зависимости вашего проекта.
- Запускает
rustc
или другие инструменты сборки со всеми необходимыми параметрами для правильной сборки вашего проекта. - Предоставляет все необходимые условия, чтобы работа с проектами на Rust стала проще.
Для тех у кого zsh, добавьте следующие строки в файл zshrc.
gedit ~/.zshrc export PATH="$HOME/.cargo/bin:$PATH"
Для программирования на Rust хороший редактор VS Code.
Определите платформу кросс-компиляции для необходимого аппратного обеспечения.
- Использовать thumbv6m-none-eabi для ARM Cortex-M0 and Cortex-M0+
- Использовать thumbv7m-none-eabi для ARM Cortex-M3
- Использовать thumbv7em-none-eabi для ARM Cortex-M4 и Cortex-M7 (без FPU )
- Использовать thumbv7em-none-eabihf для ARM Cortex-M4F и Cortex-M7F (с FPU )
Установите компонент rust-std для вашей аппаратной платформы
$ rustup target add thumbv7em-none-eabihf
Минимально достаточный проект для Cortex M7 (у меня devkit stm32f746g-disco)
Rust экосистема для embedded микроконтроллеров.
Cargo.toml
Синтаксис TOML основан на парах ключ = “значение”, [разделах] и # комментариях. Этот файл называется манифестом и содержит в себе все метаданные, которые необходимы Cargo, чтобы скомпилировать ваш проект.
[package] name = "empty" version = "0.1.0" authors = ["Roman Shulenkov postmaster@mcu.by"] edition = "2018" [dependencies] cortex-m = "0.6.2" cortex-m-log="0.6.1" [dependencies.stm32f7] version = "0.10.0" features = ["stm32f7x6", "rt"] [dependencies.cortex-m-rt] version = "0.6.12" features = ["device"]
.cargo/config
В этом файле мы обозначаем цель компиляции (аппаратная платформа) , а так же флаги компиляции. Все конфигурационные файлы хранятся в TOML формате (как манифесты), в простом формате ключ-значение, которые хранятся внутри секций (таблиц), а потом будут объединены. Ключи для значений, которые указывают на определенную программу, могут быть в формате абсолютного пути, относительного, а также можно просто указать название программы. Абсолютные пути и название программ используются как есть. Относительные пути используются исходя из родительской директории, в которой расположена директория .cargo
, в которой находится конфигурационный файл.
# Массив путей к локальным репозиториям, которые будут переопределены в качестве # зависимостей. Для более подробной информации смотрите документ Specifying Dependencies. paths = ["/path/to/override"] [cargo-new] # Настройки для имени/email, которые будут помещены в блок `authors` в новых Cargo.toml # Если эти параметры не указаны, будут взяты параметры из конфигурации `git`. А, если и их нет # запишутся `$USER` и `$EMAIL`. name = "..." email = "..." # По умолчанию команда `cargo new` создан новый Git репозиторий. Это значение может быть # изменено на `hg`, тогда будет создан Mercurial репозиторий, или `none`, чтобы отключить # данный функционал. vcs = "none" # Для следующего раздела, $triple относится к любой возможной целевой платформой, # не к строкову литералу "$triple", и будет применяться каждый раз, когда будет сборка # для целевой платформы. [target] # Для сборок Cargo, для которых не указан параметр --target, будет использован компоновщик # переданный в rustc (с помощью `-C linker=`). По умолчанию этот флаг не передан # как параметр компилятора. linker = ".." [target.$triple] # Этот раздел похож на раздел, который был описан выше, но тут указывается конкретная # целевая платформа, которая будет скомпилирована. linker = ".." # пользовательские настройки будут переданы в компилятор, каждый раз когда будет $triple # вызвана компиляция для целевой платформы. # этот параметр переопределит build.rustflags, если он указан rustflags = ["..", ".."] # Настройки для реестра [registry] index = "..." # Ссылка для индекса реестра (по умолчанию - центральный репозиторий) token = "..." # Ключ доступа (можно найти на сайте центрального репозитория) [http] proxy = "..." # HTTP прокси. Используется для HTTP запросов (по умолчанию не указан) timeout = 60000 # Таймаут для каждого HTTP запроса, в миллисекундах cainfo = "cert.pem" # Путь до ключа Центра Сертификации (опционально) [build] jobs = 1 # количество параллельно выполняемых заданий, по умолчанию - # количество ЦП rustc = "rustc" # компилятор rust rustdoc = "rustdoc" # инструмент генерации документации target = "triple" # build for the target triple target-dir = "target" # путь к директории, в которой будет скомпилированный проект rustflags = ["..", ".."] # настройки, которые будут переданы компилятору [term] verbose = false # предоставлять ли cargo развернутый вывод color = 'auto' # предоставлять ли cargo цветной вывод # Конфигурация сети [net] retry = 2 # сколько раз будет вызвана попытка повторной отправки сигнала # Псевдонимы для команд Cargo. Первые 3 псевдонима встроены. # Если вы хотите передать параметры в псевдоним, в которых есть пробелы, то используйте список. [alias] b = "build" t = "test" r = "run" rr = "run --release" space_example = ["run", "--release", "--", "\"command list\""]
[target.thumbv7em-none-eabihf] rustflags = ["-C", "link-arg=-Tlink.x"] [build] target = "thumbv7em-none-eabihf"
memory.x
Пакет cortex-m-rt требует от нас создать файл “memory.x” в корневом каталоге проекта. В нём указывается распределение адресного пространства микроконтроллера:
MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 320K } /* This is where the call stack will be allocated. */ /* The stack is of the full descending type. */ /* NOTE Do NOT modify `_stack_start` unless you know what you are doing */ _stack_start = ORIGIN(RAM) + LENGTH(RAM);
.vscode/tasks.json
VS Code содержит понятие “сборки проекта”. Редактор можно настроить таким образом, чтобы сборка Rust-проекта происходила необходимым нам способом.
Добавьте task (задание) в файл tasks.json
в директории .vscode
в корне вашего проекта:
{ "version": "2.0.0", "tasks": [ { "label": "Cargo build", "type": "shell", "command": "cargo", "args": ["build"], "problemMatcher": [ "$rustc" ], "group": "build" }, { "label": "Build binary", "type": "shell", "command": "arm-none-eabi-objcopy", "args": [ "--output-target", "binary", "./target/thumbv7em-none-eabihf/debug/empty", "./target/thumbv7em-none-eabihf/debug/empty.bin"], "problemMatcher": [ "$rustc" ], "group": { "kind": "build", "isDefault": true }, "dependsOn": "Cargo build" } ] }
.vscode/launch.json
В каталоге проекта создаётся папка .vscode, в которой создаётся файл с конфигурациями отладки launch.json.
{ "version": "0.2.0", "cortex-debug.armToolchainPath": "/home/mcuby/Downloads/gcc-arm-none-eabi-8-2019-q3-update/bin", "configurations": [ { "name": "Debug empty", "request": "launch", "type": "cortex-debug", "cwd": "${workspaceRoot}", "executable": "${workspaceFolder}/target/thumbv7em-none-eabihf/debug/empty", "svdFile": "${workspaceFolder}/STM32F7x6.svd", "servertype": "openocd", "configFiles": ["STM32F7x6.cfg"], "preLaunchTask": "Build binary", "preLaunchCommands": [ "monitor init", "monitor reset init", "monitor halt", "monitor flash write_image erase ./target/thumbv7em-none-eabihf/build/empty.bin 0x08000000" ], "postLaunchCommands": ["monitor reset halt"] } ] }
src/main.rs
Самая простая программа для микроконтроллера пустой цикл с nop.
#![no_main] #![no_std] extern crate cortex_m; extern crate cortex_m_rt as runtime; extern crate stm32f7; use core::panic::PanicInfo; use cortex_m::asm; #[no_mangle] fn main() -> ! { loop { for _i in 0..100000 { asm::nop() } } } #[panic_handler] fn panic(_panic: &PanicInfo<'_>) -> ! { loop {} }
С помощью cortex_m_log нам доступен механизм отладки. В пакете предусмотрены следующие пункты назначения для записи: Dummy – пункт назначения noop, который не выполняет запись. Полезный режим Itm – использует Cortex-M Itm для отправки вывода. Обратите внимание, что он доступен только на ARMv7-M и более новых версиях ядра Cortex, Semihosting – использует Cortex-M Semihosting для отправки вывода.
#![no_main] #![no_std] extern crate cortex_m; extern crate cortex_m_rt as runtime; extern crate stm32f7; use core::panic::PanicInfo; use cortex_m::asm; use cortex_m_log::{print, println, d_print, d_println}; use cortex_m_log::printer::Dummy; #[no_mangle] fn main() -> ! { let mut log = Dummy::new(); println!(log, "Some print with newline!"); //Debug version of print that resolves into nothing in release mode //Note that you must import print macro for it to work d_print!(log, "Print stuff: {}", "stuff"); //Note that you must import println macro for it to work d_println!(log, "Print stuff: {} and also newline", "stuff"); loop { for _i in 0..1000000 { asm::nop() } } } #[panic_handler] fn panic(_panic: &PanicInfo<'_>) -> ! { loop {} }
Необходимые пакеты для VS Code (все кроме C/C++).
Исходники демо проекта на https://github.com/mcuby/rust-embedded-example