皆さんこんにちは、グレキチです。
ベアメタルに関する記事が増えてきたので、今回からシリーズ化にすることにしました‼️
ワーイ🙌
今後も色んなデバイスを使ったら、随時記事にして配信して行きたいと考えています。
なんかもう、使ってみたいデバイスが沢山あって、何から手を付けていこうか日々迷いながら進めています😁
さてさて今回の内容は、シフトレジスタを使って7セグメントLEDの制御をやってみたので、それについて紹介します。
また、LEDか!と思ったかも知れませんが、今回はラズパイPicoに接続するデバイスが2個に増えており、レベルが上がっていますよ!!(多分🙄)
今回も取り組んでいく中で、ハマったポイントがいくつかあったので、その辺りを中心に説明していきます。皆さん、最後までお付き合い頂けると幸いです🙇
シフトレジスタとは?

シフトレジスタとは、入力されたデータを、フリップフロップと呼ばれるものを使って、bit単位で左や右にデータをシフトすることができるデジタル回路で、シリアルデータとパラレルデータもしくは各データ同士の双方向変換を行ったり、データの保持などが行えるものです。
用途はAD/DAコンバータ、ディスプレイ、メモリなど非常に多岐に渡っており、今回のような複数LEDを一度に制御するような簡単な電子回路にも使えます。種類は大きくは4つ(SISO、SIPO、PISO、PIPO)に分けられるようですが、それについての説明は此処では省略します。
なお、フリップフロップとは、1ビットのデジタル信号を記憶できる素子のことを言います。(所謂メモリみたいなものですね)
シフトレジスタを使用するメリットとしては、前回の記事で紹介した7セグメントLEDを操作する場合で言うと、マイコンのGPIOポートは8つ必要になりますが、シフトレジスタを使えばGPIOポートは3つで済むので、差引で余ったGPIOポート5つ分は他の用途に使えます。
また、消費電力が他のストレージデバイスに比べて少ないなどもあるようです。さらに、構造的にシフトレジスタ自体を複数繋げて(カスケード接続)使うこともできるので、拡張性がかなりあると思われます。
一方、注意するポイントとしては、動作速度がクロック周波数によって制限されるため、クロック周波数が高いと、データの整合性とシフト精度に問題が起きる可能性があるとのこと。また、組み込みのエラー検出や修正メカニズムなどは無いので、実際の市販製品などへの使用にあたっては注意が必要です。
今回使用したものは、直列入力並列出力(SIPO)タイプの74HC595で、一度に8bitまでのデータを扱えるICチップです。下図がそのシフトレジスタの各端子の説明と論理回路図になります。


(UNISONIC TECHNOLOGIES社データシートより抜粋)
このタイプは、左側のシリアルデータ入力(SER)から入力されたデータを、シフトクロック信号(SRCLK)によって1bit毎にデータを右にシフトして受け渡し、それを繰り返す事で各入力ピンに順次データを溜めて行って、狙ったタイミングでストレージクロック信号(RCLK)を適用して、レジスタに溜まったデータを一気に出力する(並列出力)ことができるものになります。
簡単ですが、シフトレジスタに関しての説明は以上です。詳しく知りたい方は、こちらのサイトで分かりやすく説明されていますので、確認してみて下さい。とても参考になると思います。
電子回路
次に、今回組んだ回路の説明です。電子回路は下図のように設計しました。

7セグメントLEDのカソードに繋いだ金属抵抗が8個もあるので、配線は相変わらずゴチャゴチャしている感じを受けます。
今回、ラズパイPicoのGPIOは10、11、12の3つを使用し、シフトレジスタと各々、GPIO10とSER、GPIO11とRCLK、GPIO12とSRCLKを接続しています。また、シフトレジスタと7セグメントLEDは、お互いにアルファベット記号の同じ端子同士を接続しています。(例 : レジスタのQA端子とLEDのA(7)端子 etc、※QHはDPと接続)
なぜこうしているのかというと、先に説明したシフトレジスタの論理回路図にあるように、ストレージ出力ピンはQAからQHへアルファベット順に並んでいて、順番にbitを受け渡しする(シフト操作完了後は、QAが最上位bitとなる)ので、データセットする際にどのLEDを光らせるかが把握し易くなり、ミスを避けることにも繋がるからです。
それと、特異な配線として、非同期クリア端子(SRCLR)と出力有効端子(OE)の2つがあります。前段のシフレレジスタ端子説明のところで示したように、各々の文字の上にある線は、回路図などで使われている場合は負論理であることを表していて、Low信号で有効となるものに付しているようです。
SRCLRの信号を操作出来れば、好きなタイミングで強制的にレジスタ値をクリア(Lowセット)できるようですが、今回の回路ではその機能は使わないので、そういった場合は回路のVCCと接続する事で、無効化しておきます。
またOEは、ストレージクロックでの信号を有効化したいので、回路のGNDと接続しています。
なお、今回シフトレジスタは1個しか使わないので、QH’端子には何も接続していません。
実装が完了した実際のボード画像を、以下に載せておきます。

プログラム内容
プログラム内容については察しが付くかと思いますが、前回の7セグメントLED制御のものを流用してます。お馴染みのPUT32、GET32を使っております。(この関数、本当に便利☺️)
しかし、これを使ったが為か!?、何度やっても狙ったLEDが上手く点灯出来ない事態にしばらく陥ってしまいました😵
当初原因が全く掴めず、解決のために、何度も回路の配線を確認したり、プログラムを書き換えたり、ググっても何ら解決の糸口が掴める情報が無い、といったことが2日程続いたので、少々げんなりしていました😩
そうしてようやく、プログラム内容に原因があることを突き止めました。それは、入力データをシフトレジスタへ渡す処理の所でした。該当箇所で、最初に試していたコードは、下記のようなものでした。
unsigned int val; // 表示したいLEDを表す16進数
unsigned int idx;
for (idx = 0; idx < 8; idx++)
{
PUT32(SIO_GPIO_OUT_CLR, 1 << 12); // シフトクロックをLowにして、待機
PUT32(SIO_GPIO_OUT_SET, ((val >> idx) & 0x01) << 10); // <-- ココ
PUT32(SIO_GPIO_OUT_SET, 1 << 12); // シフトクロックをHighにして、データを1ビット進める
}
上のコードで問題となったのは、forループの真ん中のPUT32関数のところです。
ArduinoやMicroPythonなどで、各々のライブラリで用意された関数を使ってコードを書く場合なら、値の処理方法は上記Cファイルのように“ (val >> idx) & 0x01 ”と書いても問題無いのですが、ラズパイPicoで今回のようにPUT32関数を使ってレジスタを指定してデータを入力する場合、ちょっと勝手が違っていて、SETレジスタでは0をセットすることが出来ません。SIO_GPIO_OUTレジスタの値を0にしたい場合は、CLRレジスタの該当箇所に1をセットして、レジスタの値を0(Low)に戻す(変更する)という仕組みとなっています。
つまり、「レジスタ値を直接書き換える」のではなく、「1を書いてレジスタ値の操作命令を出す」という処理の仕方が必要でした。
ここが、今回の最大のハマりポイントで、ラズパイPicoのレジスタの扱い方をきちんと把握出来ていなかった事例でした😓
以上をふまえて、コードは下記のように記述し直しました。
unsigned int idx;
unsigned int bv;
for (idx = 0; idx < 8; idx++)
{
PUT32(SIO_GPIO_OUT_CLR, 1 << 10); // SIO_GPIO10の出力レジスタを0にセット
PUT32(SIO_GPIO_OUT_CLR, 1 << 12); // クロックをLowにして、待機
bv = (val >> idx) & 0x01;
if (bv == 1)
{
PUT32(SIO_GPIO_OUT_SET, 1 << 10); // SIO_GPIO10の出力レジスタを1にセット
}
PUT32(SIO_GPIO_OUT_SET, 1 << 12); // クロックをHighにして、データを1ビット進める
}
このコードにしてからは、狙い通りのLEDを点灯させることが出来るようになりました。プログラムを実行して上手く行った時は、思わす立ち上がってバンザイしてしまいした😆
それともう一つ、プログラム作成上で気を付ける点がありました。
それは、受け渡すvalの値(16進数)の表現方法です。“電子回路”の項のところでも少し触れましたが、GPIO10端子を通じて、SERからフリップフロップへデータを渡す際は、反映したい8bit(ターゲット)の下位bitから順番(QHからQAに向けて)にセットしていくことになります。
先ほど説明したCファイルのコードでは、valは “( val >> idx ) & 0x01” の数式を使ってidx = 0から7へ昇順にループしてデータを渡すようになっているので、シフトレジスタがシフトしてデータを受け渡していく流れと同じく、下位bitから順番にセットしていけばよいということです。(※論理演算がちょっとややこしいかも😅)
その際、前回の記事でも説明したように、アノードコモンの7セグメントLEDを使っているので、信号Low(0)でLEDが点灯(信号High(1)でLEDが消灯)することも考慮しないといけません。
以上をふまえて、下記に16進数の決定方法の一例を示します。
<数字の“0”を表現したい場合>
点灯させるLEDは、A、B、C、D、E、Fの6つ (下図参照)
信号Lowで点灯なので、QAからQHに渡すビットを上位から記載すると、
QA QB QC QD QE QF QG QH
0 0 0 0 0 0 1 1 = 0000 0011 -> 0x03 となる

したがって、valに“ 0x03 ”を渡せば、7セグメントLEDには、数字の ” 0 ” を表示できると思います。ご参考までに、他の表現数字に対応する16進数も併せて、以下記載しておきます。
(#0) ... 0x03 (#1) ... 0x9F (#2) ... 0x25 (#3) ... 0x0D (#4) ... 0x99
(#5) ... 0x49 (#6) ... 0x41 (#7) ... 0x1B (#8) ... 0x01 (#9) ... 0x09
と言うわけで、プログラムの説明内容は以上です。
なお、私が最終的に作成したコードは、数字の0から9までを順番に表示するものとしました。実際の動作状況は、後段の“実行結果”の項をご確認頂ければと思います。
いつものように、全体プログラムはGitHubにアップしていますので、気になった方は確認してみて下さい。
実行結果
こちらが、実際にプログラムを実行したものです。
数字の0から9が順番に表示できています。めでたし、めでたし😁
まとめ
皆さん、ここまでお付き合い頂きまして、ありがとうございました。
今回は、7セグメントLEDと併せてシフトレジスタも使ってみましたが、いかがでしたか?レベルアップした私を感じて頂けたでしょうか?😆(えっ、大した事ない!?😱)
取り掛かり始めは簡単そうに思っていたんですが、意外と扱いに苦戦したデバイスたちでした。(😎:いやいや、苦戦したのはデバイスじゃなくって、プログラムなんじゃ?😅)
いざプログラムを実行してみると、ただ数字を順番に表示させるだけの単純なものに過ぎないのですが、偉大な技術者の先人達も同じように通った道だと思うので、こうやって少しずつ扱えるデバイスを増やして行って、今後もコツコツとスキルアップに取り組んでいきます。
今回1桁の7セグメントLEDを制御しましたが、他にも4桁バージョンとかも出回っているので、そのうちトライしたいと思います。
と、この記事を書きながら、もうすでに次のデバイスいじりを進めています。
次は何に関する記事かは、乞うご期待下さい。(LEDでは無いですよ〜)
それではまた🖐️
〆