音を創るということ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までの処理をきれいにまとめることができなかったからなのです。
今日の作業はここまで。ホッピーを焼酎で割ったのを飲みたいけど、飲み屋に行くとすぐ忘れてしまう。カルピスサワーで乾杯。
※このブログで書かれた内容によって生じた損害等の一切の責任を負いかねますので、予めご了承下さい。
コメント 0