MBM (Mailbox Manager)

Last-modified: 2013-08-21 (水) 03:01:28
 
 

概要

MBM (Mailbox Manager) とは EzI2C で使うメモリを管理する UM で、ヴィータちゃんにギガントフォルムのグラーフアイゼンを持たせたときのように EzI2C が最強になります。
ただでさえ簡単な EzI2C がより簡単になるので I2C のマスタ側のプログラムをしたくなくなるという弊害があります。
要するに今まで自分で作っていた構造体を代わりに作ってくれる UM です。
ロボット倶楽部の皆さんは EzI2C を使うときはかならず使いましょう。

Wizard

この UM は他の UM と違い Property で編集できません。代わりに Wizard で編集します。

構造体の中身

構造体の宣言

MBM を配置したときの初期状態の構造体は MBM.h の中で

	typedef struct
	{
		BYTE reserved0; 			// reserved variable
	} MBM_Outbox;
	typedef struct
	{
		BYTE reserved1; 			// reserved variable
	} MBM_Inbox;
	typedef struct
	{
	    #if (MBM_SERVICE_VARS_POSITION == MBM_SERVICE_VARS_ABOVE)
		BYTE MailboxConfig0; 			// Reserved for Mailbox UM usage
		BYTE MailboxConfig1; 			// Reserved for Mailbox UM usage
	    #endif
	    MBM_Outbox Outbox;
	    MBM_Inbox Inbox;
	    #if (MBM_SERVICE_VARS_POSITION == MBM_SERVICE_VARS_BELOW)
		BYTE MailboxConfig0; 			// Reserved for Mailbox UM usage
		BYTE MailboxConfig1; 			// Reserved for Mailbox UM usage
	   #endif
	} MBM_Mailbox;
	extern MBM_Mailbox MBM_MyMailbox;
	extern MBM_Mailbox* MBM_pMyMailbox;

と宣言されていて、MBM.c の中にグローバル変数の

	MBM_Mailbox MBM_MyMailbox;
	MBM_Mailbox* MBM_pMyMailbox;

が入ってます。
ここで注意するべきことは MBM_InitializeMailbox 関数を呼び出しても

	MBM_pMyMailbox = &MBM_MyMailbox;

で MBM_pMyMailbox を初期化してくれてないということです。(MBM.c を読めばわかりますが)
なので自分で MBM_pMyMailbox を初期化しましょう

結局初期状態の構造体はこのようになってます。(System Area Location を RegMap Heading にしたとき)

オフセット変数
0BYTEMBM_MyMailbox.MailboxConfig0
1BYTEMBM_MyMailbox.MailboxConfig1
2BYTEMBM_MyMailbox.Outbox.reserved0
3BYTEMBM_MyMailbox.Inbox.reserved1

また Wizard で設定した Global Alias はオフセットで #define されているので

オフセットGlobal Aliast
0MAILBOXCONFIG0
1MAILBOXCONFIG1
2OUTB_RESERVED_0
3INB_RESERVED_1

となっています。
なので 1byte だけならこの構造体には

	*( ((BYTE *)MBM_pMyMailbox) + (Global Alias) )
	( (BYTE *)MBM_pMyMailbox)[ (Global Alias) ]

でもアクセスできます。

しかし、

	MBM_MyMailbox.Outbox.foo
	MBM_pMyMailbox->Inbox.bar

でアクセスしたほうがいいでしょう。

また、Wizard で自分で追加した変数は Outbox 又は Inbox 構造体に追加されます。

構造体の順番は Outbox の方が Inbox より上なので
Outbox は マスターからスレーブへ
Inbox はスレーブからマスターへ
というふうに使いわけます。

各変数

  • MBM_MyMailbox.MailboxConfig0
    この変数は System で予約された変数で通信に関するフラグと構造体のサイズが入っています。
    MBM_InitializeMailbox 関数の中の
    	MBM_MyMailbox.MailboxConfig0 = szeof(MBM_MyMailbox);
    で構造体のサイズが入れられ、MBM_SetBusyFlag, MBM_ClearBusyFlag 関数で Busy flag
    MBM_BUSY (0x80) のフラグが操作されます。
    また、I2C のマスタが Dirty flag
    MBM_DIRTY (0x40) を立てるところでもあり、MBM_CheckDirtyFlag 関数で確認できます。
    なのでこの変数はマスタが Dirty flag を操作するとき以外はいじらないようにしましょう。
  • MBM_MyMailbox.MailboxConfig1
    この変数は今のところ UM の関数では使われていないので自分で勝手に使っても大丈夫だと思います。
    ただ MBM の Wizard では System が予約していることになっているので今後のことを考えると使わない方がいいかもしれません。
  • MBM_MyMailbox.Outbox.reserved0, MBM_MyMailbox.Inbox.reserved1
    この変数は Outbox, Inbox の変数なので型を変えたり名前を変えたり自分で好きなように編集できます。
    reserved (予約) と書いてありますが好きなように編集して大丈夫です。

関数

void MBM_InitializeMailbox(void);

MBM_MyMailbox 構造体を初期化する関数です。
MBM_MyMailbox を使う前、例えば EzI2Cs_SetRamBuffer 関数を呼ぶ前に呼んでください。
なお、上にも書きましたがこの関数は MBM_pMyMailbox を初期化してくれませんので、これを呼んだ後

	MBM_pMyMailbox = &MBM_MyMailbox;

を書いて MBM_pMyMailbox の初期化をすることをお勧めします。

また、MBM.c の中に

	// arguments:
	//      pointer mailbox to initialize

とコメントされていることから今後ポインタを引数に取る関数に変わる可能性があると思います。

void MBM_SetBusyFlag(void);

MailboxConfig0 に Busy flag (0x80 : MBM_BUSY) を立てる関数です。
マスターに現在 Busy 状態であることを知らせることができます。

この関数も今後ポインタを引数に取るように仕様変更される可能性があります。

void MBM_ClearBusyFlag(void);

MailboxConfig0 から Busy flag を下げる関数です。
Busy 状態を解除するために使います。

この関数も仕様変更される可能性があります。

BYTE MBM_CheckDirtyFlag(void);

MailboxConfig0 の Dirty flag (0x40 : MBM_DIRTY) の状態を確認する関数です。
Dirty flag が立ってたら TRUE , 立ってなかったら FALSE を返します。
マスタが立てた Dirty flag を確認するために使います。

この関数も仕様変更される可能性があります。

使い方

そのうち書きます。
とりあえずロボット倶楽部の規格を参考にしてください。

ロボット倶楽部の規格

EzI2C を使うときはこの MBM を使いましょう。

Wizard の設定

統一して読みやすくするため変数の名前等ちゃんと守って設定してください。

  • データ構造は Wizard で以下のように設定します。
    オフセット変数Global AliasBox Type
    0BYTEMailboxConfig0MailboxConfig0System
    1BYTEMailboxConfig1MailboxConfig1System
    2BYTEOutboxFlagOutboxFlagOutbox
    3Outbox
    Outbox
    nBYTEInboxFlagInboxFlagInbox
    n+1Inbox
    Inbox
  • System Area Location は RegMap Heading にしてください。
  • 片側通信しかしない時でも OutboxFlag と InboxFlag の両方を作ってください。
  • プログラムでは Box Type が System の変数はいじらないように。(フラグは関数を通して設定・確認をすること)
    • マスタも MailboxConfig0 で Dirty flag の操作、Busy flag の確認、構造体のサイズの確認をする時以外はいじらないようにしましょう。
    • MailboxConfig1 もいじらないように。
  • OutboxFlag, InboxFlag の場所は各 Box の1番目に作成し、それぞれの Box の通信フラグ用の変数とします。
    その他の … の項目は各自自由に作ってください。
  • EzI2Cs_SetRamBuffer 関数でマスタに write 許可を出すのは InboxFlag まで、つまり n + 1 byte までにしてください。
    	EzI2Cs_SetRamBuffer(sizeof(MBM_MyMailbox), INBOXFLAG + 1, (BYTE *)MBM_pMyMailbox);
    とするといいでしょう。

Flag

MailboxConfig0, OutboxFlag, InboxFlag に立てるフラグの意味を書きます。
同じ名前で #define で定義してください。スレーブ側においては Busy flag, Dirty flag は MBM.h で定義されているので自分で定義する必要はありません。(そもそも操作用の関数が用意されてますし)
MBM_SetBusyFlag, MBM_ClearBusyFlag, MBM_CheckDirtyFlag 関数の様なフラグ操作用の関数を作ることをお勧めします。

  • フラグ名 (値 : define定義名)
    説明
     
  • Busy flag (0x80 : I2CIO_BUSY)
    このフラグはスレーブが Busy 状態、つまり重たい処理をしてるとか、ちょっとの間通信をストップしたい状態であることをマスタに知らせるためのフラグです。マスタはこのフラグを確認したら 10ms ぐらい通信をストップしてあげてください。
    スレーブ側は MBM_SetBusyFlag, MBM_ClearBusyFlag 関数で操作しましょう。
    MailboxConfig0 に存在します。
  • Dirty flag (0x40 : I2CIO_DIRTY)
    このフラグはマスタが Dirty 状態であることをスレーブに知らせるためのフラグです。
    Dirty がどういう状態かはまだ決めていません。
    スレーブ側は MBM_CheckDirtyFlag 関数でチェックしましょう。
    MailboxConfig0 に存在します。
  • Write flag (0x80 : I2CIO_WRITE)
    このフラグはスレーブ、マスタが Inbox, Outbox にデータを書き込み中であることを相手に伝えるためのフラグです。
    OutboxFlag, InboxFlag の両方に存在します。
  • Read flag (0x40 : I2CIO_READ)
    このフラグはスレーブ、マスタが Inbox, Outbox を読み込み中であることを相手に伝えるためのフラグです。
    OutboxFlag, InboxFlag の両方に存在します。
  • Update flag (0x20 : I2CIO_UPDATE)
    このフラグはスレーブ、マスタが Inbox, Outbox にデータを書き込んで更新したことを相手に伝えるためのフラグです。
    OutboxFlag, InboxFlag の両方に存在します。
  • Reserved flag (0x10, 0x08, 0x04, 0x02, 0x01)
    これらのフラグはこれから作られる新しいフラグのために予約しています。勝手に使わないでください。
    なお新しいフラグに関する提案、要望等ございましたらページ下のコメント欄に記入またはhikariにご連絡ください。
    OutboxFlag, InboxFlag の両方に存在します。

書き込み、読み取り動作のフローチャート

排他制御のため順番を理解し確実に守ること。(間違ってたらごめん)

  • スレーブ書き込み
    1. InboxFlag に Write flag を立てる。
    2. InboxFlag に Read flag が立ってないことを確認する。
      (立ってる場合は下がるまで待つか、5. まで飛んで書き込みをあきらめる。)
    3. Inbox に書き込む。
    4. InboxFlag に Update flag を立てる。
    5. InboxFlag の Write flag を下げる。
    6. 終わり。
  • スレーブ読み取り
    1. OutboxFlag に Update flag が立ってることを確認する。
      (立ってない場合は立つまで待つか、7. まで飛んで読み取りをあきらめる。)
    2. OutboxFlag に Read flag を立てる。
    3. OutboxFlag に Write flag が立ってないことを確認する。
      (立ってる場合は下がるまで待つか、6. まで飛んで読み取りをあきらめる。)
    4. Outbox を読み取る。
    5. OutboxFlag から Update flag を下げる。
    6. OutboxFlag から Read flag を下げる。
    7. 終わり。
  • マスタ書き込み
    1. MailboxConfig0 に Busy flag が立ってないことを確認する。
      (立ってる場合は 7. へ飛び、書き込みをあきらめて一定時間後にもう一度チャレンジする。)
    2. OutboxFlag に Write flag を立てる。
    3. OutboxFlag に Read flag が立ってないことを確認する。
      (立ってる場合は下がるまで待つか、6. まで飛んで書き込みをあきらめる。)
    4. Outbox に書き込む。
    5. OutboxFlag に Update flag を立てる。
    6. OutboxFlag の Write flag を下げる。
    7. 終わり。
  • マスタ読み取り
    1. MailboxConfig0 に Busy flag が立ってないことを確認する。
      (立ってる場合は 8. へ飛び、読み込みをあきらめて一定時間後にもう一度チャレンジする。)
    2. InboxFlag に Update flag が立ってることを確認する。
      (立ってない場合は立つまで待つか、8. まで飛んで読み取りをあきらめる。)
    3. InboxFlag に Read flag を立てる。
    4. InboxFlag に Write flag が立ってないことを確認する。
      (立ってる場合は下がるまで待つか、7. まで飛んで読み取りをあきらめる。)
    5. Inbox を読み取る。
    6. InboxFlag から Update flag を下げる。
    7. InboxFlag から Read flag を下げる。
    8. 終わり。

プログラム例

スレーブ側

	…
	#define I2CIO_WRITE 0x80
	#define I2CIO_READ 0x40
	#define I2CIO_UPDATE 0x20
	…
	…
	//I2C & MBM Init
	MBM_InitializeMailbox();
	MBM_pMyMailbox = &MBM_MyMailbox;
	EzI2Cs_SetRamBuffer(sizeof(MBM_MyMailbox), INBOXFLAG + 1, (BYTE *)MBM_pMyMailbox);
	EzI2Cs_Start();
	EzI2Cs_EnableInt();
	…
	…
	//Read Outbox
	MBM_Outbox foo;
	while (!(MBM_MyMailbox.Outbox.OutboxFlag & I2CIO_UPDATE));
	MBM_MyMailbox.Outbox.OutboxFlag |= I2CIO_READ;
	while (MBM_MyMailbox.Outbox.OutboxFlag & I2CIO_WRITE);
	foo = MBM_MyMailbox.Outbox;
	MBM_MyMailbox.Outbox.OutboxFlag &= ~I2CIO_UPDATE;
	MBM_MyMailbox.Outbox.OutboxFlag &= ~I2CIO_READ;
	…
	…
	//Write Inbox
	MBM_Inbox bar = {…};
	MBM_MyMailbox.Inbox.InboxFlag |= I2CIO_WRITE;
	while (MBM_MyMailbox.Inbox.InboxFlag & I2CIO_READ);
	bar.InboxFlag = MBM_MyMailbox.Inbox.InboxFlag;
	MBM_MyMailbox.Inbox = bar;
	MBM_MyMailbox.Inbox.InboxFlag |= I2CIO_UPDATE;
	MBM_MyMailbox.Inbox.InboxFlag &= ~I2CIO_WRITE;
	…
	…
	//start process
	MBM_SetBusyFlag();
	…
	//heavy duty
	…
	//finish process
	MBM_ClearBusyFlag();
	…

マスタ側(PSoC 3)

	…
	#define I2CIO_BUSY   0x80
	#define I2CIO_DIRTY  0x40
	#define I2CIO_WRITE  0x80
	#define I2CIO_READ   0x40
	#define I2CIO_UPDATE 0x20
	#define I2CIO_CONFIG_ADDR 0x00
	#define I2CIO_OUTBOX_ADDR 0x02
	#define I2CIO_INBOX_ADDR  // user select
	#define SLAVE_ADDR // user select
	…
	i = I2CIO_TRY_COMMUNICATION;
	do{
		status = Ezi2c_master_read_buf(SLAVE_ADDR,I2CIO_CONFIG_ADDR,i2c_config,2);
		if(!(i2c_config[0] & I2CIO_BUSY))
		{
			Ezi2c_master_read_buf(
				SLAVE_ADDR,I2CIO_OUTBOX_ADDR,&i2c_outbox_flag,1);
			i2c_outbox_flag |= I2CIO_WRITE;
			Ezi2c_master_write_buf(
				SLAVE_ADDR,I2CIO_OUTBOX_ADDR,&i2c_outbox_flag,1);
			Ezi2c_master_read_buf(
				SLAVE_ADDR,I2CIO_OUTBOX_ADDR,&i2c_outbox_flag,1);
			if(!(i2c_outbox_flag & I2CIO_READ))
			{
				Ezi2c_master_write_buf(
					SLAVE_ADDR,I2CIO_OUTBOX_ADDR + 1,i2c_outbox_data,length);
				Ezi2c_master_read_buf(
					SLAVE_ADDR,I2CIO_OUTBOX_ADDR,&i2c_outbox_flag,1);
				i2c_outbox_flag &= (~I2CIO_WRITE);
				i2c_outbox_flag |= I2CIO_UPDATE;
				Ezi2c_master_write_buf(
					SLAVE_ADDR,I2CIO_OUTBOX_ADDR,&i2c_outbox_flag,1);
				i = 0;
			}
			else
			{
				i2c_outbox_flag &= (~I2CIO_WRITE);
			 	Ezi2c_master_write_buf(
					SLAVE_ADDR,I2CIO_OUTBOX_ADDR,&i2c_outbox_flag,1);
				i--;
			}
		}
		else
		{
			i--;
		}
	}while(i != 0);
	…
 
 
 

"こいつ意味わかんねー"とか"こんなクソ規格作んなよカス"とか何か問題があるときはhikariまで