ラズピコrustでmicro:bitと連携させてみた

ラズパイ財団プロダクト同士でなんか作ってみようと思って、micro:bitをフロントエンドとしてmic入力とLED出力を使った、音声のレベル表示(一応LED25個で25レベルの表示ができます)をやってみた

macro:bit (p1) –> ラズピコADC1(平均化処理)-> PWM出力およそ22KHzをrcでローパス -> micro:bit(p2)という流れ

<micro:bit側のコード>

from microbit import *

def show_bar(level):  # level: 0-25
    display.clear()
    height = level // 5  # 5段に分割
    width = level % 5    # 各段の幅
    
    # 下からバー積み上げ
    for y in range(5):
        if 4-y < height:
            for x in range(5):
                display.set_pixel(x, 4-y, 9)
        elif 4-y == height:
            for x in range(width):
                display.set_pixel(x, 4-y, 9)

while True:
    mic_level = microphone.sound_level()
    pin1.write_analog(mic_level * 4)
    
    control_level = pin2.read_analog()
    # 0-25に変換
    bar_level = min(control_level // 40, 25) 
    show_bar(bar_level)
    sleep(10)

<ラズピコ側>

Cargo.toml

embedded-halの1.0系は0.2と互換なし、embeded-halはobject指向言語で言うところのinterfaceでrp-235x-halは実装、つまりプロセッサが変わっても共通だろうinterfaceをembedded-halはtraitの集合として持っている


[package]
edition = "2024"
name = "rust_starter"
version = "0.1.0"
license = "MIT or Apache-2.0"


[dependencies]
cortex-m = "0.7.7"
cortex-m-rt = "0.7.5"
rp235x-hal = "0.3"
embedded-hal = "0.2"
#embedded-hal = "1.0.0" does not support rp235x-hal yet
defmt = "1.0.1"
defmt-rtt = "1.0.1"
panic-probe = { version = "1.0", features = ["print-defmt"] }

[build-dependencies]
regex = "1.11.0"

[target.'cfg( target_arch = "arm" )'.dependencies]
panic-probe = { version = "1", features = ["print-defmt"] }

[target."thumbv8m.main-none-eabihf".dependencies]
rp235x-hal = { version = "0.3", features = ["rt", "critical-section-impl"] }

<Rustコード>

Lチカコードを修正

#![no_std]
#![no_main]

use defmt_rtt as _;
use hal::pac;

use embedded_hal::adc::OneShot;
use embedded_hal::blocking::delay::DelayUs;
use embedded_hal::PwmPin;

#[cfg(target_arch = "riscv32")]
use panic_halt as _;
#[cfg(target_arch = "arm")]
use panic_probe as _;

// Alias for our HAL crate
use hal::entry;

#[cfg(rp2350)]
use rp235x_hal as hal;

#[cfg(rp2040)]
use rp2040_hal as hal;

// use bsp::entry;
// use bsp::hal;
// use rp_pico as bsp;

/// The linker will place this boot block at the start of our program image. We
/// need this to help the ROM bootloader get our code up and running.
/// Note: This boot block is not necessary when using a rp-hal based BSP
/// as the BSPs already perform this step.
#[unsafe(link_section = ".boot2")]
#[used]
#[cfg(rp2040)]
pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_W25Q080;

/// Tell the Boot ROM about our application
#[unsafe(link_section = ".start_block")]
#[used]
#[cfg(rp2350)]
pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe();

/// External high-speed crystal on the Raspberry Pi Pico 2 board is 12 MHz.
/// Adjust if your board has a different frequency
const XTAL_FREQ_HZ: u32 = 12_000_000u32;
const DELAY: u32 = 1000; // μs delay
const SAMPLES: u32 = 250;

/// Entry point to our bare-metal application.
///
/// The `#[hal::entry]` macro ensures the Cortex-M start-up code calls this function
/// as soon as all global variables and the spinlock are initialised.
///
/// The function configures the rp2040 and rp235x peripherals, then toggles a GPIO pin in
/// an infinite loop. If there is an LED connected to that pin, it will blink.
#[entry]
fn main() -> ! {
    // 1. Peripherals取得
    let mut pac = pac::Peripherals::take().unwrap();

    // 2. クロック・ウォッチドッグ初期化
    let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
    let clocks = hal::clocks::init_clocks_and_plls(
        XTAL_FREQ_HZ,
        pac.XOSC,
        pac.CLOCKS,
        pac.PLL_SYS,
        pac.PLL_USB,
        &mut pac.RESETS,
        &mut watchdog,
    )
    .ok()
    .unwrap();

    // 3. GPIO初期化
    let sio = hal::Sio::new(pac.SIO);
    let pins = hal::gpio::Pins::new(
        pac.IO_BANK0,
        pac.PADS_BANK0,
        sio.gpio_bank0,
        &mut pac.RESETS,
    );

    let mut delay = hal::Timer::new_timer0(pac.TIMER0, &mut pac.RESETS, &clocks);

    // 4. ADC設定
    let mut adc = hal::Adc::new(pac.ADC, &mut pac.RESETS);
    let mut adc_pin = hal::adc::AdcPin::new(pins.gpio26).unwrap();

    // 5. PWM設定
    let pwm_slices = hal::pwm::Slices::new(pac.PWM, &mut pac.RESETS);
    let mut pwm = pwm_slices.pwm0;
    pwm.set_ph_correct();
    pwm.set_top(4095);
    pwm.enable();

    // GP0 = PWM Slice 0 Channel A(alomost 20KHz duty 99% @3.3v full scale)

    let mut pwm_channel = pwm.channel_a;
    let _pwm_pin = pins.gpio0.into_function::(); // GP0 as a PWM output

    loop {
        let mut sum: u32 = 0;

        // measure "samples" time and make average
        for _ in 0..SAMPLES {
            let v: u16 = adc.read(&mut adc_pin).unwrap();
            sum += v as u32;
        }

        let avg = (sum / SAMPLES) as u16;
        pwm_channel.set_duty(avg);
        delay.delay_us(DELAY);
    }
}

/// Program metadata for `picotool info`
#[unsafe(link_section = ".bi_entries")]
#[used]
pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 5] = [
    hal::binary_info::rp_cargo_bin_name!(),
    hal::binary_info::rp_cargo_version!(),
    hal::binary_info::rp_program_description!(c"Blinky Example"),
    hal::binary_info::rp_cargo_homepage_url!(),
    hal::binary_info::rp_program_build_attribute!(),
];

// End of file

動いてるところの動画は以下に、

まだ周辺回路サポートのcrateは途上ではあるけれども、組み込みでも普通に使えるようになってきたのは大きな進歩

 

admin

コメントを残す