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次元リストにまとめ、それを上の流れる表示と同じように処理している。

 

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のみになることがわかる。

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