天狗会議録 Posts Pages About

Rasberry Pi PicoでLチカ

#embedded #python #rasberrypipico #rust
2024/7/17

概要

ついに、ファームウェア開発まで興味のレベルが下がった。 そのため、アケコン自作を目標に置いて、ファームウェア開発に取り組むことにした。

当記事執筆現在の進捗は、Rasberry Pi Picoとアケコンのボタンを接続し、ボタンを押してボード上のLEDを光らせる、という段階である。 つまり、まだUSB通信について全く触れていない。 とはいえ、このレベルの開発はド素人であるため、今後とも気長に学習することにする。

なお、開発環境はUbuntu 24.04である。

部品

回路

わざわざ書くまでもないが、回路は次のようである。


私のような電子系ド素人にとっては、電源→ボタン→インプットピン、という回路の方が直感的である。 しかし、これはボタンが上がっているときに、インプットピンが回路から外れてしまう。 これを解決するために、多くのマイコンボードはプルアップ抵抗を用いている。 次のような原理で、ボタンが上がっているときにHIGH・下がっているときにLOWとなる。 ただし、インプットピンの原理はその電圧を計測することであり、電圧計の内部抵抗は原理的にプルアップ抵抗より大きく、ボタンの抵抗は常識的に考えて極めて小さい。


MicroPython

MicroPythonを用いることで、簡単にプログラムを実行できる。 実行までの手順は次の通りである。

  1. Thonnyをインストールする
  2. BOOTSELボタンを押しながらRasberry Pi Picoをマシンに接続する
  3. Rasberry Pi Pico内のINDEX.HTMLを頼りにMicroPythonのUF2ファイルをダウンロードする
  4. UF2ファイルをRasberry Pi PicoにD&Dする
  5. Thonnyを起動する
  6. MicroPythonを書いて実行ボタンを押す

Lチカのコードは次のようである。

import machine
import utime
led = machine.Pin(25, machine.Pin.OUT)
while True:
    led.value(1)
    utime.sleep(0.5)
    led.value(0)
    utime.sleep(0.5)

ボタンを押したときにLEDを光らせるコードは次のようである。

import machine
import utime
led = machine.Pin(25, machine.Pin.OUT)
button = machine.Pin(14, machine.Pin.IN, machine.Pin.PULL_UP)
while True:
    if button.value() == 1:
        led.value(0)
    else:
        led.value(1)

rp-pico

私の性格上MicroPythonで開発するのは気分が良くないので、Rustにリファクタリングすることにした。 rp-picoというクレートを用いると、比較的簡単に開発できるようである。 近いうちにembedded-halまで落ちたいものだ。

thumbv6m-none-eabiターゲットを追加する必要がある。 また、elf2uf2-rsとflip-linkをインストールする。 なお、そのためにはlibudev-devのインストールが必要だった。

$ sudo apt install libudev-dev
$ rustup target add thumbv6m-none-eabi
$ cargo install elf2uf2-rs flip-link

Cargo.tomlは次のようである。

[package]
name = "led"
version = "0.1.0"
edition = "2021"

[dependencies]
rp-pico = "0.9.0"
cortex-m = "0.7.7"
cortex-m-rt = "0.7.3"
embedded-hal = "1.0.0"

DXのためにも、次の.cargo/config.tomlを作る。

[build]
target = "thumbv6m-none-eabi"

[target.thumbv6m-none-eabi]
runner = "elf2uf2-rs -d"
rustflags = [
  "-C", "linker=flip-link",
  "-C", "link-arg=--nmagic",
  "-C", "link-arg=-Tlink.x",
]

ルートディレクトリに次のmemory.xを作る。

MEMORY {
    BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100
    FLASH : ORIGIN = 0x10000100, LENGTH = 2048K - 0x100
    RAM   : ORIGIN = 0x20000000, LENGTH = 256K
}

EXTERN(BOOT2_FIRMWARE)

SECTIONS {
    .boot2 ORIGIN(BOOT2) :
    {
        KEEP(*(.boot2));
    } > BOOT2
} INSERT BEFORE .text;

以上で環境構築終了である。 以降、次の手順で実行できる。

  1. BOOTSELボタンを押しながらRasberry Pi Picoをマシンに接続する
  2. cargo runを実行する

Lチカのコードは次のようである。

#![no_std]
#![no_main]

use core::panic::PanicInfo;
use cortex_m::delay::Delay;
use embedded_hal::digital::OutputPin;
use rp_pico::{
    entry,
    hal::{
        clocks::{self, Clock},
        pac::{CorePeripherals, Peripherals},
        watchdog::Watchdog,
        Sio,
    },
    Pins,
};

const XOSC_CRYSTAL_FREQ: u32 = 12_000_000;
const DELAY_TIME: u32 = 500;

#[panic_handler]
fn panic(_: &PanicInfo) -> ! {
    loop {}
}

#[entry]
fn main() -> ! {
    let core = CorePeripherals::take().unwrap();
    let mut pac = Peripherals::take().unwrap();

    // get pins
    let pins = Pins::new(
        pac.IO_BANK0,
        pac.PADS_BANK0,
        Sio::new(pac.SIO).gpio_bank0,
        &mut pac.RESETS,
    );

    // get an LED pin
    let mut led = pins.led.into_push_pull_output();

    // create Delay to wait
    let clocks = clocks::init_clocks_and_plls(
        XOSC_CRYSTAL_FREQ,
        pac.XOSC,
        pac.CLOCKS,
        pac.PLL_SYS,
        pac.PLL_USB,
        &mut pac.RESETS,
        &mut Watchdog::new(pac.WATCHDOG),
    )
    .ok()
    .unwrap();
    let mut delay = Delay::new(core.SYST, clocks.system_clock.freq().to_Hz());

    loop {
        led.set_low().unwrap();
        delay.delay_ms(DELAY_TIME);
        led.set_high().unwrap();
        delay.delay_ms(DELAY_TIME);
    }
}

ボタンを押したときにLEDを光らせるコードは次のようである。

#![no_std]
#![no_main]

use core::panic::PanicInfo;
use embedded_hal::digital::{InputPin, OutputPin};
use rp_pico::{
    entry,
    hal::{pac::Peripherals, Sio},
    Pins,
};

#[panic_handler]
fn panic(_: &PanicInfo) -> ! {
    loop {}
}

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

    // get pins
    let pins = Pins::new(
        pac.IO_BANK0,
        pac.PADS_BANK0,
        Sio::new(pac.SIO).gpio_bank0,
        &mut pac.RESETS,
    );

    // get an LED pin and a button pin
    let mut led = pins.led.into_push_pull_output();
    let mut button = pins.gpio14.into_pull_up_input();

    loop {
        if button.is_high().ok().unwrap() {
            led.set_low().unwrap();
        } else {
            led.set_high().unwrap();
        }
    }
}

参考文献