前の10件 | -
スローなブギにしてくれ
DX7を弾きこなすのがこのブログの壮大なテーマなのに、いっこうに演奏技術が上達していないではないか。練習もしないでプロのミュージシャンの動画みたいに演奏したいというのがそもそも無理なのだ。大いに反省して、それならばコンピュータ(マイコンだけど)を駆使して上達してやろうと思い立った。
やっぱりキーボードの練習というと、楽器メーカーから出ている鍵盤が教えてくれるものが頭に浮かびます。最近のは鍵盤そのものが光ったり、音声で指示してくれるものがあったりして、短期間で上達できそうです。また、youtubeをチェックすると、ウォーターフォール タイプでのレッスン動画というのがあって、画面上で、鍵盤の上に光の点が滝のように下に降りてくるので、その部分(鍵盤)を押さえると演奏出来るというものをよく見かけます。このようにいろいろな方法がありますが、DX7でも出来る方法ということで、キーボードを簡単にマスターできる装置を考えました。以下、キーボードの演奏理論や音楽技法などは全くといって知らないのでおかしな点があることをお許し願いたいです。
やっぱりキーボードの練習というと、楽器メーカーから出ている鍵盤が教えてくれるものが頭に浮かびます。最近のは鍵盤そのものが光ったり、音声で指示してくれるものがあったりして、短期間で上達できそうです。また、youtubeをチェックすると、ウォーターフォール タイプでのレッスン動画というのがあって、画面上で、鍵盤の上に光の点が滝のように下に降りてくるので、その部分(鍵盤)を押さえると演奏出来るというものをよく見かけます。このようにいろいろな方法がありますが、DX7でも出来る方法ということで、キーボードを簡単にマスターできる装置を考えました。以下、キーボードの演奏理論や音楽技法などは全くといって知らないのでおかしな点があることをお許し願いたいです。
■キーボードマーカーの仕様
大まかな説明をすると、MIDIデータを取り込んで、ノート音データに応じた鍵盤のLEDを点灯させ、同時に指にも振動でお知らせする装置です(左のイメージ図ですが、あらためて見ると怪しい健康器具のような感じです。本当に効果あるの?と思ってしまう)
大まかな説明をすると、MIDIデータを取り込んで、ノート音データに応じた鍵盤のLEDを点灯させ、同時に指にも振動でお知らせする装置です(左のイメージ図ですが、あらためて見ると怪しい健康器具のような感じです。本当に効果あるの?と思ってしまう)
この回路図に書ききれていませんが、ダイナミック駆動回路は2系統のモーター回路と4系統のLEDドライブ回路および2系統のスイッチ入力回路を1ブロックとして4個のブロックから成り立っています。(実際に作るよりも回路図を書くほうがちょっと苦労しました。)
■部品リスト
部品 | 数量 | 特記事項 |
小計(円)
|
PIC16F1827 | 1 | 150 | |
GM009605(SSD1306) |
1
|
$1.94 | |
WaterProof Vibration motor 7×24mm | 10 | 10個セット | $9.90 |
Si2301 pch-MOS FET | 4 | 50pcs | $0.38 |
IRLML2502 nch-MOS FET | 10 | 50pcs | $1.02 |
面実装LED 1206(EIAJ 3216)タイプ | 32 | 5色各100本 | $2.10 |
1N4007 アキシャルリード | 8 | 20本/パック | 100 |
1N4148 アキシャルリード | 32 | 50本/パック | 100 |
両面ノンスルーホール基板 100×150mm | 1 |
660
|
|
XH3P ベースポスト TOP型 | 25 | @10 | 250 |
抵抗 | 80 | $0.86 | |
電解コンデンサ | 2 | 20 | |
積層セラミックコンデンサ | 2 | 40 | |
ジャンパーピン+ピンヘッダー | 8 | 100+25 | 125 |
ICソケット18P |
1
|
40 | |
スイッチング電源アダプター 3.3V 2.2A | 1 | 580 | |
Φ2.1標準DCジャック | 1 | 30 |
表の価格は参考です。また価格には送料込みと無しの場合があります。
■使用する部品の説明
●マイコン
microchip社のPIC16F1827を使用します。マイコンにはポート数が16本もありますが、今回は1オクターブ12音(ドから次のドまで)をLEDへ出力するポートと、指の指示用振動子が10本で出力は22本が必要になります。また、音階切替用に入力ポートも必要なので、4×8=32本のダイナミック駆動としました。正確には出力が4×6=24本、入力が4×2=8本となります。
●両面基板
ダイナミック入出力回路の基板はマトリクス(縦横が交差する)で配線するのが効率がよいです。片面基板だと、ランド側はすずめっき線で一直線に配線が出来ますが、反対面の方はすずめっき線の交差する箇所を何らかの方法で固定する必要があります。これがなかなか面倒で、それではと両面基板を使用するのですが、スルーホールが生成され表裏のランドに導通がある基板で配線するとかなり大変で悲劇が生じます。テスターで誤配線を探し出すのが配線にかかった時間よりも長くなるという状態になりかねません。マトリクス配線には何と言ってもノンスルーホール基板(NPTH基板とかNTH基板と呼ぶ)が良好です。
残念なことにNTH基板は取り扱いが少ないようで、通販サイトでもあまり見かけません。秋月電子でもサイズが小さな基板(95×72mm)しかありません。海外の通販サイトではNPTH基板で検索してもかかりませんでした。別名の検索ワードがあるのかもしれません。折角なので国内のネットで探しまわると値段が手頃でサイズの大きなNTH基板を見つけました。
キーワードは、e-toolz ノンスルーホール ユニバーサル基板
今回は150mm×100mmの基板を使用しました。
●MOS FET
ダイナミック駆動のビットScan0〜3側のドライブにはPch-MOS FETを使用します。このFETのことをハイサイドスイッチと呼ぶのだそうです。振動子(モーター)の駆動電流はDC1.5Vの場合0.29Aという定格なのでPNP-Trでも問題ではないのですが、ちょっと使ってみたかったのです。Pch品は種類が多くないようです。Vishay社のSi2301という品番を選びました。出力ポートのbit0〜5のドライブにはNch-MOS FETを使用しています。VGS(th)が1.2VというIRF社のIRLM2502を選びました。どちらのFETもSOT23というパッケージのチップ品で米粒くらいの大きさなのですが、個人的にははんだ付けしやすいと思っています。(このことは後ほど説明します)
●振動子(モーター)
スマホに内蔵されているブルブルする部品です。振動子と呼んでいますが中身はモーターです。形状は指にフィットするように棒状のものを探しました。時分割で駆動して問題はないのかと思いましたが、影響は無いようです。動作させるうちにマッサージする装置と勘違いしそうになりました。
●OLED
128×64ドットの表示器が2ドルほどで売られているのを見つけました。OLED(有機EL)なのでバックライトが不要となります。GM009605と本体に表示がありますが調べてみるとSSD1306という型番で探すとマニュアルを見つけることが出来ました。I2C通信タイプなので配線本数が少なくて済みます。キャラクタジェネレータが内蔵されていないので自力で用意する必要がありますが、先人の皆様のおかげで簡単に実装することが出来ました。5×8ドットの文字は小さくて見にくいのでちょっと工夫が必要でした。
キーワードは、128×64ドット 0.96インチ OLED SSD1306 I2C Arduino
表示色は白、青、黄色などがありますが、白色が見やすいです。今回の回路には特に必要という訳ではないですが、デバッグ時にあると非常に便利と個人的には思っています。
●チップLED
鍵盤の上部につけるガイド用のLEDです。白鍵盤は白色LED、黒鍵盤は黄色LEDを使用しています(黒色LEDってあるのかな)。サイズは3216(EIAJ)なのではんだ付けは比較的楽です。左の画像はLEDの裏側です。三角のマークがカソードを示していると初めて知りました。
microchip社のPIC16F1827を使用します。マイコンにはポート数が16本もありますが、今回は1オクターブ12音(ドから次のドまで)をLEDへ出力するポートと、指の指示用振動子が10本で出力は22本が必要になります。また、音階切替用に入力ポートも必要なので、4×8=32本のダイナミック駆動としました。正確には出力が4×6=24本、入力が4×2=8本となります。
●両面基板
ダイナミック入出力回路の基板はマトリクス(縦横が交差する)で配線するのが効率がよいです。片面基板だと、ランド側はすずめっき線で一直線に配線が出来ますが、反対面の方はすずめっき線の交差する箇所を何らかの方法で固定する必要があります。これがなかなか面倒で、それではと両面基板を使用するのですが、スルーホールが生成され表裏のランドに導通がある基板で配線するとかなり大変で悲劇が生じます。テスターで誤配線を探し出すのが配線にかかった時間よりも長くなるという状態になりかねません。マトリクス配線には何と言ってもノンスルーホール基板(NPTH基板とかNTH基板と呼ぶ)が良好です。
残念なことにNTH基板は取り扱いが少ないようで、通販サイトでもあまり見かけません。秋月電子でもサイズが小さな基板(95×72mm)しかありません。海外の通販サイトではNPTH基板で検索してもかかりませんでした。別名の検索ワードがあるのかもしれません。折角なので国内のネットで探しまわると値段が手頃でサイズの大きなNTH基板を見つけました。
キーワードは、e-toolz ノンスルーホール ユニバーサル基板
今回は150mm×100mmの基板を使用しました。
●MOS FET
ダイナミック駆動のビットScan0〜3側のドライブにはPch-MOS FETを使用します。このFETのことをハイサイドスイッチと呼ぶのだそうです。振動子(モーター)の駆動電流はDC1.5Vの場合0.29Aという定格なのでPNP-Trでも問題ではないのですが、ちょっと使ってみたかったのです。Pch品は種類が多くないようです。Vishay社のSi2301という品番を選びました。出力ポートのbit0〜5のドライブにはNch-MOS FETを使用しています。VGS(th)が1.2VというIRF社のIRLM2502を選びました。どちらのFETもSOT23というパッケージのチップ品で米粒くらいの大きさなのですが、個人的にははんだ付けしやすいと思っています。(このことは後ほど説明します)
●振動子(モーター)
スマホに内蔵されているブルブルする部品です。振動子と呼んでいますが中身はモーターです。形状は指にフィットするように棒状のものを探しました。時分割で駆動して問題はないのかと思いましたが、影響は無いようです。動作させるうちにマッサージする装置と勘違いしそうになりました。
●OLED
128×64ドットの表示器が2ドルほどで売られているのを見つけました。OLED(有機EL)なのでバックライトが不要となります。GM009605と本体に表示がありますが調べてみるとSSD1306という型番で探すとマニュアルを見つけることが出来ました。I2C通信タイプなので配線本数が少なくて済みます。キャラクタジェネレータが内蔵されていないので自力で用意する必要がありますが、先人の皆様のおかげで簡単に実装することが出来ました。5×8ドットの文字は小さくて見にくいのでちょっと工夫が必要でした。
キーワードは、128×64ドット 0.96インチ OLED SSD1306 I2C Arduino
表示色は白、青、黄色などがありますが、白色が見やすいです。今回の回路には特に必要という訳ではないですが、デバッグ時にあると非常に便利と個人的には思っています。
●チップLED
鍵盤の上部につけるガイド用のLEDです。白鍵盤は白色LED、黒鍵盤は黄色LEDを使用しています(黒色LEDってあるのかな)。サイズは3216(EIAJ)なのではんだ付けは比較的楽です。左の画像はLEDの裏側です。三角のマークがカソードを示していると初めて知りました。
今日はここまでです。さぁ、ビールを飲みましょう。出張の帰りに、ビール缶とおかき、いかの燻製、ピーナッツなどが入ったおつまみセットや貝のひもなんかを買いこんで新幹線で飲んだ「せんべろ出張」がなつかしいです。
文中で使用した鍵盤のイラストは「来夢来人さんのピアノの鍵盤のフリー素材」より引用しました。
https://ecsozai.civillink.net/largeillust/suisougaku3832.html
手のイラストは「季節行事の無料イラスト集」より引用しました。
https://shinbunsozai.info/hand/
https://ecsozai.civillink.net/largeillust/suisougaku3832.html
手のイラストは「季節行事の無料イラスト集」より引用しました。
https://shinbunsozai.info/hand/
※このブログで書かれた内容によって生じた損害等の一切の責任を負いかねますので、予めご了承下さい。
ギターが優しく泣いている
イントロの素晴らしい曲というと、ジョージ・ハリスンの「マイ・スウィート・ロード」が思い浮かびます。このイントロをシンセサイザーで演奏してみても、どうもしっくりいきません。それはギターには弦と弦の間に時間差があるからではないかと思い当たりました。ではと、DX7でギターをシミュレートしてやろうと考えたのです。デジタルシンセサイザーのDX7は同時発音数が16音もあります。だったら、6弦ギターではなく、12弦ギターも可能なはずです。わりと簡単な動機ですが、ギターのストローク奏法を実現しようとしたのが発端です。
■12弦ギターのチューニング
6弦の場合のチューニングは、多分、万国共通で、6弦からEADGBEですが、12弦の場合はいろいろな方法があるようです。ネットで調べると、ビートルズが愛用していたリッケンバッカー社の12弦ギターはEeAaDdGgBBEEの調弦になっていたので、今回はこの調弦方法を使いました。(他のはeEaAdDgGBBEE だそうです)。なお、12弦は低い方から、6a、6b、5a、5bという風に表しています。
6弦の場合のチューニングは、多分、万国共通で、6弦からEADGBEですが、12弦の場合はいろいろな方法があるようです。ネットで調べると、ビートルズが愛用していたリッケンバッカー社の12弦ギターはEeAaDdGgBBEEの調弦になっていたので、今回はこの調弦方法を使いました。(他のはeEaAdDgGBBEE だそうです)。なお、12弦は低い方から、6a、6b、5a、5bという風に表しています。
ギターのフレット番号(下の0から15)とmidiノートとの関連
6a
E
|
6b
e
|
5a
A
|
5b
a
|
4a
D
|
4b
d
|
3a
G
|
3b
g
|
2a
B
|
2b
B
|
1a
E
|
1b
E
|
|
---|---|---|---|---|---|---|---|---|---|---|---|---|
0
|
40 | 52 | 45 | 57 | 50 | 62 | 55 | 67 | 59 | 59 | 64 | 64 |
1
|
41 | 53 | 46 | 58 | 51 |
63
|
56
|
68 | 60 | 60 | 65 | 65 |
2 | 42 | 54 | 47 | 59 | 52 | 64 | 57 | 69 | 61 | 61 | 66 | 66 |
3 | 43 | 55 | 48 | 60 | 53 | 65 | 58 | 70 | 62 | 62 | 67 | 67 |
4 | 44 | 56 | 49 | 61 | 54 | 66 | 59 | 71 | 63 | 63 | 68 | 68 |
5 | 45 | 57 | 50 | 62 | 55 | 67 | 60 | 72 | 64 | 64 | 69 | 69 |
6 | 46 | 58 | 51 | 63 | 56 | 68 | 61 | 73 | 65 | 65 | 70 | 70 |
7 | 47 | 59 | 52 | 64 | 57 | 69 | 62 | 74 | 66 | 66 | 71 | 71 |
8 | 48 | 60 | 53 | 65 | 58 | 70 | 63 | 75 | 67 | 67 | 72 | 72 |
9 | 49 | 61 | 54 | 66 | 59 | 71 | 64 | 76 | 68 | 68 | 73 | 73 |
10 | 50 | 62 | 55 | 67 | 60 | 72 | 65 | 77 | 69 | 69 | 74 | 74 |
11 | 51 | 63 | 56 | 68 | 61 | 73 | 66 | 78 | 70 | 70 | 75 | 75 |
12 | 52 | 64 | 57 | 69 | 62 | 74 | 67 | 79 | 71 | 71 | 76 | 76 |
13 | 53 | 65 | 58 | 70 | 63 | 75 | 68 | 80 | 72 | 72 | 77 | 77 |
14 | 54 | 66 | 59 | 71 | 64 | 76 | 69 | 81 | 73 | 73 | 78 | 78 |
15 | 55 | 67 | 60 | 72 | 65 | 77 | 70 | 82 | 74 | 74 | 79 | 79 |
主弦は大文字、副弦は小文字で示します。
フレット番号が0というのは開放弦を表しています。
ノート番号は10進数表記です。
このリストを眺めているうちに重大な事実に気付きました。構想段階では、ギターの弦、フレットと音階の関連をテーブルデータに持つ必要があると考えていましたが、単純に考えて、大きなテーブルのデータを持つ必要はなく、演算で対応ができるとわかってしまったのです。(ギター弾きならあたりまえですけど)、
MIDIノート = 開放弦のデータ+フレット番号
実際に作成したテーブルデータは、MIDIノート番号の他、周波数(82Hz〜)や、音階記号(C2〜)をフレットごとに載せたものだったので、この努力の結果はいつか役に立つ時がくるのでしょう。
フレット番号が0というのは開放弦を表しています。
ノート番号は10進数表記です。
このリストを眺めているうちに重大な事実に気付きました。構想段階では、ギターの弦、フレットと音階の関連をテーブルデータに持つ必要があると考えていましたが、単純に考えて、大きなテーブルのデータを持つ必要はなく、演算で対応ができるとわかってしまったのです。(ギター弾きならあたりまえですけど)、
MIDIノート = 開放弦のデータ+フレット番号
実際に作成したテーブルデータは、MIDIノート番号の他、周波数(82Hz〜)や、音階記号(C2〜)をフレットごとに載せたものだったので、この努力の結果はいつか役に立つ時がくるのでしょう。
■使用するマイコン
曲のイントロだけなので、せいぜい数小節。4分音符で10個程度といったところです。しかし、鍵盤では、弦と弦の間のミリセカンドの時間差を手で弾くというのはむずかしいので、マイコンでmidi信号を制御する方法を考えました。(DX7のキーボードを使わないって・・・・)
回路的にはmidi出力信号だけなので、PIC12F1840のような、小ピンのマイコンで間に合うのですが、デバッグが非常にやりにくいので、PIC16F1827を使用することに決めました。回路図は以下のようになります。嬉しいことに5V単一電源が使用できます。
曲のイントロだけなので、せいぜい数小節。4分音符で10個程度といったところです。しかし、鍵盤では、弦と弦の間のミリセカンドの時間差を手で弾くというのはむずかしいので、マイコンでmidi信号を制御する方法を考えました。(DX7のキーボードを使わないって・・・・)
回路的にはmidi出力信号だけなので、PIC12F1840のような、小ピンのマイコンで間に合うのですが、デバッグが非常にやりにくいので、PIC16F1827を使用することに決めました。回路図は以下のようになります。嬉しいことに5V単一電源が使用できます。
この回路図はKiCADで描きました。実は、使いまわしで合理化しています。マイコン名とポート名を変えただけです。
ダウンストロークの場合、6弦aから矢印の方向に時間差をもって発音することになります。リアルタイムmidiで発音する場合、通常はG時間(ゲートタイム)とS時間(ステップタイム)でノートON信号とノートOFF信号をコントロールしますが、弦と弦の間の時間差を表現するためにT時間をつけることにしました。通常、S時間(ステップタイム)はG時間(ゲートタイム)と同時にカウントを開始すると定義されているようですが、ここでは簡単のため、G時間終了後としています。
プログラムの流れとしては、
プログラムの流れとしては、
●曲コードリストから演奏するコードをとりだす。
●6弦aからT時間が経過後に、コードに相当するmidiデータを発音(ノートON)。
●G時間後に発音を止めます(ノートOFF)。
●無音の状態でS時間カウントする。
●6弦aから1弦bまでの12弦の音をそれぞれ発音する。
●6弦aからT時間が経過後に、コードに相当するmidiデータを発音(ノートON)。
●G時間後に発音を止めます(ノートOFF)。
●無音の状態でS時間カウントする。
●6弦aから1弦bまでの12弦の音をそれぞれ発音する。
流れはとてもシンプルなのですが、各弦が3つのタイマーを持つことになるので、12*3=36のタイマーを制御することになりました。
■プログラムの動作確認方法
プログラム本体の説明をする前になんなのですが、どう確認するのかが問題です。2〜3音ならば聴いてわからないこともないのですが、12弦となればさすがに難しいです。
プログラム作成時に使用しているロジック・アナライザが重宝しました。このロジアナは波形で表示することはもちろん、midiプロトコルを理解して測定した結果を時間データ付きで出力してくれるので、msの時間差を確認することが出来ました。
プログラム本体の説明をする前になんなのですが、どう確認するのかが問題です。2〜3音ならば聴いてわからないこともないのですが、12弦となればさすがに難しいです。
プログラム作成時に使用しているロジック・アナライザが重宝しました。このロジアナは波形で表示することはもちろん、midiプロトコルを理解して測定した結果を時間データ付きで出力してくれるので、msの時間差を確認することが出来ました。
■便利グッズ
演奏に使用するタイマーが36個もありますので、ソフトウェアタイマーで実現することになります。といって、用意する変数が8ビット(または16ビット)サイズで36個もあると、マイコンのメモリーが不足してしまいます。仕方がないのでビット単位で制御することにしました。(実は大好きなのですけど)。こういうビット演算をする時はヒューレットパッカードのHP-16Cが役に立ちます。HEX(16進)からBIN(2進)のほとんどの演算ができるので重宝します。例えば、64ビットサイズの16進数の1の補数を2進数で表示したいなどが簡単にできます。
演奏に使用するタイマーが36個もありますので、ソフトウェアタイマーで実現することになります。といって、用意する変数が8ビット(または16ビット)サイズで36個もあると、マイコンのメモリーが不足してしまいます。仕方がないのでビット単位で制御することにしました。(実は大好きなのですけど)。こういうビット演算をする時はヒューレットパッカードのHP-16Cが役に立ちます。HEX(16進)からBIN(2進)のほとんどの演算ができるので重宝します。例えば、64ビットサイズの16進数の1の補数を2進数で表示したいなどが簡単にできます。
残念なことに本家のHP社のHP-16Cは廃版になっています。ネット中を探していたら、アンドロイド端末のアプリで、2社から出されているのを発見しました。その他、windowsのエミュレータでも存在しています。リアルの現物ではスイスの会社(スイスフランの支払いだから)がほぼ同一のものを発表されているので、今のが破損したら購入を検討したいです。余談ですが、HP社のHP-16Cはヤフオク、ebayなどのオークションでたまに見かけます。値段も高すぎず、妥当なところです。レア物という考え方ならば、本体というよりも付属のマニュアルの方でしょう。実は・・・・。ネットで探すとマニュアルもpdfファイルで見ることができるようです。
今日の作業はここまで。ビール(もとい、新ジャンル)が10月から値上げになるそうです。だもんで、飲みだめの日々に乾杯。
※このブログで書かれた内容によって生じた損害等の一切の責任を負いかねますので、予めご了承下さい。
ギターが優しく泣いている 3 (プログラム編)
変数とテーブルの説明だけで何かわからなくなってきました。時間を置くと本人が簡単に別人に変身してしまうので、開発時に作ったドキュメント(特になにげないメモ)を保存しておくことは本当に重要だと痛感します。それでも、めげずにプログラム本体の説明をします。
//*************************************************************
// 12弦ギターのシミュレート
// MPLAB Ver.5.25
// XC8 Ver2.10
//*************************************************************
uint8_t t_no;
uint8_t g_no;
uint8_t s_no;
uint8_t arpe_no;
uint16_t tab; //1bit:6(12)弦・・・12bit:1弦
uint16_t pattern[6];
uint8_t point;
uint8_t dnib;
uint8_t unib;
// CONFIG1
#pragma config IESO = ON
#pragma config BOREN = ON
#pragma config PWRTE = OFF
#pragma config FOSC = INTOSC
#pragma config FCMEN = OFF
#pragma config MCLRE = ON
#pragma config CP = OFF
#pragma config CPD = OFF
#pragma config WDTE = OFF
#pragma config CLKOUTEN = ON
// CONFIG2
#pragma config WRT = OFF
#pragma config LVP = OFF
#pragma config STVREN = ON
#pragma config PLLEN = OFF
#pragma config BORV = LO
#define _XTAL_FREQ 32000000
#define ON 1
#define OFF 0
#define EF 0xF0 // EndFileを示す識別子
#define NP 0xF1 // NotPlayを示す識別子
#define T0COUT 206 // Timer0 100us:206
#define PRESCALE 4 // 100μs:4
//*************************************************************
//割り込みルーチン
//*************************************************************
void __interrupt () InterTimer( void ){
//結局、タイマ0割り込みは使用しませんでした
if(INTCONbits.TMR0IF == 1){
TMR0 = T0COUT; //100us
if(Task_SW >= 3) Task_SW = 0;
Task_SW++;
INTCONbits.TMR0IF = 0;
}
//タイマ1割り込み
if(PIR1bits.TMR1IF == 1){
TMR1H = 224; //1ms timer
TMR1L = 192;
//T時間のカウントを行なう
for( tcount = 0; tcount < 12; tcount++ ){
// T bits 01:ON
if((ST[tcount] & 0b11111111) == 0b00000100){
timer[tcount]++;
if(timer[tcount] > tmrT[tcount]) {
ST[tcount] &= 0b11110011;
// T bits 10 up
ST[tcount] |= 0b00001000;
timer[tcount] = 0;
}
}
}
//G時間のカウントを行なう
for( gcount = 0; gcount < 12; gcount++ ){
//G bits 01:ON
if((ST[gcount] & 0b11111111) == 0b00011101){
timer[gcount]++;
if(timer[gcount] > tmrG[gcount]) {
ST[gcount] &= 0b11001111;
// 01:up
ST[gcount] |= 0b00100000;
timer[gcount] = 0;
}
}
}
//S時間のカウントを行なう
for(scount = 0; scount < 12; scount++ ){
// S bits 01:ON
if((ST[scount] & 0b11111111) == 0b01111110){
timer[scount]++;
if(timer[scount] > tmrS[scount]) {
ST[scount] &= 0b00111111;
// 01:up
ST[scount] |= 0b10000000;
timer[scount] = 0;
}
}
}
PIR1bits.TMR1IF = 0;
}
}
//*************************************************************
// メインルーチン
//*************************************************************
void main(void){
OSCCONbits.SPLLEN = 1; // PLL X4
OSCCONbits.IRCF = 0b1110; // IRCF 8MHz_HF
OSCCONbits.SCS = 0; // INTOSC
OSCSTATbits.T1OSCR = 0; // disabled
OSCSTATbits.PLLR = 0; // 4XPLL disable
OSCSTATbits.OSTS = 0; // intosc
OSCSTATbits.HFIOFR = 0; // disabled
OSCSTATbits.HFIOFL = 0; // not0.2percent_acc
OSCSTATbits.MFIOFR = 0; // disabled
OSCSTATbits.LFIOFR = 0; // dsiabled
OSCSTATbits.HFIOFS = 0; // not0.5percent_acc
OSCTUNEbits.TUN = 0;// factory-calibrated frequency
// デジタルIOのみ
ANSELA = 0b00000000 ;
// RA1:out 他は適当
TRISA = 0b00111100 ;
PORTA = 0b00000000 ; // all L
// すべてデジタルIO
// MIDI系統のみ規定。他は適当。
ANSELB = 0b00000000 ;
TRISB = 0b00001110 ;
WPUB = 0b00000000 ;
PORTB = 0b00000000 ;
APFCON1bits.TXCKSEL = 1 ; //1:RB5 0:RB2
APFCON0bits.RXDTSEL = 1 ; //1:RB2 0:RB1
//Timer0 init(今回未使用)
prescaler = PRESCALE;
if (prescaler == 0) {
OPTION_REGbits.PSA = 1;
} else {
OPTION_REGbits.PSA = 0;
OPTION_REGbits.PS = prescaler - 1;
}
OPTION_REGbits.TMR0CS = 0; //fosc/4
OPTION_REGbits.TMR0SE = 0;
INTCONbits.TMR0IE = 1;
//INTCONbits.PEIE = 1;
//Timer1 init
T1CONbits.TMR1ON = 1; //有効にする
T1CONbits.TMR1CS = 0; //fosc/4
T1CONbits.T1CKPS = 0; //prescaler 0:2^0
TMR1H = 224; //1ms timer
TMR1L = 192;
PIE1bits.TMR1IE = 1;
INTCONbits.PEIE = 1;
//UART
TXSTAbits.CSRC = 0; // don't care
TXSTAbits.TX9 = 0; // 8 bit
TXSTAbits.TXEN = 1; // enable
TXSTAbits.SYNC = 0; // 非同期モード
TXSTAbits.SENDB = 0; // break
TXSTAbits.BRGH = 0; // Low
TXSTAbits.TRMT = 0; // TSR full
TXSTAbits.TX9D = 0;
//RCSTA
RCSTAbits.SPEN = 1; // serial port enable
RCSTAbits.RX9 = 0; // 8bit
RCSTAbits.SREN = 0; // don't care
RCSTAbits.CREN = 1; // enable
RCSTAbits.ADDEN = 0; // don't care
RCSTAbits.FERR = 0; // disable
RCSTAbits.OERR = 0; // over run error
RCSTAbits.RX9D = 0;
BAUDCON = 0; //BRG16 0
SPBRG = 15; // ボーレートを31250bpsに設定
INTCONbits.GIE = 1;
// 9n : midi channel = 0;
midi_ch = 0b10010000;
//ここから主動作のルーチンです。大まかな処理を説明すると
// ①P[ ][ ] から演奏するギターコード、M[ ]で奏法を選択
// ②Upストロークの場合は発音する順番を入れ替え
// ③発音時間は奏法により異なる。
while(1){
uint8_t i;
uint8_t j;
if( P[0][measure] != EF ){
// Level= 0:OFF 1:Down 2:Up 3-5:Arpe*
Level = M[measure] ;
bass_flg = OFF;
for(i = 0 ; i < 12 ;i++ ){
// Upストロークの時
if(Level == 2){
//発音順番を変更
j = ~i - 0b11110100;
F[i] = code[ P[0][measure] ][j] ;
N[i] = open[j];
} else { //Down,Arpe*の時
F[i] = code[ P[0][measure] ][i] ;
N[i] = open[i];
}
// 発音タイミング生成
if( F[i] != NP ){
string |= ( 1 << (uint16_t) i);
if(bass_flg == OFF){
bass_flg = ON;
//root音(bass)の決定
bass = i;
}
}
ST[i] = 0;
}
switch( Level ){
case 1: // DownとUp
case 2:
Stroke();
break;
case 3: // Arpe1
arpe_cnt = 6;
Arpe1();
break;
case 4: // Arpe2
Arpe2();
break;
case 5: // Arpe3
Arpe3();
break;
default:
break;
}
measure++;
} else{
measure = 0;
}
}
}
//*************************************************************
//各演奏パターンのルーチン
//*************************************************************
void Stroke(void){
// ①使用するテーブルはP[ ] [ ] と M[ ]です(Arpeとは異なる)。
// ②ST[ ]のフラグにより、T→G→Sの各12弦分のタイマーを作動。
// ③タイマー値を設定し、割り込みルーチンでカウントする。
// ④時間差をつけて12弦分の発音をおこなう
while( string ){ //すべての弦を演奏した?
for (t_no = 0;t_no < 12 ;t_no++){
if((ST[t_no] & 0b11110011) == 0b00000000){
//OFF?
if((ST[t_no] & 0b11111111) == 0b00000000){
//tmrT_sw = ON
ST[t_no] |= 0b00000100;
tmrT[t_no] = Tbl[t_no];
}
//Tタイマーアップ?
if((ST[t_no] & 0b11111111) == 0b00001000){
if(F[t_no] != NP){
//N[]フレットの音階:12(6)弦目から記入
note = F[t_no] + N[t_no] + capo;
while(TXIF == 0); // 送信可能?
TXREG = midi_ch;// ノートオン送信
while(TXIF == 0); // 送信可能?
TXREG = note; // 送信する
while(TXIF == 0); // 送信可能?
TXREG = V[t_no]; // 送信する
}
ST[t_no] &= 0b11111100;
//Ttimerが完了
ST[t_no] |= 0b00001100;
//tmrG_sw = ここではOFF;Gset 00→01
ST[t_no] |= 0b00000001;
}
}
}
// G
for (g_no = 0;g_no < 12 ;g_no++){
if((ST[g_no] & 0b11001111 ) == 0b00001101){
//OFF?
if((ST[g_no] & 0b11111111) == 0b00001101){
//tmrG_sw = ON;
ST[g_no] |= 0b00010000;
tmrG[g_no] = Tbl[tmr];
}
//Gタイマーアップ?
if((ST[g_no] & 0b11111111) == 0b00101101){
//g_no番目の弦の鳴動OFF(midiへOFF出力)
//Gtimerが完了
ST[g_no] |= 0b00110000;
if(F[g_no] != NP){
note = F[g_no] + N[g_no];
while(TXIF == 0); // 送信可能?
TXREG = midi_ch;// ノートオン送信
while(TXIF == 0); // 送信可能?
TXREG = note; // 送信する
while(TXIF == 0); // 送信可能?
TXREG = 0; // string.V[g_no] = 0
}
ST[g_no] &= 0b11111100;
//tmrS_sw = ここではOFF;Sset→10
ST[g_no] |= 0b00000010;
}
}
}
for (s_no = 0;s_no < 12 ;s_no++){ // S
if(( ST[s_no] & 0b00111111 ) == 0b00111110){
//OFF?
if((ST[s_no] & 0b11111111) == 0b00111110){
// tmrS_sw = ON;
ST[s_no] |= 0b01000000;
// 0ms
tmrG[g_no] = Tbl[0];
}
//Sタイマーアップ?
if((ST[s_no] & 0b11111111) == 0b10111110){
//Stimerが完了;Tset→11
ST[s_no] |= 0b11111111;
string &= ~(1 << s_no);
//13-16ビット目をマスク
string &= 0b0000111111111111;
}
}
}
}
}
//*************************************************************
void Arpe1(void){
// ①使用するテーブルはP[ ] [ ]とM[ ](Strokeとは異なる)と、CT[ ]。
// ②ST[ ]のフラグにより、T→G→Sの各12弦分のタイマーを作動。
// ③タイマー値を設定し、割り込みルーチンでカウントする。
// ④CT[ ]で示す時間差で各弦を発音する。
//すべての弦を演奏した?
while( string ){
//00:T 01:G 10:S 11:END
for (t_no = 0;t_no < 12 ;t_no++){ // T
if((ST[t_no] & 0b11110011) == 0b00000000){
//OFF?
if((ST[t_no] & 0b11111111) == 0b00000000){
//tmrT_sw = ON
ST[t_no] |= 0b00000100;
tmrT[t_no] = Tbl[ CT[t_no] ];
}
//Tタイマーアップ?
if((ST[t_no] & 0b11111111) == 0b00001000){
//t_no番目の弦を鳴動(midiへ出力)
if(F[t_no] != NP){
//N[]フレットの音階:12(6)弦目から
note = F[t_no] + N[t_no] + capo;
while(TXIF == 0); // 送信可能?
TXREG = midi_ch;// ノートオン送信
while(TXIF == 0); // 送信可能?
TXREG = note; // 送信する
while(TXIF == 0); // 送信可能?
TXREG = V[t_no]; // 送信する
}
ST[t_no] &= 0b11111100;
//Ttimerが完了
ST[t_no] |= 0b00001100;
//tmrG_sw = ここではOFF;Gset 00→01
ST[t_no] |= 0b00000001;
}
}
}
}
// G
for (g_no = 0;g_no < 12 ;g_no++){
if((ST[g_no] & 0b11001111 ) == 0b00001101){
//OFF?
if((ST[g_no] & 0b11111111) == 0b00001101){
//tmrG_sw = ON;
ST[g_no] |= 0b00010000;
//G時間は各弦共通
tmrG[g_no] = Tbl[ 14 ];
}
//Gタイマーアップ?
if((ST[g_no] & 0b11111111) == 0b00101101){
//g_no番目の弦の鳴動OFF(midiへOFF出力)
ST[g_no] |= 0b00110000; //Gtimerが完了
if(F[g_no] != NP){
note = F[g_no] + N[g_no];
while(TXIF == 0); // 送信可能?
TXREG = midi_ch; // ノートオン送信
while(TXIF == 0); // 送信可能?
TXREG = note; // 送信する
while(TXIF == 0); // 送信可能?
TXREG = 0; // string.V[g_no] = 0
}
ST[g_no] &= 0b11111100;
//tmrS_sw = ここではOFF;Sset→10
ST[g_no] |= 0b00000010;
}
}
}
// S
for (s_no = 0;s_no < 12 ;s_no++){
if(( ST[s_no] & 0b00111111 ) == 0b00111110){
//OFF?
if((ST[s_no] & 0b11111111) == 0b00111110){
//tmrS_sw = ON;
ST[s_no] |= 0b01000000;
//S時間は無しに設定
tmrS[s_no] = Tbl[0];
}
//Sタイマーアップ?
if((ST[s_no] & 0b11111111) == 0b10111110){
arpe_cnt--;
if(arpe_cnt == 0){
//if(ST[s_no] == 0b11111111)
string &= ~(1 << s_no);
//13-16ビット目をマスク
string &= 0b0000111111111111;
} else {
//Stimerが完了;Tset→11
ST[s_no] = 0b00000000;
}
}
}
}
}
//*************************************************************
void Arpe2(void){
// ①使用するテーブルはP[ ] [ ](Arpe2の)とTap[ ]。
// ②ST[ ]のフラグにより、T→G→Sの各12弦分のタイマー作動。
// ③タイマー値を設定し、割り込みルーチンでカウントする。
// ④メモリ節約のため、Tap[ ]でG時間とS時間を規定している。
// 発音タイミング生成
for( arpe_no = 0 ; arpe_no < 12 ; arpe_no++ ){
F[arpe_no] = P[arpe_no][measure];
if( F[arpe_no] != NP ){
string |= ( 1 << (uint16_t) arpe_no);
N[arpe_no] = open[arpe_no];
ST[arpe_no] = 0;
dnib = Tap[arpe_no][measure] & 0b00001111;
unib = (Tap[arpe_no][measure] >> 4) & 0b00001111;
tmrG[arpe_no] = Tbl[dnib];
tmrS[arpe_no] = Tbl[unib];
}
}
tab = string;
for (t_no = 0;t_no < 12 ;t_no++){
//演奏するフレットか?
if((tab >> t_no) & 1){
if(F[t_no] != NP){
//N[]フレットの音階:12(6)弦目から記入
note = F[t_no] + N[t_no] + capo;
while(TXIF == 0); // 送信可能?
TXREG = midi_ch; // ノートオン送信
while(TXIF == 0); // 送信可能?
TXREG = note; // 送信する
while(TXIF == 0); // 送信可能?
TXREG = V[t_no]; // 送信する
}
//Tタイマーは処理済みにする
ST[t_no] = 0b00001101;
}
}
while(string){
for (g_no = 0;g_no < 12 ;g_no++){
arpe_cnt = (uint16_t) g_no;
//演奏するフレットか?
if((tab >> arpe_cnt) & 1){
//G
if((ST[g_no] & 0b11001111) == 0b00001101){
//OFF?
if((ST[g_no] & 0b11111111) == 0b00001101){
//tmrG_sw = ON
ST[g_no] |= 0b00010000;
}
//Gタイマーアップ?
if((ST[g_no] & 0b11111111) == 0b00101101){
ST[g_no] |= 0b00110000;
if(F[g_no] != NP){
//N[]フレットの音階:12(6)弦目から記入
note = F[g_no] + N[g_no] + capo;
while(TXIF == 0); // 送信可能?
TXREG = midi_ch; // ノートオフ送信
while(TXIF == 0); // 送信可能?
TXREG = note; // 送信する
while(TXIF == 0); // 送信可能?
TXREG = 0; // 送信する
}
ST[g_no] &= 0b11111100;
//tmrS_sw = ここではOFF;Sset 01→10
ST[g_no] |= 0b00000010;
}
}
}
}
// S
for (s_no = 0;s_no < 12 ;s_no++){
arpe_cnt = (uint16_t) s_no ;
//演奏するフレットか?
if((tab >> arpe_cnt) & 1){
if(( ST[s_no] & 0b00111111 ) == 0b00111110){
//OFF?
if((ST[s_no] & 0b11111111) == 0b00111110){
//tmrS_sw = ON;
ST[s_no] |= 0b01000000;
}
//Sタイマーアップ?
if((ST[s_no] & 0b11111111) == 0b10111110){
//Stimerが完了;Tset→11
ST[s_no] |= 0b11111111;
string &= ~(1 << s_no);
//13-16ビット目をマスク
string &= 0b0000111111111111;
}
}
}
}
}
}
//*************************************************************
void Arpe3(void){
// ①使用するテーブルはP[ ] [ ]とCT[ ]とpattern[ ]です。
// ②ST[ ]のフラグにより、T→G→Sの各12弦分のタイマーを作動。
// ③タイマー値を設定し、割り込みルーチンでカウントする。
// ④pattern[ ]で1ピッキングあたりの弦を順次に発音する。
uint8_t t_no;
uint8_t g_no;
uint8_t s_no;
uint8_t arpe_no;
uint16_t tab; //1bit目:6(12)弦・・・12bit目:1弦
//アルペジオのroot音の探索と順番カウンタ
for(arpe_no = 0;arpe_no < 6; arpe_no++){
tab = pattern[arpe_no];
//4(7)弦から6(12)弦でroot音は?
if( tab & 0b0000000000111111){
tab &= 0b1111111111000000;
//12弦であること
tab |= (uint16_t) ( 0b000000011 << bass );
}
string = tab;
tmr = CT[arpe_no] ; // TmrG[tmr]
for (t_no = 0;t_no < 12 ;t_no++){
//演奏するフレットか?
if((tab >> t_no) & 1){
if(F[t_no] != NP){
//N[]フレットの音階:12(6)弦目から記入
note = F[t_no] + N[t_no] + capo;
while(TXIF == 0); // 送信可能?
TXREG = midi_ch; // ノートオン送信
while(TXIF == 0); // 送信可能?
TXREG = note; // 送信する
while(TXIF == 0); // 送信可能?
TXREG = V[t_no]; // 送信する
}
//Tタイマーは処理済みとする
ST[t_no] = 0b00001101;
}
}
while(string){
for (g_no = 0;g_no < 12 ;g_no++){
arpe_cnt = (uint16_t) g_no;
//演奏するフレットか?
if((tab >> arpe_cnt) & 1){
//G
if((ST[g_no] & 0b11001111) == 0b00001101){
//OFF?
if((ST[g_no] & 0b11111111) == 0b00001101){
//tmrG_sw = ON
ST[g_no] |= 0b00010000;
tmrG[g_no] = Tbl[tmr]; //
}
//Gタイマーアップ?
if((ST[g_no] & 0b11111111) == 0b00101101){
ST[g_no] |= 0b00110000;
if(F[g_no] != NP){
//N[]フレットの音階:12(6)弦目から記入
note = F[g_no] + N[g_no] + capo;
while(TXIF == 0); // 送信可能?
TXREG = midi_ch; // ノートオフ送信
while(TXIF == 0); // 送信可能?
TXREG = note; // 送信する
while(TXIF == 0); // 送信可能?
TXREG = 0; // 送信する
}
ST[g_no] &= 0b11111100;
//tmrS_sw = ここではOFF;Sset 01→10
ST[g_no] |= 0b00000010;
}
}
}
}
for (s_no = 0;s_no < 12 ;s_no++){ // S
arpe_cnt = (uint16_t) s_no ;
//演奏するフレットか?
if((tab >> arpe_cnt) & 1){
if(( ST[s_no] & 0b00111111 ) == 0b00111110){
//OFF?
if((ST[s_no] & 0b11111111) == 0b00111110){
//tmrS_sw = ON;
ST[s_no] |= 0b01000000;
}
//Sタイマーアップ?
if((ST[s_no] & 0b11111111) == 0b10111110){
//Stimerが完了;Tset→11
ST[s_no] |= 0b11111111;
string &= ~(1 << s_no);
//13-16ビット目をマスク
string &= 0b0000111111111111;
}
}
}
}
}
}
}
//*************************************************************■実は・・・・・
YouTubeの動画を見ていると、アップ/ダウン・ストロークの演奏の後にスライド・バーによる演奏が続いていたのでした。これもイントロに含まれますね。この部分のTAB譜を見ると、奇妙な記号が出てきます。これがスライド・ギター奏法をあらわすらしいです。今回はいろいろ試してみたのですが、同じような効果が実現できませんでした。ギターの音色も変えたほうが良さそうなので、しばらく研究してみることにします。
YouTubeの動画を見ていると、アップ/ダウン・ストロークの演奏の後にスライド・バーによる演奏が続いていたのでした。これもイントロに含まれますね。この部分のTAB譜を見ると、奇妙な記号が出てきます。これがスライド・ギター奏法をあらわすらしいです。今回はいろいろ試してみたのですが、同じような効果が実現できませんでした。ギターの音色も変えたほうが良さそうなので、しばらく研究してみることにします。
キーワードは、スライドバー 、 ボトルネック
余談ですが、スライド・バーをお酒の瓶のくびを切って自作したことがあります。あれはどこに行ったのだろう。でも、最近は専用のスライド・バーが安く買えるみたいです。
■感想
アップ・ダウンストロークの演奏ですが、DX7に接続してみたところ、弦と弦の間の時間差や12弦ギターの感じは少しはでているように思いました。ストロークの時間(G時間)を適切に調整して、S時間は0にしてやると、いい線まではいくかなと思いました。著作権の問題で、コード進行や実際の演奏音は出せませんが、各時間をリアルタイムに調整できればもっと楽しめるでしょう。
そして、アルペジオ演奏(Arpe1〜Arpe3)ですが、どれもこれも一長一短があるようです。おすすめしないのはArpe2グループで、参考のデータを見て「これは・・・」と感じたら多分正解です。普通の楽譜がコスパ最高と思えるでしょう。指使いをリアルに再現できるのはArpe3のほうです。何よりピッキングのタイミングが重要みたいです。プログラムを後から付け足して行く方法で仕上げたので、テーブルのデータがわかりづらくなってしまいました。
最後にもっとおもしろいようにするために以下のように改善を図っていこうと思います。
そして、アルペジオ演奏(Arpe1〜Arpe3)ですが、どれもこれも一長一短があるようです。おすすめしないのはArpe2グループで、参考のデータを見て「これは・・・」と感じたら多分正解です。普通の楽譜がコスパ最高と思えるでしょう。指使いをリアルに再現できるのはArpe3のほうです。何よりピッキングのタイミングが重要みたいです。プログラムを後から付け足して行く方法で仕上げたので、テーブルのデータがわかりづらくなってしまいました。
最後にもっとおもしろいようにするために以下のように改善を図っていこうと思います。
●変化を目で見えるようにする
●時間(T,G,S)・ノート信号・Volumeはロータリーエンコーダー(RE)などで操作して、連続で変化できるようにすること
●コード名やテンポ、ストロークなどはわかりやすく指定できること。
やはりピアノロールのようなグラフィカル画面でタイミングやノートを変化出来るようにしたほうが良い。
●時間(T,G,S)・ノート信号・Volumeはロータリーエンコーダー(RE)などで操作して、連続で変化できるようにすること
●コード名やテンポ、ストロークなどはわかりやすく指定できること。
やはりピアノロールのようなグラフィカル画面でタイミングやノートを変化出来るようにしたほうが良い。
今日の作業はここまで。
食材店で眺めていたらコアントローの54度というのを見つけた。アブサン化したのと思ったら製菓用のらしかった。香りが大好きなコアントロ・サワーで乾杯。
**** 回路図が非公開になっていたので修正しました。*****
※このブログで書かれた内容によって生じた損害等の一切の責任を負いかねますので、予めご了承下さい。
ギターが優しく泣いている2 (ソフト設計編)
引き続き、12弦ギターをシミュレートするプログラムを説明します。
■プログラムの設計
●ダウン・ストロークとアップ・ストローク
当たり前なのですが、ギターの演奏にはダウン・ストローク(上から下へ弾く)だけでなく、アップ・ストローク(下から上へ弾く)も必要です。そのアップ・ストロークは単純に1弦から6弦の方向に発音すればいいと考えていました。プログラムの構築がほぼ出来た段階でこの事実に気付いたので、実際にmidi出力してみると、おかしな音になってしまいました。各弦がT時間のズレを持っているので、後から発音するはずの音が先に発音するという不思議なものになったのでした。
●ギターのコード表
ギターを演奏するときに必ず目にするのがコード表です。これをリストのテーブルに落としこむ時に、コード表はTAB譜にも見えるなぁと思いました。TAB譜については詳しい人に教えてもらうとして、
●ダウン・ストロークとアップ・ストローク
当たり前なのですが、ギターの演奏にはダウン・ストローク(上から下へ弾く)だけでなく、アップ・ストローク(下から上へ弾く)も必要です。そのアップ・ストロークは単純に1弦から6弦の方向に発音すればいいと考えていました。プログラムの構築がほぼ出来た段階でこの事実に気付いたので、実際にmidi出力してみると、おかしな音になってしまいました。各弦がT時間のズレを持っているので、後から発音するはずの音が先に発音するという不思議なものになったのでした。
●ギターのコード表
ギターを演奏するときに必ず目にするのがコード表です。これをリストのテーブルに落としこむ時に、コード表はTAB譜にも見えるなぁと思いました。TAB譜については詳しい人に教えてもらうとして、
キーワードは、ギター TAB譜
今回作成したプログラムも、T時間を変えてやると弦の発音順番を変えることが出来るはずで、それならばアップ/ダウン・ストロークだけでなく、アルペジオ演奏もシミュレート出来るのでは思いつきました。さらにタイミングさえ揃えれば(みんな大好き)スリーフィンガーピッキングも思いのままさと発展しました。
キーワードは、アルペジオ演奏、スリーフィンガーピッキング
プログラムはそれぞれコンセプトが違っているので(つまり、作っているうちに)別々のものになりました。このブログは備忘録を兼ねていますので、それぞれの方法のそのままプログラムリストに出しています。
●ソフトウェア・タイマーについて
T時間、G時間、S時間のタイマーは合計36個あります。といっても各々直列的に動作するので実際にカウント動作しているのは12個です。これらのタイマーが扱う時間は数msから数秒と幅広いものになります。割り込み周期を1msにしたので、変数は16ビットサイズを使うと1msから65535msを計数できます。16ビットサイズの変数を12個も用意するのはマイコンの容量ではかなりしんどいところなので、タイマー制御はビット操作で行なうことにしました。プログラムを作成する時はフローチャートは作らず、フラグの機能割当てリストだけを見ながら、いきあたりばったりなので、フラグのビットがずれたりして、おかしな動作を引き起こしたりすると、デバッグ作業はほんとうに大変です。とはいえ、ビット演算は好きなんです。パズルを解くような感覚があります。余談になりますが、アセンブラであれこれ作業をしていた頃、アキュムレータの内容をゼロにしようと苦心していたのが、「XOR A」の一行で出来ると知った時にはとても感激しました。
●タイマー割り込み
今回作成したプログラムも、T時間を変えてやると弦の発音順番を変えることが出来るはずで、それならばアップ/ダウン・ストロークだけでなく、アルペジオ演奏もシミュレート出来るのでは思いつきました。さらにタイミングさえ揃えれば(みんな大好き)スリーフィンガーピッキングも思いのままさと発展しました。
キーワードは、アルペジオ演奏、スリーフィンガーピッキング
プログラムはそれぞれコンセプトが違っているので(つまり、作っているうちに)別々のものになりました。このブログは備忘録を兼ねていますので、それぞれの方法のそのままプログラムリストに出しています。
●ソフトウェア・タイマーについて
T時間、G時間、S時間のタイマーは合計36個あります。といっても各々直列的に動作するので実際にカウント動作しているのは12個です。これらのタイマーが扱う時間は数msから数秒と幅広いものになります。割り込み周期を1msにしたので、変数は16ビットサイズを使うと1msから65535msを計数できます。16ビットサイズの変数を12個も用意するのはマイコンの容量ではかなりしんどいところなので、タイマー制御はビット操作で行なうことにしました。プログラムを作成する時はフローチャートは作らず、フラグの機能割当てリストだけを見ながら、いきあたりばったりなので、フラグのビットがずれたりして、おかしな動作を引き起こしたりすると、デバッグ作業はほんとうに大変です。とはいえ、ビット演算は好きなんです。パズルを解くような感覚があります。余談になりますが、アセンブラであれこれ作業をしていた頃、アキュムレータの内容をゼロにしようと苦心していたのが、「XOR A」の一行で出来ると知った時にはとても感激しました。
●タイマー割り込み
割り込み時間は1msとして、T時間、G時間から16ビットのtimer1を使用することにしました。 弦と弦の時間差を扱うT時間は正確なところ不明です。 Youtubeの演奏風景の動画や実際の曲を波形で観測したりして、おおよその時間を割当てました。 1回のストロークで90msなので6等分して15ms。 12弦なので奇数弦と偶数弦の時間差は5msとすることにしました。 割り込みルーチン内でのタイマーカウントはT[]→G[]→S[]の順で行っていて、12個の配列要素ごとにカウントします。 ルーチン内では割り込みSWが入っていればカウントし、設定値になればカウントアップフラグを立てています。
■使用している主な変数(フラグ)の説明
ST[] :ソフトウェアタイマーの状態を管理しています
bit 76 54 32 10 S G T L 00:T 01:G 10:S 11:END L 00:OFF 01:ON 10:UP 11:Next(END) L 00:OFF 01:ON 10:UP 11:Next(END) L 00:OFF 01:ON 10:UP 11:Next(END) F[] :ギターのフレットで、F[]=0なら開放弦を意味しています N[] :開放弦でのmidi音階 V[] :midiのVelocity値 M[] :↓(Down) ↑ (Up) やアルペジオなどの演奏モード
0:OFF 1:Down 2:Up 3:Arpe1 4:Arpe2 5:Arpe3
tmrT,tmrG,tmrS :タイマー設定値
timer :割り込み内タイマー
measure :音符の順番
capo :ギターのカポタスト(カポスタトと覚えていた)
arpe_cnt :アルペジオの回数
EF 0xF0 // EndFile
NP 0xF1 // NotPlay
●共通テーブルの説明
const uint8_t code[18][12] = { //ギターコード表
// 6a b 5a b 4a b 3a b 2a b 1a b
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, // 0:開放弦
{ NP, NP, 3, 3, 2, 2, 0, 0, 1, 1, 0, 0, }, // 1:C
{ NP, NP, 3, 3, 2, 2, 0, 0, 0, 0, 0, 0, }, // 2:CM7
{ NP, NP, 4, 4, 6, 6, 4, 4, 6, 6, 4, 4, }, // 3:C#7
{ NP, NP, NP, NP, 0, 0, 2, 2, 3, 3, 2, 2, }, // 4:D
{ NP, NP, NP, NP, 0, 0, 2, 2, 1, 1, 2, 2, }, // 5:D7
{ NP, NP, NP, NP, 0, 0, 2, 2, 3, 3, 1, 1, }, // 6:Dm
{ 0, 0, 2, 2, 2, 2, 1, 1, 0, 0, 0, 0, }, // 7:E
{ 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, }, // 8:Em
{ 1, 1, 3, 3, 3, 3, 2, 2, 1, 1, 1, 1, }, // 9:F
{ 2, 2, 4, 4, 4, 4, 2, 2, 2, 2, 2, 2, }, // 10:F#m
{ 3, 3, 2, 2, 0, 0, 0, 0, 0, 0, 3, 3, }, // 11:G
{ 3, 3, 2, 2, 0, 0, 0, 0, 0, 0, 1, 1, }, // 12:G7
{ NP, NP, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, }, // 13:A
{ NP, NP, 0, 0, 2, 2, 2, 2, 1, 1, 0, 0, }, // 14:Am
{ NP, NP, 2, 2, 4, 4, 4, 4, 4, 4, 2, 2, }, // 15:B
{ NP, NP, 2, 2, 1, 1, 2, 2, 0, 0, 2, 2, }, // 16:B7
{ NP, NP, 2, 2, 4, 4, 2, 2, 5, 5, 2, 2, }, // 17:B7sus4
};
const uint8_t open[12] = //開放弦のmidi音階
// E E A A D D G G B B E E
{ 40,52,45,57,50,62,55,67,59,59,64,64,};
const uint16_t T[12] = //各弦の時間差(ms単位)
{ 0, 5, 15, 20, 30, 35, 45, 50, 60, 65, 75, 80,};
const uint16_t G[12] = //G時間のテーブル
{ 200,200,500,500,200,200,200,200,200,200,200,200,};
const uint16_t S[12] = //S時間のテーブル
{ 0, 0, 0, 0, 50, 50, 50, 50, 50, 50, 50, 50,};
const uint16_t Tbl[24] = //ms単位
{ 0, 5, 15, 20, 30, 35, 45, 50, 60, 65, 75, 80,
90, 150,300,500,750,800,1000,1250,1500, 0, 0, 0,};
const uint8_t V[12] = // Velocity
{120,100,100,100,100,100,100,100,100,100,100,100,};
演奏にはいくつかのパターンがあり、それぞれで変数テーブルが異なっています。
テーブルの曲データは著作権の問題がありますので想定のものではありません。
●ストローク グループ
const uint8_t P[1][14] = { //曲データ code[][] =
{ 1, 9, 1, 9, 1, 1, 9, 11, 14, EF},};
const uint8_t M[] = //Down(1) Up(2)でリズムを刻みます
{ 1, 1, 1, 2, 1, 1, 1, 2, 1, EF,};
●アルペジオ1 グループ
const uint8_t P[1][9] = //曲データのコード
{ 2, 3, 2, 3, 2, 3, 2, 3,EF};
const uint8_t M[14] = //3はArpe1です
{ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, EF,};
const uint8_t CT[12] = // Timing from TBL[] 弦毎のタイミング
{NP,NP, 0, 0,NP,NP,14,14,15,15,16,16, };
●アルペジオ2 グループ
const uint8_t P[12][7] = {
{NP,NP,NP,NP,NP,NP,EF,}, // 6弦a
{NP,NP,NP,NP,NP,NP,EF,}, // 6弦b
{ 3,NP,NP, 3,NP,NP,EF,}, // 5弦a
{ 3,NP,NP, 3,NP,NP,EF,}, // 5弦b
{NP,NP,NP,NP,NP,NP,EF,}, // 4弦a
{NP,NP,NP,NP,NP,NP,EF,}, // 4弦b
{NP, 0,NP,NP,NP, 0,EF,}, // 3弦a
{NP, 0,NP,NP,NP, 0,EF,}, // 3弦b
{ 1,NP, 1,NP,NP,NP,EF,}, // 2弦a
{ 1,NP, 1,NP,NP,NP,EF,}, // 2弦b
{ 0,NP,NP,NP, 0,NP,EF,}, // 1弦a
{ 0,NP,NP,NP, 0,NP,EF,}, // 1弦b
};
const uint8_t Tap[12][7] = { // For Arp2 S:上位4bit G:下位4bit
{NP,NP,NP,NP,NP,NP,EF,}, // 6弦a
{NP,NP,NP,NP,NP,NP,EF,}, // 6弦b
{ 6,NP,NP, 5,NP,NP,EF,}, // 5弦a
{ 6,NP,NP, 5,NP,NP,EF,}, // 5弦b
{NP,NP,NP,NP,NP,NP,EF,}, // 4弦a
{NP,NP,NP,NP,NP,NP,EF,}, // 4弦b
{NP, 5,NP,NP,NP, 6,EF,}, // 3弦a
{NP, 5,NP,NP,NP, 6,EF,}, // 3弦b
{ 6,NP, 5,NP,NP,NP,EF,}, // 2弦a
{ 6,NP, 5,NP,NP,NP,EF,}, // 2弦b
{ 6,NP,NP,NP, 5,NP,EF,}, // 1弦a
{ 6,NP,NP,NP, 5,NP,EF,}, // 1弦b
};
●アルペジオ3 グループ
const uint8_t pattern[6] = { //3-finger pattern
// 112233445566 1b1a---6b6a is string no
0b0000001100001100, // 2-5
0b0000000011000000, // 3
0b0000001100000000, // 2
0b0000000000001100, // 5
0b0000110000000000, // 1
0b0000000011000000, // 3
};
今日の作業はここまで。暑かった夏もちょっとマシになって、よく眠れます。寝る前はもちろん赤ワインのオンザロックで乾杯。
※このブログで書かれた内容によって生じた損害等の一切の責任を負いかねますので、予めご了承下さい。
真夜中の訪問者2(ソフト編)
■タイミング設計について
前回、Audacityで玄関インターホンの発生周波数を測定したと書きましたが、時間変化と発生周波数の関係が読み取れません。そこでパソコンに 「Spek」という名前のソフトをインストールしました。これで時系列にスペクトラムを表示してくれます。
キーワードは、ubuntu Spek 音響スペクトラムアナライザ
前回、Audacityで玄関インターホンの発生周波数を測定したと書きましたが、時間変化と発生周波数の関係が読み取れません。そこでパソコンに 「Spek」という名前のソフトをインストールしました。これで時系列にスペクトラムを表示してくれます。
キーワードは、ubuntu Spek 音響スペクトラムアナライザ
■プログラムの流れ
●コンデンサマイクからの入力を100usの周期でAD変換
●AD変換値からゼロ点を求める
●FFT処理
●結果から2乗和の平方根を算出
●計算値が判定レベル以上ならLEDを点滅
●AD変換値からゼロ点を求める
●FFT処理
●結果から2乗和の平方根を算出
●計算値が判定レベル以上ならLEDを点滅
■動作
●起動確認のため、通電するとLEDが2回点滅します。
●電池残量確認のため、30秒に1回点滅します。
●検知の判定は2段階で、レベルとタイミングは固定値です。
①マイク入力にある周波数範囲で入力があった場合、
②それから一定時間後に、そのマイク入力が無音を検出し、
③その後、一定時間後にレベル以上を検出すると、玄関の呼び出しと 判定します
●呼び出しがあればLEDの点滅を20秒間続けます。
●起動確認のため、通電するとLEDが2回点滅します。
●電池残量確認のため、30秒に1回点滅します。
●検知の判定は2段階で、レベルとタイミングは固定値です。
①マイク入力にある周波数範囲で入力があった場合、
②それから一定時間後に、そのマイク入力が無音を検出し、
③その後、一定時間後にレベル以上を検出すると、玄関の呼び出しと 判定します
●呼び出しがあればLEDの点滅を20秒間続けます。
■開発環境について
●対象マイコンはPIC12F1840 クロック:内部 32MHz駆動
●グラフィックLCD(ST7735液晶モジュール)はきむ茶工房さんのskST7735ライブラリをまるごと使用しています。ただし、メモリサイズの関係からフォントは使用しませんでした。(#include "font.h"をコメントアウトしています)。
●MPLAB X IDEはv5.25@linux mint 18.3
●使用コンパイラはXC8 (v.1.45) 。コンパイル後のデータメモリは206バイト(80%)プログラムメモリは3583バイト(87%)でした。
●ライターはPICKIT3を使用しています。デバッグ用ビルドではコンパイルエラーが発生し、いろいろなメッセージをみかけました。このメッセージ自体がコピーできないので原因追求がしにくかったです。
●LCD用SPI通信とデバッガとの相性が悪いようです。FFT演算結果の確認をする時はLCD表示をOFFにすることで対応しました。もっと良い方法があるのかもしれません。
●LCDが誤表示をする時があります。ライブラリの問題ではないことは確認できましたが、よくわからないのでLCDを表示する直前に割り込みを禁止しています。di()とei()はマクロで、定義はヘッダーで定義済みなので気楽に使用しています。
●configのLVP(Low voltage programming enable)はONですよ。
●グラフィックLCD(ST7735液晶モジュール)はきむ茶工房さんのskST7735ライブラリをまるごと使用しています。ただし、メモリサイズの関係からフォントは使用しませんでした。(#include "font.h"をコメントアウトしています)。
●MPLAB X IDEはv5.25@linux mint 18.3
●使用コンパイラはXC8 (v.1.45) 。コンパイル後のデータメモリは206バイト(80%)プログラムメモリは3583バイト(87%)でした。
●ライターはPICKIT3を使用しています。デバッグ用ビルドではコンパイルエラーが発生し、いろいろなメッセージをみかけました。このメッセージ自体がコピーできないので原因追求がしにくかったです。
●LCD用SPI通信とデバッガとの相性が悪いようです。FFT演算結果の確認をする時はLCD表示をOFFにすることで対応しました。もっと良い方法があるのかもしれません。
●LCDが誤表示をする時があります。ライブラリの問題ではないことは確認できましたが、よくわからないのでLCDを表示する直前に割り込みを禁止しています。di()とei()はマクロで、定義はヘッダーで定義済みなので気楽に使用しています。
●configのLVP(Low voltage programming enable)はONですよ。
■プログラムリスト
/* 動作クロックは 32MHz タイマー1は10msに設定。
* PIC ST7735
* SCK ←→ SCL
* 1 VDD 8 Vss SDO ←→ SDA
* 2 LED 7 SDO VDD ←→ RES
* 3 AN3in 6 SCK GLCD_RS ←→ DC
* 4 MCLR 5 GLCD_RS VDD ←→ BL
*
* PICKIT3のピン番号
* ⑥未使用
* 7 RA0(ICSPDAT) ④
* 6 RA1(ICSPCLK) ⑤
* ③GND
* ____ ②VDD
* 4 RA3(MCLR) ①
*
* LCD_TFTのスペックは ST7735の緑タブ品(基板は1.8inch 128*120 RGB と記載あり)
* 端子を上にして左が① 接続するマイコンのpin番号
* ①GND ⑧ :GND
* ②VCC ① :VCC
* ③SCL ⑥ :SCL
* ④SDA ⑦ :SDO
* ⑤RES ⑧ :VCC
* ⑥DC ② :LCD_RS
* ⑦CS ⑤ :LCD_CS
* ⑧BL ① :VCC
*
*/
#pragma config FOSC = INTOSC //内蔵osc使用
#pragma config WDTE = OFF //watch dog timer
#pragma config PWRTE = OFF //Power up timer enable
#pragma config MCLRE = ON //Mclr/Vpp select bit
#pragma config CP = OFF //Code Protection bit
#pragma config CPD = OFF //Data Protection bit
#pragma config BOREN = OFF //Brown-out Reset enable bit
#pragma config CLKOUTEN = OFF //Clock out enable bit
#pragma config IESO = OFF //internal External switchover
#pragma config FCMEN = OFF //Fall safe Clock monitor
#pragma config WRT = OFF //Flash Memory protection
#pragma config PLLEN = ON //PLL enable bit 4*PLL
#pragma config STVREN = ON //stack overflow/underflow reset
#pragma config BORV = HI //Brown-out reset Voltage
#pragma config LVP = ON //Low voltage programming enable
#include <xc.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <pic12f1840.h>
#define xdebug 0
●LCD表示のライブラリ
LCD表示のライブラリは、きむ茶工房ガレージハウスさんの「TFT LCD(ST7735)モジュールに表示を行なう(skSDlib編)」を引用しました。
LCD表示のライブラリは、きむ茶工房ガレージハウスさんの「TFT LCD(ST7735)モジュールに表示を行なう(skSDlib編)」を引用しました。
#include "skSPIlib.h"
#include "skST7735x.h"
//#include "font.h"
#define _XTAL_FREQ 32000000
#define PRESCALE 6
#define T0COUT 131
#define ON 1
#define OFF 0
#define samples 8
#define upper_max 250
#define lower_min 0
#define buffsize 8
#define Level1 120
#define Level2 50
#define Level3 120
int16_t n; // データ点数
int16_t theta; // m*2/n
int16_t ar[samples]; //int32_t 入力データの実部(n点)
int16_t ai[samples]; //int32_t 入力データ虚部(n点)
int16_t spc[samples];
uint8_t incnt;
uint8_t AD_flag;
uint8_t spi_mode;
uint8_t acnt;
uint8_t dat;
uint8_t data;
uint8_t dat1;
uint8_t disp_mul;
uint8_t ad_mul;
uint8_t ii;
int16_t ad_buff1[buffsize];
uint8_t addata;
int16_t mysqrt(uint16_t );
uint8_t prescaler;
//TMR1割り込み
uint16_t sec_tmr;
uint8_t tmr_sw1;
uint8_t tmr_sw2;
uint8_t tmr_sw3;
uint8_t tmr_sw4;
uint8_t tmr_sw5;
uint8_t tmr_sw6;
uint8_t tmr_flg;
uint16_t sectimer1;
uint16_t sectimer2;
uint16_t sectimer3;
uint16_t sectimer4;
uint16_t sectimer5;
uint16_t sectimer6;
uint16_t xxx;
uint8_t sincnt;
uint8_t loop;
static const int8_t sin[] ={ //8*16 正弦テーブル(m点。sin(0..π))
0x00,0x03,0x06,0x09,0x0C,0x0F,0x12,0x15,
0x18,0x1C,0x1F,0x22,0x25,0x28,0x2B,0x2E,
0x30,0x33,0x36,0x39,0x3C,0x3F,0x41,0x44,
0x47,0x49,0x4C,0x4E,0x51,0x53,0x55,0x58,
0x5A,0x5C,0x5E,0x60,0x62,0x64,0x66,0x68,
0x6A,0x6C,0x6D,0x6F,0x70,0x72,0x73,0x75,
0x76,0x77,0x78,0x79,0x7A,0x7B,0x7C,0x7C,
0x7D,0x7E,0x7E,0x7F,0x7F,0x7F,0x7F,0x7F,
0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7E,0x7E,
0x7D,0x7C,0x7C,0x7B,0x7A,0x79,0x78,0x77,
0x76,0x75,0x73,0x72,0x70,0x6F,0x6D,0x6C,
0x6A,0x68,0x66,0x64,0x62,0x60,0x5E,0x5C,
0x5A,0x58,0x55,0x53,0x51,0x4E,0x4C,0x49,
0x47,0x44,0x41,0x3F,0x3C,0x39,0x36,0x33,
0x30,0x2E,0x2B,0x28,0x25,0x22,0x1F,0x1C,
0x18,0x15,0x12,0x0F,0x0C,0x09,0x06,0x03,
};
static const int8_t cos[]={ //8*16 余弦テーブル(m点。cos(0..π))
0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7E,0x7E,
0x7D,0x7C,0x7C,0x7B,0x7A,0x79,0x78,0x77,
0x76,0x75,0x73,0x72,0x70,0x6F,0x6D,0x6C,
0x6A,0x68,0x66,0x64,0x62,0x60,0x5E,0x5C,
0x5A,0x58,0x55,0x53,0x51,0x4E,0x4C,0x49,
0x47,0x44,0x41,0x3F,0x3C,0x39,0x36,0x33,
0x30,0x2E,0x2B,0x28,0x25,0x22,0x1F,0x1C,
0x18,0x15,0x12,0x0F,0x0C,0x09,0x06,0x03,
0x00,-0x03,-0x06,-0x09,-0x0C,-0x0F,-0x12,-0x15,
-0x18,-0x1C,-0x1F,-0x22,-0x25,-0x28,-0x2B,-0x2E,
-0x30,-0x33,-0x36,-0x39,-0x3C,-0x3F,-0x41,-0x44,
-0x47,-0x49,-0x4C,-0x4E,-0x51,-0x53,-0x55,-0x58,
-0x5A,-0x5C,-0x5E,-0x60,-0x62,-0x64,-0x66,-0x68,
-0x6A,-0x6C,-0x6D,-0x6F,-0x70,-0x72,-0x73,-0x75,
-0x76,-0x77,-0x78,-0x79,-0x7A,-0x7B,-0x7C,-0x7C,
-0x7D,-0x7E,-0x7E,-0x7F,-0x7F,-0x7F,-0x7F,-0x7F,
};
static const int8_t sintbl[] ={ // 8個
0x00,0x5A,0x00,-0x5A,0x00,0x5A,0x00,-0x5A,
};
●割り込みルーチン
void __interrupt () InterTimer( void ){
if(INTCONbits.TMR0IF && INTCONbits.TMR0IE){
INTCONbits.TMR0IF = 0; //基本タイマーは100uS
TMR0 = T0COUT;
}
if(PIR1bits.TMR1IF && PIE1bits.TMR1IE){
TMR1H = 99; //10ms timer
TMR1L = 192;
sec_tmr ++ ; //電池の生存確認用に30秒に一度点灯
if(sec_tmr > 3000){ //10ms*100 = 1sec
sec_tmr = 0;
PORTAbits.RA5 = 1; //LED ON
tmr_sw1 = ON;
}
if(tmr_sw1 == ON ){ //ON時間タイマー
sectimer1++;
if(sectimer1 > 25){ //LED 250ms ON
sectimer1 = 0;
PORTAbits.RA5 = 0; //LED OFF
tmr_sw1 = OFF;
}
}
//報知のシーケンスが検出レベルに達した時(tmr_sw2がONした時) 点滅動作へ
if(tmr_sw2 == ON){ //検出後の点滅タイマー
sectimer2++;
if(sectimer2 > 25){ //点滅のON時間250ms
tmr_sw2 = OFF;
sectimer2 = 0;
PORTAbits.RA5 = OFF; //LED OFF
tmr_sw3 = ON;
}
}
if(tmr_sw3 == ON){ //検出後の点滅タイマー OFF時間
sectimer3++;
if(sectimer3 > 40){ //点滅のOFF時間400ms
tmr_sw3 = OFF;
tmr_sw2 = ON;
tmr_sw4 = ON;
sectimer3 = 0;
PORTAbits.RA5 = ON; //LED
}
}
if(tmr_sw4 == ON ){ //sw2がON検出後の点滅している時間
sectimer4++;
if(sectimer4 > 2000){ //点滅を20秒で終了
tmr_sw2 = OFF;
tmr_sw3 = OFF;
tmr_sw4 = OFF;
sectimer4 = 0;
PORTAbits.RA5 = OFF; //LED
}
}
if(tmr_sw5 == ON ){
sectimer5++;
if((sectimer5 >= 175) && (sectimer5 <= 300) ) { //2375
if( data < Level2){ //規定時間経過した時無音か
tmr_sw5 = OFF;
tmr_sw6 = ON;
sectimer6 = 0;
}
}
}
if(tmr_sw6 == ON) {
sectimer6++;
if((sectimer6 >= 150) && (sectimer6 <= 325) ) { //3875
if( data > Level3){ //規定時間経過した時レベルあるか
tmr_sw6 = OFF;
tmr_sw2 = ON; //同時にLEDの点滅タイマーを起動
sectimer6 = 0;
}
}
}
if((sectimer5 > 3000)||(sectimer6 > 3000)) { //カウンターのリセット
tmr_sw5 = OFF;
tmr_sw6 = OFF;
sectimer5 = 0;
sectimer6 = 0;
}
PIR1bits.TMR1IF = 0;
}
}
●平方根ルーチン
以下のコードはプログラムモグモグさんの「平方根なんてビット演算ですればいいじゃない」より引用しました。重複防止のため、変数名を変えています。
以下のコードはプログラムモグモグさんの「平方根なんてビット演算ですればいいじゃない」より引用しました。重複防止のため、変数名を変えています。
int16_t mysqrt( uint16_t xxx ){
int16_t aaa = 0;
int16_t ccc = 0;
int16_t yyy = 0;
int16_t iii = 0;
int16_t ttt = (int16_t )xxx;
while(ttt >>= 1){
++iii;
}
for(iii += iii & 1; iii >= 0; iii -= 2){
ccc = (yyy << 1 | 1) <= xxx >> iii;
aaa = aaa << 1 | ccc;
yyy = yyy << 1 | ccc;
xxx -= ccc * yyy << iii;
yyy += ccc;
}
return aaa;
}
void main(void){
OSCCONbits.SPLLEN = 1;
OSCCONbits.IRCF = 0b1110; // 内蔵発振周波数は8MHz
OSCCONbits.SCS = 0b00; // クロックソースは内蔵発振or外部クロック
//IO_init
APFCONbits.SDOSEL = 0; // SDOに割り付け
TRISA = 0b00011000; //RA5:O, RA4:I, RA3:I, RA2:O, RA1:O, RA0:O
PORTAbits.RA5 = 0;
ANSELAbits.ANSA4 = 1; // AN3(RA4) only
TRISAbits.TRISA4 = 1;
ADCON0 = 0b00001101; // AN3(RA4) only
ADCON1 = 0b00010000;
OPTION_REGbits.nWPUEN = 0; // 弱プルアップ 0がenable
WPUA = 0b00101100;
//電源投入時 2回LED点滅
PORTAbits.RA5 = 1;
__delay_ms(200);
PORTAbits.RA5 = 0;
__delay_ms(400);
PORTAbits.RA5 = 1;
__delay_ms(300);
PORTAbits.RA5 = 0;
if(xdebug){
SSP1CON1bits.WCOL = 0;
SSP1CON1bits.SSPOV = 0;
SSP1CON1bits.SSPEN = 1; //SPI disenable
SSP1CON1bits.SSPM = 2; //SPI_MASTER 2:Fosc/64
SSP1STATbits.SMP = 0; //sampling data:middle
SSP1CON1bits.CKP = 0; //アイドル時 Lで、立ち上がりで送信
SSP1STATbits.CKE = 1;
SSP1CON1bits.SSPEN = 1; //SPI enable
}
//
// Graphic LCD (SPI) ST7735_TFT-LCD
//
if(xdebug){
GLCD_DisplayInit();
GLCD_begin(CHIP_ST7735X | DISPLAY_TYPE_1,BLACK_SCREEN) ;
__delay_ms(100);
GLCD_ColorSet(0,0,63) ; // 青色
GLCD_Box(15,25,150,120);
GLCD_CLS(0); //画面クリア 0:黒 1:白
}
prescaler = PRESCALE;
OPTION_REGbits.TMR0CS = 0;
OPTION_REGbits.TMR0SE = 0;
OPTION_REGbits.PSA = 0;
OPTION_REGbits.PS = prescaler - 1;
TMR0 = T0COUT;
INTCONbits.TMR0IE = 1; // Timer 0 Enable
INTCONbits.TMR0IF = 0; // interrupt Flag clear
//Timer1 init
T1CONbits.TMR1ON = 1;
T1CONbits.TMR1CS = 0; //fosc/4
T1CONbits.T1CKPS = 1; //prescaler 1:2^1
TMR1H = 99; //10ms timer
TMR1L = 192;
PIE1bits.TMR1IE = 1;
INTCONbits.PEIE = 1;
//AD secdtion
n = samples;
incnt = 0;
disp_mul = 3;
ad_mul = 3;
INTCONbits.GIE = 1;
while(1){
for(incnt = 0; incnt < samples ; incnt++){
ADCON0bits.GO_nDONE = 1;
while(ADCON0bits.GO_nDONE);
addata = ADRESH;
if( addata > upper_max ) addata = upper_max ;
ad_buff1[incnt] = (int16_t)addata;
__delay_us(100);
}
●FFT演算処理
以下のFFTに関わるコードはBlackfin空挺団::Blog「FFTを固定小数点化するblackfin(6)」より引用しました。16ビット→8ビットのため、変数のサイズ、castの明確化などを変更しています。
以下のFFTに関わるコードはBlackfin空挺団::Blog「FFTを固定小数点化するblackfin(6)」より引用しました。16ビット→8ビットのため、変数のサイズ、castの明確化などを変更しています。
int8_t m;
int8_t mh;
int8_t i;
int8_t j;
int8_t k;
int16_t wr;
int16_t wi;
int16_t xr;
int16_t xi;
int16_t rr_b;
int16_t ii_b;
int16_t ri_b;
int16_t ir_b;
int16_t AD_sum;
int16_t Average;
uint8_t ll;
uint8_t mm;
AD_sum = 0;
theta = 64 * 2/n;
for( acnt = 0; acnt < samples ; acnt ++){
ar[acnt] = (int16_t) ad_buff1[acnt];
AD_sum += ar[acnt] ;
ai[acnt] = 0;
}
Average = AD_sum / samples ;
for(mm = 0; mm < samples; mm++){
ar[mm] = (ar[mm] - Average) << ad_mul;
}
/* FFT演算の検証のためのテスト用データをAD変換バッファに書き込む
================================================================
//ar[]← SinWave
for(sincnt = 0; sincnt < samples ; sincnt++){
ar[sincnt] += ((int16_t) sintbl[sincnt]) ;
}
=================================================================
*/
for (m = n; m > 1; m /= 2) {
mh = m/2;
for (i = 0; i < mh; i++) {
wr = (int16_t) cos[theta * i];
wi = (int16_t) sin[theta * i];
for (j = i; j < n; j += m) {
k = j + mh;
xr = (ar[j] - ar[k]) >> 1 ;
xi = (ai[j] - ai[k]) >> 1 ;
ar[j] = (ar[j] + ar[k]) >> 1 ;
ai[j] = (ai[j] + ai[k]) >> 1 ;
rr_b = wr * xr;
ii_b = wi * xi;
ri_b = wr * xi;
ir_b = wi * xr;
ar[k] = (rr_b - ii_b) >> 8; // >>15
ai[k] = (ri_b + ir_b) >> 8; // >>15
}
}
theta *= 2;
}
// unscrambler
i = 0;
for (j = 1; j < n - 1; j++) {
for (k = n >> 1; k > (i ^= k); k >>= 1);
if (j < i) {
xr = ar[j];
xi = ai[j];
ar[j] = ar[i];
ai[j] = ai[i];
ar[i] = xr;
ai[i] = xi;
}
}
if(xdebug){
di();
GLCD_ColorSet( 63, 63, 63) ; // 白色63all→0all
GLCD_Line(2,2,2,132) ; // index yline
GLCD_Line(2,2,162,2) ; // index xline
GLCD_Line(160,2,160,132) ; // index yline
GLCD_ColorSet( 63, 63 , 0) ; // 黄色
GLCD_Line( 2, 2 , 160 ,2 ); // 0
GLCD_Line( 2, 2+127, 160 ,2+127); // 横線が 128(0x7F) のレベル
for(ii = 0;ii < samples ;ii++ ){
if(ii < 4) GLCD_ColorSet( 63, 63, 0) ; // 黄色
if(ii == 4) GLCD_ColorSet( 63, 0, 0) ; // 赤色
if(ii > 4) GLCD_ColorSet( 0, 0,63) ; // 青色
GLCD_Line( 15+ii*10, 0, 15+ii*10,3); //目盛り
}
ei();
}
for( ii = 0 ; ii < samples ; ii++ ){
spc[ii] = ((ar[ii] * ar[ii]) + (ai[ii] * ai[ii])) << 4;
if( spc[ii] < 0 ) spc[ii] = 32767 ;
dat = (uint8_t) mysqrt( spc[ii] );
if(ii == 2) data = dat;
dat1 = dat >> 1; //LCD表示用データ
if(dat1 > 128 ) dat1 = 128; //Yのはみ出し防止
}
if(xdebug){
__delay_ms(2000) ; //適当な数値
di();
GLCD_CLS(0); //画面クリア 0:黒 1:白
ei();
}
if(data >= Level1) {
if((tmr_sw5 == OFF) && (tmr_sw6 == OFF)){
tmr_sw5 = ON;
sectimer5 = 0;
}
}
}
}
■もっと、どうにかしたい時は
もっと精密に設計するためには、サンプリング数をもっと増やせば可能です。メモリーサイズが多ければ周波数を細分化できます。ネットで調べていたらTI製のLMC567というCMOSトーンデコーダを見つけました。オリジナルメーカーは不明ですが、JRCのNJM567のCMOS版だそうです。形状がDIP8P。アリババで今でも手に入ります。検知周波数はCRで決めることができますが、メカニカルスイッチで切替えるのは面白くないので、デジタルポテンショメータICのマイクロチップ社MCP41010の抵抗と組み合わせるともっと精度がいいものができそうです。アナログ+デジタルのハイブリッド構成で計測器レベルで作れそうです。
作ってから気がついたのですが、電池がすぐになくなってしまいます。特性をみると、CR1220は35mAhでした。220mAhのCR2032の方がよかったかもしれません。基板スペースも問題ないですね。でも、多分、プロならスリープ状態にしておいてマイク入力レベルで目を覚ます方式にするのでしょう。
もっと精密に設計するためには、サンプリング数をもっと増やせば可能です。メモリーサイズが多ければ周波数を細分化できます。ネットで調べていたらTI製のLMC567というCMOSトーンデコーダを見つけました。オリジナルメーカーは不明ですが、JRCのNJM567のCMOS版だそうです。形状がDIP8P。アリババで今でも手に入ります。検知周波数はCRで決めることができますが、メカニカルスイッチで切替えるのは面白くないので、デジタルポテンショメータICのマイクロチップ社MCP41010の抵抗と組み合わせるともっと精度がいいものができそうです。アナログ+デジタルのハイブリッド構成で計測器レベルで作れそうです。
作ってから気がついたのですが、電池がすぐになくなってしまいます。特性をみると、CR1220は35mAhでした。220mAhのCR2032の方がよかったかもしれません。基板スペースも問題ないですね。でも、多分、プロならスリープ状態にしておいてマイク入力レベルで目を覚ます方式にするのでしょう。
■謝辞
前回にも言いましたがFFT(高速フーリエ変換)についての知識はほとんどなく、そのプログラムについても全くの素人です。この課題を実現するため、下記のブログにある、プログラムリストを引用しました。特に固定小数点によるFFTについては、酔漢さんのBlackfin空挺団::blogの「FFTを固定小数点化する(1)〜(6)」では基礎的な知識からマイコン実現での問題点について丁寧に教えていただきました。でも、決して、各著作者様へのお問い合わせはしないようにおねがいします。
炭酸100%のハイボールがめっちゃうまいそうです。炭酸メーカーでバーボンのハイボールを作れるのですが、こずかいが・・・・・。今年の夏の目標にしましょう。
前回にも言いましたがFFT(高速フーリエ変換)についての知識はほとんどなく、そのプログラムについても全くの素人です。この課題を実現するため、下記のブログにある、プログラムリストを引用しました。特に固定小数点によるFFTについては、酔漢さんのBlackfin空挺団::blogの「FFTを固定小数点化する(1)〜(6)」では基礎的な知識からマイコン実現での問題点について丁寧に教えていただきました。でも、決して、各著作者様へのお問い合わせはしないようにおねがいします。
//********************************************************
(1)Blackfin空挺団::Blog「FFTを固定小数点化する(6)」
(2)プログラムモグモグ「平方根なんてビット演算ですればいいじゃない」
https://itchyny.hatenablog.com/entry/20101222/1293028538
(3)きむ茶工房ガレージハウス「TFT LCD(ST7735)モジュールに表示を行なう(skSDlib編)」
http://zattouka.net/GarageHouse/micon/MPLAB/18F25K22/GLCD/ST7735/ST7735x_2.htm
炭酸100%のハイボールがめっちゃうまいそうです。炭酸メーカーでバーボンのハイボールを作れるのですが、こずかいが・・・・・。今年の夏の目標にしましょう。
※このブログで書かれた内容によって生じた損害等の一切の責任を
負いかねますので、予めご了承下さい。
真夜中の訪問者
キーボードを鳴らすのは夜が多い。朝からとか昼間とかにガンガンと音楽ではなくてキュイーンとかブィーンとかの音を鳴らす(弾くとは言っていない)にはちょっと気がひける。夜もヘッドフォンをつけてすることになる。しかし、これがなかなか落ち着かないのだ。没頭している間に電話が鳴ったら、何かの急用で来客があったらどうしようかと。真夜中に人が来ることなんて無いと思っていたら、何の連絡もなしに、実際に人が来ることがあるのだ・・・・。
それはある日のこと、夜中の3時に突然ガス警報器が作動し、警報を発した。対処の方法がわからず警報機を取り外して様子をみていたら、それから1時間ほどして玄関に来訪者があった。警備会社の社員らしく、様子を見に来たという。経緯を簡単に話してお引取りをいただいたが、真夜中に寝ぼけ姿ではなく、普通の服装で出てきたのは相当不審に思えたであろう(都会ではなく地方では特に)。
それはある日のこと、夜中の3時に突然ガス警報器が作動し、警報を発した。対処の方法がわからず警報機を取り外して様子をみていたら、それから1時間ほどして玄関に来訪者があった。警備会社の社員らしく、様子を見に来たという。経緯を簡単に話してお引取りをいただいたが、真夜中に寝ぼけ姿ではなく、普通の服装で出てきたのは相当不審に思えたであろう(都会ではなく地方では特に)。
■玄関インターホンの検知
あまりに不安が募ると おちおちとしていられないので、来客や電話の検知をしてやろうと思い立ちました。ヘッドフォンに周囲の音を拾ってシンセ音とミキシングするというのではおもしろくありません。それではと、マイクに設定値以上の入力レベルがあればお知らせサインを出力するプログラムを作ってみましたが、敏感にしすぎたのか、やたらと警報が発生し、閉口しました。そこで周波数で判別してみようと思い立ちました。こういう時はそう、(みんな大好き)フーリエ変換なのだ。と言いつつ、そんな知識はまったく持ち合わせていません。FFTとかバタフライ演算などというキーワードは自慢ではないが聞いたことがある程度です。ネットの情報を探ってみた。そこで得た情報を元に、実現の条件をまとめてみる。
あまりに不安が募ると おちおちとしていられないので、来客や電話の検知をしてやろうと思い立ちました。ヘッドフォンに周囲の音を拾ってシンセ音とミキシングするというのではおもしろくありません。それではと、マイクに設定値以上の入力レベルがあればお知らせサインを出力するプログラムを作ってみましたが、敏感にしすぎたのか、やたらと警報が発生し、閉口しました。そこで周波数で判別してみようと思い立ちました。こういう時はそう、(みんな大好き)フーリエ変換なのだ。と言いつつ、そんな知識はまったく持ち合わせていません。FFTとかバタフライ演算などというキーワードは自慢ではないが聞いたことがある程度です。ネットの情報を探ってみた。そこで得た情報を元に、実現の条件をまとめてみる。
●なるべくコンパクトにしたい
●計算速度を考えるとやっぱりFFT(高速フーリエ変換)でしょう
●計測器を作るわけではないので、精度はそこそこにし、周波数範囲も可聴範囲で。
●浮動小数点演算ではなく、固定小数点演算が簡単そう。
●回転因子(これなに?)の演算は難しそうなので、ここは三角関数をメインにします。
●計算速度を考えるとやっぱりFFT(高速フーリエ変換)でしょう
●計測器を作るわけではないので、精度はそこそこにし、周波数範囲も可聴範囲で。
●浮動小数点演算ではなく、固定小数点演算が簡単そう。
●回転因子(これなに?)の演算は難しそうなので、ここは三角関数をメインにします。
■固定小数点とは何か
上にも書きましたがFFTを説明できるほどの能力はまったくありません。さらに固定小数点とは????なのでキーワードで検索してください。
キーワードは、FFT 固定小数点演算
上にも書きましたがFFTを説明できるほどの能力はまったくありません。さらに固定小数点とは????なのでキーワードで検索してください。
キーワードは、FFT 固定小数点演算
■ターゲットの音源
当面のターゲットは玄関インターフォンの音色。この周波数はどうして測ればいいのか。パソコンでは音楽関連にはAudacityというプログラムがあります。音楽・音声に関しては思いつくことはほとんど出来るので重宝しています。このソフトの解析タブにスペクトラム表示というのが用意されているのでこれを利用することにしました。
キーワードは、Audacity スペクトラム表示 前後を反転
余談ですが、Audacityに関して感動したことがあります。さる有名な曲は、放送禁止になった元の曲を逆回転で再生して着想を得たという話がありました。実際に確認してみようと、テープで、モノーラルで録音した音をステレオで再生したらその逆回転の音を得ることが出来ました。この作業は準備に時間がかかり、結果は数分というものだったのです。これがAudacityではコマンド一発でできたことで感動しました。ローマ字で watasi wa bakadesu と書き、逆にしてusedaka bawisataw と録音し、逆再生するとちゃんと言葉になって聞こえるというのも音の真髄に触れるようで感動ものです。
■PICマイコンの謎
ネットでFFT関連のプログラムを検索すると、PICについてはほぼすべてが dspicに関するもので、8ビットマイコンシリーズではほとんど紹介がありません。理由はプログラムメモリーが少ない、メモリーも不十分だからです。だったらどこまで出来るのかを確認してみようと思いました。
ネットでFFT関連のプログラムを検索すると、PICについてはほぼすべてが dspicに関するもので、8ビットマイコンシリーズではほとんど紹介がありません。理由はプログラムメモリーが少ない、メモリーも不十分だからです。だったらどこまで出来るのかを確認してみようと思いました。
■使用する部品のリスト
部品リスト | 数量 | 単価 |
PIC12F1840-I/P | 1 | 110 |
NJU7043D | 1 | 70 |
ECマイクロホン C9767 | 1 | 100(4個で) |
半固定VR(50KΩ)
|
1 | 40 |
LED 赤Φ5(Φ3の方が良好) | 1 | 20 |
バッテリケース タカチ HU1220
|
1 | 176 |
電池 CR1220 (3V) | 1 | 510(5個で) |
片面ユニバーサル基板 60×28
|
1 | - |
清涼菓子ケース | 1 | - |
●PIC12F1840のマニュアルは英文ですが、同じファミリーの12F1822のが和文です。
●デジタルとアナログ回路の分離についてはトランジスタ技術2020年3月号p.64の回路(下記)を引用しました。マイク増幅回路のノイズがほぼ解消しました。
●OPアンプはレールトゥレール入出力(フルスイング)である必要があります。
●デジタルとアナログ回路の分離についてはトランジスタ技術2020年3月号p.64の回路(下記)を引用しました。マイク増幅回路のノイズがほぼ解消しました。
●OPアンプはレールトゥレール入出力(フルスイング)である必要があります。
●液晶モジュールはST7735R(緑色タブ)が使用可能ですが、クリップで接続し使用する形になります。接続した状態でのデバッガ操作に問題がありました。
●PICKIT3との接続もクリップで行います。
●基板は清涼菓子のケースに収容しますが、フリスク品がなく、よく似たものにしました。
●マイコンやOPアンプに8P DIPソケットを使用すると、約1.5mmはみ出します。
●LEDはΦ5のものを使用しましたが、約2mm飛び出します。
●電池ケースはタカチ製がピッタリです。最適な形状のLR44は1.5V(残念)なので注意
●チップ抵抗やコンデンサはパターン面に配置しましたが、固定は接着剤のB-7000を使用しました。スマホの修理では必須部品です。熱が加わると固定が外れます。
Curtis Fullerの「BLUES-ette」を聴きながらバーボンを飲むと、一気に1960年ニューヨークのファイブスポットカフェにジャンプできますぜ。
********************************************************
以下の記事から回路を引用しました。
(1)「ライセンスフリー!MP3ソフトウェアプレーヤの製作」
白阪一郎様 トラジスタ技術 2020年3月号 p.64 CQ出版社
※このブログで書かれた内容によって生じた損害等の一切の責任を負いかねますので、予めご了承下さい。
メンテナンスしよう
ずっと前からの課題だった、DX7の液晶バックライト化とコイン電池の交換をしようと思い立ちました。ネットで情報を探ると、本当に親切丁寧に教えてくれるページが多いです。
ネットの情報、特にyoutubeの動画を見ながら本体を分解し、LCDモジュールに部品を取り付けると見事にバックライト化は完了しました。(おしまい、おしまい)・・・・・これが理想なのですが、そうはいかなかったので備忘録に書いておくことにしました。
ネットの情報、特にyoutubeの動画を見ながら本体を分解し、LCDモジュールに部品を取り付けると見事にバックライト化は完了しました。(おしまい、おしまい)・・・・・これが理想なのですが、そうはいかなかったので備忘録に書いておくことにしました。
■液晶のバックライト化
数年前からNOZ CELICAさんの「NOZ's Stylish Sound♪」を拝見していました。そのYAMAHA「DX7」の液晶ディスプレイ交換♪ のページの内容を参考にしました。
●交換に使用するバックライト付き液晶
NOZさんのページで、KD-162-626LPSG-LGという型番のバックライト液晶が紹介されています。このページがきっかけで電子部品通販サイトの鈴商で数年前に購入しました。残念なことに現在は取り扱いがないようです。しかし、親切なことに、この部品の取説を置いてくれています。httpsで暗号化されていないとブラウザに注意されます。
キーワードは、鈴商、KD-162-626LPSG-LG pdf
この製造元のCrystalfontz社をネットで調べると、キャラクターLCDのページにCFAH1602シリーズという仕様の近いものがありました。(同等かは不明です)
DX7/9 SERVICE MANUALによると、元のLCD(バックライト無し)は日立製LM016という品番だそうです。これにバックライトが付いたLCDが日立(ルネサス?)から出ているのか出ていたのかはわかりません。
キーワードは、YAMAHA、DX7/9、Service Manual
ネットをざっと調べたところでは、HD44780コントローラを使用して、16×2行のLCD部品は見つかりますが、外形が異なるとか、ピン配列が違うものばかりでした
●基板接続用のコネクタ
液晶モジュール基板には16個の穴が空いていますが、DX7の元からのコネクタは14極なので、受け側のベース付ポストは14極の日圧製 S14B-XH-A を使用しました。形状はトップ型ではなく、サイド型でないと取り付け時に泣くに泣けないことになります。電子部品店に今でもあるのかとちょっと心配でしたが、ちゃんと置いてありました。
元のLCDモジュールと上に付いている化粧パネルを分離するのが一番苦労するところです。無理に引き剥がすことはせず、ホットエアーを使います。ドライヤーでももちろんOKですが、ノズルの形状によりスポット状に熱風が当たりますので作業性が抜群です。
余談ですが、スマートフォンの部品固定には接着剤が多用されていますので、スマホの修理では必須アイテムだと思います。価格的には、工業用がウン十万していましたが、数千円で買える物もあります。それでも購入には相当悩んだのですが、パネルを3回ほど交換して、元は取れたと思いました。
●改造作業
バックライト用の電源を5Vラインから取るのですが、モジュールのピン配列に注意です。XH14Pの1番ピンの隣が16ピンで、その隣が15ピンです。1ピンはVssですから、16ピンのLED(-)とは隣同士。2ピンはVddなので15ピンのLED(+)とはアキシャルのリードが配線に便利です。
Crystalfontzの説明書ではLCDへの供給電圧がTyp 5Vのところ、バックライトLED側はTyp4.2Vです。0.8Vならダイオードの電圧降下分でいけると考え、高速スイッチングダイオードにしました。
1N4148のアキシャルリードタイプを取り付けてみましたが、5Vが4.6Vくらいしか落ちません。では、という訳で2本直列にしてみると4.2Vになりました。
輝度の調整は何もしませんでしたが、結局高くついたので抵抗にしておけばよかった。
カタログを見ていたらLCDモジュールのバックライトLEDに制限抵抗が入っていないのに気付きました。なので、ダイオードで電圧降下するのはやめて抵抗(22Ω以上)に変更します。画像もそのうち変更します。(どうもすいませんでした)
数年前からNOZ CELICAさんの「NOZ's Stylish Sound♪」を拝見していました。そのYAMAHA「DX7」の液晶ディスプレイ交換♪ のページの内容を参考にしました。
●交換に使用するバックライト付き液晶
NOZさんのページで、KD-162-626LPSG-LGという型番のバックライト液晶が紹介されています。このページがきっかけで電子部品通販サイトの鈴商で数年前に購入しました。残念なことに現在は取り扱いがないようです。しかし、親切なことに、この部品の取説を置いてくれています。httpsで暗号化されていないとブラウザに注意されます。
キーワードは、鈴商、KD-162-626LPSG-LG pdf
この製造元のCrystalfontz社をネットで調べると、キャラクターLCDのページにCFAH1602シリーズという仕様の近いものがありました。(同等かは不明です)
DX7/9 SERVICE MANUALによると、元のLCD(バックライト無し)は日立製LM016という品番だそうです。これにバックライトが付いたLCDが日立(ルネサス?)から出ているのか出ていたのかはわかりません。
キーワードは、YAMAHA、DX7/9、Service Manual
ネットをざっと調べたところでは、HD44780コントローラを使用して、16×2行のLCD部品は見つかりますが、外形が異なるとか、ピン配列が違うものばかりでした
●基板接続用のコネクタ
液晶モジュール基板には16個の穴が空いていますが、DX7の元からのコネクタは14極なので、受け側のベース付ポストは14極の日圧製 S14B-XH-A を使用しました。形状はトップ型ではなく、サイド型でないと取り付け時に泣くに泣けないことになります。電子部品店に今でもあるのかとちょっと心配でしたが、ちゃんと置いてありました。
元のLCDモジュールと上に付いている化粧パネルを分離するのが一番苦労するところです。無理に引き剥がすことはせず、ホットエアーを使います。ドライヤーでももちろんOKですが、ノズルの形状によりスポット状に熱風が当たりますので作業性が抜群です。
余談ですが、スマートフォンの部品固定には接着剤が多用されていますので、スマホの修理では必須アイテムだと思います。価格的には、工業用がウン十万していましたが、数千円で買える物もあります。それでも購入には相当悩んだのですが、パネルを3回ほど交換して、元は取れたと思いました。
●改造作業
バックライト用の電源を5Vラインから取るのですが、モジュールのピン配列に注意です。XH14Pの1番ピンの隣が16ピンで、その隣が15ピンです。1ピンはVssですから、16ピンのLED(-)とは隣同士。2ピンはVddなので15ピンのLED(+)とはアキシャルのリードが配線に便利です。
Crystalfontzの説明書ではLCDへの供給電圧がTyp 5Vのところ、バックライトLED側はTyp4.2Vです。0.8Vならダイオードの電圧降下分でいけると考え、高速スイッチングダイオードにしました。
1N4148のアキシャルリードタイプを取り付けてみましたが、5Vが4.6Vくらいしか落ちません。では、という訳で2本直列にしてみると4.2Vになりました。
輝度の調整は何もしませんでしたが、結局高くついたので抵抗にしておけばよかった。
カタログを見ていたらLCDモジュールのバックライトLEDに制限抵抗が入っていないのに気付きました。なので、ダイオードで電圧降下するのはやめて抵抗(22Ω以上)に変更します。画像もそのうち変更します。(どうもすいませんでした)
■バッテリー交換
バッテリーはCR2032なので、タカチ製コイン電池ホルダー CH7410、電池ホルダーを包み込む用の熱収縮チューブ、基板から電池端子を外すための100Wのはんだごてなどを用意しました。ところが、テスターで両端の電圧を測ってみると何と3.02Vもあります。基板を外した上に、さらに電池の端子を外すのはちょっと面倒なので、今回は保留にしました。
バッテリーはCR2032なので、タカチ製コイン電池ホルダー CH7410、電池ホルダーを包み込む用の熱収縮チューブ、基板から電池端子を外すための100Wのはんだごてなどを用意しました。ところが、テスターで両端の電圧を測ってみると何と3.02Vもあります。基板を外した上に、さらに電池の端子を外すのはちょっと面倒なので、今回は保留にしました。
炭酸メーカーで作った炭酸を飲むとほんとに美味しい。でもこれがいつの間にか強炭酸になり、そのうちに強強炭酸にエスカレートしていく予感が・・・・。
※このブログで書かれた内容によって生じた損害等の一切の責任を負いかねますので、予めご了承下さい。
音を創るということ6(ソフト作成編)
プログラムの作成2回目です。おさらいのためにプログラムを見返していますが、もっと簡単にできないのかとか、出来るプログラマーだったら、〇〇指向とか〇〇プログラミングで見通しのよい作り方をするんだろうかとか、本当に余計なことを考えてしまいます。
■voiceパラメータのMAX値テーブル
//**************************************************************
//Table voice_max[165]
//**************************************************************
const uint8_t voice_max[165]={
99, // voice[0]= ; OP6_EG_RATE1
99, // voice[1]= ; OP6_EG_RATE2
99, // voice[2]= ; OP6_EG_RATE3
99, // voice[3]= ; OP6_EG_RATE4
99, // voice[4]= ; OP6_EG_LEVEL1
99, // voice[5]= ; OP6_EG_LEVEL2
99, // voice[6]= ; OP6_EG_LEVEL3
99, // voice[7]= ; OP6_EG_LEVEL4
99, // voice[8]= ; OP6_KEY_BOARD_LEVEL_SCALE_BREAK_POINT
99, // voice[9]= ; OP6_KEY_BOARD_LEVEL_SCALE_LEFT_DEPTH
99, // voice[10]= ; OP6_KEY_BOARD_LEVEL_SCALE_RIGHT_DEPTH
3, // voice[11]= ; OP6_KEY_BOARD_LEVEL_SCALE_LEFT_CURVE
3, // voice[12]= ; OP6_KEY_BOARD_LEVEL_SCALE_RIGHT_CURVE
7, // voice[13]= ; OP6_KEY_BOARD_RATE_SCALLING
3, // voice[14]= ; OP6_MOD SENSITIVITY_AMPLITUDE
7, // voice[15]= ; OP6_OPERATOR_KEY_VELOCITY_SENSITIVITY
99, // voice[16]= ; OP6_OPERATOR_OUTPUT_LEVEL
1, // voice[17]= ; OP6_OSCILLATOR_MODE
31, // voice[18]= ; OP6_OSCILLATOR_FREQUENCY_COARSE
99, // voice[19]= ; OP6_OSCILLATOR_FREQUENCY_FINE
14, // voice[20]= ; OP6_OSCILLATOR_DETUNE
99, // voice[21]= ; OP5_EG_RATE1
99, // voice[22]= ; OP5_EG_RATE2
99, // voice[23]= ; OP5_EG_RATE3
99, // voice[24]= ; OP5_EG_RATE4
99, // voice[25]= ; OP5_EG_LEVEL1
99, // voice[26]= ; OP5_EG_LEVEL2
99, // voice[27]= ; OP5_EG_LEVEL3
99, // voice[28]= ; OP5_EG_LEVEL4
99, // voice[29]= ; OP5_KEY_BOARD_LEVEL_SCALE_BREAK_POINT
99, // voice[30]= ; OP5_KEY_BOARD_LEVEL_SCALE_LEFT_DEPTH
99, // voice[31]= ; OP5_KEY_BOARD_LEVEL_SCALE_RIGHT_DEPTH
3, // voice[32]= ; OP5_KEY_BOARD_LEVEL_SCALE_LEFT_CURVE
3, // voice[33]= ; OP5_KEY_BOARD_LEVEL_SCALE_RIGHT_CURVE
7, // voice[34]= ; OP5_KEY_BOARD_RATE_SCALLING
3, // voice[35]= ; OP5_MOD SENSITIVITY_AMPLITUDE
7, // voice[36]= ; OP5_OPERATOR_KEY_VELOCITY_SENSITIVITY
99, // voice[37]= ; OP5_OPERATOR_OUTPUT_LEVEL
1, // voice[38]= ; OP5_OSCILLATOR_MODE
31, // voice[39]= ; OP5_OSCILLATOR_FREQUENCY_COARSE
99, // voice[40]= ; OP5_OSCILLATOR_FREQUENCY_FINE
14, // voice[41]= ; OP5_OSCILLATOR_DETUNE
99, // voice[42]= ; OP4_EG_RATE1
99, // voice[43]= ; OP4_EG_RATE2
99, // voice[44]= ; OP4_EG_RATE3
99, // voice[45]= ; OP4_EG_RATE4
99, // voice[46]= ; OP4_EG_LEVEL1
99, // voice[47]= ; OP4_EG_LEVEL2
99, // voice[48]= ; OP4_EG_LEVEL3
99, // voice[49]= ; OP4_EG_LEVEL4
99, // voice[50]= ; OP4_KEY_BOARD_LEVEL_SCALE_BREAK_POINT
99, // voice[51]= ; OP4_KEY_BOARD_LEVEL_SCALE_LEFT_DEPTH
99, // voice[52]= ; OP4_KEY_BOARD_LEVEL_SCALE_RIGHT_DEPTH
3, // voice[53]= ; OP4_KEY_BOARD_LEVEL_SCALE_LEFT_CURVE
3, // voice[54]= ; OP4_KEY_BOARD_LEVEL_SCALE_RIGHT_CURVE
7, // voice[55]= ; OP4_KEY_BOARD_RATE_SCALLING
3, // voice[56]= ; OP4_MOD SENSITIVITY_AMPLITUDE
7, // voice[57]= ; OP4_OPERATOR_KEY_VELOCITY_SENSITIVITY
99, // voice[58]= ; OP4_OPERATOR_OUTPUT_LEVEL
1, // voice[59]= ; OP4_OSCILLATOR_MODE
31, // voice[60]= ; OP4_OSCILLATOR_FREQUENCY_COARSE
99, // voice[61]= ; OP4_OSCILLATOR_FREQUENCY_FINE
14, // voice[62]= ; OP4_OSCILLATOR_DETUNE
99, // voice[63]= ; OP3_EG_RATE1
99, // voice[64]= ; OP3_EG_RATE2
99, // voice[65]= ; OP3_EG_RATE3
99, // voice[66]= ; OP3_EG_RATE4
99, // voice[67]= ; OP3_EG_LEVEL1
99, // voice[68]= ; OP3_EG_LEVEL2
99, // voice[69]= ; OP3_EG_LEVEL3
99, // voice[70]= ; OP3_EG_LEVEL4
99, // voice[71]= ; OP3_KEY_BOARD_LEVEL_SCALE_BREAK_POINT
99, // voice[72]= ; OP3_KEY_BOARD_LEVEL_SCALE_LEFT_DEPTH
99, // voice[73]= ; OP3_KEY_BOARD_LEVEL_SCALE_RIGHT_DEPTH
3, // voice[74]= ; OP3_KEY_BOARD_LEVEL_SCALE_LEFT_CURVE
3, // voice[75]= ; OP3_KEY_BOARD_LEVEL_SCALE_RIGHT_CURVE
7, // voice[76]= ; OP3_KEY_BOARD_RATE_SCALLING
3, // voice[77]= ; OP3_MOD SENSITIVITY_AMPLITUDE
7, // voice[78]= ; OP3_OPERATOR_KEY_VELOCITY_SENSITIVITY
99, // voice[79]= ; OP3_OPERATOR_OUTPUT_LEVEL
1, // voice[80]= ; OP3_OSCILLATOR_MODE
31, // voice[81]= ; OP3_OSCILLATOR_FREQUENCY_COARSE
99, // voice[82]= ; OP3_OSCILLATOR_FREQUENCY_FINE
14, // voice[83]= ; OP3_OSCILLATOR_DETUNE
99, // voice[84]= ; OP2_EG_RATE1
99, // voice[85]= ; OP2_EG_RATE2
99, // voice[86]= ; OP2_EG_RATE3
99, // voice[87]= ; OP2_EG_RATE4
99, // voice[88]= ; OP2_EG_LEVEL1
99, // voice[89]= ; OP2_EG_LEVEL2
99, // voice[90]= ; OP2_EG_LEVEL3
99, // voice[91]= ; OP2_EG_LEVEL4
99, // voice[92]= ; OP2_KEY_BOARD_LEVEL_SCALE_BREAK_POINT
99, // voice[93]= ; OP2_KEY_BOARD_LEVEL_SCALE_LEFT_DEPTH
99, // voice[94]= ; OP2_KEY_BOARD_LEVEL_SCALE_RIGHT_DEPTH
3, // voice[95]= ; OP2_KEY_BOARD_LEVEL_SCALE_LEFT_CURVE
3, // voice[96]= ; OP2_KEY_BOARD_LEVEL_SCALE_RIGHT_CURVE
7, // voice[97]= ; OP2_KEY_BOARD_RATE_SCALLING
3, // voice[98]= ; OP2_MOD SENSITIVITY_AMPLITUDE
7, // voice[99]= ; OP2_OPERATOR_KEY_VELOCITY_SENSITIVITY
99, // voice[100]= ; OP2_OPERATOR_OUTPUT_LEVEL
1, // voice[101]= ; OP2_OSCILLATOR_MODE
31, // voice[102]= ; OP2_OSCILLATOR_FREQUENCY_COARSE
99, // voice[103]= ; OP2_OSCILLATOR_FREQUENCY_FINE
14, // voice[104]= ; OP2_OSCILLATOR_DETUNE
99, // voice[105]= ; OP1_EG_RATE1
99, // voice[106]= ; OP1_EG_RATE2
99, // voice[107]= ; OP1_EG_RATE3
99, // voice[108]= ; OP1_EG_RATE4
99, // voice[109]= ; OP1_EG_LEVEL1
99, // voice[110]= ; OP1_EG_LEVEL2
99, // voice[111]= ; OP1_EG_LEVEL3
99, // voice[112]= ; OP1_EG_LEVEL4
99, // voice[113]= ; OP1_KEY_BOARD_LEVEL_SCALE_BREAK_POINT
99, // voice[114]= ; OP1_KEY_BOARD_LEVEL_SCALE_LEFT_DEPTH
99, // voice[115]= ; OP1_KEY_BOARD_LEVEL_SCALE_RIGHT_DEPTH
3, // voice[116]= ; OP1_KEY_BOARD_LEVEL_SCALE_LEFT_CURVE
3, // voice[117]= ; OP1_KEY_BOARD_LEVEL_SCALE_RIGHT_CURVE
7, // voice[118]= ; OP1_KEY_BOARD_RATE_SCALLING
3, // voice[119]= ; OP1_MOD SENSITIVITY_AMPLITUDE
7, // voice[120]= ; OP1_OPERATOR_KEY_VELOCITY_SENSITIVITY
99, // voice[121]= ; OP1_OPERATOR_OUTPUT_LEVEL
1, // voice[122]= ; OP1_OSCILLATOR_MODE
31, // voice[123]= ; OP1_OSCILLATOR_FREQUENCY_COARSE
99, // voice[124]= ; OP1_OSCILLATOR_FREQUENCY_FINE
14, // voice[125]= ; OP1_OSCILLATOR_DETUNE
99, // voice[126]= ; PITCH_EG_RATE1
99, // voice[127]= ; PITCH_EG_RATE2
99, // voice[128]= ; PITCH_EG_RATE3
99, // voice[129]= ; PITCH_EG_RATE4
99, // voice[130]= ; PITCH_EG_LEVEL1
99, // voice[131]= ; PITCH_EG_LEVEL2
99, // voice[132]= ; PITCH_EG_LEVEL3
99, // voice[133]= ; PITCH_EG_LEVEL4
31, // voice[134]= ; ALGORITHM_SELECT
7, // voice[135]= ; FEED_BACK
1, // voice[136]= ; OSCILLATOR_SYNC
99, // voice[137]= ; LFO_SPEED
99, // voice[138]= ; LFO_DELAY
99, // voice[139]= ; LFO_PMD
99, // voice[140]= ; LFO_AMD
1, // voice[141]= ; LFO_SYNC
4, // voice[142]= ; LFO_WAVE
7, // voice[143]= ; MOD_SENSITIVITY_PITCH
48, // voice[144]= ; TRANSPOSE
0x7f, // voice[145]= ; VOICE_NAME1
0x7f, // voice[146]= ; VOICE_NAME2
0x7f, // voice[147]= ; VOICE_NAME3
0x7f, // voice[148]= ; VOICE_NAME4
0x7f, // voice[149]= ; VOICE_NAME5
0x7f, // voice[150]= ; VOICE_NAME6
0x7f, // voice[151]= ; VOICE_NAME7
0x7f, // voice[152]= ; VOICE_NAME8
0x7f, // voice[153]= ; VOICE_NAME9
0x7f, // voice[154]= ; VOICE_NAME10
0x3F, // voice[155]= ; OPERATOR_ON/OFF
// D6:0 D5:OP1 D4:OP2 D3:OP3 D2:OP4 D1:OP5 D0:OP6
//以降はこの機器用のパラメータ
5, // voice[156] ;selectされたOPナンバー op1:5 op6:0
1, // voice[157] ;OP6
1, // voice[158] ;OP5
1, // voice[159] ;OP4
1, // voice[160] ;OP3
1, // voice[161] ;OP2
1, // voice[162] ;OP1
99, // voice[163] ;None
99, // voice[164] ;None
};
//**************************************************************
●DX7の取扱説明書の31ページに載っている各パラメータのMAX値をテーブルにしています。右回しでMAXで止めるようにしています。左回し一杯はもちろん0です。
●パラメータの156番以降はこの装置内だけで使用するために追加しました。パラメータ163と164は、レイアウト図の上から4個目、左から2個目RE用です。今回は使用していませんが、メニュー切り替え用に利用しようとしています。パラメータ157〜162もOPの操作用に確保したのですが、今のところ予定なしです。
●コメントが途切れていますが、取扱説明書を丸写ししただけです。
●パラメータの156番以降はこの装置内だけで使用するために追加しました。パラメータ163と164は、レイアウト図の上から4個目、左から2個目RE用です。今回は使用していませんが、メニュー切り替え用に利用しようとしています。パラメータ157〜162もOPの操作用に確保したのですが、今のところ予定なしです。
●コメントが途切れていますが、取扱説明書を丸写ししただけです。
■voice初期データのテーブル
//*************************************************************
// Table voice_init[165 ] voice初期データ
//*************************************************************
const uint8_t voice_init[165]={
0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x63,0x63,0x63,
0x63,0x63,0x63,0x63,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x01,0x00,0x00,0x63,0x63,0x63,0x63,0x63,0x63,
0x63,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,
0x00,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x63,0x63,
0x63,0x63,0x63,0x63,0x63,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x63,0x63,0x63,0x63,0x63,
0x63,0x63,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x63,
0x00,0x01,0x00,0x00,0x63,0x63,0x63,0x63,0x32,0x32,0x32,0x32,
0x00,0x00,0x01,0x23,0x00,0x00,0x00,0x01,0x00,0x03,0x18,0x20,
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3F, 0, 1, 1, 1,
1, 1, 1, 0, 0,
};
//*************************************************************
●電源投入時の最初のデータです。これをベースに設定値を書き換えていくのですが、電源投入時にDX7からのデータを受け付ければもっと音作りが楽になりそう。
●voice_maxとかvoice_initとかパラメータのデータが多いです。構造体を使えばもっとすっきりした形になるのだろうと思いつつ、最初の設計が試行錯誤で進んでいるので仕方ないね。開発初期段階の設計は本当に重要なのですよ。
●実はこのデータはDX7の初期時のデータを参考にしています。こういうデータは著作権に関係するのかなとファイル名だけは変えましたけど。
●voice_maxとかvoice_initとかパラメータのデータが多いです。構造体を使えばもっとすっきりした形になるのだろうと思いつつ、最初の設計が試行錯誤で進んでいるので仕方ないね。開発初期段階の設計は本当に重要なのですよ。
●実はこのデータはDX7の初期時のデータを参考にしています。こういうデータは著作権に関係するのかなとファイル名だけは変えましたけど。
■リングバッファ
//*************************************************************
// write_buff( ) と Read_buff( )
//*************************************************************
#define MAX 256
uint8_t data3;
uint8_t data4;
void write_buff(uint8_t data3){
if(!(rd_index == (wr_index+1) % MAX)){
buff[wr_index++] = data3;
if(wr_index == MAX) wr_index = 0;
}
}
uint8_t Read_buff(void){
if(!(rd_index == wr_index)){
data4 = buff[rd_index];
++ rd_index;
}
if(rd_index == MAX) rd_index = 0;
return data4;
}
//*************************************************************
●間違いを発見しました。write_buff は小文字なのに Read_buff は大文字でした。そのまま行きます。
●入力は割り込みで行っているので、メインルーチンの処理ではバッファが必要です。MAXが256では短いかなと思ってます。
●リングバッファをもうひとつ作っていました。詳細はこの下を見てください。
●入力は割り込みで行っているので、メインルーチンの処理ではバッファが必要です。MAXが256では短いかなと思ってます。
●リングバッファをもうひとつ作っていました。詳細はこの下を見てください。
■ main()
//*************************************************************
void main(void){
//OSC_init();
OSCCONbits.IRCF = 7; // 8MHz
OSCTUNEbits.PLLEN = 1; // PLL(x6)
OSCCONbits.SCS = 0;
//IO_init();
// 0が出力、1が入力
TRISA = 0b00010000;
TRISB = 0b10001111;
UCONbits.USBEN = 0;
UCFGbits.UTRDIS = 1;
TRISC = 0b11111000;
// 0がアナログ、1がデジタル
ANCON0 = 0b11111111;
ANCON1 = 0b00011111;
INTCON2bits.RBPU = 0;
RPINR16 = 10;
RPOR9 = 6;
// PRESCALE(0-8):6
T0CONbits.TMR0ON = 1;
T0CONbits.T08BIT = 1; // 8-bit timer
T0CONbits.T0CS = 0; // use internal-OSC
if (PRESCALE == 0) {
T0CONbits.PSA = 1; // Not use prescaler
} else {
T0CONbits.PSA = 0; // use prescaler
T0CONbits.T0PS = PRESCALE - 1;
}
TMR0 = T0COUT; //T0COUT=181
INTCONbits.TMR0IE = 1;
INTCONbits.TMR0IF = 0;
INTCON2bits.TMR0IP = 0;
INTCONbits.PEIE = 1;
rmidi_index = 0;
tmidi_index = 0;
LATA = 0b11101111;
LATC = 0b00000111;
LATB = 0b00110000;
//UART_Initialize();
TXSTA2bits.SYNC = 0;
TXSTA2bits.BRGH = 0;
TXSTA2bits.TXEN = 1;
TXSTA2bits.TX9 = 0;
RCSTA2bits.SPEN = 1;
RCSTA2bits.RX9 = 0;
RCSTA2bits.CREN = 1;
BAUDCON2bits.BRG16 = 0;
BAUDCON2bits.RXDTP = 0;
BAUDCON2bits.TXCKP = 0;
SPBRG2 = 23 ; // ボーレートを31250bpsに設定
INTCONbits.PEIE = 1;
INTCONbits.GIE = 1;
//Init_buff(); //初期データを編集バッファへ転送
for(bufno = 0;bufno < 165; bufno++){
edit_voice[bufno] = voice_init[bufno];
}
SelectNo = voice_init[156]; //voiceNoにOP1をセット
while(1){
In_oprate();
DX7_task();
}
}
//**************************************************************
●メインの前半は初期設定です。特別なことは何もしていません。
●編集で使うvoice_edit[ ]に初期データ voice_init[ ]を転送します。これがないと操作時に微妙に苦労するのです。
●後半は無限ループで回していますが、スイッチ(SW)とロータリーエンコーダ(RE)の処理を行なうIn_oprate() と、 実際の動作を行なうDX7_task()で構成しています。
●編集で使うvoice_edit[ ]に初期データ voice_init[ ]を転送します。これがないと操作時に微妙に苦労するのです。
●後半は無限ループで回していますが、スイッチ(SW)とロータリーエンコーダ(RE)の処理を行なうIn_oprate() と、 実際の動作を行なうDX7_task()で構成しています。
■DX7_task( )
//**************************************************************
// DX7_task( )
//**************************************************************
void DX7_task(void ){
taskloop = paramcount;
for(i = 0;i < taskloop; i++){
midichar[i] = Read_midibuff();
while(TX2IF == 0);
TXREG2 = midichar[i];
}
}
//**************************************************************
uint8_t midi_modify[7]={0xf0,0x43,0b00010000,00,00,00,0xf7};
void StoreMidi(void){
uint8_t add1;
uint8_t add2;
uint8_t i;
add2 = param & 0b01111111;
add1 = (param >> 7) & 0b00000011;
midi_modify[3] = add1;
midi_modify[4] = add2;
midi_modify[5] = data;
for(i = 0;i < 7; i++){
Write_midibuff(midi_modify[i]);
}
}
//**************************************************************
void Write_midibuff(uint8_t data1){
if(!(rmidi_index == (tmidi_index + 1) % MAX)){
midibuff[tmidi_index++] = data1;
if(tmidi_index == MAX) tmidi_index = 0;
}
}
uint8_t Read_midibuff(void){
if(!(rmidi_index == tmidi_index)){
data2 = midibuff[rmidi_index];
++ rmidi_index;
}
if(rmidi_index == MAX) rmidi_index = 0;
return data2;
}
//**************************************************************
●さり気なく、Write_midibuff とか Read_midibuff とかの関数を書いていますが、リングバッファはパラメータ入力用とこのmidi用の2種類あったのでした。
■SW・REの入力処理
//**************************************************************
// In_oprate
//**************************************************************
void In_oprate(void){
uint8_t tmp;
uint8_t p;
uint8_t row; //行
uint8_t column; //列
uint8_t count;
uint8_t swcnt;
uint8_t swand;
uint8_t swnum;
uint8_t padr;
for(swcnt=0;swcnt < 2;swcnt++ ){
op_flag_old = op_flag;
swand = And[swcnt];
for(swnum = 0 ;swnum < 8 ;swnum = swnum + 2){
if(swcnt == 0 ){
//ONの処理
if((swand >> swnum) & 0b00000001){
op_flag |= (0b00001000 >> (swnum >> 1));
} else {
//OFFの処理
op_flag &= ~(0b00001000 >> (swnum >> 1));
}
}
if(swcnt == 1 && swnum > 3 ){
if((swand >> swnum) & 0b00000001 ){ //ONの処理
if(swnum == 4){
op_flag |= 0b00100000; //ch_no= 1 bit= 4,6
}
if(swnum == 6){
op_flag |= 0b00010000;
}
} else { //OFFの処理
if(swnum == 4){
op_flag &= ~(0b00100000);
}
if(swnum == 6){
op_flag &= ~(0b00010000);
}
}
}
}
if(op_flag ^ op_flag_old){
padr = 155;
edit_voice[155] = op_flag;
param = padr;
data = edit_voice[padr];
StoreMidi();
}
}
INTCONbits.TMR0IE = 0; //TMR0 disable
ch_no = Read_buff();
Rot_R = Read_buff();
Exor = Read_buff();
INTCONbits.TMR0IE = 1; //TMR0 enable
for(count = 0 ;count < 8;count = count + 2){
if(ch_no == 0 ||( ch_no == 1 && count > 3)) continue;
if((Exor >> count) & 0b00000001){
if((Rot_R >> count) & 0b00000001){
boundry[ch_no] |= (0b00000001 << count);
if(((Rot_R >> count) >> 1) & 0b00000001 ){
cw_ccw[ch_no] |= (0b00000010 << count);
} else {
cw_ccw[ch_no] |= (0b00000001 << count);
}
} else { //立ち下がり
boundry[ch_no] |= (0b00000001 << (count + 1));
if(((Rot_R >> count) >> 1) & 0b00000001 ){
cw_ccw[ch_no] |= (0b00000001 << count);
} else {
cw_ccw[ch_no] |= (0b00000010 << count);
}
}
}
}
for(column = 0;column < 8 ;column =column + 2) {
//いずれかの入力に変化があったら
if(boundry[ch_no]){
p=0;
if(ch_no > 5){ //row=6,7,8,9,10,11
SelectNo = edit_voice[156]; //OP1-OP6を選択
p = SelectNo * 21;
if(ch_no == 6) {
if( column > 4) p = 0;
}
if((ch_no == 8) && (column > 6)) p = 0;
}
padr = no[ch_no][column] + p;
Rotcnt[column] = edit_voice[padr];
if(((boundry[ch_no] >> column) & 0b00000011) == 1){
//CW
if(((cw_ccw[ch_no] >> column) & 0b00000011) == 1){
Rotcnt[column]++;
if(Rotcnt[column] > voice_max[padr]) {
Rotcnt[column] = voice_max[padr];
}
}
//CCW
if(((cw_ccw[ch_no] >> column) & 0b00000011) == 2){
Rotcnt[column]--;
if(Rotcnt[column] > voice_max[padr] ) {
Rotcnt[column] = 0;
}
}
if(edit_voice[padr] ^ Rotcnt[column]){
edit_voice[padr] = Rotcnt[column];
timerstart = 1; //次の入力があるとタイマーを
periodtimer = 0; //リセットし、再カウント
param = padr; //columnの必要な部分のみ送信
data = edit_voice[padr];
StoreMidi();
}
boundry[ch_no] &= ~(0b00000011 << column );
cw_ccw[ch_no] &= ~(0b00000011 << column );
}
}
}
boundry[ch_no] = 0;
cw_ccw[ch_no] = 0;
ppp = 0;
}
//**************************************************************
●前回のno[12][8]のテーブルを参照して、まず1行目と2行目の半分(bit4からbit7)にあるスイッチ(以下SW)について、ビットシフト演算により偶数ビットのSWの状態をみます。そのON/OFF状態をオペレータフラグ voice[155]に反映します。
●1行の中にSWとREが混在している上に、3行目以降はRE処理も書いているのでややこしいです。
●2行目の半分(bit0からbit3)以降はREのvoiceパラメータ確定と回転方向の判別・数値増減を行います。1行に4個のREがあるので、for文も0、2、4、6をキーにチェックします。
●余談ですが、C で for文は for ( ; ; )のスタイルですが、perlだと for ( 0, 2, 4, 6)という書き方もできます。変数に$をつけるとかの約束がありますが、Cに似ているので結構抵抗が少ないです。その分、つまづくことは何時もなんですが。しかし、最近、人気無いですね。
●for文の変数ですが、最初はswnum、その後 countとなり、すぐcolumnって統一性がないのまるわかり。これが試行錯誤プログラミングなのだ。
●セレクトされているOP番号が決まれば、簡単な計算でそのOP番号に関連するパラメータアドレスを算出しています。ややこしいことをせずにベタ書きの方がひょっとして良かったのではと思う今日このごろ。
●最後のほうでタイマをカウントしている部分がありますが、これはいわゆるウォッチドッグタイマで、REの数値を増減後の一定時間経過後にMIDIデータ(音楽データ)を送信することを考えています。そう、キーボード弾かなくてもいいのです。でも今回のテーマには沿わないので実装はしていません。
●ソースリストはこんな形よりもファイルをダウンロード出来るようにしておくのがいいですよね。このSSブログには簡単にできるような説明がありませんでした。Githubで登録するのを考えましたが時間がとれない。これからの課題にします。何しろ明日はセンター試験の2日目なんで・・・(別に関係ないけど)。
●1行の中にSWとREが混在している上に、3行目以降はRE処理も書いているのでややこしいです。
●2行目の半分(bit0からbit3)以降はREのvoiceパラメータ確定と回転方向の判別・数値増減を行います。1行に4個のREがあるので、for文も0、2、4、6をキーにチェックします。
●余談ですが、C で for文は for ( ; ; )のスタイルですが、perlだと for ( 0, 2, 4, 6)という書き方もできます。変数に$をつけるとかの約束がありますが、Cに似ているので結構抵抗が少ないです。その分、つまづくことは何時もなんですが。しかし、最近、人気無いですね。
●for文の変数ですが、最初はswnum、その後 countとなり、すぐcolumnって統一性がないのまるわかり。これが試行錯誤プログラミングなのだ。
●セレクトされているOP番号が決まれば、簡単な計算でそのOP番号に関連するパラメータアドレスを算出しています。ややこしいことをせずにベタ書きの方がひょっとして良かったのではと思う今日このごろ。
●最後のほうでタイマをカウントしている部分がありますが、これはいわゆるウォッチドッグタイマで、REの数値を増減後の一定時間経過後にMIDIデータ(音楽データ)を送信することを考えています。そう、キーボード弾かなくてもいいのです。でも今回のテーマには沿わないので実装はしていません。
●ソースリストはこんな形よりもファイルをダウンロード出来るようにしておくのがいいですよね。このSSブログには簡単にできるような説明がありませんでした。Githubで登録するのを考えましたが時間がとれない。これからの課題にします。何しろ明日はセンター試験の2日目なんで・・・(別に関係ないけど)。
飛ばして進めたので忘れていることが多いようで、まとまりがないです。一応、MIDIコントローラの話はここまで。家の中を探索すると梅酒とかりん酒がありました。寒くなってきたのでちょっとだけ飲んでみよう。
※このブログで書かれた内容によって生じた損害等の一切の責任を負いかねますので、予めご了承下さい。
音を創るということ5(ソフト設計&作成編)
今回からプログラムの作成にはいります。
■開発環境について
■開発環境について
●対象マイコンはPIC18F27J53 クロック:内部 48MHz駆動
●利用した内蔵モジュールはタイマー(TIMER0)割り込みとEUSART(MIDI出力用)という一般的なものなので、他のマイコンでも動作するはず(多分)です。
●MPLAB X IDEはv5.25@linux mint 18.3
●使用コンパイラはXC8 (v.1.45) v.2.10では不明のエラーが出ましたが、解決策はあるようです。
●ライターはPICKIT3を使用しています。PICKIT4も出ていますが、お安いということでこれにしました。アリババでも扱っています(別にまわし者では・・・・)、専用の書き込みアダプターなんてのが付属していますね。付録とか粗品とかの言葉に弱い。純正品かどうかは不明です。
keyword is microchip pickit3 programmer
●C文法およびMPLAB操作方法などはちゃんとした別のところを参照して下さい。何しろ、フローチャートなんて書かないで、思いついたらメモに書きなぐりぱなし、ネットのを丸写しなもんで(車輪は何度も発明するな・・・ですね)。
●開発時の反省点としては、コメントとドキュメントは絶対に必要だと思いました。コードを書いて、デバッガで実行し、結果をみては修正するという、いきあたりばったりの試行錯誤プログラミング ではちょっと前の記憶が飛びやすいです。
●利用した内蔵モジュールはタイマー(TIMER0)割り込みとEUSART(MIDI出力用)という一般的なものなので、他のマイコンでも動作するはず(多分)です。
●MPLAB X IDEはv5.25@linux mint 18.3
●使用コンパイラはXC8 (v.1.45) v.2.10では不明のエラーが出ましたが、解決策はあるようです。
●ライターはPICKIT3を使用しています。PICKIT4も出ていますが、お安いということでこれにしました。アリババでも扱っています(別にまわし者では・・・・)、専用の書き込みアダプターなんてのが付属していますね。付録とか粗品とかの言葉に弱い。純正品かどうかは不明です。
keyword is microchip pickit3 programmer
●C文法およびMPLAB操作方法などはちゃんとした別のところを参照して下さい。何しろ、フローチャートなんて書かないで、思いついたらメモに書きなぐりぱなし、ネットのを丸写しなもんで(車輪は何度も発明するな・・・ですね)。
●開発時の反省点としては、コメントとドキュメントは絶対に必要だと思いました。コードを書いて、デバッガで実行し、結果をみては修正するという、いきあたりばったりの試行錯誤プログラミング ではちょっと前の記憶が飛びやすいです。
■処理の流れ
簡単に言うと、タイマー割り込み毎に入力を読み込み、メインルーチンでデータの範囲を適正化したうえでmidiデータに加工し、UARTを通じてMIDIフォーマット通りに送信するプログラムです。
簡単に言うと、タイマー割り込み毎に入力を読み込み、メインルーチンでデータの範囲を適正化したうえでmidiデータに加工し、UARTを通じてMIDIフォーマット通りに送信するプログラムです。
以下のプログラムリストの説明は、忘れないように覚え書きのために書いています。なので、多少わかりにくいところがあるかもしれません。
■リスト内パラメータの説明
//**************************************************************
//parameter
//**************************************************************
●使用しているパラメータの説明
7654 3210
・Rot_RB = XXXX XXXX
|||| ||||_RB0
|||| |||__RB1
|||| ||___RB2
|||| |____RB3
||||______RC4
|||_______RC5
||________RC6
|_________RC7
・Rot_RB_OLD 一回前のRot_RBデータ
・Rot_RB_buff[ch_no]一回前のRot_RBデータch_noごとに保存
・boundry[ch_no]:A相の波形変化
7 6 5 4 3 2 1 0 00 変化なし
| | | | 01 立ち上がり
[3] [2] [1] [0] 10 立ち下がり
11 SW用
・cw_ccw[ch_no] :回転方向
7 6 5 4 3 2 1 0 00 変化なし
| | | | 01 cw 時計回り
[3] [2] [1] [0] 10 ccw 反時計回り
11 未使用
7654 3210
・op_flag = 00XX XXXX
|| ||||_OP6
|| |||__OP5
|| ||___OP4
|| |____OP3
||______OP2
|_______OP1
・voice_max[] 各ボイスパラメータの規定されている上限値
0〜155+a
param MIDI出力用ボイスパラメータのNo
data MIDI出力用ボイスパラメータのデータ
//**************************************************************
●パラメータの名称ですが、その時々の気分で変わっています。前々から関数やフラグ類の名称の付け方にはこだわりがあって、例えば
InputRotaryEncoderHogeDoit();
なんていうのがかたまりであると読み飛ばし、無視、挙句にわからなくなるというクセがあるので、その時の気分で名称をつけて済ますというスタイルなのです。でも、やはり、可読性を重視すべきでコメントとドキュメントは絶対に必要と今頃反省しています。
InputRotaryEncoderHogeDoit();
なんていうのがかたまりであると読み飛ばし、無視、挙句にわからなくなるというクセがあるので、その時の気分で名称をつけて済ますというスタイルなのです。でも、やはり、可読性を重視すべきでコメントとドキュメントは絶対に必要と今頃反省しています。
■割り込み処理
//**************************************************************
// Interrupt Routine
//**************************************************************
void interrupt InterTimer( void ){
if(INTCONbits.TMR0IF && INTCONbits.TMR0IE){
INTCONbits.TMR0IF = 0; //基本タイマーは400uS
TMR0 = T0COUT;
ReadPort();
prsc1ms++; //以下はタイマーで使用
prscdelay++;
if( prsc1ms > 5){
prsc1ms = 0;
prscdelay = 0;
}
}
}
//**************************************************************
●割り込み処理ルーチンで一番心配なのが設定通りにタイマーが動作しているかです。ここでこけると、皆こけたがこわい。特にpicマイコンは設定がわかりにくいので、ロジアナで最初に確認しました。(こう書くと何となくリッチマンのようですが、あくまでプアーマンです)。タイマーの設定については設定値を算出してくれる親切なサイトもあるようです。
キーワードは、PIC18 TIMER0 設定値 計算
●タイマー割り込み時間は400usとしました。 ReadPort()という関数でスイッチの入力を読み取ります。先だっての実験から、ロータリーエンコーダーA相の最速ONタイミングは約5.4msでした。その時、B相が約2.6ms間同じ状態を維持しているので、ノイズ対策として3回一致のタイミングだと合計して半分の1.3ms以下であれば確実に入力確定すると考えました。400usなら合計800usなので、十分余裕があります(多分)。でも、その後、ノイズ対策はこれでは意味がないと思い至りました。
キーワードは、PIC18 TIMER0 設定値 計算
●タイマー割り込み時間は400usとしました。 ReadPort()という関数でスイッチの入力を読み取ります。先だっての実験から、ロータリーエンコーダーA相の最速ONタイミングは約5.4msでした。その時、B相が約2.6ms間同じ状態を維持しているので、ノイズ対策として3回一致のタイミングだと合計して半分の1.3ms以下であれば確実に入力確定すると考えました。400usなら合計800usなので、十分余裕があります(多分)。でも、その後、ノイズ対策はこれでは意味がないと思い至りました。
■スイッチ・RE入力処理 ReadPort()
■テーブルの説明
//*************************************************************
void ReadPort(void){
for(Ich_no = 0;Ich_no < 12;Ich_no++){
Scan_out(Ich_no);
Rot_RB_OLD = Rot_RB_buff[Ich_no];
Rot_RB = (~PORTB) & 0b00001111; //RBの下位4ビット
Rot_RC = (~PORTC) & 0b11110000; //RCの上位4ビット
Rot_RB = Rot_RB | Rot_RC;
Rot_RB_buff[Ich_no] = Rot_RB;
if(Ich_no == 0){
And[Ich_no] = Rot_RB & Rot_RB_OLD; //0SWの判定用
continue;
}
if(Ich_no == 1){
And[Ich_no] = Rot_RB & Rot_RB_OLD; //1SWの判定用
}
IExor = (Rot_RB ^ Rot_RB_OLD);
if( IExor ){
write_buff(Ich_no); //これら4個のパラメータを
write_buff(Rot_RB); //リングバッファにストア
write_buff(IExor);
}
}
Scan_out(66); //0〜11以外の数字
}
//**************************************************************
//**************************************************************
// Scan_out()
//**************************************************************
void Scan_out( uint8_t sn){
LATA = 0b11101111;
LATB = 0b00110000;
LATC = 0b00000111;
switch(sn){
case 0: LATA0=0; break;
case 1: LATA1=0; break;
case 2: LATA2=0; break;
case 3: LATA3=0; break;
case 4: LATA5=0; break;
case 5: LATA6=0; break;
case 6: LATA7=0; break;
case 7: LATC0=0; break;
case 8: LATC1=0; break;
case 9: LATC2=0; break;
case 10:LATB4=0; break;
case 11:LATB5=0; break;
default:
LATA = 0b11101111;
LATC = 0b00000111;
LATB = 0b00110000;
}
}
//**************************************************************
●スイッチ・RE入力は12行を一気に読み込んでいます。その後、スイッチの前処理をしています。スイッチとロータリーエンコーダ(RE)の入力加工はメインの方で行います。割り込みルーチンの処理時間が心配でしたが、タイマー割り込み時間の400usより少なかったです。
●相変わらず()でくくるのが多いです。Cの優先順位が覚えられません。わからない時は()でくくるのが鉄則。以降、一向に反省する気配はないでしょう。
●相変わらず()でくくるのが多いです。Cの優先順位が覚えられません。わからない時は()でくくるのが鉄則。以降、一向に反省する気配はないでしょう。
■テーブルの説明
//**************************************************************
// no[ ][ ] table
//**************************************************************
const uint8_t no[12][8]= { {159,159,160,160,161,161,162,162},
{137,137,142,142,157,157,158,158},
{141,141,140,140,139,139,138,138},
{129,129,128,128,127,127,126,126},
{163,163,144,144,135,135,134,134},
{133,133,132,132,131,131,130,130},
{13,13,14,14,143,143,156,156},
{17,17,8,8,15,15,16,16},
{20,20,19,19,18,18,136,136},
{3,3,2,2,1,1,0,0},
{10,10,9,9,12,12,11,11},
{7,7,6,6,5,5,4,4} };
//*************************************************************
●switch文がお気に入りです。if文で書くと、何となくスマートではないのですね。整理、整頓、清掃、清潔が身についているからでしょうか? ・・・いいえ、単にコスパ最高だからなんですわ。
● パネル(ケース)のスイッチ・RE位置とプログラム上のスイッチ・REの関係をここで決めています。テーブルの数値は、DX7の取扱説明書のp.30のDX共通 voice Parameterの番号です。RE1個あたりに2バイト使っています。レイアウトを変更する時に使用します。
●このリストの説明は次回にします。なんせ、メインルーチンが複雑な上に、これにともなうテーブルがまたでかい。op1からop6までの処理をきれいにまとめることができなかったからなのです。
● パネル(ケース)のスイッチ・RE位置とプログラム上のスイッチ・REの関係をここで決めています。テーブルの数値は、DX7の取扱説明書のp.30のDX共通 voice Parameterの番号です。RE1個あたりに2バイト使っています。レイアウトを変更する時に使用します。
●このリストの説明は次回にします。なんせ、メインルーチンが複雑な上に、これにともなうテーブルがまたでかい。op1からop6までの処理をきれいにまとめることができなかったからなのです。
今日の作業はここまで。ホッピーを焼酎で割ったのを飲みたいけど、飲み屋に行くとすぐ忘れてしまう。カルピスサワーで乾杯。
※このブログで書かれた内容によって生じた損害等の一切の責任を負いかねますので、予めご了承下さい。
音を創るということ4(製作編)
■部品リスト
$1は110円で換算しています。
$1は110円で換算しています。
部品名 | 数量 | 単価 | 小計(円) |
PIC18F27J53 | 1 | 270 | 270 |
PC900V | 1 | $0.266/10個 | 3 |
78N05 | 1 | 30 | 30 |
NJM2845 入手したのはリード付きだった |
1
|
50 | 50 |
1N4148W | 97 | $0.75/100個 | 80 |
ロータリーエンコーダ EC12E2420801 | 42 |
80
|
3360 |
ユニバーサル基板 片面115×150 | 1 | 814 | 814 |
スイッチ | 6 | 80 | 480 |
抵抗入りTr DTD114EA デジトラ10K-10K | 4 | 10 | 40 |
XH3P ベースポスト | 52 | $2.09/100個 | 120 |
XH3P ハウジング | 52 | $0.61/50個 | 70 |
XH3P ピン | 156 | $0.92/100個 | 158 |
ICソケット 6P | 1 | 15 | 15 |
ICソケット 28P 300mil | 1 | 70 | 70 |
ピンヘッダー2P&ジャンパー | 1 | 15 | 15 |
ピンヘッダー6P | 1 | 20 | 20 |
電解コンデンサ 6.3V 22uF | 1 | 10 | 10 |
電解コンデンサ 16V 100uF | 1 | 10 | 10 |
電解コンデンサ 50V 10uF | 1 | 10 | 10 |
積層セラミック 50V 0.1uF | 4 | 15 | 60 |
SMD抵抗 220 EIAJで3216サイズ | 5 | $3.45/1250個 | 1.5 |
SMD抵抗 280 E96 系列なのでE12の270で可 | 1 | $3.45/1250個 | 0.3 |
SMD抵抗 10K | 11 | $3.45/1250個 | 3.4 |
スズメッキ線 実測すると3m未満でした | 1 | 250 | 250 |
ケース タカチ YM-200 | 1 | 1400 | 1400 |
DINソケット(メス)5P | 3 | 60 | 180 |
電源プラグΦ2.1 | 1 | 60 | 60 |
つまみ(RE用) | 42 | $0.54/10個 | 250 |
DC9V 電源アダプター | 1j |
580
|
580 |
電線 AWG28 3色で各7m程度 | 3 | 630 | 1890 |
RE用六角ナット M9×0.75×2.5mm | 42 | 571/50個 | 480 |
RE用ワッシャー 9×16×1.5mm | 42 | 497/50個 | 418 |
合計 | 11185.7 |
●手持ちの部品を多数使用したので、リストの値段は秋月電子通商などの現在での価格を調査しました。
●SMD抵抗(コンデンサも)は詰め合わせパックで買うのがお得です。80種類×25個パックなどがあります。
keyword is SMD 1206 resistor kit assorted set Capacitor
●リストを見ると、アリババ(中国)から買うのがベストみたいですが、発注してから部品が到着するまで50〜60日かかる場合があると表示されています。つまり、時間を買うことに価値があるのかを見極めることです。
●RE用六角ナットとワッシャーはREの位置決めタブをケースに出さないためのものです。
●念の為にですが、つまみを紛失した時等のためにメンテナンス用部品も確保しています。
●SMD抵抗(コンデンサも)は詰め合わせパックで買うのがお得です。80種類×25個パックなどがあります。
keyword is SMD 1206 resistor kit assorted set Capacitor
●リストを見ると、アリババ(中国)から買うのがベストみたいですが、発注してから部品が到着するまで50〜60日かかる場合があると表示されています。つまり、時間を買うことに価値があるのかを見極めることです。
●RE用六角ナットとワッシャーはREの位置決めタブをケースに出さないためのものです。
●念の為にですが、つまみを紛失した時等のためにメンテナンス用部品も確保しています。
片面ユニバーサル基板にXH3Pのベースポストを配置していきます。レイアウトは行で12個、列で4個となります。XH3Pからの電線が集中するので、各ベースポスト間は余裕を持って配置しました。でも、マイコン他の部品のスペースがなくなるのがこわいです。
マイコン、PC900Vは後々のメンテナンスを考えてICソケットを使用しました。
表面の画像に逆流防止のダイオードが見えません。実は裏側(蛇の目パターン面)にハンダ付けしています。ダイオードの電極間のピッチはカタログ値で3.7mmあります。一方、蛇の目パターンはランドも含めると4mm弱となりますので、蛇の目パターンにダイオードを載せるとちょうどよい寸法となります(下のイメージの感じ)。
ハンダ付けの際は、フラックスをはんだ面に塗り、爪楊枝で軽く押さえながらはんだ付けをします。
キーワードは、はけ付き フラックス
とはいえ、完成後にテスターで導通チェックをしたところ、5個もはんだ付け不良が発生していました。5/96の不良です。52083ppmの不良では製品としてはちょっとですね。
抵抗、コンデンサなら3216とか3225(もちろんmmサイズ)のチップサイズがちょうどよい大きさです
余談となりますが、チップ部品を扱うとき(特に1608サイズ以下)にピンセットでつまむことがあります。ところが、普通のピンセットではつまむ時にはじいてしまう事故が意外に多いのです。そんな時は、通常は閉じているピンセットを使いましょう。100均でも扱っているので見つけたら即ゲットです。
キーワードは、逆作用 ピンセット
とはいえ、完成後にテスターで導通チェックをしたところ、5個もはんだ付け不良が発生していました。5/96の不良です。52083ppmの不良では製品としてはちょっとですね。
抵抗、コンデンサなら3216とか3225(もちろんmmサイズ)のチップサイズがちょうどよい大きさです
余談となりますが、チップ部品を扱うとき(特に1608サイズ以下)にピンセットでつまむことがあります。ところが、普通のピンセットではつまむ時にはじいてしまう事故が意外に多いのです。そんな時は、通常は閉じているピンセットを使いましょう。100均でも扱っているので見つけたら即ゲットです。
キーワードは、逆作用 ピンセット
何度もしつこいほど言いますが、REとスイッチの穴数は48個、それにMIDI端子が3個、電源アダプター穴が1個で、計52個もあります。これをハンドドリルで穴を開け、その後リーマで穴を拡げるという作業をするのは趣味の範囲を超えています。
そんな時にカインズホームの通販ページを見ていると、振動ドリルがお買い得品として1480円で売り出しているのを見つけました。恥ずかしながら、それまで、振動ドリルに電気ドリルの機能があるとは知らなかったのです。(決してカインズホームのまわし者ではありません。ただの電子工作員です。)
同じ頃、パーツ店で1520円のミニホールソーを見つけました。これらでREの9mmの穴はあっという間に開けることができました。
キーワードは、振動ドリル、ミニホールソー
MIDI端子(DIN5P)の穴径は14.8mm以上とあります。まず9mmの穴を開け、その後リーマーで穴を拡げました。これくらいなら体力を消耗するなんてことはないです。
キーワードは、振動ドリル、ミニホールソー
MIDI端子(DIN5P)の穴径は14.8mm以上とあります。まず9mmの穴を開け、その後リーマーで穴を拡げました。これくらいなら体力を消耗するなんてことはないです。
ケースに部品を取り付けている時に大きな失敗に気づきました。なんと、スイッチとDIN 5Pが当たってしまうのです。あと5mmスイッチを下にずらしておけばよかった。
■電装
何度もしつこいほど言いますが、XH3Pが52個で電線は146本あります。カシメの工具は株式会社エンジニアのPA-21を使用します。1本につき2回かしめると、きれいな状態になるようです。これを続けて作業に慣れると、なんだか段々ハイな気分になっていくのです。これってcaulking highっていうのかな。
何度もしつこいほど言いますが、XH3Pが52個で電線は146本あります。カシメの工具は株式会社エンジニアのPA-21を使用します。1本につき2回かしめると、きれいな状態になるようです。これを続けて作業に慣れると、なんだか段々ハイな気分になっていくのです。これってcaulking highっていうのかな。
■つまみ
部品を集めるのに、REのつまみを探すのが一番苦労しました。リアルのパーツ屋さんや通販部品のサイトなど、「パネルを埋め尽くすような数のつまみ」に合うようなつまみがないのです(日本語むずかしい、探しきれていないだけ)。アリババで見つけようと決めました。つまみはGoogleの英訳でknobと出ています。これを出発点としてさまよった結果、なんとか見つけることができました。
keyword is 360 Degrees Rotary Encoder D Half Shaft Hole Caps Knob
物の名前がわからないとなかなか進めないです。〇〇レンズのアプリに期待。
部品を集めるのに、REのつまみを探すのが一番苦労しました。リアルのパーツ屋さんや通販部品のサイトなど、「パネルを埋め尽くすような数のつまみ」に合うようなつまみがないのです(日本語むずかしい、探しきれていないだけ)。アリババで見つけようと決めました。つまみはGoogleの英訳でknobと出ています。これを出発点としてさまよった結果、なんとか見つけることができました。
keyword is 360 Degrees Rotary Encoder D Half Shaft Hole Caps Knob
物の名前がわからないとなかなか進めないです。〇〇レンズのアプリに期待。
正月は暖かだったです。気分は、「春なのにコスモスみたい」です(・・・・ちょっと違うな)。さぁ、冷たいビールで乾杯。
※このブログで書かれた内容によって生じた損害等の一切の責任を負いかねますので、予めご了承下さい。
前の10件 | -