皆さんこんにちは、グレキチです。
10月になって、すっかり過ごしやすい季節になりました。今年は例年に比べて、台風の上陸回数が少ない気がしますが、日本列島にかなり強い高気圧が長々と鎮座でもしているのでしょうか?それとも・・・
さて今回も、ベアメタルなやつで試してみたことを語る回です。今回私、7セグメントLEDなるものをラズパイPicoと組み合わせて使ってみたので、それについて紹介します。
7セグメントLEDの概要
7セグメントLEDとは、LEDを点灯/消灯することで、数字や英文字を表現できる装置のことです。(下図参照) 画像のオモテ面の白抜きの所に各々LEDが設けられていて、右下のドットも含めると合計で8個のLEDで構成されています。(それって、8セグメントじゃね? 🤔って思いましたよね!)
これを複数個を用いると、時計やタイマーなどが製作できることが、容易に想像できると思います。

7セグメントLEDには、プラス側が共用接続されているアノードコモンと、マイナス側が共用接続されているカソードコモンの2タイプがあります。各端子と内部回路の繋がりは、各々下図のようになっています。 (端子位置と内部回路の構成は製造メーカー毎に違うので、実際の使用にあたっては、データシートでの確認が必須です)

なぜ、片方の端子をそれぞれ共用にしているのかですが、調べた結果、単純に端子数を減らせるからということのようです。また、アノードコモン型とカソードコモン型の2タイプの使い分けは、各々の型の特性を把握した上で、製作したいデバイスの構造などの用途によって決定する必要がありそうです。
それで、今回使ったのはプラス側共用のアノードコモン型で、台湾メーカーのものです。(いつもお世話になっている、秋月電子通商さんから購入しました☺️)
回路を組む際には、ピン配置と対応するLEDの場所を間違えない様にしないといけませんね。
回路の実装
回路は、下図の様に計画しました。 (ブレッドボードは必須ですね)

Pico基盤とLEDデバイスとの位置関係も考慮した結果でこうしましたが、ここでちょっとしたテクニックというか、7セグメントLED制御のコード作成を容易にする工夫をしてます。何かと言うと、使用するGPIOピンは、続き数字(GPIO12〜19)で選定したというところです。後々これがどう効いてくるかは、この後のお楽しみです😀
次に、実際に組んだ回路は、下図の通りです。
(※金属抵抗線の接触防止対応のため、実回路と計画回路図と一部金属抵抗の設置が違いますが、繋げているGPIOピンとLEDデバイスのピンの位置関係は同じです)

各カソード側にそれぞれ金属抵抗(220Ω)を設けているので、少し配線が混み合っていますが、さして難しい構成ではないですね。但し、ジャンパケーブルの差し間違えには注意です。
なお、実回路には、計画回路には無いボタンが設置されていますが、これは、ラズパイPicoへのデータ書き込みを可能にするBOOTSELモードにするため、BOOTSELボタンを押しながらUSBケーブルを差し込む、という超煩わしい作業を簡素化するための措置で、7セグメントLED制御回路には直接は関係のない所です。(端子番号30の“RUN”とGNDをボタンで繋げることで、ボタンを押すと基盤がリセット出来るようにしている)
プログラム詳細
さて、プログラム内容の説明項に来ました。
いよいよ此処からベアメタルな内容になってくる訳ですが、LEDを点灯させる制御としては、基本的には以前実施したLチカのプログラムが流用できるので、それをベースに今回のプログラムは作成しています。そのため、7セグメントLED制御にあたっての特異内容について、此処では詳細に説明したいと思います。
ラズパイPicoのプルアップ回路設定
今回選定した7セグメントLEDのデバイスはアノードコモン型ですが、このタイプは、GPIO出力がHighの時に消灯し(電流が流れない)、Lowで点灯する(電流が流れる)回路になっています。
一方、ラズパイPicoはというと、デフォルトでは出力Lowで消灯する設定になっています。
ということはつまり、ラズパイPicoのGPIO出力設定を変更(Low消灯 → Low点灯)しないといけないということです。今回の様に、出力Low時に点灯させる(言い換えると、出力Highで消灯させる)ような回路のことを、専門用語ではプルアップ回路と呼び、回路オープンの状態で、出力ピンをプラス側と抵抗を介して接続する構造を取ります。(下図赤枠部参照)

図中の“Pull-Up / Pull-Down”に繋がっている右側の部分が該当部です。上側に設定するとプルアップで、下側に設定するとプルダウンです。(デフォルト設定はプルダウン)
一般的には、マイコンには外付けで抵抗を設けてプルアップ回路を構成するみたいですが、幸いにもラズパイPicoには上図に示された通り、あらかじめこの回路をハードウェアに組み込まれているので、ソフトウェア上での設定で回路を切り替えれるようになっていました😀
それで、実際の設定方法ですが、上図右上にも記載があるように、”PAD”というレジスタの設定を変更して行うようになってました。具体的には、PADS_BANK0:GPIOレジスタです。レジスタの2bit 目と3bit目を操作することで変更できます。(この辺りの内容は、ラズパイPicoのデータシートを読むとわかると思います)
プログラムの一部を下記に抜粋します。最初に各レジスタを定義して、後で各対象のビット設定を変更します。
#define RESETS_BASE 0x4000C000
#define RESETS_RESET_CLR (RESETS_BASE+0x0+0x3000)
#define RESETS_RESET_DONE_RW (RESETS_BASE+0x8+0x0000)
#define PADS_BANK0_BASE 0x4001C000
#define PADS_BANK0_GPIO12_SET (PADS_BANK0_BASE+0x34+0x2000)
#define PADS_BANK0_GPIO12_CLR (PADS_BANK0_BASE+0x34+0x3000)
#define PADS_BANK0_GPIO13_SET (PADS_BANK0_BASE+0x38+0x2000)
#define PADS_BANK0_GPIO13_CLR (PADS_BANK0_BASE+0x38+0x3000)
#define PADS_BANK0_GPIO14_SET (PADS_BANK0_BASE+0x3C+0x2000)
#define PADS_BANK0_GPIO14_CLR (PADS_BANK0_BASE+0x3C+0x3000)
#define PADS_BANK0_GPIO15_SET (PADS_BANK0_BASE+0x40+0x2000)
#define PADS_BANK0_GPIO15_CLR (PADS_BANK0_BASE+0x40+0x3000)
#define PADS_BANK0_GPIO16_SET (PADS_BANK0_BASE+0x44+0x2000)
#define PADS_BANK0_GPIO16_CLR (PADS_BANK0_BASE+0x44+0x3000)
#define PADS_BANK0_GPIO17_SET (PADS_BANK0_BASE+0x48+0x2000)
#define PADS_BANK0_GPIO17_CLR (PADS_BANK0_BASE+0x48+0x3000)
#define PADS_BANK0_GPIO18_SET (PADS_BANK0_BASE+0x4C+0x2000)
#define PADS_BANK0_GPIO18_CLR (PADS_BANK0_BASE+0x4C+0x3000)
#define PADS_BANK0_GPIO19_SET (PADS_BANK0_BASE+0x50+0x2000)
#define PADS_BANK0_GPIO19_CLR (PADS_BANK0_BASE+0x50+0x3000)
〜 中略 〜
// PADS_BANK0のリセット状態を解除する(=使える状態にする)
PUT32(RESETS_RESET_CLR, 1 << 8); // ビット8がPADS_BANK0に相当
// リセットが完了するまでの待機処理
while (1)
{
// リセットが完了したレジスタのチェック:ビット8が1かどうかをチェック(=リセット完了なら1になる)
if ((GET32(RESETS_RESET_DONE_RW) & (1 << 8)) != 0)
break; // PADS_BANK0のリセットが完了していればループを抜ける
}
〜 中略 〜
// 各GPIOピンのデフォルトLOW(プルダウン)を無効にする
PUT32(PADS_BANK0_GPIO12_CLR, 1 << 2); // PDE disable
PUT32(PADS_BANK0_GPIO13_CLR, 1 << 2); // PDE disable
PUT32(PADS_BANK0_GPIO14_CLR, 1 << 2); // PDE disable
PUT32(PADS_BANK0_GPIO15_CLR, 1 << 2); // PDE disable
PUT32(PADS_BANK0_GPIO16_CLR, 1 << 2); // PDE disable
PUT32(PADS_BANK0_GPIO17_CLR, 1 << 2); // PDE disable
PUT32(PADS_BANK0_GPIO18_CLR, 1 << 2); // PDE disable
PUT32(PADS_BANK0_GPIO19_CLR, 1 << 2); // PDE disable
// 各GPIOピンをデフォルトHIGH(プルアップ)に変更する
PUT32(PADS_BANK0_GPIO12_SET, 1 << 3); // PUE enable
PUT32(PADS_BANK0_GPIO13_SET, 1 << 3); // PUE enable
PUT32(PADS_BANK0_GPIO14_SET, 1 << 3); // PUE enable
PUT32(PADS_BANK0_GPIO15_SET, 1 << 3); // PUE enable
PUT32(PADS_BANK0_GPIO16_SET, 1 << 3); // PUE enable
PUT32(PADS_BANK0_GPIO17_SET, 1 << 3); // PUE enable
PUT32(PADS_BANK0_GPIO18_SET, 1 << 3); // PUE enable
PUT32(PADS_BANK0_GPIO19_SET, 1 << 3); // PUE enable
使っているピンが8つもあるので、記載内容がちょっと冗長に見えますが、必要な設定なのでしっかり記述しました。(※プルダウン無効設定とプルアップ有効設定は同時に1行で行えそうでしたが、念のため、プルダウン無効を先に行うように分けて記述してます)
なお、PUT32/GET32関数については、以前の記事で紹介済みですので、此処では割愛します。
7セグメントLEDの点灯設定
それでは次に、各LEDの点灯設定についてです。ここでようやく『回路の実装』のところで言っていた、ちょっとしたテクニックの伏線を回収していきます😀
7セグメントLEDで数字なり英文字なりを表現する場合、表示させたいターゲットに合わせて、7つあるLEDの内どのLEDを同時に点灯させれば良いか、ということに尽きます。それは、対象となるLEDをHighからLow(つまり、1 → 0)へ設定変更するということです。その設定変更を実際に指示するレジスタはSIO:GPIO_OUTレジスタで、このレジスタで指示する対象のGPIOは、0から29まで1bit置きに、右から左へ順番に並んでいるので、使いたいGPIOも順番に並んでいた方が、bit操作を簡単にできることになります。且つ、今回使用するGPIOの数はGPIO12から19の8つなので、8bits = 16進数で2桁で表現できることになります‼️(まあー何て楽なんだろう😌)
これが、続き番号を選んだ理由なのです。
あとは、対象となる数字なり英文字を表現するのに必要なLEDの配列を、下記のように、エクセルなどの表にまとめて置いておけば、すぐに表現したいもののLED指示が出来ます。

表中の“レ点”がついている所を“0”もしくは“1”で置き換えて、16進数に変換したものをあらかじめ用意しておけば良いということです。例えば、数字の“0”を表現したかったら、レ点を“1”で考えた場合は、表上GPIO12から順番に、右から左にビット指示を記述すると、
0 1 1 1 0 1 1 1 = 0111 0111 = 0x77(※レ点を “0”で考えた場合は、0x88)
となりますね。
と、こんな具合に決めて、事前に設定値を用意しておけば良いということです。
そして、予め定義しておいたSIO_GPIO_OUT_SETレジスタを使って、対象数字の16進数をセットして左に12シフトすると、GPIO12から19までのbitが一度にまとめて設定されるという仕組みです。
実際のコード内容は、下記のようになります。
#define SIO_BASE 0xD0000000
#define SIO_GPIO_OUT_SET (SIO_BASE+0x14)
#define SIO_GPIO_OUT_CLR (SIO_BASE+0x18)
#define SIO_GPIO_OUT_XOR (SIO_BASE+0x1C)
#define SIO_GPIO_OE_SET (SIO_BASE+0x24)
#define SIO_GPIO_OE_CLR (SIO_BASE+0x28)
〜 中略 〜
// GPIO 12 ~ 19 の出力を無効化する(初期化のための安全動作)
PUT32(SIO_GPIO_OE_CLR, 0xFF << 12);
// GPIO 12 ~ 19 の値をクリアする
PUT32(SIO_GPIO_OUT_CLR, 0xFF << 12);
〜 中略 〜
// GPIO 12 ~ 19 の出力をHigh(=1)に設定する
PUT32(SIO_GPIO_OUT_SET, 0xFF << 12);
// GPIO 12 ~ 19 の出力を有効化する
PUT32(SIO_GPIO_OE_SET, 0XFF << 12);
〜 中略 〜
int x = 0x77;
// 多数表示の場合は、以下は関数化
// turn on leds
PUT32(SIO_GPIO_OUT_XOR, x << 12);
DELAY(0x200000);
// turn off leds
PUT32(SIO_GPIO_OUT_SET, 0xFF << 12);
DELAY(0x200000);
GPIO12〜19ピンの出力設定を一旦全てHigh( =1)にセットした後、点灯したい箇所のみ出力Low( =0 )に再セットするようにしてます。(※SIO_GPIO_OUT_XORを使っているのは、xの値が、LED選択表のレ点を“1”基準で考えた16進数になっているためです)
という事で、此処で言いたかった事は以上です。
なお、全体プログラム内容については、こちらのGithubにアップしていますので、気になった方はアクセスしてみて下さい😊
実行結果
プログラムをビルドして実行してみると、こんな感じで動作しました。
数字1個だと面白くなかったので、今回は3つの数字を順番に表示するようにしてみました。
その欲が出たせいで、データ使用量が増え、完成するまでに余計に時間が掛かったんですけどね😅
ともあれ、今回も無事に動作させることが出来てホッとしてます。
まとめ
今回は7セグメントLEDを取り扱ってみましたが、段々と扱えるデバイスが増えるのはなんとも嬉しい限りです☺️
ラズパイPicoでは、プルアップ/プルダウンの回路切り替えが容易に行えることと、7セグメントLEDの仕組みが理解出来たのことが良い収穫となりました。
また、以前実施したLチカとの大きな違いは、プログラムのデータ容量が増えたことで実行ファイルのビルドが上手く行えなかったことです。Lチカで使っていたuf2ファイル作成用のプログラムは、データ容量が252bytesまでに収まったものしか対応していなかったので、それ以上のデータ容量になるものをビルドする場合は、uf2ファイル作成用のプログラムも変更しないといけなかったです。この辺りが、ベアメタルならでは難しいところなんだと思いました。
結局、色々と情報を調べてトライアンドエラーを繰り返し、ようやくビルド出来るようなプログラムを作成出来たのでした。少々難儀しましたが、組み込み開発を続けるにあたって必須となるスキルの醸成に繋がったと思います。
実はこれが、今回の一番の収穫だったかも知れません。
ということで、今回は以上になります。さて、次は何をいじってみようか🤔
それでは、また🖐️
〆