Rust

Rust embedded. Spi и embedded-graphics.

Друзья, привет! Продолжаем тему Rust для embedded. Это уже 4 статья по теме rust embedded, для ознакомления с предыдущими статьями, вот список:

Rust embedded. Stopwatch.
Rust embedded. Spi и embedded-graphics.
Rust embedded. Gpio.
Rust embedded. Сиквел.
Rust embedded.

Сегодня у нас в статье GPIO, SPI, графический экран SSD1306 и крейт “embedded-graphics”. Задача примера по нажатию на пользовательскую кнопку на отладочной плате вращать изображение на 180 градусов, изображение выводится на дисплей SSD1306. Для этого мы сконфигурируем пользовательскую кнопку, настроим базовое тактирование RCC, настроим шину SPI и пин CS (выбор устройства на шине).

Embedded-graphics

Крейт “embedded-graphics” призван сделать рисование 2D-примитивов супер простым. В настоящее время он поддерживает следующее:

  • 1 бит на пиксель изображения
  • 8 бит на пиксель изображения
  • 16 бит на пиксель изображения
  • Примитивы
    • Линии
    • Прямоугольники (и квадраты)
    • Круги
    • Треугольники
  • Текст с несколькими шрифтами

Основная цель состоит в том, чтобы сделать вышеупомянутое без использования любых буферов; крейт должен работать без динамического распределения памяти и без предварительного выделения больших кусков памяти. Для достижения этого используется подход, основанный на итераторе, где значения и позиции пикселей рассчитываются на лету с минимальным сохраненным состоянием. Это позволяет запущенному приложению использовать намного меньше оперативной памяти при небольшом или нулевом снижении производительности. Изображение для вывода на дисплей будет браться из файлов. Выводить будем два изображения первое изображение (rust.raw) это лого Rust, второе изображение (ssd1306-image.data) талисман Rust краб (неофициальный талисман). Для выбора необходимого изображения закомментируй или раскомментируй строку кода – let im = Image1BPP::new(include_bytes!(“./rust.raw”), 64, 64); или let im = Image1BPP::new(include_bytes!(“./ssd1306-image.data”), 128, 64);

Модули
coord – знаковая 2D координация в пространстве экрана
dev – помощники в тестировании и разработки
drawable – типаж Drawable и помощники
fonts – пиксельные шрифты
image – объект изображения
pixelcolor – типаж цветного пикселя
prelude – служит вступлением
primitives – графические примитивы
style – стилистические структуры для настройки внешнего вида объектов
transform – преобразование для графических объектов
unsignedcoord – без знаковая 2D координация в пространстве экрана

Типажи (Трейты) 
Drawing – основной типаж этого крейта. Все графические объекты должны реализовывать данный момент. Типаж (trait, трейт) – это набор методов, определённых для неизвестного типа: Self. Они могут получать доступ к другим методам, которые были объявлены в том же типаже.

cargo-features = ["default-run"]

[package]
authors = ["Roman Shulenkov postmaster@mcu.by"]
categories = ["embedded", "no-std"]
name = "stm32f429i_disco"
version = "0.1.0"
edition = "2018"

[dependencies]
cortex-m = "0.6.2"
cortex-m-rt = "0.6.12"
panic-halt = "0.2.0"
panic-semihosting = "0.5.3"
cortex-m-semihosting = "0.3.5"
ssd1306 = "0.2.6"
embedded-graphics = "0.4.9"


[dependencies.stm32f4]
version = "0.10.0"
features = ["stm32f429", "rt"]

[dependencies.stm32f4xx-hal]
version = "0.7"
features = ["rt", "stm32f429"]
#![no_std]
#![no_main]

extern crate cortex_m;
extern crate cortex_m_rt as rt;
extern crate panic_semihosting;
extern crate stm32f4xx_hal as hal;

use crate::hal::{
    prelude::*,
    spi::Spi, 
};
use hal::spi::{Mode, Phase, Polarity};

use cortex_m_rt::{ExceptionFrame, entry, exception};
use embedded_graphics::{image::*,
    prelude::*};
use ssd1306::{prelude::*, Builder as SSD1306Builder};

use stm32f4::stm32f429;

#[entry]
fn main() -> ! {

    let dp = stm32f429::Peripherals::take().unwrap();
    let cp = cortex_m::peripheral::Peripherals::take().unwrap();

    let rcc = dp.RCC.constrain();
    let clocks = rcc.cfgr.sysclk(180.mhz()).freeze();

    let gpioa = dp.GPIOA.split();
    let gpioe = dp.GPIOE.split();    

    //spi4
    //sck  - pe2
    //miso - pe5
    //mosi - pe6
    //cs - pe4
    //dc - pe3

    let sck = gpioe.pe2.into_alternate_af5();
    let miso = gpioe.pe5.into_alternate_af5();
    let mosi = gpioe.pe6.into_alternate_af5();

    let spi = Spi::spi4(dp.SPI4, (sck, miso, mosi), Mode {
        polarity: Polarity::IdleLow,
        phase: Phase::CaptureOnFirstTransition,
    }, stm32f4xx_hal::time::KiloHertz(2000).into(),clocks);

    let dc = gpioe.pe3.into_push_pull_output();
    let mut cs = gpioe.pe4.into_push_pull_output();
    let mut delay = hal::delay::Delay::new(cp.SYST, clocks);

    cs.set_high().unwrap();
    delay.delay_ms(100_u32);
    cs.set_low().unwrap();

    let btn = gpioa.pa0.into_pull_down_input();

    // Set up the display
    let mut disp: GraphicsMode<_> = SSD1306Builder::new().connect_spi(spi, dc).into();
    disp.init().unwrap();
    disp.flush().unwrap();

    // Display the rustacean
    let im = Image1BPP::new(include_bytes!("./rust.raw"), 64, 64);
    // let im = Image1BPP::new(include_bytes!("./ssd1306-image.data"), 128, 64);
    
    disp.draw(im.into_iter());
    disp.flush().unwrap();

    // Set up state for the loop
    let mut orientation = DisplayRotation::Rotate0;
    let mut was_pressed = btn.is_low().unwrap();

    // This runs continuously, as fast as possible
    loop {
        // Check if the button has just been pressed.
        // Remember, active low.
        let is_pressed = btn.is_low().unwrap();
        if !was_pressed && is_pressed {
            // Since the button was pressed, flip the screen upside down
            orientation = get_next_rotation(orientation);
            disp.set_rotation(orientation).unwrap();
            // Now that we've flipped the screen, store the fact that the button is pressed.
            was_pressed = true;
        } else if !is_pressed {
            // If the button is released, confirm this so that next time it's pressed we'll
            // know it's time to flip the screen.
             was_pressed = false;
        }
    }
}

/// Helper function - what rotation flips the screen upside down from
/// the rotation we're in now?
fn get_next_rotation(rotation: DisplayRotation) -> DisplayRotation {
    return match rotation {
        DisplayRotation::Rotate0 => DisplayRotation::Rotate180,
        DisplayRotation::Rotate180 => DisplayRotation::Rotate0,

        // Default branch - if for some reason we end up in one of the portrait modes,
        // reset to 0 degrees landscape. On most SSD1306 displays, this means down is towards
        // the flat flex coming out of the display (and up is towards the breakout board pins).
        _ => DisplayRotation::Rotate0,
    };
}

#[exception]
fn HardFault(ef: &ExceptionFrame) -> ! {
    panic!("{:#?}", ef);
}

Все исходники лежат на github, клонируй и пользуйся в свое удовольствие.
git clone https://github.com/mcuby/rust-stm32f429i-disco

One thought on “Rust embedded. Spi и embedded-graphics.

Leave a Reply

Your email address will not be published. Required fields are marked *

Лимит времени истёк. Пожалуйста, перезагрузите CAPTCHA.