ラズピコでRust(11) キャラクタ型LCDとつないでみる

  • 2024.06.30
  • LCD
ラズピコでRust(11) キャラクタ型LCDとつないでみる

皆さん こんにちは。
ポンコツRustacean の moon です。

今回はキャラクタ型 20×4行のLCDと4ビットバスでつないでみました。

この記事は開発環境を構築することを前提にしています。
環境構築について知りたい方は こちらの記事 をご覧になってください。

このサイトは 書籍 基礎から学ぶ組込みRust を参考にしています。

キャラクターディスプレイ モジュール(LCD)

私は秋月電子の こちら を使ってみました。

在庫は少ないようですね。。補充するつもりはないのでしょうか。

3.3V品と5V品があるので要注意です。

ラズピコのマイコンは3.3V系で動作するため、3.3Vで動作するLCDを選択することをお勧めします。

バックライト用LED

付属品の抵抗 10Ω が3つです。
どう考えてもおかしいですね。。

Vfが3.4V Typ. と書かれているので、3.3V では電圧が不足します。

私はVBUSピンから5Vを供給して、電流制限抵抗は43Ωに決めました。

そうするとマニュアルにある Typ.値である 40mA 程度の電流が流れます。

バックライトの明るさ調整用ですから、わざわざ定電流回路を組む必要はないと思います。

接続

Raspberry Pi Pico と LCD間の接続図を書きました。
スライドスイッチをつけてバックライトをOFFできるようにしました。
LCDのVO(3番)にはマニュアルの10ページにあるように可変抵抗(20kΩ)から電圧を取り込むようにすることをお薦めします。
最初に固定抵抗(10kΩ x 2)で分割してみたのですが、表示が見えませんでした。

R/W端子はGNDに接続し、”L”固定で書き込み専用にする方法が一般的のようです。
Busy を読む部分は”待ち時間”を入れて調整します。

詳しくはモジュールのマニュアルをご覧になってください。

私は他のペリフェラルを使っている都合上、上記の接続にしました。

GPIO 6ビットは皆さんの回路の都合に合わせて接続することができます。

HD44780について

キャラクタ型LCDの元祖コントローラです。
だいたいのコントローラがこれの互換品になっているようです。

キャラクタ型のLCDを使ってみようと思い、私はまずラズピコの BSP と HAL のクレート内を探してみました。

ラズピコのBSP(例えばバージョン0.9.0)は以下のフォルダにあります。
(m3925は皆さんのユーザー名に置き換えてください)

C:\Users\m3925\.cargo\registry\src\index.crates.io-6f17d22bba15001f\rp-pico-0.9.0\examples

HAL(例えばバージョン0.9.1)は以下のフォルダにあります。

C:\Users\m3925\.cargo\registry\src\index.crates.io-6f17d22bba15001f\rp2040-hal-0.9.1\examples

私の場合、何かやろうと思ったらまずこの2つのフォルダ内を物色します。

今回はBSPの方に、pico_hd44780_display.rs というファイルがありました。

5行目に以下の記述があったので

//! [HD44780](https://crates.io/crates/hd44780-driver) driver.

crates.io で hd44780-driver を眺めてみました。

後は、後述する外部クレートの取り込みを行って使えるようにします。

パッケージの複製を作成する

まず私のGitHubリポジトリにあるRustのひな形をベースに別名でパッケージをつくります。
ひな形はデバッグ環境用のものですから、その複製もデバッグできるようになります。

新しいパッケージ名を rp2040-hd44780 として git clone します。
xxxxは皆さんのユーザー名です。
カレントディレクトリを rp2040-hd44780 に移してから、code . で VSCode を起動します。

今回は元プロジェクトを rp2040-spi-bme280 にしました。
構成が同じなのでライブラリクレートを作成したりする手間が省けます。

C:\Users\xxxx>cd pprp
C:\Users\xxxx\pprp>git clone https://github.com/moons3925/rp2040-spi-bme280.git rp2040-hd44780
C:\Users\xxxx\pprp>cd rp2040-hd44780
C:\Users\xxxx\pprp\rp2040-hd44780>code .

上位のCargo.tomlを編集する

rp2040-hd44780ディレクトリにある Cargo.toml のnameを rp2040-hd44780に変更します。

次に、[dependencies] に次の1行を加えて、hd44780-driverのライブラリクレートを取り込みます。

hd44780-driver = “0.4.0”

launch.jsonを編集する

“executable” の名前を次のように変更します。

“executable”: “./target/thumbv6m-none-eabi/debug/rp2040-hd44780”,

main.rsを編集する

#![no_std]
#![no_main]

use fugit::RateExtU32;
use hal::pac;
use hal::uart::{DataBits, StopBits, UartConfig};
use rp2040_hal::spi::Enabled;
use rp2040_hal::Clock;
use rp_pico::entry;

use panic_halt as _;
use rp2040_hal as hal;

use rp2040_lib::my_macro::UART_TRANSMITTER;
use rp2040_lib::print;
use rp2040_lib::println;

use rp2040_lib::bme280::spi::BME280;

use rp2040_hal::gpio::bank0::Gpio4;
use rp2040_hal::gpio::bank0::Gpio5;
use rp2040_hal::gpio::bank0::Gpio6;
use rp2040_hal::gpio::bank0::Gpio7;
use rp2040_hal::gpio::FunctionSio;
use rp2040_hal::gpio::Pin;
use rp2040_hal::gpio::PullDown;
use rp2040_hal::gpio::{FunctionSpi, SioOutput};

use crate::pac::SPI0;
use rp2040_hal::Spi;

use hd44780_driver::bus::FourBitBus;
use hd44780_driver::{Cursor, CursorBlink, Display, DisplayMode, HD44780};

use rp2040_hal::gpio::bank0::Gpio20;
use rp2040_hal::gpio::bank0::Gpio21;
use rp2040_hal::gpio::bank0::Gpio22;
use rp2040_hal::gpio::bank0::Gpio26;
use rp2040_hal::gpio::bank0::Gpio27;
use rp2040_hal::gpio::bank0::Gpio28;

type Lcd = HD44780<
    FourBitBus<
        Pin<Gpio28, FunctionSio<SioOutput>, PullDown>,
        Pin<Gpio27, FunctionSio<SioOutput>, PullDown>,
        Pin<Gpio26, FunctionSio<SioOutput>, PullDown>,
        Pin<Gpio22, FunctionSio<SioOutput>, PullDown>,
        Pin<Gpio21, FunctionSio<SioOutput>, PullDown>,
        Pin<Gpio20, FunctionSio<SioOutput>, PullDown>,
    >,
>;

use embedded_hal::blocking::delay::DelayMs;
use embedded_hal::blocking::delay::DelayUs;

fn lcd_display<D: DelayUs<u16> + DelayMs<u8>>(
    lcd: &mut Lcd,
    delay: &mut D,
    tup: (&f64, &f64, &f64),
) {
    let _ = lcd.set_cursor_pos(0x00, delay);
    let _ = lcd.write_str("Temp : ", delay);

    let tens_digit: u8 = ((*tup.0 as i32 / 10) % 10) as u8 | b'0';
    let ones_digit: u8 = ((*tup.0 as i32) % 10) as u8 | b'0';
    let tenths_digit: u8 = (((*tup.0 * 10.0) as i32) % 10) as u8 | b'0';

    let _ = lcd.write_char(b' ' as char, delay);
    let _ = lcd.write_char(b' ' as char, delay);
    let _ = lcd.write_char(tens_digit as char, delay);
    let _ = lcd.write_char(ones_digit as char, delay);
    let _ = lcd.write_char(b'.' as char, delay);
    let _ = lcd.write_char(tenths_digit as char, delay);
    let _ = lcd.write_char(b' ' as char, delay);
    let _ = lcd.write_char(0xdf as char, delay);
    let _ = lcd.write_char(b'C' as char, delay);

    let _ = lcd.set_cursor_pos(0x40, delay);
    let _ = lcd.write_str("Humi : ", delay);

    let tens_digit: u8 = ((*tup.1 as i32 / 10) % 10) as u8 | b'0';
    let ones_digit: u8 = ((*tup.1 as i32) % 10) as u8 | b'0';
    let tenths_digit: u8 = (((*tup.1 * 10.0) as i32) % 10) as u8 | b'0';

    let _ = lcd.write_char(b' ' as char, delay);
    let _ = lcd.write_char(b' ' as char, delay);
    let _ = lcd.write_char(tens_digit as char, delay);
    let _ = lcd.write_char(ones_digit as char, delay);
    let _ = lcd.write_char(b'.' as char, delay);
    let _ = lcd.write_char(tenths_digit as char, delay);
    let _ = lcd.write_char(b' ' as char, delay);
    let _ = lcd.write_char(b'%' as char, delay);

    let _ = lcd.set_cursor_pos(0x14, delay);
    let _ = lcd.write_str("Pres : ", delay);

    let thousands_digit: u8 = ((*tup.2 as i32 / 1000) % 10) as u8 | b'0';
    let hundreds_digit: u8 = ((*tup.2 as i32 / 100) % 10) as u8 | b'0';
    let tens_digit: u8 = ((*tup.2 as i32 / 10) % 10) as u8 | b'0';
    let ones_digit: u8 = ((*tup.2 as i32) % 10) as u8 | b'0';
    let tenths_digit: u8 = (((*tup.2 * 10.0) as i32) % 10) as u8 | b'0';

    let _ = lcd.write_char(thousands_digit as char, delay);
    let _ = lcd.write_char(hundreds_digit as char, delay);
    let _ = lcd.write_char(tens_digit as char, delay);
    let _ = lcd.write_char(ones_digit as char, delay);
    let _ = lcd.write_char(b'.' as char, delay);
    let _ = lcd.write_char(tenths_digit as char, delay);
    let _ = lcd.write_char(b' ' as char, delay);
    let _ = lcd.write_char(b'h' as char, delay);
    let _ = lcd.write_char(b'P' as char, delay);
    let _ = lcd.write_char(b'a' as char, delay);
}

#[entry]
fn main() -> ! {
    let mut pac = pac::Peripherals::take().unwrap();
    let core = pac::CorePeripherals::take().unwrap();
    let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
    let clocks = hal::clocks::init_clocks_and_plls(
        rp_pico::XOSC_CRYSTAL_FREQ,
        pac.XOSC,
        pac.CLOCKS,
        pac.PLL_SYS,
        pac.PLL_USB,
        &mut pac.RESETS,
        &mut watchdog,
    )
    .ok()
    .unwrap();

    let mut delay = cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz());

    let sio = hal::Sio::new(pac.SIO);

    let pins = rp_pico::Pins::new(
        // (8-6-7)
        pac.IO_BANK0,
        pac.PADS_BANK0,
        sio.gpio_bank0,
        &mut pac.RESETS,
    );

    let uart_pins = (pins.gpio0.reconfigure(), pins.gpio1.reconfigure());
    let uart = hal::uart::UartPeripheral::new(pac.UART0, uart_pins, &mut pac.RESETS)
        .enable(
            UartConfig::new(9600.Hz(), DataBits::Eight, None, StopBits::One),
            clocks.peripheral_clock.freq(),
        )
        .unwrap();

    let (_, uart_tx) = uart.split();

    critical_section::with(|_| unsafe {
        UART_TRANSMITTER = Some(uart_tx);
    });

    let spi_mosi = pins.gpio7.into_function::<hal::gpio::FunctionSpi>();
    let spi_miso = pins.gpio4.into_function::<hal::gpio::FunctionSpi>();
    let spi_sclk = pins.gpio6.into_function::<hal::gpio::FunctionSpi>();
    let spi = hal::spi::Spi::<_, _, _, 8>::new(pac.SPI0, (spi_mosi, spi_miso, spi_sclk));

    let cs = pins.gpio5.into_push_pull_output();

    let spi = spi.init(
        &mut pac.RESETS,
        clocks.peripheral_clock.freq(),
        10.MHz(),
        embedded_hal::spi::MODE_0,
    );

    let mut bme280 = BME280::<
        Spi<
            Enabled,
            SPI0,
            (
                Pin<Gpio7, FunctionSpi, PullDown>,
                Pin<Gpio4, FunctionSpi, PullDown>,
                Pin<Gpio6, FunctionSpi, PullDown>,
            ),
        >,
        Pin<Gpio5, FunctionSio<SioOutput>, PullDown>,
    >::new(spi, cs);

    // LCD Display

    let rs = pins.gpio28.into_push_pull_output();
    let en = pins.gpio27.into_push_pull_output();
    let d4 = pins.gpio26.into_push_pull_output();
    let d5 = pins.gpio22.into_push_pull_output();
    let d6 = pins.gpio21.into_push_pull_output();
    let d7 = pins.gpio20.into_push_pull_output();

    let mut lcd = HD44780::new_4bit(rs, en, d4, d5, d6, d7, &mut delay).unwrap();

    let _ = lcd.reset(&mut delay);
    let _ = lcd.clear(&mut delay);
    let _ = lcd.set_display_mode(
        DisplayMode {
            display: Display::On,
            cursor_visibility: Cursor::Visible,
            cursor_blink: CursorBlink::On,
        },
        &mut delay,
    );

    // DeviceのIDコード(0x60)を正しく読めれば成功としている
    if bme280.init() {
        println!("BME280 initialization successful.");
        println!("BME280 ID = 0x60.\r\n");
    } else {
        println!("BME280 initialization failed.\r\n");
    }

    loop {
        bme280.read_data();

        let (temp, humi, pres) = bme280.get_elements();

        println!("T = {:.2} ℃", temp);
        println!("H = {:.2} %", humi);
        println!("P = {:.2} hPa\r\n", pres);

        lcd_display(&mut lcd, &mut delay, (&temp, &humi, &pres));
    }
}

ビルドして実行する

Run – Start Debugging (F5) からプログラムを実行します。

都度計測される温度、湿度、気圧の値が表示されれば成功です。

解説

概略説明します。

cargo.toml の dependencies に 「 hd44780-driver = “0.4.0” 」 を記述して外部クレートを取り込みます。

main.rs に以下のuse文を記述します。

use hd44780_driver::bus::FourBitBus;
use hd44780_driver::{Cursor, CursorBlink, Display, DisplayMode, HD44780};

これでLCDを使えるようになりました。
ただ、例題が古く delay の部分が抜けているようです。
その部分を修正しながらコードを書いていきました。

弊害はないはずなので、通信で使うprintln!マクロ等は従来のプロジェクトそのままにしています。

GitHubにアップ

GitHubの rp2040-hd44780 にプロジェクトをアップしましたので参考になさってください。

お疲れさまでした。

LCDカテゴリの最新記事