液晶二枚目
今度は以前購入した液晶ディスプレイを動かしてみます。
画像の右側のやつですね。
だいぶ昔にAmazonに買いましたが、SPIで液晶(ST7735)と同じくSPIで通信できるSDカード(よくTFカードとも呼ばれますが)スロットがあります。
若干サイズが違う物もST77xx系みたいです。
物によってはタッチパネル(SPI)対応の物もあります。(こっちはILI9341)
今回はST7735の方の液晶を描画してみます。
接続
SPI0のデフォルトピン(右下)を使用しています。
他は…わかんにゃい
(調べたけど出てこなかったしLLMも教えてくれなかった)
SpiBusとSpiDevice
どうやらrustのembedded-halクレートのSPIの定義では2種類の接続方法が提供されているようです。
SpiBusではSPIレーンその物を独占(一つだけの接続)をして排他制御を行います。
SpiDeviceではCSピンによるデバイス選択を行います(一般的なのはこっち)
よってSpiBusではCSピンの制御なしに動作します(まぁ一つだけの接続状態ならCSもいりませんからね)
ソースコード SpiBus
#![no_std]
#![no_main]
use defmt::*;
use defmt_rtt as _;
use embedded_graphics::mono_font::iso_8859_1::FONT_6X10;
use embedded_graphics::mono_font::iso_8859_10::FONT_7X14;
use embedded_graphics::mono_font::MonoTextStyleBuilder;
use embedded_graphics::primitives::{Arc, Line, PrimitiveStyle, PrimitiveStyleBuilder, Rectangle};
use embedded_graphics::text::{Baseline, Text};
use embedded_hal::delay::DelayNs;
use panic_probe as _;
use rp2040_hal::fugit::RateExtU32;
use rp2040_hal::{self as hal, Clock};
use hal::pac;
use embedded_graphics::image::Image;
use embedded_graphics::prelude::*;
use embedded_graphics::pixelcolor::Rgb565;
use tinybmp::Bmp;
#[link_section = ".boot2"]
#[used]
pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_GENERIC_03H;
const XTAL_FREQ_HZ: u32 = 12_000_000u32;
// img
const BITMAP_DATA: &[u8] = include_bytes!("./img.bmp");
#[rp2040_hal::entry]
fn main() -> ! {
info!("Program start!");
let mut pac = pac::Peripherals::take().unwrap();
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();
let mut timer = rp2040_hal::Timer::new(pac.TIMER, &mut pac.RESETS, &clocks);
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 sck = pins.gpio18.into_function::<hal::gpio::FunctionSpi>();
let sda = pins.gpio19.into_function::<hal::gpio::FunctionSpi>();
let rst = pins.gpio22.into_push_pull_output();
let dc = pins.gpio28.into_push_pull_output();
let spi: rp2040_hal::Spi<_, _, _, 8> = hal::Spi::new(pac.SPI0, (sda, sck)).init(
&mut pac.RESETS,
clocks.peripheral_clock.freq(),
16u32.MHz(),
embedded_hal::spi::MODE_0,
);
let mut display = st7735_lcd::ST7735::new(spi, dc, rst, true, false, 128, 160);
display.init(&mut timer).unwrap();
let text_style_a = MonoTextStyleBuilder::new()
.font(&FONT_6X10)
.text_color(Rgb565::WHITE)
.build();
let text_style_b = MonoTextStyleBuilder::new()
.font(&FONT_7X14)
.text_color(Rgb565::GREEN)
.build();
let text_a = Text::with_baseline(
"Hello World.",
Point::new(0, 0),
text_style_a,
Baseline::Top
);
let text_b = Text::with_baseline(
"zin3.cc",
Point::new(0, 10),
text_style_b,
Baseline::Top
);
let bmp = Bmp::from_slice(BITMAP_DATA).unwrap();
let image = Image::new(
&bmp,
Point::new(0, 30)
);
let rectangle = Rectangle::new(
Point::new(16, 24), Size::new(32, 16)
).into_styled(
PrimitiveStyleBuilder::new()
.stroke_width(2)
.stroke_color(Rgb565::RED)
.fill_color(Rgb565::CYAN)
.build(),
);
let ark = Arc::new(
Point::new(60, 60),
40,
-30.0.deg(),
150.0.deg()
)
.into_styled(PrimitiveStyle::with_stroke(Rgb565::GREEN, 4));
loop {
timer.delay_ms(2000);
display.clear(Rgb565::BLACK).unwrap();
display.set_offset(0, 25);
text_a.draw(&mut display).unwrap();
text_b.draw(&mut display).unwrap();
info!("draw text!");
image.draw(&mut display).unwrap();
info!("draw image");
rectangle.draw(&mut display).unwrap();
info!("draw rectangle");
for i in 1..120 {
Line::new(
Point::new(30, 40),
Point::new(70, i)
)
.into_styled(PrimitiveStyle::with_stroke(Rgb565::RED, 2))
.draw(&mut display).unwrap();
}
info!("draw line");
ark.draw(&mut display).unwrap();
info!("draw arc");
}
}こちらはSpiBusなので液晶を扱うクレートも一つ前のバージョンです。
ST7735クレートへ渡す部分もシンプルですね。
描画もテキスト、画像、図形といくつか出してみました。
詳しくはソースを確認してください
ソースコード SpiDevice
#![no_std]
#![no_main]
use defmt::*;
use defmt_rtt as _;
use embedded_graphics::mono_font::iso_8859_1::FONT_6X10;
use embedded_graphics::mono_font::iso_8859_10::FONT_7X14;
use embedded_graphics::mono_font::MonoTextStyleBuilder;
use embedded_graphics::primitives::{Arc, Line, PrimitiveStyle, PrimitiveStyleBuilder, Rectangle};
use embedded_graphics::text::{Baseline, Text};
use embedded_graphics::image::Image;
use embedded_graphics::prelude::*;
use embedded_graphics::pixelcolor::Rgb565;
use embedded_hal::delay::DelayNs;
use embedded_hal_bus::spi::ExclusiveDevice;
use panic_probe as _;
use rp2040_hal::fugit::RateExtU32;
use rp2040_hal::{self as hal, Clock};
use hal::pac;
use tinybmp::Bmp;
#[link_section = ".boot2"]
#[used]
pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_GENERIC_03H;
const XTAL_FREQ_HZ: u32 = 12_000_000u32;
// 画像データ(img.bmp)をバイナリに含める
const BITMAP_DATA: &[u8] = include_bytes!("./img.bmp");
#[rp2040_hal::entry]
fn main() -> ! {
info!("Program start!");
let mut pac = pac::Peripherals::take().unwrap();
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();
let mut timer = rp2040_hal::Timer::new(pac.TIMER, &mut pac.RESETS, &clocks);
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,
);
// SPI ピンの設定
let sck = pins.gpio18.into_function::<hal::gpio::FunctionSpi>();
let sda = pins.gpio19.into_function::<hal::gpio::FunctionSpi>();
let rst = pins.gpio22.into_push_pull_output();
let dc = pins.gpio28.into_push_pull_output();
let cs = pins.gpio17.into_push_pull_output();
// rp2040_hal の SPI 初期化(SPI0、16MHz、MODE0)
let spi: rp2040_hal::Spi<_, _, _, 8> = hal::Spi::new(pac.SPI0, (sda, sck)).init(
&mut pac.RESETS,
clocks.peripheral_clock.freq(),
16u32.MHz(),
embedded_hal::spi::MODE_0,
);
// st7735-lcd の要求する SpiDevice を満たす
let spi_device = ExclusiveDevice::new_no_delay(spi, cs).unwrap();
let mut display = st7735_lcd::ST7735::new(
spi_device,
dc,
rst,
true, // rgb モード
false, // inverted モード
128, // 幅
160, // 高さ
);
// ハードリセットを行い、ディスプレイ初期化
display.hard_reset(&mut timer).unwrap();
display.init(&mut timer).unwrap();
// 以下、表示内容の描画例(embedded-graphics を利用)
let text_style_a = MonoTextStyleBuilder::new()
.font(&FONT_6X10)
.text_color(Rgb565::WHITE)
.build();
let text_style_b = MonoTextStyleBuilder::new()
.font(&FONT_7X14)
.text_color(Rgb565::GREEN)
.build();
let text_a = Text::with_baseline(
"Hello World.",
Point::new(0, 0),
text_style_a,
Baseline::Top
);
let text_b = Text::with_baseline(
"zin3.cc",
Point::new(0, 10),
text_style_b,
Baseline::Top
);
let bmp = Bmp::from_slice(BITMAP_DATA).unwrap();
let image = Image::new(
&bmp,
Point::new(0, 30)
);
let rectangle = Rectangle::new(
Point::new(16, 24), Size::new(32, 16)
).into_styled(
PrimitiveStyleBuilder::new()
.stroke_width(2)
.stroke_color(Rgb565::RED)
.fill_color(Rgb565::CYAN)
.build(),
);
let ark = Arc::new(
Point::new(60, 60),
40,
-30.0.deg(),
150.0.deg()
)
.into_styled(PrimitiveStyle::with_stroke(Rgb565::GREEN, 4));
loop {
timer.delay_ms(2000);
display.clear(Rgb565::BLACK).unwrap();
display.set_offset(0, 25);
text_a.draw(&mut display).unwrap();
text_b.draw(&mut display).unwrap();
info!("draw text!");
image.draw(&mut display).unwrap();
info!("draw image");
rectangle.draw(&mut display).unwrap();
info!("draw rectangle");
for i in 1..120 {
Line::new(
Point::new(30, 40),
Point::new(70, i)
)
.into_styled(PrimitiveStyle::with_stroke(Rgb565::RED, 2))
.draw(&mut display).unwrap();
}
info!("draw line");
ark.draw(&mut display).unwrap();
info!("draw arc");
}
}st7735-lcdが0.10.0(現時点の最新)になりembedded-hal-busが追加になりました
このembedded-hal-busによりExclusiveDeviceでCSピンの選択ができるようになりました。
このサンプルがrp2040でのサンプルがなくて苦戦しました…
(ChatGPTやCursorではデバイスのトレイトを作れーと一点張りされましたがすでに用意されているようです)
ところでExclusiveDevice::new_no_delayとしていましたが、コメントによるとSpiDeviceに準拠していないようなので正しく使うならnewでdelayを渡してあげた方が良さそうです。
参考













コメント