ミニ・エレキーを解剖する-C言語処理編
2013-09-22
今回製作したエレキーは、Mid Rangeの8ピンPICにXC8(オールC言語)という組み合わせで窮屈なメモリに押し込んだことなど、ちょっと記録しておきたいところがありますので、得意の備忘記事にしてみます。
なお、実プログラムの即値定義として各種のdefine定義を行っていますが、以下「ON:1、OFF:0」というもののみ残す形にしてあります。
◆ BSSの初期化を自動で行わせない
初期設定が必要な変数には初期値を事前に入れておくわけですが、XC8のLinkerはスタートアップに絡む便利そうな関数を勝手に付けてきます。とりわけ、BSSの0クリアに関しては一気にクリアする処理を付けてきますので、「初期設定は確実に自分でやる
」という方には不要でしょう。これを外すと数十バイト分のプログラムエリア消費が無くなります・・・まぁ微々たるもんでも無駄は無駄
この処理の取っ払いは、「MPLAB X IDE」のプロジェクトのプロパティにチェックボックスを外すことで行えます。

◆ SLEEP機能を「前向きに使う」
C言語のmain関数は以下のようになっています。
メインループで「やることがないぞ
」となれば即就寝・・・という作りです。変数「sts」は状態遷移表により「どんな状態か」を記憶しています。この値が「0」の時には何も実行している処理がないという判断で寝てしまいます。SLEEPからの復帰は、エレキーが待機状態で期待する動き・・・即ち、パドル操作か本体タクタイルスイッチ押下を待っていることになります。これを実現するために、初期設定関数内では以下のように初期化を行っています。
今回は全入力ポートが外付け抵抗でプルアップしていますから、パドルやスイッチがONになると当該ビットが「1⇒0」に動くため、「アクティブ・ロー」(ネガティブ・エッジ)でコントロールするようにIOCレジスタを設定しています。さらに、OPTION_REGで弱プルアップを禁止しています。
割込処理ではIOCIFをチェックし、立っていればポート読み出しをしてメインラインの処理に渡す・・・というやり方です。
パドル入力はエレキー処理のキューへ渡し(i_key_change)、スイッチ操作はメインラインのキュー(i_put_event)に渡すという形です。
この割込処理が動作するとmain関数のSLEEPから起き上がりNOPからスタートします(このNOPはデバッグ用に入れたまんまになっています)。そしてwhile文を前に戻り、割込処理でキューイングした個々のイベントを「xxx_handler」が覗き込み、実際の処理を開始します。すると、次の動作を待つ状態になるため「sts」が0以外の値となり、メインラインが空転を開始して処理を進め、やがて「sts」が0に戻るとまた寝るという形です。
イベントが起こるまでSLEEPするという作りは、今後もPICで何か作る場合には大いに利用して「省エネ化」を図りたいと思います。
◆ 状態遷移表関連の処理
状態遷移表については、ミニ・エレキーのとりあえずの完成記事に載せてありますが、これをハンドリングする処理は非常に簡単です(以下、uchar : unsigned char)。
記事にある状態遷移表のものとは違いますが、要は「ある状態でやるべき処理があれば、その関数を書いたデータ」を作っておけばいいだけです。処理のないところは「何もしない関数」(上記ではkproc_null:単にリターンのみの関数)を記述して埋めておけばOK・・・なんですが、以下の欠点があります。
1) スカスカの遷移表では、データに無駄が多くなる(上記の場合「kproc_null」だらけに・・・)
2) 横の要素数を2のべき乗の数(2,4,8・・・)にしないと、掛け算処理が必要になる
3) XC8では大きさに制限がある(最大255⇒同様な属性の部分と合わさってしまうため、実際の最大数は作りによる)
案の定、最初のバージョン完成時は何とかなっていましたが、結局3) に引っかかってしまいマイナーチェンジしました。
なお、実プログラムの即値定義として各種のdefine定義を行っていますが、以下「ON:1、OFF:0」というもののみ残す形にしてあります。
◆ BSSの初期化を自動で行わせない
初期設定が必要な変数には初期値を事前に入れておくわけですが、XC8のLinkerはスタートアップに絡む便利そうな関数を勝手に付けてきます。とりわけ、BSSの0クリアに関しては一気にクリアする処理を付けてきますので、「初期設定は確実に自分でやる


この処理の取っ払いは、「MPLAB X IDE」のプロジェクトのプロパティにチェックボックスを外すことで行えます。

◆ SLEEP機能を「前向きに使う」
C言語のmain関数は以下のようになっています。
void main(void) { initializer(); // 初期設定 while (1) { key_handler(); // キー処理 event_handler(); // イベント処理 if (sts == 0) { // 処理無し・・・ SLEEP(); // イベント待ち NOP(); } } } |
メインループで「やることがないぞ

OPTION_REG = 0b11001000; // No WPU, Prescaler 1/2 : IOCAN = 0b00001111; // Negエッジ IOCIE = ON; // IOC使用 |
今回は全入力ポートが外付け抵抗でプルアップしていますから、パドルやスイッチがONになると当該ビットが「1⇒0」に動くため、「アクティブ・ロー」(ネガティブ・エッジ)でコントロールするようにIOCレジスタを設定しています。さらに、OPTION_REGで弱プルアップを禁止しています。
割込処理ではIOCIFをチェックし、立っていればポート読み出しをしてメインラインの処理に渡す・・・というやり方です。
if (IOCIF == ON) { if ((IOCAF&0b00000011) != 0) { i_key_change(); // キー入力確認 IOCAF &= 0b11111100; } if ((IOCAF & 0b00001100) != 0) { i_put_event(); // SW押下 IOCAF &= 0b11110011; } IOCIF = OFF; } |
パドル入力はエレキー処理のキューへ渡し(i_key_change)、スイッチ操作はメインラインのキュー(i_put_event)に渡すという形です。
この割込処理が動作するとmain関数のSLEEPから起き上がりNOPからスタートします(このNOPはデバッグ用に入れたまんまになっています)。そしてwhile文を前に戻り、割込処理でキューイングした個々のイベントを「xxx_handler」が覗き込み、実際の処理を開始します。すると、次の動作を待つ状態になるため「sts」が0以外の値となり、メインラインが空転を開始して処理を進め、やがて「sts」が0に戻るとまた寝るという形です。
イベントが起こるまでSLEEPするという作りは、今後もPICで何か作る場合には大いに利用して「省エネ化」を図りたいと思います。
◆ 状態遷移表関連の処理
状態遷移表については、ミニ・エレキーのとりあえずの完成記事に載せてありますが、これをハンドリングする処理は非常に簡単です(以下、uchar : unsigned char)。
static void kproc_null(); static void kproc_0000(); static void kproc_0001(); static void kproc_0002(); static void kproc_0103(); : static void kproc_0503(); static void (*mtx_p[6][4])() = { {kproc_0000,kproc_0001,kproc_0002,kproc_null}, // 00 {kproc_null,kproc_null,kproc_null,kproc_0103}, : {kproc_null,kproc_null,kproc_null,kproc_0503} // 05 }; uchar ksts; : static void handler(uchar evno) { (mtx_p[ksts][evno])(); } |
記事にある状態遷移表のものとは違いますが、要は「ある状態でやるべき処理があれば、その関数を書いたデータ」を作っておけばいいだけです。処理のないところは「何もしない関数」(上記ではkproc_null:単にリターンのみの関数)を記述して埋めておけばOK・・・なんですが、以下の欠点があります。
1) スカスカの遷移表では、データに無駄が多くなる(上記の場合「kproc_null」だらけに・・・)
2) 横の要素数を2のべき乗の数(2,4,8・・・)にしないと、掛け算処理が必要になる
3) XC8では大きさに制限がある(最大255⇒同様な属性の部分と合わさってしまうため、実際の最大数は作りによる)
案の定、最初のバージョン完成時は何とかなっていましたが、結局3) に引っかかってしまいマイナーチェンジしました。
static void kproc_null(); static void kproc_0000(); static void kproc_0001(); static void kproc_0002(); static void kproc_0103(); : static void kproc_0503(); static void kproc_00(uchar); static void kproc_01(uchar); static void kproc_02(uchar); static void kproc_03(uchar); static void kproc_04(uchar); static void kproc_05(uchar); static void (*mtx_p[6])(uchar) = { kproc_00,kproc_01,kproc_02, kproc_03,kproc_04,kproc_05 } uchar ksts; : static void handler(uchar evno) { (mtx_p[ksts])(evno); } static void kproc_00(uchar evno) { switch (evno) { case 0 |
- 関連記事
-
- ミニ・エレキーを解剖する-モールス符号複合化編
- ミニ・エレキーを解剖する-モールス符号データ編
- ミニ・エレキーを解剖する-C言語処理編
- ミニ・エレキーのバージョンアップ完了!
- 懲りずにエレキーのバージョンアップ