こんばんは!
最近、部屋を模様替えしたのに、誰も遊びに来てくれないゆってぃです!

最近気づいたのですが、このクソみたいに内容の無いブログを、なんだかんだで結構多くの方々がご覧になってくださっているようです。

ありがとうございます!^^

そして

本当に申し訳ございません…(土下座)

きっと、「PIC C 入門」とかで検索するとヒットしちゃうんですよね…。
FC2ブログさんはしっかりしてるから、googleでも上の方に出ちゃうみたいです。

ですので、今日はきちんと技術的なことを書きますよっ!!
タイトルに書かれているような、C言語の飛び道具的なコーディング法をお伝えしようと思います。

みなさんは、オブジェクト指向言語って聞いたことはありますか?

「ナメんなよ、ハゲ」

ご…ごめんなさいっ!!って、まだハゲてないよ!!(;_;)

詳細は省きますが、クラスとかカプセル化とか、ああいう奴です。
C言語にはクラスという概念がないので、完全なオブジェクト指向の書き方は出来ないのですが、工夫次第で近づけることは出来ます。

まず、C++やJAVAで使われるクラスとは、ある関連づいたデータ(変数)と手続き(関数)をひとまとめにしたものです。
ここで、データをメンバ変数、手続きをメソッドと呼びます。
Cでも、データだけなら構造体として表現できますね。
この構造体の中に関数ポインタを持たせることで、メソッドに近い表現を行うことができます。

LEDを点滅させるだけの単純なプログラムでも、(それが良いか悪いかは置いといて)オブジェクト指向風に記述することは出来ます。

ですので、以前に紹介したPICマイコンのLED点滅プログラムを、オブジェクト指向っぽく書いてみましょう。

オブジェクト指向は、コーディングだけではありません。
コーディング以前の設計段階(設計仕様書を各段階です)から始まっています。
というか、オブジェクト指向に関わらず、ソフトウェア開発ではこの設計仕様が極めて重要です。
適当に作り始めてしまうと、コードがぐちゃぐちゃになりがちで、後からエラいことになってしまいます。
そもそも、設計が適当だとチームリーダーが実装(コーディング)に入らせてくれません(涙)

さて

さっそく、LEDを点滅させるクラスを設計してみましょう。

LEDを光点滅させるわけですから、LED制御クラスと名づけます。
クラス名は、LED_CTRL_CONDITIONとでもしましょう。(適当すぎるかな…笑)

さて、どんなメンバ変数がいるでしょうか?
点滅なのですから、点滅周期は必要でしょうね。
せっかくですから、今後の拡張に備えて点灯時間と消灯時間を異なる値にしてみましょう。
となると、点灯時間消灯時間のふたつのメンバ変数が必要ですね。
とりあえずはこれくらいでしょうか?

まとめると、メンバ変数は

1.LED点灯時間
2.LED消灯時間

のふたつです。

次はメソッドです。
メソッドは、すなわち具体的な操作なので

1.ポートを初期化する
2.LEDの点灯時間をセットする
3.LEDの消灯時間をセットする
4.LEDを点灯させる
5.LEDを消灯させる
6.LED点滅用タイマーを初期化する
7.LED点滅用タイマーを起動する

ぐらいでしょうか?
今回は多めに書きましたが、たとえば2と3を合わせて
「LEDの点・消灯時間をセットする」
としてみたり、4と5を合わせて
「LEDを点・消灯させる」
としても良いと思います。

なお、これは完全に我流なので特にオススメはしませんが、僕はマイコンのレジスタにアクセスが必要な操作は、各クラスのメソッドのみというルールを設けています。
逆に言えば、メソッド以外の関数では、決してレジスタアクセスはしません。

たとえば、

TRISA = 0x00;
TRISB = 0x00;

という操作はレジスタにアクセスしているので、LED制御クラスのメソッドに持たせます。
さきほど考えたメソッドの「1」に該当しますね。
メソッド名は、そうですね…initLedPort()とかにするでしょうね。

どうしてこのような事をするかというと、こうしておけば、ソースを別システムに移植する際に、各クラスのメソッドのみを書き直せば良いからです。(実際は、それだけじゃダメな場合も多々ありますが…コアが変わらなければイけると思います)
要は、足回り(僕はレジスタアクセスを含む下位レイヤをこう呼びます(笑))を明確に出来るわけです。

では、ここで設計したクラスをコードに直してみましょう。

typedef struct TAG_LED_CTRL_COND{

 //メンバ変数
 unsigned char isInitialized;//初期化フラグ(とりあえず無視してください…笑)
 unsigned long OnTime;//LED点灯時間
 unsigned long OffTime;//LED消灯時間

 //メソッド
 void (*initLedPort)(void); //LEDポートを初期化する
 void (*setOnTime)(unsigned long time); //LEDの点灯時間をセットする
 void (*setOffTime)(unsigned long time); //LEDの消灯時間をセットする
 void (*onLed)(void); //LEDを点灯させる
 void (*offLed)(void); //LEDを消灯させる
 void (*initLedTimer)(struct TAG_LED_CTRL_COND *this); //LED用点滅タイマーを初期化する
 void (*startLedTimer)(void); //LED用点滅タイマーを起動する

}LED_CTRL_CONDITION;

メソッドの戻り値は全てvoid型にしてしまいましたが、丁寧に作るならBOOL型(unsigned charで代用可能)とし、成功・失敗を返すと良いですね。
また、引数もバラバラですが、とりあえず自分自身(LED_CTRL_CONDITION)を渡しておくと、あとで拡張がしやすくなります。
(※引数未使用の警告が出ますが、適当にvoid型ポインタへのキャストとかを挟めば消せます)
それだけでなく、メソッドのインターフェースが統一されるので、typedefを使えば関数ポインタの宣言もラクちんになります。

typedef void (*SET_LED_REG)(struct TAG_LED_CTRL_COND *this);

と上の方に書いておけば、メソッド宣言部分が

 //メソッド
 SET_LED_REG initLedPort; //LEDポートを初期化する
 void (*setOnTime)(unsigned long time); //LEDの点灯時間をセットする
 void (*setOffTime)(unsigned long time); //LEDの消灯時間をセットする
 SET_LED_REG onLed; //LEDを点灯させる
 SET_LED_REG offLed; //LEDを消灯させる
 SET_LED_REG initLedTimer; //LED用点滅タイマーを初期化する
 SET_LED_REG startLedTimer; //LED用点滅タイマーを起動する

となってスッキリします。

ちなみに、最終的にLED_CTRL_CONDITIONという構造体名にtypedefしてるのに、なぜTAG_LED_CTRL_CONDという名前をつけたかというと、initLedTimer()メソッドの引数に自分自身を取りたかったからです。
この関数ポインタを宣言した時点では、まだLED_CTRL_CONDITIONという名前が出てきていないため、このような手段をとりました。


おお、気がつけばずいぶん長くなってしまいましたね(汗)

今日はここまでにしましょう^^
最後まで読んでいただいて、ありがとうございました!!(感謝)
関連記事
スポンサーサイト
コメント
コメントの投稿
トラックバック URL
トラックバック
ご訪問者様
プロフィール

ゆってぃ

Author:ゆってぃ
経歴7年の組み込み系・制御系エンジニアです。
("ど素人"という文言は取りました…笑)
ソフトウェア開発経験ゼロの状態から、なんとか実務がこなせるようになってきた現在に至るまでの経験を、備忘録代わりに綴っていきたいと思います。
入門者の方、大歓迎!
(上級者の方、ごめんなさい…)

あと、ブログには全然関係ないですが、Bumpy Headというバンドのギターをやっています。
ライブ情報なんかも書いたりすることがあるので、その時に「行ってもいいよ~」といった感じのコメントを戴けると、泣いて喜びます(泣)
ブログ読んでくださってる方なら、チケット代サービスしちゃいます!

最後に…滅多に流用することは無いでしょうが、このブログに書かれているソースは、特に指定の無い限りMITライセンスとします。ただし、一部それ以外のものもございますのでご注意下さい。
※ブログのリンク先にあるコードに関しては、リンク先のポリシーに従ってください。

最新記事
最新コメント
カテゴリ
RSSリンクの表示
メールフォーム

名前:
メール:
件名:
本文:

twitter
リンク
ブロとも申請フォーム
スポンサードリンク