あなたの天然記念物
ホーム更新雑談Perl鉄ゲタランドナーコースガイド自転車Linuxリンク経歴連絡先

ビット制御のBITBANDは超便利 (2012.12.07)

GPIOの1ビット制御は面倒

例えばポートの中の1ビットをH/L制御しようとすると、1ポートに32ビットあるので 現在値をポートから取得して対象のビットだけ0にして出力したいビットをORしますよね。 こんな感じ(あくまで例)
uint32_t port = LPC_GPIO0->FIOPIN;
port &= ~0x00000100;
port |= newval << 8;
LPC_GPIO0->FIOPIN = port;

BITBANDでダイレクトに設定

論より証拠、このコードをご覧ください。
*(uint32_t *)0x233802A0 = newval;
たった1行でできちゃいました。

こんな仕掛け

通常は32ビットがひとかたまりですけれど、これを1ビットずつに分離、左にダミーの31ビットをくっつけて32ビットとした写像です。 読み出すと値が0x00000000か0x00000001のどちらかで、書き込む値の左の31ビットは無視されて最右の1ビットだけが反映されます。 BITBANDの仕掛け

コーディングのための工夫

このBITBANDを上手い事使うためにマクロを用意しました。
typedef volatile uint32_t *BITBAND;
#define BITBAND_GENERIC_ADDR(base, addr, bitnum) (BITBAND)(base + 0x02000000UL + sizeof(uint32_t) * (((uint32_t)&addr - base) * 8 + bitnum))
#define BITBAND_AHB_ADDR(addr, bitnum) BITBAND_GENERIC_ADDR(0x20000000UL, addr, bitnum)
#define BITBAND_APB_ADDR(addr, bitnum) BITBAND_GENERIC_ADDR(0x40000000UL, addr, bitnum)

#define BITBAND_GENERIC(base, addr, bitnum) *BITBAND_GENERIC_ADDR(base, addr, bitnum)
#define BITBAND_AHB(addr, bitnum) BITBAND_GENERIC(0x20000000UL, addr, bitnum)
#define BITBAND_APB(addr, bitnum) BITBAND_GENERIC(0x40000000UL, addr, bitnum)
BITBANDの対象がAHBとAPBの2領域でベースアドレスが異なるため2種類用意してあります。 GPIOはAHBでペリフェラルはAPBです。 冒頭の例をマクロを使って書き直すとこんな感じに。
BITBAND_AHB(LPC_GPIO0->FIOPIN, 8) = newval;

GPIOの1ビットを一意に表現

LPC1769はGPIOポートが5個もあって、ポートとビットの組み合わせになります。 プログラムで「ポート0の場合…ポート1の場合…ポート2の場合…」と場合分けをするのはスマートじゃない。 GPIOのマッピングを見ると先頭のポート0からポート5まで連続だからFIOPINのBITBANDも連続することに気付きました。 ポート0のビット0のBITBAND、つまり「BITBAND_AHB_ADDR(LPC_GPIO0->FIOPIN, 0)」を基準にすると場合分けが不要になります。 全ポート、全ビットを個別に設定できる関数です。第1パラメタがFIOPINのBITBANDになっています。
void ksrk_gpio_setup(BITBAND GpioPin, uint32_t dir, uint32_t mode, uint32_t drain)
{
	uint32_t pin_offset = GpioPin - BITBAND_AHB_ADDR(LPC_GPIO0->FIOPIN, 0);

	// Funcを00に設定
	BITBAND_APB(LPC_PINCON->PINSEL0, 2 * pin_offset + 0) = 0;
	BITBAND_APB(LPC_PINCON->PINSEL0, 2 * pin_offset + 1) = 0;

	// モード(プルアップ/プルダウン/レピータ/スリーステート)を設定
	BITBAND_APB(LPC_PINCON->PINMODE0, 2 * pin_offset + 0) = mode >> 0;
	BITBAND_APB(LPC_PINCON->PINMODE0, 2 * pin_offset + 1) = mode >> 1;

	// 出力を0に(GPIOのピンはAHB)
	BITBAND_AHB(LPC_GPIO0->FIOPIN, pin_offset) = 0;

	// 入出力を設定
	BITBAND_AHB(LPC_GPIO0->FIODIR, pin_offset) = dir;

	// オープンドレインの有無を設定
	BITBAND_APB(LPC_PINCON->PINMODE_OD0, pin_offset) = drain;
}
ピンの指定をパラメタとして保持できて、こんなコードを書けるようになりました。
*pHgl->pGpioPin[GPIO_DB4] = cdata >> 4;

今回の鉄ゲタは…

このページ自体が鉄ゲタですよホントにもう(ぐすん)。 あと公式資料での表記は「BIT BAND」なので間にスペースあります。BITBANDと思い込んでPDFの検索できずに頭抱えました。 まあ、どうせ話が通じないと思うので、あとはソース見てください(^^;
yrntrlmnmnt20121207.zip (252,124 バイトをVPSから 00:05 で)