リストの追加・結合

概要

リストに対する要素の追加、リストの結合について整理。

  • リストへの1つの要素の追加
    • appendが簡明、ただし破壊的
    • extendも使えるがリストとして追加、破壊的
    • +演算子もリストとして追加、非破壊的
  • リスト同士の結合
    • appendは使えない
    • extendは破壊的にリストを追加
    • +演算子は非破壊的にリストを追加

リストへの要素の追加

appendは破壊的

appendで1つの要素をリストに追加できる。appendは破壊的で元のリストを改変する。戻り値はNone

appendは1つの引数しか持てない(append(4, 5)とはできない)。

extendはリストを追加(破壊的)

extendでも要素を追加できるが、要素そのものではなく、追加したい要素を含むリストとして追加する。extendは破壊的で元のリストを改変する。戻り値はNone

+演算子もリストの演算(非破壊的)

+演算子もextendと同じくリストを追加する。extendと違って非破壊的で、元のリストは改変されず、結果は戻り値で返される。

リストの結合

appendは使えない

appendは引数をリストの要素として追加するので、リストを引数に与えるとそのリストが要素として追加されてしまう。

extendはOk(破壊的)

extendは引数に与えたリストで元のリストを拡張する。ただし破壊的で元のリストが改変される。

+演算子もOk(非破壊的)

+演算子もextendと同じくリストを拡張するが非破壊的。拡張と言うよりも、くっつける/追加するというイメージ。

 

Raspberry Pi – シフトレジスタ―~4桁7セグメントLED

概要

  • Raspberry Piでシフトレジスターを介して4桁7セグメントLEDをコントロール
  • 各桁を短時間で明滅させて、視覚的に全桁が同時発光しているように見せる
  • おまけでカウントダウンタイマーにしてみた

4桁7セグメントLEDのピン配置と回路

キットに同梱されていたLEDのピン配置は下図の通り。

D1~D4のピンは左から順に各桁の7セグメントLEDに対応している。これらのピンがHIGHのLEDにa~dpのピンへの入力が反映されて表示される。LOWの時にはLEDは消灯する。

a~dpは7セグメントLEDのピン配置に対応している。

内部の回路は以下の通りで、カソードコモンとなっている。たとえば左から2桁目のLEDを点灯させたいときは、D2をHIGHにしてa~dpのうち点灯させたいセグメントのピンをLOWに、消灯させたいセグメントのピンをHIGHにする。

4桁の同時発光

セグメントの明滅を制御するa~dpのピンが1組しかないので、ある桁を特定のパターンで点灯させたいときは、他の桁は消灯させる(でなければ、DnがHIGHになっている桁の全てが同じパターンで点灯する)。

そこで、ある桁を短い時間点灯させたあと消灯するという動作を各桁入れ替えながら実行していく。点灯・入れ替えの時間が十分短いと、残像減少によって人の目には全桁が同時発光しているように見える。

Raspberry Piによる制御回路

全体の考え方は以下の通り。

  • D1~D4の桁選択ピンは4つのGPIOピンでコントロールする
  • a~dpのセグメント選択ピンはシフトレジスター74HC595を介してコントロールする
  • 7セグメントLEDの保護抵抗はD1~D4に1つずつ繋ぎ、1つの7セグメントあたり1つとする

試験点灯

ソフトウェアによる制御の考え方は以下の通り。

  1. 初期設定ではD1~D4をLOWにする
  2. DnをD1~D4と順番に入れ替えて以下を繰り返す
    1. DnをHIGHにして選択する
    2. 選択したLEDに対してシフトレジスターを介して表示パターンを送る
      • 表示パターンはa→dpの順の8ビットで定義して、LSBから順にシフトレジスターに送り込む
    3. 一定時間sleepさせる
    4. HIGHにしたDnをLOWにする
  3. CTRL-Cが押されたらD1~D4をLOWにして終了処理を行う。

以下のPythonコードでは、いくつかの関数を定義している。

initialize
LEDとシフトレジスターの初期化を行う。
destroy
終了処理を行う。
display_a_digit
指定した桁のLEDを指定したパターンで一定時間点灯させる。桁の指定は左から0~3、パターンは0~9の数字に対応したビットパターン、点灯時間はミリ秒で与える。
display_digits
表示させる4桁の数値を文字列で与えて、一定時間点灯させる。ミリ秒で与えた点灯時間はdisplay_a_digit関数にそのまま渡される。

カウントダウンタイマー

time.perf_counter

4桁7セグメントLEDでカウントダウンタイマーを作ってみた。

  • 左2桁が分、右2桁が秒に対応する
  • 動作中は秒数でコントロールし、LED表示のために分と秒を分解する
  • 点灯時間をtime.perf_count関数で計り、約1秒ごとにカウントダウンする
  • カウントダウンを終えたら’0000’を表示して割り込みを待つ

なお、整数値の分・秒を2桁表示の文字列にする関数get_two_digits関数を定義している。

69-72行で分と秒をセットし、それを総秒数に計算。

77-93行がカウントダウン部分。

  • tartとendをtime.perf_counterで取得して経過秒数を計算
  • 表示秒数が1秒たつとカウントダウン
  • カウントダウン後と残りゼロ秒になったときに内側のループを抜ける

95-98行目では、残りゼロ秒になったときにオールゼロを表示して待機。

スレッド

実行部分をシンプルにするためにスレッドを使ってみた。

66-83行目で、4桁の内容を表示し続けるスレッドクラスを準備。

95-108行目で、1秒ごとにスレッドを発生させて現在の残り時間を表示。

回路の写真

74HC595の向きを逆にしてしまったのでコードがごちゃごちゃ。

Raspberry Pi – シフトレジスタ―~7セグメントLED

 

シフトレジスターには74HC595を使う。

ここでは、別々のGPIOから7セグメントLEDを点灯させたときと逆に、a→dpのビット順にしていて、コードではLSBから順に読みだしてシフトレジスターに送ることにした。

表示 a b c d e f g dp Hex
0 11111100 0xfc
1 01100000 0x60
2 11011010 0xda
3 11110010 0xf2
4 01100110 0x66
5 10110110 0xb6
6 10111110 0xbe
7 11100000 0xe0
8 11111110 0xfe
9 11110110 0xf6
A 11101110 0xee
B 00111110 0x3e
C 10011100 0x9c
D 01111010 0x7a
E 10011110 0x9e
F 10001110 0x8e

以下はRaspberry Piで7セグメントLEDを点灯させるコード。リストのpatternsに0~Fまでのセグメント点灯パターンを入れ、対応する桁数分シフトしてシフトレジスターに送っている。そして、各パターンを0.5秒ずつ表示させてカウントアップさせている。

キットのテキストのコードでは、time.sleep()を入れていたり入れていなかったりするが、特にこれがなくても問題なく動作する。

 

7セグメントLEDの保護抵抗

キットの回路図で、7セグメントLEDの保護抵抗をコモンアノードに1本だけ接続している例があった。

一般には複数のLEDにはそれぞれに保護抵抗を繋ぐべきとされている。並列接続のパーツに同じ電圧がかかったとき、同じVfでも多く電流が流れる特性のLEDにより多くの電流が流れるから、というのは理解できる。

そこで、手持ちの7セグメントLEDの各LEDの特性値を調べてみた。回路図は大したことはないが、折角描いたので記録しておく。

抵抗Rをいろいろな値のものに変え、VrVfを計測してIfを算出した。その結果は以下のようなグラフになった。

dpを含めた8つのLEDのうち、fだけ特性が大きく違っている。この場合、1つのLEDに電流が集中するのではなく、同じ電圧でfだけが暗くなることになる。

もしこのfの特性が逆で他のLEDの特性曲線より左側に来ると、同じ電圧をかけた時にfだけ大きな電流が流れることになる。

ただ、一般的なLEDであまりばらつきが大きくないとすると、概ね規格値のVfとIfを実現するような抵抗1つでも大きな問題はなさそうだ。どのみち大きな影響を受けるLEDはデータシートから外れているので、このように一つ一つ特性曲線を確認せずに抵抗を繋ぐ限り、影響は変わらないといえる。

 

 

Raspberry Pi – シフトレジスター~LEDバーグラフ

LEDバーグラフ

LEDバーグラフの例はこちらで示したが、点滅させるLEDの数だけGPIOポートを使った。シフトレジスターを使えば、データに関してはシリアル信号1本だけで済む。以下はシフトレジスター74HC595を使った回路例。

各LEDの点滅の制御は以下の流れ。

  1. LEDの個数分以下を処理
    1. SERに点灯ならHIGH、消灯ならLOWを入力
    2. SRCLKにHIGHを入力してシフト後、LOWに
  2. RCLKをHIGHにして、QA~QHの出力をLEDに流す
  3. 全消灯するときはSRCLKをLOWにしてからHIGHに戻す

以下はこの回路をRuspberry PiのMicro Pythonで制御するコード例。

SRCLK/RCLKでHIGH/LOWに変化させた後、それぞれLOW/HIGH戻すまでに0.001秒待たせているが、これがなくても動作はする。

 

74HC595

概要

74HC595には以下の特徴がある。

  • 8bitのSIPOシフトレジスター
  • 1本のシリアル入力ピン(SER)と8本のパラレル出力ピン(QA~QH)がある
  • 非同期クリアピンがある
  • QH’ピンで複数の74HC595をカスケード接続できる

ピン配置

各ピンの機能は以下の通り。

VCC 電源電圧
GND グランド
SER シリアル入力端子(立ち上がり同期)
SRCLK シフトクロック入力(立ち上がり同期)
RCLK ストレージレジスタ書き込み(立ち上がり同期)
SRCLR HIGHでシフトレジスタの内容を保持、LOWでクリア(クロックと非同期)
OE HIGHで出力無効、LOWで出力有効
QA~QH 8ビットのパラレル出力(RCLKで読み出す)
QH’ QHの状態を常に出力

データシート

絶対最大定格

電源電圧 VCC −0.5 ~ 7 V
入力電圧 VIN −0.5 ~  VCC + 0.5 V
出力電圧 VOUT −0.5 ~  VCC + 0.5 V
入力保護ダイオード電流 IIK ±20 mA
出力寄生ダイオード電流 IOK ±20 mA
出力電流 IOUT QH’: ±25, QA~QH: ±35 mA
VCC/GND電流 ICC ±75 mA
許容損失 PD 500 (DIP) / 180 (SOP) mW

動作範囲

電源電圧 VCC 2 ~ 6 V
入力電圧 VIN 0 ~ VCC V
出力電圧 VOUT 0 ~ VCC V
動作温度 TOPR −40 ~ 85
入力上昇・下降時間 tr, tf 0 ~ 1000 (VCC = 2.0V)
0 ~ 500 (VCC = 4.5V)
0 ~ 400 (VCC = 6.0V)
ns

 

シフトレジスター

直列入力並列出力(SIPO)

直列入力並列出力(SIPO: Serial-in Parallel-out)のシフトレジスターについて。複数のDフリップフロップを出力と入力で数珠つなぎにするとSIPOのシフトレジスターを構成できる。

以下は4ビットのシフトレジスターの簡単な例。

タイミングチャートで表すと、以下の様にCLKの立ち上がりのたびにSIN→Q0、Q0→Q1、Q1→Q2、Q2→Q3と1ビットの値がシフトしていく。

ここで、CLKの立ち上がり時にSIN→Q0→Q1→Q2→Q3と一斉に値が反映されてしまわないかということが気になる。

以下はクロックパルスの部分を誇張して描いたタイミングチャート。ここで、各Dフリップフロップに入力が与えられてから、それが出力に反映されるまで、僅かな時間だが遅延があることを表現している。

このため、CLKの立ち上がりが各Dフリップフロップに一斉に届いたとしても、SINの信号が使われるのはQ0のみになることがわかる。

実際のシフトレジスターには、内容をクリアする端子や、パラレル信号を取り出すための端子などが備えられている。

 

Dフリップフロップ

概要

Dラッチは、入力Gが1になっている間入力D(データ)の値を出力Qに出し続ける。一方入力Gにクロックパルスが与えられる場合、入力データの値をクロックパルスの立ち上がりで一度だけ記憶・出力して、次のクロックの立ち上がりまでその出力を保持したいときには、Dラッチではうまくいかない。

Dフリップフロップはこのような場合に使う。Dフリップフロップ回路は、以下の様にDラッチを2つ連ねた形をしている。

この回路は、CLKの入力が0→1に立ち上がった時のDの値を保持し、次に再びCLKが0→1に立ち上がるまでその値を保持し続ける。

基本的なDフリップフロップは以下のようなシンボルで表される。

仕組み

前段・後段それぞれのラッチのGに与える信号を反転させているのがミソで、以下のような流れになる。

  • クロックレベルが0のときは前段のラッチが入力状態、後段のラッチが保持状態となる
    • 前段のラッチは入力の値を出力し続ける
    • 後段のラッチは前段の値に関わらず保持内容を出力し続ける
  • クロックレベルが1になると、前段のラッチが保持状態、後段のラッチが出力状態になる
    • 前段のラッチはそれまでの入力内容を保持する
    • 後段のラッチは前段の保持内容を出力し始める
  • 再びクロックレベルが0になっても、後段のラッチは前のステップで入力された値を保持し続ける

イメージで図にすると以下の様になるだろうか。

回路図での確認

Dフリップフロップの動作を回路図で確認すると以下の流れになる。

初期状態として、すべての端子の信号レベルが0の状態とする。このとき前段のG1への入力のみ反転して1となり、前段のラッチは出力状態、後段のラッチは保持状態となっている。

その状態で入力が1となった場合。前段が出力状態となり1を出力するが、後段は保持状態のため、最後の出力は変化しない。

ここでクロックが1となると、前段が保持状態、後段が出力状態となり、最後の出力が1に変わる。クロックが1の間に入力値が変化しても、立ち上がり時の値が前段で保持されているので、出力は変わらない。

その後クロックが0となると、後段が保持状態となり、最後の出力が維持される。

その後入力信号が0となっても、後段で保持されている値が出力され続ける。

タイミングチャート

Dフリップフロップの動作をタイミングチャートで見てみた。下図の緑/オレンジの色がついている部分が前段/後段がSetの状態、色がついていない部分がそれぞれのHoldの状態。

各ステップでの状態は以下の様に推移する。

Step CLK 入力 前段の状態 前段の出力 後段の状態 後段の出力
a 0 0 Set 0 Hold 0
b 0 1 Set 1 Hold 0
c 1 1 Hold 1 Set 1
d 0 1 Set 1 Hold 1
e 0 0 Set 0 Hold 1
f 1 0 Hold 0 Set 0
g 1 1 Hold 0 Set 0
h 0 1 Set 1 Hold 0

 

Dラッチ

 

Dラッチ(D-Rarch)回路は以下のような回路。

この回路の動作は以下の様になる。

  • G=0の間、(Q, Q̄)は保持される
  • G=1の間、Q = D、(Q, Q̄) = D̄

このラッチはゲートGのレベルが0の間は値を保持し続け、Gが1になると入力Dの値をそのままQに出力する。このことを左の回路で確認する。

回路後段はRSフリップフロップとよく似ていて、入力にNOTゲートを入れればRSフリップフロップになる。ちょうど回路前段がNANDゲートになっているので、これをANDゲートとNOTゲートに分ければ、以下の様に表現できる。

次に回路前段の動作について考える。入力の0と1の組み合わせに対して、出力は以下の様になる。

この結果とRSフリップフロップの動作を合わせると、以下のような真理値表になる。

  • Gが0の間、QとQ̄を保持
  • Gが1の間、QにはDが出力され、Q̄にはD̄が出力される

たとえば以下のようなタイミングチャートになるか。

 

 

RSフリップフロップ

RSフリップフロップの動作

今まで横目でやり過ごしていたフリップフロップ回路について整理してみた。以下のようなRSフリップフロップを考える。

 

S R Ǫ Ǭ
0 0 保持 保持
0 1 0 1
1 0 1 0
1 1 禁止

一般には(S, R)の入力が(1, 0)や(0, 1)のように与えられたとき、以下のように説明されているものが多い。

  1. たとえばS = 1のとき、Ǭ側のNORゲートの出力は0 で確定
  2. その結果r = 0となり、R = 0の入力と合わせて、Ǫ側のNORゲートの出力は1となる

ただ、以下のような点が気になった。

  • そもそも入力が1の方が必ず先に動くのか
  • 電源投入時などにǪやǬの値が不定になったときにどうなるのか

何せ素人なので半導体素子の中でどういうタイミングで何が起こっているのかよくわからないが、自分なりに追ってみることにした。

あまりうまくない例

まず、(S, R)が(禁止されている(1, 1)の組み合わせの場合も含めて)与えられたとき、(Ǫ, Ǭ)の値が予めいろいろなパターンをとっていたとして、その後の推移を確かめてみた。

以下の表は、そのような時の状態変化を追ってみたもの。(Ǫ, Ǭ)の出力が確定しているので、与えられた(S, R)との組み合わせで(r, s)にフィードバックされる値が変化していくと考えた。

この表からは、以下の様なことが読み取れる。

  • (R, S)が(0, 1)や(1, 0)のときは、2サイクル目以降想定される出力で安定
  • (R, S)が(0, 0)のときは
    • (Ǫ, Ǭ)が(0, 1)や(1, 0)のときは1サイクル目から元の値が保持される
    • (Ǫ, Ǭ)が(0, 0)や(1, 1)のときには振動する
  • (R, S)が(1, 1)のときには(Ǫ, Ǭ)が(0, 0)になる

ここでも以下のようなことが気になる。

  • (Ǫ, Ǭ)が(0, 1)や(1, 0)のときに(R, S)に同じパターンを入れると、1サイクルだけ(R, S) = (0, 0)の状態になるが、これはナノ秒のレベルで実際に起こることなのか
  • (Ǫ, Ǭ)が(0, 0)のときに(S, R)に(0, 0)や(1, 1)を入力すると、その後(Ǫ, Ǭ)がごく短周期で振動するが、実際にそうなのか

そこで、回路のゲート単位で実行順序が変わった場合にどういう結果になるか確認してみた。

回路レベルで追った例

入力と出力が同じパターンの場合

一つ目は、(S, R)に(Ǫ, Ǭ)と同じパターンを入力した場合。以下の例では(Ǫ, Ǭ) = (0, 1)のときに(S, R) = (0, 1)を入力したケース。

まず上の方のゲートが先に動作した場合、以下の様に変化する。

  1. 入力Rが変化したとき、上のゲートが動作するが下のゲートの入力sへの帰還入力は変化しない
  2. ここで入力Sが変化していたとして下のゲートが動作しても、出力は変わらない
  3. 以後、安定状態に達する

また下のゲートが先に動いた場合も同じ流れで、結果は同じになる。

パターンが逆の場合も、回路は対称なので同じ流れになる。

入力と出力のパターンが逆の場合

二つ目は、(S, R)に(Ǫ, Ǭ)と逆のパターンを入力した場合。以下の例では(Ǫ, Ǭ) = (0, 1)のときに(S, R) = (1, 0)を入力したケース。これは少しややこしい、

  1. 入力Rが変化したとき上のゲートが動作するが、その出力と下のゲートの入力sは変化しない
  2. 入力Sが変化したとき下のゲートが動作し、その出力と上のゲートへの帰還入力は1→0に変化する
  3. それにつれて上のゲートが動作し、出力と帰還入力sは1に変化する
  4. それにつれて下のゲートが動作するが、その出力と上のゲートへの帰還入力0は変化しない
  5. 以後、安定状態に達する

一方、下のゲートが先に動作した場合は、すぐそのあとに下のゲートが動作して安定する。

出力保持の場合

入力(S, R)が(0, 0)の場合は出力は保持されるが、これを確認する。

以下の例では上のゲート、下のゲートの何れが動作するときも状態は変化せず、出力は変わらずに安定する。出力が逆のパターンでも、対称性から同じように保持される。

禁止状態の場合

最後に、禁止されている(S, R) = (1, 1)を入力した場合を確認する。以下の例では元の出力が(0, 1)の場合を例にしているが、入力の何れも1なので、有限のサイクルで必ず(Ǫ, Ǭ) = (0, 0)となる。

問題はその後に(S, R) = (0, 0)の保持信号を入力したときで、同じ状態から始めても最初に動作するゲートによって結果が異なってしまう。

まず、以下は上のゲートが最初に動作した場合。結果は(Ǫ, Ǭ) = (1, 0)となる。

一方下のゲートが最初に動作した場合は、結果が逆の(Ǫ, Ǭ) = (0, 1)になる。

したがって、(S, R) = (1, 1)が禁止されている背景を整理すると以下の様になる。

  • (S, R) = (1, 1)を入力すると、結果は必ず(Ǫ, Ǭ) = (0, 0)になる。
  • このパターンは(Ǫ, Ǭ)が符号通りの否定関係にならない
  • その後、そのパターンに対して(S, R) = (0, 0)の保護信号を入力した場合、結果が一つに特定できない

以上のことから、ゲートの動作順序レベル(入力信号の到達時刻レベル)の違いに対して、RSフリップフロップの論理値表の意義が一定理解できた。

RSフリップフロップの回路構成

RSフリップフロップは、NORゲートで表現される場合とNANDゲートで表現される場合がある。それらについての覚え書き。

冒頭のNORゲートによるRSフリップフロップ回路は以下の通り。

NORゲートは入力が否定ゲートのANDゲートと等価なので、以下の様になる。

ANDゲートをNANDゲートに変え、出力を変えない様にすると以下の様になる。

最終段のNOTゲートを除き、Ǫと Ǭを反転させて以下を得る。入力と出力の配置がNORゲートと逆になるが、S (Set)とがǬが同じ側に来るので直感的にわかりやすい。

 

RSフリップフロップは、以下のような記号で表される。