Rust славится кросс-компиляцией для всех видов платформ. Варианты набора микросхем ARM входят в список. Некоторое время назад я задался целью создать ядро ​​для Teensy 4.0, написанное на ржавчине. Я быстро понял, что извлечь мой код на повторно используемую платформу будет довольно просто. Таким образом, было создано teensycore, и вы тоже можете наслаждаться этим чрезвычайно легким ядром!

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 и передачи отладочной последовательной информации. Со временем я буду добавлять в это ядро ​​и писать больше статей о том, как использовать как новые, так и старые функции.