Rust славится кросс-компиляцией для всех видов платформ. Варианты набора микросхем ARM входят в список. Некоторое время назад я задался целью создать ядро для Teensy 4.0, написанное на ржавчине. Я быстро понял, что извлечь мой код на повторно используемую платформу будет довольно просто. Таким образом, было создано teensycore, и вы тоже можете наслаждаться этим чрезвычайно легким ядром!
Teensycore имеет полностью открытый исходный код, и участие приветствуется!
Ресурсы
- Плата для разработки Teensy: https://www.pjrc.com/store/teensy40.html
- Исходный код ядра: github.com/SharpCoder/teensycore
- Crates.io: https://crates.io/crates/teensycore
- Документация: https://docs.rs/teensycore/0.0.11/teensycore/
В этой статье я объясню некоторые функции, встроенные в teensycore, и приведу несколько примеров их использования. Сначала краткий обзор функционала.
Функциональность
На момент написания этой статьи teensycore полностью оснащен следующими функциями:
- Встроенная поддержка всех 5 периферийных устройств UART
- Программное обеспечение i2c
- Периодические таймеры
- Адресация Gpio на основе номеров выводов
- Полнофункциональная система прерываний
- С точностью до наносекунды
wait()
- 132 МГц прекрасной тактовой частоты
- Аппаратно-оптимизированные вычисления с плавающей запятой
В дополнение к этим системам аппаратного уровня teensycore имеет очень легкую «стандартную библиотеку» (сомнительного качества), которая включает в себя такие структуры данных, как:
- Связанный список
- BTreeMap
- Массив фиксированного размера с операциями очереди/стека
- Вектор переменного размера с операциями очереди/стека
- Страничная память с рудиментарной системой восстановления
- Строки с поддержкой стека
- И более!
Причина, по которой у teensycore есть эти вещи, заключается в том, что они #![no_std]
совместимы. Таким образом, стандартные библиотеки, которые мы знаем и любим, недоступны.
Теперь к примеру. Давайте взглянем на всеми любимый: блинки.
Блинки
Будем мигать встроенным светодиодом. Никаких проводов не нужно! Вот пошаговое руководство по запуску нового проекта teensycore с нуля.
Компиляция проекта
Во-первых, вам нужно установить несколько инструментов сборки. Хотя мы используем cargo
для тяжелой работы, есть немного c
, некоторые действия компоновщика и куча грузовых флагов, которые происходят за кулисами.
sudo apt install gcc-arm-none-eabi jq
Затем вам нужно настроить rust, чтобы он мог использовать правильный набор инструментов.
rustup default nightly rustup target add thumbv7em-none-eabi
Теперь вы готовы создать проект teensycore.
cargo new blinky --lib cd blinky
Измените файл Cargo.toml
, чтобы в нем был этот раздел lib, и включите teensycore в зависимости.
[package] name = "blinky" version = "0.1.0" edition = "2021" [lib] crate-type = ["staticlib"] path = "src/lib.rs" [dependencies] teensycore = "^0.0.11"
В основном это стандартные вещи, но сама сборка немного странная. К сожалению, в настоящее время он написан на bash, поэтому он будет работать только на машинах Linux/WSL/Mac.
Загрузите файл сборки.
curl https://raw.githubusercontent.com/SharpCoder/teensycore/main/build-template.sh > build.sh # Add execute permission to the build chmod +x ./build.sh
Из-за дополнительных шагов компоновщика, чтобы поместить наше ядро в нужное место в файле, и из-за этого надоедливого файла .c
— родился этот скрипт build.sh
. По сути, груз создает вашу ржавчину, затем использует arm-none-eabi-ld
для подключения всего и objcopy
для создания шестнадцатеричного файла.
Все, что осталось, — это точка входа в сам teensycore. Вот код, который вы можете ввести в src/lib.rs
, чтобы начать.
#![feature(lang_items)] #![no_std] teensycore::main!({ pin_mode(13, Mode::Output); loop { pin_out(13, Power::High); wait_ns(1 * S_TO_NANO); pin_out(13, Power::Low); wait_ns(1 * S_TO_NANO); } });
Макрос teensycore::main!({ /* code here*/ })
включает в себя набор моих стандартных библиотек, настраивает UART по умолчанию, запускает периферийные устройства часов и многое другое.
Цель разработки, которую я имел в виду, заключалась в том, чтобы сделать этот макрос максимально приближенным к пустому проекту Arduino. Не нужно слишком много думать о том, что происходит, просто приступайте к делу!
pin 13
— это контакт gpio, к которому подключен внутренний светодиод в teensy 4.0. Так что переключать это каждую секунду довольно легко в teensycore.
Давайте рассмотрим более продвинутый проект.
Чтение/запись данных по I2C
Далее давайте изучим интерфейс i2c. Я беззастенчиво смоделировал его по интерфейсу Arduino, потому что в этом есть большой смысл. Предположим, у вас завалялся лишний чип eeprom (в данном примере я использую 24LC512). Вот как читать и записывать данные из него…
(Примечание: у меня линия SDA подключена к контакту 18, линия SCL подключена к контакту 19)
#![feature(lang_items)] #![crate_type = "staticlib"] #![no_std] teensycore::main!({ use teensycore::*; let mut wire = I2C::begin(18, 19); wire.set_speed(I2CSpeed::Fast400kHz); wire.begin_transmission(0x50, true); // First two bytes are memory address wire.write(&[0, 0]); // Next is a sequential write of data wire.write(b"EARTH"); wire.end_transmission(); // Settle time for whole-page write. Per docs. wait_ns(250 * MS_TO_NANO); // Select the address we wish to read wire.begin_transmission(0x50, true); wire.write(&[0, 0]); wire.end_transmission(); // Perform read request wire.begin_transmission(0x50, false); // Use the `debug_str` functionality to output this data to the // TX2 UART. Pin 8 on the teensy. For debugging purposes. debug_str(&[ // Send 'true' as the second parameter to include an ack // This tells the chip we wish to do sequential reads // with automatic addr incrementation. wire.read(true), wire.read(true), wire.read(true), wire.read(true), wire.read(true), ]); wire.end_transmission(); });
Когда я изначально писал этот код, у меня была настоящая Arduino, подключенная к контакту 8 (TX2), и я использовал последовательный монитор Arduino для отслеживания результатов eeprom. Это была крутая внешняя проверка систем UART и I2C.
Давайте немного разберемся.
let mut wire = I2C::begin(18, 19); wire.set_speed(I2CSpeed::Fast400kHz);
Согласно документации i2c, Teensycore i2c поддерживает «быстрый режим» и обычный режим. Моя библиотека i2c очень быстро работает на обычных проводах gpio. Таким образом, вы можете использовать его с любыми доступными контактами GPIO, что довольно удобно.
Транзакция выполняется аналогично библиотеке Arduino Wire
.
wire.begin_transmission(0x50, true); wire.write(&[0, 0]); wire.write(b"EARTH"); wire.end_transmission();
Вы begin_transmission( addr, write_mode )
, а затем end_transmission()
, когда закончите.
Если вы укажете false
для второго параметра, вы будете в режиме чтения. А захват байтов можно сделать так:wire.read(true)
Этот параметр просто говорит, отправлять ли обратно подтверждение или нет.
Это действительно все, что есть в этой коммуникационной библиотеке i2c.
Заключение
Мы рассмотрели основы запуска нового проекта teensycore с нуля и использования различных подсистем для мигания индикаторов, связи через i2c и передачи отладочной последовательной информации. Со временем я буду добавлять в это ядро и писать больше статей о том, как использовать как новые, так и старые функции.