Acendendo

embedded-hal

Neste capítulo, vamos fazer com que um dos muitos LEDs na parte de trás do micro:bit acenda, já que isso é basicamente o "Hello World" da programação embarcada. Para realizar essa tarefa, vamos usar um dos recursos fornecidos pelo embedded-hal, especificamente o trait OutputPin, que nos permite ligar ou desligar um pino.

Os LEDs do micro:bit

Na parte de trás do micro:bit, você pode ver um quadrado de LEDs de 5x5, geralmente chamado de matriz de LEDs. Essa disposição em matriz é usada para que, em vez de precisarmos usar 25 pinos separados para controlar cada um dos LEDs, podemos usar apenas 10 (5+5) pinos para controlar qual coluna e qual linha de nossa matriz se acende.

NOTE que a equipe do micro:bit v1 implementou isso de forma um pouco diferente. A página de esquema deles indica que na verdade é implementado como uma matriz de 3x9, mas algumas colunas simplesmente permanecem não utilizadas.

Normalmente, para determinar quais pinos específicos devemos controlar para acender um LED específico, agora teríamos que ler o esquema do micro:bit v2 ou o esquema do micro:bit v1, respectivamente. Felizmente para nós, no entanto, podemos usar o BSP do micro:bit mencionado anteriormente, que abstrai todas essas informações de forma conveniente para nós.

Realmente acendendo!

O código necessário para acender um LED na matriz é, na verdade, bastante simples, mas requer um pouco de configuração. Primeiro, dê uma olhada e depois podemos passar por ele passo a passo:

#![deny(unsafe_code)]
#![no_main]
#![no_std]

use cortex_m_rt::entry;
use panic_halt as _;
use microbit::board::Board;
use microbit::hal::prelude::*;

#[entry]
fn main() -> ! {
    let mut board = Board::take().unwrap();

    board.display_pins.col1.set_low().unwrap();
    board.display_pins.row1.set_high().unwrap();

    loop {}
}

As primeiras linhas até a função principal apenas realizam algumas importações básicas e configurações que já analisamos anteriormente. No entanto, a função principal parece bastante diferente do que vimos até agora.

A primeira linha está relacionada à forma como a maioria das HALs escritas em Rust funcionam internamente. Como discutido anteriormente, elas são construídas em cima de crates PAC que possuem (no sentido Rust) todos os periféricos de um chip. let mut board = Board::take().unwrap(); basicamente pega todos esses periféricos do PAC e os vincula a uma variável. Neste caso específico, não estamos apenas trabalhando com uma HAL, mas com todo um BSP, então isso também assume a propriedade da representação Rust de outros chips na placa.

NOTA: Se você está se perguntando por que tivemos que chamar unwrap() aqui, teoricamente é possível que take() seja chamado mais de uma vez. Isso levaria aos periféricos sendo representados por duas variáveis separadas e, portanto, muitos comportamentos possivelmente confusos, porque duas variáveis modificam o mesmo recurso. Para evitar isso, as PACs são implementadas de tal forma que causariam um pânico se você tentasse pegar os periféricos duas vezes.

Agora podemos acender o LED conectado a row1, col1 definindo o pino row1 como alto (ou seja, ligando-o). A razão pela qual podemos deixar col1 definido como baixo é por causa do funcionamento do circuito da matriz de LEDs. Além disso, embedded-hal é projetado de forma que cada operação no hardware possa retornar possivelmente um erro, mesmo apenas alternando um pino entre ligado e desligado. Como isso é altamente improvável em nosso caso, podemos apenas unwrap() o resultado.

Testando

Testar nosso pequeno programa é bastante simples. Primeiro, coloque-o em src/main.rs. Em seguida, basta executar o comando cargo embed da última seção novamente, deixe-o gravar e apenas como antes. Em seguida, abra o GDB e conecte-se ao GDB stub:

$ # Seu comando de depuração GDB da última seção
(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)

Se agora deixarmos o programa rodar usando o comando continue do GDB, um dos LEDs na parte de trás do micro:bit deve acender.