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

NXP LPCXpresso(LPC1768)でLチカ (2012.09.12)

ご注意

このページは、ワンチップマイコンLPC1768を搭載したLPCXpressoというワンボードマイコンで同搭載のLEDを点滅させる手順です。
LチカはLEDチカチカの略です。ワンボードマイコンを入手された方々の「Hello, World.」だとお考えください。
世の中の端っこを歩く私ですから手法が非常識です。ネタだと思って笑いながら、ご覧ください。

1.CPUの型番を確認

LPCXpressoのLPC1768版とLPC1769版、これら2つの違いがCPUの最高クロック数です。 1768が100MHz、1769が120MHzですのでCPUに合わせて最高クロックを設定します。 起動した際にCPUの型番を確認すると最高クロックをいくらにすればよいか分かります。 私はLPCXpressoを2枚持っていて1768と1769です。 1個のバイナリを両方に書き込んでそれぞれが最高クロックで動作すると便利な訳です。
//ハードウエア情報を返す
void ksrk_gethardinfo(TAGITEM_TYPE *pTags)
{
	//NXPのLPCシリーズのIAPでpart identification numberとdevice serial numberを読み出す
#define IAP_LOCATION 0x1FFF1FF1
#define IAP_READ_PART_ID 54
#define IAP_READ_SERIAL_NO 58
#define CMD_SUCCESS 0
	unsigned long command[5];
	unsigned long result[5];
	typedef void (*IAP)(unsigned long[],unsigned long[]);
	IAP iap_entry;
	iap_entry=(IAP) IAP_LOCATION;

	int i;
	for (i = 0; TAG_END != pTags[i].key; i++)
	{
		switch (pTags[i].key)
		{
			case HARD_PART_ID:
				//NXPのLPCシリーズのIAPでpart identification numberを読み出す
				pTags[i].value = TAG_NONE;
				command[0] = IAP_READ_PART_ID;
				iap_entry(command, result);
				if (CMD_SUCCESS == result[0])
				{
					pTags[i].value = result[1];
				}
				break;

			case HARD_SERIAL_1:
				//NXPのLPCシリーズのIAPでdevice serial numberを読み出す
				pTags[i].value = TAG_NONE;
				command[0] = IAP_READ_SERIAL_NO;
				iap_entry(command, result);
				if (CMD_SUCCESS == result[0])
				{
					pTags[i].value = result[1];
				}
				break;

			case HARD_SERIAL_2:
				//NXPのLPCシリーズのIAPでdevice serial numberを読み出す
				pTags[i].value = TAG_NONE;
				command[0] = IAP_READ_SERIAL_NO;
				iap_entry(command, result);
				if (CMD_SUCCESS == result[0])
				{
					pTags[i].value = result[2];
				}
				break;

			case HARD_SERIAL_3:
				//NXPのLPCシリーズのIAPでdevice serial numberを読み出す
				pTags[i].value = TAG_NONE;
				command[0] = IAP_READ_SERIAL_NO;
				iap_entry(command, result);
				if (CMD_SUCCESS == result[0])
				{
					pTags[i].value = result[3];
				}
				break;

			case HARD_SERIAL_4:
				//NXPのLPCシリーズのIAPでdevice serial numberを読み出す
				pTags[i].value = TAG_NONE;
				command[0] = IAP_READ_SERIAL_NO;
				iap_entry(command, result);
				if (CMD_SUCCESS == result[0])
				{
					pTags[i].value = result[4];
				}
				break;

			default:
				break;
		}
	}
}

2.タイマーのCPUクロック比を設定

CPUの種類が分かったのですぐにCPUクロックを設定したい所ですけれど、LPC1768(その他のシリーズも)には 「CPUクロックを設定後にペリフェラルのCPUクロック比を設定すると誤動作する」というエラッタ(仕様と異なる製造、設計ミス)があります。 対策はCPUクロック比を先に設定することです。 下記のコードがstaticの訳とか呼び出し順の説明は面倒なので省略します。
static void ledflash_clock(void)
{
	CLKPWR_SetPCLKDiv(CLKPWR_PCLKSEL_TIMER1, CLKPWR_PCLKSEL_CCLK_DIV_1);
}

3.CPUクロックを決める

CPUの種類がわかりましたので、種類に合わせて最高クロック数を決定します。 種類しか分からないので仕様書の種類/最高クロックの組み合わせをテーブルにしてコードに埋めます。
uint32_t ksrk_partid2clock(uint32_t uPartID)
{
	struct
	{
		uint32_t PID;
		uint32_t Clock;
	} parttable[] = {
		{HARD_LPC1769, 120000000},
		{HARD_LPC1768, 100000000},
		{HARD_LPC1767, 100000000},
		{HARD_LPC1766, 100000000},
		{HARD_LPC1765, 100000000},
		{HARD_LPC1764, 100000000},
		{HARD_LPC1759, 100000000},
		{HARD_LPC1758, 100000000},
		{HARD_LPC1756, 100000000},
		{HARD_LPC1754, 100000000},
		{HARD_LPC1752, 100000000},
		{HARD_LPC1751, 100000000},
		{HARD_LPC1751_NOCRP, 100000000},
		{0, 100000000}
	};

	uint32_t clock = 0;
	int i;
	for (i = 0; parttable[i].PID; i++)
	{
		if (uPartID == parttable[i].PID)
		{
			clock = parttable[i].Clock;
			break;
		}
	}

	return clock;
}

4.PLLの設定パラメータを決める

設定したいクロック数が分かってもそのまま入れて良い設定用レジスタはありません。 LPCXpressoは12MHzの水晶発振子を載せていてCPUはこれを元にPLLで逓倍しますから PLLを上手いこと設定して最高クロック数を作り出します。 設定パラメタの条件がややこしくて単純に逆算できないので、およそ16000通りの組み合わせを総当たりで探します。 総当たりにはデバッガでの応答時間を含めて1秒かかります。 CPUクロックを設定する前は約4MHzの内蔵RCクロック(IRC)で動作しています。
static TAGITEM_TYPE request_clock[] =
{
	{CLOCK_M,			25},
	{CLOCK_N,			2},
	{CLOCK_CPU_DIV,		3},
	{CLOCK_PERI_I2C0,	CLOCK_PERI_DIV2},
	{CLOCK_PERI_I2C1,	CLOCK_PERI_DIV2},
	{CLOCK_PERI_I2C2,	CLOCK_PERI_DIV2},
	{CLOCK_PERI_TIMER0, CLOCK_PERI_DIV1},
	{TAG_END, 			TAG_NONE}
};

static TAGITEM_TYPE clkconv[] =
{
	{CLOCK_PERI_WDT,	CLKPWR_PCLKSEL_WDT},
	{CLOCK_PERI_TIMER0,	CLKPWR_PCLKSEL_TIMER0},
	{CLOCK_PERI_TIMER1,	CLKPWR_PCLKSEL_TIMER1},
	{CLOCK_PERI_UART0,	CLKPWR_PCLKSEL_UART0},
	{CLOCK_PERI_UART1,	CLKPWR_PCLKSEL_UART1},
	{CLOCK_PERI_PWM1,	CLKPWR_PCLKSEL_PWM1},
	{CLOCK_PERI_I2C0,	CLKPWR_PCLKSEL_I2C0},
	{CLOCK_PERI_SPI,	CLKPWR_PCLKSEL_SPI},
	{CLOCK_PERI_SSP1,	CLKPWR_PCLKSEL_SSP1},
	{CLOCK_PERI_DAC,	CLKPWR_PCLKSEL_DAC},
	{CLOCK_PERI_ADC,	CLKPWR_PCLKSEL_ADC},
	{CLOCK_PERI_CAN1,	CLKPWR_PCLKSEL_CAN1},
	{CLOCK_PERI_CAN2,	CLKPWR_PCLKSEL_CAN2},
	{CLOCK_PERI_ACF,	CLKPWR_PCLKSEL_ACF},
	{CLOCK_PERI_QEI,	CLKPWR_PCLKSEL_QEI},
	{CLOCK_PERI_GPIOINT,	34},	//なぜかGPIOINTの定義がない
	{CLOCK_PERI_PCB,	CLKPWR_PCLKSEL_PCB},
	{CLOCK_PERI_I2C1,	CLKPWR_PCLKSEL_I2C1},
	{CLOCK_PERI_SSP0,	CLKPWR_PCLKSEL_SSP0},
	{CLOCK_PERI_TIMER2,	CLKPWR_PCLKSEL_TIMER2},
	{CLOCK_PERI_TIMER3,	CLKPWR_PCLKSEL_TIMER3},
	{CLOCK_PERI_UART2,	CLKPWR_PCLKSEL_UART2},
	{CLOCK_PERI_UART3,	CLKPWR_PCLKSEL_UART3},
	{CLOCK_PERI_I2C2,	CLKPWR_PCLKSEL_I2C2},
	{CLOCK_PERI_I2S,	CLKPWR_PCLKSEL_I2S},
	{CLOCK_PERI_RIT,	CLKPWR_PCLKSEL_RIT},
	{CLOCK_PERI_SYSCON,	CLKPWR_PCLKSEL_SYSCON},
	{CLOCK_PERI_MC,		CLKPWR_PCLKSEL_MC},
	{TAG_END, TAG_NONE}
};

static void ksrk_resolvclock(TAGITEM_TYPE *pTags)
{
//PLL0のクロック設定を検索
//USBにはPLL1から供給するのが前提です。
	uint32_t source_type = tag_getvalue(pTags, CLOCK_SOURCE, CLOCK_SOURCE_XTAL);
	uint32_t clock_base = 0;
	if (CLOCK_SOURCE_XTAL == source_type)
	{
		clock_base = tag_getvalue(pTags, CLOCK_XTAL, 12000000);
	}
	else if (CLOCK_SOURCE_IRC == source_type)
	{
		clock_base = tag_getvalue(pTags, CLOCK_IRC, 4000000);
	}
	else if (CLOCK_SOURCE_RTC == source_type)
	{
		clock_base = tag_getvalue(pTags, CLOCK_RTC, 32000);
	}
	else
	{
		return;
	}

	struct {
		uint32_t m;
		uint32_t n;
		uint32_t cpu_div;
		uint32_t cpu;
		uint32_t pll0;
	} pmin, pmax, ptry, pbetter;
	memset(&pbetter, 0x00, sizeof(pbetter));

	pmin.m = tag_getvalue(pTags, CLOCK_M_MIN, 6);
	pmin.n = tag_getvalue(pTags, CLOCK_N_MIN, 1);
	pmin.cpu_div = tag_getvalue(pTags, CLOCK_CPU_DIV_MIN, 1);
	pmin.cpu = tag_getvalue(pTags, CLOCK_CPU_MIN, 100000000);
	pmin.pll0 = tag_getvalue(pTags, CLOCK_PLL0_MIN, 275000000);

	pmax.m = tag_getvalue(pTags, CLOCK_M_MAX, 512);
	pmax.n = tag_getvalue(pTags, CLOCK_N_MAX, 32);
	pmax.cpu_div = tag_getvalue(pTags, CLOCK_CPU_DIV_MAX, 256);
	pmax.cpu = tag_getvalue(pTags, CLOCK_CPU_MAX, 100000000);
	pmax.pll0 = tag_getvalue(pTags, CLOCK_PLL0_MAX, 550000000);

	pbetter.cpu = 0;
	for (ptry.m = pmin.m; ptry.m <= pmax.m; ptry.m++)
	{
		for (ptry.n = pmin.n; ptry.n <= pmax.n; ptry.n++)
		{
			ptry.pll0 = clock_base * 2 * ptry.m / ptry.n;
			if (pmin.pll0 <= ptry.pll0 && ptry.pll0 <= pmax.pll0)
			{
				for (ptry.cpu_div = pmin.cpu_div; ptry.cpu_div <= pmax.cpu_div; ptry.cpu_div++)
				{
					ptry.cpu = ptry.pll0 / ptry.cpu_div;
					if (pmin.cpu <= ptry.cpu && ptry.cpu <= pmax.cpu)
					{
						if (pbetter.cpu < ptry.cpu)
						{
							pbetter = ptry;
							if (pbetter.cpu == pmax.cpu)
							{
								goto found_pll0;
							}
						}
					}
				}
			}
		}
	}
found_pll0:
	if (0 < pbetter.cpu)
	{
		tag_setvalue(pTags, CLOCK_M, pbetter.m);
		tag_setvalue(pTags, CLOCK_N, pbetter.n);
		tag_setvalue(pTags, CLOCK_CPU_DIV, pbetter.cpu_div);
	}

//USBにPLL1のクロック設定を検索
	//クロックのソースは水晶発振子だけなので周波数読み直し
	clock_base = tag_getvalue(pTags, CLOCK_XTAL, 12000000);
	uint32_t usb = tag_getvalue(pTags, CLOCK_USB, 48000000);
	uint32_t fcco_min = tag_getvalue(pTags, CLOCK_USB_FCCO_MIN, 156000000);
	uint32_t fcco_max = tag_getvalue(pTags, CLOCK_USB_FCCO_MAX, 320000000);

	//Mは逆算で
	uint32_t m = 0;
	if (0 == (usb % clock_base))
	{
		m = usb / clock_base;
	}

	//Pは検索、数字が飛び飛びなので注意
	uint32_t p = 0;
	if (0 < m)
	{
		uint32_t p_try;
		for (p_try = 1; p_try <= 8; p_try *= 2)
		{
			uint32_t fcco = usb * 2 * p_try;
			if (fcco_min <= fcco && fcco <= fcco_max)
			{
				p = p_try;
				goto found_pll1;
			}
		}
	}
found_pll1:
	if (0 < p)
	{
		tag_setvalue(pTags, CLOCK_USB_M, m);
		tag_setvalue(pTags, CLOCK_USB_P, p);
	}
}

5.PLLを設定します

PLLを設定してCPUのクロックをIRCからPLLに切り替えます。 これでようやくCPUが最高クロック数で動作を始めます。 同じプログラムを書き込んでいてもLPCXpressoのタイプに合わせてクロック数を設定する点がミソです。
static void ksrk_setupclock(TAGITEM_TYPE *pTags)
{
//ペリフェラルのクロック分周比を設定 PLLの設定後に行うと誤動作する(エラッタ)のでその防止
	int i;
	for (i = 0; TAG_END != pTags[i].key; i++)
	{
		uint32_t pclksel = tag_getvalue(clkconv, pTags[i].key, ~0);
		if (~0 != pclksel)
		{
			CLKPWR_SetPCLKDiv(pclksel, pTags[i].value);
		}
	}

//PLL0を設定
	uint32_t m = tag_getvalue(pTags, CLOCK_M, 25);
	uint32_t n = tag_getvalue(pTags, CLOCK_N, 2);
	uint32_t cpu_div = tag_getvalue(pTags, CLOCK_CPU_DIV, 3);
	uint32_t source_type = tag_getvalue(pTags, CLOCK_SOURCE, CLOCK_SOURCE_XTAL);
	if (CLOCK_SOURCE_XTAL == source_type)
	{
		uint32_t scs = 1 << 5;
		uint32_t osc = tag_getvalue(pTags, CLOCK_XTAL, 12000000);
		if (15000000 <= osc)
		{
			scs |= 1 << 4;
		}
		LPC_SC->SCS = scs;
	    /* Wait for Oscillator to be ready    */
	    while (!(LPC_SC->SCS & (1<<6)))
		{
		}

	    LPC_SC->CLKSRCSEL = 0x01;
	}
	else if (CLOCK_SOURCE_IRC == source_type)
	{
	    LPC_SC->CLKSRCSEL = 0x00;
	}
	else if (CLOCK_SOURCE_RTC == source_type)
	{
	    LPC_SC->CLKSRCSEL = 0x10;
	}

    LPC_SC->CCLKCFG = (cpu_div - 1) & 0xff;

	LPC_SC->PLL0CFG = (((n - 1) & 0xff) << 16) | ((m - 1) & 0x7fff);
	LPC_SC->PLL0CON = 0x01;

	//次の2ステートメント間をステップ実行するとPLL0がロックしないので、1行にしてステップ実行回避
	LPC_SC->PLL0FEED  = 0xAA;	LPC_SC->PLL0FEED  = 0x55;

	/* If RTC is selected, a delay of min 500us is needed */
	if (CLOCK_SOURCE_RTC == source_type)
	{
		/* Wait min. 500us before connecting PLL */
		uint32_t cntr;
		for (cntr=0; cntr <= 100; cntr++)
		{
		}
	}
	else
	{
		/* Wait for PLOCK0                    */
	    while (!(LPC_SC->PLL0STAT & (1<<26)))
		{
		}
	}

	/* PLL0 Enable & Connect              */
	LPC_SC->PLL0CON   = 0x03;
	//次の2ステートメント間をステップ実行するとPLL0がロックしないので、1行にしてステップ実行回避
	LPC_SC->PLL0FEED  = 0xAA;	LPC_SC->PLL0FEED  = 0x55;

//PLL1を設定
	m = tag_getvalue(pTags, CLOCK_USB_M, 4);
	uint32_t p = tag_getvalue(pTags, CLOCK_USB_P, 2);
	uint32_t p_bit;
	for (p_bit = 0; 0 < p; p_bit++)
	{
		p >>= 1;
	}

	LPC_SC->PLL1CFG = ((0x03 & (p_bit - 1)) << 5) | (0x1FF & (m - 1));
	LPC_SC->PLL1CON = 0x01;             /* PLL1 Enable                        */
	//次の2ステートメント間をステップ実行するとPLL0がロックしないので、1行にしてステップ実行回避
	LPC_SC->PLL1FEED  = 0xAA;	LPC_SC->PLL1FEED  = 0x55;
	/* Wait for PLOCK1                    */
	while (!(LPC_SC->PLL1STAT & (1<<10)))
	{
	}

	LPC_SC->PLL1CON   = 0x03;             /* PLL1 Enable & Connect              */
	//次の2ステートメント間をステップ実行するとPLL0がロックしないので、1行にしてステップ実行回避
	LPC_SC->PLL1FEED  = 0xAA;	LPC_SC->PLL1FEED  = 0x55;

//クロック外部出力を設定
	uint32_t out_sel = tag_getvalue(pTags, CLOCK_OUT_SEL, CLOCK_OUT_CPU);
	uint32_t out_div = tag_getvalue(pTags, CLOCK_OUT_DIV, 1);
	Bool out_enable = tag_getvalue(pTags, CLOCK_OUT_ENABLE, FALSE);
	uint32_t out_enable_bit = 0;
	if (out_enable)
	{
		out_enable_bit = 1;
	}

	/* Clock Output Configuration         */
	LPC_SC->CLKOUTCFG = (out_enable_bit << 8) | ((0x0F & (out_div - 1)) << 4) | (0x0F & out_sel);

	//外部出力する場合、有効になるまで待機
	if (out_enable)
	{
		while (!(LPC_SC->CLKOUTCFG & (1 << 8)))
		{
		}
	}
}

6.LEDのポート(GPIOのポート0ピン22)を設定

普通(?)に準備します。 BITBANDは便利ですね。 1個のレジスタに32ビットあって、その中の1ビットだけ変更したい場合に便利です。 その1ビットが別のアドレスの最右ビットにシャドーされています。 残念ながらDMACでのアクセスに使おうとしても方法が不明で使えません。 DMAC起動直後にエラーが発生して停止してしまいますのでBITBANDをあきらめて8ビット幅を書き込みます。
	// LED2点灯のGPIO設定
	// ポート0のピン22
	PINSEL_CFG_Type PinCfg;
	memset(&PinCfg, 0x00, sizeof(PinCfg));
	PinCfg.Portnum = PINSEL_PORT_0;

	PinCfg.Funcnum = PINSEL_FUNC_0;
	PinCfg.Pinmode = PINSEL_PINMODE_PULLUP;
	PinCfg.OpenDrain = PINSEL_PINMODE_NORMAL;
	PinCfg.Pinnum = PINSEL_PIN_22;
	PINSEL_ConfigPin(&PinCfg);

	// 出力に変更
	BITBAND_AHB(LPC_GPIO0->FIODIR, PINSEL_PIN_22) = 0b1;

	LPC_GPIO0->FIOMASK = ~(0b1 << PINSEL_PIN_22);

7.1秒タイマーを設定

LEDチカチカのトリガーになるタイマーを設定します。 タイマーは0~3の4個あって、0は汎用タイマーとして他のタスクが使うため、Lチカはタイマー1を使います。 CPUと同じクロックがタイマーに入りますので、まずプリスケーラで1μsに分周します。 CPUクロック数はその設定時にグローバル変数に入れてありますので参照します。 1μsのカウンタが1000000になるのをマッチレジスタで捕捉、DMACへトリガを出します。 ココを書いていて気がついたのですが、カウンタが1000000→0ということはカウンタ値が0の分だけ多いので、1000001通りあるから きっかり1sではなくて1.000001sのタイマーになっています。 誤差が+0.0001%なんで問題なし(おい)。 まあ炭素皮膜抵抗1本1円のは±5%だから正確無比と言っていいなコレ、うん。 ※アナログ人間の発言です。
	// タイマーに電源を入れる(他のデバイスがタイマーを使っていて既に電源が入っている可能性あり)
	CLKPWR_ConfigPPWR(CLKPWR_PCONP_PCTIM1, ENABLE);

	//ビット1を書いて割り込みフラグをクリア(0を書いたビットは影響なし)
	LPC_TIM1->IR = 0b111111;

	// カウント停止
	LPC_TIM1->TCR = 0b0;

	// タイマー1とマッチレジスタが一致した時の動作
	LPC_TIM1->MCR =
					// マッチレジスタ0と一致
		0b0 << 0 |	// 割り込み発生
		0b1 << 1 |	// TCを0リセット
		0b0 << 2 |	// TCを停止

					// マッチレジスタ1と一致
		0b0 << 3 |	// 割り込み発生
		0b0 << 4 |	// TCを0リセット
		0b0 << 5 |	// TCを停止

					// マッチレジスタ2と一致
		0b0 << 6 |	// 割り込み発生
		0b0 << 7 |	// TCを0リセット
		0b0 << 8 |	// TCを停止

					// マッチレジスタ3と一致
		0b0 << 9 |	// 割り込み発生
		0b0 << 10 |	// TCを0リセット
		0b0 << 11	// TCを停止
	;

	//タイマーモードで使用
	LPC_TIM1->CTCR = 0b0000;

	//プリスケーラで1MHz(1μs)クロックに落とす
	LPC_TIM1->PR = SystemFrequency / 1000000 - 1;

	//カウンタをリセット
	LPC_TIM1->TC = 0;

	//プリスケーラをリセット
	LPC_TIM1->PC = 0;

	//マッチレジスタ0で1秒間隔の一致を待つ
	LPC_TIM1->MR0 = 1000000;

8.タイマーのトリガをDMACへ繋げる

ソースのコメントの通りです(笑)。
	//転送要求の発生源切替
	LPC_SC->DMAREQSEL =
		0b0 << 0 |	// 8番を選択した場合 0:USART0のTX 1:タイマー0とマッチレジスタ0での一致
		0b0 << 1 |	// 9番を選択した場合 0:USART0のRX 1:タイマー0とマッチレジスタ1での一致
		0b1 << 2 |	// 10番を選択した場合 0:USART1のTX 1:タイマー1とマッチレジスタ0での一致
		0b0 << 3 |	// 11番を選択した場合 0:USART1のRX 1:タイマー1とマッチレジスタ1での一致
		0b0 << 4 |	// 12番を選択した場合 0:USART2のTX 1:タイマー2とマッチレジスタ0での一致
		0b0 << 5 |	// 13番を選択した場合 0:USART2のRX 1:タイマー2とマッチレジスタ1での一致
		0b0 << 6 |	// 14番を選択した場合 0:USART3のTX 1:タイマー3とマッチレジスタ0での一致
		0b0 << 7	// 15番を選択した場合 0:USART3のRX 1:タイマー3とマッチレジスタ1での一致
	;

9.DMACを起動

出力のパターンONとOFFを2個用意、LinkListも2個用意してそれぞれ関連付け、お互いのLinkListも関連付けして Ring状にします。 タイマーからのトリガで1個のLinkListを転送(中身1個)、ONとOFFを交互に出力してLEDが点滅します。
	// DMAに電源を入れる
	CLKPWR_ConfigPPWR (CLKPWR_PCONP_PCGPDMA, ENABLE);

	// リトルエンディアン、DMA停止
	LPC_GPDMA->Config = 0b00;

	// 転送要求を同期化(各ビット 0:同期、1:非同期)
	LPC_GPDMA->Sync = 0b00000000;

	// TC割り込みフラグをクリア
	LPC_GPDMA->IntTCClear = 0xFF;

	// エラーフラグをクリア
	LPC_GPDMA->IntErrClr = 0xFF;

	// リトルエンディアン、DMA起動
	LPC_GPDMA->Config = 0b01;

	// ターゲットのポート位置(ポート0、ピン22)
	// FIOPIN
	// 1.987654321.987654321.987654321.
	//          | PINSEL_PIN_22
	// 7654321.7654321.7654321.7654321.
	//         01000000
	// FIOPIN3 FIOPIN2 FIOPIN1 FIOPIN0

	static uint8_t burstdata[] = {
		0b01000000,
		0b00000000
	};

	// LinkList
	typedef struct
	{
		uint32_t CSrcAddr;
		uint32_t CDestAddr;
		uint32_t CLLI;
		uint32_t CControl;
	} TYPE_LINKLIST;

	static TYPE_LINKLIST linklist[] = {
		{(uint32_t)(burstdata + 0), (uint32_t)&LPC_GPIO0->FIOPIN2, (uint32_t)(linklist + 1), (1 & 0xFFF) << 0},
		{(uint32_t)(burstdata + 1), (uint32_t)&LPC_GPIO0->FIOPIN2, (uint32_t)(linklist + 0), (1 & 0xFFF) << 0}
	};

	// LinkListの1個目をDMACへ設定
	LPC_GPDMACH0->CSrcAddr = linklist[0].CSrcAddr;
	LPC_GPDMACH0->CDestAddr = linklist[0].CDestAddr;
	LPC_GPDMACH0->CLLI = linklist[0].CLLI;
	LPC_GPDMACH0->CControl = linklist[0].CControl;

	// DMA動作開始、タイマーからの転送要求毎(1秒毎)に転送する。
	LPC_GPDMACH0->CConfig =
		0b1 << 0 | // E (enable) 起動
		(0 & 0x1F) << 1 | // SrcPeripheral (メモリの場合は無視) DMAREQSELを選択する場合はこっちに指定しないこと
		(10 & 0x1F) << 6 | // DestPeripheral (メモリの場合は無視) 0~7:固定 8~15:USARTかタイマーのマッチレジスタ、DMAREQSELで指定した転送要求の中からタイマー1のマッチレジスタ0を選択
		0b001 << 11 | // TransferType メモリ→ペリフェラル
		0b0 << 14 | // IE (Interrupt error mask.) 1:マスク解除
		0b0 << 15 | // ITC (Terminal count interrupt mask.) 1:マスク解除
		0b0 << 16 | // L (Lock)はLPC17xxで使わない。
		0b0 << 17 | // A (Active)は読み出し専用
		0b0 << 18 | // H (Halt)
		0b0 << 19 // reserved 以下同じ
	;

10.タイマーを起動してLチカ開始

ビットを立てるだけ。 あとはタイマー→DMAC→GPIO→LEDの連携でLEDが点滅します。 この動作にプログラムが介在しないのでCPUの実行が不要です。 CPUと無関係に動作しますので、RedSuite3(NXP)のデバッガでCPUを止めていても点滅します(苦笑)。
	//カウント開始
	LPC_TIM1->TCR = 0b1;

11.プログラム終了

プログラムの仕事がなくなったので、このタスクを削除します。 このLチカの準備はFreeRTOSのタスクとして実行していますからタスクを削除してプログラムは終了です。
	//自分を削除
	TAGITEM_TYPE StartupItem[] = {
		{KSRK_TASK_TCB, (uint32_t)NULL},
		{TAG_END}
	};
	ksrk_delete_task(StartupItem);

今回の鉄ゲタは…

初めてLチカに挑戦してみましたがとても難しいですね。 まあ、どうせ話が通じないと思うので、あとはソース見てください(^^; というかページにソース入れちゃったからもういいか。 なお、このアーカイブは先日追加したページのと同じ物です(苦笑)。
yrntrlmnmnt20120909.zip (295,032 バイトをVPSから 00:05 で)