HUB75パネルをRustで駆動してみた

Arduino言語との速度比較のために、Rustでループ処理部分をベンチマークしてみた

プラットホームはembassyを使って、そのソースコードに埋め込み、表示パターンは階調表示のあるテストパターン、routine.rsは表示データの初期化を行うだけですが

コードは二つに分割

<main.rs>

//
// % cargo run --bin raspico

#![no_std]
#![no_main]

use embassy_executor::Spawner;
use embassy_rp::gpio::{Level, Output};
use {defmt_rtt as _, panic_probe as _};

mod routine;
use routine::DispData;

#[embassy_executor::main]
async fn main(_spawner: Spawner) {
    let p = embassy_rp::init(Default::default());
    let mut r1 = Output::new(p.PIN_2, Level::High);
    let mut r2 = Output::new(p.PIN_5, Level::High);
    let mut g1 = Output::new(p.PIN_3, Level::High);
    let mut g2 = Output::new(p.PIN_8, Level::High);
    let mut b1 = Output::new(p.PIN_4, Level::High);
    let mut b2 = Output::new(p.PIN_9, Level::High);
    let mut a = Output::new(p.PIN_10, Level::Low);
    let mut b = Output::new(p.PIN_16, Level::Low);
    let mut c = Output::new(p.PIN_18, Level::Low);
    let mut d = Output::new(p.PIN_20, Level::Low);
    let _e = Output::new(p.PIN_22, Level::Low);
    let mut clk = Output::new(p.PIN_11, Level::Low);
    let mut lat = Output::new(p.PIN_12, Level::High);
    let mut oe = Output::new(p.PIN_13, Level::High);

    let a_array: [u8; 16] = [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1];
    let b_array: [u8; 16] = [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1];
    let c_array: [u8; 16] = [0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1];
    let d_array: [u8; 16] = [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1];

    let mut disp_data = DispData {
        _array64: [0; 64],
        array4d: [[[[0; 64]; 32]; 3]; 2],
    };
    let new_disp_data = disp_data.new();

    loop {

        for i in 0..16 {
            // row number
            if a_array[i] == 0 {
                a.set_low();
            } else {
                a.set_high();
            };
            if b_array[i] == 0 {
                b.set_low();
            } else {
                b.set_high();
            };
            if c_array[i] == 0 {
                c.set_low();
            } else {
                c.set_high();
            };
            if d_array[i] == 0 {
                d.set_low();
            } else {
                d.set_high();
            };

            for b in 0..15 {
                // shading
                lat.set_high();
                for k in 0..64 {
                    // column data
                    clk.set_low();
                    //
                    if new_disp_data.array4d[0][2][i][k] > b {
                        r1.set_high();
                    } else {
                        r1.set_low();
                    }
                    if new_disp_data.array4d[0][2][16 + i][k] > b {
                        r2.set_high();
                    } else {
                        r2.set_low();
                    }
                    if new_disp_data.array4d[0][1][i][k] > b {
                        g1.set_high();
                    } else {
                        g1.set_low();
                    }
                    if new_disp_data.array4d[0][1][16 + i][k] > b {
                        g2.set_high();
                    } else {
                        g2.set_low();
                    }
                    if new_disp_data.array4d[0][0][i][k] > b {
                        b1.set_high();
                    } else {
                        b1.set_low();
                    }
                    if new_disp_data.array4d[0][0][16 + i][k] > b {
                        b2.set_high();
                    } else {
                        b2.set_low();
                    }
                    clk.set_high();
                }
                oe.set_high();
                lat.set_low();
                oe.set_low();
            }
        }
    }
}

<routine.rs>

表示データの作成をするだけ、


static ARRAY64: [u8; 64] = [
    0, 1, 1, 2, 3, 5, 8, 13, 13, 8, 5, 3, 2, 1, 1, 0, 0, 1, 1, 2, 3, 5, 8, 13, 13, 8, 5, 3, 2, 1,
    1, 0, 0, 1, 1, 2, 3, 5, 8, 13, 13, 8, 5, 3, 2, 1, 1, 0, 0, 1, 1, 2, 3, 5, 8, 13, 13, 8, 5, 3,
    2, 1, 1, 0,
];

pub struct DispData {
    pub _array64: [u8; 64],
    pub array4d: [[[[u8; 64]; 32]; 3]; 2],
}

impl DispData {
    pub fn new(&mut self) -> Self {
        let mut dis_data = DispData {
            _array64: ARRAY64,
            array4d: [[[[0; 64]; 32]; 3]; 2],
        };
        for j in 0..3 {
            for i in 0..32 {
                dis_data.array4d[0][j][i] = ARRAY64;
            }
        }
        dis_data
    }
}

<実行速度>

およそ10ms(Arduino言語ではおよそ14msだから3割程度高速化)、繰り返し(メモリ読み出しとgpio書き込み)回数は一回のリフレッシュループで、

64*3*16*32 = 98,304(16行*3色*パネル上下*16階調)ということで一回のr/g/bデータの書き込みでおよそ1μs費やしている勘定で、クロック数ではおよそ130クロックとなります、階調処理での条件分岐が処理時間では大きそうですね

写真は行アドレス信号のDなので一周期がリフレッシュ周期

結果だけ見るとRustの優位性はそれほど無いように見えるかもしれないけど、メモリ管理やコンパイラの出来の良さ、複数タスク管理をasync/awaitで簡単に実現できるから他の言語で実現(Arduino言語ならRTOS使うことになるけれども、async/awaitのほうが軽量だと)するよりもアドバンテージはあると思う

P.S. 2024/12/24

この程度(3割)の改善ならば、pico 2を使えば簡単に逆転できそうだよね、消費電力とか別にすればね、ハードの進化は偉大なりか

 

admin

 

コメントを残す