Category : PIC
上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
こんばんは!
このところ月1ペースで友達の結婚式に参加して、もはや自分も結婚した気でいるゆってぃです。

ていうか、よくテレビとかでカップルが
「友達の結婚式で出会って、そのまま付き合うことに・・・」

とか言ってるの見ますが

あれ、どうやるの??

いや、普通に無理でしょ!
だって、初対面だよ??
何を話せば良いのだよ!
大体ね、そんな高レベルのコミュ力がもともと備わっているなら
わざわざ結婚式で出会ってなくてもとっくに相手ができてるっつーの!(涙)


デュフンデュフン!!!!


さて、今日はPICマイコンのコンフィグレーションビットの設定方法についてお話いたします。
そもそもコンフィグレーションビットは、PICの設定項目の中でも、絶妙に厄介な項目です。
ここで躓いてしまう人も結構いるでしょう。
その原因は、他のレジスタの様に、プログラム上で設定できない
ことにあると思います。

後述しますが
PORTA = 0x01;
みたいな要領では設定が出来ない、ある意味ロックな設定値なのです。

なんでそんな事になっちゃってるのか。
PIC16Fシリーズを例に取ると、、そもそもこのシリーズのPC(プログラムカウンタ)は13bitしかありません。

------------------------------------------
<勝手にコラム PCとはなんぞや>
マイコンにしろメディアプロセッサにしろ、CPUにはPCとよばれるレジスタが必ず存在します。
このレジスタには、次に実行する命令のアドレスが格納されます。
例えば、ある関数がROM上の0x0FFに配置されていて、関数呼び出しでそこにジャンプする場合は、PCの値が0x0FFになります。

-----------------------------------------------------------------

PCが13bitしかないということは、使用できるアドレスは0x000~0x1FFまでということです。
PIC16FXXシリーズは、基本的にこの構造になっているはずです。
にも関わらず、コンフィグレーションビットのアドレスは、0x2007(PIC16F84A / PIC16F88など)に配置されていたりします。
ここはPCの範囲を超えた特別な領域であり、当たり前ですがソフト動作時にはアクセスできません。
ですので、プログラムを書き込むときに、一緒に書き込んでしまうのです。

ソフト動作時にアクセスできないということは、他のレジスタの様に
PORTA = 0x01;
とかは出来ない訳です。

じゃ、どうするか。
XC系統のコンパイラでは、pragma指令でコンフィグレーションビットを設定します。
例えば
#pragma config PWRT = 0
の様に書きます。

まぁ、PIC16F8Xシリーズの様にコンフィグの数が少なければこれでも良いのですが
例えば、PIC18Fシリーズなどになると、コンフィグレーションビットの数がかなり増えちゃいます。
実際に書くと、以下の様な感じになります。


// CONFIG1H
#pragma config FOSC = INTIO7
#pragma config FCMEN = OFF
#pragma config IESO = OFF

// CONFIG2L
#pragma config PWRT = 0
#pragma config BOREN = SBORDIS
#pragma config BORV = 18

// CONFIG2H
#pragma config WDTEN = OFF
#pragma config WDTPS = 32768

// CONFIG3H
#pragma config CCP2MX = PORTC
#pragma config PBADEN = ON
#pragma config LPT1OSC = OFF
#pragma config HFOFST = ON
#pragma config MCLRE = ON

// CONFIG4L
#pragma config STVREN = ON
#pragma config LVP = ON
#pragma config XINST = OFF

// CONFIG5L
#pragma config CP0 = OFF
#pragma config CP1 = OFF

// CONFIG5H
#pragma config CPB = OFF
#pragma config CPD = OFF

// CONFIG6L
#pragma config WRT0 = OFF
#pragma config WRT1 = OFF

// CONFIG6H
#pragma config WRTC = OFF
#pragma config WRTB = OFF
#pragma config WRTD = OFF

// CONFIG7L
#pragma config EBTR0 = OFF
#pragma config EBTR1 = OFF

// CONFIG7H
#pragma config EBTRB = OFF



ひとつひとつ手で書いていくのは、ちょっと大変ですね。
そこで、MPLAB Xでは、このコードが自動生成できるようになっています。

メニューから、Windowを選択します。
(事前に、自動生成を行うプロジェクトを、メインプロジェクトにしておいてくださいね)
次に、PIC Memory Viewsを選択し、最後にConfiguration Bitsを選択します。
すると、コンフィグレーションビットの設定画面が表示されます。
ここで、データシートを見ながら必要な設定をしたあと、Generate Source Code to Outputボタンを押すと
あら素敵!
pragma指令されたソースコードが表示されます。

あとはこれを、自分のコードにコピペすればOKです。
とっても便利な機能なのですけど、もし出来れば、一度は自分の手でプログラミングしてみて下さいね^^

最後までお読み頂いて、ありがとうございました!
スポンサーサイト
こんばんは!

すっかりご無沙汰のゆってぃです。
会社がお正月休みに入ったので、この隙にブログを更新します!(笑)
ただ、最近は残業ばかりで電子工作する暇が無くて・・・書くことがないのです(涙)

という訳で
(苦し紛れではありますが)今回は、MPLAB X IDEから、PICkit経由でPICにプログラミングを行う方法をお伝えします。
今回はPICkit3を例にとって説明します。

まずは、ブレッドボードを用いてPICkit3とPICをを接続します。
(※ここでは、オンボード書き換えは考慮していません。ブレッドボードなどでプログラミングを行った後、基板に実装することを前提としています)
基本的に、接続するピンは5本です。

PICkitは、1ピンから順に
1: /MCLR
2: VDD
3: VSS
4: PGD
5: PGC
6: LVP
となっています。このうち、1 ~ 5ピンを接続してください。6は、オンボード時の書き込みなどで使用するので、今回は割愛します。

さて、/MCLRとVDD、VSSはすぐにわかると思うのですが、PGDとPGCは、マイコンによってはピンダイアグラムに記載がない場合があります。
(数年前にご紹介したPIC16F84Aも書いてありません)
そんな時は、 PINOUT DESCRIPTIONSのページを見ていただけると、表のどこかにSerial programming clock とかSerial programming data とか書いてあります。
これらのピンがPGCやPGDに対応しているので、接続してください。

接続が完了したら、次は書き込みです。
なお、ゆってぃのMPLAB Xはバージョンが少し古くて、v1.51です。
新しいものだと、少し要領が異なるかもしれません。

まず、書き込みを行うプロジェクトの上にカーソルを移動させます。
以下の例では、K11というプロジェクトを選択しています。
プロジェクト選択

プロジェクトの上で右クリックし、Set ConfigurationからCustomizeを選択します。
コンテクストメニュー

option categories から Power を選択します。
その後、Power target circuit from PICkit3にチェックを入れ、Voltage Level を設定します。
この時、使用するマイコンに合った電圧を選択してください。
正しく設定が出来たら、OKをクリックします。
パワーサプライ選択

次に、プロジェクト名の上で右クリックし、メニューからMake and Program Deviceを選択します。
すると、プロジェクトがビルドされ、デバイスへのプログラム書き込みが行われます。
この時、ダイアログボックスが現れ、以下の様な注意文が表示されます。

CAUTION.jpg

要約すると
MPLAB Xで選択しているマイコンと、実際に接続しているマイコンは間違ってないよね?
3.3V系のマイコンに5Vを入れちゃったりすると、壊れちゃうからね!!

と確認しているのです。
念のためご確認頂き、問題がなければOKを選択します。

すると、outputウィンドウでprogramming...という文字が表示され、しばらくするとProgramming/Verify completeという文字が表示されます。

これで書き込みは完了です^^

最後までお読み頂いて、ありがとうございました!
こんばんは!
仕事も正月休みに入り、何をしようかウキウキしているゆってぃです!

実は今日が正月休み初日なのですが、とりあえずブログの更新くらいしかやるべきことが思いつかなかったので、こうして記事を書き始めてしまいました。

さて、今回は久々にPICマイコン関連の話です。
実は当ブログ、本当に多くの方からPIC関連の記事にアクセスをいただいております。
みなさま、本当にありがとうございます。
多くの方がご覧になってくださるお陰で、僕自身も新たな知識を身につけようという意欲が沸いてくるのです。
いや、これ本当ですよ(笑)

だって最近、このブログの更新以外に楽しみがないんですもん(涙)

さて、以前の記事(MPLAB導入)で、MPLABの導入からプログラムのビルドまでをご紹介致しましたが
現在ではMPLAB X IDEという次世代のIDEがMicrochip社から提供されています。

となれば、当ブログでも当然、その使用方法を記事に取り上げない訳には参りません!

それでは、早速IDEのダウンロードからご説明いたします。

1.ダウンロード

MPLAB X IDEのインストーラーは、下記のリンクからダウンロードすることが可能です。

MPLAB X IDE ダウンロードページ

まず、ページの左側にあるFREE DOWNLOADと書かれた箇所をクリックします。
(下の画像では、赤枠で囲んであります)

MPLAB_X_install_1.jpg

次に、MPLAB X IDE v1.51(バージョンは更新される可能性があります)と書かれたリンクをクリックします。
ダウンロードフォルダ選択画面が現れるので、適当なフォルダへダウンロードしてください。
なお、今回はOSがWindowsということで話を進めますが、Linux用、Mac用のIDEも用意されています。
さすがMicrochip社ですね!

MPLAB_X_install_2.jpg

また、一緒にコンパイラもダウンロードしておきましょう。
無料のコンパイラとして、XCコンパイラが提供されております。
(有償版もありますが、小さなプログラムでは無料版で問題ありません)
今回はPIC16系がターゲットデバイスなので、XC8コンパイラのインストーラーをダウンロードします。
因みに、XC16はPIC24系、XC32はPIC32系用のコンパイラです。

MPLAB_X_install_3.jpg

ダウンロードが完了したら、インストールをお願いします^^

2.MPLAB X IDEでのプロジェクト作成からビルドまで

MPLAB X IDEを起動したら、まずはメニューバーのFileをクリックします。

MPLAB_X_1.jpg

ファイルメニューが表示されるので、New Project...をクリックします。

MPLAB_X_2.jpg

プロジェクトウィザードが起動するので、
CategoriesMicrochip Embedded
ProjectsStandalone Project
を選択し、Nextを押します。

MPLAB_X_3.jpg

Select Device画面では
FamilyMid-Range 8-bit MCUs (PIC12/16/MCP)
DevicePIC16F84A
を選択し、Nextを押します。

MPLAB_X_4.jpg

Select Tool画面では、サポート外ですがとりあえずPICkit2を選択し、Nextを押します。
(書き込みには、PICkit2 Programmerを使用するため)
ただ、ここはお持ちのツールを選択していただいて結構です。

MPLAB_X_5.jpg

Select Comopiler画面では、XC8を選択し、Nextを押します。

MPLAB_X_6.jpg

Select Project Name and Folder画面では、プロジェクトの名前と、プロジェクトを作成するフォルダを入力します。
今回は、LED_SAMPLE_01というプロジェクト名にしました。
入力が完了したら、Finishを押します。

MPLAB_X_11.jpg


すると、ウィザードが自動で閉じて、また最初の画面に戻ります。
この時、画面左側のプロジェクトビューに、今作成したLED_SAMPLE_01という名前のプロジェクトのツリーが表示されていれば、プロジェクトの作成は成功です。
(以下の画面例では、LED_SAMPLE_01の下にPIC16F648A_SAMPLE_02というプロジェクトも表示されておりますが、これは僕が以前作成したプロジェクトが残っているだけですので、気になさらないで下さい)

MPLAB_X_8.jpg

次に、画面左上のNew Fileボタンをクリックします。

MPLAB_X_9.jpg

ファイル作成ダイアログが現れ、ファイルタイプ選択画面が表示されます。
今回は、C言語のソースファイルを作成するので、
CategoriesC
File TypesC Source File
を選択します。

MPLAB_X_10.jpg

次に、ファイル名と保存フォルダ選択画面が表示されます。
File Namemain
にします。
フォルダなどは、特に指定がなければそのままFinishボタンを押します。

すると、ファイル作成ダイアログは閉じられ、エディターが表示されます。
ここに、ソースコードを書いていくことになります。

MPLAB_X_12.jpg


ここまで長かったですね!
お疲れ様でした!


とりあえずここでひと休みしましょう!
ジュースを飲んだりおやつを食べたり、適度に休憩を取ってくださいね^ ^


(休憩中…)


さて、今回もLEDを点滅させるプログラムを作成してみましょう!
さっそく、以下のソースコードを、エディター内に記述してください。

おっと、何度も言いますが、コピペではダメですよ!
短いソースコードですので、自分の手で書いてくださいね><
これはね
学生時代、多くのレポートをコピペで済ませてしまっていたために
社会に出てから非常に苦労したがいうのだから

間違いありません!

学生時代にきちんと勉強してきた人と
ゆってぃの様にサボりまくっていた人では
社会に出てからの初速が全然違います!

特に組み込みエンジニア系では、学生時代にどれだけ回路やソフトを真面目にやっていたかが、仕事の速度や結果に如実に出てきてしまいます!

「学生時代に勉強したことなんて、社会じゃ役に立たないし」
なんて言わないで下さい。
僕が6年前くらいに、皆さんの分まで、十分に言っておきましたから(涙)

確かに、学生時代に勉強したことが業務に直結するケースは少ないかもしれませんが
ベーシックな部分では、どこかで必ず繋がっています。
無理をしすぎない程度でよいので、頑張ってくださいね!

ではでは、説教臭い前置きが長くなりましたが、ソースコードを以下に記載いたします(笑)


#include <stdio.h>
#include <stdlib.h>
#include <xc.h>

#pragma config CP = 0x03ff, PWRTE = 0, WDTE = 0, FOSC = 0x3

static void InitPortIOReg(void);

int main(int argc, char** argv) {

int i = 0;

InitPortIOReg();

while(1){
PORTAbits.RA0 ^= 1;
for(i=0;i<1000;i++);
}

return (EXIT_SUCCESS);

}

static void InitPortIOReg(){

TRISAbits.TRISA0 = 0;

}


XC8コンパイラは、Hi-TECH C コンパイラと少し記述方法が異なります。
まず、コンフィグレーションは、以下の方法で記述します。

#pragma config "セットするビット名" = "値", "セットするビット名" = "値", ・・・

それぞれのコンフィグレーションビットは、今回の例のようにカンマで区切って設定します。
記述方法の詳細は、XC8コンパイラのマニュアルをご参照下さい。
(英語ですが…)

※コンフィグレーションに関しては、以下の記事をご覧下さい。後半部分に簡単な説明があります。
PICマイコンで遊ぶ 超入門編(C言語)

セットするビット名は、各PICマイコンのデータシートに書かれているコンフィグレーションビットの名前をそのまま書きます。
PIC16F84Aでは、データシートの21ページにコンフィグレーション(ワード)の各ビットにおける詳細な説明がありますので、各ビット名と役割はこのページでご確認下さい。

(すでにダウンロード済みの方もいたっしゃるとは存じますが)
データシートはここからダウンロード可能です。

ここから先の説明は、このデータシートが必須になりますので
お手数ですがダウンロードをお願いしますm(_ _)m

もちろん、英語ですよ!
それだけで心が折れそうになりますが、これからの日本のものづくりを支えるためにも、一緒に頑張って行きましょう!
(いやぁ、我ながらでかい口叩くなぁ…)

それでは、今回の設定内容をひとつひとつ見ていきましょう!

まず

CP = 0x03ff

これは、コードプロテクションをオフにしています。

---

PWRTE = 0

これは、電源投入時のパワーアップタイマーをオンにしています。
(なお、このビットは"1"でオフ、"0"でオンなのでご注意を!)

--

WDTE = 0

ここでは、ウォッチドッグタイマーをオフにしています。

--

FOSC = 0x3

ここでは、発振モードを選択しています。
今回は外付けRC発振を用いるので、0x3を書き込んでいます。

--

以上で、コンフィグレーションの設定は完了です。




それでは、次にmain関数の中を見ていきましょう!

main関数内では、いきなりInitPortIOReg()関数が呼ばれています。

InitPortIOReg()関数の中を見ると、

TRISAbits.TRISA0 = 0;

と書かれています。

ここでは、TRISAレジスタのTRISA0ビットに0をセットしています。

TRISAレジスタとは、PORTA端子(RA0~RA4)の端子設定を行うレジスタです。
つまり、それぞれの端子を入力(Input)端子として使うのか、出力(Output)端子として使うのか、ということですね。
ここで、データシートの15ページを見ると、PORTAに関しての説明が書かれています。

今回必要な部分だけ要約すると
「PORTAは5ビットだよ」
「TRISAを0にセットすると出力ポート、1にセットすると入力ポートになるよ」
と書かれています。

今回はPORT0(RA0端子)を出力端子として使いたいので、TRISA0ビットに0をセットしている訳ですね。

「おい。TRISAbits.TRISA0 = 0とかの書き方はどこに書いてあるんだよハゲ!」

ゆってぃ「だ…!だからまだハゲてないって!!」

これらのレジスタ表記方法は、XC8コンパイラをインストールしたフォルダ内にある、pic16f84a.hをご覧下さい。

今回のTRISA部分だけ抜粋すると、以下のように書かれています。


// Register: TRISA
extern volatile unsigned char TRISA @ 0x085;
#ifndef _LIB_BUILD
asm("TRISA equ 085h");
#endif
// bitfield definitions
typedef union {
struct {
unsigned TRISA0 :1;
unsigned TRISA1 :1;
unsigned TRISA2 :1;
unsigned TRISA3 :1;
unsigned TRISA4 :1;
};
} TRISAbits_t;
extern volatile TRISAbits_t TRISAbits @ 0x085;


ご覧の様に、bitごとにアクセスできるようにヘッダファイル内で定義してくれているので、共用体へアクセスする要領で、それぞれのレジスタの各ビットへアクセスできます。

俺、あんま共用体を使いたくねーんだよな…

という方は、以下の記述方法でも大丈夫です。

TRISA0 = 0;

この記述方法でも、TRISAレジスタのTRISA0ビットを0に設定します。
pic16f84a.hでは、同じレジスタにアクセスする場合でも、プログラマーにコーディングスタイルに合わせて書けるように、複数の定義がなされているのですね。

素晴らしい!

どちらでも、お好きな方で結構ですよ^ ^

それでは、コードの解説を続けます。


while(1){
PORTAbits.RA0 ^= 1;
for(i=0;i<1000;i++);
}



無限ループ内で、一定周期毎にPORTAレジスタのRA0ビットを反転させています。

PORTAbits.RA0 ^= 1;

TRISAレジスタで出力に設定されたポートは、POARTAレジスタに書き込んだ値がそのまま出力されます。
今回は、PORTAレジスタのRA0レジスタの設定値を一定周期で反転させることで、RA端子からの出力波形もそれに応じて反転させています。
(なお、レジスタへのアクセス方法は、先ほどのTRISAレジスタの時と同様ですので割愛します)

以上で、ソースコードの解説は終わります!
非常に短いコードでしたが、内容はたっぷりでしたね(笑)
書き終わったソースコードは、かならず保存してくださいね。
(Ctrlキーを押しながら、Sキーで上書き保存できます)


それでは、エディタにコードが書き終わったら、いよいよビルドです!

ビルドの前に、今書いたソースコードをプロジェクトに組み入れます。
まずは、画面左のプロジェクトツリー内にある、Source Fileの上で右クリックをし
表示されたメニューからAdd Existing Item...をクリックします。

MPLAB_X_12-2.jpg

ファイル選択画面が表示されるので、先ほど作成したmain.cを選択肢、Selectボタンを押します。

MPLAB_X_12-3.jpg

Source Fileの下に、main.cが組み込まれたことをご確認下さい。

MPLAB_X_12-4.jpg

さて、次に画面上部にある、ハンマーのアイコンをクリックします。

MPLAB_X_13.jpg

ビルドが完了すると、右下にビルド結果が表示されます。
BUILD SUCCESSFULの文字が表示されれば、ビルド成功です!

MPLAB_X_14_1.jpg


これで、MPLAB X IDEを用いたプロジェクトの作成からビルドまでの説明は終了です。

最終的な出力ファイルは、プロジェクトフォルダ内の以下の場所になります。
(プロジェクト名がLED_SAMPLE_01だとすると)

LED_SAMPLE_01.X → dist → default → production → LED_SAMPLE_01.X.production.hex

あとは、このファイルをPICkit2などでマイコンへ書き込めば、全て完了です!

書き込み方法は、こちらの記事をご覧下さい。
http://technologicaladvance.blog.fc2.com/

本当にお疲れ様でした!^^


MPLAB X IDEは、従来のMPLABと比較しても、非常に強力な機能が多数追加されています。
ここでは詳細には触れませんが、是非この新しいツールを使いこなして、最強のPIC使いを目指しましょう!
AVRマイコン使いとの戦いに備えて…なんてね(笑)

最後までお読みいただいて、ありがとうございました!!(感謝)

こんばんは!
最近、仕事が不安で不安で夜しか眠れないゆってぃです!

前回のブログで書いた電子部品が、数日前に届きました。
購入したPIC16F648Aはシリアル通信機能を持っているので、早速XBeeと繋いで、PCと無線通信してみました。

因みにPIC16F648Aは駆動電圧が3.0V~なので、3.3Vを与えてあげれば、UART通信も3.3V系で行うことが出来、XBeeモジュールとの間にレベルシフターをかます必要はありません。
ただし、プログラム書き込み時は4.5V以上を与えなければなりません。
また、Brown-out Resetを使用する場合は、4.0V以下では(基本的に)リセット解除されないので、3.3V駆動を行いたいときはBORENをオフにする必要があります。

さて、例のごとく、マイコン側から一方的に文字列を投げるだけのテストを行いました。
やはり、ものすごく簡単にPCと無線通信が出来てしまいました。。。

PICで無線通信1

FM3でやったことと全く同じなので、そりゃあ上手くいきますよね(笑)
とはいうものの…こんなに簡単に無線通信できちゃうなら、やっぱり組み込みエンジニアなんていらなry

いかんいかん!
余計不安になって、ますます夜しか眠れなくなってしまう!

次のステップとして、今度は受信部分を作ります。
といっても、こっちも基本的にFM3の時と同じですが…(汗)

そのときはソースも公開しますね!

最後までお読みいただいて、ありがとうございました!
こんばんは!
携帯電話が故障してちょっぴり残念なゆってぃです><


今日は、以前に書いてすっかり忘れていた組み込みC言語をオブジェクト指向っぽく書くテクニックの続きです!

簡単におさらいをすると
構造体の中に関数ポインタを持たせることで、C++やJAVAのメソッドと同じような働きをさせることが出来るということでした。

言葉だけ聞くと簡単そうに聞こえるのですが、LEDを点滅させるだけの単純なプログラムでも、オブジェクト指向っぽく書こうとすると、意外と大変です(涙)

おまけに、過去の記事で書いたLED点滅プログラムのソースではプログラム領域が39hワード・データ領域が7hワードだったのに対し、今回のソースはプログラム領域が1D3hワード・データ領域が1Dhワードも使用しています。

すでにPIC16F84Aの貧弱なROM領域の50%弱を使ってしまっています。

LEDを点滅させるだけなのに…(T_T)

ですので、本来はこの程度のソフトであれば普通に書いた方がよいと思います。

では早速ソースコードを書いていきましょう。
なお、毎度のことですが、本ブログではインデントに全角スペースを使用しているため、コピペではコンパイルが通りませんのでご注意ください。

今回、僕が作成したヘッダファイルは2つあります。
1つは変数の型を再定義したtypedefine.h
もう1つは、LEDクラスを記述したled.hです。

//-----typedefine.h ここから-----//

#ifndef __TYPEDEFINE_H__
#define __TYPEDEFINE_H__

typedef unsigned char Bool;
typedef unsigned char Uint8;
typedef unsigned short Uint16;
typedef unsigned long Uint32;

#endif

//-----typedefine.h ここまで-----//

//-----led.h ここから-----//

#ifndef __LED_H__
#define __LED_H__

#include"typedefine.h"

#define FALSE (0)
#define TRUE  (1)

typedef struct TAG_LED_CTRL_COND{
 //メンバ変数
 Bool isInitialized;//初期化フラグ
 Uint8 OnTime;//LED点灯時間
 Uint8 OffTime;//LED消灯時間

 //メソッド
 Bool (*setOnTime)(struct TAG_LED_CTRL_COND *pLC, Uint8 time); //LEDの点灯時間をセットする
 Bool (*setOffTime)(struct TAG_LED_CTRL_COND *pLC, Uint8 time); //LEDの消灯時間をセットする
 Bool (*initLedPort)(struct TAG_LED_CTRL_COND *pLC);//LEDポートを初期化する
 Bool (*onLedBlink)(struct TAG_LED_CTRL_COND *pLC);//LEDを点灯させる
 Bool (*offLedBlink)(struct TAG_LED_CTRL_COND *pLC);//LEDを消灯させる
 Bool (*initLedTimer)(struct TAG_LED_CTRL_COND *pLC);//LED用点滅タイマーを初期化する
 Bool (*startLedTimer)(struct TAG_LED_CTRL_COND *pLC);//LED用点滅タイマーを起動する
 Bool (*stopLedTimer)(struct TAG_LED_CTRL_COND *pLC);//LED用点滅タイマーを停止する

}LED_CTRL_CONDITION;

//extern関数のプロトタイプ宣言
Bool startLedBlink(LED_CTRL_CONDITION *pLC);
LED_CTRL_CONDITION *getLC(void);

#endif

//-----led.h ここまで-----//

次に、メインルーチンと割り込みハンドラを含むmain.cを記述します。


//-----main.c ここから-----//

#include"pic.h"
#include"typedefine.h"
#include"led.h"

__CONFIG( FOSC_EXTRC & WDTE_OFF & PWRTE_ON & CP_ON);

static void initIntReg(void);

int main(void){

 LED_CTRL_CONDITION *pLC;

 initIntReg();
 
 if(startLedBlink(pLC) == TRUE){
  while(1);
 }else{
   TRISA = 0x00;
  PORTA |= 0x02;
  while(1);
 }

 return 0;

}

static void initIntReg(){
 
 GIE = 1;

}

void interrupt int_func(){

 LED_CTRL_CONDITION *pLC;



 if(T0IF){

  pLC = getLC();
  pLC->stopLedTimer(pLC);

  if(PORTA & 0x01){
   pLC->offLedBlink(pLC);
  }else{
   pLC->onLedBlink(pLC);
  }

  T0IF = 0;

  pLC->startLedTimer(pLC);

 }
  
}

//-----main.c ここまで-----//

最後に、本プログラムの要となるled.cです。

//-----led.c ここから-----//

#include"pic.h"
#include"typedefine.h"
#include"led.h"

//グローバル領域にインスタンスを生成
static LED_CTRL_CONDITION _instance_led;

//静的関数のプロトタイプ宣言
static LED_CTRL_CONDITION *initLedCond(void);

//LEDクラスの持つメソッドの実態
static Bool SET_ON_TIME(LED_CTRL_CONDITION *pLC, Uint8 time);
static Bool SET_OFF_TIME(LED_CTRL_CONDITION *pLC, Uint8 time);
static Bool INIT_LED_PORT(LED_CTRL_CONDITION *pLC);
static Bool ON_LED_BLINK(LED_CTRL_CONDITION *pLC);
static Bool OFF_LED_BLINK(LED_CTRL_CONDITION *pLC);
static Bool INIT_LED_TIMER(LED_CTRL_CONDITION *pLC);
static Bool START_LED_TIMER(LED_CTRL_CONDITION *pLC);
static Bool STOP_LED_TIMER(LED_CTRL_CONDITION *pLC);

//---------------------------

Bool startLedBlink(LED_CTRL_CONDITION *pLC){

 Bool err = FALSE;//どこかでエラーが発生したらTRUEになる。

 pLC = initLedCond();//LEDクラスのインスタンスを取得 

 if(pLC->setOnTime(pLC, 0x07) == FALSE){
  err = TRUE;
 }else if(pLC->setOffTime(pLC, 0x07) == FALSE){
  err = TRUE;
 }else if(pLC->initLedPort(pLC) == FALSE){
  err = TRUE;
 }else if(pLC->initLedTimer(pLC) == FALSE){
  err = TRUE;
 }else if(pLC->startLedTimer(pLC) == FALSE){
  err = TRUE;
 }

 if(err == TRUE){
  return FALSE;
 }else{
  return TRUE;
 }
}



static LED_CTRL_CONDITION *initLedCond(void){

 if(_instance_led.isInitialized == FALSE){
  //メンバ変数初期化
  _instance_led.isInitialized = TRUE;
  _instance_led.OnTime  = 0;
  _instance_led.OffTime = 0;

  //メソッド初期化
  _instance_led.setOnTime   = SET_ON_TIME;
  _instance_led.setOffTime = SET_OFF_TIME;
  _instance_led.initLedPort  = INIT_LED_PORT;
  _instance_led.onLedBlink = ON_LED_BLINK;
  _instance_led.offLedBlink = OFF_LED_BLINK;
  _instance_led.initLedTimer = INIT_LED_TIMER;
  _instance_led.startLedTimer = START_LED_TIMER;
  _instance_led.stopLedTimer = STOP_LED_TIMER;
 }
  
 return &_instance_led;

}

LED_CTRL_CONDITION *getLC(void){
 
 return &_instance_led;
 
}

static Bool SET_ON_TIME(LED_CTRL_CONDITION *pLC, Uint8 time){

 if(time > 0x07){
  return FALSE;
 }else{
  pLC->OnTime = time;
 }

 return TRUE;

}

static Bool SET_OFF_TIME(LED_CTRL_CONDITION *pLC, Uint8 time){

 if(time > 0x07){
  return FALSE;
 }else{
  pLC->OffTime = time;
 }

 return TRUE;

}

static Bool INIT_LED_PORT(LED_CTRL_CONDITION *pLC){

 PORTA &= 0xfe; //念のため出力はLOW
 TRISA &= 0xfe; //A0ポートを出力に設定

 return TRUE;

}

static Bool ON_LED_BLINK(LED_CTRL_CONDITION *pLC){
 
 PORTA |= 0x01;
 OPTION_REG |= pLC->OnTime;

 return TRUE;

}

static Bool OFF_LED_BLINK(LED_CTRL_CONDITION *pLC){

 PORTA &= 0xfe;
 OPTION_REG |= pLC->OffTime;

 return TRUE; 

}
static Bool INIT_LED_TIMER(LED_CTRL_CONDITION *pLC){

 T0IE = 0;
 T0CS = 0;
 PSA  = 0;
 if(pLC->OffTime <= 0x07){
     OPTION_REG |= pLC->OffTime;//offTimeに正しい値が入っていれば…
 }else{
  return FALSE;
 }
 TMR0 = 0x00;

 return TRUE; 

}

static Bool START_LED_TIMER(LED_CTRL_CONDITION *pLC){

 T0IE = 1;

 return TRUE;

}

static Bool STOP_LED_TIMER(LED_CTRL_CONDITION *pLC){

 T0IE = 0;
 TMR0 = 0x00;

 return TRUE;

}

//-----led.c ここまで-----//

はい。
読む気が失せますね…

僕自身、こんなにたいそうなコード量になるとは思ってもいませんでした(汗)
具体的な解説はしませんが(ぉぃ!)、要は関数ポインタで構造体の中にメソッドを入れているだけです。
あとは、エラー検出とかレジスタアクセスの隠蔽化とか、、、そこらへんを少しだけ真面目にやってみたら、こんなになってしまいました…(涙)
あ、ソースの解説は後日きちんと行いますからね(笑)

そうそう。構造体の中に関数ポインタを入れた場合の注意です。
関数ポインタという名前がついている以上、ひとつひとつがアドレス幅分の大きさを持ちますので、その分だけ構造体のサイズは大きくなるのでご注意くださいね。

さて、今日はここまでにしましょう!
最後までお読みいただいて、ありがとうございました!
ご訪問者様
プロフィール

ゆってぃ

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

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

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

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

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

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


上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。