Зажигаем светодиод
Закончим главу тем, что зажжём один из многочисленных светодиодов MB2. Для этого воспользуемся одним из трейтов, которые предоставляет embedded-hal, а именно трейтом OutputPin, который позволяет включать и выключать вывод.
Светодиоды micro:bit
На обратной стороне micro:bit видно квадрат 5×5 из светодиодов, который обычно называют матрицей светодиодов (LED matrix). Такое расположение позволяет обойтись не 25 отдельными выводами для управления каждым светодиодом, а всего 10 (5+5): ими задаётся, какой столбец и какая строка матрицы будут гореть.
Пока для управления светодиодами мы будем использовать крейт microbit-v2. В следующей главе подробно разберём все доступные варианты.
Зажигаем светодиод!
Код, который зажигает светодиод в матрице, на самом деле довольно прост, но требует небольшой подготовки. Сначала посмотрите на src/main.rs, а затем пройдёмся по нему шаг за шагом.
#![deny(unsafe_code)]
#![no_main]
#![no_std]
use cortex_m_rt::entry;
use embedded_hal::digital::OutputPin;
use microbit::board::Board;
use panic_halt as _;
#[entry]
fn main() -> ! {
let mut board = Board::take().unwrap();
board.display_pins.col1.set_low().unwrap();
board.display_pins.row1.set_high().unwrap();
loop {
core::hint::spin_loop();
}
}
Первые строки до функции main — это базовые импорты и настройка, с которыми мы в основном уже встречались. А вот функция main выглядит совсем иначе, чем всё, что мы видели до сих пор.
Первая строка связана с внутренним устройством большинства HAL, написанных на Rust. Как мы обсуждали ранее, они строятся поверх крейтов PAC, которые владеют (в смысле Rust) всей периферией чипа. Когда мы пишем
let mut board = Board::take().unwrap();
мы забираем всю эту периферию у PAC и связываем её с переменной. В этом случае мы работаем не просто с HAL, а со всем BSP, поэтому вызов также забирает во владение Rust-представление других чипов на плате.
ПРИМЕЧАНИЕ: Если вам интересно, зачем здесь
unwrap(): в теорииtake()может быть вызван более одного раза. Тогда периферия будет представлена двумя разными переменными, и две переменные будут менять один и тот же ресурс, что приведёт к путанице в поведении. Чтобы этого избежать, PAC реализованы так, что при повторном взятии периферии программа запаникует.
(Если всё это вас запутало, в следующей главе всё объясняется ещё раз, подробнее.)
Теперь мы можем зажечь светодиод, подключённый к row1, col1, установив вывод row1 в высокий уровень (то есть включив его). Вывод col1 можно оставить в низком уровне, потому что так устроена схема матрицы светодиодов. Кроме того, embedded-hal спроектирован так, что любая операция с аппаратурой может вернуть ошибку, даже простое включение или выключение вывода. Поскольку в нашем случае это крайне маловероятно, можно просто вызвать unwrap() для результата.
Проверка
Проверить нашу маленькую программу несложно. Запускаем cargo embed и даём ему прошить программу, как и раньше. Затем открываем GDB и подключаемся к GDB-стабу.
$ gdb ../../../target/thumbv7em-none-eabihf/debug/meet-your-software
(gdb) target remote :1337
Remote debugging using :1337
cortex_m_rt::Reset () at /home/nix/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-rt-0.6.12/src/lib.rs:489
489 pub unsafe extern "C" fn Reset() -> ! {
(gdb)
Теперь запускаем программу командой GDB continue: один из светодиодов на передней стороне micro:bit должен загореться.