aimdevel’s blog

勉強したことを書きます

Zephyr OSでRaspberry Pi Picoを使用して74HC595と7セグメントディスプレイを接続する方法

概要

前回から引き続き、ラズパイ入門キット「SunFounder Da Vinci Kit for Raspberry Pi」に入っている機器をRaspberr Pi Pico + Zephyr OSで動かしてみた。
今回は、Segment Displayを試した。

環境

74HC595と7セグメントディスプレイを使用する。 Raspberr Pi Picoとを以下のように接続する。接続方法はSunFounderのチュートリアルを参考にした。

74HC595を使用する方法については以下のサイトを参考にした。

Arduinoからシフトレジスタ 74HC595へ SPIでデータを送る | meyon's STUDY

使用方法

前回と同様に作成したソースコードを以下に格納したので、これを使用する。

GitHub - aimdevel/my-zephyr-applications

Zephyrの開発環境構築については公式ドキュメントを参照。

Getting Started Guide — Zephyr Project Documentation

環境をセットアップ

前の記事と同様。

 west init -m https://github.com/aimdevel/my-zephyr-applications.git --mr main my-workspace
cd my-workspace/
west update

この手順はこのリポジトリを利用するときに1回だけ実施すればよい。

ビルド

west build -b rpi_pico apps/apps/segment_display/

別のアプリのビルド結果が残っている場合、buildディレクトリを削除してから上記のコマンドを実行すること。

基板に焼いて動作確認

時間経過でディスプレイの表示が変化することを確認する。

ソースコード説明

作成したdevicetree

作成したdevicetreeは以下。

https://github.com/aimdevel/my-zephyr-applications/blob/main/apps/segment_display/boards/rpi_pico.overlay

全体は以下のようになっている。

&spi0 {
    status="okay";
    cs-gpios = <&gpio0 17 GPIO_ACTIVE_LOW>;
    gpio_expander_0: gpio_expander0@0 {
        reg = <0>;
        compatible = "ti,sn74hc595";
        reset-gpios = <&gpio0 20 GPIO_ACTIVE_LOW>;
        ngpios = <0x8>;
        spi-max-frequency = <2000000>;
        #gpio-cells = <0x2>;
        gpio-controller;
    };
};

ドキュメントがあるのでそれに従って記述した。

ti,sn74hc595 (on spi bus) — Zephyr Project Documentation

cs-gpios

spiのチップセレクトの指定。

cs-gpios = <&gpio0 17 GPIO_ACTIVE_LOW>;

ここで設定したGPIOが通信の際にACTIVE(=LOW)になる指定。

reset-gpios

リセットに使用するピンの指定。

ngpios

74HC595で制御できるGPIOの数。8で固定。

spi-max-frequency

周波数。適当な値でOKなはず。

このdevicetreeを記述していて気が付いたのだが、Zephyrではdevicetreeの定義を「dts/bindings/~~」にyamlファイルとして格納しておくと、その定義に合わない設定がされたときはコンパイルエラーになるようだ。
例えば、必須の設定がされていない場合や、statusに指定する値がokayやdisabled以外になっている場合などは、コンパイルに失敗する。
Linux のdevicetreeのコンパイル時には、最低限の文法に従っていればエラーが出ず、typoに気づきづらかった記憶があるので、これはありがたい。

devicetreeの関連個所

自分で作成したわけではないが、devicetreeで関係のある個所を説明する。
関係するのは以下のファイルの、spi0_default。

https://github.com/zephyrproject-rtos/zephyr/blob/main/boards/arm/rpi_pico/rpi_pico-pinctrl.dtsi

spi_defaultは以下のように設定されている。

 spi0_default: spi0_default {
        group1 {
            pinmux = <SPI0_CSN_P17>, <SPI0_SCK_P18>, <SPI0_TX_P19>;
        };
        group2 {
            pinmux = <SPI0_RX_P16>;
            input-enable;
        };
    };

GPIOの17~19をそれぞれSPI0で使用する設定になっている。

Raspberry Pi PicoのPinoutを見ると、確かにGPIO17~19はSPI0で使用できるようになっている。 また、0~3、4~7でもSPI0を使用できそうだ。

ソースコード

使用したコードは以下。

https://github.com/aimdevel/my-zephyr-applications/blob/main/apps/segment_display/src/main.c

#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>
#include <zephyr/device.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/spi.h>

#define MAX_GPIOS 1<<7 - 1

static const struct device *const gpio_ex =
                DEVICE_DT_GET(DT_NODELABEL(gpio_expander_0));

int main(void)
{
    printk("start!\n");
    if (!device_is_ready(gpio_ex)) {
        return 0;
    }
    printk("device is ready!\n");

    for (int i=0;i<8;i++) {
        printk("gpio configure loop\n");
        int ret = gpio_pin_configure(gpio_ex, i, GPIO_OUTPUT | GPIO_OUTPUT_INIT_LOW);
    }

    k_msleep(10000);

    int gpio_n = 0;
    while (1) {
        printk("loop gpio_n=%d!\n",gpio_n);
        gpio_port_set_masked_raw(gpio_ex, 0x7f, gpio_n);
        //gpio_port_set_bits_raw(gpio_ex, gpio_n);
        k_msleep(1000);
        gpio_n++;
        if (gpio_n > MAX_GPIOS) {
            gpio_port_clear_bits_raw(gpio_ex, 0x7f);
            gpio_n = 0;
        }
    }
    return 0;
}

デバイスドライバの仕様に従ってgpio APIを使用した。

  • 初めにdevicetreeから情報を取得
static const struct device *const gpio_ex =
                DEVICE_DT_GET(DT_NODELABEL(gpio_expander_0));
  • ピンの設定
    ドライバの実装的にこの設定は意味がないかも。
for (int i=0;i<8;i++) {
        printk("gpio configure loop\n");
        int ret = gpio_pin_configure(gpio_ex, i, GPIO_OUTPUT | GPIO_OUTPUT_INIT_LOW);
    }
  • 出力の制御 74HC595の出力を制御する。gpio_port_set_masked_rawを使用する。
gpio_port_set_masked_raw(gpio_ex, 0x7f, gpio_n);

使用方法は、

  • 第一引数に、deviceの構造体を渡す。
  • 第二引数に、使用できるピンを表すmaskを渡す。 8bit分の各ビットがそれぞれのピンに対応する。今回は7セグメントディスプレイのため、01111111を指定している。
  • 第三引数に、highにするピンの情報を渡す。 第二引数と同様に8bit分のデータを渡す。ここを変化させることでディスプレイの表示を変更できる。

device driver

使用したデバイスドライバは以下。
gpio APIを使用できるデバイスとして74HC595を制御できるようになっている。

zephyr/drivers/gpio/gpio_sn74hc595.c at main · zephyrproject-rtos/zephyr · GitHub

まとめ

Raspberry Pi Pico + Zephyr OSで、74HC595を使ってセグメントディスプレイを制御した。
74HC595にはドライバが用意されており、devicetreeを書けば簡単に使用できることが分かった。
devicetreeを触っていると、Linuxで使用しているdevicetreeとの差異が見えてきて面白い。Zephyrの方にはコンパイル時のdevicetreeチェックなどがあり、これは便利だと感じたのでLinuxの方にも実装してほしい。

記事の本題とは関係ないが、Hatena Blogの機能で、AIアシスタントが記事のタイトルを考えてくれるようになったので、使ってみた。
それっぽいタイトルをつけてくれるので、これは活用していきたい。

参考

GitHub - zephyrproject-rtos/zephyr: Primary Git Repository for the Zephyr Project. Zephyr is a new generation, scalable, optimized, secure RTOS for multiple hardware architectures.

Arduinoからシフトレジスタ 74HC595へ SPIでデータを送る | meyon's STUDY

1.1.4 7セグメントディスプレイ — SunFounder davinci-kit-for-raspberry-pi ドキュメント

Zephyr Project Documentation — Zephyr Project Documentation