Archive
こんにちは!
ゴールデンウィークでテンションが上がりまくりんぐのゆってぃです!
特に予定はありませんがやはり大型連休はうれしいですね^^

さて、毎年この時期になると、CQ出版社さんから付属マイコン基板つきのInterfaceが発売されます。
今年はなんとCortex-M3コアを内蔵した富士通のFM3マイコン、MB9BF618Tが付属されています!

このマイコン、超豪華です。
なんでも出来ます。
タイマーが本当に豊富でモーター制御に適しているので、エアコンだろうが洗濯機だろうがマッサージ器だろうが、たぶんこのチップのみで制御できます。

もっとも、実際の開発現場では、白物家電や車載、FAなどを除けばここまで高価なマイコンを使用することは無いでしょう。
同じFM3マイコンでも、もう少し下のグレードのものになると思います。

けれども、コアはもちろんペリフェラルレジスタ名も同じなので、この付属基板でFM3マイコン開発に慣れておけば、そのノウハウは実務でも大いに役立つことでしょう!
僕はいままでルネサス派(SH系や旧NECエレの78K0系)だったのですが、最近のARMの急速な普及状況を見ると、やはりARMマイコンにも慣れておかないといけませんね。

という訳で、さっそくLEDペコペコプログラムを作成してみましたよ!

因みに、開発環境にはIAR Embedded Workbenchのコードサイズ限定版(評価版)を使用しています。
また、インクルードファイルやスタートアップファイル・初期化ファイルは、富士通HPからダウンロードしたテンプレートファイルを使用しています。
詳細は、インターフェース6月号をご参照ください。

//-----ここから-----//

#include"mcu.h"

static void initPort(void);

int32_t main(void)
{

 int i;

 initPort();

 while(1){
  for(i=0;i<2000000;i++);
  bFM3_GPIO_PDORF_P3 = ~bFM3_GPIO_PDORF_P3;
 }

 return 1;

}

static void initPort(void){

 bFM3_GPIO_PFRF_P3 = 0;
 bFM3_GPIO_PZRF_P3 = 1;
 bFM3_GPIO_DDRF_P3 = 1;
 bFM3_GPIO_PDORF_P3 = 0;

}

//-----ここまで-----//

どうです?
我らがPIC16F84Aで学習したものと、大して変わらないでしょう?(そうか・・・?)
PIC先生で学んだことは、他のどのマイコンを開発することになっても、必ず生きてくるのです(本当か・・・?)


ちなみに、インターフェースの本誌にはSysTickタイマーを使用したサンプルコードが書かれていましたが、このタイマーはコアのタイマーを使用しているので、あまりアプリケーションプログラムからは使用しないほうがいい気がします。
ですので、あえてfor文でウエイトを入れたプログラムにしました。

タイマーはFM3のペリフェラルタイマーを使用しましょう。
次回は、FM3のもつタイマーを用いたLEDペコペコプログラムを掲載いたします!

最後までお読みいただいて、ありがとうございました!(感謝)
関連記事
スポンサーサイト
こんばんは!
携帯電話が故障してちょっぴり残念なゆってぃです><


今日は、以前に書いてすっかり忘れていた組み込み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 ここまで-----//

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

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

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

さて、今日はここまでにしましょう!
最後までお読みいただいて、ありがとうございました!
関連記事
こんばんわ!

いまさらながら「借りぐらしのアリエッティ」を見て、余韻に浸っているゆってぃです!

祝!1000アクセス!

みなさん!
本当にありがとうございます!><

薄っぺらい内容…
まとまりの無い文章…
しかも書いてるのは冴えないエンジニア…

そんないいとこ無しのブログですが、多くの方に誤ってアクセスいただいて、私は非常に感動しております。

アリエッティよりも感動しております!

これからも、組み込みC言語に関して、ハードウェアに関して、少しずつ綴っていくつもりですので
これからも、本ブログをよろしくお願いいたします!^^
関連記事
こんばんは…
一週間分の集中力を使って満身創痍のゆってぃです><

午前1は免除だったので、試験は午前2の10時50分試験からの開始でした。
10時30分くらいには試験会場の成蹊大学へ到着しました。
あんなにオシャレな大学でキャンパスライフを過ごせたら、どれだけ人生が変わったことか…(涙)

午前2は、自己採点では25問中5問のミスだったので、マークミスさえなければ通過したと思います。
問10とか、思いっきり勘で書いたら、思いっきり間違っていました(笑)

午後2
問2を選びました。電動介護ベッドシステムに関する問題。
正直、開始20分は全くペンが動きませんでした(涙)
なにこれ…今まで数千円くらいの電子小物しか作ったことない素人エンジニアには、こんな人命に関わるもの荷が重過ぎるよ(;_;)

以下に自分の回答を書きますが、こんなもん信じちゃいけませんよ!

設問1
(1)
 (a)1600(rpm), 4800(パルス)
 (b)10度~30度

(2)
 (a)制御部の電源オフされたときに、制御部は表示が"未使用状態"から始まっちゃうけど、管理PCの方では電源オフが認識できないから使用中状態のままで…うんたらかんたら(超適当…これが一番難しかった)

 (b)利用者氏名を表示する
 (c)「稼動部の位置が稼動範囲内か否かの判断」的な…

(3)
 (a)ひざ上UP、ひざ下DW(ともに適当)
 (b)θ2モータ、θ3モータ

設問2
(1)
 (a)
   a:θ2が(θ3)/2以下になろうとしたとき
   b:θ3が2×θ2以上になろうとしたとき
   c:Hが400mmになったとき

 (b)
   d:0度
   e:背中UP
   f:背中DW
 
(2)
 (a)RFIDの読み取り許可的な…
 (b)
   g:モータ制御タスク
   h:可動部位置
   i:LAN
   j:表示編集
   k:10秒タイマを起動

設問3
(1)
 l:エンコーダータスクからパルス信号が一定時間入ってこない
 m:エンコーダからの入力パルスの動作方向が制御と逆
(2)
 動作中は定期的にコネクションデータをやり取りする。
 コネクションデータが来ない。

まぁ、たぶん落ちたと思います(;_;)

合格していたら結果を報告しますが、何も言わなければ察してください(涙)

それでは!!
最後までお読みいただいて、ありがとうございました!^^


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

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

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

そして

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

きっと、「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という名前が出てきていないため、このような手段をとりました。


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

今日はここまでにしましょう^^
最後まで読んでいただいて、ありがとうございました!!(感謝)
関連記事
こんばんわ…
ついに花粉症にかかってしまったゆってぃです><

このブログを書いている今も
鼻水が垂れてきてボーちゃんそのものです

花粉症になる前は「風邪でもないのに何が辛いんだろう?」と思っていましたが
これはかなりキツいですね…。

健康で文化的な最低限度の生活すらも脅かされてきています。

はやくお医者へ行こう…。
関連記事
受験票が届きました!
これが届くと、いつもやべぇって気分になりますね(ぉぃ…

受験場所は成蹊大学とのことで。
思い返せば、入社二年目に基本情報技術者を受験したのもこの学校だったなぁ。。。
趣味でやっているバンドのベース君もこの学校の卒業生です。どうでもいいですが(笑)

なんとか合格できるように頑張ります!
関連記事
ご訪問者様
プロフィール

ゆってぃ

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

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

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

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

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

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