【中級者への第一歩?】ラズパイでオルゴールを作ろう!

RaspberryPiでオルゴールを作ってみる(PWM応用編) RaspberryPi
スポンサーリンク

こんにちは!駆け出しエンジニアのまっきーです。

今回は電子ピアノと電子オルゴールの作成です!なかなか本格的になってきました。

そもそもPWMって何?という方は過去の記事をご覧ください。

取り組む課題は以下の2つです。課題はこちらのサイトでダウンロードできます。

課題一覧

  1. 電子ピアノプログラム
  2. 電子オルゴールプログラム

電子回路

回路図は、前回と同様です。

電子ピアノプログラム

ハードウェア方式PWMで電子ピアノを模したものを作成します。

条件は以下の通りです。

・ デューティ比 約 50%
・ PWM 信号の階調(range) 100
・ PWM 信号の波形のモード PWM_MODE_MS
・ 1 本指による操作とし、和音には対応しなくてよい。

タクタルスイッチと音階の対応図

タクタルスイッチと音階の対応表

以前私が作成した「ドレミを鳴らす」プログラムに似ています。

しかし、設定する分周値があらかじめ与えられているので、計算はいらないですね!

ソースコード

実際のソースコードがこちらです。

分周比を配列に格納しました。そして、for文の中でスイッチの読み取りとPWM出力を行うようにしました。しかし…

全く動きませんでした。

スイッチの読み取りとPWM出力が重なっているからダメなのかと思い、スイッチを読み取る関数を作成して再度挑戦

これでもうまくいきませんでした。delayを入れたりしてみたのですが、それでもうまくいかず…

著者のコードをコピペしても鳴りませんでした…

原因がまだわかっていません。

もしわかる方がいらっしゃいましたら、教えて頂きたいです。

一度諦めて、次の問題に進みます。

電子オルゴール

SW0を押したら電子オルゴールが動作するプログラムを作成します。

自分の好みの歌をSW0を押すことで、スタートさせます。オルゴールの曲は、「きらきら星」を選びました。

条件は以下の通りです。

・ デューティ比 約 50%
・ PWM 信号の階調(range) 100
・ PWM 信号の波形のモード PWM_MODE_MS

仕様(簡易版)

かなり複雑でしたので、著者のサイトのヒントを参考に簡単な仕様にまとめてみました。

オクターブと鍵盤の対応

きらきら星の楽譜

                                    https://ototama.com/music/folksong/score.php?id=232

今回は、自分一人で一から作成というのは厳しかったので、著者のサンプルコードに手を加えてきらきら星のオルゴールを作成しました。

実際のオルゴールの様子

少し音が割れているところがありますが、それっぽくはなったと思います。

*音が高すぎたので200Ωの抵抗をつけました。

ソースコード

ビルドするときは、新しくmath.hを使用しているので、ビルドコマンドに-lmを付け加えてください。

ソースコード解読

1~23行目 ヘッダーファイルのインクルードとマクロ定義

25行目 デバック処理の有効化、無効化を設定できます

28~34行目 グローバル変数を定義、プロトタイプ宣言です

37~95行目 必要な変数の定義です 二次元の配列に音符を格納しているイメージです

96~109行目 SW、割り込み等の初期設定です

111行目 for文のカウントで使用するために音符の格納されている2次元配列の個数を調べています 

116~135行目 音符を順番に鳴らしていくための処理です SW1の割り込みが入った際は音を止めるように設定しています

137行目~ 音名、休符を作る関数です

142~149行目 solfa(階名)が0の時は休符ですので、それ以外の時 solfa-10をすることによりdの値が求められます。下の表と見比べながら見てみましょう。例えば、solfaがRE(3)だった場合3-10=-7ですよね。これで、REの周波数を求めるためのdがわかるわけです。

また、1オクターブ上の時は12乗(つまりd=12)することで表せます。下の時はー12乗です。

150行目 上の式を表しています。+0.5となっているのは四捨五入をするためのものです。int型ですので、小数部分は全て落とされてしまいます。ですが、あらかじめ0.5を足しておくと四捨五入したようにあらわすことができます。

152~166行目 PWMの出力設定です。もし、ラズパイ4を使っている方は、内部クロックが異なりますので、PI3_CLK の部分を 54000000 に変更してください。

このように定義することで、デバッグ機能のようなものをつけることができるということを初めて知りました。これはとっても便利ですね。

また、static volatile int g_stateという宣言がありました。

staticには関数と変数の参照範囲を限定的にする効果があります。つまり、ほかのファイルからの参照を禁止(保護)していることがわかります。

また、 volatile にはコンパイラの最適化を抑制する効果があります。

ここからは私の考えになるのですが、g_stateはwhile文の中では常に0です。割り込み処理があった場合のみ1になります。ですので、コンパイラはg_stateを使用されていない変数だと判断して最適化してしまう。ですのでvolatileで抑制しないといけないのかと思います。

volatile修飾子は組込みソフトウエアでは非常に重要な機能です。

まとめ・反省

・プログラムが長くなってくると、デバッグの仕組みを組み込んでおく必要が出てくる

・コンパイラが最適化してしまうと考えられるもの(レジスタなどの必要な変数のみ)はvolatile修飾子で最適化する必要がある。

・電子ピアノのシステムの不具合がなぜだかわからなかった。

→ハード側の問題を考慮できていなかったのでそちらを確かめてみる。

今回はかなり長くなってしまいました。一度でうまくいくことは、現場ではほとんどないと思うので、そのミスを見つけられるような仕組みをソフトウェア側で作ることもかなり重要なんだということがわかりました。

何とかある程度のコードは読めるようになってたので、そろそろ監視カメラ等の制作を始めていきたいですね。

最後までありがとうございました。

タイトルとURLをコピーしました