Python – FuncAnimationによる動画表示

概要

Pythonのmatplotlibにはanimationパッケージがあり、ArtistAnimationFuncAnimationの2つのクラスが準備されている。

  • ArtistAnimationは、予め動画に表示する各フレームを生成しておいて、これらを動画として表示する
  • FuncAnimationは、全結果を保存せず、フレームごとに描画を実行する関数を実行して、動画して表示する

ここでは、FuncAnimationによる動画の表示方法を整理する(ArtistAnimationについてはこちら)。

1つのグラフの表示

流れ

  1. animationパッケージ又はArtistAnimationクラスをインポート
  2. グラフを描画する関数を定義
  3. FuncAnimationクラスのコンストラクターに描画関数を渡してインスタンスを生成
  4. 表示

処理ステップ

インポート

パッケージをインポートする

または

フレームリストの準備

動画の各フレームを保存するリストを準備する。animationパッケージでは、個々のグラフフレームをartistと表現している。

FigureとAxesオブジェクトを準備

フレームを描画するオブジェクトを準備しておく

1フレームを描画する関数を定義

4周期分の正弦関数を描画する関数を定義している。

  • ここではループごとにxを計算しているが、今回は全フレーム共通なので、ループの外で準備してもよい
  • plot命令のcolor指定をしないと、描画するごとに違うグラフとして色を変えて描画されてしまう

アニメーション設定して表示させる

FuncAnimationクラスのコンストラクターに必要な引数を与えてインスタンスを生成し、表示させる。intervalはフレーム間の待機時間で、ミリ秒で与える。

GIF画像として保存する

結果をGIF画像として保存するには、plt.show()を実行せず、以下のようにimagemagicでファイルとして書き出す。

frames=20でフレーム数を設定しているが、これがないとGIF保存のときにファイル容量が肥大化してしまう。それでも容量は800KB程度になり、ArtistAnimationのときの150KB程度よりも大きい。

実行結果

以下のような動画が表示される。

コード

全体の実行コードは以下のとおり。

複数のグラフの表示

流れ

全体の流れは単一のグラフの場合と同じ。リスト保存時に複数グラフを加算して重ね合わせるところだけが異なる。

  1. animationパッケージ又はArtistAnimationクラスをインポート
  2. グラフを描画する関数を定義し、複数のグラフをプロット
  3. FuncAnimationクラスのコンストラクターに描画関数を渡してインスタンスを生成
  4. 表示

処理ステップ

以下の描画関数が単一グラフの場合と異なる。関数の中で、異なる2つのグラフを描画している

実行結果

以下のように、2つのグラフが同時に表示される。

コード

全体の実行コードは以下のとおり。

描画関数に引数を渡す

描画関数に独自の引数を渡すことができる。

たとえば最初の「1つのグラフの表示」で、frame_countと表示する最大時刻max_xを引数とする場合、まず描画関数でこれらを引数として宣言し、関数内でこれを使った処理を書く。

FuncAnimationインスタンス生成時に、fargsで独自宣言した引数の値をタプルで与える。

 

Python – ArtistAnimationによる動画表示

概要

Pythonのmatplotlibにはanimationパッケージがあり、ArtistAnimationFuncAnimationの2つのクラスが準備されている。

  • ArtistAnimationは、予め動画に表示する各フレームを生成しておいて、これらを動画として表示する
  • FuncAnimationは、全結果を保存せず、フレームごとに描画を実行する関数を実行して、動画して表示する

ここでは、ArtistAnimationによる動画の表示方法を整理する(FuncAnimationについてはこちら)。

1つのグラフの表示

流れ

  1. animationパッケージ又はArtistAnimationクラスをインポート
  2. フレームの数だけグラフを描画してリストに保存
  3. フレームリストを渡してArtistAnimationインスタンスを生成
  4. 表示

処理ステップ

インポート

パッケージをインポートする

または

フレームリストの準備

動画の各フレームを保存するリストを準備する。animationパッケージでは、個々のグラフフレームをartistと表現している。

FigureとAxesオブジェクトを準備

フレームを描画するオブジェクトを準備しておく

フレーム数を指定して各フレームを保存する

以下ではフレーム数を20として、20回分のグラフを少しずつずらして描画して保存している。

ここではグラフを正弦波として、1周期分を20分割して表示させている。

  • ここではループごとにxを計算しているが、今回は全フレーム共通なので、ループの外で準備してもよい
  • plot命令のcolor指定をしないと、描画するごとに違うグラフとして色を変えて描画されてしまう
  • 描画結果はオブジェクトとして、先に準備したartistコレクションに追加している

アニメーション設定して表示させる

ArtistAnimationクラスのコンストラクターに必要な引数を与えてインスタンスを生成し、表示させる。intervalはフレーム間の待機時間で、ミリ秒で与える。

GIF画像として保存する

結果をGIF画像として保存するには、plt.show()を実行せず、以下のようにimagemagicでファイルとして書き出す。

実行結果

以下のような動画が表示される。

コード

全体の実行コードは以下のとおり。

複数のグラフの表示

流れ

全体の流れは単一のグラフの場合と同じ。リスト保存時に複数グラフを加算して重ね合わせるところだけが異なる。

  1. animationパッケージ又はArtistAnimationクラスをインポート
  2. フレームの数だけグラフを描画して、重ね合わせてリストに保存
  3. フレームリストを渡してArtistAnimationインスタンスを生成
  4. 表示

処理ステップ

以下の部分だけが単一グラフの場合と異なる。2つのグラフを描画し、そのオブジェクトを加算して1フレームとした上でartistsに追加している

実行結果

以下のように、2つのグラフが同時に表示される。

コード

全体の実行コードは以下のとおり。

図形を含むアニメーション

matplotlib.patchesによる図形を含んでアニメーション表示するには、少し工夫がいる。その方法はこちら

 

PythonCGI – 基本

概要

PythonのコードでCGI(Common Gateway Interface)によってHTMLをする方法の基本。

基本の枠組み

コード例

例えば以下の内容をindex.pyとして準備。

  1. SheBangでPythonの場所を指定
  2. Content-Typeを出力して1行空ける
  3. 出力内容を記述

HTTPのヘッダーで最低限Content-Typeを出力し、ボディーとの間に空行が1行必要。これがないと、500エラーとなり、ログには以下の様に記録される。

コードのモード確認

ファイルがrootの実行権限を持っていること。実行権限がない場合はchmod 755chmod +xで設定する。

コードの配置

cgi実行用のディレクトリーにファイルを配置。Apacheの場合例えば/var/www/cgi-binなど。

ブラウザ表示

URLで[ホスト]/cgi-bin/index.pyを指定すると、ブラウザーにPython CGI Testと表示される。

HTML出力例

ASCII出力

基本の枠組みの出力に、以下の様にタグを加えると要素として表示される。

UTF-8出力

上のコードの一部を日本語にしてみる。

ところがこれをブラウザーで確認すると、h1要素は表示されるがp要素が全く表示されない。ソースを確認してもh1要素の行が表示されるだけ(このコードをコンソールで実行すると、h1要素の行もp要素の行も表示される)。

これを解決するには、ソースコードでio.TextIOWrapperを実行するとよいらしい。

上記の2行を追加すると、無事日本語まで表示された。

HTML出力

コンテンツを以下の様にHTMLの構造にして出力する。title要素も機能して、タブの表示が変更された。

ループの利用例

以下のコードでは、見栄えは良くないがforループでli要素を生成している。

出力に以下が加わる。

  1. 1番目のli要素
  2. 2番目のli要素
  3. 3番目のli要素
  4. 4番目のli要素
  5. 5番目のli要素

Apacheの設定

今回は/etc/httpd/conf/httpd.confのデフォルトのままでcgi-scriptの設定を加えていないが、そのままで適切に実行された。

 

Atom – VagrantにSFTP接続

概要

  • RemoteFTPパッケージを利用
  • VagrantのSSHの設定内容を確認
  • Atomの.ftpconfigを編集
  • 接続

VagrantのSSH設定内容確認

vagrant upの前の状態で、Vagrantfileのあるディレクトリー下でvagrant ssh-configを実行。

.ftpconfigの編集

Atomを起動、Packages→RemoteFTP→ToggleでサイドパネルにRemoteタブを表示。Edit Configurationで.ftpconfigを表示させて編集。

 

接続開始と終了

サイドパネルのConnectを実行して接続。終了時はPackages→RemoteFTP→Disconnect。

 

 

コイル(インダクター)

電流・磁界の法則

右ネジの法則

電流が流れると、その方向に対して右回りの磁界が発生する(Maxwell’s right-handed screw rule)。

右手の法則

コイルに電流を流すと、電流の向きが右手を握った各指の方向のとき親指の挿す方向に磁界が発生する。

フレミングの左手/右手の法則

絵は面倒なので割愛。人差し指が磁界、中指が電流、親指が力の方向。左手は磁界内の電流に働く力、右手は磁界内で運動する場合に発生する電流の方向。

ローレンツ力

電荷e[C]の荷電粒子が磁束密度B[T]内を運動ベクトルvで移動するとき、荷電粒子に対してフレミングの左手の法則に従って力が働く。

このとき、vが磁束に対してなす角をθとすると、荷電粒子に働く力は以下の様になる。

(1)    \begin{equation*} f = q v (\sin \theta) B \end{equation*}

ローレンツ力の方向まで含める場合、ベクトルの外積よって以下の様に表せる。

(2)    \begin{equation*} \boldsymbol{f} = q \boldsymbol{v} \times \boldsymbol{B} \end{equation*}

レンツの法則

コイルを通る磁束が変化するとき、その磁束の変化を妨げる方向に誘導電流が生じる。

たとえば以下の図では、コイルに対して磁石のN極側を近づけている。このときコイルに対して左から右へ向かう磁束が増加するので、それ打ち消す右から左の磁束を増やすように誘導電流が流れる。

この状態で磁石が離れていくときは右向きの磁束が減少するので、コイル内で同じ右向きの磁束を発生させるよう、上図と逆向きの電流が流れる。

一方、S極が近づくときは左向きの磁束が増えるので、これと逆の右向きの磁束が発生するよう、上図と逆向きの電流が流れる。

S極が離れる場合には、左向きの磁束が減少するので、これを補うように左向きの磁束を発生させる方向(上図と同じ方向)に電流が流れる。

ファラデーの法則

閉回路を貫く磁束Φが変化するとき、レンツの法則によりその変化を妨げる方向の電流が流れるが、この起電力を誘導起電力という。誘導起電力の大きさは以下のように表される。

(3)    \begin{equation*} | V | = \left| \frac{d \Phi}{dt} \right| \end{equation*}

起電力の正負の向きについて、「磁束の変化を妨げるように」というレンツの法則から、以下の様に表現される。

(4)    \begin{equation*} V = - \frac{d \Phi}{dt} \end{equation*}

またコイルの巻き数がNのときは、以下の様になる。

(5)    \begin{equation*} V = - N \frac{d \Phi}{dt} \end{equation*}

コイルの自己誘導

自己誘導とは

コイルにかかる電圧が変化した場合場合、電流の変化に応じて磁界が変化するが、レンツの法則によってそれを阻止しようとする方向の電流を流そうという起電力も生じる。このような現象をコイルの自己誘導と言い、自己誘導によって生じる元の電圧と逆の起電力を逆起電力という。

電源が立ち上がった場合

たとえば以下のような回路を考える。ここで電源電圧が0→Eに立ち上がった場合、コイルによる逆起電力、抵抗による電圧降下が生じる。電源の起電力は正極側を⊕としている。抵抗Rの電圧は電源に対して電圧降下となる電流の向きの矢印、コイルの方は電流の変化によるので両方向きの矢印としている。

電流をII(t)として、電圧のバランスを定式化すると、以下のようになる。

(6)    \begin{equation*} E - L \frac{dI}{dt} - RI = 0 \end{equation*}

この微分方程式を以下のように解いていく。

(7)    \begin{equation*} \frac{dI}{E - RI} = \frac{dt}{L} \end{equation*}

(8)    \begin{equation*} -\frac{1}{R} \ln (E - RI) = \frac{t}{L} + const \end{equation*}

(9)    \begin{equation*} I = \frac{E - k e^{- \frac{R}{L} t}}{R} \end{equation*}

ここでIt=0 = 0の条件によりkを決定する。

(10)    \begin{equation*} \frac{E - k}{R} = 0 \quad \Rightarrow \quad k = E \end{equation*}

よって、自己誘導を含めた電流変化は以下のように求められる。

(11)    \begin{equation*} I = \frac{E}{R} \left(1 - e^{- \frac{R}{L} t} \right) \end{equation*}

以下のグラフは、L = 100mH、R = 1kΩ、E = 5Vの場合にスイッチON以降の電流変化を計算したもの。自己誘導の影響がなくなり定常電圧になるのに500μsほどかかっている。

電圧が立ち下がった場合

初期電流It=0 = E/Rとして、t = 0でE = 0になったとして先の微分方程式を解いてみる。まず式(6)でE = 0と置く

(12)    \begin{equation*} - L \frac{dI}{dt} - RI = 0 \end{equation*}

これを解いて

(13)    \begin{equation*} I = k e^{- \frac{R}{L} t} \end{equation*}

初期条件を考慮して以下を得る。

(14)    \begin{equation*} I = \frac{E}{R} e^{- \frac{R}{L} t} \end{equation*}

この式形は式(11)と上下逆の形になっていて、以下のようなグラフになる。

サージ

サージの考え方?

コイルを含む回路を半導体素子で開閉した場合、スイッチOFFの際に高電圧のサージが発生するということを知った。ただし定性的な(あるいは感覚的な)原理は多くみられるが、そのメカニズムがどうも腹に落ちない。

「スイッチOFFにしても電流が流れようとするので誘導起電力が発生し・・・」という説明を見かけるが、もし理想的なスイッチで瞬間的にOFFとなるなら、誘導電流が流れる時間がゼロだから、回路としてはそのまま黙ってしまうのではないか(dI/dtが無限大になるのとどう関係するのか定かではないが)。

スイッチが完全にOFFになるまでに若干のスパークが発生することや、スイッチング阻止の抵抗が無限大ではないことを含めて説明している場合もあり、これは現実論として納得できる。

特にこちらのサイトはとても参考になったので、これを自分なりにトレースして、理解を試みた。

まず以下の図で、電源Eに接続していた状態からスイッチを抵抗S側に切り替えたとする。ただし以下の点に留意する。

  • 切り替え前は定常状態で、回路の電流はI = E/R
  • ここでのスイッチは理想的なスイッチとし、抵抗Sが現実のスイッチ切り替え時の抵抗に相当

ここで切り替えの瞬間をt = 0として微分方程式は以下のようになる。

(15)    \begin{equation*} - L \frac{dI}{dt} - RI - SI = 0 \end{equation*}

初期条件t = 0でI = E/Rとして解いて、以下を得る。

(16)    \begin{equation*} I = \frac{E}{R} e^{- \frac{R + S}{L}t} \end{equation*}

この電流に対してコイルは起電側となり、2つの抵抗で電圧降下となって、それらは以下のようになる。

(17)    \begin{equation*} \left\{ \begin{array}{l} V_R = RI = E e^{- \frac{R + S}{L}t} \\ V_S = SI = \dfrac{S}{R} E e^{- \frac{R + S}{L}t} \\ V_L = - L \dfrac{dI}{dt} = - \dfrac{R + S}{R} E e^{- \frac{R + S}{L}t} \end{array} \end{equation*}

この式によるなら、t=0でコイルによる起電力は(R + S)/Rとなり、このうちS/Rが抵抗Sの電圧降下となる。たとえばR=1kΩのときにSが10kΩでも元の電源電圧の10倍の電圧がSの両端にかかることになる。

この様子を先と同じ条件L = 100mH、R = 1kΩ、E = 5Vに加えてS = 10kΩとして描いたのが以下のグラフ。スイッチOFFのときにEの10倍の50Vが生じ、そこから急激に電圧が下がっている。また時定数(R + S)/Rも大きくなるので、減少の時間も1オーダー短くなっている。

このSが大きくなると、その倍率に応じて大きな電圧が生じ、低減時間は短くなる。すなわち、一瞬の間だけ高電圧が発生する。これがコイルによるサージと呼ばれるものではないか。

ただし元々接続されていたRの両端の電圧の最大値は変わらないので、サージの影響を受けるのはスイッチ素子ということになる。ここにトランジスターなどを使っていると、たとえばCE間に逆向きの高電圧がかかって破壊されてしまう。

保護回路

コイルサージから回路を保護するためにいろいろあるようだが、最も簡単なのは以下の様にダイオードを加えることらしい。

通常のスイッチONの状態ではダイオードが逆向きなのでコイルに電流が流れ、サージ電流はダイオードを通じて戻るので、RSには影響を与えない。

 

Raspberry Pi – ブザー

概要

Raspberry Pi用キットのアクティブ・ブザー、パッシブ・ブザーの実験。

アクティブ・ブザー

単純接続

アクティブ・ブザーは振動源を内蔵していて、電流が流すだけで音が出る。まず、単純に3.3Vの電源に繋いでみた。

抵抗なしで直接GNDに繋ぐと、「ピュー」という音が鳴る。抵抗値が10Ωで音はかなり小さくなり、100Ωにするとかすかに聞こえるくらいの音になった。

回路図

ブザーを電源に直接GPIOに繋いでもRasberry Piで制御できるが、キットの回路図はPNP型のトランジスターを介して接続していた。GPIOから流れる電流をより少なくするためと思われる。

この回路の場合、GPIOをHIGHにすると音はならず、LOWにするとC-E間にも電流が流れて音が出る。音量は直接3.3Vに繋いだ時と同じ。

コード

以下のコードでは単純にGPIOのHIGH/LOWを切り替えて、一定間隔でブザーを鳴らす。周期を短くすると「ピュピュピュピュ・・・」と連続して鳴動する。

パッシブ・ブザー

ソフトウェアによる音

パッシブブザーは入力の1パルスに対して1回だけクリック音が出る。アクティブ・ブザーと同じ回路図でブザーをパッシブブザーに取り換えて同じコードで実行すると”click, click, …”と単調な音が続く。

ここで入力をある周波数にすると、その周波数に対応した音程のビープ音になる。

以下のコードは440Hzの音を鳴らすもので、設定した周波数の周期で矩形パルスを発生させてAの音を出す。ソフトウェア制御なので、ときどき音が途切れたりする。

PWMによる音

上と同様のことを、ハードウェアのPWMで実装したのが下記のコード。

ハードウェアPWMのGPIO13にピンを変えて、周波数やデューティー・サイクルを指定してAの音を2秒間出している。

音階

PWMを使ってスケールを鳴らしてみた。

辞書定数で各音階に対応する周波数をセットして、ある周波数の1音を鳴らすクラスを定義。Cのスケールで音階を鳴らしている。

音楽

改めて音符・給付を並べて音楽を演奏するクラスを定義。ついでにテンポも指定できるようにした。

このクラスを使って、D-durの音階を鳴らしている。

 

トランジスター

バイポーラトランジスター

バイポーラ(bipolar)は「二極の」という意味で、バイポーラトランジスターはP型・N型の2つの半導体、電子(electron)と正孔(hole)の2つが対をなして機能する。

PNP型トランジスター

PNP型トランジスターの原理は以下の通りで、正孔を持つP型半導体でごく薄いN型の半導体を挟んでいる。

エミッター~コレクター間に正電圧をかけても、それぞれの正孔がベースの電子と結合して空乏層(deplation layer)が生じ、電流は流れない。

ここでエミッター~ベース間に正電圧をかけると、それぞれの間の成功と電子は結合するが、ベースが非常に薄いためエミッターの正孔が通り抜けてコレクターに達し、電流が流れる。

回路図の表現は以下の通りで、コレクター・ベースに対してにエミッターに正電圧をかける。

たとえばエミッター側に負荷素子を繋ぎ、エミッター電圧をかける。ベース側の電流のON/OFFによってE-C間の電流が流れ、IBに比べて大きなIEのスイッチングや増幅が可能になる。

 

S8550トランジスター

S8550はFairchild社のSS8550の中国製らしい。PNP型のトランジスターで、以下の特徴を持つ。

  • 低圧、大電流タイプのPNPトランジスター
  • To-92パッケージ
  • 連続最大コレクター電流:500mA
  • CE間電圧:VCEO = –25V
  • CB間電圧:VCBO = –40V
  • EB間電圧:VBEO = –5V
  • 電流増幅率:hFE = 85~300

ピン配置は以下の通り。

 

Raspberry Pi – I2C~LCD1602

概要

ここではI2C接続によるLCD1602の制御について整理する。

Raspberry Pi用のキットにはI2CがセットされたLCD1602が同梱されていたが、Pythonのモジュールが見つからなかった。ゼロから組み立てようかとCのコードを読んでみたが、いま一つわからない。そこでネット上をいろいろと探して、自分なりに理解してみた。

LCD1602

LCD1602のピン配置

LCD1602のピン配置は以下の様になっているらしい。上から順番に1~16。

各ピンの意味は以下の通り。

GND Ground
+5V Vcc(電源)
CNT L→H→LでDB0~DB7ピンと信号伝送
RS Register Select -> H: Data, L: Command
R/W H: Read, L: Write
E L→H→LでDB0~DB7ピンと信号伝送
DB0~DB7 8ビットデータ
A LEDのアノード
K LEDのカソード

表示領域~DDRAM

2行表示の場合のDDRAMアドレスの割り当ては以下の様になっている。

LCD1602のコマンド

LCD1602のコマンドは以下の通り。H→1、L→0で表している。

  • RS=0のときはコマンド、RS=1のときはデータを表す
  • R/W=1で読み出し、R/W=0で書き込みで、コマンドは常にR/W=0
  • コマンドの場合は8ビットのDB7~0で最初に1が現れたビットによってコマンドが変わる
  • 書き込み/読み出しは、EをL→H→Lと変化させて実行

8ビット/4ビットモードの設定

LCD1602起動時、確実に8ビット/4ビットモードをセットする手順は以下の通り。

起動直後の初期設定では、適切な待機時間を挟んで4ビットで0x3を3回書き込んだ後に0x2を書き込むことで4ビットモードに設定される。

I2CによるLCD1602の制御

接続

キットのLCD1602にはあらかじめI2C接続のためのボードが接続されていた。チップはHLF8574Tと書かれていたが、これは中国製らしくフィリップス製のPCF8574と同じ機能らしい。PCF8574はI2C I/Oエクスパンダ―というチップで、I2Cに準拠しSDAを介したシリアルの通信と、P0~P7の8ビットパラレルの通信を変換する。

PCF8574のP0~P7が8ビットのレジスターの各ビットに相当し、それらはLCD1602の各ピンと以下のように接続されている(他のピンは省略している)。

参考サイト:

PCF8574 LCD1602 LCD1602の機能
P7 DB7 4ビットデータのbit3
P6 DB6 4ビットデータのbit2
P5 DB5 4ビットデータのbit1
P4 DB4 4ビットデータのbit0
P3 K バックライトのカソード
P2 E L→H→LでDB0~DB7ピンと信号伝送
P1 R/W H: Read, L: Write
P0 RS Register Select -> H: Data, L: Command

DB0~DB3は接続されていないことから、このモジュールは常に4ビットモードで使用する必要がある。

P2に接続されたKピンはバックライトのカソードにあたり、トランジスターのC~Bを介してGNDに接続されている。

LCDとRaspiの接続

Rasberey PiのGPIO2と3(3番ピンと5番ピン)がSDAとSCLに対応しており、電源・GNDとともにこれらをLCDの端子(正確にはLCDに接続されたエキスパンダーの端子)に接続する。

SDAとSCLに対するプルアップ抵抗がないが、LCD/I2C内部でプルアップされているらしい。

初期手順

Raspberry Piの手順に従ってI2Cを有効化した後、デバイスアドレスを確認(I2Cを参照)。ここでは、LCD1602のデバイスアドレスは0x27。

コード

動作テスト

LCDモジュールを4ビットモードにセット後、画面をクリアしてカーソルをブリンクさせる。

冒頭で定数を定義している。TWはEを1にしてからLに戻すまでの待機時間で、データシートでは最小230nsという値もあったが、Pythonのtime.sleepの分解能もあり、1ms(以上)としている。

次に、4ビットのデータを書き込む関数を定義している。

  1. 引数のデータの下位4ビットを取り出し、4ビットシフトして上位4ビットへ
  2. 下位4ビットにコマンドセット
    • バックライトON、転送Enable、Writeモード、コマンドレジスターに書き込み指示
  3. 書き込みウェイト
  4. EnableをLowにして書き込み

もう1つの関数は、1バイトのコマンドをLCDに書き込む。4ビットモードなので上位・下位の4ビットに分けて書き込んでいる。

コマンド送信の場合の手順は、上位4ビットの場合は0xF0でマスク、下位4ビットの場合は0xFでマスクして4ビット分シフトした後、以下の通り。

  1. 下位4ビットはコマンド書き込みイネーブルで、KE(RW)(RS)=0b1100=0xC
  2. TWの間ウェイト
  3. その後Eのみ0とするため0b11111011=0xFBでマスクして書き込み

実行部分の先頭で、8ビットモードを確定してから4ビットモードを設定。

いろいろなサンプルコードで0x33→0x32とバイト単位で書き込む例が多いが、4ビットごとの待機時間を意識するならこの方がデータシートの説明とも合っている。

最後にコマンド送信。

まず001で始まるFunction setで0b00101000=0x28とする。

  • DL=0で4ビットモード
  • N=1で2行表示
  • F=0で5×8フォントを使用

次に00001で始まるDisplay ONで0b00001111=0x0Fとする。

  • D=1でディスプレイ表示
  • C=1でカーソル表示
  • B=1でブリンクさせる

最後に0b00000001=0x01でディスプレイをクリア。

文字表示

先のコードに変更・追加を加えて、LCDに文字を表示してみる。

まず、write_command関数をwrite_byte関数に変更し、引数is_commandTrue/Falseに応じてRSの0/1を切替え、コマンド・データの両方を取り扱えるようにした。

データ送信の場合イネーブル時にRS=0となるため、下位4ビットはKE(RW)(RS)=0b1101=0xDとなる

また、新たに文字を表示する関数write_charを定義した。アドレスが与えられた場合はDDRAMのそのアドレスに、アドレスを指定しない場合は現在のアドレスに文字を書き込む。1文字書き込むとアドレスはインクリメントされるため、続けて文字を書き込む場合にはアドレス指定はしなくてよい。

実行の前半では、カーソルOFF、ブリンクOFFとしている。

文字表示の部分は以下の通りで、1行目に”ABC”、2行目に半角カナで”アイウ”と表示している。

LCD1602クラス

LCD1602を扱うクラスを作成。これまで作った関数をクラスのメソッドにしただけ。インスタンス生成時にLCDのアドレスを与える。

 

Raspberry Pi – シフトレジスター~ドットマトリックスLED

概要

Raspberry Piで8×8のドットマトリックスLEDを表示させてみた。

  • 2つのシフトレジスターを使って、任意のパターンを表示させるようにした
  • パターンを周期的に流れるように表示させた

ドットマトリックスLEDの仕様

キットに同梱されていたドットマトリックスLEDはピン配置や極性の手掛かりになる表示などがなかったので、1つずつ通電しながらピン配置と点灯の確認を行った。

内部の接続は以下のようになっている。RmをHIGHに、CnをLOWにすると、m行目n列目のLEDが点灯する。

回路図

キットのマニュアルの回路は2つのシフトレジスター74HC595を使っていたが、それらをカスケードに接続しているため、行と列のパターンを同時にしか設定できない。

そこで、2つのシフトレジスターを切り離して独立して制御するようにした。1行ずつスキャンしながら行ごとの点灯パターンを設定し、8×8の64個のLEDを任意のパターンで点灯させることができる。

ブレッドボードで回路を組んだら、かなり配線がごちゃごちゃになった。LEDのRとCから8本ずつのケーブルが出て、保護抵抗も8本使っているので仕方がないか。LEDのサイズがそれなりに大きいので、ブレッドボードの穴が覆われてしまう。

表示は平仮名の「お」で、後述のサイネージコードで「おはよう」と流して表示しているときの一部。

コード例

試験点灯

まず、8×8のドットパターンを2秒間だけ表示させてみる。

ドットパターンはサブディレクトリーlibの下にled_dotmatrix_data.pyとして以下のモジュールを準備した。

パターンをスペースとスペース以外の文字(ここでは*)を使って準備し、get_bit_pattern関数で8ビット×8行のリストとする。

最初のコードで、まずGPIOピンなどの定数を定義している。ROWSはドットマトリックスの各行を単独で指定する際に、シフトレジスターに与えるビットパターン。

shift関数は、SERとSRCLK、HIGH/LOWのレベルを指定してシフトレジスターを1回シフトする。

read関数は、RCLKをHIGHにしてストレージレジスタ―の出力を取り出し、LOWに戻す。

clear関数はドットマトリックスのR1~R8をLOWにしてLEDを消灯させる。念のためC1~C8もLOWにしている。

display関数は、8ビット×8行のビットパターンを与えてsleepの秒数だけLEDを点灯させる。

DisplayThreadはビットパターンを与えて生成し、startメソッドを呼ぶと内部でrunメソッドが実行される。stopメソッドによりスレッドが停止する。

試験点灯用のコードは以下の通りで、表示パターンを与えてスレッドを生成し、2秒間点灯させた後にスレッドを停止させている。

ここで、stopメソッドの後に僅かな時間の間をおかないと、「setmodeが必要」というランタイムエラーが出る。destroyメソッドでcleanupを実行するタイミングがstopで表示が停止するよりも早いからかもしれない。

CTRL-Cでの表示停止

次に、実行後表示を続けてCTRL-Cのキーボード入力で停止するようにした。

このとき、キーボード割込を待ち受けるwhileループの内容を単にpassとすると目に見えるくらいのちらつきが生じたため、sleepを入れている。

アニメーション表示

2つのパターンを準備して、一定時間で交互に表示を切り替えてみた。

まず、led_dotmatrix_dataモジュールで準備した表示パターンは以下の通り。

2つのパターンを交互に切り替える実行部分は以下の通り。

流れる表示

8×8のパターンを横に流れるように表示してみた。led_dotmatrix_dataモジュールに以下を追加。

get_cycle_pattern関数は、8×8のパターンの表示開始位置を与えてサイクリックに表示をずらすためのパターンを返す関数。

以下は、この関数を使って流れる表示をさせるコード。データのリストの最後まで来たら、表示させる桁数を先頭に戻している。この結果、正弦波のような波が流れて表示される。

サイネージ

複数の文字が横に流れるように表示させるようにしてみた。まず、led_dotmatrix_dataモジュールに以下の関数とパターンを追加。

combine_patterns関数は複数のパターンを引数にとり、それらを横に連ねた2次元リストを返す。

実行部分は以下の通りで、複数の文字パターンを関数で1つの2次元リストにまとめ、それを上の流れる表示と同じように処理している。

 

2次元リストの結合

概要

2次元リスト同士の結合について整理。

  • 縦方向の結合
    • appendはループが必要
    • extendは破壊的に結合
    • +演算子は非破壊的に結合
  • 横方向の結合
    • extendでループが必要
    • +演算子はループの方法に注意

縦方向の結合

appendは要注意

1次元のリストの場合と同じく、appendは引数全体を要素として追加してしまう。

追加されるリストの各行を取り出して追加すれば可能。ただし破壊的。

extendはok(破壊的)

extendは引数のリストの内容をそのまま追加してくれる。ただし破壊的。

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

+演算子もextendと同じ挙動だが非破壊的。

横方向の結合

extendで一手間必要

extendで各要素に追加する場合、各要素をループで取出して追加する必要がある。

カウンターではなく要素を直接取り出してもok。

+演算子は要注意

+演算子で結合する場合は、結果のリストを準備しておく必要がある。このとき、結合するリストと同じ要素数(行数)の空のリストを要素に持つリストを準備しておく必要がある。

また、ループはカウンターを使って添字でリスト要素を指定しなければならない。

要素を直接取り出して演算した場合、変数が新たに作成されるがその結果は元のリストには反映されないので注意。