2016-09-08 14:33 | カテゴリ:PIC応用回路
MIDIシーケンサーですが、ようやく完成したと思います(笑)


細かなバグをつぶしたり、使い勝手を上げるために、いろいろな手直しをしていました。
この辺は、実際にいろいろ操作しなければできないために、時間がかかってしまいました。

外見1


続きはこちらで。


機器の背面はこんな感じです。

外見2

そして、内部です。


内部全体

細いケーブルを使用したので、断線しやすいと思いますが、配線を内部に余裕で格納できました。

折角なのでビデオ撮りました。
見てやってください。





今回製作したMIDIシーケンサーのソフトで、皆さんのお役に立ちそうな関数(サブルーチン)少しご紹介させていただこうと思います。
以下は文章で、写真はありませんので、ご興味がない方は、面白くないかもしれません。(笑)
ご了承ください。
何か間違いがあればお知らせいただけると助かります。(MIDIシーケンサーから抜粋し、手直ししましたので)


まず、スイッチの入力に関する部分です。
スイッチ入力は、PICの電子回路には結構使用されますが、今回は二種類です。

1)スイッチ入力をチェックして、その状態を返す。
  スイッチが押されていなければ、その状態を返すので処理の流れは中断しません。

2)スイッチが押されるまで、処理が中断し、押されたら結果を返します。



1)のプログラム

//---------------------------------
// SW Get scanning SWON = 0
//---------------------------------

unsigned char SW_scan(void) {

if (SW_ent == 0) {
ans = 1;
}
while (SW_ent == 0) {
}
__delay_ms(10);
if (SW_bs == 0) {
ans = 2;
}
while (SW_bs == 0) {
}
__delay_ms(10);
return ans;
}

ここで、SW_entとSW_bsは、PICのポートに接続され、V+に10Kでプルアップされています。
実際に使用する際は、#define SW_ent RB1 などとして定義してください。

プログラムをご覧いただければお分かりと思いますが、
SWが押されたと検知(=0)したらans=1を立てます。
その後、押されたSWが戻されるまで待って、離されたら、10Msec待って抜けます。
なにもSWが押されていなければ、即座に抜けます。
10msecのDelayは、チャタリング防止です。


2)のプログラムは


//---------------------------------------------------------
// SW Get SW on/off (wait until SW is on)
//---------------------------------------------------------

unsigned char SW_get(void) {
unsigned char ans = 0;
while (ans == 0) {
if (SW_ent == 0) ans = 1;
if (SW_bs == 0) ans = 2;
}
__delay_ms(5);
while (SW_ent + SW_bs < 2) {
}; // SWが離されるまで待つ
return ans;
}

初めのWHILE文で、SWが押されるまで待ち、押されたSWに対応する数字をansに代入します。
5msec待ったうえで、次のWHILE文で、SWが離されるまで、待ちます。



(ロータリーエンコーダの読み取り)

次は、ロータリーエンコーダ(RE)の読み取りです。
REは、回転部分に2つの接点があり、回転に応じて、位相が異なるようにもう一つの接点につながります。

これをもとに、REの回転方向を検出するとともに、何回クリックされたかを検出することが出来ます。

「趣味の電子工作」さんのブログにREの説明がありますので、参考にしてください。

http://www.piclist.com/images/www/hobby_elec/ckt10_3.htm


私が製作したプログラムがこちらです。

//===================================== Rotary ==============================
// TMR0のオーバーフロー割り込み発生。
// 直前のAの値がHで今回のAがLならAがたち下がったと判断
// その時点でエンコーダのB出力のレベルを読み込んで、変数REに+1かー1を代入する
//--------------------------------------------------------------

void interrupt Rotary(void) {
//GREEN_LED = 1; // ON
if (A_pin == 0) {
if (old == 1) {
if (B_pin == 0) {
RE = 1;
} else {
RE = -1;
}
__delay_ms(50);
}
}
old = A_pin;
INTCONbits.TMR0IF = 0; //タイマー0割り込みフラグリセット
}

REの処理方法は、WEBでもいろいろ紹介されています。
私のは、TMR0割り込みを使用しています。
TMR0(タイマー0)はフリーランで使用して、オーバーフローが生じる度にこのREルーチンを実行します。
割り込みごとに、現在のピンの状態と、そのひとつ前の状態を比較して。回転方向を取得します。
その回転方向によって、-1か+1を返します。

回転していない場合は、前回の値のままです。
したがって、メインプログラムの中で、REの処理を行う前に、いったんRE値をゼロにし、その後、処理の中で、REを読み取り、即座に、RE値を0にリセットします。
この方法は、メインの処理系で、処理が完了したら、RE値をゼロにしますので、それまでは、REを回して予想外の値に処理を間違えないので良いと思います。


3)次は、REの応用です。
  REを回して、任意の位置に回転による数値をリアルタイム表示し、SWが押下されたときのREの値を返します。
  REで返すことが可能な数値の上限を引数として渡します。
  下限値はゼロです。

//---------------------------------
// ロータリーエンコーダで数値入力
// その値のEEPブロックを読み出し
// ブロック番号を表示。99は空き
// 指示された値をoutに入れて返る
//---------------------------------

int RE_input(int limit) {
int out = 0;
ans = 0;
RE = 0;
while (SW_ent == 1) {//スイッチが押下されるまで繰り返す
SW_scan();//スイッチをスキャン
if (RE != 0) {//タイマー割り込みでREが処理されるまで繰り返す
out = out + RE;
RE = 0;
if (out >= limit) out = limit;//数字の上下限の制限
if (out <= 0) out = 0;

Lcd_cursor(12, 1);//LCDのカーソル位置指定
chr2_disp((unsigned char) (out)); //REの読み出した値をカーソル位置に表示(10進2桁)
__delay_ms(50);
}
}
return out; // REで設定した(out)を返す
}


(上記で使用している10進数2桁表示)

引数は、unsigned charで1バイト、結果は10進数で二桁表示。

//--------------------------------------
// 引数(unsigned char)を10進数で
// カーソル位置からLCD表示(2桁)
//--------------------------------------

void chr2_disp(unsigned char number) {
int suji;
suji = (int) (number);
if (suji != 0) {
if (suji < 10) Lcd_disp("0");
itoa(buff, suji, 10);
Lcd_disp(buff);
} else {
Lcd_disp("00");
}
}


と、こんな関数を使用しました。


最後に、素人の私がいつもはまって、頭を悩ませているプログラムの落とし穴をご紹介します。
皆さんには、当たり前でしょうが、私はいつも悩まされています。(笑) 

XC8の場合

for (i = 0; i >= 10; i++)

while( i = 5)

if (i = 5)


これらは一見、問題なく動作しそうです。

しかし、for文の動作条件は、XXXである間繰り返す、ですから、i <= 10 とするのが正解です。

while文は、どう見てもこれでよさそうなんですが、while(i == 5)としなければちゃんと動作しません。
if分も同様で、i==5 としなければなりません。
これは、何度も何度もはまりました。(笑)

for文などは、BASICで、i を0から10まで繰り返す、と覚えていましたので、XC8をはじめた時には戸惑いました。
また、while文やif分の条件は、必ず、 i==5 と等号を二つ書かねばならず、これが、結構、動作不良を呼びました。
コンパイル時にはエラーにならないため、たちが悪いバグとなります。

また、for文は、i=1 と記述します、i==1ではありません。
これも間違いを誘います。


では、また。

Memoryspace.jpg


最後に、今回のMIDIシーケンサーの全プログラムをここに置きます。
2200行あまりになってしまいました。
PICのプログラムメモリは98%使用しました。(笑)

バグがあってもこれ以上、書き足しできません(汗)



管理者のみに表示する